@arnilo/prism 0.11.0 → 0.12.0

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.
Files changed (89) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +28 -27
  3. package/dist/cli-dev.js +1 -1
  4. package/dist/cli-init.js +4 -4
  5. package/dist/cli-provider-add.js +2 -2
  6. package/dist/field-evidence.d.ts +53 -0
  7. package/dist/field-evidence.js +143 -0
  8. package/dist/index.d.ts +3 -1
  9. package/dist/index.js +2 -1
  10. package/dist/run-bundle.js +2 -2
  11. package/docs/acp-agent.md +2 -2
  12. package/docs/ag-ui.md +1 -1
  13. package/docs/attention-compiler.md +1 -1
  14. package/docs/cli-rpc.md +2 -2
  15. package/docs/coding-tools.md +3 -3
  16. package/docs/compaction-observational-memory.md +13 -3
  17. package/docs/computer-use-linux.md +2 -2
  18. package/docs/core.md +9 -10
  19. package/docs/dev-inspector.md +1 -1
  20. package/docs/disaster-recovery.md +1 -1
  21. package/docs/document-extraction.md +140 -0
  22. package/docs/evaluations.md +1 -1
  23. package/docs/execution-timeline.md +30 -0
  24. package/docs/guardrails.md +53 -0
  25. package/docs/history/130-cyclic-workflows-primitive-review.md +293 -0
  26. package/docs/history/132-document-ingestion-primitive-review.md +124 -0
  27. package/docs/history/README.md +2 -0
  28. package/docs/history/migrate-to-0.12.0.md +57 -0
  29. package/docs/history/retire-node-runtime.md +59 -0
  30. package/docs/hooks.md +1 -1
  31. package/docs/host-compositions.md +2 -2
  32. package/docs/host-security.md +1 -1
  33. package/docs/index.md +30 -25
  34. package/docs/live-testing.md +1 -1
  35. package/docs/mcp-tools.md +2 -2
  36. package/docs/messaging-channels.md +1 -1
  37. package/docs/migrate-to-0.7.md +1 -1
  38. package/docs/migrate-to-0.8.md +2 -2
  39. package/docs/migrate-to-0.9.md +1 -1
  40. package/docs/migration.md +2 -2
  41. package/docs/model-registry.md +1 -1
  42. package/docs/model-routing.md +21 -0
  43. package/docs/multi-agent-patterns.md +77 -4
  44. package/docs/obscura.md +1 -1
  45. package/docs/openapi-tools.md +1 -1
  46. package/docs/operations.md +2 -2
  47. package/docs/peer-dependencies.md +18 -17
  48. package/docs/performance.md +32 -32
  49. package/docs/prompt-registry.md +1 -1
  50. package/docs/provider-packages.md +23 -21
  51. package/docs/providers/decisions.md +134 -0
  52. package/docs/providers/laya.md +116 -0
  53. package/docs/providers/typesafe.md +148 -0
  54. package/docs/rag.md +17 -5
  55. package/docs/release-and-install.md +119 -113
  56. package/docs/run-bundle.md +4 -0
  57. package/docs/runs-and-usage.md +1 -1
  58. package/docs/signal-channel.md +1 -1
  59. package/docs/sqlite-persistence.md +5 -5
  60. package/docs/synapta-integration-review.md +238 -0
  61. package/docs/telegram-channel.md +1 -1
  62. package/docs/testing.md +33 -14
  63. package/docs/wiki.md +12 -11
  64. package/docs/work-sandbox.md +2 -0
  65. package/docs/work-tools.md +3 -3
  66. package/docs/workflows.md +213 -11
  67. package/package.json +22 -21
  68. package/templates/business-worker/README.md.tmpl +3 -3
  69. package/templates/business-worker/package.json.tmpl +4 -5
  70. package/templates/business-worker/src/tests/agent.test.ts.tmpl +1 -1
  71. package/templates/business-worker/tsconfig.json.tmpl +2 -1
  72. package/templates/deep-research/README.md.tmpl +4 -4
  73. package/templates/deep-research/package.json.tmpl +4 -5
  74. package/templates/deep-research/src/tests/research.test.ts.tmpl +1 -1
  75. package/templates/deep-research/tsconfig.json.tmpl +2 -1
  76. package/templates/init/README.md.tmpl +3 -3
  77. package/templates/init/optional/evals-example.ts.tmpl +1 -1
  78. package/templates/init/optional/workflows-example.ts.tmpl +1 -1
  79. package/templates/init/package.json.tmpl +4 -5
  80. package/templates/init/src/tests/agent.test.ts.tmpl +1 -1
  81. package/templates/init/tsconfig.json.tmpl +2 -1
  82. package/templates/personal-assistant/README.md.tmpl +3 -3
  83. package/templates/personal-assistant/package.json.tmpl +4 -5
  84. package/templates/personal-assistant/src/tests/agent.test.ts.tmpl +1 -1
  85. package/templates/personal-assistant/tsconfig.json.tmpl +2 -1
  86. package/templates/provider/README.md.tmpl +2 -2
  87. package/templates/provider/package.json.tmpl +3 -4
  88. package/templates/provider/src/tests/provider.test.ts.tmpl +1 -1
  89. package/templates/provider/tsconfig.json.tmpl +1 -1
@@ -5,114 +5,117 @@
5
5
  ## What it does
6
6
 
7
7
 
8
- Prism's current **0.11.0** line has **12 publishable manifests**: the root `@arnilo/prism` core package plus **11 workspace packages** — **20 provider adapters** (20 provider adapter subpaths inside the `@arnilo/prism-providers` family), 4 `prism-*` family packages, and 7 capability packages. (Generated by `node scripts/package-truth.mjs` → `scripts/package-truth.json` — the manifest-derived single source for counts, provider membership, umbrella closures, and profile closures.) The last lockstep cut was 0.3.0; Decision B now publishes changed packages independently inside `^0.3.0` — the plan 039 changed-package cut moved root `@arnilo/prism` and every plan-035+ changed package to **0.3.1**, and the plan 050 changed-package cut moved root plus four changed packages to **0.3.2**; the plan 041-044 changed-package cut moves root to **0.3.3** with `@arnilo/prism-memory@0.3.2` (composite recall scoring), `@arnilo/prism-evals@0.3.1` (trace-to-dataset curation), the three session-store packages at **0.3.1** (run-ledger `promptVersion` provenance), and the initial `@arnilo/prism-prompts@0.0.1` (independent opt-in, outside `prism-all`); plan 054 consolidation then folded `@arnilo/prism-browser` and `@arnilo/prism-obscura` into the `@arnilo/prism-web-tools` family as `/browser` and `/obscura` subpaths, folded `@arnilo/prism-rag`, both compaction strategies, `@arnilo/prism-graft`, and `@arnilo/prism-wiki` into the `@arnilo/prism-memory` family as `/rag`, `/compaction/llm`, `/compaction/observational-memory`, `/graft`, and `/wiki` subpaths (deleting the `@arnilo/prism-compaction` profile), and folded all 17 `@arnilo/prism-provider-*` packages into the `@arnilo/prism-providers` family as `/<adapter>` subpaths (Azure/Bedrock/Vertex stop being special all-only manifests); independent publication continues inside `^0.3.0` ranges (which satisfy 0.3.1, 0.3.2, and 0.3.3). This page describes how they are packed, what each tarball contains, how to install them, the required non-optional **caret** `@arnilo/prism@^0.11.0` peer range, the release workflow, and the offline test budget. The measurable 1.0 readiness gates (command-per-gate) live in [`0.1.0-readiness.md`](history/./0.1.0-readiness.md).
8
+ Prism's current **0.12.0** line has **12 publishable manifests**: the root `@arnilo/prism` core package plus **11 workspace packages** — **22 provider adapters** (22 provider adapter subpaths inside the `@arnilo/prism-providers` family), 4 `prism-*` family packages, and 7 capability packages. (Generated by `bun scripts/package-truth.mjs` → `scripts/package-truth.json` — the manifest-derived single source for counts, provider membership, umbrella closures, and profile closures.) The last lockstep cut was 0.3.0; Decision B now publishes changed packages independently inside `^0.3.0` — the plan 039 changed-package cut moved root `@arnilo/prism` and every plan-035+ changed package to **0.3.1**, and the plan 050 changed-package cut moved root plus four changed packages to **0.3.2**; the plan 041-044 changed-package cut moves root to **0.3.3** with `@arnilo/prism-memory@0.3.2` (composite recall scoring), `@arnilo/prism-evals@0.3.1` (trace-to-dataset curation), the three session-store packages at **0.3.1** (run-ledger `promptVersion` provenance), and the initial `@arnilo/prism-prompts@0.0.1` (independent opt-in, outside `prism-all`); plan 054 consolidation then folded `@arnilo/prism-browser` and `@arnilo/prism-obscura` into the `@arnilo/prism-web-tools` family as `/browser` and `/obscura` subpaths, folded `@arnilo/prism-rag`, both compaction strategies, `@arnilo/prism-graft`, and `@arnilo/prism-wiki` into the `@arnilo/prism-memory` family as `/rag`, `/compaction/llm`, `/compaction/observational-memory`, `/graft`, and `/wiki` subpaths (deleting the `@arnilo/prism-compaction` profile), and folded all 17 `@arnilo/prism-provider-*` packages into the `@arnilo/prism-providers` family as `/<adapter>` subpaths (Azure/Bedrock/Vertex stop being special all-only manifests); independent publication continues inside `^0.3.0` ranges (which satisfy 0.3.1, 0.3.2, and 0.3.3). This page describes how they are packed, what each tarball contains, how to install them, the required non-optional **caret** `@arnilo/prism@^0.12.0` peer range, the release workflow, and the offline test budget. The measurable 1.0 readiness gates (command-per-gate) live in [`0.1.0-readiness.md`](history/./0.1.0-readiness.md).
9
9
 
10
10
  Core `@arnilo/prism` ships runtime, CLI, templates, and docs. Every code package has a required `@arnilo/prism` peer inside the Decision B window — the caret current spec is `@arnilo/prism@^0.3.3` and every declared window peer satisfies it: packages republishing in the plan 050 cut carry `^0.3.2`; the plan 039 set keeps `^0.3.1`; unchanged packages keep their `^0.3.0` peer; profiles are pure manifests. The plan 050 republished set declares the required `@arnilo/prism@^0.3.2` peer; the plan 041-044 republished set keeps its existing `^0.3.0` window peer; unchanged packages keep their prior window. Installation activates no provider, listener, database, browser, credential, or tool capability.
11
11
 
12
- The **0.11.0 lockstep cut** moved all **twelve** manifests together. The current declared peer is `@arnilo/prism@^0.11.0` on every package, and `release.mjs` lockstep mode fails closed on any internal range that merely satisfies the cut version instead of matching it. The **0.6.0–0.10.0 lockstep cuts** each moved their then-current manifest set together (`@arnilo/prism-hooks` arrived in the 0.10.0 cut). The independent-publication history above (0.3.x, 0.4.x, 0.5.x) describes how the line grew when packages moved separately.
12
+ The **0.12.0 lockstep cut** moved all **twelve** manifests together. The current declared peer is `@arnilo/prism@^0.12.0` on every package, and `release.mjs` lockstep mode fails closed on any internal range that merely satisfies the cut version instead of matching it. The **0.6.0–0.11.1 lockstep cuts** each moved their then-current manifest set together (`@arnilo/prism-hooks` arrived in the 0.10.0 cut). The independent-publication history above (0.3.x, 0.4.x, 0.5.x) describes how the line grew when packages moved separately.
13
13
 
14
14
  <!-- generated:package-truth:inventory begin -->
15
- **12 publishable manifests** — root `@arnilo/prism` plus 11 workspace packages (4 `prism-*` family packages, 7 capability packages). Generated by `node scripts/package-truth.mjs --emit-docs` — do not hand-edit.
15
+ **12 publishable manifests** — root `@arnilo/prism` plus 11 workspace packages (4 `prism-*` family packages, 7 capability packages). Generated by `bun scripts/package-truth.mjs --emit-docs` — do not hand-edit.
16
16
 
17
17
  | package | version | notes |
18
18
  | --- | --- | --- |
