feat(hermes): admit ATOF/ATIF real-bytes gaps, state.db v19 drift, verification ledger#3092
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Warning Review limit reached
Next review available in: 40 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (20)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
…events Problem: real events.jsonl evidence (~/.hermes/observability/nemo-relay/atof/) shows the outer session-wrapping scope (kind=scope,category=agent) and hermes.session.end marks are well-defined lifecycle events, but the ATOF parser classified both as generic unrecognized hermes_observer_span, degrading fidelity for evidence the producer already emits structurally. What changed: kind=scope/category=agent scope pairs and hermes.session.* marks now normalize to typed hermes_context_span events (same treatment as hermes.turn.*), synthesizing a stable context_event key from scope_category since the scope's own name field carries the session identifier, not an event key. context_events fidelity moves from absent/degraded to exact on the real redacted ATOF fixture; unrecognized_atof_events drops to zero. Ref polylogue-fs1.2.1 Co-Authored-By: Claude <noreply@anthropic.com>
…bytes Problem: the real ATIF fixture used to admit fs1.2.1 came from a minimal message-only session, so tool_execution_spans fidelity stayed "inferred" even though the parser's tool_calls/observation mapping was never verified against real evidence -- despite real trajectories with that exact shape already sitting on disk (~/.hermes/observability/nemo-relay/atif/). What changed: appended a real, privacy-redacted step (4 parallel tool_calls with correlated observation.results, drawn from a separate live trajectory) to the checked-in ATIF fixture. tool_execution_spans now reports "exact" fidelity like llm_request_spans, since the field mapping is proven, not just this session's bytes. subagent_delegation stays "inferred" honestly -- no real ATIF export sampled so far has a non-empty subagent_trajectories. Added tests/fixtures/hermes/atif/README.md documenting fixture provenance (the ATOF fixture already had one; ATIF didn't). Ref polylogue-fs1.2.1 Co-Authored-By: Claude <noreply@anthropic.com>
Problem: live ~/.hermes/state.db is at SCHEMA_VERSION=19 (checked read-only against a snapshot), nine versions past the v16 baseline fs1.1 verified. Nine real columns -- session_key/chat_id/chat_type/thread_id/display_name/ origin_json (v18 gateway metadata consolidation, #9006) and expiry_finalized/compression_failure_cooldown_until/compression_failure_error -- were structurally present but mapped to no capability group, so every Hermes session import silently dropped them regardless of schema, violating the "every unsupported column lands in the fidelity declaration" AC. What changed: added gateway_identity and compression_recovery capability groups, extended lifecycle with expiry_finalized, and wired all nine fields into the existing hermes_session_metadata event (same treatment as source/user_id/handoff_state). Added a schema-drift canary (test_hermes_state_schema_canary.py) pinning the live-verified v19 column set against the parser's capability maps so a future schema bump that adds an unmapped column fails loudly instead of silently vanishing. This repo's live install is CLI-only (no gateway data), so a fixture proves the mapping the same way the existing v16-vs-v17 fixture pair already does. Ref polylogue-fs1.1 Co-Authored-By: Claude <noreply@anthropic.com>
Problem: Hermes writes verification_events (command/canonical_command/ kind/scope/status/exit_code/output_summary) and verification_state (changed_paths per session/root) to verification_evidence.db -- its own claim-vs-evidence ledger, live-verified against ~/.hermes/. Polylogue only retained this SQLite artifact as a watched METADATA_DOCUMENT sidecar (parse_as_session=False); it was never normalized, so Hermes sessions carried none of the structured verification evidence the archive's claim-vs-evidence lanes are built around. What changed: new polylogue/sources/parsers/hermes_verification.py parses the real producer schema (schema v1, exit_code always known, kind in lint/typecheck/build/format/check/test/ad_hoc, scope targeted/full, status passed/failed) into a verification:<hermes_session_id> observer-evidence session -- same deferred-physical-merge pattern hermes_spans.py already established for ATOF/ATIF, not a new mechanism. command/canonical_command/ output_summary round-trip verbatim (this is Hermes's own recorded evidence, not conversational content, so ATOF/ATIF's payload-hygiene bound doesn't apply). A session_id of "default" (the producer's own fallback for unknown session identity) is marked ambiguous_correlation rather than trusted. Wired through every route state.db already uses: dispatch.py (marker detection/parsing), source_walk.py + source_parsing.py (live streaming ingest), live/batch.py + live/batch_support.py (full-batch ingest), import_explain.py (CLI dry-run). The artifact-taxonomy raw-path classification stays METADATA_DOCUMENT/parse_as_session=False (protects pre-JSON-decode consumers like schema sampling from raw SQLite bytes, matching state.db's split); the marker-payload classification is now SESSION_DOCUMENT/parse_as_session=True, matching state.db's own precedent exactly. import_explain.py's SQLite structural checks now run before the raw-path taxonomy skip-check, since that skip-check exists for a different consumer than the SQLite-specific parse routes. Fixed a pre-existing gap surfaced along the way: the state.db capability-map contract test (test_hermes_state_db_contract_matches_parser_capability_map) was silently stale after the prior v19 schema-drift commit added gateway_identity/compression_recovery capabilities -- testmon hadn't selected it. Updated state_db_v16.contract.json to match. Ref polylogue-wj25 Co-Authored-By: Claude <noreply@anthropic.com>
18f3266 to
2d17a8d
Compare
) ## Summary Continuation of the maximalist Hermes fidelity program (Ref polylogue-fs1.2.1, polylogue-wj25, polylogue-fs1.15) after PR #3092 merged. Closes out the remaining ATOF fs1.2.1 AC items that were tractable in-scope, and empirically investigates two `fs1.2.1`/`fs1.15` open questions that turned up two previously-unknown, confirmed live-ingestion bugs — both pinned as `xfail(strict=True)` regression witnesses with dedicated beads, not silently left undiscovered. ## Problem fs1.2.1's remaining AC explicitly flagged "append resume" and "unpaired events" as open concerns, and fs1.15 asked whether Hermes coverage/freshness already flows through the existing named-source freshness surface. Investigating these empirically (driving the real `LiveBatchProcessor`, not just re-reading code) surfaced: 1. **Unpaired ATOF scope debt was invisible.** A `kind=scope` UUID that never sees both its `start` and `end` phase (a crashed request, or a truncated/rotated file cutting a pending scope in half) was indistinguishable from a normal completed span. 2. **Confirmed data loss (polylogue-flxh):** real `~/.hermes/observability/nemo-relay/atof/events.jsonl` is ONE file shared across every Hermes session on an install. The live-ingest raw-revision-authority replay chain requires exactly one logical session per raw revision — correct for Claude Code/Codex/Beads (always one file, one session), but genuinely violated by ATOF's shared-file shape. A growth batch spanning a session boundary silently and permanently loses the pre-existing session's new event. 3. **Confirmed crash (polylogue-1zex):** a Hermes `state.db` or `verification_evidence.db` with exactly ONE session at ingest time crashes the live watcher's full-ingest path with `UnicodeDecodeError` — `revision_backfill.py`'s `_parse_one` (shared with historical repair) has zero SQLite awareness, unlike the two other Hermes call sites that correctly special-case it. Real installs almost always have 2+ sessions, which is presumably why this escaped the earlier Phase 0 review (PR #3084, merged). ## Solution Three commits: 1. `feat(hermes): surface ATOF unpaired scope UUIDs as explicit debt` — `parse_atof_stream` now tracks scope-phase pairing per UUID and emits a typed `hermes_atof_unpaired_scope` event for any UUID missing either phase, plus a new `unpaired_scope_debt` fidelity capability. Verified against the real fixture (`llm-error-1`, a genuine unpaired scope already present in the committed fixture) and two new synthetic tests (start-only, end-only, and a paired-is-not-flagged sanity check). 2. `fix(hermes): document and pin confirmed ATOF shared-file data-loss bug` — empirical regression test driving the real watcher through a session-boundary-spanning growth batch; direct sqlite inspection proves the loss. Filed polylogue-flxh with three candidate fix directions (none chosen — this is core shared ingest plumbing used by every live provider, deserves its own reviewed pass). Added a loud fidelity caveat to `hermes_spans.py`'s module docstring. 3. `test(hermes): prove named-source freshness coverage, pin single-session crash` — proves `project_named_source_freshness` (origin-agnostic, no new code needed) correctly covers Hermes state.db/ATOF once ingestion succeeds; separately confirms and pins the single-session SQLite crash. Filed polylogue-1zex. ## Verification - `devtools test tests/unit/sources/parsers/test_hermes_spans.py` — 19/19. - `devtools test tests/unit/sources/test_live_watcher.py` — 85 passed, 1 xfailed (expected). - `devtools test tests/unit/sources/test_hermes_source_freshness_integration.py` — 2 passed, 1 xfailed (expected). - `devtools verify --quick` green after every commit. Known residual gaps, tracked not silently dropped: polylogue-flxh and polylogue-1zex (both filed, neither fixed — both require careful design review of shared core ingest plumbing rather than a rushed patch in this pass). ATIF subagent topology materialization and verification-ledger session-id profile-qualification remain explicitly deferred design decisions (documented in prior session's bead notes), not attempted here since they'd change shipped public identity contracts without operator sign-off. --------- Co-authored-by: Claude <noreply@anthropic.com>
…cannot claim (#3173) ## Summary Adds `docs/hermes-operators.md`, a source-grounded operator guide answering the question a Hermes operator asks before pointing Polylogue at their local runtime: what does it watch, what fidelity does each artifact class get, and what does it explicitly not support yet. Written for the Nous Discord `#plugins-skills-and-skins` audience. ## Problem polylogue-lrou's stated prerequisite -- source fidelity + verification-ledger paths -- has shipped (PR #3084 Hermes runtime watcher coverage, #3092 verification_evidence.db structured import, #3120 verification-coverage correlation primitive), but no consolidated operator-facing document existed. Fidelity-visibility and privacy boundaries are the trust-building content this audience needs; scattered module docstrings are not discoverable by an external reader deciding whether to trust the bridge. ## Solution - `docs/hermes-operators.md` (guide tier), registered in `devtools/docs_surface.py`; `docs/README.md` regenerated via `devtools render docs-surface`. - Every claim cites current source: five-layer `sources.hermes.root` / `POLYLOGUE_HERMES_ROOT` config resolution (`polylogue/config.py`); watched source classes and acquisition methods (`polylogue/sources/live/watcher.py`, `polylogue/sources/dispatch.py`); the closed exact/degraded/absent/inferred/redacted fidelity vocabulary with a per-artifact capability table for `state.db`, ATIF, ATOF, and `verification_evidence.db` (`hermes_state.py`, `hermes_spans.py`, `hermes_verification.py`); the `observer:`/`verification:` session-identity scheme and its deliberately-deferred physical merge; verified query/ forensics entry points (`import --explain`, `find --origin hermes-session`, `read --view raw|transcript`, the MCP `query` tool's `origin` field, citing `docs/agent-manual.md`'s live 10-tool contract over the stale ~100-tool count in `docs/mcp-reference.md`); and local-only/no-network/per-class payload-hygiene privacy boundaries. - A dedicated "What Polylogue cannot claim" section lists nine real, current limitations -- the credibility core the bead asked for -- including one empirically verified while writing this doc: importing both the checked-in ATIF and ATOF fixtures (same underlying Hermes `session_id`) into a throwaway archive collapses to **one** archive session, carrying only the most-recently-materialized artifact's evidence (content-hash revision replace, not a union); the other artifact's raw bytes remain durably retained but are not part of the queryable session. No existing test asserts either behavior for this combination. - Also notes `hermes_verification_coverage` (fs1.4) has no CLI/MCP surface yet -- Python-only today, per that bead's own scoping note -- and that no named Hermes forensics report command exists. A `KeyError('rank')` crash in `read --view transcript` after a keyword `find` (origin-agnostic, first hit via a Hermes query) was discovered while gathering verification evidence; filed as `polylogue-ajmu` rather than fixed here -- out of this bead's scope. ## Verification - `devtools render docs-surface` then `devtools render all --check` -- every generated surface reports sync OK, exit 0. - `devtools verify --quick` -- format/lint/mypy/render-all-check green, including `verify doc-commands`, which validates every CLI example in the new doc against the live command/flag surface. - Every quoted command output (`import --explain` fidelity blocks for both ATIF and ATOF fixtures; `find --origin hermes-session ... read --all --format json`; `read --view transcript` / `--view raw --format json` against an exact ref) was run live against a throwaway `POLYLOGUE_ARCHIVE_ROOT` seeded only from `tests/fixtures/hermes/`, never a live archive. Ref polylogue-lrou Co-Authored-By: Claude <noreply@anthropic.com> Co-authored-by: Claude <noreply@anthropic.com>
## Summary Closes the remaining conformance-law gaps in the OriginSpec admission kernel (`polylogue/sources/origin_specs.py`) against every clause of `polylogue-2qx.1.1`'s acceptance criteria: a public-origin/Provider-token leak guard, a stream/parser binding parity check against dispatch, and a live proof of the `lifecycle="reserved"` admission path. ## Problem `polylogue-2qx.1.1` asked for the OriginSpec admission kernel (built on the `polylogue/declarations` DeclarationSpec kernel from `polylogue-o21.1`) plus a conformance law where missing parser, stream/parser conflict, ambiguous detector order, absent fixture, undeclared coverage, leaked Provider token, and non-injective Provider-to-Origin collision each yield a source-locatable diagnostic. Auditing the live repo found the kernel **already exists on master** — `polylogue/sources/origin_specs.py` landed incrementally via unrelated feature PRs (#3051, #3087, #3088, #3092, #3201, #3228: Claude Workflow admission, Hermes ATOF/ATIF, the Grok parser, provider_completeness migration) without ever citing this bead, so `bd`'s tracking never reflected it — the 2026-07-18 warroom sweep found no bead-tagged commits and reset the bead to open even though the code was there. It already covers all 11 `Origin` tokens, consumes the declarations kernel correctly, and is wired into `provider_completeness.py`, the artifact taxonomy, the live watcher, and Claude Code orchestration. Auditing it clause-by-clause against the AC found three real gaps: 1. No diagnostic caught a public origin `public_name` colliding with a raw `Provider`-wire token — the doctrine in `docs/provider-origin-identity.md` ("no provider wording on public origin surfaces") was enforced only indirectly (public_name must equal `origin.value`, and no current `Origin`/`Provider` pair happens to collide), with no explicit guard or diagnostic for a future collision. 2. No check parity-verified declared `stream_parser_path` presence against `sources/dispatch.py:STREAM_RECORD_PROVIDERS` — a stream/parser binding could silently drift from dispatch's actual streaming behavior with no diagnostic. 3. The module docstring still claimed a live "reserved origin" pilot, but Grok shipped a real parser (`polylogue-611`, #3201) after this bead was written, so no `OriginSpec` is currently `lifecycle="reserved"` and that admission path had no live proof. ## Solution - Added `_PROVIDER_TOKEN_VALUES` and a guard in `OriginSpecRegistry.register()` that rejects a declaration whose `public_name` collides with any `Provider` enum value, ordered before the existing public-name/origin equality check so it is independently testable (a synthetic declaration can trip it without also tripping the equality check). - Added `validate_stream_parser_parity()`, parity-checked against `sources/dispatch.py:STREAM_RECORD_PROVIDERS` the same way `validate_dispatch_precedence()` already parity-checks detector tightness order. **Zero edits to `dispatch.py`** — only a new read-only import of its existing `STREAM_RECORD_PROVIDERS` constant, respecting this slice's ownership boundary with the concurrent `ingest_worker.py`/`decoders.py` lane. - Updated the module docstring to honestly state Grok's promotion from reserved to executable, and point at the new synthetic reserved-lifecycle test as the live proof of that admission path (since no real production `Origin` is currently reserved). - Added five tests to `tests/unit/sources/test_origin_specs.py`: undeclared- coverage rejection, provider-token-leak rejection, a synthetic `lifecycle="reserved"` registration (built via `replace()` of the real Grok spec) proving the reserved admission contract requires no parser/detector tightness while still requiring coverage/fixture evidence, and positive plus negative stream/parser parity checks. ## Verification - `devtools test tests/unit/sources/test_origin_specs.py` → 8 passed - `devtools test tests/unit/sources/test_dispatch_ordering.py tests/unit/sources/test_dispatch_payloads.py` → 21 passed - `devtools test -k "provider_completeness or origin_specs or dispatch_ordering"` → 26 passed - `mypy --strict polylogue/sources/origin_specs.py tests/unit/sources/test_origin_specs.py` → Success, no issues found - `devtools verify --quick` → exit 0 (all 16 steps ok: format, lint, mypy, render all, topology, layering, closure-matrix, schema roundtrip, manifests, ci-workflows, doc-commands, docs-coverage, test-infra-currency, test-clock-hygiene, pytest-timeout-overrides, degrade-loudly) - No new module/file added, so no topology projection regen was required. ## AC matrix | # | AC | Status | | --- | --- | --- | | 1 | `origin_specs.py` defines complete typed fields/lifecycle states, consumes `polylogue/declarations` without adding source semantics to that kernel | Satisfied (pre-existing on master; verified by reading `polylogue/declarations/*` and confirming `origin_specs.py` only wraps `DeclarationSpec`/`DeclarationRegistry`, adding no kernel-layer semantics) | | 2 | Claude Code / ChatGPT / reserved Grok pilots derive/validate deterministic tightness, parser/assembly registration, public origin values, coverage/completeness rows, fixtures, docs, reparse consequences from one declaration each | Satisfied for Claude Code (executable, tightness 60, stream+artifact-rule bindings) and ChatGPT (executable, tightness 70). Grok itself is now executable (real parser shipped after this bead was written, #3201) rather than reserved; the reserved-lifecycle admission contract is proven live by this PR's new synthetic test rather than by a real reserved origin, since every current public `Origin` token is admitted as executable or compatibility-only | | 3 | `dispatch.py` uses derived registration/order or is parity-checked by it; adding a synthetic executable origin requires one OriginSpec + adapter + fixtures | Satisfied — `validate_dispatch_precedence()` (pre-existing) plus this PR's new `validate_stream_parser_parity()` parity-check `dispatch.py`'s `RECORD_DETECTOR_PROVIDER_ORDER`/`STREAM_RECORD_PROVIDERS` without restructuring dispatch.py | | 4 | Missing parser / stream-parser conflict / ambiguous or cyclic detector order / absent fixture / undeclared coverage / leaked Provider token / non-injective collision each yield a source-locatable diagnostic + repair | Satisfied — missing parser, absent fixture, and non-injective collision without policy were pre-existing `ValueError` raises (now also covered by a dedicated undeclared-coverage test in this PR); ambiguous detector order was pre-existing (`ambiguous_detector_tightness`); this PR adds the previously-missing stream/parser-conflict diagnostic (`stream_parser_parity_mismatch`) and leaked-Provider-token guard. "Cyclic" detector order has no distinct failure mode beyond ambiguous tightness, since tightness is a total order, not a graph | | 5 | Provider stays in adapters, Origin is the public vocabulary, Gemini/Drive-style collisions require an explicit collision policy | Satisfied (pre-existing `collision_policy` field + registration guard, exercised by `aistudio-drive`'s `Provider.GEMINI`/`Provider.DRIVE` fiber) | | 6 | Focused declaration/dispatch/public-schema/completeness/fixture/mutation/render/quick verification passes | Satisfied — see Verification section above | `polylogue-2qx.1.2` (migrate every current origin onto OriginSpec, delete parallel inventories) is explicitly out of this slice's scope per the bead's own design text — it is largely already superseded in practice too (all 11 origins are already declared here and `provider_completeness.py` already projects from `ORIGIN_SPECS`), but a formal audit of that bead's own AC (exact-eleven-token enumeration, parallel-inventory deletion, dispatch golden fixture) is left to whoever picks it up. Ref polylogue-2qx.1.1 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Prevented origin names from exposing internal provider terminology. * Added validation to ensure origin evidence is complete before registration. * Improved consistency checks for origins that support streaming data. * **New Features** * Added support for reserved-lifecycle origins used in fixtures, including required supporting evidence without requiring parser configuration. * Added diagnostics for mismatches between streaming capabilities and origin configuration. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude <noreply@anthropic.com>
…gue-2qx.1.2) (#3250) ## Summary Audits and closes the remaining gap in polylogue-2qx.1.2 ("migrate every current origin onto OriginSpec"). Most of this bead's scope was already shipped to `master` untagged across #3051/#3087/#3088/#3092/#3201/#3228/#3246 (closing `polylogue-2qx.1.1`'s notes confirm this). This PR verifies that prior work end-to-end, proves the parity checks actually catch regressions (not just pass today), and closes one concrete residual gap: a second, un-declared provider-to-assembly-hook dispatch that Claude Code, Codex, and AI Studio/Drive depend on. ## Problem `polylogue/sources/origin_specs.py` already declares all eleven `Origin` tokens with lifecycle/detector-tightness/parser/fixture/coverage bindings, and `polylogue/sources/provider_completeness.py` already derives its rows from `ORIGIN_SPECS` (`PACKAGE_MODE_SPECS` is a tuple comprehension over it, parity-proven by `test_provider_completeness_is_a_projection_of_every_origin_spec`). Dispatch-order and stream-parser-binding parity are also already mechanically checked (`validate_dispatch_precedence`, `validate_stream_parser_parity`) against the real production values in `sources/dispatch.py` (`RECORD_DETECTOR_PROVIDER_ORDER`, `STREAM_RECORD_PROVIDERS`). What was genuinely missing: the bead's design explicitly calls for "Claude Code and Codex specs [to] expose typed assembly/orchestration/title/action extension hooks (consumed later by 2qx.2, j2zz, ih67) — a typed field on the spec, not another registry." `polylogue.sources.assembly.get_assembly_spec()` is exactly that second, un-declared provider dispatch (`ProviderAssemblySpec` factory for `Provider.CLAUDE_CODE`/`Provider.CODEX`/ `Provider.GEMINI`) — it existed in production but had no OriginSpec declaration or parity check, so a caller had no way to discover the hook from `origin_specs.py` and no test would catch it drifting from the real registry. Also: `docs/provider-origin-identity.md` documented Provider/Origin/Source doctrine but never pointed at `origin_specs.py` as the per-origin admission authority a reader should trust over any hand-maintained list. ## Solution - `polylogue/sources/origin_specs.py`: added `OriginSpec.assembly_spec_path` (`"module/path.py:ClassName"` or `None`) declaring which `ProviderAssemblySpec` a given origin's provider wire binds. Populated for the three origins with a real live hook: `claude-code-session` → `assembly_claude_code.py:ClaudeCodeAssemblySpec`, `codex-session` → `assembly_codex.py:CodexAssemblySpec`, `aistudio-drive` → `assembly_gemini.py:GeminiAssemblySpec` (its `Provider.GEMINI` wire is the one `get_assembly_spec` keys on). Added `validate_assembly_spec_parity()`, following the exact idiom of `validate_dispatch_precedence`/ `validate_stream_parser_parity`: it checks the declared field against the live `get_assembly_spec(provider)` return value rather than replacing that registry with a second one in `origin_specs.py`. - `tests/unit/sources/test_origin_specs.py`: two new tests — one asserting the three declared hook paths (and `None` for two origins without a live hook), one asserting `validate_assembly_spec_parity(get_assembly_spec) == ()` plus the mutation shape (a resolver that always returns `None` makes every origin that declares a hook path report `assembly_spec_parity_mismatch`). - `docs/provider-origin-identity.md`: added an "Origin admission" section naming `origin_specs.py` as the per-origin declaration authority and pointing at both live-production parity checks. - No parallel hand-maintained inventory was deleted, because the audit found none left inside my ownership (`polylogue/sources/**`) — see the residual below for the one still outstanding elsewhere. ### Residual found outside owned scope `polylogue/cli/shell_completion_values.py:_ORIGIN_DESCRIPTIONS` is a hand-maintained `dict[str, str]` of 8 of the 11 origin tokens (missing `beads-issue`, `grok-export`, `unknown-export`) used for shell-completion help text, and it is **not** parity-checked against `Origin`/`ORIGIN_SPECS` the way `polylogue/agent_integration/spec.py:ORIGIN_MEANINGS` already is (that one has a `RuntimeError` guard requiring exact `Origin`-enum order match — already satisfies AC4, no action needed there). `cli/**` is out of this lane's ownership (per dispatch instructions, another lane owns it concurrently); flagging as a follow-up rather than touching it here. ## Verification - `devtools test tests/unit/sources/test_origin_specs.py tests/unit/sources/test_provider_completeness.py` → **15 passed** - `devtools test tests/unit/sources/ -k "origin_specs or provider_completeness or dispatch or assembly"` → **144 passed** - `devtools test tests/unit/sources/test_parsers_props.py` (protected file) → **43 passed** - `devtools verify --quick` → all 16 steps `ok`, `"exit_code": 0` (ruff format/check, mypy --strict, render all, topology/layering/closure-matrix, manifests, ci-workflows, doc-commands, docs-coverage, test-infra-currency, test-clock-hygiene, pytest-timeout-overrides, degrade-loudly) - Anti-vacuity mutations, run manually then reverted (`git checkout --`), each confirmed the *existing* production dependency actually fails on the mutation before I added anything: - Removing `_grok_spec()` from the registration loop → `ORIGIN_SPEC_REGISTRY.diagnostics()` reports `missing_origin_spec` for `grok-export`. - Dropping Claude Code's `detector_tightness` below `gemini_cli_session`'s (contradicting its real last-in-branch-order position) → `validate_dispatch_precedence(RECORD_DETECTOR_PROVIDER_ORDER)` reports `dispatch_tightness_mismatch`. - Deleting Codex's new `assembly_spec_path` declaration → `validate_assembly_spec_parity(get_assembly_spec)` reports `assembly_spec_parity_mismatch` for `codex-session` (this is the mutation the new code in this PR is proven against). ## AC matrix (polylogue-2qx.1.2) 1. **Satisfied (pre-existing).** All eleven `Origin` tokens are declared exactly once in `ORIGIN_SPECS`; `set(by_origin) == set(Origin)` is asserted in `test_origin_specs_cover_the_public_enum_and_admission_lifecycles`, and `OriginSpecRegistry.diagnostics()` emits `missing_origin_spec` for any omission (proven above). 2. **Satisfied (pre-existing + this PR).** Every executable origin declares acquisition modes, detector/parser/stream-parser/fixture/coverage/fidelity/reparse fields; `claude-code-session` additionally declares 6 typed `OriginArtifactRule`s for its workflow/journal/transcript/sidecar/adopt-manifest/coordinator-stream family. `grok-export` is `lifecycle="executable"` today (it shipped a real parser via #3201/polylogue-611 after 2qx.1.1's pilot design assumed it would stay reserved — documented at the top of `origin_specs.py`); the `reserved` lifecycle contract itself is proven against a synthetic variant since no current `Origin` is genuinely reserved. `unknown-export` is `lifecycle="compatibility-only"` with explicit fallback semantics; `beads-issue` is executable with `issue-jsonl` acquisition and its own non-`accepted` completeness maturity caveat (no harvested schema sample yet). This PR adds the Claude/Codex/AI-Studio assembly hook declaration. 3. **Satisfied (pre-existing).** `validate_dispatch_precedence`/`validate_stream_parser_parity` parity-check tightness and stream-binding against `sources/dispatch.py`'s real `RECORD_DETECTOR_PROVIDER_ORDER`/`STREAM_RECORD_PROVIDERS`; mutation-proven above (deleting/reordering a declaration fails). 4. **Satisfied (pre-existing) for `provider_completeness.py`; this PR extends it to the assembly-hook axis.** `PACKAGE_MODE_SPECS` is a direct tuple comprehension over `ORIGIN_SPECS`, parity-proven by `test_provider_completeness_is_a_projection_of_every_origin_spec`. `agent_integration/spec.py:ORIGIN_MEANINGS` is separately parity-guarded against `Origin` at import time. **Deferred**: `cli/shell_completion_values.py:_ORIGIN_DESCRIPTIONS` remains a stale, un-parity-checked hand-maintained inventory — out of this lane's ownership (`cli/**`), flagged as a residual above rather than a named follow-up bead (small, mechanical, single-file fix for whichever lane next touches CLI completions). 5. **Satisfied (pre-existing).** `aistudio-drive`'s `provider_wires=(Provider.GEMINI, Provider.DRIVE)` with `collision_policy` is registration-enforced (`OriginSpecRegistry.register` raises `ValueError` if `len(provider_wires) > 1` without a `collision_policy`, tested in `test_origin_spec_rejects_missing_fixture_and_noninjective_collision_without_policy`); `docs/provider-origin-identity.md` already documented the non-reversal doctrine, and this PR adds the pointer from that doc to the enforcing code. 6. **Misframed — recorded honestly rather than claimed satisfied.** `polylogue-2qx.2` (Claude Code orchestration artifact admission) already closed via PR #3088, and its own close reason states it shipped as a `claude_workflow_materializer` convergence stage *without* the OriginSpec migration this bead was meant to gate — the blocking edge was explicitly force-closed as "over-blocking" because the merged implementation disproves that ordering. So AC6 as written ("Claude/Codex extension hooks are the only admission path used by 2qx.2...") cannot be retroactively true for `2qx.2`. `j2zz` (Codex child-action lowering) and `ih67` (Codex title enrichment) are still open; this PR's new `assembly_spec_path`/`validate_assembly_spec_parity` gives them a real typed admission point to consume going forward, but I did not modify `assembly_codex.py`/`ih67`/`j2zz` scope — those remain their own beads' work, not migrated by me here. ## Files touched - `polylogue/sources/origin_specs.py` - `tests/unit/sources/test_origin_specs.py` - `docs/provider-origin-identity.md` Ref polylogue-2qx.1.2 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Documentation** - Added guidance on how origin metadata is declared, validated, and kept aligned with runtime behavior. - Documented checks for provider completeness and assembly-related configuration. - **Improvements** - Added validation to detect mismatches between declared origin capabilities and available assembly behavior. - Added assembly configuration metadata for Claude Code, Codex, and AI Studio origins. - **Tests** - Added coverage confirming assembly declarations and runtime parity checks behave as expected. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude <noreply@anthropic.com>
Summary
Continuation of PR #3087 (merged as fee0799 with only its first commit — this branch grew four more commits afterward that never made it into that squash-merge, since #3087 was merged mid-session while this work was still landing). Admits real ATOF lifecycle events, proves ATIF tool_calls fidelity against real bytes, closes a real state.db v16→v19 schema-drift gap, and imports Hermes's own verification_evidence.db claim-vs-evidence ledger as structured outcomes (Ref polylogue-fs1.2.1, polylogue-wj25).
Problem
kind=scope,category=agent, andhermes.session.endmarks) fell into the generic unrecognized-event bucket despite being present, unused, in the already-committed real ATOF fixture.tool_execution_spansfidelity stayed "inferred" despite real trajectories with thetool_calls/observationshape sitting on disk.~/.hermes/state.dbschema is atSCHEMA_VERSION=19, nine versions past the v16 baseline previously verified — nine real columns (gateway-routing identity from Hermes's #9006 chat-platform bridging, compression-retry state) were silently dropped by every import.verification_evidence.db— Hermes's own command/exit-code/status verification ledger — was retained as a watched artifact (feat(daemon): watch Hermes runtime evidence #3084) but never parsed, so Hermes sessions carried none of the structured verification evidence the archive's claim-vs-evidence lanes are built around.Solution
Four commits (
b96cc97d1,c8db6706e,1d716e985,18f326632):hermes_spans.py):kind=scope,category=agentandhermes.session.*marks now normalize to typedhermes_context_spanevidence.context_eventsfidelity moves to exact;unrecognized_atof_eventsdrops to zero on the real fixture.tool_calls+ correlatedobservation.results, drawn from a separate live trajectory) to the checked-in ATIF fixture.tool_execution_spansnow reports exact fidelity likellm_request_spans. Addedtests/fixtures/hermes/atif/README.mddocumenting provenance (the ATOF fixture already had one).hermes_state.py): addedgateway_identityandcompression_recoverycapability groups, extendedlifecyclewithexpiry_finalized, wired all fields into the existinghermes_session_metadataevent. Added a schema-drift canary test pinning the live-verified v19 column set against the parser's capability maps, and fixed a pre-existing stale contract file (state_db_v16.contract.json) that testmon hadn't caught.hermes_verification.py): parsesverification_events/verification_stateinto averification:<hermes_session_id>observer session — same deferred-physical-merge pattern as ATOF/ATIF.command/canonical_command/output_summaryround-trip verbatim (this is Hermes's own recorded evidence, not conversational content, so ATOF/ATIF's payload-hygiene bound doesn't apply — documented in the module docstring). Wired through every routestate.dbalready uses (dispatch, source_walk/source_parsing live ingest, live/batch full-batch ingest, import-explain CLI).session_id="default"(the producer's own unknown-identity fallback) is markedambiguous_correlation, never trusted. Artifact-taxonomy split follows state.db's own precedent exactly: raw-path classification stays non-session (protects schema sampling from raw SQLite bytes), marker-payload classification is session-eligible.Verification
devtools test tests/unit/sources/parsers/test_hermes_spans.py— 17/17.devtools test tests/unit/sources/parsers/test_hermes_verification.py— 9/9.devtools test tests/unit/sources/parsers/test_hermes_state_schema_canary.py— 2/2.devtools verify --quickgreen after every commit.Known honest gaps, not silently assumed: ATIF
subagent_delegationstays "inferred" (no real export sampled has a non-emptysubagent_trajectories); the verification-ledger session id has no profile-qualification (same accepted gap ATOF/ATIF'sobserver:id already has); ATOF partial-final-line/rotation-truncation cursor behavior and unpaired-event debt remain deferred (fs1.2.1's remaining AC).Ref polylogue-fs1.2.1
Ref polylogue-wj25