From 14bc1ff97af7de9ce0330a1723f452ec89741fbd Mon Sep 17 00:00:00 2001 From: James Ross Date: Thu, 21 May 2026 09:55:05 -0700 Subject: [PATCH 1/7] docs: set v18 Continuum compatibility bearing --- docs/BEARING.md | 216 ++++++++++++++++++++++-------------------------- 1 file changed, 99 insertions(+), 117 deletions(-) diff --git a/docs/BEARING.md b/docs/BEARING.md index 791daa29..01215f5e 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: @@ -14,62 +14,55 @@ Scope note: ## 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-substrate 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 substrate 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` +must not pretend to be Echo's durable half, and it must not emit +Continuum-shaped values as native Continuum witnesses until that witnesshood is +actually proven. 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: `main` +- 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 + `0144-release-preflight-and-rc`; `0145-push-pr-review-merge` still needs + closeout bookkeeping. +- Local git status note: + `core.fsmonitor=true` currently emits `fsmonitor_ipc__send_query` warnings + during status/diff commands. + +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 the cold Git substrate, + 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 +102,69 @@ 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. +- `0145-push-pr-review-merge` still needs closeout bookkeeping even though the + release branch has already landed and the package is published. +- The local git fsmonitor warning adds noise to status and diff commands. - 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 substrate evidence until stronger proof exists. +- `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 + +- [ ] 1. Sync and clean the repo runway: fast-forward `main`, clear fsmonitor + noise, close stale v17/0145 bookkeeping, and record the v18 starting point. +- [ ] 2. Create the v18 Continuum compatibility charter: WARP Optic + compatibility, Continuum contract-family compatibility, Wesley-generated + artifact consumption, and `warp-ttd` acceptance. +- [ ] 3. Build the cross-repo contract matrix: Continuum family to Wesley + generated artifact to git-warp source fact to `warp-ttd` consumer need. +- [ ] 4. Define git-warp's WARP Optic realization map: observer plan, bounded + slice, lowering surface, admissibility law, and retention contract. +- [ ] 5. Add a generated-artifact ingestion path for Continuum families, with a + guard against handwritten local mirrors becoming contract authority. +- [ ] 6. Make evidence posture explicit: translated substrate 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. From dbe498a45c681ae8d2fc67aa5cf39d5c5a5f6329 Mon Sep 17 00:00:00 2001 From: James Ross Date: Thu, 21 May 2026 10:00:32 -0700 Subject: [PATCH 2/7] docs: close v17 release runway --- docs/BEARING.md | 23 ++++++----- docs/design/0124-v17-release-blocker-dag.dot | 6 ++- docs/design/0124-v17-release-blocker-dag.md | 12 +++--- docs/design/0124-v17-release-blocker-dag.svg | 8 ++-- .../0124-v17-release-blocker-status.csv | 2 +- .../push-pr-review-merge.md | 27 ++++++++++--- .../push-pr-review-merge.md | 38 +++++++++++++++++++ 7 files changed, 88 insertions(+), 28 deletions(-) create mode 100644 docs/method/retro/0145-push-pr-review-merge/push-pr-review-merge.md diff --git a/docs/BEARING.md b/docs/BEARING.md index 01215f5e..910bac19 100644 --- a/docs/BEARING.md +++ b/docs/BEARING.md @@ -12,6 +12,16 @@ 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 should remain a substrate and fact +provider, not an authority issuer or Echo runtime surrogate. Wesley compiles +artifacts and descriptors, Echo registers and admits runtime-local optic +invocations, authority layers issue grants, and applications hide handles and +basis references behind product adapters. git-warp participates by exposing +truthful graph facts and readings when a Continuum-owned family or adapter asks +for them. + ## Where are we `git-warp` has shipped `v17.0.0`. The release work is now behind us in @@ -36,16 +46,12 @@ actually proven. Current branch state at this boundary: -- Branch: `main` +- Branch: `codex/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`; `0145-push-pr-review-merge` still needs - closeout bookkeeping. -- Local git status note: - `core.fsmonitor=true` currently emits `fsmonitor_ipc__send_query` warnings - during status/diff commands. + `0145-push-pr-review-merge` The release ladder is now: @@ -118,9 +124,6 @@ read-model groundwork, sync hardening, release gates, and package publishing. ## What feels wrong -- `0145-push-pr-review-merge` still needs closeout bookkeeping even though the - release branch has already landed and the package is published. -- The local git fsmonitor warning adds noise to status and diff commands. - 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`. @@ -141,7 +144,7 @@ before the final commit for that slice, and mark completed items with `- [x]`. ## Running Task List -- [ ] 1. Sync and clean the repo runway: fast-forward `main`, clear fsmonitor +- [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. - [ ] 2. Create the v18 Continuum compatibility charter: WARP Optic compatibility, Continuum contract-family compatibility, Wesley-generated 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/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. From b9b6a326722990b7c4e987867c4c87d751a128a2 Mon Sep 17 00:00:00 2001 From: James Ross Date: Thu, 21 May 2026 10:14:53 -0700 Subject: [PATCH 3/7] docs: align v18 continuum doctrine --- docs/BEARING.md | 53 +++--- docs/VISION.md | 18 ++- .../v18-continuum-compatibility-charter.md | 152 ++++++++++++++++++ docs/design/continuum-categories.pdf | Bin 177421 -> 177741 bytes docs/design/continuum-categories.tex | 10 +- docs/method/backlog/README.md | 11 +- docs/method/backlog/WORKLOADS.md | 12 +- .../method/backlog/bad-code/RELEASE_TRIAGE.md | 12 +- .../v18.0.0/PROTO_echo-shaped-edge-records.md | 10 +- .../v18.0.0/PROTO_echo-shaped-node-records.md | 8 +- docs/method/backlog/v18.0.0/README.md | 23 +-- 11 files changed, 248 insertions(+), 61 deletions(-) create mode 100644 docs/design/0146-v18-continuum-compatibility-charter/v18-continuum-compatibility-charter.md diff --git a/docs/BEARING.md b/docs/BEARING.md index 910bac19..af6f94fb 100644 --- a/docs/BEARING.md +++ b/docs/BEARING.md @@ -14,25 +14,25 @@ Scope note: ## Continuum Optic Admission Posture -For cross-repo optic admission, git-warp should remain a substrate and fact -provider, not an authority issuer or Echo runtime surrogate. Wesley compiles -artifacts and descriptors, Echo registers and admits runtime-local optic -invocations, authority layers issue grants, and applications hide handles and -basis references behind product adapters. git-warp participates by exposing -truthful graph facts and readings when a Continuum-owned family or adapter asks -for them. +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` 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-substrate cleanup. It is Continuum +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 substrate into honest WARP Optic evidence, and +> 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. @@ -40,9 +40,19 @@ 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` -must not pretend to be Echo's durable half, and it must not emit -Continuum-shaped values as native Continuum witnesses until that witnesshood is -actually proven. +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: @@ -58,9 +68,9 @@ 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 the cold Git substrate, - through Wesley-generated contract-family artifacts and honest evidence - posture. +- `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 @@ -133,7 +143,10 @@ read-model groundwork, sync hardening, release gates, and package publishing. - 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 substrate evidence until stronger proof exists. + 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. @@ -146,16 +159,18 @@ before the final commit for that slice, and mark completed items with `- [x]`. - [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. -- [ ] 2. Create the v18 Continuum compatibility charter: WARP Optic +- [x] 2. Create the v18 Continuum compatibility charter: WARP Optic compatibility, Continuum contract-family compatibility, Wesley-generated artifact consumption, and `warp-ttd` acceptance. - [ ] 3. Build the cross-repo contract matrix: Continuum family to Wesley - generated artifact to git-warp source fact to `warp-ttd` consumer need. + 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. - [ ] 4. Define git-warp's WARP Optic realization map: observer plan, bounded slice, lowering surface, admissibility law, and retention contract. - [ ] 5. Add a generated-artifact ingestion path for Continuum families, with a guard against handwritten local mirrors becoming contract authority. -- [ ] 6. Make evidence posture explicit: translated substrate evidence first, +- [ ] 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. 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/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..63ac2c1f --- /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: + +- [ ] `BEARING.md` tracks the running v18 task list. +- [ ] A cross-repo contract matrix names each family, generated artifact, local + source fact, consumer, and missing witness. +- [ ] A WARP Optic realization map exists for `git-warp`. +- [ ] The repo can ingest at least one generated Continuum family artifact or + generated fixture. +- [ ] 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/continuum-categories.pdf b/docs/design/continuum-categories.pdf index a218496faccd358cf1d364b260bbe362c0c674b8..82c7fc818dc648843e97b50443de3436dfb3e121 100644 GIT binary patch delta 17777 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