19
- | `@arnilo/prism` | 0.11.0 | core — runtime, CLI/RPC, templates, docs |
20
- | `@arnilo/prism-channels` | 0.11.0 | family — transport-neutral messaging runtime, durable journal, pairing and one-use approvals; official /telegram (private DMs, opt-in granted groups/topics) and experimental pinned signal-cli /signal |
21
- | `@arnilo/prism-coding-tools` | 0.11.0 | family — /agent, /security, /openapi, /computer-use-linux, /dev, /impeccable subpaths |
22
- | `@arnilo/prism-core` | 0.11.0 | family — /runtime, /sessions, /governance, /credentials, /enterprise, /validation subpaths |
23
- | `@arnilo/prism-providers` | 0.11.0 | family — all provider adapters as `/<adapter>` subpaths |
24
- | `@arnilo/prism-acp-agent` | 0.11.0 | capability — ACP adapter |
25
- | `@arnilo/prism-ag-ui` | 0.11.0 | capability — AG-UI/A2A/A2UI adapter |
26
- | `@arnilo/prism-hooks` | 0.11.0 | capability — Claude/Codex-compatible hooks.json adapter compiled onto middleware, guardrail, injector, and stop-hook seams |
27
- | `@arnilo/prism-mcp` | 0.11.0 | capability — MCP client/server/OAuth interop |
28
- | `@arnilo/prism-memory` | 0.11.0 | capability — memory plus /rag, /compaction/*, /fabric, /wiki subpaths |
29
- | `@arnilo/prism-web-tools` | 0.11.0 | capability — Brave/Exa/Firecrawl plus peer-gated /browser and /obscura subpaths |
30
- | `@arnilo/prism-work` | 0.11.0 | capability — /connectors, /documents, /sheets, /diagrams, /document-reader, /sandbox, /skills, /tools subpaths |
19
+ | `@arnilo/prism` | 0.12.0 | core — runtime, CLI/RPC, templates, docs |
20
+ | `@arnilo/prism-channels` | 0.12.0 | family — transport-neutral messaging runtime, durable journal, pairing and one-use approvals; official /telegram (private DMs, opt-in granted groups/topics) and experimental pinned signal-cli /signal |
21
+ | `@arnilo/prism-coding-tools` | 0.12.0 | family — /agent, /security, /openapi, /computer-use-linux, /dev, /impeccable subpaths |
22
+ | `@arnilo/prism-core` | 0.12.0 | family — /runtime, /sessions, /governance, /credentials, /enterprise, /validation subpaths |
23
+ | `@arnilo/prism-providers` | 0.12.0 | family — all provider adapters as `/<adapter>` subpaths |
24
+ | `@arnilo/prism-acp-agent` | 0.12.0 | capability — ACP adapter |
25
+ | `@arnilo/prism-ag-ui` | 0.12.0 | capability — AG-UI/A2A/A2UI adapter |
26
+ | `@arnilo/prism-hooks` | 0.12.0 | capability — Claude/Codex-compatible hooks.json adapter compiled onto middleware, guardrail, injector, and stop-hook seams |
27
+ | `@arnilo/prism-mcp` | 0.12.0 | capability — MCP client/server/OAuth interop |
28
+ | `@arnilo/prism-memory` | 0.12.0 | capability — memory plus /rag, /compaction/*, /fabric, /wiki subpaths |
29
+ | `@arnilo/prism-web-tools` | 0.12.0 | capability — Brave/Exa/Firecrawl plus peer-gated /browser and /obscura subpaths |
30
+ | `@arnilo/prism-work` | 0.12.0 | capability — /connectors, /documents, /sheets, /diagrams, /document-extraction, /document-reader, /sandbox, /skills, /tools subpaths |
31
31
  <!-- generated:package-truth:inventory end -->
32
32
 
33
33
 
34
34
  <!-- generated:package-truth:providers begin -->
35
- **20 provider adapters** — first-party adapters ship as `@arnilo/prism-providers/<adapter>` subpaths in one tarball (importing one never evaluates another):
35
+ **22 provider adapters** — first-party adapters ship as `@arnilo/prism-providers/<adapter>` subpaths in one tarball (importing one never evaluates another):
36
36
 
37
37
  | adapter package | version |
38
38
  | --- | --- |
39
- | `@arnilo/prism-providers/ai-sdk` | 0.11.0 |
40
- | `@arnilo/prism-providers/alibaba` | 0.11.0 |
41
- | `@arnilo/prism-providers/anthropic` | 0.11.0 |
42
- | `@arnilo/prism-providers/azure` | 0.11.0 |
43
- | `@arnilo/prism-providers/bedrock` | 0.11.0 |
44
- | `@arnilo/prism-providers/clinepass` | 0.11.0 |
45
- | `@arnilo/prism-providers/commandcode` | 0.11.0 |
46
- | `@arnilo/prism-providers/deepseek` | 0.11.0 |
47
- | `@arnilo/prism-providers/google` | 0.11.0 |
48
- | `@arnilo/prism-providers/hyper` | 0.11.0 |
49
- | `@arnilo/prism-providers/kimi` | 0.11.0 |
50
- | `@arnilo/prism-providers/model-discovery` | 0.11.0 |
51
- | `@arnilo/prism-providers/neuralwatt` | 0.11.0 |
52
- | `@arnilo/prism-providers/ollama` | 0.11.0 |
53
- | `@arnilo/prism-providers/openai` | 0.11.0 |
54
- | `@arnilo/prism-providers/opencode-go` | 0.11.0 |
55
- | `@arnilo/prism-providers/openrouter` | 0.11.0 |
56
- | `@arnilo/prism-providers/vertex` | 0.11.0 |
57
- | `@arnilo/prism-providers/xai` | 0.11.0 |
58
- | `@arnilo/prism-providers/zai` | 0.11.0 |
39
+ | `@arnilo/prism-providers/ai-sdk` | 0.12.0 |
40
+ | `@arnilo/prism-providers/alibaba` | 0.12.0 |
41
+ | `@arnilo/prism-providers/anthropic` | 0.12.0 |
42
+ | `@arnilo/prism-providers/azure` | 0.12.0 |
43
+ | `@arnilo/prism-providers/bedrock` | 0.12.0 |
44
+ | `@arnilo/prism-providers/clinepass` | 0.12.0 |
45
+ | `@arnilo/prism-providers/commandcode` | 0.12.0 |
46
+ | `@arnilo/prism-providers/deepseek` | 0.12.0 |
47
+ | `@arnilo/prism-providers/google` | 0.12.0 |
48
+ | `@arnilo/prism-providers/hyper` | 0.12.0 |
49
+ | `@arnilo/prism-providers/kimi` | 0.12.0 |
50
+ | `@arnilo/prism-providers/laya` | 0.12.0 |
51
+ | `@arnilo/prism-providers/model-discovery` | 0.12.0 |
52
+ | `@arnilo/prism-providers/neuralwatt` | 0.12.0 |
53
+ | `@arnilo/prism-providers/ollama` | 0.12.0 |
54
+ | `@arnilo/prism-providers/openai` | 0.12.0 |
55
+ | `@arnilo/prism-providers/opencode-go` | 0.12.0 |
56
+ | `@arnilo/prism-providers/openrouter` | 0.12.0 |
57
+ | `@arnilo/prism-providers/typesafe` | 0.12.0 |
58
+ | `@arnilo/prism-providers/vertex` | 0.12.0 |
59
+ | `@arnilo/prism-providers/xai` | 0.12.0 |
60
+ | `@arnilo/prism-providers/zai` | 0.12.0 |
59
61
  <!-- generated:package-truth:providers end -->
60
62
 
61
63
 
62
- Core ships `dist`, docs, templates, and `CHANGELOG.md`; code packages ship compiled output, README, license, and changelog. Family/profile packages ship manifest, README, and changelog. `@arnilo/prism-providers` is the unified provider family: all provider adapters ship as `dist/<adapter>` subpaths in one tarball (Azure/Bedrock/Vertex included), with the required `@arnilo/prism` peer as the only dependency and `@ai-sdk/provider` an optional peer of `/ai-sdk`. `@arnilo/prism-core` provides the unified runtime, sessions, governance, credentials, and enterprise persistence family package. `@arnilo/prism-channels` provides the transport-neutral messaging runtime, durable journal and pairing surface; it has only the required `@arnilo/prism` peer, while its SQLite/PostgreSQL conformance drivers are development-only. `@arnilo/prism-web-tools` provides the unified web tools family: root Brave/Exa/Firecrawl research tools plus `/browser` (Playwright-peer gated) and `/obscura` (host-binary + MCP gated) subpaths. `@arnilo/prism-memory` provides the unified memory and context family: root working/vector memory plus `/rag` (with `/rag/loaders` and `/rag/parsers`), `/compaction/llm`, `/compaction/observational-memory`, and `/wiki` subpaths, including the `prism-wiki` bin and bundled skills. `@arnilo/prism-coding-tools/dev` ships the loopback dev inspector — the `prism-dev` bin, the `prism dev` CLI composition, and the `/dev/cli` export the core CLI delegates to for `prism dev` (plan 040 Tasks 4–5); dev tooling is developer-time only and must never be the production API boundary. `@arnilo/prism-core/governance/prompts` (plan 042) is the versioned prompt registry: an explicit host opt-in with no first-party package depending on it — unlike `@arnilo/prism-memory` (a family member) and `@arnilo/prism-core/governance/evals` (used by the promotion helper as an optional peer). `@arnilo/prism-work` is the unified work family: `/connectors`, `/documents`, `/sheets`, `/diagrams`, `/document-reader`, `/sandbox`, `/skills`, and `/tools` subpaths in one tarball. Office dependencies are exact-pinned; `/document-reader` keeps `mammoth` and `pdf-parse` optional and fail-closed, while `playwright-core` remains a devDependency for gated draw.io testing. Importing `/connectors` never evaluates document dependencies.
64
+ Core ships `dist`, docs, templates, and `CHANGELOG.md`; code packages ship compiled output, README, license, and changelog. Family/profile packages ship manifest, README, and changelog. `@arnilo/prism-providers` is the unified provider family: all provider adapters ship as `dist/<adapter>` subpaths in one tarball (Azure/Bedrock/Vertex included), with the required `@arnilo/prism` peer as the only dependency and `@ai-sdk/provider` an optional peer of `/ai-sdk`. `@arnilo/prism-core` provides the unified runtime, sessions, governance, credentials, and enterprise persistence family package. `@arnilo/prism-channels` provides the transport-neutral messaging runtime, durable journal and pairing surface; it has only the required `@arnilo/prism` peer, while its SQLite/PostgreSQL conformance drivers are development-only. `@arnilo/prism-web-tools` provides the unified web tools family: root Brave/Exa/Firecrawl research tools plus `/browser` (Playwright-peer gated) and `/obscura` (host-binary + MCP gated) subpaths. `@arnilo/prism-memory` provides the unified memory and context family: root working/vector memory plus `/rag` (with `/rag/loaders` and `/rag/parsers`), `/compaction/llm`, `/compaction/observational-memory`, and `/wiki` subpaths, including the `prism-wiki` bin and bundled skills. `@arnilo/prism-coding-tools/dev` ships the loopback dev inspector — the `prism-dev` bin, the `prism dev` CLI composition, and the `/dev/cli` export the core CLI delegates to for `prism dev` (plan 040 Tasks 4–5); dev tooling is developer-time only and must never be the production API boundary. `@arnilo/prism-core/governance/prompts` (plan 042) is the versioned prompt registry: an explicit host opt-in with no first-party package depending on it — unlike `@arnilo/prism-memory` (a family member) and `@arnilo/prism-core/governance/evals` (used by the promotion helper as an optional peer). `@arnilo/prism-work` is the unified work family: `/connectors`, `/documents`, `/sheets`, `/diagrams`, `/document-extraction`, `/document-reader`, `/sandbox`, `/skills`, and `/tools` subpaths in one tarball. Office dependencies are exact-pinned; `/document-reader` keeps `mammoth` and `pdf-parse` optional and fail-closed, `/document-extraction` keeps `@firecrawl/anydoc@0.2.4` optional and local, while `playwright-core` remains a devDependency for gated draw.io testing. Importing `/connectors` never evaluates document dependencies.
63
65
 
64
66
  ## When to use it
65
67
 
66
68
 
67
69
  Use this page when installing Prism into a host app, when adding a first-party package, when cutting a release, or when investigating why a tarball contains (or excludes) a file.
68
70
 
69
- Consumers install the core package for the runtime and add first-party packages for provider adapters or compaction strategies. Each first-party package requires the `@arnilo/prism` peer at its declared version; install `@arnilo/prism` alongside them or npm will report an unmet peer.
71
+ Consumers install the core package for the runtime and add first-party packages for provider adapters or compaction strategies. Each first-party package requires the `@arnilo/prism` peer at its declared version, so install `@arnilo/prism` alongside them: `bun add` installs a first-party tarball without a missing-peer warning (probed on Bun 1.4.2, plan 125 Task 1), while npm fails closed on the same install — the peer is a contract the host satisfies explicitly, not something the installer enforces for you. Hosts keep their own supply chain: `bun install --frozen-lockfile` for reproducible installs and `bun audit --audit-level=moderate` for the dependency review.
70
72
 
71
73
  ## Inputs / request
72
74
 
73
75
 
74
76
  | Operation | Command |
75
77
  | --- | --- |
76
- | Install core only | `npm install @arnilo/prism` |
77
- | Scaffold a minimal project | `npx --package @arnilo/prism prism init my-agent [--provider openai] [--template <name>] [--list-templates] [--with-workflows] [--with-evals]` |
78
- | Install core runtime & persistence family | `npm install @arnilo/prism @arnilo/prism-core` |
79
- | Install messaging channels | `npm install @arnilo/prism @arnilo/prism-channels` |
80
- | Install core + all provider adapters | `npm install @arnilo/prism @arnilo/prism-providers` (import `@arnilo/prism-providers/<adapter>`) |
81
- | Install minimal runtime (replaces `@arnilo/prism`) | `npm install @arnilo/prism @arnilo/prism-core @arnilo/prism-memory` |
82
- | Install compaction strategies only | `npm install @arnilo/prism @arnilo/prism-memory` |
83
- | Install memory + RAG context family | `npm install @arnilo/prism @arnilo/prism-memory` (RAG: `@arnilo/prism-memory/rag`) |
84
- | Install coding tools (replaces `@arnilo/prism-coding-tools`) | `npm install @arnilo/prism @arnilo/prism-core @arnilo/prism-coding-tools @arnilo/prism-mcp @arnilo/prism-providers` |
85
- | Install application SDK (replaces `@arnilo/prism-core`) | `npm install @arnilo/prism @arnilo/prism-core @arnilo/prism-mcp @arnilo/prism-providers` |
86
- | Install selected families (replaces `@arnilo/prism-all`) | `npm install @arnilo/prism @arnilo/prism-core @arnilo/prism-providers @arnilo/prism-coding-tools @arnilo/prism-web-tools @arnilo/prism-memory @arnilo/prism-mcp` |
87
- | Install core + a single provider adapter | `npm install @arnilo/prism @arnilo/prism-providers` (import `@arnilo/prism-providers/openai`) |
88
- | 0.0.12 AG-UI (after release) | `npm install @arnilo/prism@0.0.12 @arnilo/prism-ag-ui@0.0.12` |
89
- | Install bounded web research tools | `npm install @arnilo/prism @arnilo/prism-web-tools @arnilo/prism-core` |
90
- | Install browser automation tools (Playwright-peer gated `/browser`) | `npm install @arnilo/prism @arnilo/prism-web-tools playwright-core@1.63.0` |
91
- | Install Obscura browser-engine tools (host supplies the binary; `/obscura`) | `npm install @arnilo/prism @arnilo/prism-web-tools @arnilo/prism-mcp` |
92
- | Install RAG retrieval (memory family `/rag`) | `npm install @arnilo/prism @arnilo/prism-memory` |
93
- | Install the Wiki CLI and skills (memory family `/wiki`) | `npm install @arnilo/prism @arnilo/prism-memory` (`npx prism-wiki --help`) |
94
- | Install work connectors | `npm install @arnilo/prism @arnilo/prism-work` (import `@arnilo/prism-work/connectors`) |
95
- | Install document/spreadsheet/presentation engine | `npm install @arnilo/prism @arnilo/prism-work` (import `@arnilo/prism-work/documents`) |
96
- | Install spreadsheet and CSV data engine | `npm install @arnilo/prism @arnilo/prism-work` (import `@arnilo/prism-work/sheets`) |
97
- | Install draw.io embed client & diagram engine | `npm install @arnilo/prism @arnilo/prism-work` (import `@arnilo/prism-work/diagrams`) |
98
- | Install bounded document reader | `npm install @arnilo/prism @arnilo/prism-work` (optional `pdf-parse mammoth`; import `@arnilo/prism-work/document-reader`) |
78
+ | Install core only | `bun add @arnilo/prism` |
79
+ | Scaffold a minimal project | `bunx --package @arnilo/prism prism init my-agent [--provider openai] [--template <name>] [--list-templates] [--with-workflows] [--with-evals]` |
80
+ | Install core runtime & persistence family | `bun add @arnilo/prism @arnilo/prism-core` |
81
+ | Install messaging channels | `bun add @arnilo/prism @arnilo/prism-channels` |
82
+ | Install core + all provider adapters | `bun add @arnilo/prism @arnilo/prism-providers` (import `@arnilo/prism-providers/<adapter>`) |
83
+ | Install minimal runtime (replaces `@arnilo/prism`) | `bun add @arnilo/prism @arnilo/prism-core @arnilo/prism-memory` |
84
+ | Install compaction strategies only | `bun add @arnilo/prism @arnilo/prism-memory` |
85
+ | Install memory + RAG context family | `bun add @arnilo/prism @arnilo/prism-memory` (RAG: `@arnilo/prism-memory/rag`) |
86
+ | Install coding tools (replaces `@arnilo/prism-coding-tools`) | `bun add @arnilo/prism @arnilo/prism-core @arnilo/prism-coding-tools @arnilo/prism-mcp @arnilo/prism-providers` |
87
+ | Install application SDK (replaces `@arnilo/prism-core`) | `bun add @arnilo/prism @arnilo/prism-core @arnilo/prism-mcp @arnilo/prism-providers` |
88
+ | Install selected families (replaces `@arnilo/prism-all`) | `bun add @arnilo/prism @arnilo/prism-core @arnilo/prism-providers @arnilo/prism-coding-tools @arnilo/prism-web-tools @arnilo/prism-memory @arnilo/prism-mcp` |
89
+ | Install core + a single provider adapter | `bun add @arnilo/prism @arnilo/prism-providers` (import `@arnilo/prism-providers/openai`) |
90
+ | 0.0.12 AG-UI (after release) | `bun add @arnilo/prism@0.0.12 @arnilo/prism-ag-ui@0.0.12` |
91
+ | Install bounded web research tools | `bun add @arnilo/prism @arnilo/prism-web-tools @arnilo/prism-core` |
92
+ | Install browser automation tools (Playwright-peer gated `/browser`) | `bun add @arnilo/prism @arnilo/prism-web-tools playwright-core@1.63.0` |
93
+ | Install Obscura browser-engine tools (host supplies the binary; `/obscura`) | `bun add @arnilo/prism @arnilo/prism-web-tools @arnilo/prism-mcp` |
94
+ | Install RAG retrieval (memory family `/rag`) | `bun add @arnilo/prism @arnilo/prism-memory` |
95
+ | Install the Wiki CLI and skills (memory family `/wiki`) | `bun add @arnilo/prism @arnilo/prism-memory` (`bunx prism-wiki --help`) |
96
+ | Install work connectors | `bun add @arnilo/prism @arnilo/prism-work` (import `@arnilo/prism-work/connectors`) |
97
+ | Install document/spreadsheet/presentation engine | `bun add @arnilo/prism @arnilo/prism-work` (import `@arnilo/prism-work/documents`) |
98
+ | Install spreadsheet and CSV data engine | `bun add @arnilo/prism @arnilo/prism-work` (import `@arnilo/prism-work/sheets`) |
99
+ | Install draw.io embed client & diagram engine | `bun add @arnilo/prism @arnilo/prism-work` (import `@arnilo/prism-work/diagrams`) |
100
+ | Install bounded document reader | `bun add @arnilo/prism @arnilo/prism-work` (optional `pdf-parse mammoth`; import `@arnilo/prism-work/document-reader`) |
101
+ | Install local document extraction | `bun add @arnilo/prism @arnilo/prism-work @firecrawl/anydoc@0.2.4` (import `@arnilo/prism-work/document-extraction`) |
99
102
  | Build everything (core + workspaces) | `npm run build` |
100
103
  | Delete all build output (explicit one-shot, see build notes) | `npm run clean` |
101
104
  | Run the default (network-free) test suite | `npm test` |
102
- | Dry-run pack core + every package | `npm run pack:dry-run` |
103
- | Local mirror of the release verify gate | `npm run release:dry-run` |
105
+ | Dry-run pack core + every package | `bun run pack:dry-run` |
106
+ | Local mirror of the release verify gate | `bun run release:dry-run` |
104
107
  | Validate independent versions/ranges and reject registry collisions | `npm run release:check -- --allow-dirty --allow-untagged` |
105
108
  | Preview deterministic changed-package publication | `npm run release:publish -- --dry-run --allow-dirty --allow-untagged` |
106
109
  | Verify published artifacts from the registry (post-publish smoke) | `npm run post-publish:smoke` |
107
110
  | Run the same smoke against local `npm pack` tarballs (pre-publish parity) | `npm run post-publish:smoke -- --local` |
108
111
  | Resume interrupted package-tag publication | `npm run release:publish -- --resume --report release-artifacts/publish-report.json` |
109
112
  | Protected PostgreSQL enterprise suite | `PRISM_TEST_POSTGRES_URL="$DATABASE_URL" npm run test:postgres` |
110
- | Full SDK readiness gate (typecheck + offline tests + pack) | `npm run sdk:ready` |
113
+ | Full SDK readiness gate (typecheck + offline tests + pack) | `bun run sdk:ready` |
111
114
  | Non-blocking unused-code sweep (report to `scripts/unused-sweep-report.txt`, always exits 0) | `npm run sweep:unused` |
112
115
 
113
116
  > **Build notes (0.2.3+).** `npm run build` no longer runs `npm run clean` first. Emit-producing and dist-consuming leaves are serialized by `scripts/with-build-lock.mjs`, so concurrent builds/tests cannot expose partial `dist/`; run `npm run clean` after source deletions or branch switches for orphan cleanup. Direct `tsc` outside the wrapper remains an external-writer caveat.
114
117
 
115
- Run `npm run clean` explicitly after deleting source files or switching branches: a deleted `src/__tests__/*.test.ts` leaves an orphan `dist/__tests__/*.test.js` (tsc never auto-cleans). If the orphan's import chain still resolves it keeps running as a stale test — silent staleness, which is exactly what the explicit clean prevents — and if the chain is broken the next `node --test dist/__tests__/*.test.js` fails loudly (`ERR_MODULE_NOT_FOUND`), never silently swallowed. A fresh `npm run clean && npm run build` and the new `npm run build` from a clean state produce byte-identical `dist/` (tsc overwrites per-file outputs).
118
+ Run `npm run clean` explicitly after deleting source files or switching branches: a deleted `src/__tests__/*.test.ts` leaves an orphan `dist/__tests__/*.test.js` (tsc never auto-cleans). If the orphan's import chain still resolves it keeps running as a stale test — silent staleness, which is exactly what the explicit clean prevents — and if the chain is broken the next `bun test dist/__tests__/*.test.js` fails loudly (`ERR_MODULE_NOT_FOUND`), never silently swallowed. A fresh `npm run clean && npm run build` and the new `npm run build` from a clean state produce byte-identical `dist/` (tsc overwrites per-file outputs).
116
119
 
117
120
  | Specifier | Resolves to |
118
121
  | --- | --- |
@@ -152,7 +155,7 @@ A packed tarball contains only public compiled output and release files:
152
155
  - Code packages ship `README.md`, `LICENSE`, and `CHANGELOG.md`; family/profile packages ship `README.md` and `CHANGELOG.md`.
153
156
  - The core tarball additionally ships the full `docs/` directory (the docs hub), `templates/init/`, and the `templates/` gallery (e.g. `deep-research`) used by `prism init`.
154
157
  - `dist/cli.js` and the `bin` link in core.
155
- - **Tarball filenames.** npm strips the `@scope/` prefix, so the core package `@arnilo/prism` produces a tarball named `arnilo-prism-0.11.0.tgz`; family packages produce `arnilo-prism-core-0.11.0.tgz`, `arnilo-prism-coding-tools-0.11.0.tgz`, `arnilo-prism-providers-0.11.0.tgz` (all 20 adapters inside), `arnilo-prism-channels-0.11.0.tgz`, `arnilo-prism-memory-0.11.0.tgz`, and `arnilo-prism-web-tools-0.11.0.tgz`; capability packages like `arnilo-prism-hooks-0.11.0.tgz`, `arnilo-prism-mcp-0.11.0.tgz`, and `arnilo-prism-work-0.11.0.tgz` carry their own package version. Independent-package tags carry their own version. The CLI bin name `prism` is unaffected by the package name (`npx prism` still works; npm allows the bin field to differ from the package name).
158
+ - **Tarball filenames.** npm strips the `@scope/` prefix, so the core package `@arnilo/prism` produces a tarball named `arnilo-prism-0.12.0.tgz`; family packages produce `arnilo-prism-core-0.12.0.tgz`, `arnilo-prism-coding-tools-0.12.0.tgz`, `arnilo-prism-providers-0.12.0.tgz` (all 22 adapters inside), `arnilo-prism-channels-0.12.0.tgz`, `arnilo-prism-memory-0.12.0.tgz`, and `arnilo-prism-web-tools-0.12.0.tgz`; capability packages like `arnilo-prism-hooks-0.12.0.tgz`, `arnilo-prism-mcp-0.12.0.tgz`, and `arnilo-prism-work-0.12.0.tgz` carry their own package version. Independent-package tags carry their own version. The CLI bin name `prism` is unaffected by the package name (`bunx prism` still works; npm allows the bin field to differ from the package name).
156
159
 
157
160
  Excluded from every tarball by `files` negation:
158
161
 
@@ -179,7 +182,7 @@ Excluded from every tarball by `files` negation:
179
182
  }
180
183
  ```
181
184
 
182
- Installing the provider/compaction packages without `@arnilo/prism` present produces an unmet-peer error (the `@arnilo/prism` peer is required, not optional):
185
+ Installing the provider/compaction packages without `@arnilo/prism` present produces an unmet-peer error under npm (recorded 0.x behavior; the `@arnilo/prism` peer is required, not optional):
183
186
 
184
187
  ```text
185
188
  npm error code ERESOLVE
@@ -209,13 +212,13 @@ const session = createAgentSession({ agent });
209
212
  Local release dry-run mirrors the GitHub Actions `verify` job and delegates to the SDK readiness gate:
210
213
 
211
214
  ```bash
212
- npm run release:dry-run
215
+ bun run release:dry-run
213
216
  ```
214
217
 
215
218
  For SDK readiness, run the same one-command gate directly. It composes existing scripts only: examples/workspace typecheck, build, network-free core tests (docs/export/package/install smoke included), workspace tests, and pack dry-run.
216
219
 
217
220
  ```bash
218
- npm run sdk:ready
221
+ bun run sdk:ready
219
222
  ```
220
223
 
221
224
  Release publication derives the workspace graph from manifests. The final `v0.3.0` cut validates the 56-package lockstep graph; later `release:check` and `release:publish` default to independent changed-package validation/publication under package tags. Resume skips only registry versions whose internal dependency fingerprint matches the local manifest; conflicting versions fail closed. Each attempted package is written immediately to the JSON report, so a failed job can rerun safely. `--dry-run` performs registry availability checks and invokes `npm publish --dry-run` with explicit public access, provenance, and `latest` tag, but does not publish.
@@ -241,7 +244,7 @@ PRISM_CODING_FORGE_REPOSITORY=owner/repo \
241
244
  PRISM_CODING_FORGE_TOKEN=ghp_... \
242
245
  PRISM_CODING_PROVIDER=/abs/path/provider-adapter.mjs \
243
246
  PRISM_TEST_PTY_BACKEND=/abs/path/pty-host.mjs \
244
- node --test scripts/phase26-coding-journey.test.mjs
247
+ bun test --timeout=0 scripts/phase26-coding-journey.test.mjs
245
248
  ```
246
249
 
247
250
  Every side effect carries the run suffix and is cleaned up idempotently (PR closed, branch deleted, worktree removed, containers/children/browser context closed); unknown cleanup blocks the journey. The retained report `scripts/phase26-coding-journey-report.json` is regenerated on every real run — `journey.state` pass records the surface `pass` in release evidence; blocked/partial reports record `blocked` (fail closed); `not_run`/missing records are documented protected gaps (`requiredEnv PRISM_CODING_JOURNEY`). The CI profile runs in `.github/workflows/coding-journey.yml` (scheduled + dispatch; Postgres service, host Docker, pinned Chromium, provider + forge secrets from the `coding-journey` environment) and uploads the report artifact. Leak scans cover journey stdout/stderr and the report against every credential-looking env value — the report records env names only. Frozen ceilings: journey wall 20 min (hard 40 min), cleanup 5 min (hard 15 min).
@@ -249,29 +252,29 @@ Every side effect carries the run suffix and is cleaned up idempotently (PR clos
249
252
  ## 0.1.x compatibility and support matrix
250
253
 
251
254
 
252
- Frozen by Phase 12 Task 0 in `scripts/phase12-freeze-manifest.json` (schema gate `node --test scripts/phase12-freeze.test.mjs`; docs agreement tripwired in the docs test suite). Any change requires a recorded freeze deviation in plan 012.
255
+ Frozen by Phase 12 Task 0 in `scripts/phase12-freeze-manifest.json` (schema gate `bun test --timeout=0 scripts/phase12-freeze.test.mjs`; docs agreement tripwired in the docs test suite). Any change requires a recorded freeze deviation in plan 012.
253
256
 
254
257
  | Runtime | Supported | Measured in CI |
255
258
  | --- | --- | --- |
256
- | Node | 22, 24 (`engines.node >=22`) | `verify` runs the full `sdk:ready` gate on Node 24; `node22-compat` builds and imports every public root `exports` target on Node 22. Node 20 support was dropped in 0.6.0 (`dev-006`; Node 20 reached upstream end-of-life 2026-04-30); 0.11.0 keeps the same floor. |
259
+ | Node (retired in 0.12.0) | 22, 24 (`engines.node >=22`, era record) | `verify` runs the full `sdk:ready` gate on Bun and imports every public root `exports` default target on Node 24; `node22-compat` builds and imports every public root `exports` target on Node 22. Every other step in both jobs is Bun (plan 124 Task 5); the two Node steps exist because this table declares the support line. Node 20 support was dropped in 0.6.0 (`dev-006`; Node 20 reached upstream end-of-life 2026-04-30); 0.11.0 keeps the same floor. |
257
260
  | PostgreSQL | 16 (`pgvector/pgvector:pg16`) | `postgres-integration` service container |
258
261
 
259
262
  ## Extension and configuration notes
260
263
 
261
264
 
262
- - **Required `@arnilo/prism` peer.** Every first-party code package declares a non-optional **caret** `@arnilo/prism@^0.11.0` peer (the lockstep 0.11.0 cut rewrote every internal range; the version-literal gate rejects a declared range that only satisfies the cut version) (`peerDependenciesMeta` must not mark `@arnilo/prism` optional; other peers such as `playwright-core` may be optional). **Peer-version policy (plan 030, Decision B — independent packages):** internal ranges stay inside the caret window of the cut they shipped in, so a package may patch independently while consumers remain on a compatible 0.x line. A package outside that window is refused by the release gate until the next coordinated peer bump. Inside the workspace each package also declares `"@arnilo/prism": "file:../.."` in `devDependencies` so `npm install` resolves the peer locally; that devDependency is stripped from consumer installs and is not a runtime dependency.
265
+ - **Required `@arnilo/prism` peer.** Every first-party code package declares a non-optional **caret** `@arnilo/prism@^0.11.0` peer (the lockstep 0.11.0 cut rewrote every internal range; the version-literal gate rejects a declared range that only satisfies the cut version) (`peerDependenciesMeta` must not mark `@arnilo/prism` optional; other peers such as `playwright-core` may be optional). **Peer-version policy (plan 030, Decision B — independent packages):** internal ranges stay inside the caret window of the cut they shipped in, so a package may patch independently while consumers remain on a compatible 0.x line. A package outside that window is refused by the release gate until the next coordinated peer bump. Inside the workspace each package also declares `"@arnilo/prism": "file:../.."` in `devDependencies` so the workspace install resolves the peer locally; that devDependency is stripped from consumer installs and is not a runtime dependency.
263
266
  - **Public access.** All 56 manifests (root + 55 workspace packages: 49 code packages + 6 pure-manifest family/profile packages — the 10 `prism-*` family/profile set is the 6 pure-manifest profiles plus the 4 code packages `prism-caveman`, `prism-impeccable`, `prism-openapi-tools`, `prism-ponytail`) declare `"publishConfig": { "access": "public" }`; the publisher also passes `--access public` explicitly because scoped packages otherwise default to restricted on first publish.
264
267
  - **Shipped vs repository docs.** The npm tarball ships `docs/` pages linked from `docs/index.md` (public API, security, migration, providers, install). It excludes `docs/_evidence/` (per-phase evidence freezes, including `release-0.2.7-evidence.md`), `docs/release-*-evidence.md`, and `docs/api-page-template.md`. Those files remain in git for audit. `dist/__tests__` and `*.map` stay excluded.
265
268
  - **Map retention knob.** Source maps are emitted locally but stripped from tarballs by `!dist/**/*.map`. Removing that `files` negation ships maps in releases (larger tarballs, better consumer stack traces).
266
- - **Release workflow.** `.github/workflows/release.yml` has six jobs. `verify` runs network-free SDK readiness on Node 24; `node22-compat` builds/imports every public root `exports` default target on Node 22 for declared `engines.node >=22` (docs examples need Node >=22.6 native TypeScript stripping, inside the ≥22 floor); `postgres-integration` uses `pgvector/pgvector:pg16`; `supply-chain` runs high-severity audit, SPDX/license policy, and tracked-source secret scanning; and tag-only `codeql-release` runs SAST. `publish` runs on `v0.3.0` for the one lockstep cut and on `@arnilo/*@*` package tags afterward; it needs all five gates, preserves clean tagged/version/topological publication, and alone receives `NPM_TOKEN`, `id-token: write`, and `attestations: write`. Before npm publish it packs all current tarballs, generates checksums plus SPDX, scans unpacked public artifacts, creates GitHub attestations for tarballs and SBOM, then retains artifacts for 30 days. Registry state remains the resumable journal. Local `npm run release:dry-run` remains network-free SDK readiness; local PostgreSQL coverage is `PRISM_TEST_POSTGRES_URL=... npm run test:postgres`.
269
+ - **Release workflow.** `.github/workflows/release.yml` has six jobs. `verify` runs network-free SDK readiness on Bun 1.4.2 (**Node runtime support retired in 0.12.0**; the era's two measured legs are recorded in [`history/retire-node-runtime.md`](history/retire-node-runtime.md)); `postgres-integration` uses `pgvector/pgvector:pg16`; `supply-chain` runs high-severity audit, SPDX/license policy, and tracked-source secret scanning; and tag-only `codeql-release` runs SAST. `publish` runs on `v0.3.0` for the one lockstep cut and on `@arnilo/*@*` package tags afterward; it needs all five gates, preserves clean tagged/version/topological publication, and alone receives `NPM_TOKEN`, `id-token: write`, and `attestations: write`. Before npm publish it packs all current tarballs, generates checksums plus SPDX, scans unpacked public artifacts, creates GitHub attestations for tarballs and SBOM, then retains artifacts for 30 days. Registry state remains the resumable journal. Local `bun run release:dry-run` remains network-free SDK readiness; local PostgreSQL coverage is `PRISM_TEST_POSTGRES_URL=... npm run test:postgres`.
267
270
  - **Protected integration matrix (plan 060).** Required on protected branches; pull requests do not run these jobs (no PR secrets). Connection strings are masked in logs.
268
271
 
269
272
  | Job | Workflow | Cadence | Evidence |
270
273
  | --- | --- | --- | --- |
271
- | PostgreSQL sessions + enterprise | `.github/workflows/integration-postgres.yml` | push to `main`/`master` | `npm run test:postgres -w @arnilo/prism-core`; `node --test scripts/phase27-ha.test.mjs`; migration rollback/restore drill (`scripts/drill-migration-rollback.mjs`: apply → seed → downgrade 009 → compat → re-apply → checksum fail-closed, Postgres + SQLite) |
274
+ | PostgreSQL sessions + enterprise | `.github/workflows/integration-postgres.yml` | push to `main`/`master` | `npm run test:postgres -w @arnilo/prism-core`; `bun test --timeout=0 scripts/phase27-ha.test.mjs`; migration rollback/restore drill (`scripts/drill-migration-rollback.mjs`: apply → seed → downgrade 009 → compat → re-apply → checksum fail-closed, Postgres + SQLite) |
272
275
  | NATS events + cursor + restart | `.github/workflows/integration-nats.yml` | push to `main`/`master` | `npm run test:nats -w @arnilo/prism-core` |
273
276
  | Sandbox isolation + browser threats | `.github/workflows/sandbox-browser.yml` | push to `main`/`master` + weekly | adversarial fixtures; egress/policy legs; protected Docker matrix; native T9 (ubuntu-latest + Docker) |
274
- | Office golden (packed artifacts) | `.github/workflows/integration-office.yml` | push to `main`/`master` | `node --test scripts/office-golden-packed.test.mjs` (docx/xlsx/pptx round-trips + diagrams canonicalize vs packed tarball) |
277
+ | Office golden (packed artifacts) | `.github/workflows/integration-office.yml` | push to `main`/`master` | `bun test --timeout=0 scripts/office-golden-packed.test.mjs` (docx/xlsx/pptx round-trips + diagrams canonicalize vs packed tarball) |
275
278
  | Live-provider canary (operator-gated) | `.github/workflows/canary-providers.yml` | nightly `0 3 * * *` + dispatch (never blocks PRs) | job summaries (per-provider status only); one issue on failure — see [provider conformance](provider-conformance.md) |
276
279
 
277
280
  **Required checks (owner action).** Branch protection is not yet configured on this repo; after the workflows land on `main`, mark these check names required on `main` (Settings → Branches → required status checks): `postgres (sessions + enterprise)`, `nats (events + cursor + restart)`, `office golden (packed artifacts)`, `protected-matrix` (sandbox/browser job id). The canary and its `report` job are deliberately not required (nightly, issue-on-failure). Verify each registered workflow with one dispatch: `gh workflow run integration-postgres.yml --ref main` (likewise `integration-nats.yml`, `integration-office.yml`); dispatch 404s until the file exists on the default branch.
@@ -279,7 +282,7 @@ Frozen by Phase 12 Task 0 in `scripts/phase12-freeze-manifest.json` (schema gate
279
282
  **Release evidence links.** At release time, link the latest green run of each workflow from the release evidence page: `https://github.com/ashiqrniloy/prism/actions/workflows/<workflow-file>`. Evidence snapshots in [0.1.0 readiness](history/0.1.0-readiness.md) and `docs/_evidence/` stay frozen at recording time; the workflow-run link is the live pointer.
280
283
 
281
284
  **Secrets inventory.** The integration jobs need no repo secrets: postgres/nats URLs are generated in-job from their service containers (`localhost`, masked via `::add-mask::`), and the office job is network-free. `sandbox-browser.yml` uses repo **variables** only (environment `sandbox-browser`): `PRISM_TEST_DOCKER_IMAGE`, `PRISM_ENABLE_PLAYWRIGHT_GATE`, `PRISM_ENABLE_OBSCURA_GATE`, `PRISM_OBSCURA_BIN`, `PRISM_ENABLE_DRAWIO_GATE`, `PRISM_TEST_DRAWIO_URL`. Only `canary-providers.yml` uses secrets — the nine provider API keys (anthropic/google/gemini/openai/opencode/openrouter/zai/kimi/neuralwatt) in the `live-canaries` environment, plus the `PRISM_CANARY_PROVIDERS` repo variable.
282
- - **Adding a package.** New workspace packages are picked up automatically by `npm run build --workspaces`, `npm test --workspaces`, `npm run pack:dry-run`, the packaging guard (`src/__tests__/packaging.test.ts`), and the install-smoke test (`src/__tests__/install-smoke.test.ts`) via the workspace glob; add the package to both tests' config arrays for explicit per-package assertions.
285
+ - **Adding a package.** New workspace packages are picked up automatically by `bun run build`, `bun run test`, `bun run pack:dry-run`, the packaging guard (`src/__tests__/packaging.test.ts`), and the install-smoke test (`src/__tests__/install-smoke.test.ts`) via the workspace glob; add the package to both tests' config arrays for explicit per-package assertions.
283
286
 
284
287
  ## Security and performance notes
285
288
 
@@ -314,26 +317,26 @@ Frozen by Phase 12 Task 0 in `scripts/phase12-freeze-manifest.json` (schema gate
314
317
  - **Install smoke is offline.** The install-smoke test packs core + every package into a temp dir and installs tarballs with `--offline --no-audit --no-fund` into a fresh project. External dependencies are satisfied from the lockfile-backed npm cache prepared by `bun ci`; any attempted uncached registry fetch fails the gate.
315
318
  - **Packed-install e2e journeys (plan 012 Task 3).** `scripts/e2e-enterprise-journey.test.mjs` and `scripts/e2e-coding-journey.test.mjs` pack the first-party packages for their journey, install the exact tarballs into a fresh consumer project, and run the journey script inside that consumer — public exports only, no workspace-relative resolution (asserted per run). The **enterprise journey** composes OIDC identity → OPA policy decision (durable ledger) → agent run with durable events (memory, or real PostgreSQL when `PRISM_TEST_POSTGRES_URL` is set **and the `pg` peer is installed in that consumer**) → batched approval → OpenAPI side effect with idempotency → artifact upload + signed delivery, with policy-deny and hash-mismatch fail-closed injections. The durable leg is peer-gated and never assumed from the env alone: with `PRISM_TEST_POSTGRES_URL` ambient but `pg` unresolvable (the default, since `pg` is a peer of `@arnilo/prism-core` and the consumer installs only Prism tarballs), the fixture prints `SKIP durable postgres leg: …` and runs the memory event source instead of dying with `ERR_MODULE_NOT_FOUND`; the test reports that line as a TAP diagnostic and asserts it, so the skip can never be silent. Scope the env to the phase that needs it (`PRISM_TEST_POSTGRES_URL=… npm run test:postgres`, or `phase release:gate` in `.github/workflows/release.yml`) rather than exporting it globally. The **coding journey** composes an ACP editor session (init capability negotiation, session new + load/resume) → bounded coding tools (git-aware list/search, glob, read-before-write write, delete, move) → sandboxed process session → forge handoff with idempotent PR creation, with execution-policy and read-before-write denial paths. Each fixture asserts the installed version matches the packed manifest graph and stays within the frozen `e2eJourneyFixtureMsCeiling` (120 s in `scripts/phase12-freeze-manifest.json`).
316
319
  - **Protected restart-recovery leg (plan 012 Task 4).** `scripts/phase12-restart-recovery.test.mjs` (run by `npm run test:postgres` after the Phase 7 suite) spawns two real processes against one PostgreSQL schema: replica A runs a durable agent, suspends on a batched tool approval, appends durable events and is then SIGKILLed by the driver; replica B reconnects and resumes. Operators re-run the leg with `PRISM_TEST_POSTGRES_URL="postgresql://…" npm run test:postgres` against a disposable PostgreSQL 16 (e.g. `pgvector/pgvector:pg16`). Without the URL the gate records a named `BLOCKED GATE` failure instead of skipping. Reconnect p95 and 16-worker append contention p95 are asserted against the frozen `reconnectP95Ms` / `pointOpP95Ms` ceilings; set `PRISM_PHASE12_RECORD_EVIDENCE=1` to refresh the checked-in evidence file `scripts/phase12-restart-recovery.json`.
317
- - **Offline test budget.** The default `npm test` (no `PRISM_LIVE_PROVIDER_TESTS`) is pinned at **< 110s** with a measured local baseline of **~92s** (one chain sum ~92s on this host, including the Node branch-coverage audit at ~20s; the pre-audit baseline was ~72s in `docs/_evidence/phase115-suite-budget.md`). The gate stage's former 33 s critical path is split across `scripts/phase54-legacy-registry-{dry-run,apply,fail-closed}.test.mjs` so worker files overlap, and the workspace stage runs one `npm` process per package two at a time, each taking `scripts/with-build-lock.mjs --shared` (readers overlap; `tsc` keeps the exclusive lock). Every stage except the SQLite suites runs on `node --test`; the SQLite suites use `bun test --timeout=0` (the only file set the Bun 1.4.2 inventory measured `bun-ok` and faster than Node), while `npm run test:coverage` measures with `bun test --coverage` (plan 114 moved the instrument to Bun) and the PostgreSQL TAP leg stays on Node. Plan 057 retired the historical `phase11-freeze` … `phase34-freeze`/`phase30-release` gate files from the default suite (17 files, 247 tests) — they stay in the repo as immutable release evidence and remain audit-runnable standalone via `node --test scripts/<file>.test.mjs`, with their self-wiring assertions flipped to assert non-wiring so the retirement cannot silently regress. Their criteria are content-verified, not filesystem-verified (plan 071 Tasks 5 and 10): capture ordering compares the baselines' recorded `captured` dates instead of file mtimes, phase markers resolve against the live file plus the frozen lineage (the phase plan of record and `docs/history/`) so a living doc that is rewritten between releases cannot fail an audit gate (a marker set that is checked this way is declared in the gate's own manifest — `phase26-freeze-manifest.json` `lineageCheckedFiles` — so the fallback stays bounded to the files that need it), a path retired since the freeze resolves to its recorded archive instead of being hash-compared (`status: "retired"` + `retiredTo` in the baseline, with the Task 0 `sha256` kept as history), and a version literal is asserted as a *transition* (`!`-prefixed marker: the pre-cut literal must be gone) rather than an equality that the next release sweep would invalidate. Release/security gates (`release-gate`, `tooling-gate`, `budget-gate`, `phase23-quality-gates`, `phase8–11` conformance) stay in the run. The full CI `sdk:ready` gate runs on Node 24 because docs tests execute 38 `examples/*.ts` demos via native TypeScript stripping; `scripts/examples-execution.test.mjs` spawns the rest or records a manifest skip. `npm run sdk:ready` also runs typecheck, pack dry-run, and the coverage summary, so it is allowed to exceed the `npm test` budget while remaining network-free. `npm run test:coverage` additionally runs the combined coverage summary (`npm run coverage:summary`, measured ~66s local on 16 cores: core + each workspace suite once under `bun test --coverage`; the whole `test:coverage` stage is **~137s**, measured 136.8/137.1 s after plan 115 Task 6, up from ~128s under the Node instrument because the core suite is ~1.8× slower under Bun). The stage runs the core suite once and hands that run to the summary: `test:coverage` captures the first `bun test --coverage` run's output and exit code, and `coverage-summary.mjs` parses that capture for the core row instead of spawning a second ~35 s run (the standalone `coverage-summary` and a non-zero exit code still measure, so the row is never stale), which recovered ~35 s of the ~170s pre-task stage; only the `phase23-coverage` fail-closed gate rerun measures the suite again. The core gate and the per-package lines floors are the hard thresholds. The CI `sdk:ready` step has `timeout-minutes: 30` as a hang backstop; the separate Node 22 compatibility job has `timeout-minutes: 10`. The budget was raised from 30s after the default suite grew to include every first-party package, offline install smoke, packaging guards, docs examples, and workspace tests, from 60s to 80s after the plan 115 Task 2 trims (the critical-path gate file split three ways and the shared-lock concurrent workspace stage) landed the suite at 71.7/73.9 s, then to 110s after plan 120 Task 6 added the Node branch-coverage audit.
320
+ - **Offline test budget.** The default `bun run test` (no `PRISM_LIVE_PROVIDER_TESTS`) is pinned at **< 240s** with a measured local baseline of **~200s** (one chain sum on this host; the quiet-band runs measured 156–200 s and the fastest run 156 s, recorded in `docs/_evidence/phase115-suite-budget.md` §20 — the retired Node-era pin measured ~92 s, which stopped describing the runner after plan 124 Task 2). Every stage runs the Bun binary: the root and gate suites are bounded at `--parallel=4` so the run-bundle 5 ms snapshot and the tool-search/redaction caps stop flaking under host load, the workspace stage runs `bun run --cwd <dir> test` two packages at a time with every leaf taking `scripts/with-build-lock.mjs --shared` (readers overlap; `tsc` keeps the exclusive lock), and the performance-budget stage runs `scripts/budget-gate.test.mjs` alone and single-process because its startup ratio measures host contention. The gate stage's former 33 s critical path stays split across `scripts/phase54-legacy-registry-{dry-run,apply,fail-closed}.test.mjs` so worker files overlap. `bun run test:coverage` measures with `bun test --coverage` (plan 114 moved the instrument to Bun) and the PostgreSQL leg runs `bun test` with `scripts/postgres-evidence.mjs` reading its reporter; only the branch-coverage audit keeps a Node instrument, because Bun 1.4.2 emits no branch data (plan 124 Task 4 re-measured every ceiling it asserts). Plan 057 retired the historical `phase11-freeze` … `phase34-freeze`/`phase30-release` gate files from the default suite (17 files, 247 tests) — they stay in the repo as immutable release evidence and remain audit-runnable standalone via `bun test --timeout=0 scripts/<file>.test.mjs`, with their self-wiring assertions flipped to assert non-wiring so the retirement cannot silently regress. Their criteria are content-verified, not filesystem-verified (plan 071 Tasks 5 and 10): capture ordering compares the baselines' recorded `captured` dates instead of file mtimes, phase markers resolve against the live file plus the frozen lineage (the phase plan of record and `docs/history/`) so a living doc that is rewritten between releases cannot fail an audit gate (a marker set that is checked this way is declared in the gate's own manifest — `phase26-freeze-manifest.json` `lineageCheckedFiles` — so the fallback stays bounded to the files that need it), a path retired since the freeze resolves to its recorded archive instead of being hash-compared (`status: "retired"` + `retiredTo` in the baseline, with the Task 0 `sha256` kept as history), and a version literal is asserted as a *transition* (`!`-prefixed marker: the pre-cut literal must be gone) rather than an equality that the next release sweep would invalidate. Release/security gates (`release-gate`, `tooling-gate`, `budget-gate`, `phase23-quality-gates`, `phase8–11` conformance) stay in the run. The full CI `sdk:ready` gate runs on Bun (plan 124 Task 5); docs tests execute 38 `examples/*.ts` demos and `scripts/examples-execution.test.mjs` spawns the rest or records a manifest skip, all on Bun's native TypeScript execution, which is why the examples stage is ~2.8x faster than the Node-era one. `bun run sdk:ready` also runs typecheck, pack dry-run, and the coverage summary, so it is allowed to exceed the `bun run test` budget while remaining network-free. `bun run test:coverage` additionally runs the combined coverage summary (`npm run coverage:summary`, measured ~66s local on 16 cores: core + each workspace suite once under `bun test --coverage`; the whole `test:coverage` stage is **~137s**, measured 136.8/137.1 s after plan 115 Task 6, up from ~128s under the Node instrument because the core suite is ~1.8× slower under Bun). The stage runs the core suite once and hands that run to the summary: `test:coverage` captures the first `bun test --coverage` run's output and exit code, and `coverage-summary.mjs` parses that capture for the core row instead of spawning a second ~35 s run (the standalone `coverage-summary` and a non-zero exit code still measure, so the row is never stale), which recovered ~35 s of the ~170s pre-task stage; only the `phase23-coverage` fail-closed gate rerun measures the suite again. The core gate and the per-package lines floors are the hard thresholds. The CI `sdk:ready` step has `timeout-minutes: 30` as a hang backstop; the separate Node 22 compatibility job has `timeout-minutes: 10`. The budget was raised from 30s after the default suite grew to include every first-party package, offline install smoke, packaging guards, docs examples, and workspace tests, from 60s to 80s after the plan 115 Task 2 trims (the critical-path gate file split three ways and the shared-lock concurrent workspace stage) landed the suite at 71.7/73.9 s, then to 110s after plan 120 Task 6 added the Node branch-coverage audit, and re-pinned to `< 240s` by plan 124 Task 6 when the whole suite moved to the Bun binary (measured baseline ~200 s; the Node-era 92 s describes a runner the suite no longer uses).
318
321
 
319
322
  ### GitHub Actions pipeline (0.0.27+)
320
323
 
321
324
 
322
- `.github/workflows/release.yml` is the single pipeline: **push to `main`** runs CI (`verify` = `npm run sdk:ready`, `node22-compat`, `postgres-integration`, `supply-chain`), **`v0.3.0` or `@arnilo/*@*` package tags** additionally run `codeql-release` and the `publish` job (deterministic `release:publish` in dependency order with provenance attestation). `security.yml` adds CodeQL/dependency-review/SBOM on push and PR; `live-canaries.yml` and `sandbox-browser.yml` are scheduled. All actions are SHA-pinned (2026-08-06 fix: CodeQL pins were invalid 404 refs and `workflow_dispatch` was missing — re-verified every pin against its upstream repo). Prerequisites outside the repo: Actions enabled in repository settings, and the `NPM_TOKEN` secret (with `id-token: write` for provenance). To re-cut a tag after a fix commit, delete and recreate it (`git push origin :v0.0.28 && git push origin v0.0.28`) so the tag creation event fires.
325
+ `.github/workflows/release.yml` is the single pipeline: **push to `main`** runs CI (`verify` = `bun run sdk:ready`, `node22-compat`, `postgres-integration`, `supply-chain`), **`v0.3.0` or `@arnilo/*@*` package tags** additionally run `codeql-release` and the `publish` job (deterministic `release:publish` in dependency order with provenance attestation). `security.yml` adds CodeQL/dependency-review/SBOM on push and PR; `live-canaries.yml` and `sandbox-browser.yml` are scheduled. All actions are SHA-pinned (2026-08-06 fix: CodeQL pins were invalid 404 refs and `workflow_dispatch` was missing — re-verified every pin against its upstream repo). Prerequisites outside the repo: Actions enabled in repository settings, and the `NPM_TOKEN` secret (with `id-token: write` for provenance). To re-cut a tag after a fix commit, delete and recreate it (`git push origin :v0.0.28 && git push origin v0.0.28`) so the tag creation event fires.
323
326
 
324
327
  ## Formatting, linting, and coverage
325
328
 
326
329
 
327
- Prism uses one tool for formatting and linting — [Biome](https://biomejs.dev) — configured once at the repo root (`biome.json`) and inherited by every workspace. Coverage uses Bun's built-in test coverage (`bun test --coverage`); there is no third-party coverage service. `npm test` also runs a Node branch-coverage audit (`scripts/branch-coverage-audit.mjs`, `--test-coverage-include=dist/**`, floor **83.49** in `scripts/branch-coverage.test.mjs`); it does not replace the Bun gate, which still records `branches: null`.
330
+ Prism uses one tool for formatting and linting — [Biome](https://biomejs.dev) — configured once at the repo root (`biome.json`) and inherited by every workspace. Coverage uses Bun's built-in test coverage (`bun test --coverage`); there is no third-party coverage service. `npm test` also runs a Node branch-coverage audit (`scripts/branch-coverage-audit.mjs`, `--test-coverage-include=dist/**`, floor **83.49** in `scripts/branch-coverage.test.mjs`); it does not replace the Bun gate, which still records `branches: null`. `npm test` also runs a Node branch-coverage audit (`scripts/branch-coverage-audit.mjs`, `--test-coverage-include=dist/**`, floor **83.49** in `scripts/branch-coverage.test.mjs`); it does not replace the Bun gate, which still records `branches: null`.
328
331
 
329
332
  | Command | What it does |
330
333
  | --- | --- |
331
- | `npm run lint` | `biome lint .` — fails on any lint error (warnings are non-fatal). |
332
- | `npm run format:check` | `biome format .` — fails if any file is unformatted. |
334
+ | `bun run lint` | `biome lint .` — fails on any lint error (warnings are non-fatal). |
335
+ | `bun run format:check` | `biome format .` — fails if any file is unformatted. |
333
336
  | `npm run format` | `biome format --write .` — normalizes formatting in place. |
334
- | `npm run test:coverage` | `bun test --coverage --timeout=0` over the core suite with the Bun-measured core floors **lines 91.48%**, **functions 92.21%** (measured 94.48 / 95.21 − 3pp; the branch floor is dropped because Bun 1.4.2 emits no branch data), then `scripts/coverage-summary.mjs` + the `phase23-coverage` gate. The first run's output and exit code are captured and handed to the summary (`PRISM_COVERAGE_CORE_*`), so the core row is parsed from the run the contributor already saw — one measurement per stage; the standalone entry point and any non-zero captured exit still measure for themselves. Scoping is `bunfig.toml` `coveragePathIgnorePatterns`: the root file for the core row, one package-local file per gated workspace. |
337
+ | `bun run test:coverage` | `bun test --coverage --timeout=0` over the core suite with the Bun-measured core floors **lines 91.48%**, **functions 92.21%** (measured 94.48 / 95.21 − 3pp; the branch floor is dropped because Bun 1.4.2 emits no branch data), then `scripts/coverage-summary.mjs` + the `phase23-coverage` gate. The first run's output and exit code are captured and handed to the summary (`PRISM_COVERAGE_CORE_*`), so the core row is parsed from the run the contributor already saw — one measurement per stage; the standalone entry point and any non-zero captured exit still measure for themselves. Scoping is `bunfig.toml` `coveragePathIgnorePatterns`: the root file for the core row, one package-local file per gated workspace. |
335
338
 
336
- All four gates run inside `npm run sdk:ready` (after `typecheck`, before `pack:dry-run`). A few rules are disabled in `biome.json` because they are false positives for this codebase: `noControlCharactersInRegex` and `noAssignInExpressions` (security/redaction code intentionally matches control characters and uses `while ((m = re.exec(…)))` loops), `noShadowRestrictedNames`, `noThenProperty` (the workflow DSL has a legitimate `then` branch field), `noExplicitAny`, `noVoidTypeReturn`, and `useYield`. Raise coverage floors only by re-measuring: the core floors live in `scripts/coverage-thresholds.json` `core` and the per-package rows in the same file; `package.json` carries no numeric threshold.
339
+ All four gates run inside `bun run sdk:ready` (after `typecheck`, before `pack:dry-run`). A few rules are disabled in `biome.json` because they are false positives for this codebase: `noControlCharactersInRegex` and `noAssignInExpressions` (security/redaction code intentionally matches control characters and uses `while ((m = re.exec(…)))` loops), `noShadowRestrictedNames`, `noThenProperty` (the workflow DSL has a legitimate `then` branch field), `noExplicitAny`, `noVoidTypeReturn`, and `useYield`. Raise coverage floors only by re-measuring: the core floors live in `scripts/coverage-thresholds.json` `core` and the per-package rows in the same file; `package.json` carries no numeric threshold.
337
340
 
338
341
  ### Coverage denominators and per-package thresholds
339
342
 
@@ -355,7 +358,7 @@ A new workspace package must add an evidence-based threshold entry (or a `protec
355
358
  ### Release evidence and protected skips
356
359
 
357
360
 
358
- `npm run release:evidence` (run automatically at the start of `npm run release:gate`, and therefore at the end of `npm run sdk:ready`) aggregates every test surface into `scripts/release-evidence.json` — the machine-auditable release skip manifest. It records env var **names only, never values** (the manifest is retained and uploaded by CI).
361
+ `npm run release:evidence` (run automatically at the start of `bun run release:gate`, and therefore at the end of `bun run sdk:ready`) aggregates every test surface into `scripts/release-evidence.json` — the machine-auditable release skip manifest. It records env var **names only, never values** (the manifest is retained and uploaded by CI).
359
362
 
360
363
  | State | Meaning | Gate effect |
361
364
  | --- | --- | --- |
@@ -364,7 +367,7 @@ A new workspace package must add an evidence-based threshold entry (or a `protec
364
367
  | `protected` | a documented, permitted gap with a reason (+ required env where applicable) | pass, always visible |
365
368
  | `blocked` | a required release surface cannot be attested (required env absent, or evidence missing) | **fail closed** — `release.mjs gate` refuses to release |
366
369
 
367
- Surfaces: core `npm test` (counts and the skip total come from the latest `phase*-baseline.json` `exitGate.counts` — currently 33 protected/live skips, a frozen floor), `security:threat-suites`, every workspace suite (evidence from `scripts/coverage-summary.json` of the same run; `protectedException` packages are named with their reason), `test:postgres` durable conformance (**required**: `PRISM_TEST_POSTGRES_URL` must be set when `release:gate` runs, and gitignored `scripts/postgres-evidence.json` must be written by a successful `npm run test:postgres` at the current `git rev-parse HEAD`; a phase baseline or stale evidence is blocked. The wrapper records only `gitHead`, capture time, and TAP counts — never a DSN. The release workflow's verify job declares the env on the `release:gate` phase only, so it never leaks into env-gated docs demos / durable integration suites of `npm test`; the `postgres-integration` job runs the suite against a real server; a local release must set it too), `test:nats` real JetStream legs (protected, 0.3.0), the `PRISM_LIVE_PROVIDER_TESTS` provider legs (protected, per package), and the four live canaries from `scripts/live-canary.mjs` (provider/MCP/A2A/web; run by the scheduled `live-canaries` workflow with real credentials — recorded `protected`, never `pass`). The manifest cross-references the latest baseline's `exitGate`/`protectedEvidence` so per-phase records stay the source of truth.
370
+ Surfaces: core `bun run test` (counts and the skip total come from the latest `phase*-baseline.json` `exitGate.counts` — currently 33 protected/live skips, a frozen floor), `security:threat-suites`, every workspace suite (evidence from `scripts/coverage-summary.json` of the same run; `protectedException` packages are named with their reason), `test:postgres` durable conformance (**required**: `PRISM_TEST_POSTGRES_URL` must be set when `release:gate` runs, and gitignored `scripts/postgres-evidence.json` must be written by a successful `bun run test:postgres` at the current `git rev-parse HEAD`; a phase baseline or stale evidence is blocked. The wrapper records only `gitHead`, capture time, and reporter counts — never a DSN. The release workflow's verify job declares the env on the `release:gate` phase only, so it never leaks into env-gated docs demos / durable integration suites of `npm test`; the `postgres-integration` job runs the suite against a real server; a local release must set it too), `test:nats` real JetStream legs (protected, 0.3.0), the `PRISM_LIVE_PROVIDER_TESTS` provider legs (protected, per package), and the four live canaries from `scripts/live-canary.mjs` (provider/MCP/A2A/web; run by the scheduled `live-canaries` workflow with real credentials — recorded `protected`, never `pass`). The manifest cross-references the latest baseline's `exitGate`/`protectedEvidence` so per-phase records stay the source of truth.
368
371
 
369
372
  Override `PRISM_RELEASE_EVIDENCE` to redirect the manifest (used by the gate regression). The manifest is gitignored and CI-retained (`release-evidence` artifact). A release cannot ship with a required env absent and unexplained — the operator sees every blocked surface in the retained manifest.
370
373
 
@@ -374,12 +377,12 @@ Protected legs fail closed through one convention (`scripts/blocked-gate.mjs`):
374
377
  BLOCKED GATE <id> requires=<names> evidence=<surface> hint=<how to unblock>
375
378
  ```
376
379
 
377
- `node scripts/blocked-gate.mjs` audits the same registry — which protected legs cannot run with the current environment, which of them a release profile owns (their evidence surface decides the gate), and which are documented gaps that never block. `scripts/blocked-gate.test.mjs` (in-chain) asserts the registry, the record shape, and the fail-closed exits; `scripts/phase27-freeze-manifest.json` freezes the record template.
380
+ `bun scripts/blocked-gate.mjs` audits the same registry — which protected legs cannot run with the current environment, which of them a release profile owns (their evidence surface decides the gate), and which are documented gaps that never block. `scripts/blocked-gate.test.mjs` (in-chain) asserts the registry, the record shape, and the fail-closed exits; `scripts/phase27-freeze-manifest.json` freezes the record template.
378
381
 
379
382
  ### Quality-gate reports and the Biome baseline
380
383
 
381
384
 
382
- `npm run lint` runs Biome 2.x with the canonical config (`linter.rules.preset: "recommended"` — the deprecated `recommended: true` key is gone; `npx biome migrate --write` performs the rewrite) and writes a machine-readable SARIF report to `scripts/lint-report.sarif` in the same run (stable `--reporter=sarif`; the experimental `--reporter=json` schema is not used). The repo target is **zero** lint diagnostics; every remaining intentional diagnostic carries a justified `biome-ignore lint/<rule>: reason` comment (shell-interpolated strings, verbatim upstream fixtures, and literal grep targets in tests). Unused-code diagnostics are auto-fixed by `biome lint --write --unsafe .`; public-but-unused exports are never removed by Biome — they route to the unused-code sweep.
385
+ `bun run lint` runs Biome 2.x with the canonical config (`linter.rules.preset: "recommended"` — the deprecated `recommended: true` key is gone; `bunx biome migrate --write` performs the rewrite) and writes a machine-readable SARIF report to `scripts/lint-report.sarif` in the same run (stable `--reporter=sarif`; the experimental `--reporter=json` schema is not used). The repo target is **zero** lint diagnostics; every remaining intentional diagnostic carries a justified `biome-ignore lint/<rule>: reason` comment (shell-interpolated strings, verbatim upstream fixtures, and literal grep targets in tests). Unused-code diagnostics are auto-fixed by `biome lint --write --unsafe .`; public-but-unused exports are never removed by Biome — they route to the unused-code sweep.
383
386
 
384
387
  `npm run sweep:unused` (also part of `npm test`) runs the tsc `--noUnusedLocals/--noUnusedParameters` sweep across the core and every workspace tsconfig and writes both `scripts/unused-sweep-report.txt` (human) and `scripts/unused-report.json` (`--json`, machine-readable: per-tsconfig counts + the dead-export scan). It stays non-blocking by design. Both reports are gitignored and CI-retained (`quality-gate-reports` artifact, 30 days).
385
388
 
@@ -388,7 +391,7 @@ Timing assertions in tests follow a deterministic-barrier policy: racy wall-cloc
388
391
  ## Build serialization
389
392
 
390
393
 
391
- `dist/` is compiled by `tsc` in many small writes, so a concurrent build and test in the same working tree could race — a test importing `@arnilo/prism` mid-emit could observe a partially-written module (reproduced during the 0.2.3 review). Emit-producing leaves (`tsc` builds) and dist-consuming test leaves (`node --test dist/__tests__/*.test.js`) are therefore serialized through a dependency-free lock: `node scripts/with-build-lock.mjs <command>` acquires an `O_EXCL` lockfile at `node_modules/.prism-build.lock` (contents: holder `pid` + timestamp, no secrets), waits with a 100ms backoff, and fails closed — never proceeding without the lock. A stale lock whose holder PID is dead is reclaimed; a live lock is never stolen. Acquisition is leaf-only (never the `npm test`/`sdk:ready` orchestrators), so nested `npm run build` children cannot deadlock.
394
+ `dist/` is compiled by `tsc` in many small writes, so a concurrent build and test in the same working tree could race — a test importing `@arnilo/prism` mid-emit could observe a partially-written module (reproduced during the 0.2.3 review). Emit-producing leaves (`tsc` builds) and dist-consuming test leaves (`bun test dist/__tests__/*.test.js`) are therefore serialized through a dependency-free lock: `node scripts/with-build-lock.mjs <command>` acquires an `O_EXCL` lockfile at `node_modules/.prism-build.lock` (contents: holder `pid` + timestamp, no secrets), waits with a 100ms backoff, and fails closed — never proceeding without the lock. A stale lock whose holder PID is dead is reclaimed; a live lock is never stolen. Acquisition is leaf-only (never the `npm test`/`sdk:ready` orchestrators), so nested `npm run build` children cannot deadlock.
392
395
 
393
396
  | Fact | Value |
394
397
  | --- | --- |
@@ -396,7 +399,7 @@ Timing assertions in tests follow a deterministic-barrier policy: racy wall-cloc
396
399
  | Timeout | 120s default; override with `PRISM_BUILD_LOCK_TIMEOUT_MS` |
397
400
  | Retry | 100ms backoff; stale-PID reclaim via `process.kill(pid, 0)` |
398
401
  | Fail-closed | acquisition error or timeout exits non-zero, nothing runs |
399
- | Wrapped | `build:core`, every workspace `build`, the `node --test` runs in `test`/workspace tests, the `bun test --coverage` run in `test:coverage`, `coverage-summary.mjs`, the script-gate `node --test` run (the `phase*-conformance`/`phase*-security` gates import `@arnilo/prism` from `dist`) |
402
+ | Wrapped | `build:core`, every workspace `build`, the `bun test` runs in `test`/workspace tests, the `bun test --coverage` run in `test:coverage`, `coverage-summary.mjs`, the script-gate `bun test` run (the `phase*-conformance`/`phase*-security` gates import `@arnilo/prism` from `dist`) |
400
403
  | Not wrapped | `npm run clean` (standalone), `tsc -p examples --noEmit` and workspace `typecheck` (read `dist` `.d.ts`; within any single script the build completes before reads, so only a concurrent external emitter can cause a spurious typecheck error), `scripts/phase23-build-race.test.mjs` (the lock's own regression — it runs unwrapped so its children acquire the real lock) |
401
404
 
402
405
  Directly invoking `tsc` instead of `npm run build` bypasses the lock — use the npm scripts when another build/test could be running in the same tree (CI runs them sequentially).
@@ -404,20 +407,19 @@ Directly invoking `tsc` instead of `npm run build` bypasses the lock — use the
404
407
  ## Dependency major-upgrade isolation
405
408
 
406
409
 
407
- Major dependency upgrades are **isolated, compatibility-tested changes — never bundled into a feature release.** A major bump (TypeScript, `@types/node`, `diff`, or any third-party runtime dependency and its successors) ships as its own commit/PR that runs `npm run sdk:ready` plus packed-install evidence, and is reviewed separately from feature work. Release commits contain no unreviewed major bumps.
410
+ Major dependency upgrades are **isolated, compatibility-tested changes — never bundled into a feature release.** A major bump (TypeScript, `@types/node`, `diff`, or any third-party runtime dependency and its successors) ships as its own commit/PR that runs `bun run sdk:ready` plus packed-install evidence, and is reviewed separately from feature work. Release commits contain no unreviewed major bumps.
408
411
 
409
412
  **Current third-party upgrade surface** (internal `@arnilo/prism-*` ranges are version-managed by the release tooling, not dependency upgrades; the core `@arnilo/prism` package has **zero** runtime dependencies, asserted by `core-boundaries.test.ts`):
410
413
 
411
- Development type packages track the **declared** engines floor, not the machine that builds them: `@types/node` stays on the oldest supported Node line (`^22.20.0` while `engines.node` is `>=22`), so an API that the floor cannot provide fails the build instead of compiling clean against a newer type surface. Raising the floor is a support-matrix change (freeze manifest + CI legs + `docs/migration.md`), never a side effect of bumping the types package.
414
+ Development type packages track the **declared** engines floor: `@types/node` stays on the 0.x Node floor (`^22.20.0`, the `engines.node >=22` every published 0.11.x tarball declares — the live manifests declare `engines.bun` and Node runtime support retired in 0.12.0, but the registry line a Node host can still install targets Node), so an API that floor cannot provide fails the build instead of compiling clean against a newer type surface. Moving it is a support-matrix change (freeze manifest + `docs/migration.md`), never a side effect of bumping the types package.
412
415
 
413
416
  | Dependency | Range | Resolved (lockfile) | Used by |
414
417
  | --- | --- | --- | --- |
415
418
  | `typescript` (dev) | `^7.0.2` | 7.0.2 | root build |
416
- | `@types/node` (dev) | `^22.20.0` | 22.20.2 | root build (pinned to the `engines.node` floor) |
419
+ | `@types/node` (dev) | `^22.20.0` | 22.20.2 | root build (pinned to the retired 0.x `engines.node` floor; the live manifests declare `engines.bun`) |
417
420
  | `@biomejs/biome` (dev) | `^2.5.13` | 2.5.13 | lint/format |
418
421
  | `diff` | `^9.0.0` | 9.0.0 | `@arnilo/prism-coding-tools/agent` |
419
422
  | `pg` | `^8.23.0` | 8.23.0 | `@arnilo/prism-memory`, `@arnilo/prism-core/sessions/postgres` |
420
- | `better-sqlite3` | `^13.0.3` | 13.0.3 | `@arnilo/prism-core/sessions/sqlite` |
421
423
  | `ajv` | `^8.17.1` | 8.20.0 | `@arnilo/prism-core/validation/json-schema` |
422
424
  | `zod` | `^4.6.2` | 4.6.2 | `@arnilo/prism-mcp` |
423
425
  | `@napi-rs/keyring` | `^1.3.0` | 1.3.0 | `@arnilo/prism-core/credentials/node` |
@@ -430,41 +432,45 @@ Development type packages track the **declared** engines floor, not the machine
430
432
 
431
433
  | Leg | Node | Result |
432
434
  | --- | --- | --- |
433
- | Full SDK readiness (`npm run sdk:ready`: typecheck, lint, format, test, coverage, pack, release:gate) | 24.18.0 (current) | ✅ green — 1312/1312 tests, lint 0 errors, format clean, coverage 64/72/79 vs 60/70/75 thresholds. |
435
+ | Full SDK readiness (`bun run sdk:ready`: typecheck, lint, format, test, coverage, pack, release:gate) | 24.18.0 (current) | ✅ green — 1312/1312 tests, lint 0 errors, format clean, coverage 64/72/79 vs 60/70/75 thresholds. |
434
436
  | Build toolchain (`tsc` 7.0.2, `biome` 2.5.13) | 20.20.2 (LTS iron) | ✅ both run under Node 20. |
435
437
  | Public surface import smoke (all 21 root `exports` default targets) | 20.20.2 | ✅ all import cleanly. |
436
438
  | Full core test suite | 20.20.2 | 1311/1312 — the single failure is `examples_demos_run_to_completion_and_emit_no_secret`, which executes `examples/*.ts` via Node's native TypeScript stripping (Node 22.6+). This is a test-harness capability, not an SDK runtime incompatibility, and is exactly why CI scopes Node 20 to build + import smoke. |
437
439
 
438
- **CI enforcement** (`.github/workflows/release.yml`): the `verify` job runs `npm run sdk:ready` on Node 24; `node22-compat` runs `bun ci`, `npm run build`, and the public-import smoke on Node 22; `supply-chain` runs `bun audit --audit-level=moderate`, SPDX/license checks, SBOM, and source-secret scans; `publish` `needs:` all of `verify`, `node22-compat`, `postgres-integration`, `codeql-release`, and `supply-chain`, so nothing publishes unless every leg — including the audit/SBOM gates — passes.
440
+ **CI enforcement** (`.github/workflows/release.yml`): the `verify` job runs `bun run sdk:ready` on Node 24; `node22-compat` runs `bun ci`, `npm run build`, and the public-import smoke on Node 22; `supply-chain` runs `bun audit --audit-level=moderate`, SPDX/license checks, SBOM, and source-secret scans; `publish` `needs:` all of `verify`, `node22-compat`, `postgres-integration`, `codeql-release`, and `supply-chain`, so nothing publishes unless every leg — including the audit/SBOM gates — passes.
439
441
 
440
- **Process for a major-upgrade PR:** (1) bump exactly one dependency major in its own branch; (2) `npm run sdk:ready` green; (3) packed-install evidence (`npm run pack:dry-run`, or a scratch `npm install <tarball>` import smoke for native deps like `better-sqlite3`); (4) review lockfile churn line-by-line; (5) the `supply-chain` job supplies audit/SBOM; (6) confirm no build-time regression beyond measured noise on the matrix above; (7) merge separately from any feature work.
442
+ **Process for a major-upgrade PR:** (1) bump exactly one dependency major in its own branch; (2) `bun run sdk:ready` green; (3) packed-install evidence (`bun run pack:dry-run`, or a scratch `bun add <tarball>` import smoke for native or WASM dependencies); (4) review lockfile churn line-by-line; (5) the `supply-chain` job supplies audit/SBOM; (6) confirm no build-time regression beyond measured noise on the matrix above; (7) merge separately from any feature work.
441
443
 
442
444
  ## Release checklist
443
445
 
444
446
 
445
- Every release gate maps to an exact enforcement test or command, so the checklist is executable rather than manual. Run `npm run sdk:ready` for the full local SDK readiness gate: `npm run typecheck`, `npm run lint`, `npm run format:check`, network-free `npm test`, `npm run test:coverage`, `npm run pack:dry-run`, and `npm run release:gate`. `npm run release:dry-run` is an alias for the same gate. The GitHub Actions `verify` job runs `bun ci` and `npm run sdk:ready` on Node 24; `node22-compat` runs `bun ci`, `npm run build`, and public export imports on Node 22; `postgres-integration` runs the opt-in PostgreSQL adapter suite against a CI Postgres service. Contributor installs use Bun: the committed lockfile is `bun.lock` (JSONC-shaped, read by `scripts/bun-lock.mjs`), `bun ci` is the frozen CI install, and `package-lock.json` is retired; `npm pack` and `npm publish` remain the publish path.
447
+ Every release gate maps to an exact enforcement test or command, so the checklist is executable rather than manual. Run `bun run sdk:ready` for the full local SDK readiness gate: `bun run typecheck`, `bun run lint`, `bun run format:check`, network-free `npm test`, `bun run test:coverage`, `bun run pack:dry-run`, and `bun run release:gate`. `bun run release:dry-run` is an alias for the same gate. The GitHub Actions `verify` job runs `bun ci` and `bun run sdk:ready` on Node 24; `node22-compat` runs `bun ci`, `npm run build`, and public export imports on Node 22; `postgres-integration` runs the opt-in PostgreSQL adapter suite against a CI Postgres service. Contributor installs use Bun: the committed lockfile is `bun.lock` (JSONC-shaped, read by `scripts/bun-lock.mjs`), `bun ci` is the frozen CI install, and `package-lock.json` is retired; `npm pack` and `npm publish` remain the publish path on the release host, which ships its own Node. **Node is not on the contributor path** (plan 124): every local and CI step is the Bun binary, and the only Node steps left in CI are the two declared support legs in this table. Whether *consumers* of the published packages still need Node is the runtime contract plan 125 owns — this page keeps the current `engines.node >=22` declaration until that plan lands.
446
448
 
447
449
  | Gate | Enforcement |
448
450
  | --- | --- |
449
451
  | Docs coverage for persistence/runtime/migration surfaces | `docs.test.ts` enrolls every API page in `apiPages` (heading + index-link + bare-specifier + secret-scan checks); dedicated section assertions pin `database-persistence.md`, `runs-and-usage.md`, `session-stores-and-branching.md`, `migration.md`, `agent-definitions.md`, `performance.md`, and the Phase 41 `external_app_example_*` / `phase41_external_app_surfaces_*` gates. |
450
- | Package exports/subpaths resolve to built output | `public-export-contract.test.ts` asserts every `exports`/`main`/`types`/`bin` target resolves to a built file under `dist/` with a sibling `.d.ts` (`dist/index.js` + `dist/index.d.ts` for a root package), and no target escapes `dist/` (no `src/` or `examples/` leak). CI `node22-compat` also imports every public root `exports` default target on Node 22. |
452
+ | Package exports/subpaths resolve to built output | `public-export-contract.test.ts` asserts every `exports`/`main`/`types`/`bin` target resolves to a built file under `dist/` with a sibling `.d.ts` (`dist/index.js` + `dist/index.d.ts` for a root package), and no target escapes `dist/` (no `src/` or `examples/` leak). The packed-consumer sweep imports every public subpath of every package on Bun (`scripts/packaging-current.test.mjs`, plan 125 Task 2) — the Bun successor to the retired Node import smoke. |
451
453
  | Public-API drift | `public-export-contract.test.ts` `phase39_public_protocol_exports_and_types_do_not_drift` pins the runtime protocol (`providerToolCallDelta`, `ToolCallDeltaContent`), the `/testing/provider-conformance` subpath shape, and the observational-memory runtime `.d.ts` surface. |
452
454
  | Root SDK export surface freeze | `public-export-contract.test.ts` `root export surface is frozen` snapshots every value and type export of `src/index.ts` (107 value + 69 type) so any add/remove is a deliberate test update; `every frozen value export resolves at runtime` rebuilds `dist/index.js` and asserts each value export is present (catches build drift), and `every frozen type export appears in the built type declarations` asserts each type export is in `dist/index.d.ts`. |
453
- | Examples compile and are listed; runnable demos execute | `npm run typecheck` runs `tsc -p examples --noEmit`; `docs.test.ts` checks every `examples/*.ts` file is listed in `examples/README.md`, then runs demos offline and scans output for secrets. |
455
+ | Examples compile and are listed; runnable demos execute | `bun run typecheck` runs `tsc -p examples --noEmit`; `docs.test.ts` checks every `examples/*.ts` file is listed in `examples/README.md`, then runs demos offline and scans output for secrets. |
454
456
  | Examples run to completion with no secret leakage | `docs.test.ts` `examples_demos_run_to_completion_and_emit_no_secret` runs each demo (Node strips TypeScript types natively) with exit-0 and real-secret scans; `external_app_example_*` pins the DB-backed adapter reference exercising the `RunLedger`, branch-handle checkout, fork, and prior-run resume. |
455
- | Tarball excludes built tests, source maps, and source | `packaging.test.ts` rejects `dist/__tests__/`, `*.map`, `src/`, `plans/`, and internal files; confirms every package ships README/changelog (and code packages ship LICENSE), core ships docs + CLI, and every export target exists. Profile manifests (`prism-base`/`prism-code`/`prism-sdk`/`prism-all`) are retired; hosts install explicit family packages. `@arnilo/prism-work` ships isolated `/connectors`, `/documents`, `/sheets`, `/diagrams`, and `/document-reader` subpaths. |
457
+ | Tarball excludes built tests, source maps, and source | `packaging.test.ts` rejects `dist/__tests__/`, `*.map`, `src/`, `plans/`, and internal files; confirms every package ships README/changelog (and code packages ship LICENSE), core ships docs + CLI, and every export target exists. Profile manifests (`prism-base`/`prism-code`/`prism-sdk`/`prism-all`) are retired; hosts install explicit family packages. `@arnilo/prism-work` ships isolated `/connectors`, `/documents`, `/sheets`, `/diagrams`, `/document-extraction`, and `/document-reader` subpaths. |
456
458
  | NeuralWatt package/docs/examples release gate | `packaging.test.ts` pins `@arnilo/prism-providers/neuralwatt` package exports/type declarations and `@arnilo/prism-providers` family membership; `docs.test.ts` asserts `docs/index.md` links `providers/neuralwatt.md` and `provider-caching.md`, and that `examples/cache-aware-prompt-assembly.ts` plus `examples/neuralwatt-agent-run.ts` exist and are listed. |
457
459
  | Enterprise PostgreSQL package/docs/example gate | Packaging/install/public-contract tests include `@arnilo/prism-core/enterprise/postgres`; `docs.test.ts` pins its API page, four-store migration/ownership/unknown-outcome/async-router guidance, and `examples/enterprise-postgres-state.ts`; `npm run test:postgres` exercises migration, restart, contention, and cleanup with an explicit database URL. |
458
460
  | Version graph and resumable publication | `release.test.ts` covers exact package/lock/range validation, topological order, registry collisions, dry-run, interrupted reports/resume, clean tagged git state, provenance/public/tag arguments, and token-safe errors. `release:check` and `release:publish` derive the workspace graph without a manual package list. |
459
461
  | Freeze-test current-side package presence | `workspacePackageCounts()` (`scripts/package-truth.mjs`) counts every `packages/*` directory with a manifest. A new workspace package is counted, and the frozen expected side fails until one regen. |
462
+ | Freeze-test current-side package presence | `workspacePackageCounts()` (`scripts/package-truth.mjs`) counts every `packages/*` directory with a manifest. A new workspace package is counted, and the frozen expected side fails until one regen. |
460
463
  | Release version claims have one source | `currentVersion()` (`scripts/package-truth.mjs`) reads the root manifest, and `scripts/version-literal-gate.test.mjs` fails when any surface that claims the release version disagrees with it: all 12 manifests, every internal `@arnilo/*` caret range, `bun.lock`, the `src/index.ts` version constant, the `docs/index.md` current-line banner, the `release.yml` tag list, and the generated `scripts/package-truth.json`. Each stale surface is named in one pre-flight (with a positive control for a half-finished cut), and the release-line suites (`packaging.test.ts`, `docs.test.ts`, `release.test.ts`, `phase24-truth`, `phase34-freeze`) derive their expectations from the manifest instead of pinning the cut. |
461
464
  | Pre-publish compatibility gates | `release:gate` (in `sdk:ready`) fails on removed/changed `.d.ts` exports vs `scripts/compat-baseline/` (unless `--allow-break` + migration note), version-range/lockfile drift, and tarball deny-list violations (`plans/`, `code-reviews/`, `docs/review-coverage-*`, `*.map`, `__tests__/`); unit-tested in `scripts/release-gate.test.mjs`. |
462
465
  | Legacy registry markers (plan 054 Task 7) | `scripts/phase54-legacy-registry.mjs --dry-run` verifies every retired name's final published version exists and `latest` is unchanged, and that each deprecation URL anchor exists in `docs/migrate-to-0.4.md`, without mutating the registry; `--apply --confirm` pre-flights all 54 entries and fails closed (zero mutations) on any mismatch, then idempotently adds the `legacy` dist-tag and `<0.4.0` deprecation warning (already-correct entries skipped; per-entry status in `release-artifacts/legacy-registry-plan.json` for safe resume). `packaging.test.ts` asserts the generated plan covers all 54 retired names with uniform messages and valid guide anchors; the offline fixture suite `scripts/phase54-legacy-registry-{dry-run,apply,fail-closed}.test.mjs` proves the dry-run/apply/resume behavior without network or tokens. |
463
- | Formatting, linting, and coverage thresholds | `npm run lint` and `npm run format:check` run Biome (single root `biome.json`, workspaces inherit) and fail on any lint error or unformatted file; `npm run test:coverage` uses Bun's built-in `bun test --coverage` with Bun-measured floors (core lines 91.48 / functions 92.21; per-package lines rows in `scripts/coverage-thresholds.json`; no branch floor — Bun 1.4.2 emits no branch data) and no third-party service. All three run inside `sdk:ready`. |
466
+ | Release-host registry toolchain | `npm pack`/`npm publish`/`npm sbom` stay on the release host: runner images ship Node, `bun pack` does not exist, and `bun publish` has no `--provenance` on 1.4.2 (plan 125 Task 5). Everything else — install, build, test, audit (`bun audit`), coverage, pack dry-run — is Bun; contributors never invoke npm. |
467
+ | Release-host registry toolchain | `npm pack`/`npm publish`/`npm sbom` stay on the release host: runner images ship Node, `bun pack` does not exist, and `bun publish` has no `--provenance` on 1.4.2 (plan 125 Task 5). Everything else — install, build, test, audit (`bun audit`), coverage, pack dry-run — is Bun; contributors never invoke npm. |
468
+ | Release-host registry toolchain | `npm pack`/`npm publish`/`npm sbom` stay on the release host: runner images ship Node, `bun pack` does not exist, and `bun publish` has no `--provenance` on 1.4.2 (plan 125 Task 5). Everything else — install, build, test, audit (`bun audit`), coverage, pack dry-run — is Bun; contributors never invoke npm. |
469
+ | Formatting, linting, and coverage thresholds | `bun run lint` and `bun run format:check` run Biome (single root `biome.json`, workspaces inherit) and fail on any lint error or unformatted file; `bun run test:coverage` uses Bun's built-in `bun test --coverage` with Bun-measured floors (core lines 91.48 / functions 92.21; per-package lines rows in `scripts/coverage-thresholds.json`; no branch floor — Bun 1.4.2 emits no branch data) and no third-party service. All three run inside `sdk:ready`. |
464
470
  | Supply-chain and live-canary policy | `supply-chain-security.test.ts` verifies SPDX allow/deny behavior, bounded source/artifact secret detection, credential-free canary reports, timeout/redacted failures, immutable action revisions, no `pull_request_target`, protected live environment, attestation paths, and publish dependency on `supply-chain`; CI adds CodeQL and PR dependency review. |
465
- | Release secret scan covers the tracked release set | `scripts/phase27-release.test.mjs` enumerates `git ls-files` (plus built `packages/prism-core/dist` when present) and passes that explicit list to the unchanged `scanSecrets` — the same tracked set CI scans (`git ls-files -z \| xargs -0 node scripts/scan-secrets.mjs` in `release.yml`/`security.yml`), so no untracked or gitignored working-tree file can fail the gate or mask a tracked finding; a bare `node scripts/scan-secrets.mjs <path>` still walks everything for pre-commit use except the two local-only credential names `.gitignore` already excludes (`scripts/live.env`, `*.local.env`), and the gate reports the mode, file count, and untracked-but-unignored findings as a note without failing on them. |
471
+ | Release secret scan covers the tracked release set | `scripts/phase27-release.test.mjs` enumerates `git ls-files` (plus built `packages/prism-core/dist` when present) and passes that explicit list to the unchanged `scanSecrets` — the same tracked set CI scans (`git ls-files -z \| xargs -0 bun scripts/scan-secrets.mjs` in `release.yml`/`security.yml`), so no untracked or gitignored working-tree file can fail the gate or mask a tracked finding; a bare `bun scripts/scan-secrets.mjs <path>` still walks everything for pre-commit use except the two local-only credential names `.gitignore` already excludes (`scripts/live.env`, `*.local.env`), and the gate reports the mode, file count, and untracked-but-unignored findings as a note without failing on them. |
466
472
  | Workflow and script references resolve | `scripts/workflow-liveness.test.mjs` resolves every `-w <pkg>` / `--workspace <pkg>` target in `.github/workflows/*.yml` against the live workspace inventory and every named npm script against that package's manifest (root scripts included for a bare `npm run x`), resolves every `@arnilo/*` specifier in `scripts/**/*.mjs` against the live package and its `exports` subpaths, and rejects any `uses:` reference that is not a full 40-hex commit SHA — the drift class that left `sandbox-browser.yml` building four retired packages, `scripts/fixtures/phase26-coding-journey.mjs` packing `packages/coding-agent`/`-security` and `scripts/benchmark-scenarios/phase11-auth.mjs` importing `@arnilo/prism-openapi-tools`/`-server` after plan 054 folded them, with a positive control for each. |
467
- | Network-free + offline test budget | `network-free-guard.test.ts` keeps the default suite network-free; budget pinned `< 110s` (measured baseline above). Install-smoke is offline (`--offline --no-audit --no-fund`, zero registry fetches). |
473
+ | Network-free + offline test budget | `network-free-guard.test.ts` keeps the default suite network-free; budget pinned `< 240s` (measured baseline above). Install-smoke is offline (`--offline --no-audit --no-fund`, zero registry fetches). |
468
474
  | Core security invariants reaffirmed | Runtime/docs tests hold the trust boundary: **no built-in app tools** (hosts register tools; the core ships only the mock provider and contract helpers), **no hidden provider/credential globals** (providers/credentials are host-owned `AgentConfig` fields, resolved via explicit `providerSource`/`CredentialResolver`), **no auto package discovery** (provider/tool/skill packages are opt-in and individually installed; contribution discovery is realpath-contained and emits inert envelopes the host registers), and **no secret persistence in core** (redaction applies before any `RunLedger`/`SessionStore` append; the ledger gate asserts each message event is written exactly once and redacted). |
469
475
 
470
476
  A change that adds a public persistence/runtime surface, a new package, or a new example must extend the matching row's enforcement (add the page to `apiPages`, the package to the `packages` array, or the example to the demos list) so the checklist stays self-maintaining.
@@ -476,13 +482,13 @@ A change that adds a public persistence/runtime surface, a new package, or a new
476
482
 
477
483
  | Action | Command |
478
484
  | --- | --- |
479
- | List packages changed since a baseline tag | `node scripts/release.mjs changed [--baseline <tag>]` |
480
- | Bump one package (patch/minor/major) | `node scripts/release.mjs bump --package @arnilo/<name> --type patch` |
481
- | Validate current/mixed versions | `node scripts/release.mjs check --allow-dirty --allow-untagged` |
482
- | Validate the final lockstep cut | `node scripts/release.mjs check --lockstep --version 0.3.0 --allow-dirty --allow-untagged` |
483
- | Dry-run independent publish | `node scripts/release.mjs publish --dry-run --allow-dirty --allow-untagged` |
484
- | Resume interrupted package-tag publish | `node scripts/release.mjs publish --resume --report release-artifacts/publish-report.json` |
485
- | Convert the final cut to caret ranges | `node scripts/release.mjs bump --ranges caret --from 0.3.0 --to 0.3.0` |
485
+ | List packages changed since a baseline tag | `bun scripts/release.mjs changed [--baseline <tag>]` |
486
+ | Bump one package (patch/minor/major) | `bun scripts/release.mjs bump --package @arnilo/<name> --type patch` |
487
+ | Validate current/mixed versions | `bun scripts/release.mjs check --allow-dirty --allow-untagged` |
488
+ | Validate the final lockstep cut | `bun scripts/release.mjs check --lockstep --version 0.3.0 --allow-dirty --allow-untagged` |
489
+ | Dry-run independent publish | `bun scripts/release.mjs publish --dry-run --allow-dirty --allow-untagged` |
490
+ | Resume interrupted package-tag publish | `bun scripts/release.mjs publish --resume --report release-artifacts/publish-report.json` |
491
+ | Convert the final cut to caret ranges | `bun scripts/release.mjs bump --ranges caret --from 0.3.0 --to 0.3.0` |
486
492
 
487
493
  **Independent validate rules** (`validateReleaseIndependent`):
488
494
 
@@ -498,10 +504,10 @@ No Changesets. No new runtime dependency. The 0.x caret window (`^0.3.0` = `>=0.
498
504
  ## Pre-publish compatibility gates (`release:gate`)
