* ci(build-webview): sweep stale hashed chunks before rsync (#46)
Vite emits content-hashed asset names (e.g. `index-CXPb0I5z.js`), so the
active bundle filename rotates on every source/dependency edit. The
existing rsync runs without `--delete` (to preserve `bus-harness.html`
and `.gitignore`), so old chunks accumulated in
`App/Resources/webview/assets/` — observed 9 stale `index-*.js` /
`.js.map` pairs locally before this fix.
Add an explicit `rm -f` of the asset glob (JS + JS map + CSS + CSS map)
before the rsync drops the fresh bundle. The preserved files
(`bus-harness.html`, `index.html`, `.gitignore`) live one level up in
`$DEST`, not in `$DEST/assets`, so the rm cannot reach them.
Verified locally: `bash scripts/build-webview.sh` now leaves exactly one
`index-*.js` + `.js.map` in `assets/` (matching the reference in
`index.html`).
Refs #46
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* ci(scorecard): add OpenSSF Scorecard workflow + README badge (#178)
Adds `.github/workflows/scorecard.yml` running `ossf/scorecard-action@v2.4.0`
on:
- Weekly cron (Mondays 06:00 UTC) for posture drift detection
- Push to develop for prompt regression visibility
- workflow_dispatch for ad-hoc runs
Permissions follow the canonical Scorecard set (`id-token: write`,
`contents: read`, `security-events: write`) with top-level `read-all` and
job-scoped writes. Results are published to the OpenSSF public dataset
(powers the README badge URL) and uploaded as SARIF to code-scanning.
The workflow consumes no untrusted user inputs in `run:` blocks, so it is
not exposed to workflow-injection patterns documented in
github.blog/security/.../how-to-catch-github-actions-workflow-injections.
README.md gains the standard Scorecard badge next to the existing
license/platform/swift badges.
Refs #178
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
The default GITHUB_TOKEN cannot trigger downstream workflows (GitHub
prevents the chain to avoid infinite loops). publish-tag's `git push
origin "$TAG"` therefore never fires release.yml's `push: tags:`
listener — observed on v0.1.0-dev.50, where the tag landed but the
Release object was missing until manually created.
Fold the Release creation into the publish-tag job using `gh release
create` with GH_TOKEN=secrets.GITHUB_TOKEN. Direct API calls work fine
with the default token; only the push→workflow chain is blocked.
- Idempotent: skips if release already exists.
- Notes: auto-generated from `git log $prev..$TAG` plus a Verification
block linking back to the CI run.
- Prerelease flag respects steps.version.outputs.prerelease (true for
dev/rc tags, false for vX.Y.Z final tags on main).
release.yml stays untouched as a fallback for manually-pushed tags
(humans pushing tags directly CAN trigger workflows).
Closes#186
Grogu will host multiple self-hosted runners simultaneously (planned: 3
runners). Each runner's job-start cleanup step previously killed ANY
xctest/swift-test/swift-driver — meaning sibling runners would nuke
each other's in-flight tests.
Real CI jobs complete in <10 min. Zombies from interactive Xcode use
are typically >hours old. A 1-hour age cutoff cleanly separates the
two populations.
awk filter: ps -o etime formats are `MM:SS` (<1h), `HH:MM:SS` (>=1h),
or `DD-HH:MM:SS` (days). Match the latter two by testing for `-` (days
prefix) or three `:`-separated groups.
Single-runner behavior unchanged. Validated against synthetic ps
output covering all three etime format cases.
Closes#188
Grogu is a shared workstation, not an ephemeral VM. Interactive Xcode
test runs, aborted swift test sessions, or debugger crashes can leave
orphan xctest/swift-test/swift-driver processes around for hours,
holding Mach services + sockets + ports that subsequent CI jobs block
on when mounting their own test infrastructure.
Concrete incident: develop run 25824607025 hung in SPM AgentCore for
34 min (0% CPU, sleeping state) while 6 unrelated xctest zombies aged
22-24h were alive from prior interactive use. The runner only executes
one job at a time, so any such process at job start belongs to no live
job and is safe to kill.
Add a first step that runs SIGTERM then SIGKILL on any
xctest/swift-test/swift-driver process. Gated to runner.environment ==
'self-hosted' since github-hosted VMs start clean.
Closes#182
* ci(actions): move swift-packages matrix to self-hosted runner (Grogu)
The github-hosted macos-15 pool was the rate-limiting step for v0.1: 13
matrix jobs + 1 app-build job all needed macOS slots, GitHub scheduled
them 1-3 at a time, and total wall-clock was ~1-2h per run versus actual
work of <10m. Grogu is registered with labels [self-hosted, macOS, ARM64,
jarvis] and _work/ symlinked to HD-2 (1.3 TB) for headroom.
Scope:
- swift-packages matrix only — app-build stays on macos-15 for now.
- `if:` clause blocks fork PRs from triggering self-hosted jobs (push,
workflow_dispatch, and same-repo PRs only).
- Xcode select switches from `sudo xcode-select -s` to a per-process
DEVELOPER_DIR env so the runner doesn't need passwordless sudo. The
fallback chain now also accepts /Applications/Xcode.app/Contents/Developer
so Grogu's local Xcode 26.5 is picked up.
Closes#180
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* ci(actions): fix self-hosted runner env quirks for keychain + cache
Three SPM failures on PR #181's first self-hosted CI run, all
environment-specific:
- SPM Keychain: errSecInteractionNotAllowed (-25308). The LaunchAgent
has no UI session and the login keychain is locked.
- SPM Voice + SPM Harness: github-hosted actions/cache@v4 entries with
absolute paths (/Users/runner/work/...) baked into
.build/artifacts/onnxruntime.xcframework. Cache key only contained
runner.os (= macOS for both environments), so self-hosted hit the
poisoned github-hosted cache.
Fixes:
- Restrict actions/cache@v4 to runner.environment == 'github-hosted'.
Self-hosted's _work/ on HD-2 persists between jobs naturally, so the
cache action is unnecessary there and prevents path-baked cache
poisoning between environments.
- Add a "Unlock runner keychain" step gated to runner.environment ==
'self-hosted'. Unlocks a pre-created jarvis-runner.keychain-db with
password from the repo secret JARVIS_RUNNER_KEYCHAIN_PASSWORD and
prepends it to the user search list so SecItemAdd writes there.
Document both in CLAUDE.md "Self-hosted CI runner".
Refs #180
* ci: retrigger after JARVIS_RUNNER_KEYCHAIN_PASSWORD secret fix
Previous run failed all 13 SPM jobs with 'passphrase you entered is not
correct' because the secret value was set to literal '-' (gh secret set
parsed '--body -' as the body value, not as 'read from stdin'). Secret
now contains the correct password. This empty commit triggers a fresh
CI run that will resolve the corrected secret.
Refs #180
* ci(actions): drop set-keychain-settings from unlock step
The Security framework requires user interaction for set-keychain-settings
even when the keychain is unlocked, and the LaunchAgent on Grogu has no
UI. The settings (no-timeout, no-lock-on-sleep) are persistent and were
applied once at keychain-creation time; only the unlock step needs to
run every job.
Refs #180
* ci(actions): also set default-keychain so SecItemAdd targets jarvis-runner
SecItemAdd on the legacy file-based macOS keychain writes to the user's
DEFAULT keychain, not the first writable keychain in the search list.
Changing list-keychains alone left SecItemAdd targeting the locked
login.keychain-db, still returning errSecInteractionNotAllowed (-25308).
Also set the user's default keychain to jarvis-runner.keychain-db in the
unlock step. This is persistent for the runner user (james.maes), so
SecItemAdd from interactive use will also go there — documented as a
side effect in CLAUDE.md with the undo command.
Refs #180
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
PR #164 stripped pnpm+node setup from app-build expecting the artifact to be sufficient, but Xcode's build-phase script invokes scripts/build-webview.sh which needs pnpm on PATH. Re-adds Setup pnpm + Setup Node.js steps. Surfaced by PR #170's error-visibility improvement.
CI run 25799596445 failed on 6 SPM packages + the App target. Root cause
was a toolchain skew between local dev (Swift 6.3.2 / Xcode 26) and CI
(Swift 6.0 / Xcode 16):
- mlx-audio-swift v0.1.2 ships swift-tools-version:6.2 which Swift 6.0
cannot parse, breaking Voice + Harness + the App target with
"Dependencies could not be resolved because 'mlx-audio-swift' contains
incompatible tools version (6.2.0)".
- MCP swift-sdk 0.12.0 uses the new `withThrowingTaskGroup` overload
(no `of:` parameter) that only exists in Swift 6.2+.
- AgentCore tests use `@MainActor` XCTestCase with `await super.setUp()`,
a pattern Swift 6.0 rejects but Swift 6.1+ accepts.
The macos-15 runner already ships Xcode 26.3 / 26.2 / 26.1 / 26.0
alongside 16.x, so the fix is to switch the Xcode select. Fallback to
Xcode_26.app preserves robustness against patch-level shuffles.
Also:
- Drop `VoiceLog` from the SPM matrix. No `packages/VoiceLog` directory
exists; the job failed with `chdir error: No such file or directory`.
Voice-log work is parked under v1.1.
- Bump BatcherTests `waitForDrain` from 80ms to 250ms. The batcher
window is 33ms; 80ms was tight enough that macos-15 dropped
test_tokenDeltaConcat with sendCount=0 because the scheduled drain
hadn't completed its actor + main-actor hop before the test resumed.
250ms (~7.5× the window) survives CI jitter; verified locally — all
five Batcher tests pass in 1.27s.
Refs: CI run 25799596445.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
PR #165 fixed the one real strict-undefined error in SegmentedRing.tsx;
the other ~30 errors reported in CI run 25797533005 were downstream of
`TS2307: Cannot find module '@jarvis/bus'` and resolve automatically
once `scripts/build-webview.sh` produces the bus package's
`dist/index.d.ts` (which CI already runs before typecheck).
Removes `continue-on-error: true` from the Typecheck step so typecheck
regressions block CI going forward.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Three fixes that #161 attempted but only partially landed: webview build-before-typecheck (missed in #161 due to security-hook short-circuit), artifact rename webview-dist→webview-resources (matching the downloads), typecheck continue-on-error:true (~30 pre-existing TS18048 strict-undefined errors in hud src — filed as v0.2 cleanup). After this lands, SPM + app-build + publish-tag chain unblocks.
Three surgical fixes to .github/workflows/ci.yml from the first develop-push validation cycle:
1. Webview job: typecheck ran before build, so packages/hud TS imports of @jarvis/bus failed (TS2307). Reordered: install → build → typecheck → test. Build invokes scripts/build-webview.sh, producing App/Resources/webview/ directly.
2. swift-packages job: 6 packages (AgentCore/Bus/Harness/MCP/Voice/VoiceLog) failed with 'bus not mounted' because BusTests mount real WKWebView. Added needs: [webview] + download-artifact for the new webview-resources artifact.
3. app-build job: replaced inline pnpm+node+build-webview.sh with the same artifact download. One webview build serves all consumers.
Artifact renamed webview-dist → webview-resources (production-stamped App/Resources/webview/ tree). Old name was 1 day old, no external consumers.