Commit graph

7 commits

Author SHA1 Message Date
Claude
9f2c50e285
fix(agent-memory): address automated review; harden the session-id fallback
Five of six findings were real. Verified each against the code rather than
taking them at face value; one was wrong and is noted below.

1. validate_examples.py carried a stale header from the spec-only phase --
   "PARKED AS AN ASSET (deliberately not .py yet)... ON IMPLEMENTATION: rename
   to skills/agent-memory/scripts/validate_examples.py". The file is already at
   exactly that path. Rewritten to describe what it now is, keeping the
   substantive part: why it exists, the seven check families, why it compares
   the doc's algorithm by source text rather than exec()-ing a code fence, and
   that nothing runs it automatically.

2. Missing shebang -- added. The same finding also claimed mode 100644; that
   half is wrong, `git ls-files -s` shows 100755 for all five scripts.

3. hooks.json's description still opened "CONTRACT ONLY -- the referenced
   scripts are not yet implemented", true of none of them now. Trimmed to keep
   only the UserPromptSubmit provisionality (9.5 is genuinely still open) and
   to record the measured latency alongside it.

4. The session-id fallback was the finding worth the most. `session_id` is the
   right key -- engineering/security-guidance's shipped hook reads the same one
   -- but the fallback was the CONSTANT "unknown-session", and sessions dedupe
   by value. Had the key ever been absent, every session would collapse onto
   one id, len(set(sessions)) would plateau at 1, and every claim would cap at
   L1 forever with no error anywhere. Now falls back to the transcript's own
   basename, which IS the session id. Verified end-to-end with session_id
   omitted from the payload: the atom records the real session UUID.

5. Dead `now` parameter on _eligible_l1 -- removed.

6. marketplace.json metadata still said 104 agents / 120 slash commands,
   pre-existing drift on a line this branch already edits. Trued up to 110/130.

Re-verified after: 69 checks 0 failures, SKILL.md 6/6 PASS, both blocking gates
still fire by name, check_paths 620 files clean, counters and plugin-json pass,
all 5 scripts --help, all 3 hooks parse.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EM5xmJ7AmTMg31rq68BCym
2026-08-24 18:45:52 +00:00
Claude
38a23f1911
docs(agent-memory): state the redaction gate, the reverse join, quote hook paths
Round-44 review, three findings, all verified before fixing.

1. The schema's `redacted` description has always asserted "never
   promoted to a committed tier without human review" -- a real gate that
   DESIGN.md stated nowhere. Confirmed by grep. Added to 4.1 with the
   reason the flag carries: redacted:true means the pass ALTERED the
   claim, which is positive evidence the source was sensitive, and
   redaction is lexical so finding one thing is not proof of finding
   everything. Recurrence cannot substitute -- three sightings of a
   scrubbed claim are three sightings of the same unresolved risk.

2. hooks.json did not quote ${CLAUDE_PLUGIN_ROOT} while both precedents
   this PR cites do (handoff: python3 "${...}/hooks/session_start.py";
   skillopt-sleep: "${...}/hooks/on-session-end.sh"). A path with a space
   would break the command. Quoted all three.

3. 4.2.1 says "both sit at L1" but the schema marks only the older atom
   (contested, contested_by), so the promotion gate cannot be a field
   read on both sides. Specified the reverse join: blocked if own
   `contested` is set OR own id appears in another atom's contested_by.
   Deliberately not a mirrored `contests` field -- same fact in two
   places, needing sync, with nothing able to say which copy is right.
   Cheap by construction: 5.2 caps the store at 500 atoms and measured a
   full pass at 2-3ms.

Did NOT add a schema->doc dangling-section-ref check. Wrote one, it
reported 3.1.1 and 4.1.2 as dangling, and both exist -- my heading regex
required a trailing period that sub-sections do not carry. Re-ran
correctly: zero dangling refs. A brittle checker for an empty class,
which I got wrong twice inside two minutes, is worse than no checker.

Verified: hooks.json parses; 69 checks, 0 failures; derive_counters.py
--check passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EM5xmJ7AmTMg31rq68BCym
2026-08-21 09:39:56 +00:00
Claude
67a1228828
docs(agent-memory): mark UserPromptSubmit provisional in hooks.json itself
Round-38 review noticed that the committed contract files already encode
an answer to some of 9's open decisions -- hooks.json wires
UserPromptSubmit even though 9.5's option (c) is to delete that hook.

