@north-light/crouter 0.3.227 → 0.3.229
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +5 -5
- package/dist/api/client.js +3 -3
- package/dist/api/dto/broker-ops.d.ts +8 -0
- package/dist/api/dto/memory.d.ts +8 -7
- package/dist/api/dto/memory.js +2 -2
- package/dist/builtin-memory/04-base-worker.md +1 -1
- package/dist/builtin-memory/insights/init.md +5 -5
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/agent-shaping.md +11 -11
- package/dist/builtin-memory/internal/examples/INDEX.md +1 -1
- package/dist/builtin-memory/internal/memory-loading.md +6 -6
- package/dist/builtin-memory/internal/plugins.md +1 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +50 -37
- package/dist/clients/attach/__tests__/pane-tag-successor.test.js +56 -0
- package/dist/clients/attach/__tests__/profile-files.test.js +21 -0
- package/dist/clients/attach/chrome/inbox-strip.js +11 -7
- package/dist/clients/attach/session/pane-tag.d.ts +28 -2
- package/dist/clients/attach/session/pane-tag.js +48 -22
- package/dist/clients/attach/session/profile-files.d.ts +6 -0
- package/dist/clients/attach/session/profile-files.js +44 -11
- package/dist/clients/attach/slash/dispatch.js +4 -7
- package/dist/clients/attach/viewer.js +588 -596
- package/dist/commands/__tests__/surface-reload-target.test.js +21 -0
- package/dist/commands/api-client.d.ts +3 -7
- package/dist/commands/api-client.js +7 -12
- package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +292 -0
- package/dist/commands/memory/__tests__/repository-root-lint.test.js +146 -0
- package/dist/commands/memory/delete.js +51 -24
- package/dist/commands/memory/edit.js +50 -10
- package/dist/commands/memory/find.js +105 -56
- package/dist/commands/memory/history.js +51 -31
- package/dist/commands/memory/lint.d.ts +0 -9
- package/dist/commands/memory/lint.js +78 -322
- package/dist/commands/memory/list.d.ts +12 -4
- package/dist/commands/memory/list.js +64 -25
- package/dist/commands/memory/move.js +103 -73
- package/dist/commands/memory/origin.js +35 -5
- package/dist/commands/memory/read.d.ts +4 -0
- package/dist/commands/memory/read.js +132 -80
- package/dist/commands/memory/shared.d.ts +115 -25
- package/dist/commands/memory/shared.js +331 -74
- package/dist/commands/memory/write.js +72 -21
- package/dist/commands/memory.js +2 -2
- package/dist/commands/node/create.d.ts +1 -1
- package/dist/commands/node/create.js +1 -1
- package/dist/commands/node/inspect.js +1 -1
- package/dist/commands/node/lifecycle.js +5 -5
- package/dist/commands/pkg/market-manage.js +25 -24
- package/dist/commands/pkg/plugin-manage.d.ts +22 -6
- package/dist/commands/pkg/plugin-manage.js +118 -25
- package/dist/commands/pkg/shared.d.ts +8 -0
- package/dist/commands/pkg/shared.js +23 -20
- package/dist/commands/surface-reload.d.ts +5 -0
- package/dist/commands/surface-reload.js +9 -1
- package/dist/commands/sys/__tests__/migrate.test.js +1140 -22
- package/dist/commands/sys/__tests__/sync-project-guidance.test.js +218 -0
- package/dist/commands/sys/migrate.js +126 -140
- package/dist/commands/sys/panels/profiles-panel.d.ts +66 -0
- package/dist/commands/sys/panels/profiles-panel.js +599 -0
- package/dist/commands/sys/settings-shell.d.ts +4 -2
- package/dist/commands/sys/settings-shell.js +35 -4
- package/dist/commands/sys/settings.js +21 -5
- package/dist/commands/sys/sync-deps.d.ts +2 -0
- package/dist/commands/sys/sync-deps.js +26 -15
- package/dist/commands/sys/sync-project-guidance.js +228 -145
- package/dist/commands/sys/sync-skills.js +28 -17
- package/dist/commands/sys/update.js +11 -3
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +15 -2
- package/dist/core/__tests__/config-change-delta.test.js +106 -0
- package/dist/core/__tests__/context-intro.test.js +13 -9
- package/dist/core/__tests__/daemon-boot.test.js +22 -16
- package/dist/core/__tests__/fixtures/memory-slash-live-probe.js +5 -1
- package/dist/core/__tests__/human-deliver.test.js +19 -0
- package/dist/core/__tests__/inline-memory-refs.test.js +50 -46
- package/dist/core/__tests__/{serial → integration}/broker-fork-seam.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/broker-sdk-wiring.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/broker-snapshot-history.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/command-plugins.test.js +94 -3
- package/dist/core/__tests__/{serial → integration}/deferred-no-wake.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/flagship-lifecycle.test.js +13 -27
- package/dist/core/__tests__/{serial → integration}/host-teardown-process-group.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/human-deliver-e2e.test.js +1 -1
- package/dist/core/__tests__/{live-mutation-verbs.test.js → integration/live-mutation-verbs.test.js} +22 -33
- package/dist/core/__tests__/{serial → integration}/live-mutation.test.js +21 -41
- package/dist/core/__tests__/integration/refresh-stall-recycle.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/refresh-stall-recycle.test.js +1 -1
- package/dist/core/__tests__/integration/revive.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/revive.test.js +38 -19
- package/dist/core/__tests__/integration/spawn-root.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/spawn-root.test.js +104 -1
- package/dist/core/__tests__/integration/subscription-delivery.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/subscription-delivery.test.js +1 -1
- package/dist/core/__tests__/integration/tmux-surface.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/tmux-surface.test.js +1 -1
- package/dist/core/__tests__/integration/worktree-land.test.d.ts +1 -0
- package/dist/core/__tests__/integration/worktree-land.test.js +400 -0
- package/dist/core/__tests__/integration/worktree-reap.test.d.ts +1 -0
- package/dist/core/__tests__/{serial/worktree.test.js → integration/worktree-reap.test.js} +6 -338
- package/dist/core/__tests__/kickoff.test.js +16 -5
- package/dist/core/__tests__/memory-resolver-precedence.test.js +122 -91
- package/dist/core/__tests__/nested-store-discovery.test.js +5 -3
- package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
- package/dist/core/__tests__/on-read-dedup-resume.test.js +39 -27
- package/dist/core/__tests__/on-read-nested-store.test.js +19 -13
- package/dist/core/__tests__/profile-project-memory-delivery.test.js +138 -44
- package/dist/core/__tests__/repository-association.test.d.ts +1 -0
- package/dist/core/__tests__/repository-association.test.js +153 -0
- package/dist/core/__tests__/repository-root-identity.test.d.ts +1 -0
- package/dist/core/__tests__/repository-root-identity.test.js +219 -0
- package/dist/core/__tests__/review-model-floor.test.js +12 -4
- package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +27 -11
- package/dist/core/__tests__/warm-claim-preference-snapshot.test.d.ts +1 -0
- package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +40 -0
- package/dist/core/canvas/canvas.d.ts +3 -0
- package/dist/core/canvas/canvas.js +30 -10
- package/dist/core/canvas/db.js +28 -1
- package/dist/core/canvas/paths.d.ts +4 -6
- package/dist/core/canvas/paths.js +8 -6
- package/dist/core/canvas/render-source.js +2 -2
- package/dist/core/canvas/types.d.ts +9 -13
- package/dist/core/config.d.ts +3 -3
- package/dist/core/config.js +3 -3
- package/dist/core/exclusive-lock.d.ts +2 -0
- package/dist/core/exclusive-lock.js +21 -0
- package/dist/core/git.d.ts +0 -1
- package/dist/core/git.js +0 -3
- package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +1 -0
- package/dist/core/human/feedback-companion.js +3 -0
- package/dist/core/human/scan.js +8 -1
- package/dist/core/keybindings/catalog.d.ts +2 -2
- package/dist/core/keybindings/catalog.js +2 -1
- package/dist/core/memory/doc-link-grammar.js +2 -2
- package/dist/core/memory/history.d.ts +24 -0
- package/dist/core/memory/history.js +66 -1
- package/dist/core/memory/identity.d.ts +65 -0
- package/dist/core/memory/identity.js +185 -0
- package/dist/core/memory/inline-ref-guidance.d.ts +1 -1
- package/dist/core/memory/inline-ref-guidance.js +1 -1
- package/dist/core/memory/inline-ref-inventory.d.ts +3 -10
- package/dist/core/memory/inline-ref-inventory.js +30 -62
- package/dist/core/memory/lint.d.ts +129 -0
- package/dist/core/memory/lint.js +517 -0
- package/dist/core/memory/project-namespace.d.ts +31 -0
- package/dist/core/memory/project-namespace.js +85 -0
- package/dist/core/memory/repository-association.d.ts +31 -0
- package/dist/core/memory/repository-association.js +129 -0
- package/dist/core/memory/tree.d.ts +39 -0
- package/dist/core/memory/tree.js +93 -0
- package/dist/core/memory-resolver.d.ts +193 -90
- package/dist/core/memory-resolver.js +461 -375
- package/dist/core/nested-stores.js +6 -13
- package/dist/core/profiles/select.d.ts +4 -1
- package/dist/core/profiles/select.js +108 -48
- package/dist/core/review/__tests__/capture-origin.test.js +2 -2
- package/dist/core/review/__tests__/stage-identity.test.js +2 -2
- package/dist/core/review/companion.js +11 -2
- package/dist/core/runtime/bearings.d.ts +2 -2
- package/dist/core/runtime/bearings.js +3 -3
- package/dist/core/runtime/broker/daemon-ops.d.ts +2 -2
- package/dist/core/runtime/broker/rebind.js +5 -0
- package/dist/core/runtime/broker-extension-render.d.ts +7 -3
- package/dist/core/runtime/broker-extension-render.js +10 -5
- package/dist/core/runtime/broker-persona-guidance.d.ts +19 -5
- package/dist/core/runtime/broker-persona-guidance.js +122 -27
- package/dist/core/runtime/deliver-live.d.ts +16 -4
- package/dist/core/runtime/deliver-live.js +29 -15
- package/dist/core/runtime/kickoff.d.ts +3 -3
- package/dist/core/runtime/kickoff.js +8 -16
- package/dist/core/runtime/launch.js +1 -1
- package/dist/core/runtime/lifecycle.js +2 -3
- package/dist/core/runtime/nodes.d.ts +3 -4
- package/dist/core/runtime/nodes.js +3 -4
- package/dist/core/runtime/persona.d.ts +8 -12
- package/dist/core/runtime/persona.js +24 -96
- package/dist/core/runtime/promote.d.ts +2 -2
- package/dist/core/runtime/promote.js +8 -21
- package/dist/core/runtime/revive.js +20 -17
- package/dist/core/runtime/spawn.js +18 -7
- package/dist/core/runtime/tmux-bindings.js +2 -3
- package/dist/core/runtime/warm-pool.js +3 -4
- package/dist/core/scope.js +2 -0
- package/dist/core/self-update.d.ts +0 -2
- package/dist/core/self-update.js +2 -35
- package/dist/core/substrate/__tests__/surface-match-memory-read.test.d.ts +1 -0
- package/dist/core/substrate/__tests__/surface-match-memory-read.test.js +28 -0
- package/dist/core/substrate/index.d.ts +2 -2
- package/dist/core/substrate/index.js +1 -1
- package/dist/core/substrate/injected-store.d.ts +43 -27
- package/dist/core/substrate/injected-store.js +208 -104
- package/dist/core/substrate/listings.d.ts +19 -12
- package/dist/core/substrate/listings.js +75 -52
- package/dist/core/substrate/on-read-node.d.ts +4 -7
- package/dist/core/substrate/on-read-node.js +6 -8
- package/dist/core/substrate/on-read.d.ts +22 -25
- package/dist/core/substrate/on-read.js +103 -147
- package/dist/core/substrate/render-node.d.ts +4 -7
- package/dist/core/substrate/render-node.js +5 -7
- package/dist/core/substrate/render.d.ts +21 -3
- package/dist/core/substrate/render.js +291 -223
- package/dist/core/substrate/schema.d.ts +1 -13
- package/dist/core/substrate/schema.js +5 -40
- package/dist/core/substrate/session-cache.d.ts +14 -4
- package/dist/core/substrate/session-cache.js +40 -22
- package/dist/core/substrate/surface-match.d.ts +9 -7
- package/dist/core/substrate/surface-match.js +26 -25
- package/dist/daemon/__tests__/helpers/source-daemon.d.ts +30 -0
- package/dist/daemon/__tests__/helpers/source-daemon.js +174 -0
- package/dist/daemon/__tests__/integration/migration-startup.test.d.ts +1 -0
- package/dist/daemon/__tests__/integration/migration-startup.test.js +97 -0
- package/dist/daemon/api/__tests__/bridge-heartbeat.test.js +21 -5
- package/dist/daemon/api/handlers/broker-ops.js +21 -16
- package/dist/daemon/api/handlers/memory.js +2 -0
- package/dist/daemon/crtrd.js +2 -0
- package/dist/daemon/human/finish.js +8 -1
- package/dist/daemon/manage.d.ts +0 -1
- package/dist/daemon/manage.js +7 -16
- package/dist/daemon/startup-policy.d.ts +1 -0
- package/dist/daemon/startup-policy.js +1 -0
- package/dist/migrations/001-surfaces-frontmatter.js +21 -109
- package/dist/migrations/002-profile-project-memory.js +1 -0
- package/dist/migrations/003-repository-root-memory-identity/front-door.d.ts +26 -0
- package/dist/migrations/003-repository-root-memory-identity/front-door.js +231 -0
- package/dist/migrations/003-repository-root-memory-identity/index.d.ts +2 -0
- package/dist/migrations/003-repository-root-memory-identity/index.js +513 -0
- package/dist/migrations/003-repository-root-memory-identity/references.d.ts +95 -0
- package/dist/migrations/003-repository-root-memory-identity/references.js +469 -0
- package/dist/migrations/003-repository-root-memory-identity/repository-facts.d.ts +39 -0
- package/dist/migrations/003-repository-root-memory-identity/repository-facts.js +349 -0
- package/dist/migrations/__tests__/activation-concurrency.test.d.ts +1 -0
- package/dist/migrations/__tests__/activation-concurrency.test.js +145 -0
- package/dist/migrations/__tests__/activation.test.d.ts +1 -0
- package/dist/migrations/__tests__/activation.test.js +149 -0
- package/dist/migrations/__tests__/deletion-and-root-declaration.test.d.ts +1 -0
- package/dist/migrations/__tests__/deletion-and-root-declaration.test.js +149 -0
- package/dist/migrations/activation.d.ts +16 -0
- package/dist/migrations/activation.js +78 -0
- package/dist/migrations/convergent.d.ts +14 -3
- package/dist/migrations/convergent.js +21 -10
- package/dist/migrations/corpus.d.ts +78 -0
- package/dist/migrations/corpus.js +497 -0
- package/dist/migrations/frontmatter-splice.d.ts +15 -0
- package/dist/migrations/frontmatter-splice.js +176 -0
- package/dist/migrations/registry.d.ts +6 -1
- package/dist/migrations/registry.js +7 -2
- package/dist/migrations/runner.d.ts +41 -0
- package/dist/migrations/runner.js +81 -0
- package/dist/migrations/types.d.ts +148 -9
- package/dist/migrations/types.js +9 -2
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +225 -17
- package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +11 -3
- package/dist/pi-extensions/canvas-context-intro.d.ts +3 -5
- package/dist/pi-extensions/canvas-context-intro.js +50 -46
- package/dist/pi-extensions/canvas-doc-substrate.d.ts +1 -8
- package/dist/pi-extensions/canvas-doc-substrate.js +55 -122
- package/dist/pi-extensions/canvas-stophook.js +6 -13
- package/dist/shared/generated-context.d.ts +0 -3
- package/dist/shared/generated-context.js +0 -57
- package/dist/shared/tool-groups.js +2 -3
- package/dist/types.d.ts +8 -11
- package/dist/types.js +4 -31
- package/package.json +5 -4
- package/runtime.lock.json +2 -2
- package/dist/builtin-memory/05-kinds/design/00-base.md +0 -17
- package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +0 -15
- package/dist/builtin-memory/05-kinds/design/design-contract.md +0 -19
- package/dist/builtin-memory/05-kinds/developer/00-base.md +0 -17
- package/dist/builtin-memory/05-kinds/developer/01-orchestrator.md +0 -15
- package/dist/builtin-memory/05-kinds/plan/00-base.md +0 -17
- package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +0 -17
- package/dist/builtin-memory/05-kinds/plan/plan-contract.md +0 -28
- package/dist/builtin-memory/05-kinds/plan/reviewers/architecture-fit.md +0 -15
- package/dist/builtin-memory/05-kinds/plan/reviewers/code-smells.md +0 -15
- package/dist/builtin-memory/05-kinds/plan/reviewers/lens-contract.md +0 -13
- package/dist/builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md +0 -17
- package/dist/builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md +0 -17
- package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +0 -17
- package/dist/builtin-memory/05-kinds/spec/00-base.md +0 -17
- package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +0 -15
- package/dist/builtin-memory/05-kinds/spec/requirements.md +0 -15
- package/dist/builtin-memory/design/guide.md +0 -35
- package/dist/builtin-memory/design/roadmap.md +0 -21
- package/dist/builtin-memory/development.md +0 -113
- package/dist/builtin-memory/plan/roadmap.md +0 -25
- package/dist/builtin-memory/spec/guide.md +0 -53
- package/dist/builtin-memory/spec/requirements.md +0 -29
- package/dist/builtin-memory/spec/roadmap.md +0 -36
- package/dist/builtin-memory/testing.md +0 -39
- /package/dist/api/__tests__/{serial → integration}/client.test.d.ts +0 -0
- /package/dist/api/__tests__/{serial → integration}/client.test.js +0 -0
- /package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/{serial → integration}/provider-rotation.test.ts +0 -0
- /package/dist/clients/{inbox/__tests__/serial/inbox-controller.test.d.ts → attach/__tests__/pane-tag-successor.test.d.ts} +0 -0
- /package/dist/clients/{inbox/__tests__/serial/mount-panel.test.d.ts → attach/__tests__/profile-files.test.d.ts} +0 -0
- /package/dist/{core/__tests__/live-mutation-verbs.test.d.ts → clients/inbox/__tests__/integration/inbox-controller.test.d.ts} +0 -0
- /package/dist/clients/inbox/__tests__/{serial → integration}/inbox-controller.test.js +0 -0
- /package/dist/{core/__tests__/serial/broker-fork-seam.test.d.ts → clients/inbox/__tests__/integration/mount-panel.test.d.ts} +0 -0
- /package/dist/clients/inbox/__tests__/{serial → integration}/mount-panel.test.js +0 -0
- /package/dist/{core/__tests__/serial/broker-sdk-wiring.test.d.ts → commands/__tests__/surface-reload-target.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/broker-snapshot-history.test.d.ts → commands/memory/__tests__/command-selector-and-mutation-guards.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/command-plugins.test.d.ts → commands/memory/__tests__/repository-root-lint.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/deferred-no-wake.test.d.ts → commands/sys/__tests__/sync-project-guidance.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/flagship-lifecycle.test.d.ts → config-change-delta.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/host-teardown-process-group.test.d.ts → integration/broker-fork-seam.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/human-deliver-e2e.test.d.ts → integration/broker-sdk-wiring.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/live-mutation.test.d.ts → integration/broker-snapshot-history.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/refresh-stall-recycle.test.d.ts → integration/command-plugins.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/revive.test.d.ts → integration/deferred-no-wake.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/spawn-root.test.d.ts → integration/flagship-lifecycle.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/subscription-delivery.test.d.ts → integration/host-teardown-process-group.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/tmux-surface.test.d.ts → integration/human-deliver-e2e.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/worktree.test.d.ts → integration/live-mutation-verbs.test.d.ts} +0 -0
- /package/dist/core/{human/__tests__/serial/inbox-core.test.d.ts → __tests__/integration/live-mutation.test.d.ts} +0 -0
- /package/dist/core/human/__tests__/{serial → integration}/inbox-core.test.js +0 -0
|
@@ -1,26 +1,30 @@
|
|
|
1
|
-
// Regression matrix for
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
1
|
+
// Regression matrix for exact canonical memory identity: a project store
|
|
2
|
+
// declares a namespace and every document in it answers ONLY at
|
|
3
|
+
// `<namespace>/<local name>`. Nothing else addresses it — not the old
|
|
4
|
+
// unqualified name, not the physical path, not a unique leaf, not a
|
|
5
|
+
// scope-prefixed spelling, and not a trailing `/INDEX` (a directory's own
|
|
6
|
+
// document answers at the directory name). Linked worktrees that declare the
|
|
7
|
+
// same repository namespace expose the same canonical identities: source order
|
|
8
|
+
// picks the address winner, every candidate stays ordered and reachable through
|
|
9
|
+
// its exact store.
|
|
8
10
|
//
|
|
9
|
-
// Fixture shape:
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
11
|
+
// Fixture shape: one repository's main checkout plus a linked worktree, both
|
|
12
|
+
// declaring `namespace: root`. The cwd is the linked worktree and the main
|
|
13
|
+
// checkout enters the precedence stack through the selected profile:
|
|
14
|
+
// main/.crouter/memory/... (farther store, namespace root)
|
|
15
|
+
// linked/.crouter/memory/... (nearer store, namespace root; cwd)
|
|
16
|
+
// linked/grandchild/.crouter/memory/... (metadata-free nested store)
|
|
15
17
|
//
|
|
16
|
-
// Run: node --import tsx/esm --test src/core/__tests__/memory-resolver-precedence.test.ts
|
|
18
|
+
// Run: node --conditions=crtr-src --import tsx/esm --test src/core/__tests__/memory-resolver-precedence.test.ts
|
|
17
19
|
import { test, before, beforeEach, after } from 'node:test';
|
|
18
20
|
import assert from 'node:assert/strict';
|
|
19
21
|
import { mkdtempSync, mkdirSync, rmSync, writeFileSync, realpathSync } from 'node:fs';
|
|
20
22
|
import { tmpdir } from 'node:os';
|
|
21
23
|
import { join } from 'node:path';
|
|
24
|
+
import { gitSync } from '../git.js';
|
|
25
|
+
import { createProfile } from '../profiles/manifest.js';
|
|
22
26
|
import { resetScopeCache } from '../scope.js';
|
|
23
|
-
import { resolveMemoryDoc, resolveMemoryDocs } from '../memory-resolver.js';
|
|
27
|
+
import { loadMemoryStoreView, openProjectMemoryStore, resolveMemoryCandidates, resolveMemoryDoc, resolveMemoryDocInStore, resolveMemoryDocs, } from '../memory-resolver.js';
|
|
24
28
|
let home;
|
|
25
29
|
let base;
|
|
26
30
|
let root;
|
|
@@ -28,42 +32,58 @@ let child;
|
|
|
28
32
|
let grandchild;
|
|
29
33
|
let prevCwd;
|
|
30
34
|
let prevHome;
|
|
31
|
-
|
|
35
|
+
let prevProfile;
|
|
36
|
+
function writeDoc(path, body, frontmatter) {
|
|
32
37
|
mkdirSync(join(path, '..'), { recursive: true });
|
|
33
|
-
const fm =
|
|
38
|
+
const fm = frontmatter ? `---\n${frontmatter}\n---\n` : '';
|
|
34
39
|
writeFileSync(path, `${fm}${body}\n`, 'utf8');
|
|
35
40
|
}
|
|
41
|
+
function initWorktreeRepository(main, linked) {
|
|
42
|
+
mkdirSync(main, { recursive: true });
|
|
43
|
+
assert.equal(gitSync(['-C', main, 'init', '-q'], undefined).status, 0);
|
|
44
|
+
writeFileSync(join(main, 'README.md'), 'fixture\n', 'utf8');
|
|
45
|
+
assert.equal(gitSync(['-C', main, 'add', 'README.md'], undefined).status, 0);
|
|
46
|
+
assert.equal(gitSync(['-C', main, '-c', 'user.name=Fixture', '-c', 'user.email=fixture@example.com', 'commit', '-qm', 'initial'], undefined).status, 0);
|
|
47
|
+
assert.equal(gitSync(['-C', main, 'worktree', 'add', '-q', '-b', 'linked', linked], undefined).status, 0);
|
|
48
|
+
}
|
|
49
|
+
function assertNotFound(name) {
|
|
50
|
+
assert.throws(() => resolveMemoryDoc(name), (err) => err.code === 'not_found', `${name} must not resolve`);
|
|
51
|
+
}
|
|
36
52
|
before(() => {
|
|
37
53
|
home = mkdtempSync(join(tmpdir(), 'crtr-resolver-precedence-home-'));
|
|
38
54
|
prevHome = process.env.HOME;
|
|
55
|
+
prevProfile = process.env.CRTR_PROFILE_ID;
|
|
39
56
|
process.env.HOME = home;
|
|
40
|
-
// A
|
|
41
|
-
// fixture for the --kind shadowing regression case).
|
|
57
|
+
// A wider user-scope doc whose name a project doc used to shadow.
|
|
42
58
|
mkdirSync(join(home, '.crouter', 'memory'), { recursive: true });
|
|
43
59
|
writeFileSync(join(home, '.crouter', 'memory', 'testing-stance.md'), '---\nkind: preference\n---\nUSER PREFERENCE BODY\n', 'utf8');
|
|
44
60
|
});
|
|
45
61
|
beforeEach(() => {
|
|
46
62
|
resetScopeCache();
|
|
47
63
|
base = realpathSync(mkdtempSync(join(tmpdir(), 'crtr-resolver-precedence-')));
|
|
48
|
-
root = join(base, '
|
|
49
|
-
child = join(
|
|
64
|
+
root = join(base, 'main');
|
|
65
|
+
child = join(base, 'linked');
|
|
50
66
|
grandchild = join(child, 'grandchild');
|
|
51
|
-
|
|
52
|
-
mkdirSync(child, { recursive: true });
|
|
67
|
+
initWorktreeRepository(root, child);
|
|
53
68
|
mkdirSync(grandchild, { recursive: true });
|
|
54
|
-
// ---
|
|
55
|
-
writeDoc(join(root, '.crouter', 'memory', '
|
|
56
|
-
writeDoc(join(root, '.crouter', 'memory', 'shared.md'), 'ROOT SHARED BODY'); //
|
|
57
|
-
writeDoc(join(root, '.crouter', 'memory', 'taste', '
|
|
58
|
-
writeDoc(join(root, '.crouter', 'memory', 'taste', '
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
writeDoc(join(child, '.crouter', 'memory', '
|
|
63
|
-
writeDoc(join(child, '.crouter', 'memory', '
|
|
64
|
-
writeDoc(join(child, '.crouter', 'memory', '
|
|
65
|
-
|
|
66
|
-
writeDoc(join(
|
|
69
|
+
// --- main (FARTHER store, namespace root) ---
|
|
70
|
+
writeDoc(join(root, '.crouter', 'memory', 'INDEX.md'), 'ROOT STORE INDEX', 'namespace: root');
|
|
71
|
+
writeDoc(join(root, '.crouter', 'memory', 'shared.md'), 'ROOT SHARED BODY'); // root/shared
|
|
72
|
+
writeDoc(join(root, '.crouter', 'memory', 'taste', 'INDEX.md'), 'ROOT TASTE INDEX'); // root/taste
|
|
73
|
+
writeDoc(join(root, '.crouter', 'memory', 'taste', 'document-substrate.md'), 'ROOT TASTE BODY');
|
|
74
|
+
writeDoc(join(root, '.crouter', 'memory', 'dup.md'), 'ROOT DUP PREFERENCE', 'kind: preference'); // root/dup
|
|
75
|
+
// --- linked (NEARER store; the same repository namespace means its docs are
|
|
76
|
+
// alternate physical candidates for the SAME canonical identities) ---
|
|
77
|
+
writeDoc(join(child, '.crouter', 'memory', 'INDEX.md'), 'CHILD STORE INDEX', 'namespace: root');
|
|
78
|
+
writeDoc(join(child, '.crouter', 'memory', 'dup.md'), 'CHILD DUP KNOWLEDGE'); // root/dup
|
|
79
|
+
writeDoc(join(child, '.crouter', 'memory', 'taste', 'document-substrate.md'), 'CHILD TASTE BODY');
|
|
80
|
+
writeDoc(join(child, '.crouter', 'memory', 'testing-stance.md'), 'CHILD TESTING STANCE BODY');
|
|
81
|
+
writeDoc(join(child, '.crouter', 'memory', 'AGENTS.md'), 'CHILD AGENTS BODY', 'name: notes/agents');
|
|
82
|
+
// --- grandchild (metadata-free nested store: identity comes from linked's
|
|
83
|
+
// repository-root declaration) ---
|
|
84
|
+
writeDoc(join(grandchild, '.crouter', 'memory', 'orphan.md'), 'ORPHAN BODY');
|
|
85
|
+
process.env.CRTR_PROFILE_ID = createProfile('precedence', [{ path: root, memory: 'content' }]).profileId;
|
|
86
|
+
resetScopeCache();
|
|
67
87
|
prevCwd = process.cwd();
|
|
68
88
|
});
|
|
69
89
|
after(() => {
|
|
@@ -75,119 +95,130 @@ after(() => {
|
|
|
75
95
|
delete process.env.HOME;
|
|
76
96
|
else
|
|
77
97
|
process.env.HOME = prevHome;
|
|
98
|
+
if (prevProfile === undefined)
|
|
99
|
+
delete process.env.CRTR_PROFILE_ID;
|
|
100
|
+
else
|
|
101
|
+
process.env.CRTR_PROFILE_ID = prevProfile;
|
|
78
102
|
});
|
|
79
|
-
test('a
|
|
103
|
+
test('a project doc answers only at its full canonical name', () => {
|
|
80
104
|
process.chdir(child);
|
|
81
105
|
resetScopeCache();
|
|
82
106
|
try {
|
|
83
|
-
const
|
|
84
|
-
assert.equal(
|
|
85
|
-
assert.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
107
|
+
const doc = resolveMemoryDoc('root/taste/document-substrate');
|
|
108
|
+
assert.equal(doc.path, join(child, '.crouter', 'memory', 'taste', 'document-substrate.md'));
|
|
109
|
+
assert.equal(doc.localName, 'taste/document-substrate');
|
|
110
|
+
assert.match(doc.body, /CHILD TASTE BODY/);
|
|
111
|
+
assertNotFound('taste/document-substrate'); // the old unqualified project name
|
|
112
|
+
assertNotFound('document-substrate'); // a unique leaf
|
|
113
|
+
assertNotFound('project/root/taste/document-substrate'); // a scope-prefixed spelling
|
|
114
|
+
assertNotFound('root/taste/document-substrate.md'); // a physical path spelling
|
|
89
115
|
}
|
|
90
116
|
finally {
|
|
91
117
|
process.chdir(prevCwd);
|
|
92
118
|
}
|
|
93
119
|
});
|
|
94
|
-
test('
|
|
120
|
+
test("a directory's own document answers at the directory name, a store root at its namespace", () => {
|
|
95
121
|
process.chdir(child);
|
|
96
122
|
resetScopeCache();
|
|
97
123
|
try {
|
|
98
|
-
const
|
|
99
|
-
assert.equal(
|
|
100
|
-
assert.
|
|
124
|
+
const dir = resolveMemoryDoc('root/taste');
|
|
125
|
+
assert.equal(dir.path, join(root, '.crouter', 'memory', 'taste', 'INDEX.md'));
|
|
126
|
+
assert.equal(dir.representation, 'directory-index');
|
|
127
|
+
assert.equal(dir.routingAnchor, 'root/taste');
|
|
128
|
+
assert.match(dir.body, /ROOT TASTE INDEX/);
|
|
129
|
+
const storeRoot = resolveMemoryDoc('root');
|
|
130
|
+
assert.equal(storeRoot.path, join(child, '.crouter', 'memory', 'INDEX.md'));
|
|
131
|
+
assert.equal(storeRoot.localName, '');
|
|
132
|
+
assert.match(storeRoot.body, /CHILD STORE INDEX/);
|
|
133
|
+
assertNotFound('root/taste/INDEX');
|
|
134
|
+
assertNotFound('root/INDEX');
|
|
101
135
|
}
|
|
102
136
|
finally {
|
|
103
137
|
process.chdir(prevCwd);
|
|
104
138
|
}
|
|
105
139
|
});
|
|
106
|
-
test('a
|
|
107
|
-
process.chdir(
|
|
140
|
+
test('a namespaced project doc no longer shadows a wider unqualified name', () => {
|
|
141
|
+
process.chdir(child);
|
|
108
142
|
resetScopeCache();
|
|
109
143
|
try {
|
|
110
|
-
const
|
|
111
|
-
assert.equal(
|
|
112
|
-
assert.
|
|
113
|
-
|
|
144
|
+
const wider = resolveMemoryDoc('testing-stance');
|
|
145
|
+
assert.equal(wider.scope, 'user');
|
|
146
|
+
assert.match(wider.body, /USER PREFERENCE BODY/);
|
|
147
|
+
const project = resolveMemoryDoc('root/testing-stance');
|
|
148
|
+
assert.equal(project.scope, 'project');
|
|
149
|
+
assert.match(project.body, /CHILD TESTING STANCE BODY/);
|
|
114
150
|
}
|
|
115
151
|
finally {
|
|
116
152
|
process.chdir(prevCwd);
|
|
117
153
|
}
|
|
118
154
|
});
|
|
119
|
-
test('
|
|
155
|
+
test('equal canonical identities keep every candidate in source order and stay exact-store targetable', () => {
|
|
120
156
|
process.chdir(child);
|
|
121
157
|
resetScopeCache();
|
|
122
158
|
try {
|
|
123
|
-
const
|
|
124
|
-
assert.
|
|
125
|
-
assert.
|
|
159
|
+
const set = resolveMemoryCandidates('root/dup');
|
|
160
|
+
assert.deepEqual(set.candidates.map((d) => d.path), [join(child, '.crouter', 'memory', 'dup.md'), join(root, '.crouter', 'memory', 'dup.md')], 'nearest-first candidate order, both linked-worktree stores retained');
|
|
161
|
+
assert.equal(set.winner.path, join(child, '.crouter', 'memory', 'dup.md'));
|
|
162
|
+
const store = openProjectMemoryStore(root);
|
|
163
|
+
assert.equal(store.namespace, 'root');
|
|
164
|
+
assert.equal(store.mountStatus, 'ready');
|
|
165
|
+
const nonWinner = resolveMemoryDocInStore('root/dup', store);
|
|
166
|
+
assert.equal(nonWinner.path, join(root, '.crouter', 'memory', 'dup.md'));
|
|
167
|
+
assert.match(nonWinner.body, /ROOT DUP PREFERENCE/);
|
|
126
168
|
}
|
|
127
169
|
finally {
|
|
128
170
|
process.chdir(prevCwd);
|
|
129
171
|
}
|
|
130
172
|
});
|
|
131
|
-
test('
|
|
173
|
+
test('--kind is a constraint across equal-canonical candidates, not a post-filter', () => {
|
|
132
174
|
process.chdir(child);
|
|
133
175
|
resetScopeCache();
|
|
134
176
|
try {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
177
|
+
// The nearer candidate is frontmatter-less (kind defaults to knowledge) and
|
|
178
|
+
// must be SKIPPED, not returned and not not_found.
|
|
179
|
+
const doc = resolveMemoryDoc('root/dup', { kind: 'preference' });
|
|
180
|
+
assert.equal(doc.path, join(root, '.crouter', 'memory', 'dup.md'));
|
|
181
|
+
assert.match(doc.body, /ROOT DUP PREFERENCE/);
|
|
139
182
|
}
|
|
140
183
|
finally {
|
|
141
184
|
process.chdir(prevCwd);
|
|
142
185
|
}
|
|
143
186
|
});
|
|
144
|
-
test(
|
|
145
|
-
// The assembled memory tree addresses a directory INDEX by its extensionless
|
|
146
|
-
// resolver identity, so `<dir>/INDEX` must resolve to the physical INDEX.md
|
|
147
|
-
// and report back the same identifier it was asked for.
|
|
187
|
+
test('an explicit frontmatter name is store-local and composes under the namespace', () => {
|
|
148
188
|
process.chdir(child);
|
|
149
189
|
resetScopeCache();
|
|
150
190
|
try {
|
|
151
|
-
const doc = resolveMemoryDoc('
|
|
152
|
-
assert.equal(doc.path, join(child, '.crouter', 'memory', '
|
|
153
|
-
assert.equal(doc.
|
|
154
|
-
|
|
191
|
+
const doc = resolveMemoryDoc('root/notes/agents');
|
|
192
|
+
assert.equal(doc.path, join(child, '.crouter', 'memory', 'AGENTS.md'));
|
|
193
|
+
assert.equal(doc.localName, 'notes/agents');
|
|
194
|
+
assertNotFound('notes/agents'); // the explicit name is not an address by itself
|
|
195
|
+
assertNotFound('root/AGENTS'); // nor is the physical filename
|
|
155
196
|
}
|
|
156
197
|
finally {
|
|
157
198
|
process.chdir(prevCwd);
|
|
158
199
|
}
|
|
159
200
|
});
|
|
160
|
-
test('
|
|
161
|
-
process.chdir(
|
|
201
|
+
test('a metadata-free nested project store mounts under its derived prefix', () => {
|
|
202
|
+
process.chdir(grandchild);
|
|
162
203
|
resetScopeCache();
|
|
163
204
|
try {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
assert.equal(
|
|
167
|
-
assert.
|
|
168
|
-
|
|
169
|
-
// and must be SKIPPED (not returned, not not_found) so resolution keeps
|
|
170
|
-
// walking outward to the farther user-scope doc that actually carries
|
|
171
|
-
// kind: preference.
|
|
172
|
-
const withKind = resolveMemoryDoc('testing-stance', { kind: 'preference' });
|
|
173
|
-
assert.equal(withKind.scope, 'user');
|
|
174
|
-
assert.match(withKind.body, /USER PREFERENCE BODY/);
|
|
205
|
+
const store = openProjectMemoryStore(grandchild);
|
|
206
|
+
assert.equal(store.mountStatus, 'ready');
|
|
207
|
+
assert.equal(store.namespace, 'root/grandchild');
|
|
208
|
+
assert.equal(resolveMemoryDoc('root/grandchild/orphan').path, join(grandchild, '.crouter', 'memory', 'orphan.md'));
|
|
209
|
+
assert.deepEqual(loadMemoryStoreView(store, true).docs.map((d) => d.name), ['root/grandchild/orphan'], 'exact-store inspection uses the same derived identity');
|
|
175
210
|
}
|
|
176
211
|
finally {
|
|
177
212
|
process.chdir(prevCwd);
|
|
178
213
|
}
|
|
179
214
|
});
|
|
180
|
-
test('
|
|
215
|
+
test('batch resolution takes canonical names only', () => {
|
|
181
216
|
process.chdir(child);
|
|
182
217
|
resetScopeCache();
|
|
183
218
|
try {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const doc = resolveMemoryDoc('shared');
|
|
188
|
-
assert.equal(doc.path, join(root, '.crouter', 'memory', 'shared.md'));
|
|
189
|
-
assert.equal(doc.name, 'shared');
|
|
190
|
-
assert.match(doc.body, /ROOT SHARED BODY/);
|
|
219
|
+
const resolved = resolveMemoryDocs(['root/shared', 'shared']);
|
|
220
|
+
assert.equal(resolved.get('root/shared')?.path, join(root, '.crouter', 'memory', 'shared.md'));
|
|
221
|
+
assert.equal(resolved.get('shared'), undefined);
|
|
191
222
|
}
|
|
192
223
|
finally {
|
|
193
224
|
process.chdir(prevCwd);
|
|
@@ -72,16 +72,18 @@ test('non-git owner: the walk finds a store at the depth cap and stops below it'
|
|
|
72
72
|
test('resolveMemoryDocForTarget resolves a nested doc only with includeDescendants', () => {
|
|
73
73
|
const proj = join(fakeHome, 'proj');
|
|
74
74
|
mkdirSync(join(proj, '.crouter', 'memory'), { recursive: true }); // project scope engages
|
|
75
|
+
writeFileSync(join(proj, '.crouter', 'memory', 'INDEX.md'), '---\nnamespace: proj\n---\nPROJ STORE\n');
|
|
75
76
|
const nestedMemory = join(proj, 'sub', '.crouter', 'memory');
|
|
76
77
|
mkdirSync(nestedMemory, { recursive: true });
|
|
78
|
+
writeFileSync(join(nestedMemory, 'INDEX.md'), '# nested store\n');
|
|
77
79
|
writeFileSync(join(nestedMemory, 'guide.md'), '---\nkind: knowledge\n' +
|
|
78
80
|
'name: nested-guide\n' +
|
|
79
81
|
'when-and-why-to-read: When working in sub, this knowledge should be read because it carries the subsystem constraints.\n---\n' +
|
|
80
82
|
'NESTED GUIDE BODY\n');
|
|
81
83
|
const target = { cwd: proj, profileId: null, nodeId: null };
|
|
82
|
-
assert.throws(() => resolveMemoryDocForTarget('nested-guide', target, {}), (e) => e instanceof CrtrError && e.code === 'not_found', 'without the opt-in the flat ancestor stack does not see the nested store');
|
|
83
|
-
const doc = resolveMemoryDocForTarget('nested-guide', target, { includeDescendants: true });
|
|
84
|
-
assert.equal(doc.name, 'nested-guide', '
|
|
84
|
+
assert.throws(() => resolveMemoryDocForTarget('proj/sub/nested-guide', target, {}), (e) => e instanceof CrtrError && e.code === 'not_found', 'without the opt-in the flat ancestor stack does not see the nested store');
|
|
85
|
+
const doc = resolveMemoryDocForTarget('proj/sub/nested-guide', target, { includeDescendants: true });
|
|
86
|
+
assert.equal(doc.name, 'proj/sub/nested-guide', 'the root namespace composes with the nested owner path and local name');
|
|
85
87
|
assert.equal(doc.scope, 'project');
|
|
86
88
|
assert.ok(doc.body.includes('NESTED GUIDE BODY'));
|
|
87
89
|
});
|
|
@@ -14,6 +14,7 @@ import { closeDb } from '../canvas/db.js';
|
|
|
14
14
|
import { resetScopeCache } from '../scope.js';
|
|
15
15
|
import { spawnNode } from '../runtime/nodes.js';
|
|
16
16
|
import { renderOnReadDocs } from '../substrate/on-read-node.js';
|
|
17
|
+
import { emptyContextExposureState, exposureTarget } from '../substrate/injected-store.js';
|
|
17
18
|
let canvasHome;
|
|
18
19
|
let fakeHome;
|
|
19
20
|
let prevHomeEnv;
|
|
@@ -58,7 +59,7 @@ test('a store nested in the crouter home does not leak into a canvas-artifact re
|
|
|
58
59
|
const reportFile = join(fakeHome, '.crouter', 'canvas', 'nodes', 'somenode', 'reports', 'r.md');
|
|
59
60
|
mkdirSync(join(fakeHome, '.crouter', 'canvas', 'nodes', 'somenode', 'reports'), { recursive: true });
|
|
60
61
|
writeFileSync(reportFile, '# a node report\n');
|
|
61
|
-
const rendered = renderOnReadDocs(node, reportFile,
|
|
62
|
+
const rendered = renderOnReadDocs(node, reportFile, exposureTarget(emptyContextExposureState(), 'transcript'));
|
|
62
63
|
assert.ok(!rendered.includes('PRIVATE ASSISTANT MEMORY MUST NOT LEAK'), `crouter-home-nested store must not surface on a canvas read; got: ${rendered}`);
|
|
63
64
|
assert.ok(!rendered.includes('private-thing'), `crouter-home-nested store name must not surface on a canvas read; got: ${rendered}`);
|
|
64
65
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// On-read context surfaces
|
|
2
|
-
// at nodes/<id>/injected-docs.json
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
1
|
+
// On-read context surfaces once per loaded context at the requested rung. The
|
|
2
|
+
// exposure ledger lives at nodes/<id>/injected-docs.json, survives a strict
|
|
3
|
+
// resume, and resets at a fresh-context boundary. A project store delivers
|
|
4
|
+
// only once it declares its namespace, and an explicit frontmatter `name` is
|
|
5
|
+
// store-local: the injected envelope carries the composed canonical name.
|
|
6
6
|
//
|
|
7
7
|
// Run: node --import tsx/esm --test src/core/__tests__/on-read-dedup-resume.test.ts
|
|
8
8
|
import { test, before, beforeEach, after } from 'node:test';
|
|
@@ -14,7 +14,18 @@ import { closeDb } from '../canvas/db.js';
|
|
|
14
14
|
import { resetScopeCache } from '../scope.js';
|
|
15
15
|
import { spawnNode } from '../runtime/nodes.js';
|
|
16
16
|
import { renderOnReadDocs } from '../substrate/on-read-node.js';
|
|
17
|
-
import {
|
|
17
|
+
import { clearContextExposureState, emptyContextExposureState, exposureTarget, loadContextExposureState, saveContextExposureState, } from '../substrate/injected-store.js';
|
|
18
|
+
function freshTranscriptTarget() {
|
|
19
|
+
return exposureTarget(emptyContextExposureState(), 'transcript');
|
|
20
|
+
}
|
|
21
|
+
/** A repository-root project store mounts only once its root INDEX declares
|
|
22
|
+
* the namespace its documents compose under. */
|
|
23
|
+
function declareRepositoryRoot(memDir) {
|
|
24
|
+
mkdirSync(memDir, { recursive: true });
|
|
25
|
+
writeFileSync(join(memDir, 'INDEX.md'), '---\nnamespace: work\nkind: knowledge\n' +
|
|
26
|
+
'when-and-why-to-read: When working in this workspace, this knowledge should be read because it is the front door.\n---\n' +
|
|
27
|
+
'FRONT DOOR BODY\n');
|
|
28
|
+
}
|
|
18
29
|
const FIXTURE_BODY = 'Fixture body that must surface exactly once.';
|
|
19
30
|
let home;
|
|
20
31
|
let work;
|
|
@@ -56,25 +67,25 @@ test('project and user memory docs without read surfaces do not fire on file rea
|
|
|
56
67
|
'when-and-why-to-read: When reading any file, this preference should be read because this fixture must not fire without an explicit route\n---\n' +
|
|
57
68
|
'NOISY USER BODY\n');
|
|
58
69
|
const projectMemDir = join(fakeHome, 'work', '.crouter', 'memory');
|
|
59
|
-
|
|
70
|
+
declareRepositoryRoot(projectMemDir);
|
|
60
71
|
writeFileSync(join(projectMemDir, 'local.md'), '---\nkind: knowledge\n' +
|
|
61
72
|
'when-and-why-to-read: When reading project files, this reference should be read because this fixture proves explicit file routing\n---\n' +
|
|
62
73
|
'LOCAL PROJECT BODY\n');
|
|
63
74
|
const readFile = join(fakeHome, 'work', 'ideas.md');
|
|
64
75
|
writeFileSync(readFile, 'turn off xml closer\n');
|
|
65
|
-
const rendered = renderOnReadDocs(node, readFile,
|
|
76
|
+
const rendered = renderOnReadDocs(node, readFile, freshTranscriptTarget());
|
|
66
77
|
assert.ok(!rendered.includes('NOISY USER BODY'), 'user-global doc has no explicit file route');
|
|
67
78
|
assert.ok(!rendered.includes('LOCAL PROJECT BODY'), 'project doc also has no implicit file route');
|
|
68
79
|
assert.equal(rendered, '', 'no file context renders without an explicit trigger');
|
|
69
80
|
rmSync(fakeHome, { recursive: true, force: true });
|
|
70
81
|
});
|
|
71
|
-
test('on-read
|
|
82
|
+
test('on-read exposure survives a strict resume and resets for a fresh context', () => {
|
|
72
83
|
const node = spawnNode({ kind: 'general', cwd: work, parent: null }).node_id;
|
|
73
84
|
// A file-routed substrate doc in a `.crouter/memory/` ancestor. The
|
|
74
85
|
// `./`-anchored glob pins the repo-relative anchor: `src/file.ts` matches
|
|
75
86
|
// only relative to the doc's owning repo dir, never absolutely.
|
|
76
87
|
const memDir = join(work, '.crouter', 'memory');
|
|
77
|
-
|
|
88
|
+
declareRepositoryRoot(memDir);
|
|
78
89
|
writeFileSync(join(memDir, 'onread-fixture.md'), '---\nkind: knowledge\n' +
|
|
79
90
|
'when-and-why-to-read: When reading work files, this reference should be read because it is the on-read regression fixture\n' +
|
|
80
91
|
'surfaces:\n' +
|
|
@@ -84,25 +95,26 @@ test('on-read doc surfaces once, stays deduped across a revive(resume), re-surfa
|
|
|
84
95
|
mkdirSync(join(work, 'src'), { recursive: true });
|
|
85
96
|
writeFileSync(readFile, 'export const x = 1;\n');
|
|
86
97
|
// --- pi process #1: cold load; first read surfaces the doc ---
|
|
87
|
-
const
|
|
88
|
-
const
|
|
98
|
+
const state = loadContextExposureState(node);
|
|
99
|
+
const transcript = exposureTarget(state, 'transcript');
|
|
100
|
+
const first = renderOnReadDocs(node, readFile, transcript);
|
|
89
101
|
assert.match(first, new RegExp(FIXTURE_BODY.replace(/[.]/g, '\\.')), 'first read surfaces the doc body');
|
|
90
|
-
|
|
91
|
-
//
|
|
92
|
-
assert.ok(!renderOnReadDocs(node, readFile,
|
|
93
|
-
// --- dormancy → revive(resume): NEW process rehydrates the
|
|
94
|
-
const
|
|
95
|
-
assert.deepEqual(
|
|
96
|
-
assert.ok(!renderOnReadDocs(node, readFile,
|
|
97
|
-
// --- fresh launch (revive resume=false, e.g. a refresh-yield) clears the
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
assert.ok(renderOnReadDocs(node, readFile,
|
|
102
|
+
saveContextExposureState(node, state);
|
|
103
|
+
// The active ledger suppresses an equal-rung repeat.
|
|
104
|
+
assert.ok(!renderOnReadDocs(node, readFile, transcript).includes(FIXTURE_BODY), 'same-context repeat read is already exposed');
|
|
105
|
+
// --- dormancy → revive(resume): NEW process rehydrates the state from disk ---
|
|
106
|
+
const stateAfterRevive = loadContextExposureState(node);
|
|
107
|
+
assert.deepEqual(stateAfterRevive, state, 'revive(resume) rehydrates the persisted context state');
|
|
108
|
+
assert.ok(!renderOnReadDocs(node, readFile, exposureTarget(stateAfterRevive, 'transcript')).includes(FIXTURE_BODY), 'strict resume does not re-deliver the document');
|
|
109
|
+
// --- fresh launch (revive resume=false, e.g. a refresh-yield) clears the state ---
|
|
110
|
+
clearContextExposureState(node);
|
|
111
|
+
const stateFresh = loadContextExposureState(node);
|
|
112
|
+
assert.ok(renderOnReadDocs(node, readFile, exposureTarget(stateFresh, 'transcript')).includes(FIXTURE_BODY), 'a fresh transcript surfaces the doc again');
|
|
101
113
|
});
|
|
102
114
|
test('an explicitly routed doc injects on-read under its frontmatter name, not the path-derived one', () => {
|
|
103
115
|
const node = spawnNode({ kind: 'general', cwd: work, parent: null }).node_id;
|
|
104
116
|
const memDir = join(work, '.crouter', 'memory');
|
|
105
|
-
|
|
117
|
+
declareRepositoryRoot(memDir);
|
|
106
118
|
writeFileSync(join(memDir, 'physical-guide.md'), '---\nkind: knowledge\n' +
|
|
107
119
|
'name: acme-project\n' +
|
|
108
120
|
'when-and-why-to-read: When reading acme source, this knowledge should be read because it carries the project-specific implementation constraints.\n' +
|
|
@@ -112,8 +124,8 @@ test('an explicitly routed doc injects on-read under its frontmatter name, not t
|
|
|
112
124
|
const readFile = join(work, 'src', 'file.ts');
|
|
113
125
|
mkdirSync(join(work, 'src'), { recursive: true });
|
|
114
126
|
writeFileSync(readFile, 'export const x = 1;\n');
|
|
115
|
-
const rendered = renderOnReadDocs(node, readFile,
|
|
127
|
+
const rendered = renderOnReadDocs(node, readFile, freshTranscriptTarget());
|
|
116
128
|
assert.ok(rendered.includes('ACME PROJECT OPERATING GUIDE'), 'the matching explicit route fires');
|
|
117
|
-
assert.ok(rendered.includes('<memory kind="knowledge" name="acme-project"'), `expected explicit
|
|
118
|
-
assert.ok(!rendered.includes('
|
|
129
|
+
assert.ok(rendered.includes('<memory kind="knowledge" name="work/acme-project"'), `expected the explicit local name composed under the store's namespace, got: ${rendered}`);
|
|
130
|
+
assert.ok(!rendered.includes('work/physical-guide'), 'must not fall back to the path-derived name when frontmatter sets one');
|
|
119
131
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// A `.crouter/memory` store nested BELOW a mounted project root is delivered
|
|
2
2
|
// by the read path, not by boot: its `./**`-routed docs surface on the first
|
|
3
|
-
// read beneath the store's owning dir
|
|
4
|
-
// workspace-open render
|
|
3
|
+
// read beneath the store's owning dir and share the active transcript exposure
|
|
4
|
+
// ledger with the workspace-open render. The `./`
|
|
5
5
|
// anchor stays fenced to project-store docs — a hand-authored `./` glob in a
|
|
6
6
|
// user-scope doc must never match a read.
|
|
7
7
|
//
|
|
@@ -16,6 +16,10 @@ import { resetScopeCache } from '../scope.js';
|
|
|
16
16
|
import { spawnNode } from '../runtime/nodes.js';
|
|
17
17
|
import { renderOnReadDocs, renderWorkspaceOpenDocs } from '../substrate/on-read-node.js';
|
|
18
18
|
import { clearSessionCache } from '../substrate/session-cache.js';
|
|
19
|
+
import { emptyContextExposureState, exposureTarget, } from '../substrate/injected-store.js';
|
|
20
|
+
function freshTranscriptTarget() {
|
|
21
|
+
return exposureTarget(emptyContextExposureState(), 'transcript');
|
|
22
|
+
}
|
|
19
23
|
const ROOT_BODY = 'ROOT WORKSPACE FRONT DOOR BODY';
|
|
20
24
|
const NESTED_BODY = 'NESTED PKG FRONT DOOR BODY';
|
|
21
25
|
let canvasHome;
|
|
@@ -44,14 +48,14 @@ after(() => {
|
|
|
44
48
|
else
|
|
45
49
|
process.env['HOME'] = prevHomeEnv;
|
|
46
50
|
});
|
|
47
|
-
/** A work tree with a root front door
|
|
48
|
-
* carrying the explicit front-door pair (workspace-open
|
|
49
|
-
* Returns the work dir. */
|
|
51
|
+
/** A work tree with a root front door declaring the repository namespace and a
|
|
52
|
+
* nested store front door carrying the explicit front-door pair (workspace-open
|
|
53
|
+
* + `./**` read), but no nested namespace declaration. Returns the work dir. */
|
|
50
54
|
function makeWorkTree() {
|
|
51
55
|
const work = join(fakeHome, 'work');
|
|
52
56
|
const rootStore = join(work, '.crouter', 'memory');
|
|
53
57
|
mkdirSync(rootStore, { recursive: true });
|
|
54
|
-
writeFileSync(join(rootStore, 'INDEX.md'), '---\nkind: knowledge\n' +
|
|
58
|
+
writeFileSync(join(rootStore, 'INDEX.md'), '---\nnamespace: work\nkind: knowledge\n' +
|
|
55
59
|
'when-and-why-to-read: When working in this workspace, this knowledge should be read because it is the operating guide.\n' +
|
|
56
60
|
'surfaces:\n' +
|
|
57
61
|
' - {on: workspace-open, at: content}\n' +
|
|
@@ -73,26 +77,28 @@ test('a nested store front door surfaces on a read beneath its owning dir, not o
|
|
|
73
77
|
const inside = join(work, 'pkg', 'lib', 'file.ts');
|
|
74
78
|
mkdirSync(join(work, 'pkg', 'lib'), { recursive: true });
|
|
75
79
|
writeFileSync(inside, 'export const x = 1;\n');
|
|
76
|
-
const rendered = renderOnReadDocs(node, inside,
|
|
80
|
+
const rendered = renderOnReadDocs(node, inside, freshTranscriptTarget());
|
|
77
81
|
assert.ok(rendered.includes(NESTED_BODY), `read beneath pkg surfaces the nested front door; got: ${rendered}`);
|
|
78
82
|
const outside = join(work, 'docs', 'notes.ts');
|
|
79
83
|
mkdirSync(join(work, 'docs'), { recursive: true });
|
|
80
84
|
writeFileSync(outside, 'export const y = 2;\n');
|
|
81
|
-
const renderedOutside = renderOnReadDocs(node, outside,
|
|
85
|
+
const renderedOutside = renderOnReadDocs(node, outside, freshTranscriptTarget());
|
|
82
86
|
assert.ok(!renderedOutside.includes(NESTED_BODY), `read outside pkg must not surface the nested front door; got: ${renderedOutside}`);
|
|
83
87
|
});
|
|
84
|
-
test('workspace-open and on-read share one
|
|
88
|
+
test('workspace-open and on-read share one exposure ledger', () => {
|
|
85
89
|
const work = makeWorkTree();
|
|
86
90
|
const node = spawnNode({ kind: 'general', cwd: work, parent: null }).node_id;
|
|
87
|
-
const
|
|
88
|
-
const opened = renderWorkspaceOpenDocs(node,
|
|
91
|
+
const transcript = freshTranscriptTarget();
|
|
92
|
+
const opened = renderWorkspaceOpenDocs(node, transcript);
|
|
89
93
|
assert.ok(opened.includes(ROOT_BODY), 'workspace open delivers the mounted root front door');
|
|
94
|
+
assert.ok(opened.includes('name="work"'), `a store's root INDEX answers at its declared namespace, with no trailing INDEX segment; got: ${opened}`);
|
|
90
95
|
assert.ok(!opened.includes(NESTED_BODY), 'boot stays flat: the nested store is not in the mounted stack');
|
|
91
96
|
const readFile = join(work, 'pkg', 'lib', 'file.ts');
|
|
92
97
|
mkdirSync(join(work, 'pkg', 'lib'), { recursive: true });
|
|
93
98
|
writeFileSync(readFile, 'export const x = 1;\n');
|
|
94
|
-
const onRead = renderOnReadDocs(node, readFile,
|
|
99
|
+
const onRead = renderOnReadDocs(node, readFile, transcript);
|
|
95
100
|
assert.ok(onRead.includes(NESTED_BODY), 'the nested front door delivers on the first read beneath it');
|
|
101
|
+
assert.ok(onRead.includes('name="work/pkg"'), 'the nested front door answers under the root namespace plus owner path');
|
|
96
102
|
assert.ok(!onRead.includes(ROOT_BODY), 'the root front door already delivered at workspace open — never twice');
|
|
97
103
|
});
|
|
98
104
|
test('a hand-authored `./`-anchored read glob in a user-scope doc never matches a read', () => {
|
|
@@ -114,7 +120,7 @@ test('a hand-authored `./`-anchored read glob in a user-scope doc never matches
|
|
|
114
120
|
const readFile = join(fakeHome, 'notes', 'thing.md');
|
|
115
121
|
mkdirSync(join(fakeHome, 'notes'), { recursive: true });
|
|
116
122
|
writeFileSync(readFile, '# a note\n');
|
|
117
|
-
const rendered = renderOnReadDocs(node, readFile,
|
|
123
|
+
const rendered = renderOnReadDocs(node, readFile, freshTranscriptTarget());
|
|
118
124
|
assert.ok(rendered.includes('USER CONTROL BODY'), 'the glob-routed user doc proves the user store is in the corpus');
|
|
119
125
|
assert.ok(!rendered.includes('USER DOT BODY MUST NOT FIRE'), `the \`./\` anchor is project-store only — a user doc's owning root is ~; got: ${rendered}`);
|
|
120
126
|
});
|