dependabot/github_actions/github/codeql-action-4
24
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5ebe510627 |
docs: scrub Phase-7 + JarvisBusWorld doc drift (#19, #45) (#195)
* docs(memory): scrub Phase-7 hedge language from MemoryStore (#19) Audit 2026-05-12 flagged five doc-comment references that mis-described the implementation state. The cited plans (07-02, 07-03, 07-06) shipped months ago — the bounded extraction channel, the hybrid FTS5 + vec0 read path, the ReplayEvent emission, the regression corpus test seams, and `installMemory`'s replay-sink wiring are all live, not "wires" or "will replace" promises. Five touchpoints rewritten to describe the code as it is: - `MemoryReplaySink` doc: drop the "Plan 07-06" plan reference; the seam is just used at startup. - `MemoryStore` actor doc: replace the "Plan 07-02 wires / Plan 07-03 wires" sentence with the actual contract (single writer; hybrid search through `MemoryReadStore`; `recordRetrieval` emits `memoryRetrieval`). - Read-path MARK: drop the "Plan 07-03" plan label; keep the requirement-ID list (MEM-07 / TEXT-03 / D-02 / D-05) since those are still useful for traceability to the audit corpus. - `recordRetrieval` doc: replace "Mirrors recordMemoryMutation from 07-02" with "Mirrors recordMemoryMutation on the write path." - Test-seams MARK: drop "Plan 07-06" prefix; describe by the test target (JARVIS_REAL_MODELS=1 corpus suite) instead. The 2026-05-11 D-5/D-6 markers and the "Boot policy (changed 2026-05-11)" doc are preserved unchanged — they document current behaviour, not historical hedges. `swift build --package-path packages/Memory` clean. Closes #19 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(bus): scrub JarvisBusWorld drift + add grep gate (#45) `fb41c5f` (2026-05-02) unified `Injection.js` and the HUD bundle into the default `WKContentWorld.page` and retired the prior isolated `JarvisBusWorld` named world. The doc-comments and test fixtures across 20+ lines kept describing the bug-era topology — pre-existing-bus attach "because cross-world isolation", message-handler-only-visible-in-named- world, etc. — for months afterward. Result: a new reader following the docs would conclude the bus uses two worlds when it actually uses one. Five rationale rewrites: - `WebviewBridge.swift` class doc — handler is on the page world, not an isolated named world. Page scripts in the loaded HUD bundle address it via `window.webkit.messageHandlers.jarvisBus`. - `Injection.js` header — names the page world explicitly; one-line nod to `fb41c5f` for the historical context. - `bridge.ts` JSDoc + `isJarvisBusShape` rationale — recast the H-02 attach-to-existing path as a "defensive double-install guard" inside ONE page world. Injection.js fires first at document-start; the bundle installer fires later as a top-level `main.tsx` side-effect; the guard prevents the bundle from clobbering Injection.js's queued early messages and captured handshake state. The duck-type `isJarvisBusShape` CODE is unchanged — only the comment narrative was misleading. - `webview/packages/hud/src/main.tsx` — drop the cross-world-isolation framing; describe the single-world reality. - `App/AppDelegate.swift` install comment — page world, not isolated. Test-side updates (same scrub, plus one latent test bug surfaced): - `WebviewBridgeTests.test_scriptMessageHandlerIsRegisteredUnderConfigured- Name`: was removing from `WKContentWorld.world(name: "JarvisBusWorld")` while the bridge registered against `.page` — silent no-op since `removeScriptMessageHandler` doesn't throw on absent. Now removes from `.page` (the world the bridge actually uses), making the "registration-is-evidence-of-success" assertion meaningful again. - `WebviewBridgeOutboundTests`: comment-only. - `RealWKWebViewIntegrationTests`: comment-only (forensic narrative kept, literal world name replaced with "prior isolated named content world"). - `webview/packages/bus/tests/bridge.test.ts`: H-02 describe-block + one it-name rewritten to match the new defensive-double-install rationale. Infrastructure: - `scripts/check-no-jarvis-bus-world-references.sh`: new boundary gate. Greps for the literal `JarvisBusWorld` in tracked files; excludes `.planning/`, `docs/`, `webview/packages/*/dist/`, and this script itself. Wired into `CLAUDE.md`'s gate listing; the CI `boundary-gates` job auto-discovers all `scripts/check-*.sh`, so no workflow edit is needed. - `webview/packages/bus/package.json`: 0.0.0 → 0.0.1 (per spec — comment-only changes still bump patch so consumers can pin). - `webview/packages/bus/dist/bridge.{js,d.ts}`: rebuilt via `pnpm --filter @jarvis/bus build`; NOT committed because `dist/` is not tracked (the build phase regenerates on each xcodebuild invocation). Verified locally: - `swift build --package-path packages/Bus` clean - `pnpm --filter @jarvis/bus test` — 37/37 pass - `bash scripts/check-no-jarvis-bus-world-references.sh` — PASS - `bash scripts/check-bus-protocol-version.sh` — PASS (v2.3.0) - `bash scripts/check-bus-harness-parity.sh` — PASS Closes #45 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
a121d79174 |
fix: STT backend constant casing + audioLevel NaN guard (#37, #47) (#196)
* docs(voice): pin STT backend constant identity contract (#37) The runtime mismatch flagged by #37 was already fixed at the call sites in `App/AppDelegate.swift:1631-1642` and `:2562-2575` — both routes now reference `STTBackendSelector.backendSpeechAnalyzer` / `backendWhisperKit` (snake_case) instead of the pre-2026-05-12 camelCase string literals. The regression test `STTBackendSwitchTests.testB5_canonicalConstantsRoundTripThroughMake` already locks both halves of the contract: 1. The snake_case constants round-trip through `make(backend:)` without hitting the unknown-backend default branch. 2. The pre-fix camelCase shape (`"whisperKit"`) DOES land in the default branch and DOES emit an unknown-backend warning. So the #37 close action is just to make that contract self-documenting at the declaration site rather than relying on readers finding the test. Add a doc-comment to the two `public static let backend*` constants that explains the wire shape, names the regression test, references Issues #29 + #37, and warns against renaming without updating `PerTurnSnapshot` and the test in lockstep. No code change; `swift test --package-path packages/Voice` continues to pass (26 tests). Closes #37 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(bus): clamp non-finite audioLevel rms to 0.0 at encode (#47) `BusOutbound.audioLevel` carries a `Float rms` that `AudioLevelEmitter` produces per audio frame (~100 Hz once the consumer is wired up). Foundation's `JSONEncoder.NonConformingFloatEncodingStrategy` defaults to `.throw`, so a single NaN — easy to land via a CoreAudio over-/underflow or an all-zero buffer fed through certain RMS shapes — would throw inside `WebviewBridge.sendRaw` and tear down the per-frame bus path. The HUD-side consumer in `webview/packages/hud/src/bus/ client.ts:135` is currently a no-op (the ring shader is still on the synthetic-sine animation from Plan 03-03), so the bug is LATENT, not actively firing — but the clamp is cheap, defensive, and pins the contract before the consumer activates. Inline clamp at the encode site (`BusOutbound.encode(to:)` switch arm for `.audioLevel`): replace non-finite floats with `0.0` via `rms.isFinite ? rms : 0.0` immediately before `container.encode(_:)`. No refactor of `AudioLevelEmitter` — the per-spec call here is defensive at the serialization boundary, where every encoder-level overflow path converges. Two new unit tests in `CodableRoundTripTests`: - `test_audioLevel_nanIsClampedToZero` — encodes `.audioLevel(rms: .nan)`, decodes, asserts `rms == 0.0` and `rms.isFinite`. - `test_audioLevel_infinityIsClampedToZero` — companion for ±∞ so a future refactor cannot narrow the guard to NaN-only. `swift test --package-path packages/Bus --filter CodableRoundTripTests` passes (30 tests, +2 from this commit). Closes #47 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
bcc55c68be |
fix(ci): pin Xcode 26.x, drop phantom VoiceLog, harden Batcher timing (#167)
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> |
||
|
|
a6f91e6b73 |
fix(bus,hud): v0.1 S1 cluster — webview didFail + menu-bar icon transition (#40, #41, #151)
#40: BridgeNavigationDelegate gains onDidFail closure (covers didFailProvisionalNavigation + didFail). WebviewBridge.failHandshake(reason:) transitions to new terminal .loadFailed state, cancels pending 2s timeout, fires existing alertPresenter, idempotent. AppDelegate.handleWebviewLoadFailure dual-surfaces: failHandshake + critical non-dismissible banner (defense-in-depth across test seams). WebviewBootHealthProbe gets .loadFailed case. #41: HudStateCoordinator emit closure now ALSO calls menuBarController.transition(to:) in addition to bridge.send(.hudState(...)). Doc-comment correctly notes this is not a HUD-08 single-writer violation — transition writes currentState, not BusOutbound.hudState. scripts/check-single-writer-hudstate.sh still green. Tests: 4 new Handshake cases (load-failure-from-sentHello with alert-not-duplicated, from-idle, idempotency, post-armed no-op), BridgeNavigationFailureTests end-to-end + per-arm, MenuBarIconStateTransitionTests drives dormantAgentContinuation and asserts .thinking → .speaking → .idle. 62/62 Bus pass; all 18 boundary gates green. |
||
|
|
508cf924d0 |
docs(packages): per-package README files
Adds README.md to each of the 13 SPM packages (AgentCore, Bus, Config, DevOverlay, Harness, Keychain, Logging, MCP, Memory, Replay, Shell, Vision, Voice). Each covers purpose, key public types, deps, consumers, invariants (pulled from CLAUDE.md + grep-gates + audit findings), test stack, and notable files. Memory README explicitly marks the package functionally OFF until vec0.dylib + Ollama models land. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
f9cd25d66e |
chore(test-naming): converge on Mock/Fake/Stub convention; document in CLAUDE.md; rename worst-mismatch stragglers
Per audit-2026-05-04 P2-15: 26 Mock*, 21 Fake*, 12 Stub* across the test corpus, often used interchangeably for the same role. New CLAUDE.md section "Test naming conventions" (line 127) sets the rule: - Mock*: records calls AND returns scripted responses (assertion driver). - Stub*: returns canned data without recording (deterministic input). - Fake*: alternative implementation with realistic behavior (in-memory state-holder). Renamed 15 worst-mismatch stragglers in the older corpus (per audit constraint, did NOT touch types added in today's P1+P2 fix commits): Stub* → Mock* (records + scripts): - HybridSearchTests: StubEmbedder → MockEmbedder - HybridSearchTests: StubStore → MockStore - SessionHistoryTests: StubStore → MockStore - InProcessMemoryToolsTests: StubHybrid → MockHybrid - InProcessMemoryToolsTests: StubHistory → MockHistory - InProcessMemoryToolsTests: StubForget → MockForget Mock* → Stub* (canned data, no recording): - STTBackendSwitchTests: MockSpeechAnalyzerBridge → StubSpeechAnalyzerBridge - STTBackendSwitchTests: MockWhisperKitBridge → StubWhisperKitBridge - VoiceWiringTests: MockVADEngineForW4 → StubVADEngineForW4 - VoiceWiringTests: MockSTTProviderForW4 → StubSTTProviderForW4 - VoiceWiringTests: MockTTSForW4 → StubTTSForW4 - VoiceWiringTests: MockOrchestratorForW4 → StubOrchestratorForW4 - VoiceWiringTests: MockBannerForW4 → StubBannerForW4 - VoiceWiringTests: MockBusForW4 → StubBusForW4 Fake* → Mock* (records + scripts): - WebviewBridgeOutboundTests: FakeJSEvaluator → MockJSEvaluator - HudStateCoordinatorBusWiringTests: FakeJSEvaluator → MockJSEvaluator (the comment on the type already said "Records each call for assertion") Skipped per audit constraint: - StubCapture/StubSink (Vision FrameAttachConfirmSendWiringTests, P1+P2) - MockVADEngine + MockOrchestratorForController siblings (Voice VoiceControllerTests/VADGatedSessionTests, today's track-B churn) Tests pass: Voice 87+23, MCP 85, Memory 88, Bus 58, AgentCore 19+72, Vision 19, Shell 7+5. App target builds clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
6f6617e67b |
fix(test): update F-A2-01 stale JarvisBusWorld assertion to .page
The Bus production switched from `WKContentWorld.world(name: "JarvisBusWorld")` to `WKContentWorld.page` in |
||
|
|
1f8e03edeb |
fix(track-a): demo-blocker class + audit reports + chat-turn e2e tests
User feedback: 50+ hours of agent work, nothing demonstrably working.
Fanned out 6 deep auditors (voice, HUD, vision, memory, LLM, tests);
all reports landed at .planning/audit-2026-05-03/ with brutal-honesty
mandates. Synthesis (SYNTHESIS.md) confirmed: every modality has at
least one fatal break between code-that-passes-tests and
code-that-runs. Track A is the smallest credible path to one working
modality (text turn + animated rings).
Closed under TDD discipline:
**Item 4 — HUD idle ring static rendering (F-A1-01).**
- webview/packages/hud/src/hud/stateUniforms.ts:30 — was
`pulse: 0.0, rotate: 0.0` for idle, the all-static configuration
that rendered STANDBY as dots instead of animated rings. Now
pulse: 0.4, rotate: 0.05 — gentle ambient distinct from booting +
thinking.
- webview/packages/hud/tests/stateUniforms.test.ts — table updated;
new regression-guard `STATE_PARAMS[*] is not all-static` (7 cases,
one per HudState) prevents anyone from re-zeroing a state.
**Item 1 — CacheHints eligibility gate (likely streamTruncated cause).**
- packages/AgentCore/Sources/AgentCore/CacheHints.swift —
+ static func eligibleForSystemPrompt(_:ttl:). Anthropic's prompt
cache requires ≥1024 tokens at the breakpoint; below that, the
server returns 200 OK + immediate-EOF for `extended-cache-ttl`
hints. The orchestrator's default system prompt is ~10 tokens.
Helper returns nil for sub-threshold prompts so no cache_control
marker is emitted.
- packages/AgentCore/Sources/AgentOrchestrator/AgentOrchestrator.swift
— call sites now pass `CacheHints.eligibleForSystemPrompt(finalSystem)`
through runTurnLoop instead of unconditional `.extended1h`.
- Tests: CacheHintsEligibilityTests (6 cases), RequestBodyTests R5a/R5b
(2 cases verifying small-prompt → no cache_control in encoded JSON,
large-prompt → cache_control present).
**Item 2 — StreamOutcome diagnostic.**
- packages/AgentCore/Sources/AnthropicProvider/StreamOutcome.swift —
new pure classifier that distinguishes:
- non-2xx HTTP status
- 200 OK + 0 bytes (cache_control / auth-header rejection class)
- 200 OK + bytes + 0 frames (malformed body)
- 200 OK + frames + EOF before message_stop (mid-stream drop)
- 200 OK complete (healthy)
AnthropicProvider's read loop counts frames + bytes and logs the
summary once per stream. Production logs now distinguish failure
modes that previously all surfaced as `streamTruncatedFinal`.
- Tests: StreamOutcomeTests (7 cases, all branches).
**Item 3 — KeychainError surfacing.**
- packages/AgentCore/Sources/AnthropicProvider/AnthropicAPIKeyProvider.swift
— new builder `make(keychain:item:)` returns the
`@Sendable () async throws -> String` closure with errors
propagating verbatim. Closes the audit's hidden-401 finding:
`(try? keychainStoreLocal.get(.anthropic)) ?? ""` swallowed the
real KeychainError into an empty x-api-key, producing
`401 x-api-key header is required` indistinguishable from a
stale-key 401. Now the actual error reaches the chat panel via
AnthropicProvider → orchestrator.error → BusForwarder →
submitRejected.
- App/AppDelegate.swift — both Anthropic provider construction sites
(installAgent, installVision T3 wiring) now use the propagating
builder.
- Tests: AnthropicAPIKeyProviderTests (6 cases — itemNotFound,
unexpectedStatus, duplicateItem propagation; happy path; explicit
item; default item).
**Item 5 — F1 #2 chat-turn round-trip e2e tests.**
- packages/Bus/Tests/BusTests/RealWKWebViewIntegrationTests.swift —
+ 2 new tests using a real WKWebView:
- test_chatTurnRoundTrip_busOutboundReachesPageHandler — Swift
`bridge.send(.turnStarted/tokenDelta/turnEnded)` reaches the
page's onOutbound handler. This is the test the v0.12.0 audit
called out as catching BLOCKER-INT-1, INT-2, INT-3,
F-E-RACE-1, F-E-FK-1, F-E-WIRE-1 in one shot.
- test_chatTurnRoundTrip_busInboundFromJSReachesSwiftHandler —
JS-side `window.jarvisBus.send({type:'chatSubmit',...})` —
exactly what ChatInput.tsx does — reaches the bridge's
onInbound handler with the right body.
**Verification:**
- AgentCore: 215/215 (was 178; +37 across BusForwarder, CacheHints,
StreamOutcome, AnthropicAPIKeyProvider, RequestBody R5).
- Replay: 33/33.
- Bus: 57/58 (1 pre-existing failure: F-A2-01 stale `JarvisBusWorld`
assertion documented in AUDIT-FINDINGS, separate scope).
- webview/packages/hud: 94/94 (was 87; +7 stateUniforms regression-
guards + idle table updates).
- All 15 boundary grep gates: PASS.
- scripts/check-app-builds.sh: PASS.
**Audit reports retained at .planning/audit-2026-05-03/:**
- SYNTHESIS.md (consolidated, with triage plan)
- hud-audit.md, llm-audit.md, memory-audit.md, tests-audit.md,
vision-audit.md, voice-audit.md, voice.md (per-modality detail)
**Carry-forward (NOT closed this commit):**
- F-A2-01 stale WebviewBridgeOutboundTests assertion (pre-existing).
- Voice (F): models never bundled, audio graph never instantiated,
STT bridge body is `_ = chunk`, TTS engine nil. ~1 day.
- Vision (F): AVCapturePhotoOutput allocated but capturePhoto never
called; HUD camera button doesn't exist. ~1 day.
- Memory (F): Apple's stripped libsqlite3 prevents sqlite-vec from
loading; "future ops plan" has zero commits. ~3 days.
- LLM (D+): NoopBusGateway still drops tool-call events
(BLOCKER-INT-1).
- Tests (C-): 5 XCTAssertTrue(true) tautologies + multiple
rubber-stamp tests defending stubs.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
326a27d530 |
test(bus,f1): real-WKWebView handshake integration test — would have caught the WKContentWorld bug class
Phase F1 first deliverable, AUDIT-AND-FIX-PLAN.md. Pragmatic scope
adjustment: lands as `packages/Bus/Tests/BusTests/RealWKWebViewIntegrationTests.swift`
(SPM test target, runnable via `swift test`) instead of a brand-new top-
level `IntegrationTests` Xcode target. Reasoning:
• Xcode-target scaffolding requires substantial pbxproj surgery (new
PBXNativeTarget + dependency proxy + build-config list + 2 build
configs + sources/frameworks phases + N package-product-dependencies)
that risks breaking the project file.
• The Bus package's existing SPM test target already imports WebKit and
uses real WKWebView in its other tests; adding to it is a pure file-
addition with no project-graph changes.
• SPM `swift test` runtime is immune to upstream Xcode 26 RunningBoard
error 5 that had blocked Phase 3's xcodebuild test runtime.
• Phase D-Phase-2 of AUDIT-AND-FIX-PLAN.md specifies this exact path
(`packages/Bus/Tests/BusTests/RealWKWebViewIntegrationTests.swift`),
so F1's "first starter test" + D-Phase-2's required test collapse
into one shipping file.
Two tests, both pass in ~1.0–1.4s on the fixed branch:
test_endToEndHandshake_reachesArmed
Constructs a real WKWebView, loads a tiny HTML page so Injection.js
runs at document-start, calls bridge.startHandshake(), polls for
handshakeState == .armed within 2.5s. Asserts armed AND alert-path
NOT fired (alert is the FAILURE handler).
On the fb41c5f-fixed branch: passes (~1.0s — load + Injection.js +
handshake round-trip in unified .page world; auto-ack-hello at the
stub layer means no bundle handler needed).
On the bug branch (WKContentWorld(name: "JarvisBusWorld") for
Injection.js + page world for the message handler): would hang on
.sentHello, hit the 2s production timeout, transition to .timedOut,
invoke alertPresenter. Both assertions fail. THIS is the test that
would have caught the WKContentWorld isolation bug before shipping.
test_endToEndHandshake_passesThroughSentHello
Same setup but samples handshakeState rapidly post-startHandshake()
to confirm .sentHello was observed before .armed. Defends against a
future regression where Swift short-circuits .armed without the JS
round-trip (which would make the first test pass spuriously).
Test verdict on production code: green. Bus suite count delta: 54 → 56
tests. One failure (F-A2-01) remains unchanged — pre-existing stale
JarvisBusWorld assertion in WebviewBridgeOutboundTests.test_sendWhenArmedCallsEvaluator.
Honest scope: 3 of the 4 planned F1 starter tests still TODO:
- Wizard happy path
- Toggle HUD via hotkey
- Ask-the-agent-something flow (would catch BLOCKER-INT-2 directly)
The third is the highest-priority remaining F1 work — once it lands,
BLOCKER-INT-2 becomes a failing test we can then TDD-fix in Phase E.
Refs: .planning/AUDIT-AND-FIX-PLAN.md Phase F1 + Phase D-Phase-2.
|
||
|
|
2c349a9b45 |
chore(diag): strip BootDiag instrumentation; keep WKContentWorld + Injection.js production fixes
Phase A2 of AUDIT-AND-FIX-PLAN.md. The BootDiag instrumentation served its purpose during the 2026-05-02 session — it produced the JS-state snapshot (`handler=null pending=1`) that pinpointed the WKContentWorld isolation bug behind the HUD-stuck-on-BOOTING. With the production fix landed at |
||
|
|
fb41c5f1df |
fix(bus): unify WKContentWorld + always-ack hello at stub level
Two related bugs blocking the HUD from advancing past BOOTING after handshake. Found via BootDiag instrumentation that proved Swift was correctly emitting .idle but the JS handler was never receiving it (_handler null, _pendingMessages stuck with 1 entry). Root cause #1 — content-world isolation: Injection.js installed window.jarvisBus in WKContentWorld 'JarvisBusWorld'. The page bundle (<script type=module>) ran in the DEFAULT page world. WKContentWorld isolates window.* property access (the comment in main.tsx claiming otherwise was wrong — content worlds share the DOM *object identity* but properties are per-world). Result: bundle's attachBus() saw window.jarvisBus = undefined in default world, so installJarvisBus's Vy() created a SECOND fresh bus object there. Two stubs, two different objects, no shared handler. Swift sent outbound to the JarvisBusWorld stub which had _handler=null forever. Fix: WebviewBridge defaults to WKContentWorld.page so Injection.js, the page bundle, and the script-message-handler all share one world. Injection.js stub IS the only window.jarvisBus, bundle's Vy() shape- checks it and skip-replaces (correct), bundle's onOutbound(handler) sets _handler on the same object Swift's outbound hits. Public init gains a contentWorld parameter (default .page) for tests to inject. Root cause #2 — hello ack lost when handler is registered first: The stub's receive() routed messages to _handler if registered, falling through to auto-ack only when _handler was null. Once world-isolation was fixed, the bundle's handler registered BEFORE Swift's first hello arrived. The bundle's outbound switch has 'case hello: break' (it intentionally treats hello as a no-op at the application layer). With no auto-ack and no handler ack, the handshake timed out and the bridge never reached .armed. Fix: Injection.js receive() now ALWAYS auto-acks hello at the stub level, before checking _handler. Hello is owned by the stub regardless of bundle handler state — only the stub knows the protocolVersion the Swift side needs to match and the bundle's case-hello-break makes routing through to the bundle pointless. The 'case hello: break' line in the bundle becomes dead code but harmless. Verified via BootDiag trace: Before: snapshotJSBus: handler=handler-null pending=1 (stuck) After: snapshotJSBus: handler=handler-set pending=0 (delivered) |
||
|
|
e8a956c69f |
fix(bus): replace single-slot _pendingHello with FIFO queue in Injection.js
The injected stub at packages/Bus/Sources/Bus/Resources/Injection.js buffered exactly one non-hello message in _pendingHello before the bundle's handler registered via onOutbound(). Subsequent non-hello messages overwrote the slot, so only the LAST early message survived. In production this manifested as HUD-stuck-on-booting after Phase 9 shipped: handshake armed correctly (after the v2.3.0 fix), markReady() fired, HudStateCoordinator emitted .hudState(.idle) — but that message landed in _pendingHello while the bundle was still loading, then got clobbered by a subsequent boot-time outbound (telemetry, indicator state, etc.) before the bundle's handler finished registering. The HudStateCoordinator never re-emits .idle after the initial promotion (state changes only fire on intent stream activity), and there's no 'reissue current state on uiReady' path on the Swift side, so the HUD stayed pinned to its initial .booting render. Fix: change _pendingHello to _pendingMessages (a FIFO array). Push every pre-handler non-hello message; on onOutbound() registration, drain the queue in arrival order. Hello still auto-acks inline so the Swift handshake can arm before the bundle finishes loading. Verified: rebuilt, launched, HUD progresses past booting; bus parity gate green at v2.3.0 across Swift / TS / Injection.js. |
||
|
|
2bb710d3e6 |
fix(bus): bump Injection.js placeholder to v2.3.0 + extend parity gate
The WKUserScript at packages/Bus/Sources/Bus/Resources/Injection.js installs window.jarvisBus at document-start with a hardcoded `protocolVersion` placeholder. Because the full TS bundle's installer shape-checks window.jarvisBus and skip-replaces when send/receive/ onOutbound already exist (Vy() in the minified output), the placeholder IS the version reported in helloAck at runtime. The placeholder had been stale at 2.0.0 since the first protocol bump (2.0.0 → 2.1.0 → 2.2.0 → 2.3.0), surfacing as runtime 'HUD bundle was built against bus protocol v2.0.0; the app expects v2.3.0' hard-fails after Phase 9's Plan 04 re-bumped the protocol. Fix: - Bump Injection.js's protocolVersion literal to '2.3.0'. - Extend scripts/check-bus-protocol-version.sh to assert three-way parity (Swift Protocol.swift ↔ TS protocol.ts ↔ Injection.js) so this drift can never recur silently. Verified: bus parity OK at v2.3.0; check-app-builds green; runtime launch with PID 32607. |
||
|
|
96a8e2da72 |
feat(09-04): add chatSubmit / chatCancelAndSubmit / submitRejected bus messages + AppDelegate text-input handlers (D-10, D-12, BLOCKER-1, WARNING-4)
Wire layer: - BusInbound gains chatSubmit(text:) + chatCancelAndSubmit(text:) cases. - BusOutbound gains submitRejected(reason:) case. - BUS_PROTOCOL_VERSION 2.2.0 -> 2.3.0 (Swift + TS); fixtures byte-identical. AppDelegate (chat-panel handlers): - handleChatSubmit + handleChatCancelAndSubmit dispatch into AgentOrchestrator.submit(.text(...)) / cancelAndSubmit(.text(...)) (D-12). - BOTH handlers call tryPhraseAttachIfMatch BEFORE submit so phrase-matched text input arms the FrameAttachController (WARNING-4 closure — previous plan only wired chatSubmit). - BLOCKER-1 user-side: appendUserTextIfRunning appends user text to TurnTranscriptStore on .ran/.superseded so MemoryExtractionCoordinator finds non-nil pair text. - handleTextOutcome routes SubmitOutcome.rejected to a Bus toast via RejectReasonCopy.body — byte-identical wording to the voice-path HUD banner (D-10 single source of truth). VISION-03 gate adjustment: - check-presence-vision-isolation.sh Layer 3 no longer flags `cancelAndSubmit` in App/ files holding PresenceSignalBus references. Plan 4 legitimately introduces orchestrator.cancelAndSubmit at the chat-panel handler call site; the SEC-06 invariant (presence enrichment lives outside the wrapper) is enforced by AgentOrchestrator.runTurn and proven by AgentOrchestratorPresenceEnrichmentTests. TTSEngine* and runTurn (private) remain forbidden tripwires. Tests: - CodableRoundTripTests adds round-trips for chatSubmit / chatCancelAndSubmit / submitRejected; webview round-trip.test.ts mirrors. - 54/54 Bus tests pass; 37/37 TS bus tests pass; check-bus-protocol-version reports parity at v2.3.0. |
||
|
|
87aec282a0 |
feat(09-02): wire FrameAttachController + Bus frameAttachRequested + broadcaster release subscriber (D-15, D-16, INT-07-04)
- App/Vision/AppDelegateFrameAttachAdapters.swift (NEW): FrameAttachCaptureSourceAdapter wraps CameraCapture, FrameAttachReplaySinkAdapter wraps Replay.ReplayLog (placeholders log via JarvisLogChannel.replay — see file docstring for FK rationale), FrameAttachControllerReplaySinkBridge bridges Vision's FrameAttachReplaySink actor to FrameAttachController.ReplaySink protocol - AppDelegate: new frameAttachController + frameAttachReleaseTask properties; installVision now constructs FrameAttachController (D-15); installAgent waits for visionInstallTask before constructing the orchestrator and passes visionRouter:; new step 5d frame-attach release subscriber subscribes broadcaster's .frameAttach priority and fires onAssistantTurnComplete() on .turnEnd for image-bearing turns (D-16) - AppDelegate: tryPhraseAttachIfMatch(_:) helper exposed for Plan 4's chatSubmit + chatCancelAndSubmit handlers (WARNING-4: must call from BOTH paths) - AppDelegate.installBus: bridge.onInbound now switches on BusInbound; .frameAttachRequested → frameAttachController.requestAttach(reason: .hudButton) - BusInbound: additive .frameAttachRequested case + Discriminator + Codable arms (NO default — exhaustive-switch drift preventer); BUS_PROTOCOL_VERSION 2.1.0 → 2.2.0 in Swift + TS mirror - TS protocol: matching frameAttachRequested case + decoder arm; round-trip.test.ts asserts version 2.2.0 + decodes new fixture - New fixtures (parity): packages/Bus/Tests/BusTests/Fixtures/frameAttachRequested.json + webview/packages/bus/fixtures/frameAttachRequested.json (byte-identical, no trailing newline per check-bus-protocol-version.sh) - Memory package bumped from .v13 to .v14 (cascade: Memory depends on AgentCore which now requires v14 due to JarvisVision dependency added in Task 1) - xcodegen regen for the new App/Vision/AppDelegateFrameAttachAdapters.swift source file - 2 new FrameAttachReleaseSubscriberTests prove broadcaster→onAssistantTurnComplete glue (image-bearing turns release once; text-only turns NEVER release); subscription uses devOverlay priority for the sync wait to avoid competing with broadcaster's drain on orch.events - 1 new CodableRoundTripTests entry (test_roundTrip_frameAttachRequested) - Phase 7 SOLE-emission-site invariant intact: FrameAttachDiscardSiteGrepTests passes; check-presence-vision-isolation.sh exits 0; orchestrator-events single-consumer grep gate exits 0; 65 AgentOrchestratorTests + 51 BusTests + 34 webview tests all green - App target compiles cleanly (check-app-builds.sh exits 0) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
52457f0159 |
feat(07-03): in-process MCP tools + BusOutbound.sessionHistory + version bump
- InProcessTool protocol + InProcessToolRegistry actor (PATTERNS 2.18A): bypasses stdio framing for memory tools - SearchMemoryTool (D-07 all-sessions, requiresConfirmation=false) - SearchConversationTool (TEXT-03 session-scoped, requiresConfirmation=false) - ForgetFactTool (D-02 destructive, requiresConfirmation=true) - BusOutbound.sessionHistory(turns: [TurnRow]) additive case + hand-written Codable arms; Bus.TurnRow local mirror (no Bus->Memory dependency edge introduced) - BUS_PROTOCOL_VERSION 2.0.0 -> 2.1.0 (MINOR: additive case); TS-side catch-up deferred to Plan 07-06 per plan body — check-bus-protocol- version.sh xcodebuild pre-script will fail until then (expected) Bus 50 tests, MCP 85 tests, Memory 67/9 skipped, Replay 27 — all green. .memoryRetrieval grep gate stays at 1 production hit; .memoryMutation grep gate stays at 1; no new chat-panel components. |
||
|
|
a5107218e8 |
test(07-03): add failing in-process MCP tools + Bus session-history tests
- InProcessMemoryToolsTests: dispatch coverage for search_memory,
search_conversation, forget_fact through InProcessToolRegistry; stub
dispatchers; requiresConfirmation gate (D-02 forget_fact only);
unknown-tool error
- SessionHistoryOutboundTests: BusOutbound.sessionHistory round-trip +
shape ("type":"sessionHistory") + version constant present
RED: tests reference InProcessTool, InProcessToolRegistry,
SearchMemoryTool, SearchConversationTool, ForgetFactTool, plus
BusOutbound.sessionHistory + Bus.TurnRow — none exist yet.
|
||
|
|
daae27e720 |
feat(05-05): App-level MCPRuntimeWiring + ME-04 channel + SEC-07 observer
Closes ME-04 (Plan 04-05 deferred state). AppDelegate.applicationWill FinishLaunching now instantiates BoundedAsyncChannel<ReplayEvent>( capacity: 2048, policy: .dropOldest) for the orch→replay seam — the production instance that Plan 04-05's ChannelTopologyTests CT2 only verified as a primitive. - App/MCP/MCPRuntimeWiring.swift: dependency-graph constructor for the full chain (MCPClient → ToolRegistry → MCPToolDispatcher → ConfirmingToolDispatcher) plus a compose() test seam. The ConfirmationPresenterHolder breaks the broker↔presenter cyclic init. - App/MCP/ReplayingToolResultObserver.swift: SEC-07 observer that records pre-/post-sanitize bytes via two ReplayEvent.toolResultFull rows (one canonical, one ':raw'-suffixed toolUseId). Uses an injected turnIDResolver closure since orchestrator wiring lands in a later plan. - AppDelegate stores the orchToReplayChannel + drain Task and tears them down on applicationWillTerminate. - App/Tests/AppTests/MCPRuntimeWiringTests.swift: 3 structural tests (compose-returns-ConfirmingToolDispatcher; channel-capacity-+-policy; MCPRuntime-struct-addressable). No xcodebuild test launch — known Xcode 26 RunningBoard blocker per .planning/debug/. Deviations: - Replay / AgentCore / MCP / Bus Package.swifts had implicit transitive swift-log deps that SPM tolerated but Xcode framework-linker rejects once the App pulls them in as separate frameworks. Added explicit swift-log deps to all four (Rule 3, blocking). - HudStateCoordinatorBusWiringTests qualified two HudState references to Jarvis.HudState — the new framework deps disturbed the App vs Bus symbol disambiguation (Rule 1, surgical). - The plan's plain ReplayEvent.toolResultFull(toolUseId:rawBytes: sanitizedBytes:) variant doesn't exist (Phase 4 schema closed); the observer writes two rows with a ':raw' toolUseId suffix instead. xcodebuild build (Debug) succeeds with the new MCP deps wired into the App target. Test build (build-for-testing) also succeeds; runtime test execution is the documented Xcode 26 RunningBoard blocker per 03-05-PLAN. |
||
|
|
61ba047068 |
chore: merge executor worktree (worktree-agent-af785327 / plan 02-04)
# Conflicts: # project.yml |
||
|
|
a63d521d38 |
feat(02-04): add bus parity + HUD-04 lint + harness-parity pre-build scripts
Three build-breaking check scripts + two smoke test harnesses + two fixture dirs: - scripts/check-bus-protocol-version.sh: greps Swift vs TS BUS_PROTOCOL_VERSION constants, diffs the two fixture directories (packages/Bus/Tests/BusTests/ Fixtures/ vs webview/packages/bus/fixtures/). Fails build on any drift. - scripts/check-no-evaluate-javascript.sh: scans App/ + packages/ for `.evaluateJavaScript(` outside test dirs; HUD-04 architectural lint. - scripts/check-bus-harness-parity.sh: byte-identical check between canonical webview/bus-harness.html and bundle copy (Plan 02-03 provides the bundle copy; this script enforces no-drift going forward). - scripts/test-check-bus-protocol-version.sh + bus-mismatch/ fixture: drives the protocol-version checker against a v2.0.0/v1.9.0 mismatch, asserts exit 1 + both versions in error text. - scripts/test-check-no-evaluate-javascript.sh + bus-evaluate-js/ fixture: drives HUD-04 lint against bad.swift (evaluateJavaScript) + good.swift (callAsyncJavaScript). Asserts bad.swift flagged, good.swift not flagged. Fixture is copied to a temp dir to bypass the production */test-fixtures/* path-skip rule. [Rule 3 - Blocking] Normalized Swift-side fixtures to drop trailing newlines. The 14 JSON files under packages/Bus/Tests/BusTests/Fixtures/ had stray trailing \n while the TS mirror at webview/packages/bus/fixtures/ did not. Plan 02-02's SUMMARY documented the no-trailing-newline convention (matches Swift JSONEncoder default) but Plan 02-01's fixtures were authored via a text editor that added them. Stripping \n lets check-bus-protocol-version.sh pass without relaxing the byte-equality invariant. Swift CodableRoundTripTests remain green (24/24) because JSONSerialization ignores trailing whitespace. |
||
|
|
de59a2d211 |
feat(02-03): add OutboundBatcher + fill in WebviewBridge outbound path
- OutboundBatcher: actor-based 33ms coalescer (latest-wins audio, concat tokens, flush-then-send for state transitions). Task.sleep-based window per RESEARCH §HUD-06. Order preserved on flushAndSend. - WebviewBridge.sendRaw(_:): real callAsyncJavaScript call with primitive string payload argument (HUD-04 / T-02-13 — never concat-into-body). - WebviewBridge.send(_:): gates on handshakeState == .armed, else throws BusError.bridgeNotReady; delegates to sendRaw. - WebviewBridge.startHandshake(): now actually sends the hello frame via sendRaw (bypasses the armed gate — hello is what STARTS the handshake). - WKUserScript at .atDocumentStart in JarvisBusWorld installs window.jarvisBus stub before any page script runs (pitfall 3). - JSEvaluator protocol seam: WebviewBridge holds an evaluator, production uses WKWebViewJSEvaluator (WKWebView wrapper); tests use a recording fake so the outbound suite doesn't need a real WebKit process. - WebviewBridge: OutboundBatcher.Sink conformance — sendRaw already matches the Sink signature. Tests: 11 new (5 Batcher + 6 Outbound), 47 total all green. Invariants: 0 .evaluateJavaScript( call sites in packages/Bus/Sources/; 1 webView.callAsyncJavaScript call site (in WKWebViewJSEvaluator). |
||
|
|
f7f87d8dba |
test(02-03): add failing OutboundBatcher + outbound WebviewBridge tests
RED phase — these tests reference APIs that do not yet exist:
- OutboundBatcher actor (postAudio, postToken, flushAndSend)
- WebviewBridge.JSEvaluator protocol + internal test-only init
- WebviewBridge WKUserScript injection resource
Adds packages/Bus/Sources/Bus/Resources/Injection.js + declares
resources: [.process("Resources")] on the Bus target so the bundle
contains the document-start stub that GREEN will load.
|
||
|
|
a555093865 |
feat(02-01): add WebviewBridge + Handshake state machine
- `Handshake.swift` — `HandshakeState` enum (`.idle` → `.sentHello` → `.armed` / `.mismatched` / `.timedOut`) + `HandshakeTiming.timeout = .seconds(2)`. - `WebviewBridge.swift` — `@MainActor final class` conforming to `WKScriptMessageHandlerWithReply` with `MainActor.assumeIsolated` to read `WKScriptMessage.body` under Swift 6 strict concurrency (Apple DevForums 751086). Installs its handler in `WKContentWorld.world(name: "JarvisBusWorld")` so page scripts cannot observe or post to the bus. - Inline handshake handling: `helloAck` matching `BUS_PROTOCOL_VERSION` arms the bridge; mismatch or 2s timeout flips to terminal state and invokes an injectable `alertPresenter` (NSAlert-free under test). `onInbound` is never called for `helloAck`. - Internal `handleInboundString(_:replyHandler:)` seam lets tests exercise the decode path without synthesizing `WKScriptMessage` (not constructible from user code). - `send(_:)` is a stub that serializes via `BusCoder` but raises `BusError.bridgeNotReady` until armed. Plan 03 swaps the stub body for a real `callAsyncJavaScript` wiring. - 5 HandshakeTests + 7 WebviewBridgeTests cover: match/mismatch/timeout/ cancel-on-arm transitions, decode-success dispatch, decode-failure reply, non-string body reply, onInbound-throws reply, nil-return implicit success, handler registration. All green (36 total Bus tests pass). |
||
|
|
cf61b6bac8 |
feat(02-01): add Bus package with discriminated-union Codable + fixtures
- New `packages/Bus` SPM package (Swift 6 strict concurrency, WebKit linked).
- `BUS_PROTOCOL_VERSION = "2.0.0"` + shared `HudState` / `TurnTerminator` /
`BusCoder` in `Protocol.swift`.
- `BusOutbound` (8 cases) + `BusInbound` (2 cases) with hand-written
`Codable` using a `type` discriminator and exhaustive switches — SE-0295
synthesized `{"case": {...}}` shape is rejected by design.
- `BusReply` plist-serializable reply + `BusError` error surface.
- 14 JSON fixtures + 24 XCTest round-trip tests asserting byte-equal
re-encoding, shape invariants, UUID-lowercasing, and negative-path decode
failures. All green.
- `JarvisLogChannel` gains `case bus` (OBS-06 extension per RESEARCH-07);
`LoggingTests.test_fiveChannelsAreDefined` updated to match.
|