The doc knew: 9.5 already says "if (c) wins, hooks.json must shrink too."
But that only helps a reader holding both files. Read on its own a
hooks.json says "these three hooks exist", which is precisely the wrong
impression, and a contract file is exactly the artifact someone reads on
its own before implementing. Same lesson as round 36's contested-tag gap,
one level up: a cross-reference is not a contract, and that applies
between files as much as between sections.

hooks.json's own description now marks the entry PROVISIONAL, names the
open decision, gives the reason (the budget is dominated by interpreter
cold-start, not by the script's work), and says plainly that listing the
hook is a contract for the shape it would take IF it survives -- not
evidence the decision was made. SessionStart and SessionEnd are marked
not provisional so the warning stays scoped.

Verified: hooks.json parses; 69 checks, 0 failures; derive_counters.py
--check unchanged (363/89/663).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EM5xmJ7AmTMg31rq68BCym
2026-08-09 07:57:13 +00:00
Claude
ea33484467
docs(agent-memory): 5.1 conflict constraint, env-var consistency, CI snippet
Round-28 review. Three changes, two held.

5.1 now carries the consequence 9.6 leaves live: L2 and L3 are injected
together and 4.2.1's detector cannot reach L3, so nothing upstream
guarantees they agree. Stated as a constraint on the hook -- never emit
two colliding claims as plain assertions -- rather than a mechanism, so
it holds under all three of 9.6's candidates. The reviewer is right that
retrofitting conflict-marking after session_start.py ships costs more
than honouring it in the first version; that does not require settling
9.6 itself, only refusing to ship the unmarked case.

AGENT_MEMORY_RECALL -> AGENT_MEMORY_USERPROMPTSUBMIT, in DESIGN.md and
hooks.json. The old justification (shorter; matches section 3's
vocabulary) traded away a property worth more: with all three vars
mirroring their hook name, a user who knows Claude Code's hook names can
derive all three without reading this doc. Three vars under two
conventions also invites the typo report the reviewer predicts.

Added the exact CI workflow snippet to 10.1. Still not wiring it into
ci-quality-gate.yml -- that runs on every PR in the repo, for a folder
9.3 permits deleting after a two-week trial -- but saying yes now costs
one paste rather than a design conversation.

Held: the placement decision, and the suggestion to split mechanical
rationale into references/. The second turns out to be blocked by the
first, which was worth measuring: a references/*.md under
engineering/agent-memory/ moves the references counter 746 -> 747, while
the same file under audit/ is pruned and free. The status header now
tables that alongside the parking hack and the double-relocation cost --
three open items resolving from one decision, which is the argument for
answering it before the implementation PR.

Verified: 67 checks, 0 failures; hooks.json parses; derive_counters.py
--check unchanged (363/89/663).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EM5xmJ7AmTMg31rq68BCym
2026-08-09 06:59:32 +00:00
Claude
4247f41ea3
docs(agent-memory): close round-22 review findings
Three findings from the automated review, all contract-level:

- memory_schema.json: the tier=L3 -> promoted_from_projects conditional
  was one-sided, so an L1/L2 atom carrying promoted_from_projects was
  accepted. Added the else branch forbidding it outside L3 — the field
  is not merely unnecessary there, it is meaningless, since scope is
  still `project` and the array would claim cross-project evidence the
  atom does not have.

- hooks.json: SessionStart declared no timeout and fell back to the
  Claude Code default. Pinned to 5s. UserPromptSubmit keeps its 1s
  backstop; SessionEnd stays async.

- DESIGN.md 4.1.1: state that the L2 -> L3 merge is lexical. normalize()
  collapses whitespace/case/punctuation only, so two projects holding
  the same rule in different words never merge. The failure is
  one-directional — L3 under-fires, the claim stays live at L2 in each
  project — and widening it needs an LLM or a per-user synonym table,
  neither of which belongs in v1. Named in the same style as 4.2.1's
  contradiction-detector limits.

Verified: assets/validate_examples.py.txt 57 checks / 0 failures; all
three schema examples still validate; an L1 atom carrying
promoted_from_projects is now rejected; derive_counters.py --check
passes unchanged (363 skills, 89 plugins, 663 tools).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EM5xmJ7AmTMg31rq68BCym
2026-08-09 06:12:49 +00:00
Claude
df43ed65c7
docs(agent-memory): fix spec/contract drift found in review
Six findings from automated review, all verified against the files before
acting. None architectural; all resolved in place.

1. Latency budget contradicted its own contract. DESIGN.md led with a
   "hard 100 ms budget" while hooks.json set "timeout": 1 — the hook
   timeout field is in SECONDS, so the contract permitted 10x the stated
   budget. Split into two explicitly-named limits: a 100 ms internal
   self-budget the script enforces against a monotonic clock, and the 1 s
   hook timeout as a wedged-process backstop. States outright that
   finishing under 1 s does not satisfy the spec.

2. The 100 ms budget was asserted with no mechanism to reach it. Bounded
   the work: .memory/atoms.jsonl capped at 500 atoms with last_seen
   eviction, single linear pass, bounded top-5 heap. Added open decision
   9.5 requiring the budget be MEASURED before implementation, since
   interpreter cold-start is the dominant cost and is not controllable
   from inside the script — and naming "drop UserPromptSubmit entirely"
   as an acceptable outcome. A recall hook that misses its budget every
   prompt is worse than no recall hook.

3. The section 3.1 atom example omitted the required `tier` field, so a
   reader could copy an invalid atom out of the doc that is meant to BE
   the contract. Added; verification now parses every JSON block in
   DESIGN.md, not only the schema's own examples, which is why this
   drifted undetected.

4. Session ids were 8 chars in the schema examples and 24 in DESIGN.md.
   Normalized to 24 everywhere; check asserts a single length across all
   examples.

5. Schema $id was not a resolvable URL (GitHub blob path missing /blob/
   <ref>/), which fails silently if tooling ever resolves it for $ref.
   Now a raw.githubusercontent.com URL.

6. The "stated" fast path (2 sessions instead of 3) did not say whether
   the >= 2-distinct-days clause survived, so one long working day could
   have minted an L2 claim. Clause explicitly retained; "verified"
   documented as the only exemption.

Counters unchanged (362 skills / 88 plugins) — still no SKILL.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EM5xmJ7AmTMg31rq68BCym
2026-08-09 03:49:18 +00:00
Claude
775cf3b14f
docs(agent-memory): spec L0-L3 tiered memory as a Claude Code hook layer
Design spec only — no SKILL.md, no plugin.json, no Python. Repo counters
are deliberately untouched (derive_counters counts skills by SKILL.md).

Derived from an inspection of TencentCloud/TencentDB-Agent-Memory (MIT).
Borrows two design ideas — the L0->L3 memory tiering and the
ownership/visibility model — and rejects its integration mechanism. No
code vendored.

The core idea: flat CLAUDE.md has exactly one injection policy (always,
in full), which causes bloat, staleness, and false permanence. Tiering
splits memory by durability and gives each tier its own retrieval policy:
L0 transcripts never injected, L1 atoms recalled on relevance, L2
injected per-project at SessionStart, L3 always in context under a cap.

Promotion is deterministic and recurrence-based (>= 3 distinct sessions
across >= 2 days for L1->L2), not importance-based, and requires a live
L0 back-pointer — keeping it stdlib-only per the no-LLM-in-scripts rule.

Includes a grounded overlap analysis against existing skills. Notably
skillopt-sleep already implements the L0 reader (harvest.py walks
~/.claude/projects/*/*.jsonl) and the protected-marker-block write; the
delta is tiering, prompt-time recall, and a durability gate. Spec
concludes agent-memory must be a separate self-contained plugin rather
than an extension, since skillopt-sleep is a vendored copy carrying 23
re-vendor deviations, and cross-skill imports are a repo anti-pattern.

Rejects MemoryProxy (ANTHROPIC_BASE_URL interception) on four grounds:
reverse-engineered CC internals, subscription-to-metered billing change,
raw conversation persistence incompatible with the repo's compliance
posture, and zero test coverage.

Files: DESIGN.md, hooks/hooks.json (contract), assets/memory_schema.json.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EM5xmJ7AmTMg31rq68BCym
2026-08-09 03:41:44 +00:00