Repository navigation
fix(ingest): persist per-turn authoring model for Claude Code and Codex chunks (Refs #655) - #1639
Conversation
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 22 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
|
|
Overall Grade |
Security Reliability Complexity Hygiene |
Code Review Summary
| Analyzer | Status | Updated (UTC) | Details |
|---|---|---|---|
| Python | Nov 5, 2026 7:28a.m. | Review ↗ | |
| Swift | Nov 5, 2026 7:28a.m. | Review ↗ | |
| JavaScript | Nov 5, 2026 7:28a.m. | Review ↗ | |
| Shell | Nov 5, 2026 7:28a.m. | Review ↗ | |
| Secrets | Nov 5, 2026 7:28a.m. | Review ↗ |
Important
AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.
BrainLayer ratchetEvery Value below was measured by this run. A row this machine cannot measure says Hosted success gives no verdict on mapped bytes, search p50/p95, idle CPU, hotlane backlog CPU, or fallback replay debt.
🟢 GREEN measured, within budget · 🔴 RED measured, out of budget — a finding to clear before merge · ⚪ n/a not measurable on this machine, never guessed. No RED rows. Measured on Linux/x86_64 · measured |
…ex chunks (#655) Claude Code transcripts record message.model on every assistant turn, but _extract_entry_metadata dropped it, so realtime-watcher and batch-index rows could not answer 'which model wrote this?'. Read it into chunk metadata ['model'] for assistant entries only, verbatim, and leave it unknown when it is absent, non-string, malformed, or Claude Code's '<synthetic>' placeholder. The Codex parser, cited as the working path, actually stored model_provider ('openai') under 'model' and kept the first value for the whole session. It now takes each turn_context model as the author of the following turn, records model_provider separately, and stops attributing user and tool chunks to a model. Authoring provenance stays distinct from enrichment_model/enrichment_backend. Refs #655 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L3NoZDshC2wAT1TQqUTtFa
a03bca7 to
1dd44ef
Compare
TASK MARKER:
cloud-credit-20261105-brainlayer-final-issue-655Draft, partial. Refs #655, not Fixes. This fixes Part A (transcript ingestion) and corrects the Codex path that #655 called "working". Part B (self-reported
caller_modelon BrainBarbrain_store) is not done. See Unresolved acceptance below.Change and evidence
Base and head
origin/maind6b82f04f6baa00081c9dc38afca2ada9d114c78d6b82f04f6baa00081c9dc38afca2ada9d114c78(same commit)claude/orc-final-credit-batch-z2uwd0@a03bca748a74dd2836b4b613a1fd21d8e15d888b, one commitTrace on the current active surfaces
watcher.normalize_provider_entrykeeps Claudeuser/assistantentries verbatim, includingmessage.model. The entry then goes throughwatcher_bridge.flush_to_db,classify_content,chunk_contentand themetadataJSON written tochunks.metadata. On main,pipeline/classify._extract_entry_metadataread onlysessionId,timestampandsender, so the model was dropped. The batchbrainlayer indexpath uses the sameclassify_content, so this fix covers it too (source-level; not separately exercised).ingest/codex.py) was not actually working. On asession_metaline it setmodel = payload.get("model_provider", "openai"), and aturn_contextmodel was used onlyif not model. So every real Codex chunk stored"model": "openai", which is the provider, not the model. The first value also stuck for the whole session, and user and tool chunks were labelled with it as well.brain_store. The served schema inbrain-bar/Sources/BrainBar/MCPRouter.swiftiscontent,tags,importanceandproject.handleBrainStorethen callsstoreOrQueueWithinBudgetor the pending-queue replay. There is no model field anywhere on it. The Pythonmcp/store_handler.pypath named in the issue is not served.What changed
pipeline/classify.py: newtranscript_author_model(raw). It returns the transcript's own model string, stripped, orNone. Nothing is inferred. These values are treated as unknown: absent, non-string, empty or blank, Claude Code's"<synthetic>"placeholder (written on turns no model produced), longer than 128 characters, or containing non-printable/control characters._extract_entry_metadatasetsmeta["model"]forassistantentries only. A user entry, a tool_result carried on a user entry, or a task-notification result never gets a model.ingest/codex.py: eachturn_context.model, passed through the same validator, becomes the author of the turns that follow it.model_provideris now recorded separately on every chunk.modelappears only on assistant chunks, and isNonewhen no turn_context named one.enrichment_modelorenrichment_backend, and a test asserts both stay NULL whilemetadata.modelis set.chunks.metadataJSON, the same key Codex already used. Existing rows are untouched: old Claude rows have nomodelkey (unknown), and old Codex rows keep"openai".Commands and observed output (Linux cloud container, Python 3.13.18, editable venv)
Fail-first, old code (
git stash push src/, new tests kept):New code:
16 passed.Mutant controls (each one applied to HEAD, then restored):
<synthetic>filter[synthetic])test_user_entry_claiming_a_model_is_not_attributed)model = model or …)…unknown_not_the_provider)Focused neighbours:
tests/test_author_model_provenance.py test_classify.py test_ingest_codex.py test_watcher_bridge.py test_watcher_provenance_ingest.py test_watcher.py test_jsonl_watcher.py test_chunk.py test_chunker.py test_context_pipeline.py test_prompt_classification.py test_precompact_chunk_origin.pygave 342 passed.ruff checkandruff format --checkpass on all three files.Repo gate:
BRAINLAYER_PREPUSH_SCOPE=changed-only BRAINLAYER_CHANGED_FILES_RANGE=origin/main..HEAD scripts/run_tests.shfell back to the full suite becausesrc/brainlayer/ingest/codex.pyis unmapped, and it hit my 500s timeout at about 42%. Up to that point there were 4,860 PASSED and 65 FAILED, in test_backup_*, test_brainbar_service_render, test_brainbar_update_script and test_audit_recursion_filter. All 65 fail identically on a cleanorigin/mainworktree (65 failed), so they are Linux-container environment failures (zstd, macOS service rendering), not this diff. The rest of the full suite (about 58%) is UNRUN.The test fixtures are synthetic: scratch SQLite under pytest
tmp_path, made-up model strings (claude-fixture-model-a,codex-fixture-model-b), no real transcripts, no network, and no model loads.Unresolved acceptance (why this is Refs #655)
Part B, BrainBar
brain_storecaller_model: NOT implemented. The served surface is Swift. Doing it needs:caller_modelstring in thebrain_storeinputSchema, plus a length cap in thelimitedInputSchemamap;handleBrainStore, thenstoreOrQueueWithinBudget/queueBrainStore, the pending-store queue JSON, and replay;metadata.modelwithmetadata.model_source = "caller_self_reported", and absent when not supplied (never inferred fromagent_idor the session);This container has no Swift toolchain and BrainBar is a macOS app, so I could not compile or test any of it. I would rather leave it out than ship an untested change to the store/queue write path. Owner: BrainBar lane.
Codex sessions read by the realtime watcher still carry no model.
normalize_provider_entryrebuilds each Codexresponse_itemwithout context, and the model lives on earlierturn_contextlines. Fixing it needs per-file state across polls (offsets restart mid-file). That is out of scope here, and nothing tests it.Retrieval surfacing.
metadata.modelis persisted, but no BrainBar search formatter shows it. Answering "which model wrote this?" today means queryingchunks.metadata(json_extract(metadata,'$.model')). There is nochunks.modelcolumn; this PR deliberately adds none.The arbitrated (writer-queue) watcher path is source-level only. It passes the same
metadatadict toenqueue_chunk. The test exercisesarbitrated=False.Native, installed or live proof is UNRUN. No Mac, no BrainBar, no production DB, no installed watcher. Proof here is source and fixture only.
Risks
None) instead of"openai". A query onmetadata.model = 'openai'would split between old and new rows. I found no reader of that key insrc/.Owner handoff,
@brainlayerleadbrain_storecaller_model) as a follow-up in the BrainBar lane, and whether retrieval should showmetadata.model.a03bca7.SELECT json_extract(metadata,'$.model'), count(*) FROM chunks WHERE source='realtime_watcher' GROUP BY 1.orchestrator/docs.local/plans/2026-11-04-claude-cloud-batch/collab.md. That file is not mounted in the cloud, so this body is the handoff for the dispatcher to import.Implementer/harness: Claude Code cloud worker (Anthropic Claude, effort high, fast mode off). There were no subagents and no paid API calls. A cost meter was not available in-session; the work ran from 02:11Z to about 02:35Z, well inside the 07:00Z bound.
Loop-record: implementer=anthropic/claude-code-cloud/high; reviewer=none; rounds=0; defects=0
Generated by Claude Code
Note
Persist per-turn author model for Claude Code and Codex ingest
classify.transcript_author_modelthat trims whitespace and rejects non-strings, blank values, synthetic placeholders, strings over 128 chars, and non-printable characters_extract_entry_metadatain classify.py now stores the validated model on assistant entries only; user entries never get an author modelturn_context, and tracks the sessionmodel_providerseparately on user, tool, and assistant chunksMacroscope summarized a03bca7.