Skip to content

fix: unify memory-dir resolution and harden release gates (0.2.1 WIP) - #2

Closed
grpcer wants to merge 5 commits into
mainfrom
fix/0.2.1-hardening
Closed

grpcer wants to merge 5 commits into
mainfrom
fix/0.2.1-hardening

Conversation

@grpcer

@grpcer grpcer commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Observation-period WIP for the next release — do not merge until dogfood acceptance completes. Supersedes #1 (auto-closed by an accidental, immediately-reverted push to main; all context and the machine-bootstrap comment live there).

All 23 review defects are fixed and verified; CI was green on all 5 lanes (run 32498277237). This branch now also carries the observation runbook as a repo-owned .ownmem topic plus the generated agent entry files (CLAUDE.md, MEMORY_INSTRUCTIONS.md, .claude/), so a fresh clone bootstraps an agent with full context through the repository's own memory — the product dogfooding itself.

New-machine bootstrap: clone this branch, then follow .ownmem/observation_period_bootstrap.md (or #1 (comment)).

🤖 Generated with Claude Code

https://claude.ai/code/session_01QGkc3Poso3PUfsPR8xc3sH

ifconfig and others added 5 commits August 21, 2026 23:30
A deep review of v0.1.2..v0.2.0 surfaced 15 defects; an adversarial
re-review of the fixes surfaced 8 more. All 23 are fixed and verified.

- Add lib/memory-paths.mjs as the single memory-dir resolver (explicit
  flag, then config.json probed in .ownmem/.memory, then the layout on
  disk), replacing five divergent copies of the rule. Legacy .memory
  installations now work across every command.
- ownmem embed pins root/memory-dir/index-dir correctly; the bare
  command prints usage; embed ab gains --cases and an actionable error
  instead of a raw ENOENT leaking the install path.
- ownmem recall rejects unknown flags loudly (they were silently
  searched as query text) and routes --stdio/--runtime and the other
  runtime flags to the snapshot resident runtime.
- ownmem hook status|enable|disable|serve are reachable and documented.
- compile validates the corpus directory before taking the index lock,
  so it can no longer create an empty .ownmem/ that poisons resolution;
  the dashboard embedding endpoints share the CLI's snapshot directory.
- Optional-layer detection fires only when the layer entry itself is
  missing; transitive module errors surface unchanged.
- ownmem init rejects a memory directory the resolver could never find.
- Structure gate: git ls-files --cached -z with a graceful non-git
  fallback, dynamic lazy-import scanning, directory-driven skill-mirror
  and schema-id checks, and a shipped-scripts-vs-files invariant.
- The npm package ships test/ and benchmarks/ so the self-test and the
  locked benchmark reproduce from the release artifact.
- engines raised to the honest >=20.6.0 (import.meta.resolve floor).
- CI: pack gate on all three platforms, cancel-in-progress only for
  pull requests, benchmark on a single lane.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QGkc3Poso3PUfsPR8xc3sH
Temporary observation setup: hook_enabled=true, claude added to the
adapters, gates/compiler added to the layers. Revert this commit before
release if the repository should not carry the dogfood state. The
generated root files (CLAUDE.md, MEMORY_INSTRUCTIONS.md, .claude/) stay
untracked -- the locked root layout is a release contract -- and are
listed in .git/info/exclude; regenerate them on any machine with
ownmem init --update --hook --hosts claude,generic.

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

Dogfood: the 0.2.1 observation-period runbook becomes a real .ownmem
topic (observation_period_bootstrap, routed in the General L2, snapshot
recompiled), so any fresh clone of this branch wakes an agent with the
full bootstrap and acceptance context through the repository's own
memory. The generated agent entry files (CLAUDE.md,
MEMORY_INSTRUCTIONS.md, .claude/) are committed so a new machine gets
the recall entry point without any manual setup; the locked root layout
in the structure gate gains the three entries. Revert together with the
dogfood commit before release if the repository should not keep them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QGkc3Poso3PUfsPR8xc3sH
The instrumentation health check folded three different things into one
"zero variance (a placeholder, or the producer is broken)" warning, and
acting on that warning writes false data.

recall.completed.cache_hit is always null on purpose: production recall
has no result cache, and the producer says so in a comment. "Fixing" it
to false would report a measured cache miss that nothing measured.

Three names on EXPECTED_MEMORY_COMPONENTS have no emitter anywhere in
this package, so every run reported them as code paths that had never
run: memory-recall-runtime is named only by the list itself, while
memory-read and memory-maintenance belong to CLIs this package does not
ship (recall.consumed is emitted by memory-observe, and
maintenance.completed has a consumer in features/report.mjs but no
producer at all). A permanent false alarm is how a genuinely silent
component gets lost in the noise.

EXPLAINED_CONSTANT_FIELDS now carries the constants whose second value
is impossible, and they are still printed with their reason rather than
suppressed: hiding one is exactly how the next reader makes a
contractual constant vary. detectConstantPayloadFields keeps its
existing `ignore` semantics untouched, so explicit suppression stays
explicit and non-default.

Two drift guards keep the mapping honest: every payload field the event
schema pins to a const must be declared, and every expected component
must be named by some source file. Both were verified in reverse --
re-adding the ghost fails the suite with the exact assertion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6ZiTen2S5DGy8g1JLeM7z
A half-finished incremental build let stale vectors read as fresh, with
no visible degradation.

checkpointEmbeddingBatch rewrote the whole artifact from the new corpus
on every batch, so the new corpus_snapshot_id landed on disk from the
very first one. An incremental build starts from {...existing.entries},
so at that moment the artifact claims the new snapshot while carrying
the previous vectors for everything not yet embedded.

staleState in memory-embedding-channel.mjs takes a fast path whenever
the artifact's snapshot id equals the planner's, and that path compares
presence only -- it never compares content_sha256. Every carried-over
entry therefore read as fresh, the channel reported
degraded_reason: null, and ranking ran on vectors it knew were stale.
That inverts the position the channel takes everywhere else: fall back
to the deterministic ranking with a named reason rather than answer
quietly from something unmeasured.

The build now keeps the previous snapshot id for the whole incremental
run and finalizes the new one only after the last batch lands, which
forces the reconciling path that compares content hashes. A full
rebuild carries nothing forward, so presence alone is already correct
there and it keeps the new id from the first batch. An aborted build
never reaches the finalize, leaving the previous id in place; resume is
unaffected because reconciliation skips already-embedded batches by
content hash.

This package had no embedding tests at all, so the suite from the
upstream working repository is added here and wired into npm test,
giving CI 18 cases over build, reconciliation, resume and schema
retirement. The regression case was verified in reverse: reverting the
fix fails it with the exact assertion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6ZiTen2S5DGy8g1JLeM7z
@grpcer grpcer closed this Aug 24, 2026
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