499
505
 
500
506
 
501
- `npm run release:gate` (also run inside `npm run sdk:ready`) is the offline gate that must pass before `release:check`/`release:publish`. It runs three stages over the exact version graph; independent mode reads local git history for changed-package validation but never contacts the registry:
507
+ `bun run release:gate` (also run inside `bun run sdk:ready`) is the offline gate that must pass before `release:check`/`release:publish`. It runs three stages over the exact version graph; independent mode reads local git history for changed-package validation but never contacts the registry:
502
508
 
503
509
  - **ranges**: reuses `validateReleaseIndependent` by default, or `validateRelease` for explicit `--lockstep --version 0.3.0`; both verify internal ranges and lockfile entries.
504
- - **compat**: diffs every package's packed `.d.ts` surface (exported names + normalized declaration signatures, `export *` resolved within the package) against `scripts/compat-baseline/<pkg>.txt`. Removed or changed exports fail unless `--allow-break` is passed **and** `docs/migration.md` mentions the target version. Manifest-only profiles (no `main`/`types`/`exports`) are skipped. Regenerate baselines after a deliberate reviewed change with `node scripts/release.mjs gate --update-baseline`.
510
+ - **compat**: diffs every package's packed `.d.ts` surface (exported names + normalized declaration signatures, `export *` resolved within the package) against `scripts/compat-baseline/<pkg>.txt`. Removed or changed exports fail unless `--allow-break` is passed **and** `docs/migration.md` mentions the target version. Manifest-only profiles (no `main`/`types`/`exports`) are skipped. Regenerate baselines after a deliberate reviewed change with `bun scripts/release.mjs gate --update-baseline`.
505
511
  - **tarball**: `npm pack --dry-run --json` file lists must not match the deny list (`code-reviews/`, `bug-reports/`, `plans/`, `scripts/benchmark-*`, `docs/review-coverage-*`, `__tests__/`, `*.map`). Root `files` excludes `docs/review-coverage-*` historical reviews.
506
512
 
507
513
  Gate behavior is unit-tested in `scripts/release-gate.test.mjs`. Signature diff is name + normalized first-declaration-line level; full structural `.d.ts` diffing (api-extractor or equivalent) is the recorded upgrade path if line-level proves insufficient.