Skip to content

feat: add granite hot-seat review pipeline - #1

Merged
aarzamen merged 7 commits into
mainfrom
codex/granite-hot-seat-sprint
May 7, 2026
Merged

feat: add granite hot-seat review pipeline#1
aarzamen merged 7 commits into
mainfrom
codex/granite-hot-seat-sprint

Conversation

@aarzamen

@aarzamen aarzamen commented May 7, 2026

Copy link
Copy Markdown
Owner

This branch adds the Granite hot-seat processing path and hardens the surrounding operator-safety gates. It keeps Apple Speech and Apple Foundation Models as defaults while making Granite an explicit, evidence-bound review/adjudication path rather than a direct state writer.

  • Adds transcript segment evidence, hot-seat packet construction, Granite candidate patch validation, and review queue wiring.
  • Adds the explicit Granite text backend path with structured candidate patch generation and missing-weight gating before loader/download behavior.
  • Adds an unavailable Granite Speech TranscriptStream scaffold so the backend can be represented without silently starting audio, network, or model loads.
  • Adds report-level critique schema validation so Granite can flag report concerns without rewriting 9-Line, ZMIST, or DD-1380 exports.
  • Adds bake-off, device profiling, and report critique specs under docs/granite/specs/.
  • Includes review-hardening fixes for 9-line readiness/transmit gating and stale build docs.

Behavior Change

  • Apple Foundation Models remains the runtime LLM default.
  • Apple Speech remains the runtime ASR default.
  • Pending or demo location cannot count as transmit-ready.
  • Granite output queues reviewable patches/critique findings and does not directly mutate PatientState.