s;5+-g}e`j zkXwuSCVd&=b|v<`qS5bD>#p$z#9n`HB=BF#g;_a=j`3eD#k^8_y&nbM`?cmWk#&5r z*8$Z5nwDO86TR^%Kg47$$RPRTS365wtVp4X38xeo) z*%qj>J3Aw(?=dR-n_}4yAj#^7Los_M{^5wTV?(*Gd_xkigbe*=^2~68n{DV)k`Sufs7j z5h?7pfk=7iq|(9d_qp>ws>!eDc5bVh#P?QkkVAMvkw|!*nhbFqNu*IldlRyd#5`y14F3u@ za&Vip5!~Kzv|ghyw=M!tQGb+U5xyrisKduvJf6R|;AsA!bLZV4Y|QP_NmDxp8UP)> zuR1rh1p=TyHZI-5%*xkN>}w*v`v2y*sJmK}6Sa@G0C;k$2N6OWeHEaC@;*v?+*kfdcAnv@|99OST9}!Y(_iv8s z+0UwJasSq-Jey$RoSDno;^cs|#Q6*n{vjaBpBa|ZSJ5+&PoRg~AS~1M4=OMk(t&&s z9zQ|&LW?-WEC(%gB0Sx|YbW?94B}IBZJsrv>SYUKE?diZuo^kcDVtIulGq4A+}qDi z-yj7ODmJ;fZ==si7^!2Ttt)gpWIT~4%|GfSK7kF;T{LCo!R0vV!!c|ex=1)&z#)<) zgCsPj4nykw0Yj49*xQhRQ4Pegz>W~Zh*cumos|pg0^on*ikEqT8MP70!FugJ9#1uO zdHn&Yw=DNuM7+|L(I*&up*tlL7H5Cj+-hpC4VV!B`N4LQd0k8jdIKiga(e6N`q4PB|}SN zD1c&t)}{YNp&Y{`#gaFO)d>`yqnb;g>X}q7b%Zk#?G(h4e`0ouNPvyrnIBA|sWZ~P zXG4e1n}C)}uf`RKrsq=uALd>8l%8;|D>jVi$&f7~LunJ+wkKw2k_n9U)<8w17A;Qw zarbSdWhRpD6Lpp)w*^}D@I-zlg8NPAOg=j33*Zw$LV)f+&ei-%(pNs-6rEiL*i~+I z)t1o{Q6>c=vvf9?|1~q7F@ujUcaFq~qqd?G9!{5dJq?G(=q+B%Kn}cx7&#&13H;d;T3Eh_KnR*apVKriwDx8&g(i zB06<%x@yDj$O1nGe&>`_4wDW+f!r`;$nd*>Ycb?jmoXCdnU1L(GgxAIb9o@22mUI@ zfo%(?FpVapOkJu};ai+gAx!(jD?}8d&y)?-7Wc0}OEmBG@F36@3w%ex^as&LK-tpU z>-oK_3$afB5oQ*LyUj!C*3YxgT?W~=YOr4dQNXvKau~?1I;m5EygwJLy~YX@ffczf z-2WLayMro&YWbVHic|pQl=6M&1zCtv8s9#$Pxluvn-SHXUfev)-vAiq(kA|m4kOjZ zO9if^0K-Ohk-V$fy`T4vegogtnYY($hKNs(0i znO{p&0|!joXfv!p3mhn(iP%_ES}{gxMV=p(ARF{CI?~K3XI_vrmKBXCgwD(0A8QF@ zEaTW?XjIo7Bfd0CQs)E& z;yAZGb}pO59eT|fvw;>0jhVue%!QTKmT5k*ks^dn4EM+= zJolq;#L6XpHq&sz#ScMjIich3m&-@5GEkPg8V*8oC{7FoqVlrXzgugwvV0Zj&S0BWAn%-}U{~VtHc8PDG(jFNh zcR)P^tbcKaa`PG0eE+zSyn}5MBVhsensku~K~JASB;21ss*epmm(Gw@8t_kC zkuDrwE4gsUXM7#TbX!KtwYWL1JYH(|n-Gr(%1GQY_2%xi|Z8HPW5PP z`E(dxMt!|suF6N3+UC(+W)r;M=f```iW0QG)se{j3ZLKz25|U*!JSw$iZhzAQ*tNh z$e^!__EsliyGtv*m*K?1y;UiJm|!}DQ8=pl-jDY;(?1{%;2$eV{)kin&fotV=uK{6C!9$Z1C)8x{d{o{m4m^p#IqV` z%aB2l&g@)(U#`x*eZa?SZ>_&n0}#|_SKp4Qth(VDBoGt;NcARzd2{iJ&%9RwzYqSY zF&Utqr0`sq*$nnO%Pjap=|YPy@Ztnch^o@+r)@L&JB;h?+zJ!&rPM}7ptP&9qCp_6 z>J3i`>1oI~K492c_vnnZfaHrJ4ZAbTBf-IB5REVE0L}ODN{1wiT9@j;Op}3mVlDa* z@ay7AT$#jF6T_+%i@s`ui%N-z&%M*;Is5qR{J#EqD691PGg|+=hhtF(h!y{Lqvd8d zq{s{|6}8%lTSqar1o}fjO;4}&1!-p9*qGw!pm6naFce$2VBWPWk1<_1=6uTx;2N{5 z`XNvhG~5!j09h;+K&>{56K|w8KJL#+rNEMoau;MI#VYXX?7Xl!q2||cLO<=O&3#9a z+X_Clp3K2qGRJZ)Cwr^{+%YLjr8Y*2wao47&jL4Z;8<2+n4a&7_@KvHc)q9uPsM_(8aMm=( zx3$k48_F*9T7T|!ub#ZFjQL|0UbQuvLb!SE8?P0spsP5^WQxrRZ0}DDHU`o>vll?p z*P|E|jhd54Jy`i@(t*F*_2z52jZhaqV@aLfu-qzFzIo>&=}GI}VMgZm$v&xuAGeRc z{nb_$ML_Svoil6xTTxe9k8(GE{!NFpWr!FFGiC5hlh%M@&KV6eixMg|cxSOLdE0b& zqkLsF09wWxemVOPd%Fxm%-xK6 zal|;$+raNLtb86l37rvOcja23qTsiPETf9N`S?mD>fN3>GhJ(}Q8$_yyk0+t>f2Bz z&6<(S9{#EV2%0Rv^|=~rVWbK6_#awGTG*{QGGt|O82MHK@z(=GswR_VW*af2sh*t8 zC%tT?z`I4~H|iF?TQt3R8m+4{eBW)p6q^v|meut*YvB&C9ZoI;zEM!$huh5?l1$Gm zRcvdx(!;foc;luIA>~cR1@ffK!~ElHKIr;%>9N1vKDVZ<@ znFi+fY!DP8Kbo34LFUVWkf=-&9O*JwglUG8(u`CfOr@)d@lXTvBB&{^SN>MiF z+Uc)n{yXjM!13f}Pn6F3iLP_-Av6|9f-5h&`u9f8D)#=v*d@t9NP3)UpJ z9|U4G+M)-Nm4uExHOZmjr)FGv=d{I6pPLY&fY!~`P>?xBm6}?eK~MePGB_DmXCoM3 zd6LpMy4s7UFId6;-2=5&l?iN6!ymg>5(jU3f!x*>tV&eZ8t#l#_M&vKt1nO`ccuLB zpO~;+b)c*@-J<}!u`Z|6I5-K9b6zFiqaf~MRDT5@M)g!}6Y`nR=u>_jJ%c-`ycw;{ zPpqYzc~|D-V{~rHh{p=SudCWU{~hb$7t zELK(wo)*CenOa5_`$p;or!tcrE6k1_0vnEe7wAGMXo7_zChcYDowk5Vq}K?<%p)5~ z%N>NMZ2M%OPqjst4FZ!K`O)>~T*XXeCtbHGhB=eTTXipF6!LFcSWDv17$t#pPN_vy zD91We?bD4Na-RSG$d8G^<#u_sM^qpd-Ezpgq$Y?k;5k|alA>E5Qgx_Q-@MnD0OiEVpAD|Ht-oI_KVraCr1S_l5!vx+ zQq)h^OxDtxq5d<~-oL^lau}V7C<}6@O*5dBD32s@MVEfY9VPKx4?wlE+`;}v=Ak+q ztP}awsfD_L05$ZD3NFFFh&hDzE!Jqic+O|+kzQ0mVF8kBpc*6MG8;2&&z)>v^PuKF zH8}6ED&x;9lx6Kh7lJ?v>c^wA9o&G z;+))@OrzK6l1m@_E067f@pJ2Vv;g5Es;R7i*=?;ZMn45}yD(~}9Jp(s`H2XjG`_Aj zRa5Jxp>t4hSs^U*xwvnnL;oS8hT^-6cag%bwq9zp^E&Jq2PYC$MY|eP5h`r5w6wa( zE(yPoF*zz%_XN~R%P8y%j`)(Q9trv>gQZlb3YKl6H#lg`lT~A^>e)76*}z1{%yALV z8S-m1C^~e0TWx&?Sdf2eOndWy=~b0f#BRhG`FV61P0r z6CV3*A=}Q)16jnDl)bxFs#w{4rydX9lZ0#zNeK;{ZfA) zvYwodaS{*Lkz83jWqW6eyQNHK{7edQgGvH3xL^WLt>|a2rIKZ4>R{@%@}Ri9w(i+F zk<#Z+BPX-f$_nrUBJgOLaCTEqTdXsqu70=F^r=xXsiB%s>C}<BLAii9}4slF$Uxa<68Yw^d&9aZR%4f*llyD{l??KBK#T@7HjQ(NSojN}S4SgTuvK zTPzbO(80QIr+oDOcT_th{f#KbDVJWON>Hp+A6L4_Xa=3~l6~X4DT+s6shR_6!pXdX zchz*5Ed`JyhMV#6tcJk*`Y+s9#lB!n-b}6SRp!3oAzJJNc6c19zd0BVKGN9LO{L}N z`5*DKvn$EbruXW(0DAcLqoU!Ph0WbjK3Il5NXL<@MppD?;3fJ5d_; ztiGiUu~<(&9I+(k4pljvRjV8Gyqr0#4H9rNQB!)!9k!eij5-SKt2pvoJnw_<%Q}%5 z|8(BKQ7bXqFhw5b1e-Z$$y)F3>p|q58+9_XuL$ya$-i zKw`_|8|J;B?D4C(gY2l}pIrH!NcIyp4kgXA3hs3DbdwWZU>`qZ?dSVo55UonbGqxO&Kg2~y(xS-d?z);Dgz|p+++_OHal$4dz+oWCS#xsn=pOJRk5dG# zj*udW8~T)^v}dO6)lT3Kd+5-Sa7lOY6?zxpswE?sq1Q^c zgiU(Us@8bP=cmk4Wuw0`8LA{Pe9PEZHkqOX&)n-jaApt_fiGms;_{+ck@jaNsV6$m z`)WD|NH}-+bsNxiEwAI1_!S`SOq*%0Xpr#OP^#U2XXpmUQV(ue?5ROH)Q@l`Rie-@#-6>j8`x>5ijCute&E}zedx|N6{!zU2!N_q4SA&u^rU-&`3Bbhwm zw%y2Ae^6Z&^yS|rLw#A$l~vJ^1)E)x*P~~W!R4y_f$z;dEd!)z0Yl>*29S}TV2fLL z)L6s!XIq7KBVx7JHGUvba$`n&MG{qR`it`U_7=@}g#h;lyuA0@_x=7?5Qr-%y5Mo{ zh?usma=tS)huqKIO`D0cKeHm$lF0hW`s@{l7WpAcDOr}?V_q1L&*$}AWwBdJd8BbK z5GSCMZmsSZ!5&j+lwSRqUq=68q7IkZvd1}yEN1cZ1+wF>01y~-Oe(P^tNOcg$YAf| zgspb5NK!fik+m}eFD;o;um}R6SsYe%OU+Sy-|E`8m%|tS*Ym5NTSmI_l`99)(-hIP z!P0lwJf(7CCMDHLHrRl4{P0lVW(R{M?e5oaZT|E8*G1G3VqQ2ZK$5QDjs8CsV4qZg zQII@*)AMmi_bqU0l>ZCz6^y-u(g2=%gfi3rz|{|YSd)?ryy}kR(Nd^B6VlO>sS{T{ zz}tlpBPmojnY?_E?m`dbeZNJb|1#*N87?kSvNWcSaOxS_Ny?^YkaxnvcLEmW-!ZdO z2^Gw_N|!xPKdgX>=PH-iWObV!{2Zbq#zmW@SlAYm-9A2U#f!QVLdQ<8aeDb-DZb5nB8HlCM8 zsp&?!W;jhuX1TZLVO)9;{$s2KYcz{a_y;nnxVN=G!EsM4Ej<`GwHCxDvHW4OY+uG{ z`fyqm5GM)&B}#ShaR7UeAJ3ymKaB1msd1+TqX!{S5<@?eq3wUyN)gnR9ry zplBtVi8Xd^)=ZkHjk=wlA1~hFXFC+&_}Hs1ZS8=?L40Dlm`3VeDTTJD6_c7 zXON~E0M8-L;Vp(+YQIed*VLT9+oyad2C+yE(98HQ{y;TK`t`k90x%dY?638~$l)2O)^wOghrj-aTZMQ0g zjc4I@1|?qRvm$oKfPo#&mr#so z?RVlm^4ufkAfuzzNmJAry)B<8h;{Va0s|eLwv{SRvaqmP1GUjl0`ryfP6C^X)Ye`Y z8e{v~5@>#*uZ_(x9}JkNy{E}f7$U3NOSG{&*26GLsxTZug{%n}7x>Kt}q z;A4_3WU2CR3`8YQ*a&i2*M)Lp+ZG%$+7hf60nX{_hE^ec)qGzxPRwsn0`bofM7Cbo zblkdlOiS>gc#lcAYYzL*_Q7oJbYRxG+g*`uJ`jpJl(tv6;E9bdeAc@Op)QTba|*M= z-t0{&Yi@Y@A}hixx}kEg#Fm&^-NM&Cmw&U9D=~1|=-Wb?^4CPsIq)i}BPUNqMC#lg z0C{_JLfkVs(H!T3A%os-y!1IPmICp=z~!!LK^gu?wYLd1`?#hG`9$aQ?nJ-z^zmhQ zMVSsT!X?BHwcyiFH53&;P?(GOOW?_nwlSC=R&+PtX>B49WB$TTbtKrrU&?UXyJI3) ze#*A;U`H{yZWB_K!E1zpO2+Avw>tPE2ShA>M}^F@nw2pcL^s+bTz`D&KQ=mD>Mu!c zK!btLa@R)>u!L<&oJv8`McZ|F&o9k{+qaHVyp%D5ZkbCP@AIbkcGnZ>IGm7a*9Xs> zA_KWWgE35>mO1VMmwxyIe&mSvdCr@u(w>56*YWBS%T*f3HvEt(h<+W1MbBWX3*6GA zp^$8xPg~eOVTjwS;*Wi2i@Dtr;&1ex5E2e&ij@FP`CjQZ3Qz~;CQfPXn9|}2U%99J zj;(CW>}nFw>Up;0X;V8p*L%Q-YgOk~m{~f72!;*`rc@4>$cf8s5Q}2&%~xI2eCK6K zks#pO5V7~6{j+3VRHMCe2w%N8!P0P4|_c-$iIoi2prBJvg!rM0_KMAvFF zN5?m~8rQ0-tg-mFzoH2ZL4NevvzUZl$x+rSpCWvKxBnbwC_OsNWv<5z6o}rU&J0t$ zEYE{$@`%}~rx@0=`VgiFvX&MzX^2YhDbyk^?TC_!vt9_5G&SO(P4sVvtV2vSIB zp&KV0@Q21I{D6oH6^TW~b&{ zj`jSD{`c3Jt7T@@Yk#}t@>dq%8WJf2t9PWKZ@;*WYUMG^;cI?=Qv`qNaj(ZoV{M;{ zAF8n`F&cJH#UBY@7xB<&2Xx}3yyls6>Dl-+YG0`hC#wz$>0Bv&5{NZ$9?(s`0oeq1 zumOkgb$s?0^#B(>K15p-vl+YzAxwI8{fkNx@NiLQLw$>HSxc7r%t9u~V@-l@)3UaZ zs1*Vf^xD)HBBR*kiz57edFKcYeK*L$pO3U3dMAs+PAm=~+IN{%7WZcg8D1oBfmH|f zq{y;UCZjVwc&F1-6}URG^}u-doLvlIjQJ~Dgik;hB>cEWD&+v4fGS$@fW#C2VNnuC znX@Ze;4D@V%hLN@pWur#U=C z4~XN}@RT;UlP|LSIMs3c_!acZ*i+u_$1eCM$YaMfea}uFlaEsg1BLU|c+M7!aE_A` ze$>haS3h6FNf*C_xCu&r81MHG3oUiD9CfsySrm~!kg(|F;&!lVM7SmTF~S;(n>5y7 zaMR4vfC%U%zz;pti&`ZE-m-OT2uzCmn|ZK~$6bohC2PdL^PwAv-#Gy^%g?Wk7;Z$1 zv2Q$*CRdW0)>6iUx`ytc&|4-~`8oT7?WdJPJI*fjU@a_*7QuXWCYd2n`$uXM7W27$ z_3cnQ)i$$Pd-l2DvYsb{7n#%*LgL@YX5I!)@SfpB*wj*D$;sz5bdywd>z!P7eE+^~ zCVm1xLva81uLiKPr0vgxqBSYdt@D5}v$FjUh1!(vYAl$h_ESL%KZsK*T}TBT=BThU~&1-9bfy%*$Vt6{0}(K@(gOZ6)TUXe?|VL{XIH_OGGKTB8|M zfpn2XBd=aD@mUk`z5wwc9l%5|gkJ{<*96S?N11B>(ku+g(@cW~vdVgr3>*lO!Qw&? z*$W|CdS7AIWC?-dlGe=C*Okiq2k^0-WTz+%VR(|G@H}lD{Y(xhgM)2k3!< zl2!#~Q$Gv~YJ=rdp}LZ|?%7?u4#uz^tN_?cJ6EktGR+e0rd`?#F z5g!)1y5&Dc+?VLeJI=Iz7;3KEy9>>BRP+dU{;O*|MrKa=H6b6;tEqqSpMI@{`|+>5 z7oHB#_Ti;Y-Aq0k_r2MfH=Xj$g67-NT@p?Hsh-e)?#6Nha?*8fqJN3?+3jS7d49Pj zn*_P>z6NB*@hyXV5uNcJ`~%bHzQslW(0P3aZFPM+$W7ZS09_T!hJ-`(3UXhyznMp` zKB@lZJ`I5>-+rWw>9F2NzgROa?V10*TYPgRokaY)1JU4!Enxiplb(l+ir7AtDH4}JV@!K7{dCrxsM}9C> z=(`xQk~9+`S)a>`Hr@S|_agkmCCSfzi}e0l{HNvg^Q~rf1t<`-PAKh@fDK0Ddhu`=k`7+%~3WKdN7U z$+YUr+IPz49hIpXiCM^}Q@xuY8b>(0pSRDD<5>hpC1qzYjo%nbK>J~>=o z?h-Kp?nN9}1r9^N7=Dpu{ECrC;TAG0vQ}JCNc7zH5a_6AiH9FK0>`F#3Y}c0X1T@1 zrQ}$icigld0_$N9F6wp@ zg*14qr)0|^Nh+0UH z^A0*ND|*H1bo-?{P`G)B6*R;h?f4>m+H4OpI=m z)7XZb^(ya~TZ{Dp|4tVt_#3OnsadVOj8lx|18^ij|#V`C_a~&M4n73ZdAO_`Ty)|r8mWtQ}k)N@KTf! ziWBB7-H)VRRyx<_C}o;&&FpiOJe;K#G(ljCdgnRWMdgYTIG4?~hIFQ&3oC&QdzcLj zh5jjQac35wb&bxjZ9OextHG?ch2WO3S{oNZc*}m_yA|0C6I=Ceif1>m1D#RAV5D zI#eXYGU$4Dq6PTV$N1TW+l7T+K?8*Fw8{Ham zNfH1ak$i2}BGy`H{`fdA2^=W51X**EL!rD5zVm&@HS34^095Dc%PAm2J?TG?_57=N z>JGVxwboqOvdS|Sq(0hOdGhp{r*cdBv(MbHU&k7(xl zDjr@EP5O`8L^4fmD6T-0^?w56;W&=sN>s;gMPQcMI+Vv6Ul%O}r%X@T2__O@%qHw_ zVTDvNcZku2l_kS}#xF_s6${}d0U|SS3Gz<09Nm&GrxM(S(1==;GG*x|x3VJ=&~k&@ z&bnqMs5*g2j@e(lvn0}_I*nFPs?~oC7LxC%PKn~MPr^;4B$I$Swq#O{YP4=_sm|D+ zpKh{9K7JsD{PwN z9#Xb>OSw9Pu$aTxk#3TQ-;~DM+ zqJn53n%lxJIL9`CvNC7>!vIYp?n5|>Sf6&qJ+PI^c) zn{x5OX-vS-ruKw$zgp2)M_Ai=j%9*{ncdtb-OSZh-;@BU7MyS|45|hzTL%Qyx0X4FTDpr4%$pym6y!hcskx%@Nv?;(8etrnJIv+=+O`&F# z9dNPg=sN1&H?_Ii8q8Yd&aG+OAgxd4H%190*3HN^3#+dxSVNABHo5KA*`IAjX_R&oBgZ~iT7PDNlIOsfTSHb2RaQ5@6IM3Vv+6RU9eohA^uR`#pISNbKezVt+j z|D@I;@N@FR@^R@>`L{-);XUif!g9p=D0t6>9xUYk3V75K$7}kl|BW1>}SbUhXFLFT^&7wna5kwj@TwYnW)*0tET=?O#j5014SQRNX{@?( z)jR9RYgb-48Jf3nQl>J^xP#AxiMGKc3MtG2&5x#hun${8yyeG#dFEF5u#G$*{>5V+uW1goTj>$UV{1d&Z$CM zeaDrvC8r=-MNFMwf^?q#v^17XlMc+Co4v~-(FNW93$=Gj$1P>?nHKMur2TCSkM{Oy(ZHp*W#a3R1R5P8e_ zPf?=?+?6?e55A3+9{gEM*Fu%TRgx-fDNP7gL`^J2f z@>%A!mR%!W*IhrphDS6WfHLgv&Eb2BcUrO~_r?P5$Z0)<2 z|NQIKYjMBUd#&iQyv4}dbnWWZb=B+oc3$_h5oj^!ozBdmCyHa~{9fP=S<2E-_;!Av zBP~HBGKHoV13sjGV;7rrLRA1MqnEMg-Wz;ZzL&nczDmZf(c`^U>b()4(#1Xs0`CAs z`hFcf;HtQE_89(7U}s;F-|K#Mea*lYplj7Jmg>jv{M_E}^XJ!D(2G~7Z3P{FY2bWS zbrTvaSf%<({v@pO);*%2l}Jdr((l5yC(!T8kNR(8#{>g)(a^Sr&~IgHHlLTztD|W+ zi?_!n5u8CpXMZVN%B|6@Q}#Hj+Qw8N!lj2XH$PWznAx)xXU?&I5AR+(40T;m|0qK3 zFqytDoM46wbZ5sp$6{`NpfYIba{f0tOyjNuz^7f!gTgdHKWF17UCg7kwWENFswO=? zp|>HMgN{O13Ur#8{z?=r*Xyt8rxc<0Jea% zyg&e2T7EJ>yv;WjumMQh2?n6GStkI9!O~8{{)7LC{)2as|7qDI0QlNa(g8<+G_qI# zYT8aNprdUt3jhO_rkU{H0{_v2U}-VQ|5-H`0%*bhX9s9)k0pR#U;s|8G|&nFkdBRs zi-n1eiS0K%69**|6D2h)qny2osF8~)5tS%63lj_bf71V3C9S#w5cl8lcq;*T*sQRO z;+9U%E<|i>|6gj&!t|Rd&GCP7>rJ~uP88o)jk|^*p;Y$-03;Y^M2`IFNo&$I8{}6$ zXJn0@j?%B5X?yd zJ76HBtfWEm=pRb*5`i+fqGHY7s4OTL8oATmX&#u*t1~JIul&Ik-PX*aMH2Zrk^L^X zwFe6qeoh?0H)l_8@0_6lL=-)WSXffh5!Cw!RyYA`5mpKK5vc(r(4##k%n|ZYWOmW; zkv|$k#=?rf-wfmNDm^m{Q>#AmWXPidb^s8ng;DzxgC_f|7>kk}m}DA1Gx)1#=+-fi zLjuCz5qyXWJTU)!udYvj#GJjOleZ6&Uq!m}pMPHZ(rIpQr@15PpE`P z^lZQ;#J7Ll9euAJFf0m0;ZA~ggw-cZU>`)_fGd}>iY&D6($zoDA$+K=%P=JGp7m4o z?vZ|Yc7kC9u6uYYFUu6`5c&t|2ea-DXN)uU0O?6)?-tTNkS$tc-&uXD1d?zk*fN=S zzwYglVPbGF^?-gS^s+qN2luOgQ>|X_;FtLn+b_o#z5$+3AUtqP;7;Ka?gw_U7@OEn zGT5#xEiKL5Gg#9-*rWG=>S0>=8y_^W2b4R6C><#s9-V_54EyX`_TK8TcvV~z?P4EuS7$eEl3WgBXe z#DD%A{K29Fc#ZatUP(>gURCbbsvOHVu}SUhPTY(DXvTUqV^lwvD){b1g4$P-_=?1NF3e+p&bxtkAD!5-&Y?gVpfcjW`#OV|2%yAeImbY={QKjXa%DL1Q$mJ^RNM{OCzwH@}Jn}GhNVRPSNylP-tER}&zg~7mtz(xE0o-|I z=!h$z@Bl#SD?DqyB&T})pk(E6lD}7wT1dpd z*oizjiOSO5K_gOyi``*dK^_pGj+SZ}i0WEO#R!SyH~g3O{j)0hq`*J3sFf~87_mW;$BJBe6X%aL%Bw*ph;=EPRFEYG2;+qH~%vijCW zLsE7@2khk!pXF8{Cj6l-hHgi_}y#$hptLM?S!1FuF$O zqHATure160pF>)x=t*+(y+SJqv?fLUxM&tCvq^3oM5UHA%9BIkRG zSZbXiQ}blfwulT9$9^1+>|$K?SG})a_;DF3V$G*R&luvXjX>dbStV|N+}ddvaNSQb zEKggzS7DVn^^<36AM=NZmc_rg! zF=KVCP(*m!%PtxV9~VZJi|U8;!O^2CLEXWfUAu8yr>* zo&5qxec5C5K&}FIkU4fU^BTX8@f<_3RcECF+^otQ-iQ(gdBc3k_~&>lz?oiKeXmY! z3KsRBEn@%wwR8GARr+rm{1v-$*-$)RxbO0Q)_Zk11)sj@u&uamCcR`$u3127{q3AX zj+wXf7CA+qKH&JYCu8D`0~1qEoGN;>hxhT6{H={`{%f}xTojvjP)9Lp)gI5q6>_;@ zJ8v)gx9MZ3u-UJziC6g{+QQ{J8@BIwe`CSyg5L*Kj$XgQ z79`z#cYEu$IyNWITVHawwCFmp@c7eXc~M&5E$Ajhi^uFI;fs z`|S@)U+uoj*YeR|zTC-QOa8L2($HA|8ML=Fm>#p4NvYm2mP+U zR4`Tm2?piomnaxofOZcDap`;JrR4*8mQbFHLbQ#uo3WdrfvbsyxvQ&@nUSlBxr?c} zvAKzhsiUcjtD%LR0%0YwJn)$S6HDMU!aOF;dfCe-y{86B9RK*-vg+#XlGAHUX6FFg zy`OJAxscQB8#jAv&VdD?-DQs598C+@#T_p$Y899zt@w-o7TZOu6|sMr9d%o*?%#iQ z_`S{dea~&4&-rd@{CoPDNjv|(F}^g*Cr8mz<%UD9@k*{$jfpu*q6^rz1>W{p)oS%( z!7-MV9G@4|`{deY+UYd>y%OcYohT@?a9dwU)6s&aTTz#UR%{sWtQGemFtH7bt?>X8tEU&OF7reFf zTvs!%6xZ5q+_&y<-QN;mZNK63g1Z5eqZ6&2=N{y{!MXRs%dq-ii_^=U#Y;Y?%N~)N z)K{$krkVKyv--mGYcu_XzZ`b7U}=A-A=CWWfICdr$3KY=+1f_tC-(#U+VFB^5=fX Date: Thu, 21 May 2026 10:21:00 -0700 Subject: [PATCH 4/7] docs: add v18 continuum contract matrix --- docs/BEARING.md | 2 +- .../v18-continuum-compatibility-charter.md | 2 +- .../v18-continuum-contract-matrix.md | 110 ++++++++++++++++++ 3 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 docs/design/0147-v18-continuum-contract-matrix/v18-continuum-contract-matrix.md diff --git a/docs/BEARING.md b/docs/BEARING.md index af6f94fb..4fc2fe14 100644 --- a/docs/BEARING.md +++ b/docs/BEARING.md @@ -162,7 +162,7 @@ before the final commit for that slice, and mark completed items with `- [x]`. - [x] 2. Create the v18 Continuum compatibility charter: WARP Optic compatibility, Continuum contract-family compatibility, Wesley-generated artifact consumption, and `warp-ttd` acceptance. -- [ ] 3. Build the cross-repo contract matrix: Continuum family to Wesley +- [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. 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 index 63ac2c1f..b47d8810 100644 --- 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 @@ -123,7 +123,7 @@ contract and not merely mapped from local git-warp facts. The v18 opening campaign is on track when: - [ ] `BEARING.md` tracks the running v18 task list. -- [ ] A cross-repo contract matrix names each family, generated artifact, local +- [x] A cross-repo contract matrix names each family, generated artifact, local source fact, consumer, and missing witness. - [ ] A WARP Optic realization map exists for `git-warp`. - [ ] The repo can ingest at least one generated Continuum family artifact or 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. From 487ff2b6df73b21b57b30418aa443ea8c22468ec Mon Sep 17 00:00:00 2001 From: James Ross Date: Thu, 21 May 2026 10:22:24 -0700 Subject: [PATCH 5/7] docs: map git-warp optic realization --- docs/BEARING.md | 2 +- .../v18-continuum-compatibility-charter.md | 2 +- .../v18-warp-optic-realization-map.md | 113 ++++++++++++++++++ 3 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 docs/design/0148-v18-warp-optic-realization-map/v18-warp-optic-realization-map.md diff --git a/docs/BEARING.md b/docs/BEARING.md index 4fc2fe14..a8d31590 100644 --- a/docs/BEARING.md +++ b/docs/BEARING.md @@ -166,7 +166,7 @@ before the final commit for that slice, and mark completed items with `- [x]`. 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. -- [ ] 4. Define git-warp's WARP Optic realization map: observer plan, bounded +- [x] 4. Define git-warp's WARP Optic realization map: observer plan, bounded slice, lowering surface, admissibility law, and retention contract. - [ ] 5. Add a generated-artifact ingestion path for Continuum families, with a guard against handwritten local mirrors becoming contract authority. 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 index b47d8810..002b7c32 100644 --- 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 @@ -125,7 +125,7 @@ The v18 opening campaign is on track when: - [ ] `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. -- [ ] A WARP Optic realization map exists for `git-warp`. +- [x] A WARP Optic realization map exists for `git-warp`. - [ ] The repo can ingest at least one generated Continuum family artifact or generated fixture. - [ ] A guard prevents generated-family local mirrors from becoming hidden 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. From c3d03dc6ed0d1ada87b7d7b1d04dbe341627fb8f Mon Sep 17 00:00:00 2001 From: James Ross Date: Thu, 21 May 2026 10:30:33 -0700 Subject: [PATCH 6/7] feat: add continuum artifact ingestion guard --- docs/BEARING.md | 2 +- .../v18-continuum-compatibility-charter.md | 4 +- .../v18-continuum-artifact-ingestion.md | 100 +++++++++++++++ index.ts | 26 +++- .../continuum/ContinuumArtifactAuthority.ts | 61 ++++++++++ .../continuum/ContinuumArtifactDescriptor.ts | 96 +++++++++++++++ .../ContinuumArtifactIngestionPolicy.ts | 21 ++++ src/domain/continuum/ContinuumFamilyId.ts | 58 +++++++++ .../errors/ContinuumArtifactAuthorityError.ts | 8 ++ src/domain/errors/index.ts | 1 + .../ContinuumArtifactJsonFileAdapter.ts | 115 ++++++++++++++++++ .../receipt-family-generated-artifact.json | 11 ++ .../ContinuumArtifactIngestionPolicy.test.ts | 57 +++++++++ test/unit/domain/index.exports.test.ts | 36 ++++++ .../ContinuumArtifactJsonFileAdapter.test.ts | 64 ++++++++++ 15 files changed, 656 insertions(+), 4 deletions(-) create mode 100644 docs/design/0149-v18-continuum-artifact-ingestion/v18-continuum-artifact-ingestion.md create mode 100644 src/domain/continuum/ContinuumArtifactAuthority.ts create mode 100644 src/domain/continuum/ContinuumArtifactDescriptor.ts create mode 100644 src/domain/continuum/ContinuumArtifactIngestionPolicy.ts create mode 100644 src/domain/continuum/ContinuumFamilyId.ts create mode 100644 src/domain/errors/ContinuumArtifactAuthorityError.ts create mode 100644 src/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.ts create mode 100644 test/fixtures/continuum/receipt-family-generated-artifact.json create mode 100644 test/unit/domain/continuum/ContinuumArtifactIngestionPolicy.test.ts create mode 100644 test/unit/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.test.ts diff --git a/docs/BEARING.md b/docs/BEARING.md index a8d31590..ce239209 100644 --- a/docs/BEARING.md +++ b/docs/BEARING.md @@ -168,7 +168,7 @@ before the final commit for that slice, and mark completed items with `- [x]`. track. - [x] 4. Define git-warp's WARP Optic realization map: observer plan, bounded slice, lowering surface, admissibility law, and retention contract. -- [ ] 5. Add a generated-artifact ingestion path for Continuum families, with a +- [x] 5. Add a generated-artifact ingestion path for Continuum families, with a guard against handwritten local mirrors becoming contract authority. - [ ] 6. Make evidence posture explicit: translated git-warp evidence first, native Continuum evidence only after native witnesshood is proven. 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 index 002b7c32..aa2a3993 100644 --- 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 @@ -126,9 +126,9 @@ The v18 opening campaign is on track when: - [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`. -- [ ] The repo can ingest at least one generated Continuum family artifact or +- [x] The repo can ingest at least one generated Continuum family artifact or generated fixture. -- [ ] A guard prevents generated-family local mirrors from becoming hidden +- [x] A guard prevents generated-family local mirrors from becoming hidden authority. - [ ] The first receipt-family projection reaches `warp-ttd` without adapter folklore. 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..e7f8d79d --- /dev/null +++ b/docs/design/0149-v18-continuum-artifact-ingestion/v18-continuum-artifact-ingestion.md @@ -0,0 +1,100 @@ +--- +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 JSON loader; +- `test/fixtures/continuum/receipt-family-generated-artifact.json` as the first + generated-family fixture descriptor. + +The guard accepts only: + +- `generated-artifact` +- `generated-fixture` + +It rejects: + +- `local-mirror` +- `handwritten-mirror` + +## Boundary Law + +JSON parsing stays in `src/infrastructure/adapters/`. Domain code receives +validated constructor fields and runtime-backed objects. + +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/domain/errors/ContinuumArtifactAuthorityError.ts \ + src/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.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 +``` + +Observed focused test result: + +```text +Test Files 2 passed (2) +Tests 9 passed (9) +``` + +## 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. +- 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; mirror descriptors are rejected + before ingestion. + +## Closeout + +This closes BEARING task 5 and gives later receipt-family projection work a +safe generated-artifact entry point. diff --git a/index.ts b/index.ts index e4719aed..ce8b8572 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,14 @@ 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'; export { GitGraphAdapter, @@ -247,6 +256,7 @@ export { // Error types for integrity failure handling AuditError, + ContinuumArtifactAuthorityError, EncryptionError, PatchError, ForkError, @@ -318,6 +328,13 @@ export { exportCoordinateComparisonFact, exportCoordinateTransferPlanFact, + // Continuum compatibility artifacts + ContinuumArtifactAuthority, + ContinuumArtifactDescriptor, + ContinuumArtifactIngestionPolicy, + ContinuumFamilyId, + ContinuumArtifactJsonFileAdapter, + // Tick receipts (LIGHTHOUSE) createTickReceipt, tickReceiptCanonicalJson, @@ -360,7 +377,14 @@ export { SyncSecret, }; -export type { PropValue, SnapshotPropValue, SyncRateLimitConfig }; +export type { + PropValue, + SnapshotPropValue, + SyncRateLimitConfig, + ContinuumArtifactAuthorityValue, + ContinuumArtifactDescriptorFields, + 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..589b1a7a --- /dev/null +++ b/src/domain/continuum/ContinuumArtifactAuthority.ts @@ -0,0 +1,61 @@ +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 { + switch (value) { + case GENERATED_ARTIFACT_AUTHORITY: + return GENERATED_ARTIFACT_AUTHORITY; + case GENERATED_FIXTURE_AUTHORITY: + return GENERATED_FIXTURE_AUTHORITY; + case LOCAL_MIRROR_AUTHORITY: + return LOCAL_MIRROR_AUTHORITY; + case HANDWRITTEN_MIRROR_AUTHORITY: + return HANDWRITTEN_MIRROR_AUTHORITY; + default: + throw new WarpError( + `Continuum artifact authority must be one of: ${CONTINUUM_ARTIFACT_AUTHORITIES.join(', ')}`, + 'E_VALIDATION', + ); + } +} diff --git a/src/domain/continuum/ContinuumArtifactDescriptor.ts b/src/domain/continuum/ContinuumArtifactDescriptor.ts new file mode 100644 index 00000000..d474b8b1 --- /dev/null +++ b/src/domain/continuum/ContinuumArtifactDescriptor.ts @@ -0,0 +1,96 @@ +import ContinuumArtifactAuthority from './ContinuumArtifactAuthority.ts'; +import ContinuumFamilyId from './ContinuumFamilyId.ts'; +import WarpError from '../errors/WarpError.ts'; + +export type ContinuumArtifactDescriptorFields = { + readonly familyId: string | ContinuumFamilyId; + readonly version: string; + readonly sourceSchemaPath: string; + readonly generatedBy: string; + readonly artifactKind: string; + readonly authority: string | ContinuumArtifactAuthority; + readonly targets: readonly string[]; + readonly witnessScope?: string; + readonly artifactDigest?: string; +}; + +/** Runtime-backed descriptor for a generated Continuum family artifact. */ +export default class ContinuumArtifactDescriptor { + readonly familyId: ContinuumFamilyId; + readonly version: string; + readonly sourceSchemaPath: string; + readonly generatedBy: string; + readonly artifactKind: string; + readonly authority: ContinuumArtifactAuthority; + readonly targets: readonly string[]; + readonly witnessScope: string | undefined; + readonly artifactDigest: string | undefined; + + constructor(fields: ContinuumArtifactDescriptorFields) { + const { familyId, version, sourceSchemaPath, generatedBy, artifactKind, authority, targets } = fields; + this.familyId = normalizeFamilyId(familyId); + this.version = requireNonEmptyString(version, 'version'); + this.sourceSchemaPath = requireNonEmptyString(sourceSchemaPath, 'sourceSchemaPath'); + this.generatedBy = requireNonEmptyString(generatedBy, 'generatedBy'); + this.artifactKind = requireNonEmptyString(artifactKind, 'artifactKind'); + this.authority = normalizeAuthority(authority); + this.targets = freezeTargets(targets); + this.witnessScope = optionalNonEmptyString(fields.witnessScope, 'witnessScope'); + this.artifactDigest = optionalNonEmptyString(fields.artifactDigest, 'artifactDigest'); + 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 (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 (targets.length === 0) { + throw new WarpError('targets must contain at least one generated target', 'E_VALIDATION'); + } + const normalized: string[] = []; + for (const target of targets) { + normalized.push(requireNonEmptyString(target, 'targets[]')); + } + return Object.freeze(normalized); +} diff --git a/src/domain/continuum/ContinuumArtifactIngestionPolicy.ts b/src/domain/continuum/ContinuumArtifactIngestionPolicy.ts new file mode 100644 index 00000000..bd0fad1b --- /dev/null +++ b/src/domain/continuum/ContinuumArtifactIngestionPolicy.ts @@ -0,0 +1,21 @@ +import ContinuumArtifactAuthorityError from '../errors/ContinuumArtifactAuthorityError.ts'; +import type ContinuumArtifactDescriptor from './ContinuumArtifactDescriptor.ts'; + +/** 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 { + if (descriptor.hasGeneratedAuthority()) { + return; + } + throw new ContinuumArtifactAuthorityError( + `Continuum family ${descriptor.familyId.toString()} must be loaded from a generated artifact or fixture, not ${descriptor.authority.toString()}`, + ); + } +} diff --git a/src/domain/continuum/ContinuumFamilyId.ts b/src/domain/continuum/ContinuumFamilyId.ts new file mode 100644 index 00000000..7c15288e --- /dev/null +++ b/src/domain/continuum/ContinuumFamilyId.ts @@ -0,0 +1,58 @@ +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 { + switch (value) { + case RECEIPT_FAMILY_ID: + return RECEIPT_FAMILY_ID; + case SETTLEMENT_FAMILY_ID: + return SETTLEMENT_FAMILY_ID; + case NEIGHBORHOOD_CORE_FAMILY_ID: + return NEIGHBORHOOD_CORE_FAMILY_ID; + case RUNTIME_BOUNDARY_FAMILY_ID: + return RUNTIME_BOUNDARY_FAMILY_ID; + default: + throw new WarpError( + `Continuum family id must be one of: ${CONTINUUM_FAMILY_IDS.join(', ')}`, + 'E_VALIDATION', + ); + } +} 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..60042a9e --- /dev/null +++ b/src/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.ts @@ -0,0 +1,115 @@ +import { readFile } from 'node:fs/promises'; + +import ContinuumArtifactDescriptor, { + type ContinuumArtifactDescriptorFields, +} from '../../domain/continuum/ContinuumArtifactDescriptor.ts'; +import ContinuumArtifactIngestionPolicy from '../../domain/continuum/ContinuumArtifactIngestionPolicy.ts'; +import AdapterValidationError from '../../domain/errors/AdapterValidationError.ts'; + +type JsonObject = Readonly>; + +/** 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): Promise { + const raw = await readFile(path, 'utf8'); + return this.loadString(raw); + } + + /** Ingests a generated artifact descriptor from JSON text. */ + loadString(raw: string): ContinuumArtifactDescriptor { + const parsed: unknown = JSON.parse(raw); + const fields = parseDescriptorFields(parsed); + return this.policy.ingest(new ContinuumArtifactDescriptor(fields)); + } +} + +/** Converts untrusted JSON into descriptor fields. */ +function parseDescriptorFields(value: unknown): ContinuumArtifactDescriptorFields { + const source = requireJsonObject(value); + const base = { + familyId: readRequiredString(source, 'familyId'), + version: readRequiredString(source, 'version'), + sourceSchemaPath: readRequiredString(source, 'sourceSchemaPath'), + generatedBy: readRequiredString(source, 'generatedBy'), + artifactKind: readRequiredString(source, 'artifactKind'), + authority: readRequiredString(source, 'authority'), + targets: readStringArray(source, 'targets'), + }; + return withOptionalFields(base, source); +} + +/** Adds optional descriptor fields when present. */ +function withOptionalFields( + base: ContinuumArtifactDescriptorFields, + source: JsonObject, +): ContinuumArtifactDescriptorFields { + const witnessScope = readOptionalString(source, 'witnessScope'); + const artifactDigest = readOptionalString(source, 'artifactDigest'); + return { + ...base, + ...(witnessScope !== undefined ? { witnessScope } : {}), + ...(artifactDigest !== undefined ? { artifactDigest } : {}), + }; +} + +/** Requires a non-array JSON object. */ +function requireJsonObject(value: unknown): JsonObject { + if (!isJsonObject(value)) { + throw new AdapterValidationError('Continuum artifact descriptor JSON must be an object'); + } + return value; +} + +/** Returns true when a value is a non-array JSON object. */ +function isJsonObject(value: unknown): value is JsonObject { + return value !== null && typeof value === 'object' && !Array.isArray(value); +} + +/** Reads a required string field. */ +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 an optional string field. */ +function readOptionalString(source: JsonObject, key: string): string | undefined { + const value = source[key]; + if (value === undefined) { + return undefined; + } + if (typeof value !== 'string' || value.length === 0) { + throw new AdapterValidationError(`Continuum artifact descriptor field "${key}" must be a non-empty string when present`); + } + return value; +} + +/** Reads a required string array field. */ +function readStringArray(source: JsonObject, key: string): readonly string[] { + const value = source[key]; + if (!Array.isArray(value) || value.length === 0) { + throw new AdapterValidationError(`Continuum artifact descriptor field "${key}" must be a non-empty string array`); + } + const strings: string[] = []; + for (const entry of value) { + strings.push(readStringArrayEntry(entry, key)); + } + return Object.freeze(strings); +} + +/** 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/test/fixtures/continuum/receipt-family-generated-artifact.json b/test/fixtures/continuum/receipt-family-generated-artifact.json new file mode 100644 index 00000000..cc6c9029 --- /dev/null +++ b/test/fixtures/continuum/receipt-family-generated-artifact.json @@ -0,0 +1,11 @@ +{ + "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", "echo"], + "witnessScope": "receipt-family", + "artifactDigest": "sha256:receipt-fixture" +} diff --git a/test/unit/domain/continuum/ContinuumArtifactIngestionPolicy.test.ts b/test/unit/domain/continuum/ContinuumArtifactIngestionPolicy.test.ts new file mode 100644 index 00000000..b8b86bf2 --- /dev/null +++ b/test/unit/domain/continuum/ContinuumArtifactIngestionPolicy.test.ts @@ -0,0 +1,57 @@ +import { describe, expect, it } from 'vitest'; + +import ContinuumArtifactAuthorityError from '../../../../src/domain/errors/ContinuumArtifactAuthorityError.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'; + +/** Builds a receipt-family descriptor for policy tests. */ +function makeDescriptor(authority: string): ContinuumArtifactDescriptor { + return 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, + targets: ['typescript', 'echo'], + witnessScope: 'receipt-family', + }); +} + +describe('ContinuumArtifactIngestionPolicy', () => { + it('accepts documented generated fixtures', () => { + const descriptor = makeDescriptor('generated-fixture'); + const policy = new ContinuumArtifactIngestionPolicy(); + + expect(policy.ingest(descriptor)).toBe(descriptor); + }); + + it('accepts generated artifacts', () => { + const descriptor = makeDescriptor('generated-artifact'); + const policy = new ContinuumArtifactIngestionPolicy(); + + expect(policy.ingest(descriptor)).toBe(descriptor); + }); + + it('rejects local mirrors as family authority', () => { + const descriptor = makeDescriptor('local-mirror'); + const policy = new ContinuumArtifactIngestionPolicy(); + + expect(() => policy.ingest(descriptor)).toThrow(ContinuumArtifactAuthorityError); + }); + + it('rejects handwritten mirrors as family authority', () => { + const descriptor = makeDescriptor('handwritten-mirror'); + const policy = new ContinuumArtifactIngestionPolicy(); + + expect(() => policy.ingest(descriptor)).toThrow(ContinuumArtifactAuthorityError); + }); + + it('keeps family ids runtime-backed', () => { + const descriptor = makeDescriptor('generated-fixture'); + + expect(descriptor.familyId).toBeInstanceOf(ContinuumFamilyId); + expect(descriptor.familyId.toString()).toBe('receipt-family'); + }); +}); 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..e1a08079 --- /dev/null +++ b/test/unit/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.test.ts @@ -0,0 +1,64 @@ +import { describe, expect, it } from 'vitest'; +import { fileURLToPath } from 'node:url'; + +import ContinuumArtifactJsonFileAdapter from '../../../../src/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.ts'; +import ContinuumArtifactAuthorityError from '../../../../src/domain/errors/ContinuumArtifactAuthorityError.ts'; +import AdapterValidationError from '../../../../src/domain/errors/AdapterValidationError.ts'; + +const generatedFixtureJson = `{ + "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", "echo"], + "witnessScope": "receipt-family", + "artifactDigest": "sha256:receipt-fixture" +}`; + +const localMirrorJson = `{ + "familyId": "receipt-family", + "version": "0.1.0", + "sourceSchemaPath": "src/domain/continuum/local-receipt.ts", + "generatedBy": "git-warp", + "artifactKind": "continuum.family.fixture", + "authority": "local-mirror", + "targets": ["typescript"] +}`; + +const generatedFixturePath = fileURLToPath( + new URL('../../../fixtures/continuum/receipt-family-generated-artifact.json', import.meta.url), +); + +describe('ContinuumArtifactJsonFileAdapter', () => { + it('loads generated fixture descriptors', () => { + const adapter = new ContinuumArtifactJsonFileAdapter(); + const descriptor = adapter.loadString(generatedFixtureJson); + + expect(descriptor.familyId.toString()).toBe('receipt-family'); + expect(descriptor.hasTarget('typescript')).toBe(true); + expect(descriptor.hasGeneratedAuthority()).toBe(true); + expect(descriptor.artifactDigest).toBe('sha256:receipt-fixture'); + }); + + it('loads generated fixture descriptor files', async () => { + const adapter = new ContinuumArtifactJsonFileAdapter(); + const descriptor = await adapter.loadFile(generatedFixturePath); + + expect(descriptor.familyId.toString()).toBe('receipt-family'); + expect(descriptor.witnessScope).toBe('receipt-family'); + }); + + it('rejects local mirrors before they become authority', () => { + const adapter = new ContinuumArtifactJsonFileAdapter(); + + expect(() => adapter.loadString(localMirrorJson)).toThrow(ContinuumArtifactAuthorityError); + }); + + it('rejects malformed descriptor JSON', () => { + const adapter = new ContinuumArtifactJsonFileAdapter(); + + expect(() => adapter.loadString('{ "familyId": "receipt-family" }')).toThrow(AdapterValidationError); + }); +}); From 59d69b17b579700fa8196ffad80ec2ffdfbd38dc Mon Sep 17 00:00:00 2001 From: James Ross Date: Thu, 21 May 2026 10:34:19 -0700 Subject: [PATCH 7/7] test: cover continuum error export --- test/unit/domain/errors/index.test.ts | 1 + 1 file changed, 1 insertion(+) 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',