Files
Alex Newman cf450cec00 fix(build): enforce shipped dependency-closure boundary (plan-10, closes #2783) (#2800)
* plan-10 Phase 1: ship deterministic plugin runtime dependency closure

Approach A — commit & ship plugin/bun.lock so the plugin's runtime
node_modules install is deterministic, fixing the recurring
`Cannot find module 'zod/v3'` (#2730).

- align generated plugin zod range to root (^4.4.3) in build-hooks.js
- new scripts/gen-plugin-lockfile.cjs generates plugin/bun.lock as a
  build artifact after build-hooks.js writes plugin/package.json
- track & ship plugin/bun.lock (.gitignore negation, .npmignore, files allowlist)
- install with `bun install --frozen-lockfile --ignore-scripts` at runtime

Refs #2783, #2730

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* plan-10 Phase 2: fail loud at install time on a broken dependency closure

Strengthen verifyCriticalModules to assert each dependency is actually
importable via require.resolve (not merely a directory), and assert the
worker-required zod subpaths resolve: zod/v3, zod/v4, zod/v4-mini.
A partial/stale install now fails `npx claude-mem install` immediately
instead of surfacing later as a Stop-hook `Cannot find module 'zod/v3'`.

Bin-only packages (e.g. tree-sitter-cli, which has no bare-name entry
point) fall back to resolving <dep>/package.json so a healthy install
isn't falsely rejected.

Adds tests/cli/verify-critical-modules.test.ts covering a missing zod/v3
subpath (throws), a complete zod (passes), and a bin-only dep (passes).

Refs #2783, #2730

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* plan-10 Phase 3: clean-room install + import smoke test (#2730 backstop)

Add scripts/smoke-clean-room.cjs and a `smoke:clean-room` npm script.
Against fresh temp dirs (never the repo's node_modules) it:
- copies plugin/, runs `bun install --frozen-lockfile --ignore-scripts`,
  asserts zod, zod/v3, zod/v4, zod/v4-mini resolve, and boots the bundled
  worker asserting no `Cannot find module` — the direct #2730 regression guard;
- `npm pack`s, installs the tarball into a second temp dir, and load-tests
  the published bin entrypoint, warning loudly on any declared main/exports
  target missing from the tarball (latent #2537 gap).

Exits non-zero naming the missing module on any failure; cleans up all
temp dirs and the tarball in a finally.

Refs #2783, #2730

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* plan-10 Phase 4: gate CI and publish on the clean-room dependency closure

- ci.yml: new `clean-room-deps` job (between build and the docker e2e job)
  runs a frozen-lockfile drift check on the committed plugin lockfile, then
  `npm run build` + `npm run smoke:clean-room`. The drift step catches a
  contributor who changed plugin deps without regenerating plugin/bun.lock.
- npm-publish.yml: add setup-bun and run `npm run smoke:clean-room` between
  build and `npm publish`, so a broken runtime closure cannot be published
  on a tag push (ci.yml does not run on tags). Secrets block untouched.

Refs #2783, #2730

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* plan-10: doc recluster note + Phase 0 execution slice for #2730

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* plans: backlog recluster (2026-06-04) — cross-cluster execution order + plan-13 doc

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* plan-10: gen-plugin-lockfile degrades gracefully when bun is absent

The Windows build CI job has no bun on PATH; regenerating the lockfile there
threw and failed the build. The committed plugin/bun.lock is already the
deterministic closure, so skip regeneration (non-fatal) when bun is missing
and a lockfile exists; fail loud only when neither is available.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 20:27:34 -07:00

2.2 KiB

[plan-09] Data-Pipeline Integrity & Migration Transparency — stop "healthy worker, frozen observations"

Defect

The worker can report healthy while data silently fails to reach or be retrievable from the store, and the failures are swallowed so distinct root causes present identically. Session identity is stamped inconsistently across the live-observer / re-sync / manual paths; generated summaries and observations are not stamped with the originating content_session_id at write time; EXCLUDED_PROJECTS is honored on capture but not in the Stop hook so excluded projects still get summaries; the FTS5 index omits the type column so a type-token conjunction returns zero; and manual memory-write tools are blocked in the worker runtime. The fix is a verified, consistent write-and-read contract: stamp identity once at write time on every path, apply exclusion uniformly, index every queryable column, and keep the manual write tools functional.

Children

  • #2769 — Stamp originating content_session_id onto generated session_summaries/observations at write time
  • #2772 — Chroma memory_session_id holds the content session id on the live-observer path (inconsistent with re-sync/manual)
  • #2767 — EXCLUDED_PROJECTS does not block the Stop hook — summaries still generated for excluded projects
  • #2729 — FTS5 conjunction with type-token returns 0 (type column not in the search index)
  • #2705 — Manual memory-write tools (observation_add / memory_add) blocked in the worker runtime

Fix sequence

Design doc: plans/09-data-pipeline-integrity.md. Stamp content_session_id at write time on all three paths; apply EXCLUDED_PROJECTS in the Stop hook; add type (and other queried columns) to the FTS5 schema with a migration; re-enable the worker-runtime write tools; assert the field→column→trigger chain.

Test matrix

Path Required behavior
live observer / re-sync / manual identical content_session_id stamping
excluded project Stop hook generates nothing
search type:bugfix + token returns matching rows
worker runtime observation_add / memory_add succeed

Out of scope

Observer output validity (plan-11); grammar/parse coverage (plan-13).