diff --git a/CHANGELOG.md b/CHANGELOG.md
index a9bba2e8..92803c09 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Fixed
+
+- Continuum artifact ingestion now enforces artifact-kind/authority pairing at
+ the domain policy layer, keeps review fixtures repo-neutral, and splits the
+ JSON adapter into focused parser, validation, fixture, and Wesley inventory
+ modules so the adapter entry point stays below the source-size policy cap.
+- Continuum adapter review follow-up now keeps adapter-local JSON type carriers
+ in one file per exported type and names the policy-test authority fixtures so
+ review fixes preserve the source-structure and magic-string rules.
+
## [17.0.0] — 2026-05-05
### Changed
diff --git a/docs/BEARING.md b/docs/BEARING.md
index 791daa29..cd51494d 100644
--- a/docs/BEARING.md
+++ b/docs/BEARING.md
@@ -1,6 +1,6 @@
# BEARING
-Updated at cycle boundaries. Not mid-cycle.
+Updated at cycle boundaries and before the final commit of each v18 slice.
Scope note:
@@ -12,64 +12,73 @@ Scope note:
- For later-major horizon planning, use
[release-horizon-v20-v21.md](design/release-horizon-v20-v21.md).
+## Continuum Optic Admission Posture
+
+For cross-repo optic admission, git-warp is a complete Continuum participant,
+not an Echo runtime surrogate. Continuum is the protocol for exchanging
+witnessed causal history. Wesley compiles artifacts and descriptors, Echo
+admits Echo-local runtime invocations, git-warp admits git-warp-local causal
+history and readings, authority layers issue grants, and applications hide
+handles and basis references behind product adapters.
+
## Where are we
-`git-warp` is executing the v17 release-blocker DAG. The current work is
-not a broad RuntimeHost rewrite; it is one bounded release blocker at a
-time, with the DAG status and SVG kept current after each completed
-cycle.
+`git-warp` has shipped `v17.0.0`. The release work is now behind us in
+repo history, npm, and JSR; the active direction is `v18.0.0`.
+
+The v18 hill is not generic graph-model cleanup. It is Continuum
+compatibility:
+
+> Make `git-warp` a Continuum-compatible sibling WARP runtime: consume
+> Wesley-generated artifacts for Continuum-owned contract families, map
+> git-warp's append-only Git-backed causal history into honest WARP Optic
+> evidence, and
+> give `warp-ttd` generated-family facts instead of handwritten adapter
+> folklore.
+
+The long-term compatibility target is the WARP Optic shape described in
+`~/git/blog/aion-paper-07/dist/aion-paper-07.txt`, plus the Continuum
+contract families authored in `~/git/continuum/schemas/` and compiled by
+Wesley. Echo and `git-warp` are sibling runtime implementations. `git-warp`
+has its own Continuum role, and it must not emit Continuum-shaped values as
+native Continuum witnesses until that witnesshood is actually proven.
+
+Backlog fold-in: the repo-visible v18 lane is
+`WL-4A-v18-graph-substrate-convergence` in
+[WORKLOADS.md](method/backlog/WORKLOADS.md), backed by the eight notes in
+[method/backlog/v18.0.0](method/backlog/v18.0.0/). Treat that lane as the
+graph-model track inside this compatibility campaign: node and edge record
+identity, attachment slots, graph-op algebra, content cutover, legacy property
+projection, migration tooling, and genesis replay equivalence. Existing
+`echo-shaped` backlog identities are historical shorthand for graph-model
+pressure already exercised by Echo, not a claim that Echo owns `git-warp`'s
+Continuum role.
Current branch state at this boundary:
-- Branch: `release/v17.0.0`
-- Push state: local branch remains ahead of `origin/release/v17.0.0`;
- push only after explicit approval.
-- DAG map:
- [0124-v17-release-blocker-dag.md](design/0124-v17-release-blocker-dag.md)
+- Branch: `v18-continuum-opening`
+- Release tag: `v17.0.0`
+- Latest remote head inspected: `origin/main` at `5afdd3eb`
+- Latest package version: `17.0.0`
- Latest closed cycle:
- `0144-release-preflight-and-rc`
-- Latest full unit gate shape:
- `npm run test:local` is green with `438` files and `6771` tests.
-- Latest validation shape:
- lint, anti-sludge shell checks, source/test typecheck, consumer
- typecheck, markdown lint, markdown code-sample lint, high-level npm
- audit, release preflight, and whitespace diff checks are green at this
- boundary.
-
-The current release ladder remains:
-
-- `v17.0.0`: TypeScript migration, public API honesty,
- materialization-frontdoor deletion, readings/optics direction, and
- query read-model groundwork
-- `v18.0.0`: graph substrate convergence
+ `0145-push-pr-review-merge`
+
+The release ladder is now:
+
+- `v17.0.0`: shipped TypeScript migration, public API honesty,
+ materialization-frontdoor deletion, readings/optics direction, and query
+ read-model groundwork.
+- `v18.0.0`: Continuum/WARP Optic compatibility for git-warp as an independent
+ Continuum participant, through Wesley-generated contract-family artifacts and
+ honest evidence posture.
- `v19.0.0`: observation, doctrine, and slice-first runtime convergence
- `v20.0.0`: slice-first read execution
- `v21.0.0`: distributed observer geometry and admission reality
-Recent work narrowed v17 honestly, removed public materialization
-frontdoor docs, fixed runtime read guidance, made checkpoint schema `5`
-the single runtime checkpoint contract, removed the checkpoint, patch,
-subscription, and sync controller private materialization dependencies,
-retired stale materialize-spy expectations, pinned default observer
-readings, and aligned remaining checkpoint/materialize unit tests with
-the current checkpoint contract, and replaced plain sync HMAC credential
-flow with an opaque `SyncSecret`. The sync server now fails closed for
-non-local unauthenticated serving and requires an explicit unsafe option
-for unauthenticated localhost serving. It also applies per-key token-bucket
-rate limiting for configured sync auth and requires an explicit rate-limit
-budget for non-local enforced sync auth. The package upgrade command now has
-a real checkpoint upgrade path for retired checkpoint envelopes. Unexpected
-HTTP sync `500` responses are now sanitized to `E_SYNC_INTERNAL`, with
-internal details kept in structured logs.
-
-The runtime is still partially state-first in important places. The
-important current truth is narrow: the non-security `test:local` blockers
-from the v17 materialization cleanup and the direct sync security hardening
-nodes are closed. File-level anti-sludge quarantines are also graduated, and
-the full gate matrix is green, and the release cut/version/changelog node is
-closed. Final local release preflight is also green. The remaining blocker is
-release coordination: push, PR, review, green CI, and an explicit merge
-decision.
+The v18 compatibility work is bigger than ten slices. The first ten slices are
+the opening campaign. Slice 11 is an explicit re-plan point after the repo has
+real evidence from generated artifact ingestion, evidence posture, and the
+first receipt-family projection.
## Invariants
@@ -109,80 +118,79 @@ mapping, and concrete checks live in `docs/invariants/`.
## What just shipped
-Cycles `0132-subscription-controller-reading-basis` through
-`0144-release-preflight-and-rc`:
-
-- Removed `_materializeGraph()` from subscription/watch and sync
- controller read paths.
-- Made default sync metadata-only unless callers explicitly request
- `materialize: true`.
-- Rewrote stale auto-materialize and materialize-spy tests around the v17
- reading-basis contract.
-- Pinned default `graph.observer()` reads to the caller's fresh reading
- basis.
-- Aligned remaining checkpoint/materialize tests with schema `5` or
- explicit retired-schema upgrade rejection.
-- Added `SyncSecret` so sync auth secrets redact in string, JSON, and
- inspect output while still signing HMAC requests.
-- Hardened sync serve defaults: non-local bind hosts require enforced
- auth, and local unauthenticated serving must opt into unsafe localhost
- mode.
-- Added per-key token-bucket sync auth rate limiting and required explicit
- `auth.rateLimit` for non-local enforced sync hosts.
-- Sanitized unexpected HTTP sync `500` responses and routed internal error
- detail through `LoggerPort`.
-- Graduated the anti-sludge file-level quarantine manifests to empty
- `files` lists and narrowed remaining legacy hits to owning-cycle inline
- suppressions.
-- Recorded the full gate matrix green after quarantine graduation.
-- Cut the v17.0.0 changelog section for May 5 and aligned the release note with
- the honest 0123 bounded-query scope.
-- Cleared the local release preflight from a clean commit. The hard preflight
- repairs landed in `bdafca51`, and the final preflight reports all hard checks
- passed.
-- Brought `npm run test:local` back to green.
-- Marked `PORT_subscription-controller-reading-basis`,
- `PORT_sync-controller-reading-basis`,
- `SPEC_materialize-spy-test-clusters`,
- `SPEC_observer-coordinate-pinning`, and
- `SPEC_checkpoint-materialize-test-drift` complete in the DAG, then
- marked `HEX_sync-secret-plain-string` and
- `HEX_sync-production-auth-defaults` complete, then marked
- `HEX_sync-no-rate-limiting`, `HEX_sync-500-sanitization`, and
- `REL_quarantine-graduate-clean`, then
- `REL_full-gate-matrix-green`, then
- `REL_release-cut-version-changelog`, then
- `REL_release-preflight-and-rc` complete.
+`v17.0.0` shipped and was followed by release hardening:
+
+- The v17 release branch landed through PR #84.
+- Follow-up repair and package migration work landed through PR #85.
+- Release hardening landed through PR #86.
+- The final v17 coverage ratchet landed through PR #87; the signed
+ `v17.0.0` tag points at that merge.
+- npm publish recovery landed through PR #88.
+- PR #89 simplified the README model sentence after the release line.
+
+The shipped v17 scope remains: TypeScript migration, public API honesty,
+materialization-frontdoor deletion, readings/optics direction, query
+read-model groundwork, sync hardening, release gates, and package publishing.
## What feels wrong
-- v17 is still not releasable until the branch is pushed, reviewed, green in
- CI, and explicitly approved for merge.
-- `REL_push-pr-review-merge` is now the open node.
- The release preflight fix lowered the coverage ratchet to the measured
full-suite v17 line baseline `91.74%`; this is tracked as v19 bad-code debt
in `SPEC_coverage-ratchet-baseline-drop.md`.
-- Broader historical version-suffixed substrate names still exist in
- `src/`; the checkpoint upgrade slice removed the touched checkpoint and
- migration names only.
-- The branch remains local-only relative to origin; pushing is a separate
- release/coordination decision.
+- v18 can easily turn into adapter folklore if `git-warp` hand-authors local
+ mirrors of Continuum-owned families instead of consuming Wesley-generated
+ artifacts.
+- v18 can also lie in the other direction: Continuum-shaped values are not
+ Continuum-native witnesses unless the runtime has actually proven native
+ witnesshood. Initial git-warp compatibility evidence should be treated as
+ translated git-warp evidence until stronger proof exists.
+- The v18 backlog already names a graph-model convergence lane. The plan must
+ fold that lane into Continuum compatibility instead of replacing it with a
+ parallel cross-repo adapter plan.
+- `warp-ttd` needs git-warp facts as generated-family nouns, but the existing
+ ecosystem still contains handwritten adapter and protocol residue.
## What comes next
-Continue executing the DAG one open node at a time.
-
-Recommended next pull:
-
-- `REL_push-pr-review-merge`
-
-Why:
-
-- It is open.
-- The full gate matrix, release cut, and local preflight are green.
-- The branch is still local-only relative to origin.
-- Merge must remain gated on review, green CI, and explicit human approval.
-
-Keep the loop strict: write the cycle doc, capture RED, green the slice,
-update changelog/DAG/SVG/retro, validate, commit, then pull the next open
-node.
+Run the v18 opening campaign. Update this task list at the end of each slice,
+before the final commit for that slice, and mark completed items with `- [x]`.
+
+## Running Task List
+
+- [x] 1. Sync and clean the repo runway: fast-forward `main`, clear fsmonitor
+ noise, close stale v17/0145 bookkeeping, and record the v18 starting point.
+- [x] 2. Create the v18 Continuum compatibility charter: WARP Optic
+ compatibility, Continuum contract-family compatibility, Wesley-generated
+ artifact consumption, and `warp-ttd` acceptance.
+- [x] 3. Build the cross-repo contract matrix: Continuum family to Wesley
+ generated artifact to git-warp source fact to `warp-ttd` consumer need,
+ with `WL-4A-v18-graph-substrate-convergence` folded in as the graph-model
+ track.
+- [x] 4. Define git-warp's WARP Optic realization map: observer plan, bounded
+ slice, lowering surface, admissibility law, and retention contract.
+- [x] 5. Add a generated-artifact ingestion path for Continuum families, with a
+ guard against handwritten local mirrors becoming contract authority. The
+ current seam admits Continuum receipt-family fixture JSON and Wesley
+ realization manifest JSON through explicit load context; it binds each
+ accepted JSON shape to the matching context authority, the domain policy
+ independently rejects descriptor kind/authority mismatches, the adapter entry
+ point and adapter-local JSON type carriers are split below the source-size
+ and one-file-per-concept caps, self-attested authority fields from artifact
+ JSON are rejected, policy-test authority fixtures are named constants, and
+ empty or internally inconsistent Wesley generated inventory is rejected.
+- [ ] 6. Make evidence posture explicit: translated git-warp evidence first,
+ native Continuum evidence only after native witnesshood is proven.
+- [ ] 7. Prove the patch commit visibility contract: success means canonical
+ writer-tip advancement and visible graph truth, not just object creation.
+- [ ] 8. Add the same-writer concurrent patch race witness with final-frontier
+ and visible-state assertions.
+- [ ] 9. Project git-warp receipt facts into the generated Continuum
+ receipt-family shape with conformance tests.
+- [ ] 10. Add the first `warp-ttd` smoke over generated-family git-warp receipt
+ facts instead of handwritten adapter-local receipt folklore.
+- [ ] 11. Re-plan with evidence in hand before expanding into reading-envelope,
+ suffix/runtime-boundary, neighborhood-core, and settlement-family slices.
+
+The loop stays strict: write or update the cycle doc, capture RED, green the
+slice, update this BEARING task list before the final commit, validate, then
+commit only the files touched in that slice.
diff --git a/docs/VISION.md b/docs/VISION.md
index dc41296c..8b4bf682 100644
--- a/docs/VISION.md
+++ b/docs/VISION.md
@@ -60,13 +60,14 @@ The read-side correction now matters just as much as the admission-side one:
- Observer-first read surfaces through worldlines and apertures
- Decentralized sync through Git transport
-In other words: `git-warp` owns the cold causal substrate and the lawful
-read/folding surfaces over it. It should not have to pretend that a giant
-in-memory graph is the ontology.
+In other words: `git-warp` is a complete Continuum participant for witnessed
+causal history, append-only Git-backed persistence, and lawful read/folding
+surfaces. It should not have to pretend that a giant in-memory graph is the
+ontology.
## What git-warp does not own
-- Hot deterministic execution → Echo
+- Echo's runtime-local deterministic execution → Echo
- Time-travel debugging UI → warp-ttd
- Shared schemas and contract surfaces → Wesley
- Application domain semantics → yours
@@ -87,10 +88,11 @@ Sync is just `git push` / `git fetch` of WARP refs.
## The Continuum horizon
-When used in the wider stack, `git-warp` is the cold causal substrate.
-The Continuum vision (Paper VII §5) reframes processes as strands
-whose live realization is a shadow working set over shared machine
-history:
+When used in the wider stack, `git-warp` and Echo are sibling Continuum
+participants. Continuum is the protocol for exchanging witnessed causal
+history, not a runtime hierarchy. The Continuum vision (Paper VII §5) reframes
+processes as strands whose live realization is a shadow working set over
+shared machine history:
- **Ephemeral scratch** — local, weakly retained, disposable
- **Author-only speculative lane** — durable, replayable, sealed
diff --git a/docs/design/0124-v17-release-blocker-dag.dot b/docs/design/0124-v17-release-blocker-dag.dot
index 8cf1b28b..db782657 100644
--- a/docs/design/0124-v17-release-blocker-dag.dot
+++ b/docs/design/0124-v17-release-blocker-dag.dot
@@ -150,8 +150,10 @@ digraph V17ReleaseBlockers {
penwidth=2.4
];
"REL_push-pr-review-merge" [
- fillcolor="#fee2e2",
- label="OPEN\nREL_push / PR\nreview / merge"
+ color="#15803d",
+ fillcolor="#bbf7d0",
+ label="DONE\nREL_push / PR\nreview / merge",
+ penwidth=2.4
];
"SPEC_docs-materialize-frontdoor-drift" -> "SPEC_runtime-error-reading-basis-guidance";
diff --git a/docs/design/0124-v17-release-blocker-dag.md b/docs/design/0124-v17-release-blocker-dag.md
index 2ae27f38..e081b0c2 100644
--- a/docs/design/0124-v17-release-blocker-dag.md
+++ b/docs/design/0124-v17-release-blocker-dag.md
@@ -164,8 +164,9 @@ closure. A blank cell means "not directly blocked by that task," not
documented runbook warning.
`REL_push-pr-review-merge`
-: Open. Push the branch, open or update the release PR, get review and green
- CI, and merge only after explicit approval.
+: Closed in cycle 0145. The release branch was pushed, reviewed, repaired,
+ merged, tagged, and published. Follow-up hardening and npm publish recovery
+ also landed on `main`.
## Excluded From v17 Blockers
@@ -180,9 +181,9 @@ substrate convergence are also excluded from this release-blocker graph.
## Current Open Front
-The tasks with no direct blockers are:
+There are no open v17 release-blocker nodes.
-- `REL_push-pr-review-merge`
+The v17 DAG is closed.
`SPEC_consumer-typecheck-materialize-residue` closed in cycle 0125.
`SPEC_docs-materialize-frontdoor-drift` closed in cycle 0126 and unlocks
@@ -208,7 +209,8 @@ quarantine graduation. Quarantine graduation closed in cycle 0141,
opening the full gate matrix. The full gate matrix closed in cycle 0142,
opening release cut/version/changelog. Release cut/version/changelog closed in
cycle 0143, opening release preflight and RC. Release preflight and RC closed in
-cycle 0144, opening push, PR, review, and merge.
+cycle 0144, opening push, PR, review, and merge. Push, PR, review, merge, tag,
+and publish closed in cycle 0145.
## Regeneration
diff --git a/docs/design/0124-v17-release-blocker-dag.svg b/docs/design/0124-v17-release-blocker-dag.svg
index b49b6865..a87a246f 100644
--- a/docs/design/0124-v17-release-blocker-dag.svg
+++ b/docs/design/0124-v17-release-blocker-dag.svg
@@ -419,16 +419,16 @@
REL_push-pr-review-merge
-
-OPEN
+
+DONE
REL_push / PR
review / merge
REL_release-preflight-and-rc->REL_push-pr-review-merge
-
-
+
+
diff --git a/docs/design/0124-v17-release-blocker-status.csv b/docs/design/0124-v17-release-blocker-status.csv
index da7f0a0a..29afca4b 100644
--- a/docs/design/0124-v17-release-blocker-status.csv
+++ b/docs/design/0124-v17-release-blocker-status.csv
@@ -19,4 +19,4 @@ REL_quarantine-graduate-clean,complete,BND_checkpoint-schema-contract-drift;PORT
REL_full-gate-matrix-green,complete,SPEC_consumer-typecheck-materialize-residue;SPEC_docs-materialize-frontdoor-drift;SPEC_runtime-error-reading-basis-guidance;BND_checkpoint-schema-contract-drift;PORT_patch-controller-reading-basis;PORT_checkpoint-controller-reading-basis;PORT_subscription-controller-reading-basis;PORT_sync-controller-reading-basis;SPEC_materialize-spy-test-clusters;SPEC_observer-coordinate-pinning;SPEC_checkpoint-materialize-test-drift;SPEC_uniform-git-cas-upgrade-contract-drift;HEX_sync-no-rate-limiting;HEX_sync-500-sanitization;REL_quarantine-graduate-clean,,no,Closed by 0142 full gate matrix.
REL_release-cut-version-changelog,complete,REL_full-gate-matrix-green,,no,Closed by 0143 release cut/version/changelog.
REL_release-preflight-and-rc,complete,REL_release-cut-version-changelog,,no,Closed by 0144 release preflight and RC readiness.
-REL_push-pr-review-merge,incomplete,REL_release-preflight-and-rc,,yes,Open node; release preflight is green.
+REL_push-pr-review-merge,complete,REL_release-preflight-and-rc,,no,Closed by 0145 push PR review merge; v17 merged tagged and published.
diff --git a/docs/design/0145-push-pr-review-merge/push-pr-review-merge.md b/docs/design/0145-push-pr-review-merge/push-pr-review-merge.md
index efd3106e..b4f3b3db 100644
--- a/docs/design/0145-push-pr-review-merge/push-pr-review-merge.md
+++ b/docs/design/0145-push-pr-review-merge/push-pr-review-merge.md
@@ -1,11 +1,12 @@
---
cycle: 0145
task_id: REL_push-pr-review-merge
-status: Draft
+status: Complete
sponsors:
human: James
agent: Codex
started_at: 2026-05-05
+completed_at: 2026-05-21
release_home: v17.0.0
---
@@ -24,6 +25,21 @@ unless James explicitly says `YES`.
The release branch is visible on GitHub, represented by a PR against `main`,
and blocked only by external review, CI, or the explicit merge approval gate.
+## Closeout
+
+The coordination hill is closed in repo-visible history:
+
+- PR #84 merged the `release/v17.0.0` branch to `main`.
+- PR #85 landed v17 follow-up repair and package migration work.
+- PR #86 landed release publish hardening.
+- PR #87 finalized the v17 coverage ratchet and produced the signed
+ `v17.0.0` tag.
+- PR #88 recovered npm release publishing.
+- PR #89 landed the post-release README wording cleanup.
+
+The release branches have been pruned from `origin`, `origin/main` is at
+`5afdd3eb`, and `v17.0.0` is visible on npm and JSR.
+
## Playback Questions
1. Is `release/v17.0.0` pushed to `origin` at the current local commit?
@@ -139,11 +155,10 @@ Current evidence before implementation:
### Goldens
-- Local and remote branch tips match.
-- The PR body records the v17 scope, validation evidence, and the explicit
- non-merge gate.
-- The final release decision remains outside automation until James says
- `YES`.
+- `main` contains the release merge and follow-up release hardening.
+- The signed `v17.0.0` tag points at the final coverage-ratchet release merge.
+- npm and JSR both expose `17.0.0` as the latest package version.
+- The final release branches are no longer open remote work.
### Known Fails
diff --git a/docs/design/0146-v18-continuum-compatibility-charter/v18-continuum-compatibility-charter.md b/docs/design/0146-v18-continuum-compatibility-charter/v18-continuum-compatibility-charter.md
new file mode 100644
index 00000000..bc935fdb
--- /dev/null
+++ b/docs/design/0146-v18-continuum-compatibility-charter/v18-continuum-compatibility-charter.md
@@ -0,0 +1,152 @@
+---
+cycle: 0146
+task_id: V18_continuum_compatibility_charter
+status: Complete
+sponsors:
+ human: James
+ agent: Codex
+started_at: 2026-05-21
+completed_at: 2026-05-21
+release_home: v18.0.0
+---
+
+# V18 Continuum Compatibility Charter
+
+## Pull
+
+v17 made the current engine shippable. v18 must make `git-warp` compatible
+with the shared Continuum/WARP Optic stack as a complete sibling Continuum
+participant without collapsing it into Echo, Wesley, or `warp-ttd`.
+
+## Hill
+
+`git-warp` becomes a Continuum-compatible sibling WARP runtime:
+
+- it consumes Wesley-generated artifacts for Continuum-owned contract families;
+- it maps append-only Git history into honest WARP Optic evidence;
+- it exposes generated-family facts to `warp-ttd`;
+- it separates translated git-warp evidence from native Continuum witnesshood.
+
+## Source Artifacts
+
+- `~/git/blog/aion-paper-07/dist/aion-paper-07.txt`
+- `~/git/continuum/schemas/`
+- `~/git/continuum/docs/contract-family-registry.md`
+- `~/git/wesley/README.md`
+- `~/git/echo/docs/BEARING.md`
+- `~/git/warp-ttd/docs/BEARING.md`
+- [VISION.md](../../VISION.md)
+- [BEARING.md](../../BEARING.md)
+- [backlog/WORKLOADS.md](../../method/backlog/WORKLOADS.md)
+- [backlog/v18.0.0/README.md](../../method/backlog/v18.0.0/README.md)
+
+## Compatibility Law
+
+The target WARP Optic shape is:
+
+```text
+Psi = (Omega, chi, rho, Pi, Lambda)
+Lower_Psi(F*, P) = (R, W, theta)
+```
+
+For v18, `git-warp` should interpret that shape as a compatibility boundary:
+
+- `Omega`: observer/read discipline, basis, and emission posture;
+- `chi`: bounded frontier-relative support slice;
+- `rho`: append-only Git/CRDT lowering surface over patch chains;
+- `Pi`: admission law producing derived, plural, conflict, or obstruction
+ outcomes;
+- `Lambda`: retained replay, audit, transport, revelation, and reliance
+ obligations.
+
+This charter does not require one generic optic engine. It requires each
+published compatibility surface to say which part of the optic shape it
+implements and which evidence supports that claim.
+
+## Contract Families
+
+The first v18 compatibility families are the Continuum-authored families:
+
+- `receipt-family`
+- `settlement-family`
+- `neighborhood-core-family`
+- `runtime-boundary-family`
+
+Wesley is the compiler and artifact authority for these shared families.
+`git-warp` must consume generated artifacts or documented generated fixtures.
+Handwritten mirrors may exist only as temporary local adapters with explicit
+non-authority status.
+
+## Backlog Integration
+
+The repo-visible v18 backlog already names
+`WL-4A-v18-graph-substrate-convergence` as the first major workload. This
+charter folds that work into the Continuum campaign rather than replacing it.
+
+That workload contributes the graph-model track:
+
+- node and edge record identity;
+- attachment-plane substrate work;
+- graph-op algebra convergence;
+- content migration out of legacy property conventions;
+- property-bag reads reduced to projections;
+- graph-model migration tooling;
+- replay equivalence from genesis.
+
+The existing `PROTO_echo-shaped-*` task identities are retained as backlog
+history. In this charter, `echo-shaped` means graph-model pressure already
+exercised by Echo. It does not mean Echo owns `git-warp`'s Continuum role or
+defines `git-warp`'s participant obligations.
+
+## Evidence Posture
+
+The default posture for existing git-warp facts mapped into Continuum-family
+shapes is translated evidence. A value may be Continuum-shaped without being
+Continuum-native.
+
+`git-warp` may claim native Continuum witnesshood only after a runtime witness
+proves the value was produced through the corresponding Continuum family
+contract and not merely mapped from local git-warp facts.
+
+## Non-Goals
+
+- Do not make Echo the owner or authority for `git-warp`'s Continuum role.
+- Do not make `git-warp` a semantic owner for Continuum contract families.
+- Do not make `warp-ttd` hand-normalize git-warp facts into substitute shared
+ contracts.
+- Do not build a generic WARP Optic runtime before repeated concrete
+ compatibility cuts justify it.
+- Do not claim native Continuum witnesshood for translated git-warp evidence.
+
+## Acceptance
+
+The v18 opening campaign is on track when:
+
+- [x] `BEARING.md` tracks the running v18 task list.
+- [x] A cross-repo contract matrix names each family, generated artifact, local
+ source fact, consumer, and missing witness.
+- [x] A WARP Optic realization map exists for `git-warp`.
+- [x] The repo can ingest at least one generated Continuum family artifact or
+ generated fixture.
+- [x] A guard prevents generated-family local mirrors from becoming hidden
+ authority.
+- [ ] The first receipt-family projection reaches `warp-ttd` without adapter
+ folklore.
+
+## SSJS Scorecard
+
+- Runtime-backed forms: green for this documentation slice; no runtime forms
+ introduced.
+- Boundary validation: green; the charter names generated artifacts as the
+ boundary authority.
+- Behavior ownership: green; Continuum owns shared semantics, Wesley compiles,
+ `git-warp` emits git-warp-local facts, and `warp-ttd` observes.
+- Message parsing: green; no behavior branches are introduced.
+- Ambient time or entropy: green; no runtime code introduced.
+- Fake shape trust or cast-cosplay: green; the charter explicitly rejects fake
+ native witnesshood.
+
+## Closeout
+
+This charter closes BEARING task 2 and defines the standard later v18 slices
+must satisfy before making stronger compatibility claims.
diff --git a/docs/design/0147-v18-continuum-contract-matrix/v18-continuum-contract-matrix.md b/docs/design/0147-v18-continuum-contract-matrix/v18-continuum-contract-matrix.md
new file mode 100644
index 00000000..5f68a4f7
--- /dev/null
+++ b/docs/design/0147-v18-continuum-contract-matrix/v18-continuum-contract-matrix.md
@@ -0,0 +1,110 @@
+---
+cycle: 0147
+task_id: V18_continuum_contract_matrix
+status: Complete
+sponsors:
+ human: James
+ agent: Codex
+started_at: 2026-05-21
+completed_at: 2026-05-21
+release_home: v18.0.0
+---
+
+# V18 Continuum Contract Matrix
+
+## Pull
+
+The v18 charter names the compatibility target. This slice names the actual
+contract families and the current proof gap for each one.
+
+## Hill
+
+`git-warp` can now point at a concrete matrix:
+
+- Continuum owns the shared family semantics.
+- Wesley compiles and witnesses generated family artifacts.
+- Echo and `git-warp` are sibling runtimes that may emit or consume conforming
+ values.
+- `warp-ttd` is the structured debugger/read-model consumer.
+- Existing `git-warp` facts are translated evidence until native Continuum
+ witnesshood is proven.
+
+## Evidence Snapshot
+
+The matrix below is based on these inspected local sources:
+
+| Repo | Head | Evidence |
+| --- | --- | --- |
+| Continuum | `01e0735` | `docs/contract-family-registry.md`, `schemas/*.graphql`, `wesley/profile/scopes.mjs` |
+| Wesley | `19b2c1c9` | `README.md`, `docs/architecture/continuum-minimum-shared-contract-surface.md` |
+| Echo | `b1d979d` | `docs/BEARING.md` |
+| `warp-ttd` | `0491be6` | `docs/BEARING.md`, `schemas/warp-ttd-protocol.graphql` |
+| `git-warp` | this branch | `docs/BEARING.md`, `src/domain/types/TickReceipt.ts`, `src/domain/types/DeliveryObservation.ts`, read/materialize capabilities |
+
+## Family Matrix
+
+| Family | Authored home | Wesley status | `git-warp` source facts | Primary `warp-ttd` need | Missing witness |
+| --- | --- | --- | --- | --- | --- |
+| `receipt-family` | `~/git/continuum/schemas/continuum-receipt-family.graphql` | `profiled`, `fixture-witnessed`; scope `receipt-family` checks cross-leg schema hash, TTD fixture shape, Echo fixture shape, boundary fixture, roundtrip vectors, and receipt/witness separation | `TickReceipt`, op outcomes, `DeliveryObservation`, audit receipt chains, materialize/provenance receipt collection | Receipt and delivery facts as generated-family nouns, not adapter-local summaries | Live `git-warp` receipt publication mapped through generated artifacts with translated evidence posture, then witnessed as native only after a Continuum runtime witness exists |
+| `settlement-family` | `~/git/continuum/schemas/continuum-settlement-family.graphql` | `profiled`, `fixture-witnessed`; scope `settlement-family` checks cross-leg coherence and settlement boundary fixtures | Patch diffs, conflict traces, merge/conflict analysis, strand/braid conflict artifacts, writer frontier state | Import/settlement explanation for cross-runtime history and merge inspection | Live settlement values from `git-warp` suffix/import or merge flows, plus generated-artifact conformance |
+| `neighborhood-core-family` | `~/git/continuum/schemas/continuum-neighborhood-core-family.graphql` | `authored`; not yet profiled in the current Continuum Wesley scope list | Graph name, writer refs, worldline/frontier facts, local site-like participation facts still unnamed as a stable family | Neighborhood focus, participant catalog, and site navigation across Echo and `git-warp` targets | Wesley profile and fixture witness first; then `git-warp` participant values with explicit translated/native evidence status |
+| `runtime-boundary-family` | `~/git/continuum/schemas/continuum-runtime-boundary-family.graphql` | `authored`; not yet profiled in the current Continuum Wesley scope list | Materialize/read requests, observer/read basis, patch suffixes, frontiers, provenance refs, receipt collections, import outcomes still split across local APIs | Admission-chain read model: observer plans, reading envelopes, evidence posture, suffix shells, causal suffix bundles, import outcomes | Wesley profile, generated fixtures, and a live witnessed suffix exchange/admission proof between sibling runtimes |
+
+## Source-Fact Map
+
+| Continuum noun | Current `git-warp` anchor | Current posture |
+| --- | --- | --- |
+| `Receipt` | `TickReceipt`, audit receipts, receipt shards | Translated evidence; shape is not yet generated-family native |
+| `DeliveryObservation` | `DeliveryObservation` and effect sink observations | Local fact with strong name overlap; not yet Continuum family output |
+| `Witness` | checkpoint-tail witnesses, conflict witnesses, audit chain proofs | Local witness forms; no shared generated family surface yet |
+| `SettlementDelta` / `ConflictArtifact` | `PatchDiff`, conflict traces, merge/conflict services | Candidate source facts; missing shared generated settlement adapter |
+| `NeighborhoodCore` / `NeighborhoodParticipant` | graph name, writers, frontiers, worldline metadata | Candidate source facts; missing stable local site/participant object |
+| `ObserverPlan` / `ObservationRequest` | query/read basis, materialize options, traversal context | Candidate source facts; missing generated runtime-boundary profile |
+| `ReadingEnvelope` | materialize/query/read results plus provenance/receipt options | Candidate source facts; missing explicit evidence status wrapper |
+| `TranslatedSubstrateEvidence` | append-only Git-backed causal history, patch SHAs, writer refs, receipts | Correct initial evidence posture for compatibility outputs |
+| `WitnessedSuffixShell` / `CausalSuffixBundle` | writer patch chains, frontier maps, transport/sync suffixes | Candidate source facts; missing compact generated shell and admission witness |
+| `ImportOutcome` | sync/import/materialization outcomes and conflict posture | Candidate source facts; missing runtime-boundary family emission |
+
+## `warp-ttd` Consumer Matrix
+
+`warp-ttd`'s active bearing pressures these generated-family facts first:
+
+| `warp-ttd` target | Needed from `git-warp` | Contract-family lane |
+| --- | --- | --- |
+| Dual live app debugging | Read-only posture for a live `git-warp` target without host mutation | runtime-boundary-family |
+| Admission-chain read model | Artifact registration, evidence posture, receipts, witnesses, and reading envelopes as distinct facts | runtime-boundary-family, receipt-family |
+| Neighborhood and site catalog | Participant/site summaries that can compare Echo and `git-warp` targets | neighborhood-core-family |
+| Receipt shell summary | Generated-family receipt facts and delivery observations | receipt-family |
+| Merge and import inspection | Conflict artifacts, import candidates, settlement plans, and outcomes | settlement-family |
+
+## First Implementation Pressure
+
+The first implementation slice should not try to ingest every family. It should
+build a generated-artifact ingestion seam around one generated fixture family,
+then guard against hidden handwritten mirrors.
+
+Recommended order:
+
+1. Ingest or locally fixture the `receipt-family` generated artifact manifest.
+2. Reject local `git-warp` files that claim to be authoritative mirrors of
+ Continuum-owned families.
+3. Map `TickReceipt` and `DeliveryObservation` into a translated
+ `receipt-family` projection without claiming native Continuum witnesshood.
+4. Let `warp-ttd` consume that projection as generated-family-shaped input.
+
+## SSJS Scorecard
+
+- Runtime-backed forms: green for this documentation slice; no runtime forms
+ introduced.
+- Boundary validation: green; the matrix keeps authored schemas and Wesley
+ generated artifacts as authority.
+- Behavior ownership: green; each row separates authored home, compiler,
+ runtime source fact, consumer, and missing witness.
+- Message parsing: green; no behavior branches introduced.
+- Ambient time or entropy: green; no runtime code introduced.
+- Fake shape trust or cast-cosplay: green; every current `git-warp` mapping is
+ marked as translated evidence until a stronger witness exists.
+
+## Closeout
+
+This closes BEARING task 3 and supplies the evidence table for slices 4 and 5.
diff --git a/docs/design/0148-v18-warp-optic-realization-map/v18-warp-optic-realization-map.md b/docs/design/0148-v18-warp-optic-realization-map/v18-warp-optic-realization-map.md
new file mode 100644
index 00000000..6c859f4b
--- /dev/null
+++ b/docs/design/0148-v18-warp-optic-realization-map/v18-warp-optic-realization-map.md
@@ -0,0 +1,113 @@
+---
+cycle: 0148
+task_id: V18_warp_optic_realization_map
+status: Complete
+sponsors:
+ human: James
+ agent: Codex
+started_at: 2026-05-21
+completed_at: 2026-05-21
+release_home: v18.0.0
+---
+
+# V18 WARP Optic Realization Map
+
+## Pull
+
+The contract matrix names the cross-repo families. This slice maps
+`git-warp`'s current runtime facts onto the WARP optic tuple without claiming a
+generic optic engine.
+
+## Hill
+
+For v18, `git-warp` interprets:
+
+```text
+Psi = (Omega, chi, rho, Pi, Lambda)
+Lower_Psi(F*, P) = (R, W, theta)
+```
+
+as a compatibility map over existing repo facts.
+
+## Component Map
+
+| Optic component | WARP role | Current `git-warp` anchors | v18 compatibility gap |
+| --- | --- | --- | --- |
+| `Omega` | Observer discipline: projection, basis, observer state, update discipline, and emission | `WorldlineOptic`, `NodeOptic`, `NodePropertyOptic`, `ReadIdentity`, `Observer`, `QueryRunner`, `QueryReadModelProvider`, `ExternalizationPolicy` | Generated `ObserverPlan`, `ObservationRequest`, and `ReadingEnvelope` family shapes |
+| `chi` | Bounded frontier-relative optic slice | `CheckpointTailWitnessLocator`, `CheckpointTailBasisLoader`, `CheckpointTailOpticSource`, `CheckpointTailReadIdentityBuilder`, `ProvenanceController.materializeSlice` | Shared vocabulary for slice support, tail budget, and graph-model attachment plane |
+| `rho` | Set-side lowering surface that presents comparable claims over `chi` | `reduceV5`, `applyWithReceipt`, `JoinReducerSession`, op classes, `PatchDiff`, `syncDelta`, materialize-coordinate paths | Generated lowering inputs for receipt, settlement, and runtime-boundary families |
+| `Pi` | Admission law for derived, plural, conflict, or obstruction outcomes | `OpStrategies`, `ReceiptBuilder`, `TickReceipt` outcomes, `ConflictAnalyzerService`, `SyncTrustGate`, `OpticReadFailureCause` | First-class outcome algebra aligned with Continuum `AdmissionOutcomeKind` and runtime-boundary admission nouns |
+| `Lambda` | Retention contract for replay, audit, transport, revelation, and reliance obligations | append-only Git commits, writer refs, patch SHAs, checkpoints, `ReceiptShard`, `AuditReceiptService`, `ReadIdentity`, sync suffixes | Generated evidence-status wrappers, suffix shells, and explicit retention obligations for `warp-ttd` |
+
+## Lowering Scales
+
+| Scale | Weave `P` | Frontier `F*` | Result `R` | Witness `W` | Retained shell `theta` | Current posture |
+| --- | --- | --- | --- | --- | --- | --- |
+| Tick / patch | One patch or ordered patch sequence | Writer and graph frontier | State transition, `PatchDiff`, or op outcomes | `TickReceipt`, op outcome details | Patch commit, receipt, optional audit receipt | Real local runtime fact; not native Continuum receipt-family output yet |
+| Read / optic | Observer/read target plus basis | Live, coordinate, or strand source | Node/property/traversal/materialized reading | `ReadIdentity`, checkpoint-tail witnesses, failure cause | Read identity plus checkpoint/tail anchors | Real local read fact; missing generated `ReadingEnvelope` |
+| Provenance slice | Backward cone for a target | Patch graph reachable from target | Bounded reconstructed state and patch count | Causal patch list, optional receipts | Provenance payload and source SHAs | Real local source fact; missing Continuum evidence wrapper |
+| Strand / braid | Strand overlay or braided strand set | Parent frontier plus overlay heads | Materialized strand state or conflict trace | conflict receipts, conflict anchors, participant traces | strand descriptor, overlay patches, conflict analysis | Candidate settlement-family source facts |
+| Replica / sync | Remote suffix family or frontier delta | Local and remote writer frontiers | Needed patch ranges, trust posture, import/sync result | writer trust gate result, ancestry checks | transferred patch commits and refs | Candidate runtime-boundary suffix/import facts |
+
+## Outcome Algebra Posture
+
+The WARP paper's outcome space is:
+
+```text
+O(X) = Derived(X) + Plural(X) + Conflict + Obstruction
+```
+
+Current `git-warp` facts map into it conservatively:
+
+| Outcome | Local anchors | Current limitation |
+| --- | --- | --- |
+| `Derived` | successful reducer outcomes, materialized readings, query results | Not wrapped as a generated Continuum outcome |
+| `Plural` | strand/braid coexistence and multi-writer frontier facts | Plurality is represented structurally, not as a named outcome |
+| `Conflict` | conflict traces, diagnostics, conflict receipt refs | Settlement-family projection is still missing |
+| `Obstruction` | optic read failures, sync trust rejection, validation errors | Obstruction is not yet one shared runtime-boundary noun |
+
+## Evidence Posture
+
+The first v18 compatibility layer must mark `git-warp` outputs as translated
+evidence unless a native Continuum runtime witness exists.
+
+That means:
+
+- a `TickReceipt` can be mapped toward `receipt-family`;
+- a conflict trace can be mapped toward `settlement-family`;
+- a read result can be mapped toward `runtime-boundary-family`;
+- a sync suffix can be mapped toward `WitnessedSuffixShell`;
+- none of those mappings may claim native Continuum witnesshood by shape alone.
+
+## Next Engineering Cut
+
+Slice 5 should create a generated-artifact ingestion seam that can load one
+Continuum family artifact descriptor or fixture and reject hidden handwritten
+authority. The first useful family is `receipt-family` because `git-warp`
+already has strong local source facts: `TickReceipt`, op outcomes,
+`DeliveryObservation`, receipt shards, and audit receipts.
+
+The seam should not:
+
+- parse arbitrary GraphQL in the domain;
+- generate types at runtime;
+- make `git-warp` the owner of Continuum family semantics;
+- equate translated `git-warp` evidence with native Continuum witnesshood.
+
+## SSJS Scorecard
+
+- Runtime-backed forms: green for this documentation slice; no runtime forms
+ introduced.
+- Boundary validation: green; the map treats generated artifacts as later
+ boundary inputs.
+- Behavior ownership: green; optic components are mapped to owning local
+ modules and their gaps are named.
+- Message parsing: green; no behavior branches introduced.
+- Ambient time or entropy: green; no runtime code introduced.
+- Fake shape trust or cast-cosplay: green; translated evidence is explicit.
+
+## Closeout
+
+This closes BEARING task 4 and gives slice 5 a narrow implementation target:
+ingest a generated-family artifact descriptor or fixture and guard against
+shadow authority.
diff --git a/docs/design/0149-v18-continuum-artifact-ingestion/v18-continuum-artifact-ingestion.md b/docs/design/0149-v18-continuum-artifact-ingestion/v18-continuum-artifact-ingestion.md
new file mode 100644
index 00000000..e98f3744
--- /dev/null
+++ b/docs/design/0149-v18-continuum-artifact-ingestion/v18-continuum-artifact-ingestion.md
@@ -0,0 +1,147 @@
+---
+cycle: 0149
+task_id: V18_continuum_artifact_ingestion
+status: Complete
+sponsors:
+ human: James
+ agent: Codex
+started_at: 2026-05-21
+completed_at: 2026-05-21
+release_home: v18.0.0
+---
+
+# V18 Continuum Artifact Ingestion
+
+## Pull
+
+The contract matrix and optic map both point at the same first implementation
+pressure: `git-warp` needs a generated-family artifact seam before it can map
+local facts into Continuum-family shapes.
+
+## Hill
+
+Add a narrow ingestion path for generated Continuum family artifact descriptors
+and reject local mirrors before they can become hidden family authority.
+
+## Implementation
+
+This slice adds:
+
+- `ContinuumFamilyId` for the four Continuum-owned family ids;
+- `ContinuumArtifactAuthority` for generated artifacts, generated fixtures,
+ local mirrors, and handwritten mirrors;
+- `ContinuumArtifactDescriptor` as the runtime-backed descriptor object;
+- `ContinuumArtifactIngestionPolicy` as the authority guard;
+- `ContinuumArtifactJsonFileAdapter` as the infrastructure-edge file/string
+ entry point;
+- adapter-local JSON parser, validation, Continuum fixture, and Wesley manifest
+ inventory modules for Continuum fixture JSON and Wesley realization manifest
+ JSON;
+- `test/fixtures/continuum/receipt-family-generated-artifact.json` as the first
+ receipt-family Continuum fixture;
+- `test/fixtures/continuum/receipt-family-wesley-realization-manifest.json` as
+ the first Wesley realization manifest fixture.
+
+The guard accepts only:
+
+- `generated-artifact`
+- `generated-fixture`
+
+It rejects:
+
+- `local-mirror`
+- `handwritten-mirror`
+- JSON that attempts to self-attest an `authority` field
+
+## Boundary Law
+
+JSON parsing stays in `src/infrastructure/adapters/`. Domain code receives
+validated constructor fields and runtime-backed objects.
+
+Authority is not read from untrusted artifact JSON. The adapter receives
+authority through explicit load context, validates the artifact shape, requires
+the context authority that belongs to that shape, and then lets the domain
+policy decide whether that context can become descriptor authority. The domain
+policy also independently verifies that a descriptor's artifact kind and
+generated authority remain paired, so hand-built descriptors cannot bypass the
+adapter seam.
+
+Wesley realization manifests must contain at least one generated leg. When
+Wesley records an `artifactCount`, it must match the generated file inventory
+for that leg.
+
+The descriptor does not parse GraphQL, generate TypeScript, or claim family
+semantics. It only records which generated-family artifact or fixture is being
+admitted and whether that admission posture is allowed.
+
+## Verification
+
+Focused checks:
+
+```text
+npx eslint src/domain/continuum/ContinuumFamilyId.ts \
+ src/domain/continuum/ContinuumArtifactAuthority.ts \
+ src/domain/continuum/ContinuumArtifactDescriptor.ts \
+ src/domain/continuum/ContinuumArtifactIngestionPolicy.ts \
+ src/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.ts \
+ test/unit/domain/continuum/ContinuumArtifactIngestionPolicy.test.ts \
+ test/unit/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.test.ts \
+ test/unit/domain/index.exports.test.ts
+npm run typecheck:src
+npm run typecheck:test
+npm run typecheck:surface
+npx vitest run \
+ test/unit/domain/continuum/ContinuumArtifactIngestionPolicy.test.ts \
+ test/unit/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.test.ts \
+ test/unit/domain/index.exports.test.ts \
+ test/unit/domain/errors/index.test.ts
+```
+
+Observed focused Continuum-suite test result:
+
+```text
+Test Files 2 passed (2)
+Tests 27 passed (27)
+```
+
+Observed focused export/error sweep:
+
+```text
+Test Files 4 passed (4)
+Tests 77 passed (77)
+```
+
+Coverage gate:
+
+```text
+npm run test:coverage:ci
+Test Files 447 passed (447)
+Tests 6826 passed (6826)
+All files 92.13% lines
+```
+
+Targeted coverage diagnostics are not recorded as green slice gates because the
+repository global threshold applies to subset runs. The authoritative coverage
+gate for this slice is the full-suite CI coverage command above.
+
+## SSJS Scorecard
+
+- Runtime-backed forms: green; new Continuum concepts are classes with
+ constructor validation and frozen instances.
+- Boundary validation: green; untrusted JSON is parsed only in the
+ infrastructure adapter.
+- Behavior ownership: green; the descriptor owns descriptor invariants and the
+ ingestion policy owns authority decisions, including kind/authority pairing.
+- Message parsing: green; no behavior branches parse free-form messages.
+- Ambient time or entropy: green; no ambient time or entropy introduced.
+- Fake shape trust or cast-cosplay: green; generated-family authority is carried
+ by load context, self-attested JSON authority is rejected, each accepted JSON
+ shape is bound to its context authority, the policy independently checks
+ descriptor kind/authority pairing, and Wesley generated inventory is checked
+ before descriptor construction.
+
+## Closeout
+
+This closes BEARING task 5 and gives later receipt-family projection work a
+safe generated-artifact entry point without making local mirrors or
+self-attested descriptors contract authority.
diff --git a/docs/design/continuum-categories.pdf b/docs/design/continuum-categories.pdf
index a218496f..82c7fc81 100644
Binary files a/docs/design/continuum-categories.pdf and b/docs/design/continuum-categories.pdf differ
diff --git a/docs/design/continuum-categories.tex b/docs/design/continuum-categories.tex
index e4d5d1ef..66d3e7ed 100644
--- a/docs/design/continuum-categories.tex
+++ b/docs/design/continuum-categories.tex
@@ -53,13 +53,17 @@ \section{Context}
The intended runtime family is the present Continuum surface:
\begin{itemize}[leftmargin=2em]
-\item \Echo{} as the hot runtime,
-\item \GitWarp{} as the cold causal runtime,
+\item \Echo{} as a Continuum participant,
+\item \GitWarp{} as a Continuum participant,
\item \TTD{} as the host-neutral debugger protocol surface,
\item \Fuse{}-style materializations as host adapters over worldlines, strands,
or braids.
\end{itemize}
+Continuum is the protocol for exchanging witnessed causal history. It is not a
+runtime hierarchy, and it does not make \Echo{} the owner of \GitWarp{}'s
+participant role.
+
\section{Design Claim}
\begin{principle}[Continuum split]
@@ -272,7 +276,7 @@ \section{Host-Indexed Optic Interpretations}
\toprule
Host or realization context & Interpreted role \\
\midrule
-\Echo{} & execute hot-side rewrites \\
+\Echo{} & execute Echo-local rewrites \\
\GitWarp{} & admit and materialize causal patches \\
\TTD{} & expose debugger playback and explanation surfaces \\
\Fuse{} & materialize editable filesystem projections \\
diff --git a/docs/method/backlog/README.md b/docs/method/backlog/README.md
index caa37341..74e38a7f 100644
--- a/docs/method/backlog/README.md
+++ b/docs/method/backlog/README.md
@@ -127,7 +127,7 @@ lanes.
| `B1` | backlog root | Unlaned maintenance and reference work. Needs classification or direct pull before it should block committed work. | `B0` or direct human pull. |
| `B2` | `bad-code/` | Foundational debt and invariant repair. This lane can legitimately block release or execution work. | `B0`, `B1`, or lower-level debt in `B2`. |
| `B3` | `v17.0.0/` | Current committed release work. Explicit per-note edges win here. | `B2`, same-band work, or explicit edges. |
-| `B4` | `v18.0.0/`, `up-next/` | Next-major substrate work plus unslotted near-term feature overflow after the active release. | `B2`, `B3`, or explicit same-band edges. |
+| `B4` | `v18.0.0/`, `up-next/` | Next-major graph-model work plus unslotted near-term feature overflow after the active release. | `B2`, `B3`, or explicit same-band edges. |
| `B5` | `v19.0.0/` | Doctrine/runtime follow-through after the substrate cut. | `B2`, `B3`, `B4`, or explicit same-band edges. |
| `B6` | `v20.0.0/`, `v21.0.0/`, `cool-ideas/` | Far-horizon slice-first/runtime and distributed/plural follow-through plus speculative orbit. | Promotion into another lane or completion of lower numbered bands. |
@@ -139,8 +139,8 @@ lanes.
| backlog root | `B1` | These notes are real work, but still need lane assignment or an explicit pull decision. When `feature:` is present, treat that as the subsystem home while release-home remains undecided. |
| `bad-code/` | `B2` | Invariant debt can block `v17.0.0`, `v18.0.0`, and `up-next`. |
| `v17.0.0/` | `B3` | This is the active release graph for TypeScript migration and streaming ORSets. |
-| `v18.0.0/` | `B4` | This is the next-major graph-substrate convergence lane. |
-| `up-next/` | `B4` | Feature-overflow queue behind the active release and next-major substrate work unless explicitly promoted into a numbered lane. |
+| `v18.0.0/` | `B4` | This is the next-major graph-model convergence lane. |
+| `up-next/` | `B4` | Feature-overflow queue behind the active release and next-major graph-model work unless explicitly promoted into a numbered lane. |
| `v19.0.0/` | `B5` | Doctrine, observer, and admission convergence after the substrate cut. |
| `v20.0.0/` | `B6` | Slice-first runtime realization after `v19.0.0/` hardens the noun and support law. |
| `v21.0.0/` | `B6` | Common-basis, braid, and fuller distributed observer geometry after `v20.0.0/`. |
@@ -296,13 +296,14 @@ Prefix counts:
| `SLUDGE` | 1 |
| `TS` | 11 |
-### `v18.0.0/` — `B4` Graph-Substrate Convergence
+### `v18.0.0/` — `B4` Graph-Model Convergence
Dependency posture:
- downstream of `v17.0.0/`
- explicit frontmatter edges carry the actual substrate cut order
-- this lane is the Echo-shaped graph-model cut, not full repo parity
+- this lane is the Continuum-compatible graph-model cut pressure-tested against
+ Echo, not full repo parity or runtime hierarchy
Canonical lane readme:
diff --git a/docs/method/backlog/WORKLOADS.md b/docs/method/backlog/WORKLOADS.md
index be8715b3..30502d45 100644
--- a/docs/method/backlog/WORKLOADS.md
+++ b/docs/method/backlog/WORKLOADS.md
@@ -42,7 +42,7 @@ dependency graph and likely write-surface overlap.
| `3.0` | `B3` | 5 | 25 | Ready-now v17 foundations and independent release hygiene |
| `3.1` | `B3` | 2 | 7 | Downstream v17 runtime split work |
| `3.x` parked | `B3` | 1 | 6 | Launch-prep proof and package tail |
-| `4` | `B4` | 9 | 43 | v18 substrate cut plus current `up-next/` queue |
+| `4` | `B4` | 9 | 43 | v18 graph-model cut plus current `up-next/` queue |
| `5` | `B5` | 3 | 11 | v19 observer/admission/runtime convergence |
| `6` | `B6` | 10 | 100 | v20/v21 horizon plus speculative orbit |
| Grand total | all | 44 | 371 | Full live backlog |
@@ -114,12 +114,12 @@ engineering trunks.
## Wave 4 — `B4`
-This wave contains the next-major graph-substrate lane and the current
-`up-next/` queue.
+This wave contains the next-major graph-model lane and the current `up-next/`
+queue.
| Workload | Count | Items | Agent surface | Preconditions |
|----------|------:|-------|---------------|---------------|
-| `WL-4A-v18-graph-substrate-convergence` | 8 | `PROTO_echo-shaped-node-records`, `PROTO_echo-shaped-edge-records`, `PROTO_attachment-plane-substrate`, `PROTO_graph-op-algebra-convergence`, `PROTO_content-attachment-plane-cutover`, `PROTO_legacy-props-as-projection`, `INFRA_graph-model-migration-tool`, `TRUST_genesis-replay-equivalence` | Echo-shaped graph model cut, migration tooling, and replay proof | v17 core release work complete |
+| `WL-4A-v18-graph-substrate-convergence` | 8 | `PROTO_echo-shaped-node-records`, `PROTO_echo-shaped-edge-records`, `PROTO_attachment-plane-substrate`, `PROTO_graph-op-algebra-convergence`, `PROTO_content-attachment-plane-cutover`, `PROTO_legacy-props-as-projection`, `INFRA_graph-model-migration-tool`, `TRUST_genesis-replay-equivalence` | Continuum-compatible graph model cut pressure-tested against Echo, migration tooling, and replay proof | v17 core release work complete |
| `WL-40-upnext-api-capability-contracts` | 4 | `DX_modular-type-declarations`, `DX_plumbing-to-gitplumbing-rename`, `PROTO_controller-capability-interfaces`, `PROTO_patch-commit-visibility-contract` | API capability and declaration-contract cleanup | none |
| `WL-41-upnext-runtime-boundaries` | 9 | `DX_max-file-size-policy`, `DX_trailer-codec-dts`, `NDNM_delete-vv-orset-shims`, `PROTO_cbor-op-hydration`, `PROTO_drop-v5-runtime-nouns`, `PROTO_op-consumer-instanceof-migration`, `PROTO_warpkernel-port-cleanup`, `PROTO_warpruntime-open-options-class`, `PROTO_wire-format-migration-edgepropset` | Runtime boundary cleanup and noun drift removal | none |
| `WL-42-upnext-streaming-read-chain` | 5 | `NDNM_defaultcodec-to-infrastructure`, `PERF_stream-read-migration`, `PERF_stream-cleanup`, `PERF_async-generator-traversal`, `PERF_stream-memory-tests` | Streaming read migration and memory witnesses | root `PERF_out-of-core-materialization` |
@@ -132,7 +132,7 @@ This wave contains the next-major graph-substrate lane and the current
## Wave 5 — `B5`
This wave is `v19.0.0`: observer, admission, and doctrine convergence
-after the graph-substrate cut.
+after the graph-model cut.
| Workload | Count | Items | Agent surface | Preconditions |
|----------|------:|-------|---------------|---------------|
@@ -189,7 +189,7 @@ If you want to staff agents immediately, start here:
3. Pull Wave `3.1` only after the named `v17` prerequisites are true.
4. Run Wave `2` in parallel when a release slice hits the same
invariant family.
-5. Use Wave `4` for `v18.0.0` planning or selective substrate prep
+5. Use Wave `4` for `v18.0.0` planning or selective graph-model prep
without starving active `B3` work.
6. Keep Waves `5` and `6` parked unless deliberately working doctrine,
horizon, or speculative follow-through.
diff --git a/docs/method/backlog/bad-code/RELEASE_TRIAGE.md b/docs/method/backlog/bad-code/RELEASE_TRIAGE.md
index 3e95a3f0..0ff84d70 100644
--- a/docs/method/backlog/bad-code/RELEASE_TRIAGE.md
+++ b/docs/method/backlog/bad-code/RELEASE_TRIAGE.md
@@ -28,8 +28,8 @@ Use the release theme, not the filename prefix, as the slotting rule:
| Release | Bad-Code That Belongs Here |
|---------|----------------------------|
| `v17.0.0` | Current engine cleanup: TypeScript migration fallout, capability/API honesty, `WarpRuntime` deletion fallout, current sync/security hardening, current trie/checkpoint/index correctness, and test/doc debt that blocks the v17 package from being honest. |
-| `v18.0.0` | Echo-shaped graph substrate convergence: node/edge record identity, attachment plane, typed payload model, graph-op algebra, graph-model migration, and replay equivalence from genesis. |
-| `v19.0.0` | Observer/admission/doctrine convergence: audit/admission seams, observer-readable receipts, patch/session admission semantics, trust/admission boundary models, and doctrine cleanup that should not be mixed into graph-substrate migration. |
+| `v18.0.0` | Continuum-compatible graph-model convergence pressure-tested against Echo: node/edge record identity, attachment plane, typed payload model, graph-op algebra, graph-model migration, and replay equivalence from genesis. |
+| `v19.0.0` | Observer/admission/doctrine convergence: audit/admission seams, observer-readable receipts, patch/session admission semantics, trust/admission boundary models, and doctrine cleanup that should not be mixed into graph-model migration. |
| `v20.0.0` | Slice-first runtime realization: bounded-support reads, streaming/page-shaped APIs, causal indexes, query/index cost surfaces, external-memory global operators, and materialization paths that must stop assuming whole-graph residency. |
| `v21.0.0` | Distributed observer geometry and admission reality: strand/braid/common-basis/local-site semantics, merge runtime nouns, witnessed admission, conflict witnesses, wormhole/plurality surfaces, and public noun cleanup for those later semantics. |
@@ -70,10 +70,10 @@ Other v17 recheck candidates:
## `v18.0.0` Fit
-The first metadata pass promoted only graph-substrate bad-code into
+The first metadata pass promoted only graph-model bad-code into
`v18.0.0`. This should stay narrow.
-Cards now pinned to v18 for the Echo-shaped graph substrate cycle:
+Cards now pinned to v18 for the graph-model convergence cycle:
- `CAST_warpstate-prop-unknown-value`: property value truth belongs with typed attachment/payload substrate decisions.
- `MODEL_neighbor-edge-typedef`: neighbor edge shape should line up with stable edge identity and edge-record nouns.
@@ -199,7 +199,7 @@ The first metadata pass did this:
3. Pulled the direct `WarpRuntime`/Worldline escape-hatch cards forward
into v17 or graveyard them if the current runtime-deletion line
already removed the smell.
-4. Promoted only graph-substrate cards into v18. Do not move generic
+4. Promoted only graph-model cards into v18. Do not move generic
typedef cleanup there just because v18 has room.
5. Recomputed bad-code release counts from frontmatter and updated
`bad-code/README.md`.
@@ -212,7 +212,7 @@ Use feature/release overlap:
1. During the v17 burndown, pay down or graveyard bad-code cards that
sit in the files already touched by the active v17 task.
-2. At the v18 pull, move only the graph-substrate candidates listed
+2. At the v18 pull, move only the graph-model candidates listed
above into the v18 lane and leave unrelated cleanup behind.
3. At the v19 pull, resolve admission/trust/audit cards as part of the
observer/admission surface work.
diff --git a/docs/method/backlog/v18.0.0/PROTO_echo-shaped-edge-records.md b/docs/method/backlog/v18.0.0/PROTO_echo-shaped-edge-records.md
index 2d8101f2..16f92bb4 100644
--- a/docs/method/backlog/v18.0.0/PROTO_echo-shaped-edge-records.md
+++ b/docs/method/backlog/v18.0.0/PROTO_echo-shaped-edge-records.md
@@ -8,13 +8,17 @@ blocks:
feature: graph-model-substrate
---
-# Echo-shaped edge records
+# Shared graph-model edge records
+
+Identity note: the backlog id keeps the older `echo-shaped` shorthand for
+continuity. The target is graph-model alignment pressure-tested by Echo, not
+Echo ownership of `git-warp`.
## Why
`git-warp` still identifies edges by the `(from, to, label)` triple
-and derived key encodings. Echo's substrate gives edges first-class
-identity and treats edge type as part of the record, not the identity
+and derived key encodings. Echo has already pressure-tested edges as
+first-class records where edge type is part of the record, not the identity
carrier.
Shared graph shape requires stable edge records.
diff --git a/docs/method/backlog/v18.0.0/PROTO_echo-shaped-node-records.md b/docs/method/backlog/v18.0.0/PROTO_echo-shaped-node-records.md
index fcf1152c..7861ce70 100644
--- a/docs/method/backlog/v18.0.0/PROTO_echo-shaped-node-records.md
+++ b/docs/method/backlog/v18.0.0/PROTO_echo-shaped-node-records.md
@@ -8,12 +8,16 @@ blocks:
feature: graph-model-substrate
---
-# Echo-shaped node records
+# Shared graph-model node records
+
+Identity note: the backlog id keeps the older `echo-shaped` shorthand for
+continuity. The target is graph-model alignment pressure-tested by Echo, not
+Echo ownership of `git-warp`.
## Why
`git-warp` still treats node identity as a bare string plus property
-bag fallout. Echo's substrate treats nodes as skeleton records with
+bag fallout. Echo has already pressure-tested nodes as skeleton records with
stable identity and explicit type.
If the repos are going to share the same graph model, node records
diff --git a/docs/method/backlog/v18.0.0/README.md b/docs/method/backlog/v18.0.0/README.md
index 77e76e32..82dd5327 100644
--- a/docs/method/backlog/v18.0.0/README.md
+++ b/docs/method/backlog/v18.0.0/README.md
@@ -1,10 +1,16 @@
-# v18.0.0 — Echo-Shaped Graph Substrate Convergence
+# v18.0.0 — Continuum-Compatible Graph Model Convergence
-The hill: make `git-warp` and Echo share the same graph substrate
-shape without pretending they must immediately share the same public
-API, admission model, or runtime shell.
+The hill: make `git-warp`'s graph model compatible with the concrete
+shape Echo has already exercised while keeping both engines as equal
+Continuum participants. They do not need to share the same public API,
+admission model, or runtime shell.
-This release cuts the graph layer to the Echo-shaped two-plane model:
+Doctrine note: `PROTO_echo-shaped-*` task identities are historical
+backlog shorthand for graph-model alignment pressure from Echo. They do
+not mean Echo owns `git-warp`, and they do not make Continuum a runtime
+hierarchy.
+
+This release cuts the graph layer toward the shared two-plane model:
- skeleton-only node records
- skeleton-only edge records with stable edge identity
@@ -55,10 +61,9 @@ LAYER 2 (migration and proof):
## Practical rule
-`v18.0.0` is graph-substrate convergence, not "make git-warp become
-Echo." Keep the `git-warp` causal envelope if it can faithfully carry
-the shared graph model. Change the envelope only if replay honesty
-requires it.
+`v18.0.0` is graph-model convergence, not "make git-warp become Echo."
+Keep the `git-warp` causal envelope if it can faithfully carry the shared
+graph model. Change the envelope only if replay honesty requires it.
## Status key
diff --git a/docs/method/retro/0145-push-pr-review-merge/push-pr-review-merge.md b/docs/method/retro/0145-push-pr-review-merge/push-pr-review-merge.md
new file mode 100644
index 00000000..30e8df71
--- /dev/null
+++ b/docs/method/retro/0145-push-pr-review-merge/push-pr-review-merge.md
@@ -0,0 +1,38 @@
+# 0145 Push PR Review Merge Retro
+
+## Outcome
+
+`REL_push-pr-review-merge` is closed. The v17 release branch was merged to
+`main`, the signed `v17.0.0` tag was created, npm and JSR publish recovery
+completed, and the short-lived release branches were pruned from `origin`.
+
+## What Went Well
+
+- The final release coordination happened through visible PR history instead of
+ local-only release state.
+- Follow-up repair PRs kept publication and CI hardening close to the release
+ merge, which makes the release story auditable from `main`.
+- Registry checks confirmed that both npm and JSR expose `17.0.0` as the
+ current package version.
+
+## What Was Messy
+
+- `BEARING.md` and the 0145 design doc drifted behind reality after the
+ package published.
+- The v17 DAG still showed the final coordination node as open after the branch
+ had landed.
+- Local git fsmonitor was producing noisy status/diff warnings during the
+ closeout inspection.
+
+## Follow-Up
+
+Start v18 from a truthful signpost: Continuum/WARP Optic compatibility through
+Wesley-generated artifacts, explicit evidence posture, and `warp-ttd`
+acceptance over generated-family facts.
+
+## Battle Report
+
+The release train made it into the station, then the station sign kept saying
+"boarding soon." This retro fixes the sign. The next mess is bigger: teach
+`git-warp` to speak Continuum contract families without dressing adapter
+folklore up as native witnesshood.
diff --git a/index.ts b/index.ts
index e4719aed..260c1a82 100644
--- a/index.ts
+++ b/index.ts
@@ -46,6 +46,7 @@ import NoOpLogger from './src/infrastructure/adapters/NoOpLogger.ts';
import ConsoleLogger, { LogLevel } from './src/infrastructure/adapters/ConsoleLogger.ts';
import {
AuditError,
+ ContinuumArtifactAuthorityError,
EncryptionError,
ForkError,
IndexError,
@@ -207,6 +208,15 @@ import {
exportCoordinateComparisonFact,
exportCoordinateTransferPlanFact,
} from './src/domain/services/CoordinateFactExport.ts';
+import ContinuumArtifactAuthority from './src/domain/continuum/ContinuumArtifactAuthority.ts';
+import ContinuumArtifactDescriptor from './src/domain/continuum/ContinuumArtifactDescriptor.ts';
+import ContinuumArtifactIngestionPolicy from './src/domain/continuum/ContinuumArtifactIngestionPolicy.ts';
+import ContinuumFamilyId from './src/domain/continuum/ContinuumFamilyId.ts';
+import ContinuumArtifactJsonFileAdapter from './src/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.ts';
+import type { ContinuumArtifactAuthorityValue } from './src/domain/continuum/ContinuumArtifactAuthority.ts';
+import type { ContinuumArtifactDescriptorFields } from './src/domain/continuum/ContinuumArtifactDescriptor.ts';
+import type { ContinuumFamilyIdValue } from './src/domain/continuum/ContinuumFamilyId.ts';
+import type { ContinuumArtifactJsonLoadContext } from './src/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.ts';
export {
GitGraphAdapter,
@@ -247,6 +257,7 @@ export {
// Error types for integrity failure handling
AuditError,
+ ContinuumArtifactAuthorityError,
EncryptionError,
PatchError,
ForkError,
@@ -318,6 +329,13 @@ export {
exportCoordinateComparisonFact,
exportCoordinateTransferPlanFact,
+ // Continuum compatibility artifacts
+ ContinuumArtifactAuthority,
+ ContinuumArtifactDescriptor,
+ ContinuumArtifactIngestionPolicy,
+ ContinuumFamilyId,
+ ContinuumArtifactJsonFileAdapter,
+
// Tick receipts (LIGHTHOUSE)
createTickReceipt,
tickReceiptCanonicalJson,
@@ -360,7 +378,15 @@ export {
SyncSecret,
};
-export type { PropValue, SnapshotPropValue, SyncRateLimitConfig };
+export type {
+ PropValue,
+ SnapshotPropValue,
+ SyncRateLimitConfig,
+ ContinuumArtifactAuthorityValue,
+ ContinuumArtifactDescriptorFields,
+ ContinuumArtifactJsonLoadContext,
+ ContinuumFamilyIdValue,
+};
// WarpApp is the primary product-facing API for v15.
export default WarpApp;
diff --git a/src/domain/continuum/ContinuumArtifactAuthority.ts b/src/domain/continuum/ContinuumArtifactAuthority.ts
new file mode 100644
index 00000000..3059a07d
--- /dev/null
+++ b/src/domain/continuum/ContinuumArtifactAuthority.ts
@@ -0,0 +1,60 @@
+import WarpError from '../errors/WarpError.ts';
+
+const GENERATED_ARTIFACT_AUTHORITY = 'generated-artifact';
+const GENERATED_FIXTURE_AUTHORITY = 'generated-fixture';
+const LOCAL_MIRROR_AUTHORITY = 'local-mirror';
+const HANDWRITTEN_MIRROR_AUTHORITY = 'handwritten-mirror';
+
+export type ContinuumArtifactAuthorityValue =
+ | typeof GENERATED_ARTIFACT_AUTHORITY
+ | typeof GENERATED_FIXTURE_AUTHORITY
+ | typeof LOCAL_MIRROR_AUTHORITY
+ | typeof HANDWRITTEN_MIRROR_AUTHORITY;
+
+export const CONTINUUM_ARTIFACT_AUTHORITIES: readonly ContinuumArtifactAuthorityValue[] = Object.freeze([
+ GENERATED_ARTIFACT_AUTHORITY,
+ GENERATED_FIXTURE_AUTHORITY,
+ LOCAL_MIRROR_AUTHORITY,
+ HANDWRITTEN_MIRROR_AUTHORITY,
+]);
+
+/** Runtime-backed authority posture for an ingested Continuum artifact. */
+export default class ContinuumArtifactAuthority {
+ readonly value: ContinuumArtifactAuthorityValue;
+
+ constructor(value: string) {
+ this.value = requireContinuumArtifactAuthority(value);
+ Object.freeze(this);
+ }
+
+ /** Returns true for Wesley-generated artifacts and documented fixtures. */
+ isGeneratedAuthority(): boolean {
+ return (
+ this.value === GENERATED_ARTIFACT_AUTHORITY ||
+ this.value === GENERATED_FIXTURE_AUTHORITY
+ );
+ }
+
+ /** Returns the stable authority string. */
+ toString(): string {
+ return this.value;
+ }
+}
+
+/** Validates a raw authority posture string. */
+export function requireContinuumArtifactAuthority(value: string): ContinuumArtifactAuthorityValue {
+ if (typeof value !== 'string') {
+ throw new WarpError(
+ `Continuum artifact authority must be one of: ${CONTINUUM_ARTIFACT_AUTHORITIES.join(', ')}`,
+ 'E_VALIDATION',
+ );
+ }
+ const valid = CONTINUUM_ARTIFACT_AUTHORITIES.find((candidate) => candidate === value);
+ if (valid === undefined) {
+ throw new WarpError(
+ `Continuum artifact authority must be one of: ${CONTINUUM_ARTIFACT_AUTHORITIES.join(', ')}`,
+ 'E_VALIDATION',
+ );
+ }
+ return valid;
+}
diff --git a/src/domain/continuum/ContinuumArtifactDescriptor.ts b/src/domain/continuum/ContinuumArtifactDescriptor.ts
new file mode 100644
index 00000000..bfb48b29
--- /dev/null
+++ b/src/domain/continuum/ContinuumArtifactDescriptor.ts
@@ -0,0 +1,139 @@
+import ContinuumArtifactAuthority from './ContinuumArtifactAuthority.ts';
+import ContinuumFamilyId from './ContinuumFamilyId.ts';
+import WarpError from '../errors/WarpError.ts';
+
+export type ContinuumArtifactDescriptorFields = {
+ readonly familyId: string | ContinuumFamilyId;
+ readonly sourceSchemaPath: string;
+ readonly generatedBy: string;
+ readonly artifactKind: string;
+ readonly authority: string | ContinuumArtifactAuthority;
+ readonly targets: readonly string[];
+ readonly version?: string;
+ readonly witnessScope?: string;
+ readonly artifactDigest?: string;
+ readonly schemaHash?: string;
+ readonly sourceHash?: string;
+ readonly integrityStatus?: string;
+ readonly integrityScope?: string;
+ readonly hashAlgorithm?: string;
+ readonly signatureAlgorithm?: string;
+ readonly signatureKeyId?: string;
+ readonly generatedLegs?: readonly string[];
+ readonly generatedFiles?: readonly string[];
+};
+
+/** Runtime-backed descriptor for a generated Continuum family artifact. */
+export default class ContinuumArtifactDescriptor {
+ readonly familyId: ContinuumFamilyId;
+ readonly sourceSchemaPath: string;
+ readonly generatedBy: string;
+ readonly artifactKind: string;
+ readonly authority: ContinuumArtifactAuthority;
+ readonly targets: readonly string[];
+ readonly version: string | undefined;
+ readonly witnessScope: string | undefined;
+ readonly artifactDigest: string | undefined;
+ readonly schemaHash: string | undefined;
+ readonly sourceHash: string | undefined;
+ readonly integrityStatus: string | undefined;
+ readonly integrityScope: string | undefined;
+ readonly hashAlgorithm: string | undefined;
+ readonly signatureAlgorithm: string | undefined;
+ readonly signatureKeyId: string | undefined;
+ readonly generatedLegs: readonly string[] | undefined;
+ readonly generatedFiles: readonly string[] | undefined;
+
+ constructor(fields: ContinuumArtifactDescriptorFields) {
+ const { familyId, sourceSchemaPath, generatedBy, artifactKind, authority, targets } = fields;
+ this.familyId = normalizeFamilyId(familyId);
+ this.sourceSchemaPath = requireNonEmptyString(sourceSchemaPath, 'sourceSchemaPath');
+ this.generatedBy = requireNonEmptyString(generatedBy, 'generatedBy');
+ this.artifactKind = requireNonEmptyString(artifactKind, 'artifactKind');
+ this.authority = normalizeAuthority(authority);
+ this.targets = freezeTargets(targets);
+ this.version = optionalNonEmptyString(fields.version, 'version');
+ this.witnessScope = optionalNonEmptyString(fields.witnessScope, 'witnessScope');
+ this.artifactDigest = optionalNonEmptyString(fields.artifactDigest, 'artifactDigest');
+ this.schemaHash = optionalNonEmptyString(fields.schemaHash, 'schemaHash');
+ this.sourceHash = optionalNonEmptyString(fields.sourceHash, 'sourceHash');
+ this.integrityStatus = optionalNonEmptyString(fields.integrityStatus, 'integrityStatus');
+ this.integrityScope = optionalNonEmptyString(fields.integrityScope, 'integrityScope');
+ this.hashAlgorithm = optionalNonEmptyString(fields.hashAlgorithm, 'hashAlgorithm');
+ this.signatureAlgorithm = optionalNonEmptyString(fields.signatureAlgorithm, 'signatureAlgorithm');
+ this.signatureKeyId = optionalNonEmptyString(fields.signatureKeyId, 'signatureKeyId');
+ this.generatedLegs = optionalStringArray(fields.generatedLegs, 'generatedLegs');
+ this.generatedFiles = optionalStringArray(fields.generatedFiles, 'generatedFiles');
+ Object.freeze(this);
+ }
+
+ /** Returns true when the descriptor includes the requested generation target. */
+ hasTarget(target: string): boolean {
+ return this.targets.includes(target);
+ }
+
+ /** Returns true when the artifact may be used as generated authority. */
+ hasGeneratedAuthority(): boolean {
+ return this.authority.isGeneratedAuthority();
+ }
+}
+
+/** Normalizes a family id carrier. */
+function normalizeFamilyId(value: string | ContinuumFamilyId): ContinuumFamilyId {
+ if (value instanceof ContinuumFamilyId) {
+ return value;
+ }
+ return new ContinuumFamilyId(value);
+}
+
+/** Normalizes an authority carrier. */
+function normalizeAuthority(value: string | ContinuumArtifactAuthority): ContinuumArtifactAuthority {
+ if (value instanceof ContinuumArtifactAuthority) {
+ return value;
+ }
+ return new ContinuumArtifactAuthority(value);
+}
+
+/** Validates a required non-empty string. */
+function requireNonEmptyString(value: string, name: string): string {
+ if (typeof value !== 'string' || value.length === 0) {
+ throw new WarpError(`${name} must be a non-empty string`, 'E_VALIDATION');
+ }
+ return value;
+}
+
+/** Validates an optional non-empty string. */
+function optionalNonEmptyString(value: string | undefined, name: string): string | undefined {
+ if (value === undefined) {
+ return undefined;
+ }
+ return requireNonEmptyString(value, name);
+}
+
+/** Freezes and validates a generated target list. */
+function freezeTargets(targets: readonly string[]): readonly string[] {
+ if (!Array.isArray(targets) || targets.length === 0) {
+ throw new WarpError('targets must contain at least one generated target', 'E_VALIDATION');
+ }
+ return freezeStringArray(targets, 'targets[]');
+}
+
+/** Validates an optional generated string list. */
+function optionalStringArray(value: readonly string[] | undefined, name: string): readonly string[] | undefined {
+ if (value === undefined) {
+ return undefined;
+ }
+ if (!Array.isArray(value)) {
+ throw new WarpError(`${name} must be a string array`, 'E_VALIDATION');
+ }
+ return freezeStringArray(value, `${name}[]`);
+}
+
+/** Freezes and validates a generated string list. */
+function freezeStringArray(values: readonly string[], name: string): readonly string[] {
+ const normalized: string[] = [];
+ for (const value of values) {
+ normalized.push(requireNonEmptyString(value, name));
+ }
+ return Object.freeze(normalized);
+}
diff --git a/src/domain/continuum/ContinuumArtifactIngestionPolicy.ts b/src/domain/continuum/ContinuumArtifactIngestionPolicy.ts
new file mode 100644
index 00000000..d35ad1e0
--- /dev/null
+++ b/src/domain/continuum/ContinuumArtifactIngestionPolicy.ts
@@ -0,0 +1,41 @@
+import ContinuumArtifactAuthorityError from '../errors/ContinuumArtifactAuthorityError.ts';
+import type ContinuumArtifactDescriptor from './ContinuumArtifactDescriptor.ts';
+
+const WESLEY_REALIZATION_MANIFEST_KIND = 'wesley.realization.manifest.v1';
+const WESLEY_REALIZATION_MANIFEST_AUTHORITY = 'generated-artifact';
+const CONTINUUM_FIXTURE_KIND = 'continuum.family.fixture';
+const CONTINUUM_FIXTURE_AUTHORITY = 'generated-fixture';
+
+/** Policy gate for admitting generated Continuum family artifacts. */
+export default class ContinuumArtifactIngestionPolicy {
+ /** Accepts generated artifacts and documented fixtures, rejecting mirrors. */
+ ingest(descriptor: ContinuumArtifactDescriptor): ContinuumArtifactDescriptor {
+ this.assertGeneratedAuthority(descriptor);
+ return descriptor;
+ }
+
+ /** Rejects descriptors whose authority would make local mirrors canonical. */
+ assertGeneratedAuthority(descriptor: ContinuumArtifactDescriptor): void {
+ const expectedAuthority = expectedGeneratedAuthority(descriptor);
+ const actualAuthority = descriptor.authority.toString();
+ if (descriptor.hasGeneratedAuthority() && actualAuthority === expectedAuthority) {
+ return;
+ }
+ throw new ContinuumArtifactAuthorityError(
+ `Continuum family ${descriptor.familyId.toString()} artifact kind ${descriptor.artifactKind} must use authority ${expectedAuthority}, not ${actualAuthority}`,
+ );
+ }
+}
+
+/** Returns the generated authority required for the descriptor's artifact kind. */
+function expectedGeneratedAuthority(descriptor: ContinuumArtifactDescriptor): string {
+ if (descriptor.artifactKind === WESLEY_REALIZATION_MANIFEST_KIND) {
+ return WESLEY_REALIZATION_MANIFEST_AUTHORITY;
+ }
+ if (descriptor.artifactKind === CONTINUUM_FIXTURE_KIND) {
+ return CONTINUUM_FIXTURE_AUTHORITY;
+ }
+ throw new ContinuumArtifactAuthorityError(
+ `Continuum family ${descriptor.familyId.toString()} artifact kind ${descriptor.artifactKind} is not a recognized generated Continuum artifact shape`,
+ );
+}
diff --git a/src/domain/continuum/ContinuumFamilyId.ts b/src/domain/continuum/ContinuumFamilyId.ts
new file mode 100644
index 00000000..fab49ef0
--- /dev/null
+++ b/src/domain/continuum/ContinuumFamilyId.ts
@@ -0,0 +1,57 @@
+import WarpError from '../errors/WarpError.ts';
+
+const RECEIPT_FAMILY_ID = 'receipt-family';
+const SETTLEMENT_FAMILY_ID = 'settlement-family';
+const NEIGHBORHOOD_CORE_FAMILY_ID = 'neighborhood-core-family';
+const RUNTIME_BOUNDARY_FAMILY_ID = 'runtime-boundary-family';
+
+export type ContinuumFamilyIdValue =
+ | typeof RECEIPT_FAMILY_ID
+ | typeof SETTLEMENT_FAMILY_ID
+ | typeof NEIGHBORHOOD_CORE_FAMILY_ID
+ | typeof RUNTIME_BOUNDARY_FAMILY_ID;
+
+export const CONTINUUM_FAMILY_IDS: readonly ContinuumFamilyIdValue[] = Object.freeze([
+ RECEIPT_FAMILY_ID,
+ SETTLEMENT_FAMILY_ID,
+ NEIGHBORHOOD_CORE_FAMILY_ID,
+ RUNTIME_BOUNDARY_FAMILY_ID,
+]);
+
+/** Runtime-backed identifier for a Continuum-owned contract family. */
+export default class ContinuumFamilyId {
+ readonly value: ContinuumFamilyIdValue;
+
+ constructor(value: string) {
+ this.value = requireContinuumFamilyId(value);
+ Object.freeze(this);
+ }
+
+ /** Returns true when both ids name the same Continuum family. */
+ equals(other: ContinuumFamilyId): boolean {
+ return this.value === other.value;
+ }
+
+ /** Returns the stable family id string. */
+ toString(): string {
+ return this.value;
+ }
+}
+
+/** Validates a raw family id string. */
+export function requireContinuumFamilyId(value: string): ContinuumFamilyIdValue {
+ if (typeof value !== 'string') {
+ throw new WarpError(
+ `Continuum family id must be one of: ${CONTINUUM_FAMILY_IDS.join(', ')}`,
+ 'E_VALIDATION',
+ );
+ }
+ const valid = CONTINUUM_FAMILY_IDS.find((candidate) => candidate === value);
+ if (valid === undefined) {
+ throw new WarpError(
+ `Continuum family id must be one of: ${CONTINUUM_FAMILY_IDS.join(', ')}`,
+ 'E_VALIDATION',
+ );
+ }
+ return valid;
+}
diff --git a/src/domain/errors/ContinuumArtifactAuthorityError.ts b/src/domain/errors/ContinuumArtifactAuthorityError.ts
new file mode 100644
index 00000000..4734cd51
--- /dev/null
+++ b/src/domain/errors/ContinuumArtifactAuthorityError.ts
@@ -0,0 +1,8 @@
+import WarpError, { type WarpErrorOptions } from './WarpError.ts';
+
+/** Error thrown when a Continuum artifact would create shadow authority. */
+export default class ContinuumArtifactAuthorityError extends WarpError {
+ constructor(message: string, options: WarpErrorOptions = {}) {
+ super(message, 'E_CONTINUUM_ARTIFACT_AUTHORITY', options);
+ }
+}
diff --git a/src/domain/errors/index.ts b/src/domain/errors/index.ts
index 2dd7cb41..f087fcc3 100644
--- a/src/domain/errors/index.ts
+++ b/src/domain/errors/index.ts
@@ -1,6 +1,7 @@
/** Custom error classes for domain operations. */
export { default as AuditError } from './AuditError.ts';
+export { default as ContinuumArtifactAuthorityError } from './ContinuumArtifactAuthorityError.ts';
export { default as EncryptionError } from './EncryptionError.ts';
export { default as ForkError } from './ForkError.ts';
export { default as IndexError } from './IndexError.ts';
diff --git a/src/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.ts b/src/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.ts
new file mode 100644
index 00000000..c6e790e4
--- /dev/null
+++ b/src/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.ts
@@ -0,0 +1,34 @@
+import { readFile } from 'node:fs/promises';
+
+import ContinuumArtifactDescriptor from '../../domain/continuum/ContinuumArtifactDescriptor.ts';
+import ContinuumArtifactIngestionPolicy from '../../domain/continuum/ContinuumArtifactIngestionPolicy.ts';
+import type { ContinuumArtifactJsonLoadContext } from './ContinuumArtifactJsonLoadContext.ts';
+import { parseContinuumArtifactDescriptorFields } from './continuumArtifactJsonParser.ts';
+import { validateLoadContext } from './continuumArtifactJsonValidation.ts';
+
+export type { ContinuumArtifactJsonLoadContext } from './ContinuumArtifactJsonLoadContext.ts';
+
+/** Loads Continuum artifact descriptors from JSON files at the adapter edge. */
+export default class ContinuumArtifactJsonFileAdapter {
+ private readonly policy: ContinuumArtifactIngestionPolicy;
+
+ constructor(policy: ContinuumArtifactIngestionPolicy = new ContinuumArtifactIngestionPolicy()) {
+ this.policy = policy;
+ }
+
+ /** Reads and ingests a generated artifact descriptor from disk. */
+ async loadFile(
+ path: string,
+ context: ContinuumArtifactJsonLoadContext,
+ ): Promise {
+ const raw = await readFile(path, 'utf8');
+ return this.loadString(raw, context);
+ }
+
+ /** Ingests a generated artifact descriptor from JSON text. */
+ loadString(raw: string, context: ContinuumArtifactJsonLoadContext): ContinuumArtifactDescriptor {
+ validateLoadContext(context);
+ const fields = parseContinuumArtifactDescriptorFields(raw, context);
+ return this.policy.ingest(new ContinuumArtifactDescriptor(fields));
+ }
+}
diff --git a/src/infrastructure/adapters/ContinuumArtifactJsonLoadContext.ts b/src/infrastructure/adapters/ContinuumArtifactJsonLoadContext.ts
new file mode 100644
index 00000000..466baa09
--- /dev/null
+++ b/src/infrastructure/adapters/ContinuumArtifactJsonLoadContext.ts
@@ -0,0 +1,13 @@
+import type ContinuumArtifactAuthority from '../../domain/continuum/ContinuumArtifactAuthority.ts';
+import type ContinuumFamilyId from '../../domain/continuum/ContinuumFamilyId.ts';
+
+export type ContinuumArtifactJsonLoadContext = {
+ readonly familyId: string | ContinuumFamilyId;
+ readonly authority: string | ContinuumArtifactAuthority;
+ readonly sourceSchemaPath?: string;
+ readonly generatedBy?: string;
+ readonly version?: string;
+ readonly targets?: readonly string[];
+ readonly witnessScope?: string;
+ readonly artifactDigest?: string;
+};
diff --git a/src/infrastructure/adapters/DescriptorFieldSource.ts b/src/infrastructure/adapters/DescriptorFieldSource.ts
new file mode 100644
index 00000000..1970efe6
--- /dev/null
+++ b/src/infrastructure/adapters/DescriptorFieldSource.ts
@@ -0,0 +1,15 @@
+export type DescriptorFieldSource = {
+ readonly sourceSchemaPath: string;
+ readonly generatedBy: string;
+ readonly artifactKind: string;
+ readonly targets: readonly string[];
+ readonly schemaHash?: string;
+ readonly sourceHash?: string;
+ readonly integrityStatus?: string;
+ readonly integrityScope?: string;
+ readonly hashAlgorithm?: string;
+ readonly signatureAlgorithm?: string;
+ readonly signatureKeyId?: string;
+ readonly generatedLegs?: readonly string[];
+ readonly generatedFiles?: readonly string[];
+};
diff --git a/src/infrastructure/adapters/GeneratedLegInventory.ts b/src/infrastructure/adapters/GeneratedLegInventory.ts
new file mode 100644
index 00000000..b2b2e0f8
--- /dev/null
+++ b/src/infrastructure/adapters/GeneratedLegInventory.ts
@@ -0,0 +1,4 @@
+export type GeneratedLegInventory = {
+ readonly names: readonly string[];
+ readonly files: readonly string[];
+};
diff --git a/src/infrastructure/adapters/JsonObject.ts b/src/infrastructure/adapters/JsonObject.ts
new file mode 100644
index 00000000..962622a0
--- /dev/null
+++ b/src/infrastructure/adapters/JsonObject.ts
@@ -0,0 +1 @@
+export type JsonObject = Readonly>;
diff --git a/src/infrastructure/adapters/WesleyIntegrityFields.ts b/src/infrastructure/adapters/WesleyIntegrityFields.ts
new file mode 100644
index 00000000..08f6e5f9
--- /dev/null
+++ b/src/infrastructure/adapters/WesleyIntegrityFields.ts
@@ -0,0 +1,7 @@
+export type WesleyIntegrityFields = {
+ readonly integrityStatus: string;
+ readonly integrityScope: string;
+ readonly hashAlgorithm: string;
+ readonly signatureAlgorithm: string;
+ readonly signatureKeyId: string;
+};
diff --git a/src/infrastructure/adapters/continuumArtifactJsonParser.ts b/src/infrastructure/adapters/continuumArtifactJsonParser.ts
new file mode 100644
index 00000000..2932209d
--- /dev/null
+++ b/src/infrastructure/adapters/continuumArtifactJsonParser.ts
@@ -0,0 +1,240 @@
+import type { ContinuumArtifactDescriptorFields } from '../../domain/continuum/ContinuumArtifactDescriptor.ts';
+import AdapterValidationError from '../../domain/errors/AdapterValidationError.ts';
+import type { ContinuumArtifactJsonLoadContext } from './ContinuumArtifactJsonLoadContext.ts';
+import type { DescriptorFieldSource } from './DescriptorFieldSource.ts';
+import type { JsonObject } from './JsonObject.ts';
+import { readGeneratedLegs, readSealedIntegrity } from './continuumWesleyManifestInventory.ts';
+import {
+ isJsonObject,
+ readContextString,
+ readOptionalStringArray,
+ readOptionalString,
+ readRequiredString,
+ readStringArray,
+ rejectUnknownKeys,
+ requireContextAuthority,
+ requireJsonObject,
+} from './continuumArtifactJsonValidation.ts';
+import { readOptionalFootprints } from './continuumFamilyFixtureValidation.ts';
+
+const WESLEY_REALIZATION_MANIFEST_KIND = 'wesley.realization.manifest.v1';
+const WESLEY_REALIZATION_MANIFEST_AUTHORITY = 'generated-artifact';
+const CONTINUUM_FIXTURE_KIND = 'continuum.family.fixture';
+const CONTINUUM_FIXTURE_AUTHORITY = 'generated-fixture';
+const CONTINUUM_FIXTURE_GENERATOR = 'continuum/wesley fixture';
+const CONTINUUM_FIXTURE_TARGET = 'continuum-fixture';
+
+/** Converts untrusted JSON text into descriptor fields. */
+export function parseContinuumArtifactDescriptorFields(
+ raw: string,
+ context: ContinuumArtifactJsonLoadContext,
+): ContinuumArtifactDescriptorFields {
+ return parseDescriptorFields(parseJson(raw), context);
+}
+
+/** Parses untrusted descriptor JSON without leaking platform SyntaxError. */
+function parseJson(raw: string): unknown {
+ try {
+ return JSON.parse(raw);
+ } catch {
+ throw new AdapterValidationError('Continuum artifact descriptor JSON must be valid JSON');
+ }
+}
+
+/** Converts untrusted JSON into descriptor fields. */
+function parseDescriptorFields(
+ value: unknown,
+ context: ContinuumArtifactJsonLoadContext,
+): ContinuumArtifactDescriptorFields {
+ const source = requireJsonObject(value, 'Continuum artifact descriptor JSON');
+ if (source['kind'] === WESLEY_REALIZATION_MANIFEST_KIND) {
+ return parseWesleyRealizationManifest(source, context);
+ }
+ if (isContinuumFamilyFixture(source)) {
+ return parseContinuumFamilyFixture(source, context);
+ }
+ throw new AdapterValidationError(
+ 'Continuum artifact descriptor JSON must be a Wesley realization manifest or Continuum family fixture',
+ );
+}
+
+/** Converts a Wesley realization manifest into descriptor fields. */
+function parseWesleyRealizationManifest(
+ source: JsonObject,
+ context: ContinuumArtifactJsonLoadContext,
+): ContinuumArtifactDescriptorFields {
+ requireContextAuthority(context, WESLEY_REALIZATION_MANIFEST_AUTHORITY, 'Wesley realization manifest');
+ validateWesleyManifestEnvelope(source);
+ const integrity = readSealedIntegrity(source);
+ const legs = readGeneratedLegs(source);
+ return descriptorFields(context, {
+ ...wesleyManifestFields(source, context),
+ ...integrity,
+ generatedLegs: legs.names,
+ generatedFiles: legs.files,
+ });
+}
+
+/** Validates the non-semantic envelope fields on a Wesley manifest. */
+function validateWesleyManifestEnvelope(source: JsonObject): void {
+ rejectUnknownKeys(
+ source,
+ [
+ 'kind',
+ 'schemaPath',
+ 'canonicalSchemaPath',
+ 'schemaHash',
+ 'sourceHash',
+ 'outDir',
+ 'targets',
+ 'integrity',
+ 'generatedLegs',
+ 'proves',
+ 'doesNotProve',
+ ],
+ 'Wesley realization manifest',
+ );
+ readOptionalString(source, 'canonicalSchemaPath');
+ readOptionalString(source, 'outDir');
+ readOptionalStringArray(source, 'proves');
+ readOptionalStringArray(source, 'doesNotProve');
+}
+
+/** Reads the descriptor-facing fields from a Wesley realization manifest. */
+function wesleyManifestFields(
+ source: JsonObject,
+ context: ContinuumArtifactJsonLoadContext,
+): DescriptorFieldSource {
+ return {
+ sourceSchemaPath: readRequiredString(source, 'schemaPath'),
+ generatedBy: context.generatedBy ?? 'wesley compile',
+ artifactKind: WESLEY_REALIZATION_MANIFEST_KIND,
+ targets: readStringArray(source, 'targets'),
+ schemaHash: readRequiredString(source, 'schemaHash'),
+ sourceHash: readRequiredString(source, 'sourceHash'),
+ };
+}
+
+/** Converts a Continuum family fixture into descriptor fields. */
+function parseContinuumFamilyFixture(
+ source: JsonObject,
+ context: ContinuumArtifactJsonLoadContext,
+): ContinuumArtifactDescriptorFields {
+ requireContextAuthority(context, CONTINUUM_FIXTURE_AUTHORITY, 'Continuum family fixture');
+ rejectUnknownKeys(
+ source,
+ ['objectTypes', 'enumTypes', 'ops', 'invariants', 'footprints', 'types'],
+ 'Continuum family fixture',
+ );
+ readOptionalStringArray(source, 'objectTypes');
+ readOptionalStringArray(source, 'enumTypes');
+ readOperations(source);
+ readOptionalStringArray(source, 'invariants');
+ readOptionalFootprints(source);
+ readOptionalTypeMap(source);
+
+ return descriptorFields(context, {
+ sourceSchemaPath: readContextString(context.sourceSchemaPath, 'sourceSchemaPath'),
+ generatedBy: context.generatedBy ?? CONTINUUM_FIXTURE_GENERATOR,
+ artifactKind: CONTINUUM_FIXTURE_KIND,
+ targets: context.targets ?? [CONTINUUM_FIXTURE_TARGET],
+ });
+}
+
+/** Builds descriptor fields without trusting authority from the untrusted JSON. */
+function descriptorFields(
+ context: ContinuumArtifactJsonLoadContext,
+ required: DescriptorFieldSource,
+): ContinuumArtifactDescriptorFields {
+ return {
+ familyId: context.familyId,
+ sourceSchemaPath: required.sourceSchemaPath,
+ generatedBy: required.generatedBy,
+ artifactKind: required.artifactKind,
+ authority: context.authority,
+ targets: required.targets,
+ ...contextDescriptorFields(context),
+ ...sourceHashFields(required),
+ ...integrityDescriptorFields(required),
+ ...signatureDescriptorFields(required),
+ ...generatedInventoryFields(required),
+ };
+}
+
+/** Selects optional descriptor fields provided by load context. */
+function contextDescriptorFields(
+ context: ContinuumArtifactJsonLoadContext,
+): Partial {
+ return {
+ ...(context.version !== undefined ? { version: context.version } : {}),
+ ...(context.witnessScope !== undefined ? { witnessScope: context.witnessScope } : {}),
+ ...(context.artifactDigest !== undefined ? { artifactDigest: context.artifactDigest } : {}),
+ };
+}
+
+/** Selects optional schema hash fields from source evidence. */
+function sourceHashFields(required: DescriptorFieldSource): Partial {
+ return {
+ ...(required.schemaHash !== undefined ? { schemaHash: required.schemaHash } : {}),
+ ...(required.sourceHash !== undefined ? { sourceHash: required.sourceHash } : {}),
+ };
+}
+
+/** Selects optional integrity fields from source evidence. */
+function integrityDescriptorFields(required: DescriptorFieldSource): Partial {
+ return {
+ ...(required.integrityStatus !== undefined ? { integrityStatus: required.integrityStatus } : {}),
+ ...(required.integrityScope !== undefined ? { integrityScope: required.integrityScope } : {}),
+ ...(required.hashAlgorithm !== undefined ? { hashAlgorithm: required.hashAlgorithm } : {}),
+ };
+}
+
+/** Selects optional signature fields from source evidence. */
+function signatureDescriptorFields(required: DescriptorFieldSource): Partial {
+ return {
+ ...(required.signatureAlgorithm !== undefined ? { signatureAlgorithm: required.signatureAlgorithm } : {}),
+ ...(required.signatureKeyId !== undefined ? { signatureKeyId: required.signatureKeyId } : {}),
+ };
+}
+
+/** Selects optional generated inventory fields from source evidence. */
+function generatedInventoryFields(required: DescriptorFieldSource): Partial {
+ return {
+ ...(required.generatedLegs !== undefined ? { generatedLegs: required.generatedLegs } : {}),
+ ...(required.generatedFiles !== undefined ? { generatedFiles: required.generatedFiles } : {}),
+ };
+}
+
+/** Returns true when the top-level object has the Continuum fixture shape. */
+function isContinuumFamilyFixture(source: JsonObject): boolean {
+ return Array.isArray(source['ops']) && (
+ Array.isArray(source['objectTypes']) ||
+ isJsonObject(source['types'])
+ );
+}
+
+/** Reads and validates Continuum fixture operations. */
+function readOperations(source: JsonObject): void {
+ const { ops } = source;
+ if (!Array.isArray(ops) || ops.length === 0) {
+ throw new AdapterValidationError('Continuum family fixture field "ops" must be a non-empty operation array');
+ }
+ for (const entry of ops) {
+ const op = requireJsonObject(entry, 'Continuum family fixture operation');
+ rejectUnknownKeys(op, ['name', 'resultType'], 'Continuum family fixture operation');
+ readRequiredString(op, 'name');
+ readRequiredString(op, 'resultType');
+ }
+}
+
+/** Reads and validates an optional Continuum boundary type map. */
+function readOptionalTypeMap(source: JsonObject): void {
+ const { types } = source;
+ if (types === undefined) {
+ return;
+ }
+ const typeMap = requireJsonObject(types, 'Continuum family fixture types');
+ for (const name of Object.keys(typeMap)) {
+ readStringArray(typeMap, name);
+ }
+}
diff --git a/src/infrastructure/adapters/continuumArtifactJsonValidation.ts b/src/infrastructure/adapters/continuumArtifactJsonValidation.ts
new file mode 100644
index 00000000..44330601
--- /dev/null
+++ b/src/infrastructure/adapters/continuumArtifactJsonValidation.ts
@@ -0,0 +1,157 @@
+import ContinuumArtifactAuthority from '../../domain/continuum/ContinuumArtifactAuthority.ts';
+import AdapterValidationError from '../../domain/errors/AdapterValidationError.ts';
+import type { ContinuumArtifactJsonLoadContext } from './ContinuumArtifactJsonLoadContext.ts';
+import type { JsonObject } from './JsonObject.ts';
+
+const LOAD_CONTEXT_KEYS = Object.freeze([
+ 'familyId',
+ 'authority',
+ 'sourceSchemaPath',
+ 'generatedBy',
+ 'version',
+ 'targets',
+ 'witnessScope',
+ 'artifactDigest',
+]);
+
+/** Validates the caller-supplied load context boundary. */
+export function validateLoadContext(context: ContinuumArtifactJsonLoadContext): void {
+ rejectUnknownKeys(
+ requireJsonObject(context, 'Continuum artifact load context'),
+ LOAD_CONTEXT_KEYS,
+ 'Continuum artifact load context',
+ );
+}
+
+/** Requires the context authority that matches the parsed artifact shape. */
+export function requireContextAuthority(
+ context: ContinuumArtifactJsonLoadContext,
+ expected: string,
+ label: string,
+): void {
+ const actual = readContextAuthority(context.authority);
+ if (actual !== expected) {
+ throw new AdapterValidationError(`${label} load context authority must be ${expected}`);
+ }
+}
+
+/** Requires a non-array JSON object. */
+export function requireJsonObject(value: unknown, label: string): JsonObject {
+ if (!isJsonObject(value)) {
+ throw new AdapterValidationError(`${label} must be an object`);
+ }
+ return value;
+}
+
+/** Returns true when a value is a non-array JSON object. */
+export function isJsonObject(value: unknown): value is JsonObject {
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
+}
+
+/** Rejects unexpected fields at a parsed JSON boundary. */
+export function rejectUnknownKeys(source: JsonObject, allowed: readonly string[], label: string): void {
+ for (const key of Object.keys(source)) {
+ if (!allowed.includes(key)) {
+ throw new AdapterValidationError(`${label} field "${key}" is not allowed`);
+ }
+ }
+}
+
+/** Reads a required string field. */
+export function readRequiredString(source: JsonObject, key: string): string {
+ const value = source[key];
+ if (typeof value !== 'string' || value.length === 0) {
+ throw new AdapterValidationError(`Continuum artifact descriptor field "${key}" must be a non-empty string`);
+ }
+ return value;
+}
+
+/** Reads a required context string. */
+export function readContextString(value: string | undefined, key: string): string {
+ if (typeof value !== 'string' || value.length === 0) {
+ throw new AdapterValidationError(`Continuum artifact load context field "${key}" must be a non-empty string`);
+ }
+ return value;
+}
+
+/** Reads a required number field. */
+export function readRequiredNumber(source: JsonObject, key: string): number {
+ const value = source[key];
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
+ throw new AdapterValidationError(`Continuum artifact descriptor field "${key}" must be a finite number`);
+ }
+ return value;
+}
+
+/** Reads an optional generated artifact count. */
+export function readOptionalArtifactCount(source: JsonObject, key: string): number | undefined {
+ const count = readOptionalNumber(source, key);
+ if (count === undefined) {
+ return undefined;
+ }
+ if (!Number.isInteger(count) || count < 0) {
+ throw new AdapterValidationError(`Continuum artifact descriptor field "${key}" must be a non-negative integer`);
+ }
+ return count;
+}
+
+/** Reads an optional string array field. */
+export function readOptionalStringArray(source: JsonObject, key: string): readonly string[] | undefined {
+ const value = source[key];
+ if (value === undefined) {
+ return undefined;
+ }
+ return readStringArray(source, key);
+}
+
+/** Reads an optional string field. */
+export function readOptionalString(source: JsonObject, key: string): string | undefined {
+ const value = source[key];
+ if (value === undefined) {
+ return undefined;
+ }
+ return readRequiredString(source, key);
+}
+
+/** Reads a required string array field. */
+export function readStringArray(source: JsonObject, key: string): readonly string[] {
+ const value = source[key];
+ if (!Array.isArray(value)) {
+ throw new AdapterValidationError(`Continuum artifact descriptor field "${key}" must be a string array`);
+ }
+ const strings: string[] = [];
+ for (const entry of value) {
+ strings.push(readStringArrayEntry(entry, key));
+ }
+ return Object.freeze(strings);
+}
+
+/** Reads an optional number field. */
+function readOptionalNumber(source: JsonObject, key: string): number | undefined {
+ const value = source[key];
+ if (value === undefined) {
+ return undefined;
+ }
+ return readRequiredNumber(source, key);
+}
+
+/** Reads a context authority carrier as a string. */
+function readContextAuthority(value: string | ContinuumArtifactAuthority): string {
+ if (typeof value === 'string') {
+ return value;
+ }
+ if (value instanceof ContinuumArtifactAuthority) {
+ return value.toString();
+ }
+ throw new AdapterValidationError('Continuum artifact load context field "authority" must be an authority carrier');
+}
+
+/** Reads one string array entry. */
+function readStringArrayEntry(value: unknown, key: string): string {
+ if (typeof value !== 'string' || value.length === 0) {
+ throw new AdapterValidationError(
+ `Continuum artifact descriptor field "${key}" must contain only non-empty strings`,
+ );
+ }
+ return value;
+}
diff --git a/src/infrastructure/adapters/continuumFamilyFixtureValidation.ts b/src/infrastructure/adapters/continuumFamilyFixtureValidation.ts
new file mode 100644
index 00000000..861e185e
--- /dev/null
+++ b/src/infrastructure/adapters/continuumFamilyFixtureValidation.ts
@@ -0,0 +1,28 @@
+import AdapterValidationError from '../../domain/errors/AdapterValidationError.ts';
+import type { JsonObject } from './JsonObject.ts';
+import {
+ readRequiredString,
+ readStringArray,
+ rejectUnknownKeys,
+ requireJsonObject,
+} from './continuumArtifactJsonValidation.ts';
+
+/** Reads and validates optional Continuum fixture footprints. */
+export function readOptionalFootprints(source: JsonObject): void {
+ const { footprints } = source;
+ if (footprints === undefined) {
+ return;
+ }
+ if (!Array.isArray(footprints)) {
+ throw new AdapterValidationError('Continuum family fixture field "footprints" must be an array');
+ }
+ for (const entry of footprints) {
+ const footprint = requireJsonObject(entry, 'Continuum family fixture footprint');
+ rejectUnknownKeys(footprint, ['opName', 'reads', 'writes', 'creates', 'deletes'], 'Continuum family fixture footprint');
+ readRequiredString(footprint, 'opName');
+ readStringArray(footprint, 'reads');
+ readStringArray(footprint, 'writes');
+ readStringArray(footprint, 'creates');
+ readStringArray(footprint, 'deletes');
+ }
+}
diff --git a/src/infrastructure/adapters/continuumWesleyManifestInventory.ts b/src/infrastructure/adapters/continuumWesleyManifestInventory.ts
new file mode 100644
index 00000000..2198f037
--- /dev/null
+++ b/src/infrastructure/adapters/continuumWesleyManifestInventory.ts
@@ -0,0 +1,108 @@
+import AdapterValidationError from '../../domain/errors/AdapterValidationError.ts';
+import type { GeneratedLegInventory } from './GeneratedLegInventory.ts';
+import type { JsonObject } from './JsonObject.ts';
+import type { WesleyIntegrityFields } from './WesleyIntegrityFields.ts';
+import {
+ readOptionalArtifactCount,
+ readOptionalStringArray,
+ readRequiredNumber,
+ readRequiredString,
+ rejectUnknownKeys,
+ requireJsonObject,
+} from './continuumArtifactJsonValidation.ts';
+
+/** Reads and validates a Wesley integrity block. */
+export function readSealedIntegrity(source: JsonObject): WesleyIntegrityFields {
+ const integrity = requireJsonObject(source['integrity'], 'Wesley realization manifest integrity');
+ rejectUnknownKeys(
+ integrity,
+ ['status', 'scope', 'hashAlgorithm', 'signatureAlgorithm', 'signatureKeyId'],
+ 'Wesley realization manifest integrity',
+ );
+ const status = readRequiredString(integrity, 'status');
+ if (status !== 'sealed') {
+ throw new AdapterValidationError('Wesley realization manifest integrity status must be sealed');
+ }
+ return {
+ integrityStatus: status,
+ integrityScope: readRequiredString(integrity, 'scope'),
+ hashAlgorithm: readRequiredString(integrity, 'hashAlgorithm'),
+ signatureAlgorithm: readRequiredString(integrity, 'signatureAlgorithm'),
+ signatureKeyId: readRequiredString(integrity, 'signatureKeyId'),
+ };
+}
+
+/** Reads and validates Wesley generated leg inventory. */
+export function readGeneratedLegs(source: JsonObject): GeneratedLegInventory {
+ const generatedLegs = requireJsonObject(source['generatedLegs'], 'Wesley realization manifest generatedLegs');
+ const names = readGeneratedLegNames(generatedLegs);
+ const files: string[] = [];
+ for (const name of names) {
+ for (const path of readGeneratedLegFiles(generatedLegs, name)) {
+ files.push(path);
+ }
+ }
+ return { names, files: Object.freeze(files.sort()) };
+}
+
+/** Reads the sorted generated leg names from a Wesley manifest. */
+function readGeneratedLegNames(generatedLegs: JsonObject): readonly string[] {
+ const names = Object.freeze(Object.keys(generatedLegs).sort());
+ if (names.length === 0) {
+ throw new AdapterValidationError('Wesley realization manifest generatedLegs must contain at least one leg');
+ }
+ return names;
+}
+
+/** Reads and validates one Wesley generated leg inventory. */
+function readGeneratedLegFiles(generatedLegs: JsonObject, name: string): readonly string[] {
+ const leg = requireJsonObject(generatedLegs[name], `Wesley generated leg "${name}"`);
+ validateGeneratedLegEnvelope(leg, name);
+ const artifactCount = readOptionalArtifactCount(leg, 'artifactCount');
+ const legFiles = readGeneratedFiles(leg, name);
+ requireArtifactCountMatchesFiles(artifactCount, legFiles.length, name);
+ return legFiles;
+}
+
+/** Validates one Wesley generated leg envelope. */
+function validateGeneratedLegEnvelope(leg: JsonObject, name: string): void {
+ rejectUnknownKeys(
+ leg,
+ ['outDir', 'schemaHash', 'sourceHash', 'targets', 'artifactCount', 'files'],
+ `Wesley generated leg "${name}"`,
+ );
+ readRequiredString(leg, 'outDir');
+ readRequiredString(leg, 'schemaHash');
+ readRequiredString(leg, 'sourceHash');
+ readOptionalStringArray(leg, 'targets');
+}
+
+/** Requires Wesley's artifact count to match the generated file inventory. */
+function requireArtifactCountMatchesFiles(count: number | undefined, fileCount: number, legName: string): void {
+ if (count !== undefined && count !== fileCount) {
+ throw new AdapterValidationError(
+ `Wesley generated leg "${legName}" field "artifactCount" must match generated file count`,
+ );
+ }
+}
+
+/** Reads generated file entries from one Wesley generated leg. */
+function readGeneratedFiles(source: JsonObject, legName: string): readonly string[] {
+ const value = source['files'];
+ if (value === undefined) {
+ return [];
+ }
+ if (!Array.isArray(value)) {
+ throw new AdapterValidationError(`Wesley generated leg "${legName}" field "files" must be an array`);
+ }
+ const files: string[] = [];
+ for (const entry of value) {
+ const file = requireJsonObject(entry, `Wesley generated leg "${legName}" file`);
+ rejectUnknownKeys(file, ['path', 'size', 'contentHash', 'signature'], `Wesley generated leg "${legName}" file`);
+ files.push(readRequiredString(file, 'path'));
+ readRequiredNumber(file, 'size');
+ readRequiredString(file, 'contentHash');
+ readRequiredString(file, 'signature');
+ }
+ return Object.freeze(files);
+}
diff --git a/test/fixtures/continuum/receipt-family-generated-artifact.json b/test/fixtures/continuum/receipt-family-generated-artifact.json
new file mode 100644
index 00000000..b7d6c950
--- /dev/null
+++ b/test/fixtures/continuum/receipt-family-generated-artifact.json
@@ -0,0 +1,77 @@
+{
+ "objectTypes": [
+ "Capability",
+ "ContinuumReceiptFamilyInvariants",
+ "DeliveryObservation",
+ "Receipt",
+ "Witness"
+ ],
+ "enumTypes": [
+ "CapabilityScope",
+ "DeliveryOutcome",
+ "ExecutionMode",
+ "WitnessKind"
+ ],
+ "ops": [
+ {
+ "name": "capabilities",
+ "resultType": "Capability"
+ },
+ {
+ "name": "deliveryObservations",
+ "resultType": "DeliveryObservation"
+ },
+ {
+ "name": "receipts",
+ "resultType": "Receipt"
+ },
+ {
+ "name": "witnesses",
+ "resultType": "Witness"
+ }
+ ],
+ "invariants": [
+ "delivery_links_receipt",
+ "receipt_input_tick_non_negative",
+ "receipt_output_tick_non_negative",
+ "witness_links_receipt"
+ ],
+ "footprints": [
+ {
+ "opName": "capabilities",
+ "reads": [
+ "Capability"
+ ],
+ "writes": [],
+ "creates": [],
+ "deletes": []
+ },
+ {
+ "opName": "deliveryObservations",
+ "reads": [
+ "DeliveryObservation"
+ ],
+ "writes": [],
+ "creates": [],
+ "deletes": []
+ },
+ {
+ "opName": "receipts",
+ "reads": [
+ "Receipt"
+ ],
+ "writes": [],
+ "creates": [],
+ "deletes": []
+ },
+ {
+ "opName": "witnesses",
+ "reads": [
+ "Witness"
+ ],
+ "writes": [],
+ "creates": [],
+ "deletes": []
+ }
+ ]
+}
diff --git a/test/fixtures/continuum/receipt-family-wesley-realization-manifest.json b/test/fixtures/continuum/receipt-family-wesley-realization-manifest.json
new file mode 100644
index 00000000..399ff90f
--- /dev/null
+++ b/test/fixtures/continuum/receipt-family-wesley-realization-manifest.json
@@ -0,0 +1,67 @@
+{
+ "kind": "wesley.realization.manifest.v1",
+ "schemaPath": "schemas/continuum-receipt-family.graphql",
+ "canonicalSchemaPath": "continuum/schemas/continuum-receipt-family.graphql",
+ "schemaHash": "16bf631145b60e0ec240f97484ff2cb5f534cd38c963cf12044985915766a602",
+ "sourceHash": "16bf631145b60e0ec240f97484ff2cb5f534cd38c963cf12044985915766a602",
+ "outDir": "out/proof",
+ "targets": [
+ "warp-ttd",
+ "echo"
+ ],
+ "integrity": {
+ "status": "sealed",
+ "scope": "generated-leg-files",
+ "hashAlgorithm": "sha256",
+ "signatureAlgorithm": "hmac-sha256",
+ "signatureKeyId": "builtin:wesley-v0.1.0-local-dev-hmac-key"
+ },
+ "generatedLegs": {
+ "warpTtd": {
+ "outDir": "out/proof/warp-ttd",
+ "schemaHash": "16bf631145b60e0ec240f97484ff2cb5f534cd38c963cf12044985915766a602",
+ "sourceHash": "16bf631145b60e0ec240f97484ff2cb5f534cd38c963cf12044985915766a602",
+ "targets": [
+ "manifest",
+ "typescript"
+ ],
+ "files": [
+ {
+ "path": "manifest/schema.json",
+ "size": 12101,
+ "contentHash": "f3322bcc5f07b8a635e24302d77926c983d9dc43c63c1ba7ab50f6ca6e229e0a",
+ "signature": "dc916e2920dc599d1ca1ca5b197b7bc7deb6af7a0c319abf2d26d931d0015e2c"
+ },
+ {
+ "path": "typescript/types.ts",
+ "size": 2463,
+ "contentHash": "54b5d24902357b7504e8bccadaa329133808eed6e3a77433ceaf80cf285a5afe",
+ "signature": "831994c0a68c78a7ed1100568966ab90f6a7a76c8abce5f4419bb1bec2c622f1"
+ }
+ ]
+ },
+ "echo": {
+ "outDir": "out/proof/echo",
+ "schemaHash": "16bf631145b60e0ec240f97484ff2cb5f534cd38c963cf12044985915766a602",
+ "sourceHash": "16bf631145b60e0ec240f97484ff2cb5f534cd38c963cf12044985915766a602",
+ "artifactCount": 1,
+ "files": [
+ {
+ "path": "ir.json",
+ "size": 11881,
+ "contentHash": "ba2e2fa15ed865571fde7a588f346ff4bc4ceded28907d4ab383d393fdab6dfd",
+ "signature": "e164174476ccbbc336bf0262b2926937794eedf0a06bdb812fff5032111506be"
+ }
+ ]
+ }
+ },
+ "proves": [
+ "one authored schema path was compiled into one or more generated consumer legs",
+ "generated legs share one authored schema hash",
+ "the emitted files for each selected target are inspectable from this realization manifest"
+ ],
+ "doesNotProve": [
+ "cross-leg conformance beyond shared schema identity",
+ "runtime semantics"
+ ]
+}
diff --git a/test/unit/domain/continuum/ContinuumArtifactIngestionPolicy.test.ts b/test/unit/domain/continuum/ContinuumArtifactIngestionPolicy.test.ts
new file mode 100644
index 00000000..ddfdbfa8
--- /dev/null
+++ b/test/unit/domain/continuum/ContinuumArtifactIngestionPolicy.test.ts
@@ -0,0 +1,199 @@
+import { describe, expect, it } from 'vitest';
+
+import ContinuumArtifactAuthorityError from '../../../../src/domain/errors/ContinuumArtifactAuthorityError.ts';
+import ContinuumArtifactAuthority from '../../../../src/domain/continuum/ContinuumArtifactAuthority.ts';
+import ContinuumArtifactDescriptor from '../../../../src/domain/continuum/ContinuumArtifactDescriptor.ts';
+import ContinuumArtifactIngestionPolicy from '../../../../src/domain/continuum/ContinuumArtifactIngestionPolicy.ts';
+import ContinuumFamilyId from '../../../../src/domain/continuum/ContinuumFamilyId.ts';
+import WarpError from '../../../../src/domain/errors/WarpError.ts';
+
+const RECEIPT_SCHEMA_PATH = 'schemas/continuum-receipt-family.graphql';
+const CONTINUUM_FIXTURE_KIND = 'continuum.family.fixture';
+const WESLEY_REALIZATION_MANIFEST_KIND = 'wesley.realization.manifest.v1';
+const AUTHORITY_GENERATED_FIXTURE = 'generated-fixture';
+const AUTHORITY_GENERATED_ARTIFACT = 'generated-artifact';
+const AUTHORITY_LOCAL_MIRROR = 'local-mirror';
+const AUTHORITY_HANDWRITTEN_MIRROR = 'handwritten-mirror';
+
+type DescriptorFixtureFields = {
+ readonly artifactKind?: string;
+ readonly authority?: string;
+};
+
+/** Builds a receipt-family descriptor for policy tests. */
+function makeDescriptor(fields: DescriptorFixtureFields = {}): ContinuumArtifactDescriptor {
+ return new ContinuumArtifactDescriptor({
+ familyId: 'receipt-family',
+ sourceSchemaPath: RECEIPT_SCHEMA_PATH,
+ generatedBy: 'wesley witness-continuum --scope receipt-family',
+ artifactKind: fields.artifactKind ?? CONTINUUM_FIXTURE_KIND,
+ authority: fields.authority ?? AUTHORITY_GENERATED_FIXTURE,
+ targets: ['typescript', 'echo'],
+ version: '0.1.0',
+ witnessScope: 'receipt-family',
+ });
+}
+
+describe('ContinuumArtifactIngestionPolicy', () => {
+ it('accepts documented generated fixtures', () => {
+ const descriptor = makeDescriptor({ authority: AUTHORITY_GENERATED_FIXTURE });
+ const policy = new ContinuumArtifactIngestionPolicy();
+
+ expect(policy.ingest(descriptor)).toBe(descriptor);
+ });
+
+ it('accepts generated artifacts', () => {
+ const descriptor = makeDescriptor({
+ artifactKind: WESLEY_REALIZATION_MANIFEST_KIND,
+ authority: AUTHORITY_GENERATED_ARTIFACT,
+ });
+ const policy = new ContinuumArtifactIngestionPolicy();
+
+ expect(policy.ingest(descriptor)).toBe(descriptor);
+ });
+
+ it('rejects generated authority that does not match the descriptor kind', () => {
+ const policy = new ContinuumArtifactIngestionPolicy();
+
+ expect(() => policy.ingest(makeDescriptor({
+ artifactKind: CONTINUUM_FIXTURE_KIND,
+ authority: AUTHORITY_GENERATED_ARTIFACT,
+ }))).toThrow(ContinuumArtifactAuthorityError);
+
+ expect(() => policy.ingest(makeDescriptor({
+ artifactKind: WESLEY_REALIZATION_MANIFEST_KIND,
+ authority: AUTHORITY_GENERATED_FIXTURE,
+ }))).toThrow(ContinuumArtifactAuthorityError);
+ });
+
+ it('rejects generated authority for unknown artifact kinds', () => {
+ const descriptor = makeDescriptor({
+ artifactKind: 'continuum.unknown.fixture',
+ authority: AUTHORITY_GENERATED_FIXTURE,
+ });
+ const policy = new ContinuumArtifactIngestionPolicy();
+
+ expect(() => policy.ingest(descriptor)).toThrow(ContinuumArtifactAuthorityError);
+ });
+
+ it('rejects local mirrors as family authority', () => {
+ const descriptor = makeDescriptor({ authority: AUTHORITY_LOCAL_MIRROR });
+ const policy = new ContinuumArtifactIngestionPolicy();
+
+ expect(() => policy.ingest(descriptor)).toThrow(ContinuumArtifactAuthorityError);
+ });
+
+ it('rejects handwritten mirrors as family authority', () => {
+ const descriptor = makeDescriptor({ authority: AUTHORITY_HANDWRITTEN_MIRROR });
+ const policy = new ContinuumArtifactIngestionPolicy();
+
+ expect(() => policy.ingest(descriptor)).toThrow(ContinuumArtifactAuthorityError);
+ });
+
+ it('keeps family ids runtime-backed', () => {
+ const descriptor = makeDescriptor({ authority: AUTHORITY_GENERATED_FIXTURE });
+
+ expect(descriptor.familyId).toBeInstanceOf(ContinuumFamilyId);
+ expect(descriptor.familyId.toString()).toBe('receipt-family');
+ });
+
+ it('accepts every Continuum family id and compares ids by value', () => {
+ const receipt = new ContinuumFamilyId('receipt-family');
+
+ expect(receipt.equals(new ContinuumFamilyId('receipt-family'))).toBe(true);
+ expect(receipt.equals(new ContinuumFamilyId('settlement-family'))).toBe(false);
+ expect(new ContinuumFamilyId('neighborhood-core-family').toString()).toBe('neighborhood-core-family');
+ expect(new ContinuumFamilyId('runtime-boundary-family').toString()).toBe('runtime-boundary-family');
+ });
+
+ it('rejects unknown family ids and authorities', () => {
+ expect(() => new ContinuumFamilyId('not-a-family')).toThrow(WarpError);
+ expect(() => new ContinuumArtifactAuthority('not-authority')).toThrow(WarpError);
+ });
+
+ it('constructs descriptors from runtime-backed carriers', () => {
+ const descriptor = new ContinuumArtifactDescriptor({
+ familyId: new ContinuumFamilyId('receipt-family'),
+ sourceSchemaPath: RECEIPT_SCHEMA_PATH,
+ generatedBy: 'wesley compile',
+ artifactKind: WESLEY_REALIZATION_MANIFEST_KIND,
+ authority: new ContinuumArtifactAuthority(AUTHORITY_GENERATED_ARTIFACT),
+ targets: ['warp-ttd'],
+ generatedLegs: ['warpTtd'],
+ generatedFiles: ['manifest/schema.json'],
+ });
+
+ expect(descriptor.familyId.toString()).toBe('receipt-family');
+ expect(descriptor.authority.toString()).toBe(AUTHORITY_GENERATED_ARTIFACT);
+ expect(descriptor.generatedLegs).toEqual(['warpTtd']);
+ expect(descriptor.generatedFiles).toEqual(['manifest/schema.json']);
+ });
+
+ it('rejects invalid descriptor fields with WarpError', () => {
+ expect(() => new ContinuumArtifactDescriptor({
+ familyId: 'receipt-family',
+ sourceSchemaPath: '',
+ generatedBy: 'wesley compile',
+ artifactKind: WESLEY_REALIZATION_MANIFEST_KIND,
+ authority: AUTHORITY_GENERATED_ARTIFACT,
+ targets: ['warp-ttd'],
+ })).toThrow(WarpError);
+
+ expect(() => new ContinuumArtifactDescriptor({
+ familyId: 'receipt-family',
+ sourceSchemaPath: RECEIPT_SCHEMA_PATH,
+ generatedBy: 'wesley compile',
+ artifactKind: WESLEY_REALIZATION_MANIFEST_KIND,
+ authority: AUTHORITY_GENERATED_ARTIFACT,
+ targets: [],
+ })).toThrow(WarpError);
+
+ expect(() => new ContinuumArtifactDescriptor({
+ familyId: 'receipt-family',
+ sourceSchemaPath: RECEIPT_SCHEMA_PATH,
+ generatedBy: 'wesley compile',
+ artifactKind: WESLEY_REALIZATION_MANIFEST_KIND,
+ authority: AUTHORITY_GENERATED_ARTIFACT,
+ targets: ['warp-ttd'],
+ generatedLegs: [''],
+ })).toThrow(WarpError);
+ });
+
+ it('rejects wrong runtime types even when JavaScript bypasses TypeScript', () => {
+ expect(() => new ContinuumArtifactDescriptor({
+ familyId: 'receipt-family',
+ // @ts-expect-error runtime guard for JS callers
+ sourceSchemaPath: 7,
+ generatedBy: 'wesley compile',
+ artifactKind: WESLEY_REALIZATION_MANIFEST_KIND,
+ authority: AUTHORITY_GENERATED_ARTIFACT,
+ targets: ['warp-ttd'],
+ })).toThrow(WarpError);
+
+ expect(() => new ContinuumArtifactDescriptor({
+ familyId: 'receipt-family',
+ sourceSchemaPath: RECEIPT_SCHEMA_PATH,
+ generatedBy: 'wesley compile',
+ artifactKind: WESLEY_REALIZATION_MANIFEST_KIND,
+ authority: AUTHORITY_GENERATED_ARTIFACT,
+ // @ts-expect-error runtime guard for JS callers
+ targets: 'warp-ttd',
+ })).toThrow(WarpError);
+
+ expect(() => new ContinuumArtifactDescriptor({
+ familyId: 'receipt-family',
+ sourceSchemaPath: RECEIPT_SCHEMA_PATH,
+ generatedBy: 'wesley compile',
+ artifactKind: WESLEY_REALIZATION_MANIFEST_KIND,
+ authority: AUTHORITY_GENERATED_ARTIFACT,
+ targets: ['warp-ttd'],
+ // @ts-expect-error runtime guard for JS callers
+ generatedFiles: 'manifest/schema.json',
+ })).toThrow(WarpError);
+
+ // @ts-expect-error runtime guard for JS callers
+ expect(() => new ContinuumFamilyId(null)).toThrow(WarpError);
+ // @ts-expect-error runtime guard for JS callers
+ expect(() => new ContinuumArtifactAuthority(null)).toThrow(WarpError);
+ });
+});
diff --git a/test/unit/domain/errors/index.test.ts b/test/unit/domain/errors/index.test.ts
index 30fc4cf7..59c75c57 100644
--- a/test/unit/domain/errors/index.test.ts
+++ b/test/unit/domain/errors/index.test.ts
@@ -6,6 +6,7 @@ describe('domain/errors index barrel', () => {
expect(Object.keys(errors).sort()).toEqual([
'AuditError',
+ 'ContinuumArtifactAuthorityError',
'EncryptionError',
'ForkError',
'IndexError',
diff --git a/test/unit/domain/index.exports.test.ts b/test/unit/domain/index.exports.test.ts
index 9e768ac7..c96d5be5 100644
--- a/test/unit/domain/index.exports.test.ts
+++ b/test/unit/domain/index.exports.test.ts
@@ -40,6 +40,7 @@ import WarpAppDefault, {
TraversalError,
OperationAbortedError,
Observer,
+ ContinuumArtifactAuthorityError,
// Cancellation utilities
checkAborted,
@@ -57,6 +58,11 @@ import WarpAppDefault, {
compareVisibleState,
normalizeVisibleStateScope,
scopeMaterializedState,
+ ContinuumArtifactAuthority,
+ ContinuumArtifactDescriptor,
+ ContinuumArtifactIngestionPolicy,
+ ContinuumFamilyId,
+ ContinuumArtifactJsonFileAdapter,
} from '../../../index.ts';
const { WarpGraph, WarpRuntime, Worldline, ObserverView } = (await import('../../../index.ts') as any);
@@ -244,6 +250,36 @@ describe('index.ts exports', () => {
expect(OperationAbortedError).toBeDefined();
expect(typeof OperationAbortedError).toBe('function');
});
+
+ it('exports ContinuumArtifactAuthorityError', () => {
+ expect(ContinuumArtifactAuthorityError).toBeDefined();
+ expect(typeof ContinuumArtifactAuthorityError).toBe('function');
+ });
+ });
+
+ describe('Continuum compatibility artifacts', () => {
+ it('exports the artifact descriptor classes', () => {
+ expect(ContinuumArtifactAuthority).toBeDefined();
+ expect(ContinuumArtifactDescriptor).toBeDefined();
+ expect(ContinuumArtifactIngestionPolicy).toBeDefined();
+ expect(ContinuumFamilyId).toBeDefined();
+ expect(ContinuumArtifactJsonFileAdapter).toBeDefined();
+ });
+
+ it('constructs a generated receipt-family descriptor from public exports', () => {
+ const descriptor = new ContinuumArtifactDescriptor({
+ familyId: 'receipt-family',
+ version: '0.1.0',
+ sourceSchemaPath: '~/git/continuum/schemas/continuum-receipt-family.graphql',
+ generatedBy: 'wesley witness-continuum --scope receipt-family',
+ artifactKind: 'continuum.family.fixture',
+ authority: 'generated-fixture',
+ targets: ['typescript'],
+ });
+
+ expect(descriptor.familyId).toBeInstanceOf(ContinuumFamilyId);
+ expect(descriptor.hasGeneratedAuthority()).toBe(true);
+ });
});
describe('cancellation utilities', () => {
diff --git a/test/unit/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.test.ts b/test/unit/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.test.ts
new file mode 100644
index 00000000..ffd4ef77
--- /dev/null
+++ b/test/unit/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.test.ts
@@ -0,0 +1,455 @@
+import { describe, expect, it } from 'vitest';
+import { fileURLToPath } from 'node:url';
+
+import ContinuumArtifactJsonFileAdapter, {
+ type ContinuumArtifactJsonLoadContext,
+} from '../../../../src/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.ts';
+import AdapterValidationError from '../../../../src/domain/errors/AdapterValidationError.ts';
+
+const generatedFixturePath = fileURLToPath(
+ new URL('../../../fixtures/continuum/receipt-family-generated-artifact.json', import.meta.url),
+);
+
+const wesleyManifestPath = fileURLToPath(
+ new URL('../../../fixtures/continuum/receipt-family-wesley-realization-manifest.json', import.meta.url),
+);
+
+const fixtureContext: ContinuumArtifactJsonLoadContext = {
+ familyId: 'receipt-family',
+ authority: 'generated-fixture',
+ sourceSchemaPath: '~/git/continuum/schemas/continuum-receipt-family.graphql',
+ witnessScope: 'receipt-family',
+ artifactDigest: 'sha256:receipt-fixture',
+};
+
+const artifactContext: ContinuumArtifactJsonLoadContext = {
+ familyId: 'receipt-family',
+ authority: 'generated-artifact',
+ witnessScope: 'receipt-family',
+};
+
+const localMirrorContext: ContinuumArtifactJsonLoadContext = {
+ familyId: 'receipt-family',
+ authority: 'local-mirror',
+ sourceSchemaPath: '~/git/continuum/schemas/continuum-receipt-family.graphql',
+};
+
+const fixtureAsArtifactContext: ContinuumArtifactJsonLoadContext = {
+ familyId: 'receipt-family',
+ authority: 'generated-artifact',
+ sourceSchemaPath: '~/git/continuum/schemas/continuum-receipt-family.graphql',
+};
+
+const artifactAsFixtureContext: ContinuumArtifactJsonLoadContext = {
+ familyId: 'receipt-family',
+ authority: 'generated-fixture',
+ witnessScope: 'receipt-family',
+};
+
+const selfAttestedFixtureJson = `{
+ "objectTypes": ["Receipt"],
+ "enumTypes": [],
+ "ops": [
+ {
+ "name": "receipts",
+ "resultType": "Receipt"
+ }
+ ],
+ "invariants": [],
+ "footprints": [],
+ "authority": "generated-artifact"
+}`;
+
+const unknownFixtureFieldJson = `{
+ "objectTypes": ["Receipt"],
+ "enumTypes": [],
+ "ops": [
+ {
+ "name": "receipts",
+ "resultType": "Receipt"
+ }
+ ],
+ "invariants": [],
+ "footprints": [],
+ "extra": "drift"
+}`;
+
+const invalidOperationJson = `{
+ "objectTypes": ["Receipt"],
+ "enumTypes": [],
+ "ops": [
+ {
+ "name": 7,
+ "resultType": "Receipt"
+ }
+ ],
+ "invariants": [],
+ "footprints": []
+}`;
+
+const fixtureWithoutFootprintsJson = `{
+ "objectTypes": ["Receipt"],
+ "enumTypes": [],
+ "ops": [
+ {
+ "name": "receipts",
+ "resultType": "Receipt"
+ }
+ ],
+ "invariants": []
+}`;
+
+const emptyOperationsJson = `{
+ "objectTypes": ["Receipt"],
+ "enumTypes": [],
+ "ops": [],
+ "invariants": [],
+ "footprints": []
+}`;
+
+const invalidFootprintsJson = `{
+ "objectTypes": ["Receipt"],
+ "enumTypes": [],
+ "ops": [
+ {
+ "name": "receipts",
+ "resultType": "Receipt"
+ }
+ ],
+ "invariants": [],
+ "footprints": "none"
+}`;
+
+const typeMapFixtureJson = `{
+ "objectTypes": ["Receipt"],
+ "enumTypes": [],
+ "ops": [
+ {
+ "name": "receipts",
+ "resultType": "Receipt"
+ }
+ ],
+ "invariants": [],
+ "footprints": [],
+ "types": {
+ "Receipt": ["receiptId"]
+ }
+}`;
+
+const unsealedWesleyManifestJson = `{
+ "kind": "wesley.realization.manifest.v1",
+ "schemaPath": "schemas/continuum-receipt-family.graphql",
+ "schemaHash": "hash",
+ "sourceHash": "hash",
+ "targets": ["warp-ttd"],
+ "integrity": {
+ "status": "open",
+ "scope": "generated-leg-files",
+ "hashAlgorithm": "sha256",
+ "signatureAlgorithm": "hmac-sha256",
+ "signatureKeyId": "fixture-key"
+ },
+ "generatedLegs": {}
+}`;
+
+const invalidWesleyArtifactCountJson = `{
+ "kind": "wesley.realization.manifest.v1",
+ "schemaPath": "schemas/continuum-receipt-family.graphql",
+ "schemaHash": "hash",
+ "sourceHash": "hash",
+ "targets": ["warp-ttd"],
+ "integrity": {
+ "status": "sealed",
+ "scope": "generated-leg-files",
+ "hashAlgorithm": "sha256",
+ "signatureAlgorithm": "hmac-sha256",
+ "signatureKeyId": "fixture-key"
+ },
+ "generatedLegs": {
+ "warpTtd": {
+ "outDir": "dist/warp-ttd",
+ "schemaHash": "hash",
+ "sourceHash": "hash",
+ "artifactCount": "one"
+ }
+ }
+}`;
+
+const wesleyManifestWithoutGeneratedFilesJson = `{
+ "kind": "wesley.realization.manifest.v1",
+ "schemaPath": "schemas/continuum-receipt-family.graphql",
+ "schemaHash": "hash",
+ "sourceHash": "hash",
+ "targets": ["warp-ttd"],
+ "integrity": {
+ "status": "sealed",
+ "scope": "generated-leg-files",
+ "hashAlgorithm": "sha256",
+ "signatureAlgorithm": "hmac-sha256",
+ "signatureKeyId": "fixture-key"
+ },
+ "generatedLegs": {
+ "warpTtd": {
+ "outDir": "dist/warp-ttd",
+ "schemaHash": "hash",
+ "sourceHash": "hash",
+ "artifactCount": 0
+ }
+ }
+}`;
+
+const invalidWesleyGeneratedFilesJson = `{
+ "kind": "wesley.realization.manifest.v1",
+ "schemaPath": "schemas/continuum-receipt-family.graphql",
+ "schemaHash": "hash",
+ "sourceHash": "hash",
+ "targets": ["warp-ttd"],
+ "integrity": {
+ "status": "sealed",
+ "scope": "generated-leg-files",
+ "hashAlgorithm": "sha256",
+ "signatureAlgorithm": "hmac-sha256",
+ "signatureKeyId": "fixture-key"
+ },
+ "generatedLegs": {
+ "warpTtd": {
+ "outDir": "dist/warp-ttd",
+ "schemaHash": "hash",
+ "sourceHash": "hash",
+ "files": "dist/warp-ttd/types.ts"
+ }
+ }
+}`;
+
+const invalidWesleyTargetsJson = `{
+ "kind": "wesley.realization.manifest.v1",
+ "schemaPath": "schemas/continuum-receipt-family.graphql",
+ "schemaHash": "hash",
+ "sourceHash": "hash",
+ "targets": "warp-ttd",
+ "integrity": {
+ "status": "sealed",
+ "scope": "generated-leg-files",
+ "hashAlgorithm": "sha256",
+ "signatureAlgorithm": "hmac-sha256",
+ "signatureKeyId": "fixture-key"
+ },
+ "generatedLegs": {}
+}`;
+
+const invalidWesleyTargetEntryJson = `{
+ "kind": "wesley.realization.manifest.v1",
+ "schemaPath": "schemas/continuum-receipt-family.graphql",
+ "schemaHash": "hash",
+ "sourceHash": "hash",
+ "targets": [""],
+ "integrity": {
+ "status": "sealed",
+ "scope": "generated-leg-files",
+ "hashAlgorithm": "sha256",
+ "signatureAlgorithm": "hmac-sha256",
+ "signatureKeyId": "fixture-key"
+ },
+ "generatedLegs": {}
+}`;
+
+const emptyWesleyGeneratedLegsJson = `{
+ "kind": "wesley.realization.manifest.v1",
+ "schemaPath": "schemas/continuum-receipt-family.graphql",
+ "schemaHash": "hash",
+ "sourceHash": "hash",
+ "targets": ["warp-ttd"],
+ "integrity": {
+ "status": "sealed",
+ "scope": "generated-leg-files",
+ "hashAlgorithm": "sha256",
+ "signatureAlgorithm": "hmac-sha256",
+ "signatureKeyId": "fixture-key"
+ },
+ "generatedLegs": {}
+}`;
+
+const mismatchedWesleyArtifactCountJson = `{
+ "kind": "wesley.realization.manifest.v1",
+ "schemaPath": "schemas/continuum-receipt-family.graphql",
+ "schemaHash": "hash",
+ "sourceHash": "hash",
+ "targets": ["warp-ttd"],
+ "integrity": {
+ "status": "sealed",
+ "scope": "generated-leg-files",
+ "hashAlgorithm": "sha256",
+ "signatureAlgorithm": "hmac-sha256",
+ "signatureKeyId": "fixture-key"
+ },
+ "generatedLegs": {
+ "warpTtd": {
+ "outDir": "dist/warp-ttd",
+ "schemaHash": "hash",
+ "sourceHash": "hash",
+ "artifactCount": 2,
+ "files": [
+ {
+ "path": "manifest/schema.json",
+ "size": 1,
+ "contentHash": "hash",
+ "signature": "signature"
+ }
+ ]
+ }
+ }
+}`;
+
+const positiveWesleyArtifactCountWithoutFilesJson = `{
+ "kind": "wesley.realization.manifest.v1",
+ "schemaPath": "schemas/continuum-receipt-family.graphql",
+ "schemaHash": "hash",
+ "sourceHash": "hash",
+ "targets": ["warp-ttd"],
+ "integrity": {
+ "status": "sealed",
+ "scope": "generated-leg-files",
+ "hashAlgorithm": "sha256",
+ "signatureAlgorithm": "hmac-sha256",
+ "signatureKeyId": "fixture-key"
+ },
+ "generatedLegs": {
+ "warpTtd": {
+ "outDir": "dist/warp-ttd",
+ "schemaHash": "hash",
+ "sourceHash": "hash",
+ "artifactCount": 1
+ }
+ }
+}`;
+
+describe('ContinuumArtifactJsonFileAdapter', () => {
+ it('loads real Continuum receipt-family fixture descriptors', async () => {
+ const adapter = new ContinuumArtifactJsonFileAdapter();
+ const descriptor = await adapter.loadFile(generatedFixturePath, fixtureContext);
+
+ expect(descriptor.familyId.toString()).toBe('receipt-family');
+ expect(descriptor.hasTarget('continuum-fixture')).toBe(true);
+ expect(descriptor.hasGeneratedAuthority()).toBe(true);
+ expect(descriptor.artifactDigest).toBe('sha256:receipt-fixture');
+ expect(descriptor.witnessScope).toBe('receipt-family');
+ });
+
+ it('loads Wesley realization manifest descriptors without local descriptor fields', async () => {
+ const adapter = new ContinuumArtifactJsonFileAdapter();
+ const descriptor = await adapter.loadFile(wesleyManifestPath, artifactContext);
+
+ expect(descriptor.artifactKind).toBe('wesley.realization.manifest.v1');
+ expect(descriptor.sourceSchemaPath).toBe('schemas/continuum-receipt-family.graphql');
+ expect(descriptor.schemaHash).toBe('16bf631145b60e0ec240f97484ff2cb5f534cd38c963cf12044985915766a602');
+ expect(descriptor.sourceHash).toBe('16bf631145b60e0ec240f97484ff2cb5f534cd38c963cf12044985915766a602');
+ expect(descriptor.integrityStatus).toBe('sealed');
+ expect(descriptor.signatureAlgorithm).toBe('hmac-sha256');
+ expect(descriptor.targets).toEqual(['warp-ttd', 'echo']);
+ expect(descriptor.generatedLegs).toEqual(['echo', 'warpTtd']);
+ expect(descriptor.generatedFiles).toEqual([
+ 'ir.json',
+ 'manifest/schema.json',
+ 'typescript/types.ts',
+ ]);
+ });
+
+ it('rejects local mirrors before they become authority', async () => {
+ const adapter = new ContinuumArtifactJsonFileAdapter();
+
+ await expect(adapter.loadFile(generatedFixturePath, localMirrorContext)).rejects.toThrow(AdapterValidationError);
+ });
+
+ it('requires authority to match the parsed artifact shape', async () => {
+ const adapter = new ContinuumArtifactJsonFileAdapter();
+
+ await expect(adapter.loadFile(wesleyManifestPath, artifactAsFixtureContext)).rejects.toThrow(AdapterValidationError);
+ expect(() => adapter.loadString(typeMapFixtureJson, fixtureAsArtifactContext)).toThrow(AdapterValidationError);
+ });
+
+ it('rejects stale load context artifact kind overrides', () => {
+ const adapter = new ContinuumArtifactJsonFileAdapter();
+ const contextWithArtifactKind = {
+ ...artifactContext,
+ artifactKind: 'continuum.family.fixture',
+ };
+
+ expect(() => adapter.loadString(wesleyManifestWithoutGeneratedFilesJson, contextWithArtifactKind)).toThrow(AdapterValidationError);
+ });
+
+ it('rejects self-attested authority fields inside artifact JSON', () => {
+ const adapter = new ContinuumArtifactJsonFileAdapter();
+
+ expect(() => adapter.loadString(selfAttestedFixtureJson, fixtureContext)).toThrow(AdapterValidationError);
+ });
+
+ it('wraps invalid JSON syntax as adapter validation failure', () => {
+ const adapter = new ContinuumArtifactJsonFileAdapter();
+
+ expect(() => adapter.loadString('{ "objectTypes": [', fixtureContext)).toThrow(AdapterValidationError);
+ });
+
+ it('rejects unsupported top-level JSON shapes', () => {
+ const adapter = new ContinuumArtifactJsonFileAdapter();
+
+ expect(() => adapter.loadString('null', fixtureContext)).toThrow(AdapterValidationError);
+ expect(() => adapter.loadString('[]', fixtureContext)).toThrow(AdapterValidationError);
+ expect(() => adapter.loadString('{ "familyId": "receipt-family" }', fixtureContext)).toThrow(AdapterValidationError);
+ });
+
+ it('rejects unknown fixture keys and malformed nested entries', () => {
+ const adapter = new ContinuumArtifactJsonFileAdapter();
+
+ expect(() => adapter.loadString(unknownFixtureFieldJson, fixtureContext)).toThrow(AdapterValidationError);
+ expect(() => adapter.loadString(invalidOperationJson, fixtureContext)).toThrow(AdapterValidationError);
+ expect(() => adapter.loadString(emptyOperationsJson, fixtureContext)).toThrow(AdapterValidationError);
+ expect(() => adapter.loadString(invalidFootprintsJson, fixtureContext)).toThrow(AdapterValidationError);
+ });
+
+ it('accepts fixture artifacts with omitted optional footprints', () => {
+ const adapter = new ContinuumArtifactJsonFileAdapter();
+
+ expect(adapter.loadString(fixtureWithoutFootprintsJson, fixtureContext).artifactKind).toBe('continuum.family.fixture');
+ });
+
+ it('requires source schema context for fixture artifacts', () => {
+ const adapter = new ContinuumArtifactJsonFileAdapter();
+ const contextWithoutSchemaPath: ContinuumArtifactJsonLoadContext = {
+ familyId: 'receipt-family',
+ authority: 'generated-fixture',
+ };
+
+ expect(() => adapter.loadString(typeMapFixtureJson, contextWithoutSchemaPath)).toThrow(AdapterValidationError);
+ });
+
+ it('rejects unsealed Wesley realization manifests', () => {
+ const adapter = new ContinuumArtifactJsonFileAdapter();
+
+ expect(() => adapter.loadString(unsealedWesleyManifestJson, artifactContext)).toThrow(AdapterValidationError);
+ });
+
+ it('rejects malformed Wesley realization manifest numbers and targets', () => {
+ const adapter = new ContinuumArtifactJsonFileAdapter();
+
+ expect(() => adapter.loadString(invalidWesleyArtifactCountJson, artifactContext)).toThrow(AdapterValidationError);
+ expect(() => adapter.loadString(invalidWesleyGeneratedFilesJson, artifactContext)).toThrow(AdapterValidationError);
+ expect(() => adapter.loadString(invalidWesleyTargetsJson, artifactContext)).toThrow(AdapterValidationError);
+ expect(() => adapter.loadString(invalidWesleyTargetEntryJson, artifactContext)).toThrow(AdapterValidationError);
+ });
+
+ it('rejects empty or inconsistent Wesley generated inventory', () => {
+ const adapter = new ContinuumArtifactJsonFileAdapter();
+
+ expect(() => adapter.loadString(emptyWesleyGeneratedLegsJson, artifactContext)).toThrow(AdapterValidationError);
+ expect(() => adapter.loadString(mismatchedWesleyArtifactCountJson, artifactContext)).toThrow(AdapterValidationError);
+ expect(() => adapter.loadString(positiveWesleyArtifactCountWithoutFilesJson, artifactContext)).toThrow(AdapterValidationError);
+ });
+
+ it('accepts Wesley generated legs before the compiler writes file inventory', () => {
+ const adapter = new ContinuumArtifactJsonFileAdapter();
+ const descriptor = adapter.loadString(wesleyManifestWithoutGeneratedFilesJson, artifactContext);
+
+ expect(descriptor.generatedLegs).toEqual(['warpTtd']);
+ expect(descriptor.generatedFiles).toEqual([]);
+ });
+});