Skip to content

feat(memory): injection receipts + blame path (T1+T2+T3, decision 4255039) - #83

Merged
cdeust merged 5 commits into
mainfrom
feat/injection-receipts-t1
Jul 7, 2026
Merged

feat(memory): injection receipts + blame path (T1+T2+T3, decision 4255039)#83
cdeust merged 5 commits into
mainfrom
feat/injection-receipts-t1

Conversation

@cdeust

@cdeust cdeust commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Summary

Implements the blame path (decision 4255039) in three slices, rebased on main post-PR82 (bf7872e8, supersede-write-path) — receipts and supersession cohabit cleanly.

  • T1 — injection receipts, write path (2e13baf9): every memory injected into context carries a ⟦rcpt:N⟧ receipt recording what was injected, where, and why.
  • T2 — hook channels (6079b513): receipts wired through the injection hook channels.
  • T3 — why tool resolves receipts (63380af6): the why tool resolves ⟦rcpt:N⟧ markers back to their originating memories, closing the blame loop.
  • I2 invariant refresh (152865f3): heat-writers allow-list line numbers recomputed deterministically against the rebased tree via the test's own _scan_heat_writers() (pg_store.py 694/754, sqlite_store.py 384/428).

Verification

  • Full suite post-rebase: 4136 passed, 0 failed (6:16). Pre-rebase reference: 4122 passed — the +14 tests come from main/PR82; no receipts test regressed.
  • Targeted post-rebase: 44 receipts/why/test_main tests green + 28 PR82 supersession tests green (cohabitation verified).
  • 39 I2 invariants green after allow-list recomputation.
  • 13 review findings applied across the change set.

Notes

  • Single rebase conflict (I2 allow-list, double re-pin vs main 9d204a04) resolved by deterministic recomputation — neither ours nor theirs was correct.

🤖 Generated with Claude Code

https://claude.ai/code/session_013NkxreafqWnvPCwrL6KJEs

cdeust and others added 5 commits July 7, 2026 14:53
…5039)

Every recall response now emits an append-only injection receipt
mirroring exactly the bound payload (emitted AFTER bound_payload:
transcript-DB parity invariant). Presence-in-context evidence only,
never causality.

- injection_receipts + injection_receipt_items DDL, both backends
  (session_id NULLable; memory_id deliberately unconstrained so a
  hard-forget never rewrites the audit trail)
- PgReceiptsMixin: single data-modifying-CTE INSERT — _execute borrows
  a pool connection per call, two statements would lose atomicity
- emit_injection_receipt: I/O failure is the only degradation mode;
  a missing memory_id is an upstream contract violation and raises
- receipt_id surfaced in the recall response + outputSchema

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013NkxreafqWnvPCwrL6KJEs
…s (decision 4255039)

Every hook channel injecting memory content now emits an append-only
receipt at injection time, with the ⟦rcpt:id⟧ marker carried inside the
injected text so the model can hand receipt ids back to cortex:why
(receipt-based primary path — server-side session-temporal resolution
does not exist in MCP).

- session_start banner, auto_recall (UserPromptSubmit) and agent_briefing
  (SubagentStart — the forgotten 4th channel, correction 3) each emit one
  receipt mirroring exactly what is printed: auto_recall's char-budget
  drops are excluded from the receipt items (parity invariant,
  correction 11; mutation-tested), truncated entries keep their id.
- channel enum hardened to {recall, session_start, auto_recall,
  agent_briefing}: loud app-level validation, DDL CHECK on both backends,
  PG migration for T1-created free-TEXT tables (exercised live in test),
  code↔DDL parity asserted by test. SQLite pre-T2 tables keep free TEXT
  (CHECK needs a table rebuild — same stance as the non-enforced FK
  migrations); the emitter validation is the enforcement point there.
- session_id = transcript_path basename (correction 7 — the event's
  session_id field diverges 148/200 lines on fixture 7374abf5); absent
  or non-string transcript_path degrades to NULL at the hook boundary.
- superseded_by_id IS NULL lock on every hook injection query
  (correction 8): banner anchors/team/hot, briefing both passes,
  auto_recall FTS.
- receipt-write I/O failure degrades to a marker-less injection; the
  hook's primary output never breaks.

Adversarially reviewed (3-lens multi-agent pass, findings verified by
refutation): 9 confirmed findings fixed, incl. budget-drop parity test,
live PG CHECK + migration tests, banner-order assertion anchored to the
rendered text, receipts tables added to test cleanup, stub script row
contract. 1139 tests green (hooks/handlers/infrastructure/integration).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013NkxreafqWnvPCwrL6KJEs
…cision 4255039)

Adversarial review applied: receipt_ids bounded at 999
(SQLITE_MAX_VARIABLE_NUMBER, sqlite.org/limits.html §9) with uniform
rejection above int4, O(n) set-based dedup with precomputed
resolved_ids, documented tiebreak "receipt id DESC" in commands/why.md.
Full suite green: 4122 passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013NkxreafqWnvPCwrL6KJEs
…s landing

Pre-existing drift from commit 9917e17f (tranche 1): canonical-writer
lines shifted 581→583, 641→643, 301→303, 345→347.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013NkxreafqWnvPCwrL6KJEs
CI lint pins nothing and pulled ruff 0.15.20, whose formatter diverges
from the local 0.15.6 on 8 receipts files; reformatted with 0.15.20.
Renamed the ambiguous comprehension variable `l` to `text` in
test_hook_receipts.py (E741). No behavior change — 61 affected tests
pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013NkxreafqWnvPCwrL6KJEs
@cdeust
cdeust merged commit 84e911c into main Jul 7, 2026
13 checks passed
@cdeust
cdeust deleted the feat/injection-receipts-t1 branch July 7, 2026 13:54
pull Bot pushed a commit to asleekgeek/Cortex that referenced this pull request Jul 7, 2026
The Lint job installed ruff unpinned, so CI silently tracked the latest
release while local environments lagged. Formatter output changes across
ruff minor versions: PR cdeust#83 hit a 0.15.6-local vs 0.15.20-CI divergence
where CI reported 8 files "would reformat" that were clean locally.

Pin the CI install to 0.15.20 — the version the repo is currently
formatted with (verified: `ruff@0.15.20 format --check .` passes on 769
files). Future upgrades bump the pin and reformat in the same commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013NkxreafqWnvPCwrL6KJEs
pull Bot pushed a commit to asleekgeek/Cortex that referenced this pull request Jul 7, 2026
Re-ink the README banner and the 9 docs diagrams to the AI Architect
Proof Dossier system: paper surface (paper-0/sheet), warm ink (pfg-0/2),
terracotta accent (accent-deep), ruled hairlines, and the data-only
stage palette. Every colour literal traces to a DS token; oklch->sRGB
conversions are scripted (CSS Color 4 math), not eyeballed — the legend
in each file documents the mapping.

banner.svg also carries the corrected benchmark headline numbers
(same values as PR cdeust#83's 9f90291); whichever merges second resolves
the overlap by taking this file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013NkxreafqWnvPCwrL6KJEs
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