Testing

  • xcodegen generate succeeded.
  • xcodebuild test -project TCCC_IOS.xcodeproj -scheme TCCC_IOS -destination 'id=DE7116A4-74E0-40EA-85C2-0D19C290BD0E' -configuration Debug CODE_SIGNING_ALLOWED=NO -skipMacroValidation passed: 37 tests, 0 failures.
  • swift test in Packages/TCCCKit passed: 724 tests, 0 failures.
  • YAML/spec parsing passed for docs/granite/specs/*.yaml and docs/granite/SPRINT_BOARD.yaml.
  • git diff --check passed.

Known-But-Deferred

  • Real Granite Speech ASR runtime remains unavailable in Swift and is scaffolded as controlled-unavailable.
  • WhisperKit bake-off, iPhone device profiling, and report critique UI are specified but not yet operationalized in docs/granite/SPRINT_BOARD.yaml and docs/superpowers/plans/2026-05-07-granite-sprint-2.md.

ama added 7 commits May 7, 2026 01:18
…nits, thermal state for capture-rate root cause
… + AAC write — root cause of slow playback + 14% capture rate
…estamps

Bug report 2026-05-07 — at minute 01:40, four distinct utterances were
visually overwriting each other in a single MEDIC 01:40 bubble. Two
collaborating bugs:

(1) emitPartial only updated currentPartial; if Parakeet started a new
utterance partial without firing setEouCallback (continuous fast speech
without a 2 s silence gap), the prior utterance was never committed —
the partial bubble silently replaced itself instead of growing into a
new finalised line. Fix: detect non-continuation/non-revision partials
and commit the prior text as an implicit utterance boundary.

(2) displayTimestamp used HH:mm — multiple finalised lines within the
same minute all displayed 'MEDIC 01:40', making them visually
indistinguishable. Fix: HH:mm:ss.
…nscript behind operator's back

Bug report 2026-05-07 root cause: AppState.scheduleAutoClean() ran 5 s
after each silence-commit and 60 s wall-clock fallback, writing the
LLM-cleaned output to state.transcriptCleaned. displayedTranscript in
LiveCaptureScreen reads transcriptCleaned ?? transcript, so once
auto-clean fired even once, the UI was bound to the cleaned snapshot
and new utterances appended to state.transcript were not visible until
the next auto-clean re-cleaned the whole window — producing the
bubble-overwrite pattern (LLM merging multiple utterances into one
paragraph, replacing what the operator just saw).

The manual Clean transcript button remains. Auto-pass stays off until
VAD-driven firing replaces the wall-clock fallback.
Adds the Granite-centered evidence ledger, packet contract, schema validators, explicit Granite text backend, unavailable Granite Speech scaffold, report critique schema, and sprint specs. Keeps Apple Speech and Apple Foundation Models as defaults while ensuring model output stays reviewable and evidence-bound.
Add a local-only Granite real-model runner, Swift artifact plumbing, manual test gate, and result documentation so the hot-seat path can be evaluated with actual MLX output while preserving the existing safety boundary. Harden candidate patch decoding and reject empty no-op patches so malformed model output cannot become review-ready.
@aarzamen
aarzamen merged commit 2818b89 into main May 7, 2026
1 check failed
aarzamen pushed a commit that referenced this pull request May 10, 2026
- SPRINT_1_ACCEPTANCE.md (repo root) walks v3 §13's nine criteria
  with PASS / PARTIAL / DEFERRED markings + per-criterion evidence
  (test names, commit refs, operator-validated metrics, screenshots).
  Outcome: 7 PASS / 1 PARTIAL / 1 DEFERRED.
  - 1, 2, 3, 4, 7, 8, 9: PASS.
  - 5 (5-min fixture / live mic stable): DEFERRED. Two compounding
    causes — encoder long-form crash (G2 §1) + live-mic mailbox
    unbounded growth (G3 §2). Sprint 2 fix paths in
    PRIOR_AUDIO_PATTERNS.md: chunked-encode (Top Pattern #1) +
    back-pressured writer (Top Pattern #2).
  - 6 (memory gauges + cap held): PARTIAL. Gauges visible + cap
    held for short audio; long-form correctly triggers jetsam (the
    cap is being honored — that's why the crash happens). Same
    Sprint 2 fix unblocks both.

- GraniteLiveView gains a long-form warning banner per the G4
  discipline-point review:
  - Triggers on phase == .recording AND
    (recordingDuration ≥ 60 s OR memory pressure ≠ .normal).
  - Banner copy: "Long-form recording is unstable in Sprint 1 —
    see G3 limitations in CLAUDE.md."
  - Subtitle line surfaces the actual trigger ("duration 67s ≥ 60s
    · memory pressure: warning"), and the border color escalates
    warn → crit on critical pressure.
  - This is preventive UX, not a bug fix — it stops the operator
    walking into the live-mic memory leak without warning.

- MemoryMonitorCSVLogger wired into GraniteLiveView's recording
  loop (Documents/MemoryMonitorLog.csv). Rows on record_start /
  primed / per-tick / pressure_warning / pressure_critical /
  record_stop / transcribe_complete / failed. This moves v3 §13
  criterion 7 from FAIL to PASS — log file is non-empty after a
  Granite Live session and is pullable via xcrun devicectl.

- Body wrapped in TimelineView(.periodic(by: 1.0)) so the duration
  sentinel + banner re-evaluate every second even when no
  MemoryMonitor reading changes.

- Packages/TCCCAudio/CLAUDE.md G4 section appended with the
  acceptance-gate outcome, the PASS-list cross-reference, and the
  Sprint 2 carry-over pointer to SPRINT_1_ACCEPTANCE.md.

Hardware validation: G4 polish covers the live-mic surface only;
no new heavy-runtime change. Sim build clean. Device deploy +
operator validation can land in next session — banner + CSV
logger are local UI / file I/O, both well-trodden patterns.

Tag: sprint-1-granite-speech-foundation applied to this commit on
push. Sprint 2 reads back from this tag.

Files changed: 3

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
aarzamen pushed a commit that referenced this pull request May 10, 2026
Sprint 2 spec is gated on a research pass comparing three approaches
for chunked-encode in Granite Speech 4.0 1B 5-bit:

1. Single-shot encode (current Sprint 1 baseline) vs a non-encoded
   chunking variant that feeds audio without a per-chunk encoder pass.
2. Non-encoded chunking with very short (~3-4 s) windows.
3. Encoded chunking with longer (~10 s) windows aligned to the
   encoder's native context_size=200.

The choice has load-bearing architectural consequences for the
chunked-encode + back-pressured-writer work that closes Sprint 1's
DEFERRED + PARTIAL acceptance criteria (5 + 6). Spec-blocking until
the research lands; recorded in Packages/TCCCAudio/CLAUDE.md so the
next session resumes from the blocker rather than re-discovering it.

Spec-side resumption pointer also added: the design pass reads from
SPRINT_1_ACCEPTANCE.md §"Sprint 2 carry-over" + PRIOR_AUDIO_PATTERNS.md
Top Pattern #1 + this blocker section, and should spawn a research
subagent (same shape as the one that produced PRIOR_AUDIO_PATTERNS.md)
to fetch the upstream Granite Speech model card + any chunked-decode
notes from mlx-audio-swift since v0.1.2.

This commit lands AFTER the sprint-1-granite-speech-foundation tag —
the tag stays at e13ecc4. Documentation drift only; no code touched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
aarzamen pushed a commit that referenced this pull request Jun 29, 2026
…witch (whole-branch review #1)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
aarzamen pushed a commit that referenced this pull request Jun 29, 2026
…vents

Whole-branch review #1 (PHI-bleed): dirName carried only an integer-second
stamp; End Care reuses the same casualtyId with startUnix=now and
createDirectory does not throw on an existing dir, so a same-second rotation
reused the just-archived dir and could resurrect its PHI as active on next
launch. Add a UUID suffix to the dir name + a regression test (same-id
same-second rotation must not inherit archived events).

Whole-branch review #2 (audit completeness): the .rejected arm of
acceptGraniteFact appended an operatorRejectedFact event but never flushed it;
add persistNewEvents() so the rejection reaches disk immediately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
aarzamen pushed a commit that referenced this pull request Jun 29, 2026
Whole-branch review finding #1 (important). persistNewEvents() reads
persistedCursor, crosses two awaits (engine.newEvents, store.appendToActive),
then advances the cursor — with no mutual exclusion. It is invoked from
fire-and-forget Tasks (per committed ASR line via appendFinal; from
GraniteReviewQueue). Under rapid dictation two invocations read the SAME cursor,
slice overlapping [cursor...] ranges, and both append.

BLOCK A worsened the consequence: the cursor commit changed from absolute
(persistedCursor = log.events.count, which converges to the true count → dup
only) to relative (persistedCursor += new.count, which overshoots → dup AND
skipped events = loss on the encrypted JSONL). This restores correctness and
goes further than the pre-BLOCK-A behavior: a coalescing guard lets only one
drain run at a time; a call arriving mid-drain sets persistAgain and the active
drain loops to pick up what it missed. Only the active invocation reads/advances
the cursor, so each slice is exact — no overlap, no skip. Flag reads/writes never
straddle an await, so on @mainactor they are atomic w.r.t. reentrancy.

App suite 84/0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
aarzamen pushed a commit that referenced this pull request Jul 3, 2026
…eech lane, WER/recall/extraction scoring, --transcription-benchmark gate

- AppleSpeechFileTranscriber actor: re-arming file-feed loop mirroring
  SpeechRecognizer.handleFinalResult production semantics; on-device only;
  30s no-result / 60s hard-cap drain; strict-concurrency-safe (no
  non-Sendable capture in @sendable callback).
- BenchmarkReference: slug rule + Bundle.main .txt/.json sidecar loader.
- BenchStateMapper: flat [String:String] from PatientState for
  ExtractionScorer (moi/hr/bp/rr/spo2/classification/interventions keys).
- TranscriptionBenchmarkView: --transcription-benchmark launch-arg gate;
  fixtures → transcribe → score WER/keyword/extraction → JSON + summary.md.
  BenchmarkRunResult stores werPercent/keywordRecallPercent/
  extractionRecallPercent/extractionPassed/extractionTotal at construction
  (correction #1: computed properties are omitted by JSONEncoder, stored
  fields are not — JSON is self-describing without touching TCCCBench types).
  Memory fields use MemoryStat.availableBytes() (nil on sim, real on device).
- TCCC_IOSApp: TranscriptionBenchmarkView branch between Granite and Content.
- project.yml: TCCC_IOS gains TCCCBench; TCCC_IOSTests gains TCCCExtractor.
- BenchStateMapperTests: 2 tests (RED→GREEN): mapper reads bp/hr/rr from
  a fresh standard() engine; nil state returns empty dict.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BArcT7XTFUirDSk6Xb7Keh
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant