Commit Graph
10 Commits
Author SHA1 Message Date
james.maesandClaude Opus 4.7 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>
2026-05-14 09:51:03 -05:00
james.maes 08095281fc fix(deps): close vite + esbuild CVEs via pnpm.overrides + vitest@3 bump (#159)
Closes Dependabot alerts #1 (esbuild dev-server response read) and #2 (Vite path traversal). Adds pnpm.overrides pinning vite >= 8.0.5 (drops esbuild transitive) + bumps vitest 2.1 → 3 in root + bus workspaces (vitest 2 incompat with vite 8 SSR transform). 37/37 bus + 101/101 hud tests pass; both workspaces build cleanly. Deferred to v0.2: vitest 4 major + oxc plugin migration.
2026-05-13 06:41:52 -05:00
james.maes 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.
2026-05-01 22:21:14 -05:00
james.maesandClaude Opus 4.7 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>
2026-05-01 19:41:29 -05:00
james.maes dfc237db0c feat(07-06): installMemory + installVision + TS bus protocol 2.1.0 (Task 1)
- AppDelegate gains installMemory() and installVision() mirroring installVoice's
  six-step pattern. Memory installs BEFORE voice (memory ready before first
  turn); vision AFTER voice (camera lifecycle independent).
- MemoryStore + MemoryExtractionOrchestrator + MemoryExtractionCoordinator
  constructed; AppDelegateMemoryReplaySink bridges Memory.MemoryReplaySink ->
  Replay.ReplayLog.record(_:for:). Coordinator wiring deferred until
  AgentOrchestrator lands in AppDelegate (documented in SUMMARY).
- CameraCapture + PresenceMonitor instantiated; the SINGLE PresenceSignalBus
  reference (owned by PresenceMonitor) flows via AppDelegateContextBuilderAdapter
  to ContextBuilder.installPresence and via hudStateCoordinator?.attachPresence
  to the HUD coordinator. VISION-03 boundary preserved.
- VisionRouter constructed with T1=Ollama, T2=Ollama (sidecar deferred), T3=
  Anthropic (key from Keychain); the runTurn dispatcher branch is deferred
  until AgentOrchestrator wires up.
- DisablePresence menu-bar toggle wired alongside MuteWakeWord.
- BannerContent.cameraDenied + cameraRevoked presets; cameraDegradationTask
  watches CameraCapture.degradationStream and enqueues banners.
- HudStateCoordinator.attachPresence is a generic AsyncSequence subscriber
  preserving HUD-08 single-writer invariant (presence is record-only).
- ContextBuilder.installPresence(_:) is a static no-op drain (deferred wiring
  for the future per-turn system-prompt enricher).
- TS BUS_PROTOCOL_VERSION 2.0.0 -> 2.1.0; sessionHistory + TurnRow added to
  the BusOutbound discriminator union with full decode arm. Round-trip test
  bumped accordingly. scripts/check-bus-protocol-version.sh now passes.
2026-04-29 18:40:47 -05:00
james.maes a0d82711da fix(03-review): H-02 attach to existing window.jarvisBus from JarvisBusWorld
Option B (minimal edit): `installJarvisBus()` now detects a pre-existing
`window.jarvisBus` with the expected send/onOutbound/receive shape and
returns verbatim rather than overwriting it.

Root cause: Swift's WebviewBridge installs `Injection.js` as a WKUserScript
`in: JarvisBusWorld` and registers `webkit.messageHandlers.jarvisBus`
in the same content world. WebKit isolates message handlers per world,
so the default-world HUD bundle (loaded via `<script type="module">`)
can NOT reach them. `window.jarvisBus` IS visible cross-world because
WebKit keeps `window` as one shared object — only variable bindings are
world-scoped. Phase 3's `main.tsx` was therefore installing a fresh bus
whose `send()` path throws on every outbound (including the first
`uiReady`). The bus-harness.html marker script in Phase 2 never called
`send()`, which is why it wasn't observed earlier.

Fix ensures the HUD dispatcher's outbound path routes through the
Injection.js bus's `send()` (which captured `webkit.messageHandlers`
at document-start inside JarvisBusWorld). The fresh-install branch is
preserved unchanged for the bus-harness.html path (no pre-existing
bus present in Phase 2 tests).

Tests:
- 5 new H-02 cases in bridge.test.ts exercise the attach-to-existing
  path (identity preserved, send/onOutbound routed through the
  pre-existing bus, fall-through when no pre-existing bus, fall-through
  for non-bus-shaped leftovers).
- Existing 28 bus tests + 80 hud tests remain green.

Also adds a doc comment to main.tsx explaining the pattern so future
readers don't re-litigate the world-isolation model.
2026-04-24 16:48:14 -05:00
james.maes c21df441fd feat(02-02): implement window.jarvisBus glue + bus-harness.html
- installJarvisBus() wires receive/send/onOutbound around decode pipeline
- receive() routes to handler if set; auto-acks hello when unset so the
  minimal harness completes the handshake without page-side code
- send() is the ONLY surface that calls webkit.messageHandlers.jarvisBus
- Missing-webkit surfaces a helpful Error instead of a TypeError
- bus-harness.html is the static stub Plan 03's loadFileURL targets;
  imports compiled ./packages/bus/dist/index.js, calls installJarvisBus
- index.ts now re-exports protocol + bridge
- 7 bridge tests green; 28 total vitest assertions; tsc --strict clean
2026-04-23 17:20:58 -05:00
james.maes 2d3387a1d8 test(02-02): add failing bridge.test.ts against stub installJarvisBus
- Stub bridge.ts exports typed installJarvisBus skeleton
- 7 vitest assertions cover: window.jarvisBus install, outbound dispatch,
  hello auto-ack, decode-error callback, send posts JSON to webkit,
  missing-webkit helpful error, onOutbound latest-wins
- All 7 fail against the stub (RED)
2026-04-23 17:18:47 -05:00
james.maes 4e68fa21d0 feat(02-02): implement protocol decode/encode with never-sentinel exhaustiveness
- BUS_PROTOCOL_VERSION = "2.0.0" (byte-identical to Swift mirror)
- decodeOutbound/decodeInbound return DecodeResult<T>, never throw
- `const _exhaustive: never = type;` (no `as never` cast) in each default
  branch — adding a BusOutbound variant without a case now fails tsc --strict
  with "Type '...' is not assignable to type 'never'" (verified empirically)
- Isolated type-guard arrays HUD_STATES / TURN_TERMINATORS with
  `satisfies readonly HudState[]` catch drift at compile time
- 21 round-trip + error-path vitest assertions pass
2026-04-23 17:17:11 -05:00
james.maes 6ce4754142 test(02-02): add failing round-trip suite + pnpm workspace scaffold
- Scaffold webview/ pnpm workspace (pnpm 10.30.2, TS 5.9, Vitest 2.1)
- @jarvis/bus package with strict tsconfig inheritance
- 14 fixture JSONs byte-identical to Plan 01 Swift mirror (no trailing NL)
- RED: round-trip.test.ts has 19 failing assertions against stub decoders
2026-04-23 17:12:38 -05:00