f6637ec39a052d26199dacb70eff239ffbc7cfd3
561
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f6637ec39a |
ci(actions): fix typecheck/build order + share webview artifact across jobs (#161)
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. |
||
|
|
a8c7f1a09b |
chore(deps): batch Dependabot bumps — fiber + plugin-react + jsdom + vite (#144, #145, #147, #148, #160)
Lands four SAFE-MERGE Dependabot bumps in one commit. Each was confirmed SAFE-MERGE by the H' triage on PR #155, then re-verified locally on the new lockfile baseline (vitest@3 + vite@8 + esbuild dropped) from PR #159. - @react-three/fiber 9.6.0 → 9.6.1 (closes #148) - @vitejs/plugin-react 5.x → 6.0.1 (closes #147) - jsdom 25.x → 29.1.1 (closes #144) - vite pinned to >=8.0.12 via pnpm.overrides (closes #145) 37/37 bus + 101/101 hud tests; both workspaces build; production webview bundle stamps cleanly. PR #146 (TypeScript 6) NOT included; deferred for focused evaluation. |
||
|
|
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. |
||
|
|
795c3bf5c9 |
feat(app): v0.1 UX surfaces — startup panels + InputMonitoring banner + PTT (#87, #88, #89, #156)
#89: new AppDelegate.startupSurfacesMode enum (.minimal/.diagnostic). Debug defaults .diagnostic; Release defaults .minimal. openStartupSurfacesIfDiagnostic() runs at tail of bootHealthTask (skipped under XCTest or when wizard is open). Status panel top-right + Dev Overlay bottom-right of NSScreen.main.visibleFrame. DevOverlay opens to Logs tab. #88: BannerContent.inputMonitoringDenied is now nonDismissible: true. New HUDBannerCoordinator.dismiss(id:) clears a banner programmatically without writing dismissedThisLaunch. AppDelegate.reprobeInputMonitoring fires on NSApplication.didBecomeActiveNotification; rebinds the global hotkey on re-grant. Uses IOHIDCheckAccess (query-only, no prompt) per CLAUDE.md guidance. 3 new coordinator tests. #87: WizardState.pttHotkey persisted under Jarvis.pttHotkey (independent of summon hotkey). Ships unbound per CLAUDE.md hotkey hygiene. AppDelegate.rebindPTTFromState() called from installVoice, foreground TCC re-grant, Settings Save/Clear. SettingsView extended with PTT hotkey section. Audio-graph isolation: NSEvent monitors only, no audio-graph surgery — safe alongside fix/voice-crit-v0.1. 3 new WizardState tests. DevOverlay 14 + Voice 26 tests pass; all 17 grep boundary gates green (incl. single-writer-hudstate + no-modal-presentation). |
||
|
|
bc74494ada |
fix(mcp): v0.1 CRIT cluster — turnID resolver wired + tool-result double-cap (#20, #21, #157)
#21 (CRIT): ToolResultPacker.pack(_:) is now a pass-through decoder; redundant inner 8 KB cap removed. MCP dispatcher's SanitizeForModel.prepareForBoundary(_, capBytes: 8192) is the sole cap site. Doc-comment rewritten honestly: replay row reflects boundary-prepared bytes (8 KB), not original helper output. Replay fidelity beyond 8 KB lifted to v0.5/M-3 once SanitizeForModel can move to a shared module without dep cycle. #20 (CRIT): ReplayingToolResultObserver.turnIDResolver lock-protected via OSAllocatedUnfairLock<ResolverBox> (same Swift-6-async-safe pattern as InProcessConfirmationCache). New attach(turnIDResolver:) method. AgentOrchestrator.currentTurnID() actor-isolated accessor. installAgent attaches the resolver immediately after orchestrator assignment, with defensive systemLogger.warning if observer type mismatches. Tests: ToolResultPackerTests TP2/TP5 updated to assert no [TRUNCATED] marker; OrchestratorSubmitTests OS6 reframed + new OS6b reads SQLite events table directly to assert 8 KB exact; MCPRuntimeWiringTests adds attach pre/post-attach + replay-log dual-write correlation. AgentCore 243 + Memory 98 + Replay 34 + MCP all green; all 18 boundary gates green. |
||
|
|
4b1dd6404b |
fix(voice): v0.1 CRIT cluster — wake / STT / TTS / boot probes (#28, #29, #30, #31, #32, #33, #34, #36, #158)
#28 (CRIT): setCancelInFlight calls stopFeed() (preserves stream); rebuild consumer re-arms DAG against new ring. Wake-word no longer dies on audio-graph rebuild. #29 (CRIT): sttFactory reads PerTurnSnapshot.stt.whisperKitFallback; get_self_state returns canonical snake_case backend constants. Factory signature: () -> any STTProvider → () async -> any STTProvider (existing test sites compile unchanged under Swift 6 implicit-async-closure rules). #30 (CRIT): TTSEngineActor.synthesize emits .ttsStopped after .finished on natural completion (two-emit-site model: barge-in + natural-completion). #31 (CRIT): VoiceTTSAdapter.tierResolver wired to read snap.tts.tier from configStore. #32 (HIGH): Rolling-window trim in OpenWakeWordSession.runORT — mel ≤ 84, embeddings ≤ 16, nextEmbStart slides on drop. #33 (HIGH): three new boot probes — WakeWordBootHealthProbe, STTBootHealthProbe, TTSBootHealthProbe. New WakeWordDAG.isFeedArmed + VoiceTTSAdapter.hasEngine accessors. #34 (HIGH): vad.reset() at top of startSTTSession clears Silero VAD LSTM hidden state + wasSpeech between sessions. #36 (HIGH): new Broadcaster.Priority.agentHud (protects stateChange/turnEnd/error); installAgent adds subscriber pumping AgentHudIntent into dormantAgentContinuation. Voice 90 + AgentCore 242 tests pass; all 18 boundary gates green. |
||
|
|
f59f5d12e3 |
fix(memory): v0.1 CRIT cluster — facts_vec / replay correlation / FTS5 sanitize / search_memory preamble (#12, #13, #14, #15, #16, #153)
#14 (CRIT): ContextBuilder.selfAwarePreamble names search_memory + forget_fact with explicit cue 'call search_memory FIRST. Never claim to remember without searching.' #16 (HIGH): MemoryQueries.sanitizeFTS5Query strips non-alphanumeric, quotes tokens; HybridSearch.searchFacts calls it before binding to MATCH ?. Embedding still runs on raw query. #12 (CRIT): MemoryStore.init accepts optional embedder; insertNewFact writes facts_vec after the facts commit. Vec failures log + leave FTS row (no rollback). installMemory shares one embedder between writer + reader. #15 (HIGH): drop stableHash; pass sentinel 0 as sourceTurnId (option 1 per audit — smaller change surface than threading real rowid through 5 files). Schema comment updated. #13 (CRIT): two-layer fix. Layer 1: AppDelegate owns a per-process sentinel TurnID; installAgent writes the matching turns row after beginSession; AppDelegateMemoryReplaySink attributes events to it. Layer 2: ReplayLog.flushPending retries failed batches event-by-event so one poisoned row only drops itself. Memory 98 + Replay 34 + AgentCore 243 tests pass; all 18 boundary gates green. |
||
|
|
05411f82ff |
fix(vision): v0.1 cluster — T2 honesty + pendingFrame leak + TCC revoke observation (#1, #2, #3, #152)
#1 (HIGH): new hasRealT2Provider init param on AgentOrchestrator; t2AvailableForThisTurn now requires both decision.tier != .t3Cloud AND hasRealT2Provider. Production passes false (MissingT2Provider only). Pre-existing escalation test still passes true with mock T2. #2 (MED): new FrameAttachController.releaseAfterRejectedSubmit() routes through the sole discardFrame() emission site (D-15 single-emission grep gate still green). Both handleChatSubmit and handleChatCancelAndSubmit call it on .rejected. #3 (MED): AVCaptureSessionWasInterruptedNotification observer yields .midSessionRevoked. macOS limitation surfaced: AVCaptureSessionInterruptionReasonKey is API_UNAVAILABLE(macos), so we cannot discriminate by reason — any interruption surfaces the banner. NSApplication.didBecomeActive observer calls becameAuthorized() on non-authorized → .authorized flip. Vision 75 + AgentCore 243 tests pass; all 18 boundary gates green. |
||
|
|
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. |
||
|
|
b643159735 |
chore: PR + dependency triage report — 2026-05-12 (#155)
Triage report for the 5 Dependabot PRs (4 SAFE-MERGE / 1 NEEDS-INVESTIGATION), the 2 moderate CVEs (vite + esbuild path traversal — root cause is vitest@2 → vite@5.4.21 → esbuild@0.21.5 transitive chain; existing PR #145 alone does NOT close them; surgical fix is pnpm.overrides), and the 6 stashes (5 DROP + 1 PRESERVE-AS-BRANCH for Voice Log v1.1 + 1 POP-TO-LANE-G). Methodology: each Dependabot PR was tested in an isolated tmp worktree; no main repo HEAD mutations. |
||
|
|
223310aa27 |
ci(actions): full pipeline — build / test / scan / publish (#154)
Adds five GitHub Actions workflows + CODEOWNERS + BUILDING.md CI section. - ci.yml: boundary gates (Ubuntu), webview pnpm test+build (Ubuntu), 14-package SPM matrix on macos-15, app target via xcodebuild, optional self-hosted real-hardware lane, semver tag-publish on push to develop/rc/release/main. - codeql.yml: Swift + JS/TS with security-extended queries, weekly cron. - semgrep.yml: p/default + p/swift + p/typescript + p/secrets, SARIF upload, optional SEMGREP_APP_TOKEN. - dependency-review.yml: blocks severity:high on PRs touching manifests/lockfiles. - release.yml: tag-triggered GitHub Release with auto-generated changelog. Branch + tag conventions: feature/fix branches run checks only; develop publishes v0.X.Y-dev.<run_number>; rc/release publishes v0.X.Y-rc.N; main publishes v0.X.Y. Conventional-Commits-aware bumping (feat → minor, fix → patch, feat!/BREAKING → major). Seed version v0.1.0. |
||
|
|
5bb1e778b5 |
fix(hud): chat panel auto-scroll on streaming append — B-06 tactical (#51, #150)
Adds a useLayoutEffect on ChatPanel keyed to total content length (captures streaming token append where chatEvents.length is constant). 24px bottom-pin hysteresis; user scroll-up breaks the pin; auto-re-arms on scroll-back-to-bottom. scrollTo feature-detected with scrollTop fallback for JSDOM. 4 vitest cases assert decision logic. |
||
|
|
ba42b6918a |
docs: BUILDING.md + finalize README MIT license section (#149)
Extracts the long-form build reference (per-package SPM tests, app-target xcodebuild wrapper, webview workflow, 18 boundary-gate linters, real-hardware env-flag tests) into BUILDING.md so the README can stay first-screen weight. Finalizes the README License section against today's MIT decision (
|
||
|
|
71b93898ac |
docs(handoff): end-of-day 2026-05-12 — round 2/3/4 + audits + tracking migration + roadmap + branding
Closes the session that took Jarvis from "wired-but-dead-everywhere" to fully-mapped + branded + license-clear + audit-tracked. Highlights: - Round 2/3/4 strict-mode boot health shipped (probes → status panel → severity model + non-dismissible banners + agent self-awareness preamble + red menu-bar icon). Toby fix verified end-to-end mid-dogfood. - facts.source_turn_id FK dropped ( |
||
|
|
0cc94c637a |
docs(branding): assets/branding/ + restore MIT license section
- Add assets/branding/README.md with shot list for the five high-impact
brand assets (social preview, logo, hero shot, app icon, banner) and
GitHub upload instructions for each.
- Add assets/branding/STYLE.md documenting the colour palette (cyan
family + HUD navy/black + warning/critical), typography (Eurostile /
Saira / Orbitron family for display, SF Pro for UI), and tone of
voice ("terse, technical, dryly opinionated, slightly sardonic").
- Drop five .todo placeholder files so the directory tree shows intent
without faking assets.
- Restore the README License section to a clean MIT pointer (a
concurrent agent had reverted it to "decision still open"; that
decision is now made and lives at .planning/decisions/license.md).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
281b9093e7 |
docs(appdelegate): apply commenting style guide (P0 of #64)
Rewrites App/AppDelegate.swift docstrings + inline comments to the .planning/code-commenting-guide.md standard. Comments + docstrings only — zero code behavior changes. Closes the P0 entry in the migration table (#65 under epic #64). Highlights: - File header: 5-line opener + "See also" + audit-trail suffix. - Class docstring: install-order ASCII DAG, threading model, "Before you edit" block listing the five sharp-edged invariants (single-writer HudState, locked install order, shared in-process tool registry, JIT entitlement, no-modal presentation). - Property docstrings reorganized under role-named MARKs (Test-injectable seams / AppKit surfaces / HUD state coordinator + bus producer continuations / Orch→replay seam + MCP runtime / Boot health / Voice / Memory / Vision / Agent). Every property now leads with WHY; audit references (Plan IDs, B-/D-/CR- numbers, dated audits) are parenthetical suffixes. - Public + key private method docstrings rewritten: applicationWillFinishLaunching gets the 16-step bootstrap + the hard-block branches called out; applicationWillTerminate explains why each cancellation is explicit; runBootHealth + register- BootHealthProbes list every probe + its role; installVoice / installMemory / installAgent / installVision / installSelfKnowledgeTools open with the subsystem's responsibility + fail-soft-vs-hard policy + step sequence. - The motivating bad example from the guide (`inProcessToolRegistry` Plan-ID-lede comment) is rewritten exactly as the guide prescribes. - Inline comments still carrying Plan-ID ledes ("Phase E 2026-05-03 audit fix:", "Track B-4 (...):") rewritten to lead with WHY/WHAT with audit trail demoted to parentheticals at end. Boundary gates: - 18/18 green (full sweep: app-builds, bus-harness-parity, bus-protocol-version, corpus-secrets, embedding-dim-literal, install-order, no-evaluate-javascript, no-leftover-stubs, no-modal-presentation, no-null-voice-adapters, orchestrator-events-single-consumer, presence-bus-no-tts-orchestrator, presence-vision-isolation, single-memory-mutated-emit, single-memory-used-emit, single-writer-hudstate, vision-isolation, applescript-confirmation). - check-install-order verified literal Task spawn lines + the `await self?.agentInstallTask?.value` invariant are intact. - check-single-writer-hudstate verified AppDelegate still only references `.hudState(_:)` inside the allowlisted wiring closure. Surfaced during review: - None — only comments touched. The audit-2026-05-04 P3-16 split (AppDelegate+Voice/+Vision/+Memory/+Agent) remains outstanding but is out of scope for this P0. Line delta: +965 / -740, net +225. Net growth is from the install- order DAG ASCII art + the "Before you edit" block + expanded six-step method docstrings. Refs: #64, closes #65. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
b2710eee48 |
docs(readme): branding pass — badges, MIT license, acknowledgments
- Add status badges (license, last commit, issues, platform, Swift) under
the tagline so the GitHub card carries signal at a glance.
- Replace the "personal use only / no license granted" footer (which
contradicted the actual MIT LICENSE file committed at
|
||
|
|
e1fcdc520f |
docs(license): MIT decision + rationale
Confirms MIT (already at
|
||
|
|
c4c32f7aae |
docs(readme): refresh — diagrams + architecture + culture + roadmap
Audit-and-rewrite of root README against current truth (2026-05-12). The previous README was accurate but list-shaped and unillustrated; the rewrite reorients around what a new reader (human or AI agent) needs in 60 seconds. New / changed sections: - Tagline + two-paragraph intro (what / who / why) - Status banner pointing at GitHub Issues + Milestones (canonical) - Screenshots placeholder + shot list under assets/screenshots/ - "Architecture at a glance" with a top-level mermaid flowchart (Swift host + LLM providers + WKWebView, with 14 nodes) - Subsystems table — eight first-class + supporting packages - "Install order" section with a mermaid DAG of the AppDelegate install sequence (vision → agent → voice, plus parallel memory/MCP/replay), matching check-install-order.sh - "Agent turn lifecycle" mermaid showing the orchestrator → single broadcaster fanout, with the strict-mode degradation preamble inject point called out - "Boot-health probes" mermaid for the eight NOT FAKED probes → rollup → menu-bar icon / banner / agent preamble - "Project culture" section codifying the in-code-docs philosophy, NOT FAKED probes, strict-mode escalation, 18 boundary gates, the three-name test-double taxonomy, audit-and-stabilize cadence, and the GitHub-Issues-is-canonical policy - "Roadmap" reorganized around the v0.1 → v1.1 milestones from GitHub Milestones (user-outcome-focused, not subsystem-focused) - Build & run cleaned up; boundary-gates one-liner; opt-in real-hardware test commands kept Also adds assets/screenshots/README.md with a prioritized shot list — eight screenshots in priority order — and a note on why diagrams stay as inline mermaid (GitHub-native, no re-export). Removed: the older "Status" subsystem-state table, which had become a mix of v0.12.0 milestone audit terms (Track A/B/C/D, B-8) that don't map cleanly to the new GitHub Issues taxonomy. Status now points at GitHub Issues + Milestones as the source of truth. Gates: 17/18 pass. check-app-builds.sh fails on the in-flight AppDelegate.swift rewrite (unstaged, owned by a parallel agent) — unrelated to this markdown-only change. Confirmed only README.md + assets/screenshots/README.md are staged here. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
4e6911472d |
docs(changelog): add CHANGELOG.md with milestone history
Keep a Changelog format. [Unreleased] tracks the May 2026 audit-and- stabilize work (boot-health probes, status panel, Dev Overlay Logs tab, memory FK fix). Milestone history groups v0.10.0 → v0.12.0 by phase rather than commit; per-commit detail stays in git log. |
||
|
|
e0c5bcba69 |
docs(github): issue + PR templates + dependabot config
Four issue templates (bug / enhancement / audit-finding / epic) pre-fill existing labels (severity:*, area:*, audit-finding, epic). PR template references the 18 boundary gates + comment style guide. Dependabot covers swift, npm (/webview), and github-actions on a weekly cadence. |
||
|
|
9b62db5c33 |
docs(github): add LICENSE + SECURITY.md
MIT license (permissive, attribution required); SECURITY.md documents the personal-use threat model and private reporting channels (email + GitHub Security Advisories). |
||
|
|
61e06fb2dc |
docs(roadmap): user-outcome-focused milestone roadmap
All 142 open issues triaged into 6 GitHub milestones organized by user-facing capability rather than technical area: - v0.1 — It actually works (28 issues): voice, listening, seeing, facts, debugging dialogs, basic UI. Tactical patches for CRITICAL audit findings so user has working Jarvis NOW. - v0.2 — Observable + stable (23): MED/LOW audit punch-list + PTT/TCC. - v0.5 — API M-0..M-3 (31): Self/Settings/Diagnostics surfaces; B-08. - v0.8 — API M-4..M-7 (36): Memory/Vision/Voice/Turn; B-03/B-04/B-05/B-06. - v1.0 — Shipping (13): commenting, doc reconciliation. - v1.1 — Backlog (11): Orpheus, WhisperKit, Voice Log, presence. ROADMAP-2026-05-12.md supersedes the build-phase ROADMAP.md from 2026-04-22 (kept for history; describes shipped phases). CLAUDE.md gains a Roadmap section pointing to the new doc + milestones URL. Also lands the 5 audit-2026-05-12 reports referenced by the roadmap (bus-hud, mcp-agent, memory, vision, voice). |
||
|
|
6ea2852af1 |
feat(devoverlay,logging): live log stream — every swift-log line into the overlay
Slice 3 of dev-overlay-end-to-end. Adds a fourth DevOverlay tab that
tails every log record from every Jarvis subsystem in real time —
agent, voice, vision, memory, MCP, bus, replay, system — without
polling files or relying on Console.app.
How it works:
1. `LogBroadcaster` (new) — process-wide actor with a 500-line ring
buffer + subscriber set. Borrowed shape from
OrchestratorEventBroadcaster so the project's three broadcasters
share a mental model. New subscribers replay the buffer in order,
then go live.
2. `BroadcastLogHandler` (new) — swift-log LogHandler conformance.
Formats each log call into a `LogLine` and schedules an unstructured
`Task { await LogBroadcaster.shared.publish(...) }` so the logging
caller never blocks on actor isolation. Synchronous handler contract
preserved; ordering is best-effort under heavy load.
3. `LoggingBootstrap.make(label:)` now returns
`MultiplexLogHandler([file, os, broadcast])` — the new handler is
purely additive. File rotation + os.Logger streaming unchanged.
4. `DevOverlayLogsView` (new) — fourth tab in the overlay. Subscribes
to `LogBroadcaster.shared` on appear, caps at 1000 lines, renders
monospaced. Per-channel filter (Menu w/ checkboxes), per-level
filter (debug → critical), substring search, Pause/Resume button
(Cmd+P), auto-scroll-to-bottom while not paused. Color-coded by
level (warn=orange, error/critical=red).
Tests:
- 5 new LogBroadcasterTests covering publish/cap/replay/live/handler.
- swift test --package-path packages/Logging → 22/22 pass.
- swift test --package-path packages/DevOverlay → 14/14 pass.
- bash scripts/check-app-builds.sh → PASS.
- 18/18 boundary gates green.
Redaction note: file handler still routes through Redact.apply for
disk writes; the broadcaster sees raw messages because the DevOverlay
is a developer-only in-process surface. If this ever becomes
user-facing, route through Redact before publishing.
|
||
|
|
2a872fc597 |
docs(session-state): point to GitHub Issues; preserve handoff narrative
Adds a note at the top declaring GitHub Issues as the live tracker. Replaces the Pending Work bullet list with a crosswalk to issue numbers. Narrative paragraphs (session-handoff history) preserved unchanged. Refs #4..#63 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
60f179cf39 |
docs(todo): deprecate TODO.md — issues moved to GitHub
All open work has migrated to GitHub Issues. This file is replaced by a
crosswalk from former sections to issue numbers. Historical content is
preserved in git history (see docs/TODO.md at commit
|
||
|
|
4f4a5581c1 |
docs(claude.md): declare GitHub Issues as canonical tracker
Adds a new "Issue tracking" section near the top declaring github.com/KofTwentyTwo/Jarvis/issues as the canonical tracker for all bugs, audit findings, and feature work going forward. docs/TODO.md is retained for historical reference only. Refs #4..#63 (migration epics + audit findings + carry-forward bugs filed 2026-05-12). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
50c2783a8c |
feat(devoverlay): wire DevSnapshotEmitter end-to-end + add tabbed view
The DevOverlay panel has been silently rendering DevSnapshot.initial since Phase 4 because three independent breaks left the data path open-circuit (see .planning/audit-2026-05-12/devoverlay-diagnosis.md for the full trace). Slice 2 closes all three: 1. AppDelegate constructs `DevSnapshotEmitter` in `installAgent` and stores it on the delegate. Provider/model is set from configStore so the header row shows the active provider rather than a blank. 2. The `.devOverlay` broadcaster subscriber that previously drained to `/dev/null` now calls `emitter.apply(event)` for every event. This produces real DevSnapshots that flow through the bounded output channel (capacity 32, .dropOldest — AGENT-10 observational seam). 3. `DevOverlayWindow` now accepts the live emitter + BootHealthOrchestrator + a HudState reader closure. It instantiates its own DevOverlayBridge and attaches the bridge to `emitter.output` so viewModel.apply is actually called as snapshots arrive. Also reshapes the SwiftUI surface as a TabView with three observational panes (Agent, Tools, Health). The Health pane is read-only — the Status… menu remains the place to trigger re-probes. The Logs pane lands in Slice 3 (live log stream). ViewModel extends with `bootHealth` + `hudStateLabel` slices, each with its own single-writer mutator and `private(set)` enforcement. Verification: - swift test --package-path packages/DevOverlay → 14/14 pass - swift test --package-path packages/AgentCore --filter DevSnapshotEmitterTests → 9/9 pass - bash scripts/check-app-builds.sh → PASS - 18/18 boundary gates green |
||
|
|
8d94c1c398 |
docs(devoverlay): diagnose why the panel renders nothing
Three independent breaks identified: 1. DevSnapshotEmitter exists but is never instantiated in App/AppDelegate. 2. The .devOverlay broadcaster subscriber drains every event to /dev/null (AppDelegate.swift:1808-1813 — wired-but-dead per Phase 9 audit pattern). 3. DevOverlayWindow constructs a fresh viewModel(.initial) with no DevOverlayBridge attached, so viewModel.apply(_:) is never called. Slice 1 of the dev-overlay-end-to-end work. Slices 2 (wire it up) and 3 (live log stream) follow. |
||
|
|
a5999e2dde |
fix(memory): drop broken FK on facts.source_turn_id — closes Toby-fact persistence
Root cause of "facts never persist" surfaced during 2026-05-12 dogfood:
ERROR [memory.orchestrator] memory applyOp failed: applyOpFailed(
underlying: Replay.SQLiteError.stepFailed(
code: 19, message: "FOREIGN KEY constraint failed"))
The schema declared:
source_turn_id INTEGER REFERENCES turns(id)
But the writer at MemoryExtractionOrchestrator.swift:125 inserts:
let turnIdInt: Int64 = stableHash(job.turnId.rawValue)
`TurnID` is a UUID-string wrapper; the writer FNV-1a-hashes it to an
Int64 for the integer column. The hash space and `turns.id`
(AUTOINCREMENT) are disjoint number systems by design, so every
INSERT into `facts` violated the FK and SQLite rolled back the row.
The BootHealth probe reports the symptom honestly ("26 turns, 0/0
facts") but couldn't flag this as a failure — 0 facts is the
legitimate empty-history case. The bug stayed invisible until the
user asked "is Toby a fact in the database?" and search returned
nothing despite asynchronous extraction running.
This bug was latent for the entire project history — the original
RESEARCH §1 schema specified the FK, but the writer was never wired
to use real `turns.id` rowids. Reintroducing the FK without first
fixing the writer (forward through MemoryStore.appendTurn returning
the rowid, then plumbed back to MemoryExtractionCoordinator) would
just re-break persistence.
Fix: drop the REFERENCES on `source_turn_id`. The column is now
documented as a "correlation tag", not a hard foreign key. The
`superseded_by REFERENCES facts(id)` constraint stays — that one
IS valid because supersede chains use `priorFact.id` (a real
AUTOINCREMENT rowid).
Existing DBs: `CREATE TABLE IF NOT EXISTS` won't ALTER an existing
table; the user must delete `~/Library/Application Support/Jarvis/
jarvis.db*` and relaunch. Their facts table has 0 rows so nothing
of value is lost. A proper migration framework (PRAGMA user_version)
is deferred to M-1+.
Follow-up (M-1+): plumb the real `turns.id` rowid through
`MemoryStore.appendTurn` → orchestrator state → coordinator →
applyOp so `source_turn_id` becomes a real FK again. Requires
changes across 4 files; not in scope for this hotfix.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
b76327364d |
docs(style): code-commenting style guide — felt orientation in 30s
Research pass produced a 583-line normative style guide capturing
the project's commitment to in-code documentation over external
markdown. The adversarial benchmark: a new developer reading any
one file must understand WHY every non-obvious line exists,
without opening a Plan ledger or a PR description.
Synthesized from:
* Apple's Swift API design guidelines + DocC
* Code Complete / Clean Code / Knuth literate programming
* Reference Swift OSS (swift-nio, swift-log, swift-async-algorithms)
* Survey of existing Jarvis files to characterize current state
Coverage:
1. Five principles (lead with what+why, audit IDs as footnotes,
felt orientation, no comment rot, no fluff).
2. Five comment shapes (file header, type docstring, method
docstring, inline //, MARK: -) with good/bad/rewritten examples.
3. ASCII diagrams for orchestration / state machines / multi-actor
flows — when they pay for themselves and when they don't.
4. DocC cross-references for jump-to-definition from comments.
5. Concurrency / actor / single-writer invariants worth always
calling out.
6. "Before you edit" invariants block — explicit anti-patterns
per load-bearing file.
7. What NOT to comment.
8. Comment-rot prevention.
9. Migration plan — leads with AppDelegate (the install-chain
cliff), then load-bearing actors, then leaf packages
opportunistically. One file = one atomic commit.
Anti-goals (explicit): not building ARCHITECTURE.md, ONBOARDING.md,
or a wiki. The code teaches itself. The guide itself is meta
(documentation about how to comment) and is allowed at
.planning/code-commenting-guide.md as a planning artifact, not
runtime documentation.
Follow-ups (next sessions):
* Add TL;DR section to CLAUDE.md so future agents inherit the
convention automatically.
* Apply the guide to AppDelegate.swift as the first atomic
rewrite commit — proves the convention on the steepest cliff.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
1557302e63 |
fix(build): force ProcessInfoPlistFile before pre-codesign
Symptom: app hard-blocks at boot with "JarvisEntitlementsVerified missing/false" after every incremental build, even though the build log shows pre-codesign writing YES into the bundle Info.plist. Root cause: Xcode 26's new build system schedules the built-in ProcessInfoPlistFile task (which copies App/Info.plist into Contents/Info.plist) independently from postBuildScripts. On incremental builds with no Swift compile work, ProcessInfoPlistFile ran AFTER pre-codesign, codesign, post-codesign, AND verify-codesign-settings — silently clobbering the bundle Info.plist with the source one. Source App/Info.plist carries <key>JarvisEntitlementsVerified</key><false/> by design as the static-state default; pre-codesign is supposed to flip it to <true/> inside the bundle. With the new ordering, the bundle ended every build at <false/> (and the signature was also stale relative to that plist, though codesign --verify happened to still pass). Fix: declare $(TARGET_BUILD_DIR)/$(INFOPLIST_PATH) as an inputFile of the pre-codesign phase, plus a per-build stamp file as its outputFile. This forces Xcode to make ProcessInfoPlistFile a prerequisite of pre-codesign, and chains codesign (also gaining the stamp as inputFile) deterministically behind it. Xcode 26's "mutable output but no other virtual output node" check rejects naming the plist as both input and output of the same phase, so the separate stamp file is required. Verified: clean and incremental builds both produce a bundle with JarvisEntitlementsVerified=true and a valid signature. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
beb9d51aa5 |
test(agent-core): update qwen2.5-coder literal to q8_0 variant
Two test assertions still expected `"qwen2.5-coder:32b"` after commit |
||
|
|
740bed4cbf |
fix(round-4): tag-agnostic ollama probe + mcp probe counts in-process tools
Two false-positive degraded reports surfaced during the second Round 4 dogfood run: 1. Ollama probe was exact-string matching against required model names. `requiredModels` had `"nomic-embed-text"` (no tag), but `/api/tags` returns `"nomic-embed-text:latest"` (with default tag) — so the probe reported "missing models: nomic-embed-text" even after `ollama pull` succeeded. Fix: when a required name has no `:tag`, accept any tag (`name == required OR name.hasPrefix(required + ":")`). Required names that DO include a tag (e.g., `qwen3.6:latest`) keep their strict match. 2. MCP probe was reading ONLY `runtime.client.registeredToolNames()` — the stdio side (3 helpers: get_clipboard, get_time, run_applescript). The other 4+ in-process tools (self-knowledge + memory) live in `InProcessToolRegistry` and are routed through `InProcessAwareToolDispatcher` (commit 1b7fb81's B-01b fix). Probe now reads both registries, unions the names, and reports the combined count. Evidence line breaks out stdio vs in-process counts so the operator can see the split. Verified: app build clean, app target compiles. Re-launch should show ollama=ok (all 3 models present) and mcp=ok (8 tools after self-knowledge tools register at install step 15). The Toby fix verification mid-dogfood: the model received the degradation summary in its system prompt and truthfully reported "my memory subsystem is currently degraded (nomic-embed-text is missing)" instead of yesterday's fake "Got it" promise. Round 4 slice 3 confirmed working end-to-end. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
a9531e65e3 |
feat(round-4): menu-bar icon turns red when subsystems are degraded
Slice 4 of Round 4 — the visible polish. With Slices 1–3 the agent
preamble + non-dismissible banner cover the "agent must know +
user must see" loop, but a closed HUD with no banner visible can
still hide the warning. This slice paints the menu-bar icon red so
the warning is glanceable from anywhere on the screen.
Option A from the slice plan (single-writer-safe):
- `MenuBarIconController.applyHealth(_ health: OverallHealth)` paints
`button.contentTintColor = NSColor.systemRed` for `.loud` and
`.critical`, clears the tint for `.ok` and `.soft`. Template images
recolor automatically with `contentTintColor` so no second icon
asset is needed.
- `currentHealth: OverallHealth?` exposed read-only for tests.
- `AppDelegate.runBootHealth` calls `menuBarController?.applyHealth(
snapshot.overallHealth)` after computing the degradation summary,
so the icon updates on every probe sweep (cold boot + Status panel
re-probe).
Single-writer invariant for HudState (HUD-08) is intact:
`contentTintColor` lives on `NSStatusItemButton`, not in HudState.
`scripts/check-single-writer-hudstate.sh` is green.
1 new test in MenuBarIconControllerTests:
* test_applyHealthTintsRedForLoudAndCritical — exercises all four
OverallHealth cases and asserts contentTintColor toggles correctly.
Boundary gates 18/18 green; App target compiles cleanly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
23b4ca7712 |
feat(round-4): agent system prompt knows when subsystems are degraded
Slice 3 of Round 4 — the Toby-the-dog fix. With Slice 1 the probes
report severity and with Slice 2 banners route by severity, but the
critical behavioral change is here: the agent's system prompt itself
must carry the degradation truth so the model can't confidently lie
about features that don't work.
The Toby case in detail: user said "remember my dog Toby is a golden
retriever". The Ollama probe correctly reported missing nomic-embed-text
(degraded). The memory extractor tried to embed Toby, the embed call
failed, no row was inserted in `facts`. But the agent's system prompt
said "you are Jarvis with memory" and the model answered "Got it —
Toby." A lie, dressed up as competence.
Implementation:
- `AgentOrchestrator` gains a private `var degradationSummary: String?`
+ a public `setDegradationSummary(_:)` actor-isolated setter. Mutable
because installAgent runs BEFORE runBootHealth in the boot order;
the orchestrator is constructed with `nil` and AppDelegate pushes
the summary in post-construction. Re-probes from the Status panel
update the value mid-session.
- `runTurn` prepends the summary BEFORE `composedSystem` (which already
includes the UntrustedWrapper nonce + locked self-aware preamble),
so the warning lands at the very top of the system message — outranks
the preamble, outranks presence/memory enrichment.
- `AppDelegate.runBootHealth` now calls
`orchestrator.setDegradationSummary(buildDegradationSummary(snapshot:))`
after every probe sweep. The builder produces text like:
CRITICAL — SUBSYSTEMS DEGRADED:
- memory: failed (vec0 missing) — DO NOT promise to use memory
- ollama: degraded — missing models: nomic-embed-text
Be honest. Do not claim to use tools that are unavailable. Do not
pretend memory works if it doesn't.
When every subsystem is `.ok`, the function returns `nil` and the
orchestrator's behavior is identical to pre-Round-4.
- Cache-hints invariant preserved: the locked preamble is ~700 chars,
the worst-case degradation summary is <500 chars; sum stays well
under the 4096-char Anthropic cache-eligibility boundary so adding
the summary doesn't unintentionally flip `cache_control` emission.
CacheHintsEligibilityTests/preambleDoesNotEnableCacheUnintentionally
remains green.
4 new tests in AgentOrchestratorDegradationPreambleTests (all pass):
* DP1: initial summary threaded into first turn's system message
* DP2: nil summary leaves prompt unchanged (no "CRITICAL" / "DO NOT")
* DP3: setDegradationSummary updates the next turn's prompt
* DP4: setting summary back to nil clears it from the next turn
Boundary gates 18/18 green. `swift test --package-path packages/AgentCore`
runs 242/240 (4 new minus 2 pre-existing unrelated failures from
qwen2.5-coder model-ID literal drift, present before Round 4).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
79955b67b7 |
feat(round-4): non-dismissible banners for critical subsystem failures
Slice 2 of Round 4. With Slice 1 the probe layer now reports severity;
this slice routes severity to the banner system so the user CANNOT
silently dismiss a "your memory is dead, Jarvis will lie to you" warning.
- `BannerContent.nonDismissible: Bool` (default false).
- `HUDBanner` hides the dismiss X when `content.nonDismissible == true`.
- `HUDBannerCoordinator.dismissCurrent()` no-ops on non-dismissible
banners; the banner stays on screen until programmatically cleared
(`clear()` on app quit, or a re-probe finding the subsystem healthy
enqueues a different banner that pre-empts it).
- Two new presets:
* `bootHealthCritical(subsystem:reason:)` — priority 1, non-dismissible.
Title format: "Memory: critical failure". Body promises that the
agent will be warned not to rely on this subsystem (Slice 3).
* `bootHealthLoud(subsystem:reason:)` — priority 3, dismissible.
- Existing `bootHealthFailed(...)` preset kept as backwards-compat
fallback (no current call sites after this slice).
- `AppDelegate.enqueueFailedBootHealthBanners` rewritten to iterate
every non-ok subsystem and route by severity:
.critical → bootHealthCritical + `[system] CRITICAL` log line
.loud → bootHealthLoud
.soft → no banner (Status panel only)
Previously only `.failed` cases enqueued banners, which silently
dropped `.degraded` (the Toby case) entirely.
Two new tests in HUDBannerCoordinatorTests cover the new behavior:
* test_nonDismissibleBannerIgnoresDismiss
* test_criticalBannerPreemptsLowerPriorityCurrent
(Note: `xcodebuild test -only-testing JarvisAppTests` is pre-existing-
broken on this checkout due to onnxruntime static-link duplication —
confirmed by `git stash` before this slice. The test code compiles
cleanly via the regular App build; SPM-target tests for the
AgentCore-side severity model are green.)
Boundary gates 18/18 green. `swift test --package-path packages/AgentCore
--filter BootHealthOrchestratorTests` → 13/13.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
cfd469f72b |
feat(round-4): probe severity model — ok/soft/loud/critical
Round 4 of boot-health work. The Toby-the-dog incident — Jarvis said "Got
it" while the embedder model was missing and the fact never persisted —
proved that Round 2's probe banner was too quiet. Probes were already
reporting `.degraded` correctly; what was missing was a way for the App
layer to route a critical failure differently from a soft one (e.g.,
non-dismissible banner for memory off vs. silent Status row for camera
TCC not yet requested).
This slice introduces a severity axis without changing behavior:
- `FailureSeverity { critical, loud, soft }` on `ProbeStatus`'s non-ok
cases (degraded/failed/unknown). `.ok` carries no severity.
- `ProbeStatus.severity` accessor (nil for `.ok`).
- `BootHealthSnapshot.overallHealth: OverallHealth { ok, soft, loud,
critical }` — worst severity wins; `.ok` only if every subsystem ok.
- Each concrete probe annotates its failure modes:
* Memory: failed → .critical (agent grounding; the Toby case)
* Anthropic: missing/empty key → .critical (agent can't run)
* Ollama: missing models → .loud (fact extraction broken, chat fine)
* Voice: mic denied → .loud; dormant install → .soft
* Vision: TCC denied → .soft (vision is optional)
* MCP: 0 tools → .critical; <minimum → .loud
* Replay: init failed → .critical; missing file → .loud
* Webview: mismatched/timedOut → .critical; sentHello → .soft
- DormantSubsystemProbe gains `severity` parameter (default .soft);
AppDelegate wires memory-dormant as .critical.
No behavioral change yet — banners still treat all failures equally;
the agent system prompt is unchanged. Slices 2 + 3 land those.
Tests: 13/13 in BootHealthOrchestratorTests pass, including 4 new tests
for overallHealth + severity accessor + JSON roundtrip with severity.
2 pre-existing failures in LLMEventTests/OllamaRequestBodyTests are
unrelated (qwen model-ID literal drift from commit
|
||
|
|
b2f0abb670 |
fix(round-2): await probe registration inline to close runAll race
registerBootHealthProbes was spawning 8 fire-and-forget Task blocks,
each doing `await bootHealthOrchestrator.register(...)`. Then
runBootHealth called `await bootHealthOrchestrator.runAll()` —
which could fire BEFORE any register Task landed, yielding an empty
snapshot. Worst case: silent failure (no log lines, no banners,
empty Status panel).
The dogfood pass on commit
|
||
|
|
67748f5336 |
feat(round-3): Status… menu item + NSPanel — live re-probe on open
New AppKit panel (NSPanel + SwiftUI hosting view) renders the live
BootHealthSnapshot, re-probes on every appearance, and offers
"Re-probe all" + "Copy report" buttons. Operator/dev tool, not a
user-facing surface — AppKit deliberately so it doesn't ride the
R3F render pipeline.
Anti-fake-status carries forward from Round 2: the panel's `.task`
modifier fires a fresh `runBootHealth()` sweep on every open, never
showing a cached boot snapshot. Cmd+R re-probes; Cmd+Shift+C copies.
Non-modal: NSPanel with [.titled, .closable, .resizable,
.nonactivatingPanel] + makeKeyAndOrderFront(_:) — no runModal calls.
scripts/check-no-modal-presentation.sh stays green.
isReleasedWhenClosed=false so the user can close and re-open without
re-creating the model (preserves "Copied X B" caption + last
snapshot across opens, in addition to surviving the AppKit
"window deallocs on close" default that would otherwise crash on
second open).
Files:
- App/MenuBar/StatusPanel.swift (new):
* StatusPanelModel — @MainActor ObservableObject, owns the
snapshot + isProbing + copyConfirmation @Published state.
`probeRunner` closure injected so "Re-probe all" hits the
production AppDelegate.runBootHealth() path (single source
of truth — panel + boot use the same code).
* StatusPanelView — SwiftUI body. Scrollable rows, header
with spinner during probe, footer with re-probe + copy
buttons + caption.
* SubsystemRow (private) — glyph (✓/⚠/✗/?), name, status
label, latency, evidence, reason. Colors per status
(green/orange/red/gray). Multi-line evidence + reason.
* StatusPanel: NSPanel — hosts the SwiftUI view, `present()`
method centers + brings front + activates app.
- App/MenuBar/MenuBarContextMenu.swift:
* `build` now takes a `statusAction:` closure (5th param).
* "Status…" NSMenuItem between "Settings…" and "Show Dev Overlay".
- App/AppDelegate.swift:
* statusPanel: StatusPanel? lazy property.
* openStatusPanel() method — lazy-creates panel on first click,
passes self.runBootHealth as the probe runner closure.
* installMenuBar's MenuBarContextMenu.build call updated for
the new param.
- Jarvis.xcodeproj/project.pbxproj: PBXBuildFile + PBXFileReference
for StatusPanel.swift, added to MenuBar PBXGroup + Sources phase.
Verification:
- bash scripts/check-app-builds.sh: PASS
- bash scripts/check-no-modal-presentation.sh: OK
- bash scripts/check-install-order.sh: PASS
- bash scripts/check-no-leftover-stubs.sh: PASS
Dogfooding next: cold-launch → right-click menu bar icon → Status…,
verify rows render with concrete evidence (mic device name, model
ID list, etc.); click Re-probe and confirm rows refresh with new
latency values; click Copy report and paste into a text editor to
verify JSON is well-formed and complete.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
687b80b3f0 |
feat(round-2): boot-phase health probes — NOT FAKED, structured logs
After all install Tasks join, runs one live probe per subsystem and
emits structured `BootHealth: subsystem=X state=Y latencyMs=Z evidence="..."`
log lines. Any subsystem reporting .failed enqueues a non-modal banner
via HUDBannerCoordinator (priority 3 — sits between hotkey-bind-failed
and ollama-url-rejected). The snapshot is stored on bootHealthOrchestrator
so Round 3's Status menu can read + re-probe.
Anti-fake-status invariant is type-level: ProbeStatus is a sum type with
.ok / .degraded(reason:) / .failed(reason:) / .unknown(reason:) — every
non-ok case carries a reason string, so a snapshot can never report
state=unknown without explaining why. A probe that can't execute MUST
return .unknown — never .ok with a fake evidence line.
New types in packages/AgentCore/Sources/AgentCore/BootHealth.swift:
- ProbeStatus, ProbeOutcome, BootHealthProbe protocol
- SubsystemHealth (Codable for Round 3 "Copy report"), BootHealthSnapshot
- BootHealthOrchestrator actor — parallel TaskGroup fan-out,
registration-order-preserved snapshot, injectable clock for tests
Eight concrete probes in App/Boot/BootHealthProbes.swift:
- MemoryBootHealthProbe — MemoryStatsStoreAdapter.getMemoryStats round-trip
- AnthropicBootHealthProbe — Keychain structural check (no billable
network call at boot; Round 3 panel offers explicit network re-probe)
- OllamaBootHealthProbe — GET /api/tags, 3s timeout, asserts qwen3.6 +
qwen2.5-coder + nomic-embed-text all present
- VoiceBootHealthProbe — AudioGraphOwner state + mic TCC
- VisionBootHealthProbe — camera TCC + DiscoverySession (read-only,
does NOT prompt TCC per RESEARCH §Example 2)
- MCPBootHealthProbe — runtime.client.registeredToolNames count
- ReplayBootHealthProbe — replay.sqlite presence + parent-dir writability
- WebviewBootHealthProbe — WebviewBridge.handshakeState (.armed = live)
- DormantSubsystemProbe — placeholder for subsystems whose actor was nil
at probe-registration; reports .unknown honestly rather than skipping
AppDelegate wiring (step 16, runs after selfKnowledgeInstallTask which
transitively awaits every other install Task):
- bootHealthOrchestrator: BootHealthOrchestrator stored property
- bootHealthTask: Task<Void, Never>? cancelled in applicationWillTerminate
- runBootHealth() async: registers probes (idempotent — re-probes from
Round 3 reuse the existing registry), runs, logs, enqueues banners
- emitBootHealthLog / enqueueFailedBootHealthBanners helpers
Tests (packages/AgentCore/Tests/AgentCoreTests/BootHealthOrchestratorTests.swift):
- 8/8 passing (210ms wall — 3×200ms parallel probes prove TaskGroup
fan-out works; serial would be 600ms+)
- Coverage: runAll invokes every probe, snapshot preserves registration
order, parallel execution timing, lastSnapshot persistence, .failed
propagation, .unknown not coerced to .ok, deterministic clock
injection for latencyMs, JSON round-trip for all 4 ProbeStatus variants
Verification before commit:
- swift test --package-path packages/AgentCore --filter
BootHealthOrchestratorTests: 8/8 PASS (0.210s)
- swift test --package-path packages/Memory --filter
MemoryExtractorTests: 11/11 PASS
- bash scripts/check-app-builds.sh: PASS
- 18/18 boundary gates: PASS
Dogfooding next: cold-launch + grep system.log for `BootHealth:
subsystem=` lines; force a failure (e.g. kill Ollama) and verify
banner appears. Status menu (Round 3) is next session task.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
5718bdd820 |
test(memory): rename qwen25Coder32B test to qwen36
Matches commit
|
||
|
|
70dd0d41c6 |
docs(handoff): scope Rounds 2 (boot health) + 3 (status menu) for fresh session
Context-budget hand-off. Round 1 (model split) shipped in
|
||
|
|
8d0a959a19 |
feat(model): qwen3.6 for memory extractor; q8_0 for agent-loop fallback
Round 1 of the post-D-5/D-6 model strategy split. The user has both `qwen3.6:latest` (~22GB MoE) and `qwen2.5-coder:32b-instruct-q8_0` (~32GB) pulled. CLAUDE.md previously pinned the entire local-model strategy to the base `qwen2.5-coder:32b` because of an April 2026 Ollama tool-call bug affecting Qwen 3 / 3.5 / Gemma 4. That ban was correct then; today (2026-05-11) it isn't. **Empirical retest 2026-05-11:** POST /api/chat (non-streaming) against qwen3.6:latest with a representative `apply_memory_ops` tool definition returned a clean tool_calls array, well-formed JSON args, no leaked <think> tags. Upstream Ollama appears to have closed the relevant issues (#14493 / #14601 / #14745 / #15315) in the interim. Documented in ModelID.qwen36 doc comment. Two-model strategy: - **MemoryExtractor default → qwen3.6:latest** (`ModelID.qwen36`) Single-turn, single-tool, no cap recovery — the lowest-risk place to validate the newer model. If extraction stays stable for a few days of dogfooding, we revisit the agent-loop fallback. - **Agent-loop Ollama fallback → qwen2.5-coder:32b-instruct-q8_0** (`ModelID.qwen25coder32b`'s rawValue is now the Q8_0 variant the user already has — higher fidelity than the default Q4_K_M, same model family, same tool-call wire format). The agent loop has multi-turn tool chains, cap recovery, tool_choice=none paths, and streaming all in scope — kept conservative until Qwen3.6 is verified there too. Changes: - packages/AgentCore/Sources/AgentCore/ModelID.swift + `qwen36` constant for qwen3.6:latest with empirical retest doc. ! `qwen25coder32b` rawValue is now `qwen2.5-coder:32b-instruct-q8_0` (same constant name, the Q8_0 raw value). All ModelID.qwen25coder32b consumers pick up the change automatically. - packages/Memory/Sources/Memory/MemoryExtractor.swift - `model: ModelID = .qwen25coder32b` → `.qwen36`. Docstring updated to explain the empirical retest + why the extractor is the safest place for the newer model. - packages/Harness/Sources/Harness/Runners/LiveOllamaRunner.swift `requiredModelID` constant now sources from `ModelID.qwen25coder32b.rawValue` so a future single-edit on the canonical model name propagates here automatically. Doc updated to point at the q8_0 variant + ~32GB pull size. User-facing note: no model pulls required for this commit; both models are already on the user's machine. Round 2 (boot-phase health check) and Round 3 (Status menu dialog) deferred to a fresh session for context budget — handoff doc to follow. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
cb584a2ac8 |
feat(d5d6): real memory database — vec0 statically linked, hard-fail boot, stats tool
Closes D-5 / D-6 (sqlite-vec dylib bundling) carry-forwards from the 2026-05-04 audit by adopting jkrukowski/SQLiteVec as a SPM dependency. The package's CSQLiteVec target compiles sqlite3.c + sqlite-vec.c into one statically-linked C target with `core_vec_init()` registering vec0 as an auto-extension. All sqlite3_* calls in Replay's SQLiteConnection and Memory's MemoryStore resolve to this bundled SQLite (vec0 always available) instead of Apple's stripped /usr/lib/libsqlite3.dylib. The retired hand-rolled approach (dlsym pathway + JARVIS_VEC0_STUB_PATH env-flag + Bundle.module placeholder text file) never worked on macOS because Apple stripped sqlite3_load_extension from the system libsqlite3. Memory init has been silently dormant since Phase 7 (2026-04 onward), which is what made B-02 (history threading) appear permanently broken to the user during dogfooding even after the |
||
|
|
48ad8d631c |
fix(b-02): thread recent turns into AgentOrchestrator messages array
Carry-forward bug B-02 (live evidence 2026-05-07): conversation
continuity broken — "yes" / "why 4.5?" lose prior turn context because
AgentOrchestrator.process(_:) built [system, user(input.userText)]
with no history fetch.
Tactical patch outside the M-0..M-7 migration sequence, per
JARVIS-API-MIGRATION-PLAN.md §3 and UQ-2 (option B). Lands on develop
directly. M-4 (Memory surface) will reframe this path against the new
API contract but the user-visible bug closes today.
Substrate gap discovered + addressed in-scope. The `turns` table was
unwritten (schema present at MemorySchema.swift, FTS5 trigger ready,
read API at MemoryStore.recentTurnsForSession, but no INSERT site
anywhere in the codebase). The migration plan assumed turn writes
existed; they did not. Patch adds the missing writer plus the read
side wiring.
Changes:
- packages/Memory/Sources/Memory/MemoryQueries.swift
+ turnInsertSQL — single-row INSERT into turns
- packages/Memory/Sources/Memory/MemoryStore.swift
+ appendTurn(sessionId:role:content:source:createdAt:)
Single-row INSERT; FTS5 trigger mirrors content into turns_fts
automatically. T-06-05-03: function MUST NOT log content.
- packages/AgentCore/Sources/AgentOrchestrator/AgentOrchestrator.swift
+ PriorTurn struct + SessionHistoryLookup typealias
+ emptySessionHistoryLookup default (preserves pre-B-02 test setups)
+ sessionHistoryLookup ctor parameter
Replaces the [system, user] messages-array build with prior-history
hydration: system + chronological prior user/assistant rows + current
user. Lookup throws -> warning log + empty history (matches D-3
PriorFactsLookup resilience contract).
- App/AppDelegate.swift
+ sessionHistoryLookup closure: recentTurnsForSession(limit: 10),
reverse DESC -> chronological, filter to user/assistant only, return
[PriorTurn]
+ extends existing turnContent flush closure with appendTurn calls
(rides on memory-extraction drain path; assistant row offset +1 ms
so DESC + id-tiebreaker preserves user->assistant order)
Tests:
- packages/AgentCore/Tests/AgentOrchestratorTests/HistoryThreadingRegressionTests.swift
HT-1: third turn includes prior user/assistant pairs in chronological
order. 3/3 PASS.
HT-2: empty-history default produces [system, user] (pre-B-02
baseline preserved).
HT-3: three-turn happy path emits zero LLMProviderError
.streamTruncatedFinal events. Pins R-006 / Phase E gate behavior:
patch grows messages array, not finalSystem, so
CacheHints.eligibleForSystemPrompt(finalSystem) is unchanged.
- packages/Memory/Tests/MemoryTests/MemoryStoreAppendTurnTests.swift
AT-1: appendTurn round-trips via recentTurnsForSession.
AT-2: user/assistant pair reverses to chronological.
AT-3: session_id scoping isolates rows.
AT-4: turns_fts trigger fires on append.
All env-gated on JARVIS_VEC0_STUB_PATH per existing MemoryStore-test
convention; skip cleanly without vec0.dylib.
Verification:
- check-app-builds.sh PASS.
- Boundary gates sampled (check-no-leftover-stubs, applescript-confirm,
corpus-secrets, bus-protocol-version, single-memory-mutated-emit,
single-writer-hudstate) PASS.
- AgentCore affected-subset (Submit/Retry/TextEnd2End/CapRecovery/
HistoryThreading) 26/26 PASS, 0 failures.
- Memory full suite 92 run, 23 skipped (env-gated), 0 failures.
Degradation contract: when MemoryStore is nil (dormant memory — vec0
or Ollama unavailable), turn persistence is also dormant; the
appendTurn call is guarded by memoryStore != nil. When D-5/D-6 close
and memory comes up live, persistence activates without further code
changes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
46590cd9aa |
docs(architecture): API design swarm output — v1.0 contract locked
Six-agent design swarm executed end-to-end on 2026-05-07 evening per
.planning/architecture/HANDOFF-2026-05-07.md. The architectural pivot
to API-first reaches lock state: v1.0 = JARVIS-API-DESIGN-v0.1.md +
JARVIS-API-DESIGN-v0.2.md (UQ-1..UQ-5 answered).
Swarm output (.planning/architecture/):
- INVENTORY.md (~190 capabilities, file:line cited; ALIVE/DEAD/
DEFERRED/RETIRED-CANDIDATE classification)
- JARVIS-API-DESIGN-v0.1.md (1036 lines; 7 surfaces — Turn / Voice /
Memory / Vision / Settings / Diagnostics / Self; CQRS framing;
TurnID universal correlation; typed Results)
- REVIEW-API-CORRECTNESS.md (21 findings: 2 BLOCKING, 5 HIGH)
- REVIEW-MIGRATION-RISK.md (19 findings: 1 BLOCKING, 7 HIGH;
M-0..M-7 cutover sequence)
- REVIEW-TEST-CONTRACT.md (16 findings: 2 BLOCKING, 5 HIGH; harness
shape recommendation)
- JARVIS-API-DESIGN-v0.2.md (synthesis delta; every critic finding
dispositioned ACCEPT/DEFER/USER-DECISION; UQ-1..UQ-5 locked in §5)
- JARVIS-API-TEST-CONTRACT.md (~70 scenarios across 7 surfaces +
cross-surface; B-02..B-08 reproduction matrix)
- JARVIS-API-MIGRATION-PLAN.md (M-0..M-7 + B-02 tactical patch;
~52 distinct gates; 19-26 engineer-day estimate)
- README.md (artifact index + reading order)
Locked decisions:
- UQ-1: runtime gate JARVIS_HARNESS=1 (single binary)
- UQ-2: B-02 tactical patch outside migration + streamTruncated
regression test; B-04 stays in M-6
- UQ-3: explicit Voice.synthesizeTurn(turnId:text:tier:) command
- UQ-4: Settings owns ALL configuration mutation; Voice retains only
runtime-control verbs (pttDown/pttUp/cancelTTS/synthesizeTurn);
bargeIn migrates to Turn surface
- UQ-5: parametric harness mandated — every scenario runs
.inProcessActor AND .jsonRoundTripWebView
Tools/jarvis-diag/ — Swift Package skeleton authored by Phase 5:
type signatures + scenario stubs only, no implementation logic.
Compiles green (swift build under Tools/jarvis-diag/). M-0 step 3
wires it against packages/JarvisAPI/ once that package exists.
Constraints honored: zero code changes outside .planning/architecture/
and Tools/jarvis-diag/. No fixes to B-02..B-08 inline. Parallel agents
wrote disjoint files (.continue-here.md anti-pattern preserved).
Next session: cross-AI peer review of v1.0 (GPT-5 Pro / Gemini 2.5 Pro
/ fresh Opus), then B-02 tactical patch on develop, then M-0 (pre-
migration gates), then M-1 (Self surface) on its own feature branch.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
b36e8ebbe5 |
docs(handoff): architectural pivot to API-first — close session 2026-05-07
After two days of fix → test green → relaunch → still broken loop, the
verification gate (executor self-reports + boundary gates) cannot detect
substrate failures. Today's substrate fixes (B-01a / B-01b — tool catalog
enumeration + dispatch routing) shipped and live-verified, but live testing
surfaced B-02..B-08: history threading, camera click, voice input, TTS
output, chat scroll, voice menu, model-name paraphrase. Stop-loss decision:
pivot to API-first architecture.
This commit packages the session-end state for clean handoff:
- .planning/architecture/HANDOFF-2026-05-07.md — primary handoff doc.
Specifies the 6-agent design swarm (inventory → architect → 3 critics in
parallel → synthesis → test contract + migration in parallel) that the
next session dispatches. Goal: JARVIS-API-DESIGN.md v1.0 +
test contract + migration plan, implementation-ready by midnight.
- docs/SESSION-STATE.md — updated with 2026-05-07 session-end state and a
▶ Next Session pointer to the handoff. Earlier 2026-05-04 entry preserved
below the line.
- docs/TODO.md — top-of-file pivot directive + B-01 closures + B-02..B-08
carry-forward + Phase 10 status + housekeeping loose ends.
Branch: develop @
|
||
|
|
1b7fb81408 |
fix(10-02c): wire InProcessAwareToolDispatcher into runtime — closes B-01b
Prior B-01a fix ( |
||
|
|
0961fef40b |
feat(10-02c): add InProcessAwareToolDispatcher composite for B-01b dispatch routing
- Composite ToolDispatcher: name-routes between InProcessToolRegistry (memory + four self-knowledge tools) and inner stdio dispatcher (MCPToolDispatcher). - Sits INSIDE ConfirmingToolDispatcher's chain so confirmation gating, bus emission, and observer hooks fire identically on both paths. - In-process result bytes flow through the same SanitizeForModel.prepareForBoundary + ToolResultObserver pipeline as stdio results (audit-trail parity). - Tools registered AFTER composite construction are still routable because the composite holds the registry actor reference and queries by name on every dispatch (resolves the installSelfKnowledgeTools-runs-after-installAgent install-order wrinkle). - Closes 5/5 cases in InProcessAwareDispatchRoutingTests. |