diff --git a/CHANGELOG.md b/CHANGELOG.md index 558ac896c..70ee5f0c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- V18 Continuum compatibility now exposes a runtime-backed generated-family + readiness inventory for receipt, settlement, neighborhood-core, and + runtime-boundary families, separating projection-ready families from + authored-only families before later source-fact projections. +- V18 Continuum compatibility now exposes a runtime-backed tick witness ladder + that separates patch replay core, receipt witness core, and receipt shell + explanation while rejecting mismatched patch/receipt facts. +- V18 Continuum compatibility now exposes runtime-boundary reading-envelope + source facts for translated git-warp read results while preserving the + authored-only readiness status until Wesley profiles and fixtures exist. +- V18 Continuum compatibility now exposes runtime-boundary witnessed-suffix + source facts for translated git-warp sync/export suffixes while keeping the + existing sync protocol intact. + ### Fixed - V18 Continuum review follow-up now validates constructor envelopes before diff --git a/docs/BEARING.md b/docs/BEARING.md index 8e91c7a59..a5090c680 100644 --- a/docs/BEARING.md +++ b/docs/BEARING.md @@ -57,14 +57,14 @@ Continuum role. Current branch state at this boundary: -- Branch: `v18-continuum-slices-6-10` +- Branch: `v18-continuum-slices-11-15` - Base branch: `main` -- Latest remote head inspected: `origin/main` at `42f15812` +- Latest remote head inspected: `origin/main` at `a4c5467e` - Latest released package line: `17.0.1` -- Latest merged PR: #93, recursive tree OID read fanout and v17.0.1 release - repair +- Latest merged PR: #94, v18 Continuum slices 6 through 10 plus review + repairs - Latest completed v18 implementation cycle: - `0158-v18-warp-ttd-receipt-smoke` + `0163-v18-witnessed-suffix-source-facts` The release ladder is now: @@ -166,9 +166,9 @@ read-model groundwork, sync hardening, release gates, and package publishing. ## What comes next -Run the next v18 slices in order. Each slice gets a design document before -implementation, RED before GREEN, and a BEARING update before the final commit -for that slice. +Run v18 slices 11 through 15 in order. Each slice gets a design document +before implementation, RED before GREEN, and a BEARING update before the final +commit for that slice. ## Running Task List @@ -230,8 +230,37 @@ for that slice. sibling `~/git/warp-ttd` adapter at execution time, rejects plain local receipt DTOs, and proves `warp-ttd` can summarize generated-family git-warp receipt projection facts while preserving translated evidence posture. -- [ ] 11. Re-plan with evidence in hand before expanding into reading-envelope, - suffix/runtime-boundary, neighborhood-core, and settlement-family slices. +- [x] 11. Re-plan with evidence in hand after slices 1 through 10 and PR #94: + [0159-v18-replan-with-evidence](design/0159-v18-replan-with-evidence/v18-replan-with-evidence.md) + keeps slices 12 through 15 as translated source-fact compatibility work, not + a full v19 observer/runtime rewrite. +- [x] 12. Refresh the generated Continuum/Wesley family inventory before + projecting more families: + [0160-v18-generated-family-inventory-refresh](design/0160-v18-generated-family-inventory-refresh/v18-generated-family-inventory-refresh.md) + adds runtime-backed readiness rows for the four current Continuum families. + Receipt and settlement are projection-ready; neighborhood core and runtime + boundary stay authored-only until Wesley profiles and fixtures exist. +- [x] 13. Audit the `TickPatch`/`TickReceipt` witness ladder into replay core, + witness core, and receipt shell: + [0161-v18-tickpatch-tickreceipt-witness-ladder](design/0161-v18-tickpatch-tickreceipt-witness-ladder/v18-tickpatch-tickreceipt-witness-ladder.md) + names `GitWarpTickPatchReplayCore`, `GitWarpTickReceiptWitnessCore`, + `GitWarpTickReceiptShell`, and `GitWarpTickWitnessLadder`, validates + patch/receipt alignment, and promotes the old up-next backlog note into the + cycle packet. +- [x] 14. Project one git-warp read result into runtime-boundary + reading-envelope source facts: + [0162-v18-reading-envelope-source-facts](design/0162-v18-reading-envelope-source-facts/v18-reading-envelope-source-facts.md) + adds `GitWarpReadingEnvelopePayloadFact` and + `GitWarpReadingEnvelopeSourceFacts`, requires git-warp evidence to be + translated, and keeps runtime-boundary marked as authored-only until Wesley + profiles and fixtures exist. +- [x] 15. Project one git-warp sync/export suffix into translated + witnessed-suffix source facts: + [0163-v18-witnessed-suffix-source-facts](design/0163-v18-witnessed-suffix-source-facts/v18-witnessed-suffix-source-facts.md) + adds `GitWarpWitnessedSuffixPatchFact` and + `GitWarpWitnessedSuffixSourceFacts`, rejects empty suffix patch lists, and + keeps the current sync protocol intact while runtime-boundary remains + authored-only. 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 diff --git a/docs/design/0159-v18-replan-with-evidence/v18-replan-with-evidence.md b/docs/design/0159-v18-replan-with-evidence/v18-replan-with-evidence.md new file mode 100644 index 000000000..519e0f2c8 --- /dev/null +++ b/docs/design/0159-v18-replan-with-evidence/v18-replan-with-evidence.md @@ -0,0 +1,155 @@ +--- +cycle: 0159 +task_id: V18_replan_with_evidence +status: Complete +sponsors: + human: James + agent: Codex +started_at: 2026-05-22 +completed_at: 2026-05-22 +release_home: v18.0.0 +bearing_task: 11 +--- + +# V18 Re-Plan With Evidence + +## Sponsor Human + +James. + +## Sponsor Agent + +Codex. + +## Hill + +Re-plan v18 slices 11 through 15 from the evidence that actually landed in +PR #94, without widening v18 into the full v19/v21 observer and distributed +runtime program. + +## Playback Questions + +- Can an agent tell which v18 evidence is now real, and which compatibility + surfaces remain projections rather than native Continuum witnesshood? +- Can the next five slices be executed as one-commit cycle boundaries without + losing the generated-artifact authority posture from slices 5 through 10? +- Can a human reviewer see why reading envelopes, suffix shells, and + runtime-boundary facts are source-fact projections in this campaign, not a + full observer or sync rewrite? +- Does BEARING now name the active 11 through 15 runway instead of leaving + slice 11 as a vague re-plan placeholder? + +## Accessibility / Assistive Reading Posture + +The deliverable is text-first and table-friendly. The next-slice list in +BEARING uses ordered task entries and explicit family names so screen readers +and plain text readers do not need layout or color to recover priority. + +## Localization / Directionality Posture + +No UI copy or locale-sensitive formatting is introduced. The plan uses stable +repository nouns, file paths, and ASCII punctuation so later translation work +does not have to infer visual directionality. + +## Agent Inspectability / Explainability Posture + +The slice records: + +- current inspected cross-repo heads; +- the merged PR that supplies the evidence base; +- which slices are compatibility projections; +- which later substrate cuts are deliberately deferred. + +Future agents should be able to audit the plan from BEARING and this design +without reading chat history. + +## Evidence Snapshot + +Current inspected sources at re-plan time: + +| Source | Head / ref | Evidence | +| --- | --- | --- | +| `git-warp` | `origin/main` at `a4c5467e` | PR #94 merge, BEARING task list, cycles 0146 through 0158 | +| Continuum | `01e0735` | `docs/contract-family-registry.md`, `schemas/continuum-*-family.graphql`, `wesley/profile/scopes.mjs` | +| Wesley | `62328dba` | `out/proof/realization/manifest.json`, Continuum role docs | +| `warp-ttd` | `0491be6` | `src/adapters/gitWarpAdapter.ts`, receipt shell summary tests | +| Echo | `f8d8720` | Continuum alignment and neighborhood/settlement design docs | + +## What PR #94 Proved + +- Generated Continuum artifact descriptors can be admitted only through an + explicit load context and generated authority. +- Evidence posture is explicit; generated family shape alone does not imply + native Continuum witnesshood. +- Receipt-family projection exists for local `TickReceipt`, + `DeliveryObservation`, and optional `ReceiptShard` source facts. +- `warp-ttd` can consume generated-family git-warp receipt projection facts + through an explicit smoke without becoming a runtime dependency. +- Patch commit success now means visible writer-tip advancement, and + same-writer races use CAS conflict posture instead of hidden overwrite. + +## Re-Plan + +The next five slices should keep the compatibility line narrow: + +| Slice | Boundary | Purpose | +| ---: | --- | --- | +| 11 | Re-plan | Close the explicit re-plan placeholder with repo-visible evidence. | +| 12 | Generated-family inventory | Refresh family readiness before projecting more families. | +| 13 | Witness ladder | Split replay core, witness core, and receipt shell for `TickPatch`/`TickReceipt`. | +| 14 | Reading-envelope source facts | Project one read result toward runtime-boundary family shape. | +| 15 | Witnessed-suffix source facts | Project one export suffix toward runtime-boundary suffix shape. | + +Slices 14 and 15 are intentionally source-fact projections. They are not the +full v19 observer-plan runtime or a replacement sync protocol. + +## Non-Goals + +- Do not implement the node-record, edge-record, or attachment-plane substrate + cuts in this PR. +- Do not claim native Continuum witnesshood for translated git-warp facts. +- Do not pull the full v19 observer-plan runtime into v18. +- Do not pull the full v21 local-site or distributed neighborhood calculus + into v18. +- Do not make Echo the owner of git-warp's Continuum role. + +## RED + +Observed before this slice: + +```text +docs/BEARING.md still named task 11 only as "Re-plan with evidence in hand" +and did not enumerate the 11 through 15 execution runway. +``` + +## GREEN + +Added this cycle document and updated BEARING to: + +- anchor the re-plan to PR #94 and the current cross-repo evidence snapshot; +- mark task 11 complete; +- name tasks 12 through 15 as the active next five slices. + +## Verification + +```text +npx markdownlint-cli2 docs/BEARING.md docs/design/0159-v18-replan-with-evidence/v18-replan-with-evidence.md +``` + +## Closeout + +Slice 11 closes the planning placeholder and gives slices 12 through 15 a +bounded reviewable runway. The next commit should implement the generated +family inventory refresh as its own cycle boundary. + +## SSJS Scorecard + +- Runtime-backed forms: green; this slice introduces no runtime code. +- Boundary validation: green; generated authority stays in the next-slice + inventory plan. +- Behavior ownership: green; Continuum owns family semantics, Wesley compiles, + git-warp projects source facts, and `warp-ttd` consumes. +- 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 remains the + named default posture. diff --git a/docs/design/0160-v18-generated-family-inventory-refresh/v18-generated-family-inventory-refresh.md b/docs/design/0160-v18-generated-family-inventory-refresh/v18-generated-family-inventory-refresh.md new file mode 100644 index 000000000..a5085998c --- /dev/null +++ b/docs/design/0160-v18-generated-family-inventory-refresh/v18-generated-family-inventory-refresh.md @@ -0,0 +1,147 @@ +--- +cycle: 0160 +task_id: V18_generated_family_inventory_refresh +status: Complete +sponsors: + human: James + agent: Codex +started_at: 2026-05-22 +completed_at: 2026-05-22 +release_home: v18.0.0 +bearing_task: 12 +--- + +# V18 Generated Family Inventory Refresh + +## Sponsor Human + +James. + +## Sponsor Agent + +Codex. + +## Hill + +Add a runtime-backed inventory of current Continuum/Wesley family readiness so +later v18 projections can ask whether a family is projection-ready before they +emit generated-family-shaped facts. + +## Playback Questions + +- Can the runtime name every current Continuum family in one inventory without + relying on an external chat transcript? +- Can code distinguish profiled fixture-witnessed families from authored-only + families before projecting more source facts? +- Does the inventory reject missing, duplicate, or unknown family entries? +- Does the public export surface include the inventory nouns needed by later + slices without making git-warp the semantic owner of Continuum families? + +## Accessibility / Assistive Reading Posture + +The inventory remains code-level data with tests and a design table. It has no +visual-only affordances. Human reviewers can read one ordered table in this doc +and one ordered test fixture in the unit test. + +## Localization / Directionality Posture + +No UI text is introduced. The status labels are stable ASCII protocol labels, +not localized prose. + +## Agent Inspectability / Explainability Posture + +The inventory objects answer the questions later agents need before adding +projections: + +- which family exists; +- which authored schema path anchors it; +- whether Wesley has profiled and fixture-witnessed it; +- what source facts git-warp can currently provide; +- what compatibility cut remains open. + +## Evidence Snapshot + +The slice reflects the same inspected cross-repo evidence used by slice 11: + +| Family | Authored home | Wesley status | Current v18 posture | +| --- | --- | --- | --- | +| `receipt-family` | `schemas/continuum-receipt-family.graphql` | `profiled`, `fixture-witnessed` | projection-ready | +| `settlement-family` | `schemas/continuum-settlement-family.graphql` | `profiled`, `fixture-witnessed` | projection-ready | +| `neighborhood-core-family` | `schemas/continuum-neighborhood-core-family.graphql` | `authored` | authored-only | +| `runtime-boundary-family` | `schemas/continuum-runtime-boundary-family.graphql` | `authored` | authored-only | + +## Design + +Add these domain concepts: + +- `ContinuumGeneratedFamilyStatus` for the readiness label; +- `ContinuumGeneratedFamilyInventoryEntry` for one family row; +- `ContinuumGeneratedFamilyInventory` for complete inventory validation; +- `createCurrentContinuumGeneratedFamilyInventory()` for the current v18 + source-fact inventory. + +`profiled-fixture-witnessed` means the family is safe for translated +git-warp source-fact projection when the projection also uses generated +descriptor authority. `authored-only` means the family exists but must not be +treated as projection-ready yet. + +## Non-Goals + +- Do not parse Continuum Markdown in domain code. +- Do not make git-warp the authority for Continuum family semantics. +- Do not infer native Continuum witnesshood from inventory readiness. +- Do not load sibling repos at runtime. + +## RED + +Observed first failure: + +```text +npx vitest run test/unit/domain/continuum/ContinuumGeneratedFamilyInventory.test.ts --reporter=verbose +Error: Cannot find module '../../../../src/domain/continuum/ContinuumGeneratedFamilyInventory.ts' +``` + +The test failed because the inventory nouns did not exist yet. + +## GREEN + +Implemented the runtime-backed inventory classes, exported them from the +package entry point, and updated BEARING task 12. + +## Verification + +```text +npx vitest run test/unit/domain/continuum/ContinuumGeneratedFamilyInventory.test.ts \ + test/unit/domain/index.exports.test.ts --reporter=verbose +npm run typecheck +npx eslint src/domain/continuum/ContinuumGeneratedFamilyStatus.ts \ + src/domain/continuum/ContinuumGeneratedFamilyInventoryEntry.ts \ + src/domain/continuum/ContinuumGeneratedFamilyInventory.ts \ + src/domain/continuum/createCurrentContinuumGeneratedFamilyInventory.ts \ + test/unit/domain/continuum/ContinuumGeneratedFamilyInventory.test.ts \ + test/unit/domain/index.exports.test.ts +npx markdownlint-cli2 docs/BEARING.md \ + docs/design/0160-v18-generated-family-inventory-refresh/v18-generated-family-inventory-refresh.md +``` + +The first lint pass rejected the inventory factory for exceeding the +max-lines-per-function limit. The GREEN implementation splits each family row +into a focused entry function. + +## Closeout + +Slice 12 gives later projections one complete, validated readiness inventory. +Receipt and settlement are projection-ready; neighborhood core and runtime +boundary are authored-only until Wesley profiles and fixtures exist. + +## SSJS Scorecard + +- Runtime-backed forms: expected green; inventory concepts are classes. +- Boundary validation: expected green; no raw sibling-repo parsing enters the + domain. +- Behavior ownership: expected green; Continuum and Wesley authority stays + descriptive, while git-warp only records projection readiness. +- Message parsing: expected green. +- Ambient time or entropy: expected green. +- Fake shape trust or cast-cosplay: expected green; readiness does not imply + native witnesshood. diff --git a/docs/design/0161-v18-tickpatch-tickreceipt-witness-ladder/v18-tickpatch-tickreceipt-witness-ladder.md b/docs/design/0161-v18-tickpatch-tickreceipt-witness-ladder/v18-tickpatch-tickreceipt-witness-ladder.md new file mode 100644 index 000000000..0ac58f823 --- /dev/null +++ b/docs/design/0161-v18-tickpatch-tickreceipt-witness-ladder/v18-tickpatch-tickreceipt-witness-ladder.md @@ -0,0 +1,135 @@ +--- +cycle: 0161 +task_id: V18_tickpatch_tickreceipt_witness_ladder +status: Complete +sponsors: + human: James + agent: Codex +started_at: 2026-05-22 +completed_at: 2026-05-22 +release_home: v18.0.0 +bearing_task: 13 +backlog_source: docs/method/backlog/up-next/PROTO_tickpatch-tickreceipt-witness-ladder-audit.md +--- + +# V18 TickPatch TickReceipt Witness Ladder + +## Sponsor Human + +James. + +## Sponsor Agent + +Codex. + +## Hill + +Name the runtime-backed ladder that separates patch replay core, receipt +witness core, and receipt shell explanation for git-warp tick evidence. + +## Playback Questions + +- Can a `Patch` plus matching `TickReceipt` be summarized into explicit replay + core, witness core, and receipt shell concepts? +- Does the ladder reject mismatched patch SHA, writer, or Lamport facts instead + of letting unrelated objects appear causally connected? +- Can `warp-ttd` and Wesley target named layers without reinterpreting + `TickReceipt` as one undifferentiated witness blob? +- Does the promoted backlog item stop living in `up-next/` once this design + owns the work? + +## Accessibility / Assistive Reading Posture + +This slice introduces code-level nouns and text documentation only. The ladder +layers are ordered and named in plain text so the distinction survives without +visual hierarchy. + +## Localization / Directionality Posture + +No UI text is introduced. Runtime labels are stable ASCII family/layer labels +and should not be localized. + +## Agent Inspectability / Explainability Posture + +The ladder gives future agents one inspected runtime object to ask: + +- what is the replay core; +- what is the local witness core; +- what is explanatory receipt shell; +- whether the supplied patch and receipt actually agree. + +This avoids adapter-local summary code inferring the split differently in +`warp-ttd`, Wesley fixtures, or future projections. + +## Design + +Add four domain concepts: + +- `GitWarpTickPatchReplayCore` for substrate replay facts from `Patch`; +- `GitWarpTickReceiptWitnessCore` for outcome counts from `TickReceipt`; +- `GitWarpTickReceiptShell` for explanatory receipt-shell facts; +- `GitWarpTickWitnessLadder` for validated patch/receipt alignment. + +The current codebase does not have a concrete `TickPatch` class. The source +runtime noun is `Patch`; this slice treats "TickPatch" from the backlog note +as the patch-at-one-tick concept represented by `Patch` plus the commit SHA. + +## Non-Goals + +- Do not redesign `Patch`. +- Do not redesign `TickReceipt`. +- Do not emit Continuum receipt, runtime-boundary, or settlement family values + from this slice. +- Do not claim native Continuum witnesshood. +- Do not change reducer semantics. + +## RED + +Observed first failure: + +```text +npx vitest run test/unit/domain/continuum/GitWarpTickWitnessLadder.test.ts --reporter=verbose +Error: Cannot find module '../../../../src/domain/continuum/GitWarpTickPatchReplayCore.ts' +``` + +The test failed because the ladder nouns did not exist yet. + +## GREEN + +Implemented the ladder classes, public exports, backlog promotion, changelog +entry, and BEARING task 13 closeout. + +## Verification + +```text +npx vitest run test/unit/domain/continuum/GitWarpTickWitnessLadder.test.ts \ + test/unit/domain/index.exports.test.ts --reporter=verbose +npm run typecheck +npx eslint src/domain/continuum/GitWarpTickPatchReplayCore.ts \ + src/domain/continuum/GitWarpTickReceiptWitnessCore.ts \ + src/domain/continuum/GitWarpTickReceiptShell.ts \ + src/domain/continuum/GitWarpTickWitnessLadder.ts \ + test/unit/domain/continuum/GitWarpTickWitnessLadder.test.ts \ + test/unit/domain/index.exports.test.ts +npx markdownlint-cli2 CHANGELOG.md docs/BEARING.md \ + docs/design/0161-v18-tickpatch-tickreceipt-witness-ladder/v18-tickpatch-tickreceipt-witness-ladder.md +``` + +## Closeout + +The promoted backlog item now lives as this design cycle rather than as an +`up-next/` backlog note. The runtime ladder rejects mismatched patch SHA, +writer, or Lamport facts and exposes named replay core, witness core, and +receipt shell layers for later projections. + +## SSJS Scorecard + +- Runtime-backed forms: expected green; the new layers are classes. +- Boundary validation: expected green; the ladder accepts existing domain + objects, not raw wire values. +- Behavior ownership: expected green; `Patch` owns replay facts and + `TickReceipt` owns local outcome facts. +- Message parsing: expected green. +- Ambient time or entropy: expected green. +- Fake shape trust or cast-cosplay: expected green; patch and receipt alignment + is validated at construction. diff --git a/docs/design/0162-v18-reading-envelope-source-facts/v18-reading-envelope-source-facts.md b/docs/design/0162-v18-reading-envelope-source-facts/v18-reading-envelope-source-facts.md new file mode 100644 index 000000000..91b34cfac --- /dev/null +++ b/docs/design/0162-v18-reading-envelope-source-facts/v18-reading-envelope-source-facts.md @@ -0,0 +1,129 @@ +--- +cycle: 0162 +task_id: V18_reading_envelope_source_facts +status: Complete +sponsors: + human: James + agent: Codex +started_at: 2026-05-22 +completed_at: 2026-05-22 +release_home: v18.0.0 +bearing_task: 14 +--- + +# V18 Reading Envelope Source Facts + +## Sponsor Human + +James. + +## Sponsor Agent + +Codex. + +## Hill + +Represent one git-warp read result as translated runtime-boundary reading +envelope source facts without claiming runtime-boundary projection readiness or +native Continuum witnesshood. + +## Playback Questions + +- Can git-warp name observer plan, observation request, source, basis, payload, + witness reference, and evidence posture for one read result? +- Does the source-fact object require the `runtime-boundary-family` inventory + row instead of accepting an unrelated family? +- Does it preserve the slice 12 truth that runtime-boundary is currently + `authored-only`, not generated projection-ready? +- Does it reject native Continuum evidence posture for translated git-warp read + facts? + +## Accessibility / Assistive Reading Posture + +No UI is introduced. The source-fact fields use explicit names rather than +position-dependent tuple ordering. + +## Localization / Directionality Posture + +No locale-sensitive UI copy is introduced. Runtime identifiers remain stable +ASCII source-fact labels. + +## Agent Inspectability / Explainability Posture + +Later agents can inspect a single object to answer: + +- which runtime-boundary family row authorized the source-fact shape; +- which read source and basis produced the payload; +- which witness reference keeps the reading tied to git-warp evidence; +- whether a generated runtime-boundary profile is still required. + +## Design + +Add two domain concepts: + +- `GitWarpReadingEnvelopePayloadFact` names the payload kind, payload digest, + and optional state hash of the reading result. +- `GitWarpReadingEnvelopeSourceFacts` names the runtime-boundary family row, + translated evidence posture, observer plan id, observation request id, + source reference, basis reference, payload fact, witness reference, and budget + status. + +The source facts intentionally accept an authored-only runtime-boundary +inventory row. That means they are compatibility source facts for `warp-ttd` +and future Wesley profiles, not generated runtime-boundary projections. + +## Non-Goals + +- Do not redesign `Observer`, `QueryController`, or materialization. +- Do not add a generated runtime-boundary descriptor until Wesley has one. +- Do not claim native Continuum evidence. +- Do not make reading envelopes public product UI. + +## RED + +Observed first failure: + +```text +npx vitest run test/unit/domain/continuum/GitWarpReadingEnvelopeSourceFacts.test.ts --reporter=verbose +Error: Cannot find module '../../../../src/domain/continuum/GitWarpReadingEnvelopePayloadFact.ts' +``` + +The test failed because the reading-envelope source-fact nouns did not exist +yet. + +## GREEN + +Implemented the source-fact classes, public exports, changelog entry, and +BEARING task 14 closeout. + +## Verification + +```text +npx vitest run test/unit/domain/continuum/GitWarpReadingEnvelopeSourceFacts.test.ts \ + test/unit/domain/index.exports.test.ts --reporter=verbose +npm run typecheck +npx eslint src/domain/continuum/GitWarpReadingEnvelopePayloadFact.ts \ + src/domain/continuum/GitWarpReadingEnvelopeSourceFacts.ts \ + test/unit/domain/continuum/GitWarpReadingEnvelopeSourceFacts.test.ts \ + test/unit/domain/index.exports.test.ts +npx markdownlint-cli2 CHANGELOG.md docs/BEARING.md \ + docs/design/0162-v18-reading-envelope-source-facts/v18-reading-envelope-source-facts.md +``` + +## Closeout + +Slice 14 creates source facts for runtime-boundary reading envelopes while +keeping the inventory truth visible: runtime-boundary is authored-only and +still requires a generated Wesley profile before generated-family projection. + +## SSJS Scorecard + +- Runtime-backed forms: expected green; source facts and payload facts are + classes. +- Boundary validation: expected green; raw read outputs are not parsed here. +- Behavior ownership: expected green; the runtime-boundary row comes from the + inventory, while git-warp owns only its translated read facts. +- Message parsing: expected green. +- Ambient time or entropy: expected green. +- Fake shape trust or cast-cosplay: expected green; authored-only inventory + status remains visible. diff --git a/docs/design/0163-v18-witnessed-suffix-source-facts/v18-witnessed-suffix-source-facts.md b/docs/design/0163-v18-witnessed-suffix-source-facts/v18-witnessed-suffix-source-facts.md new file mode 100644 index 000000000..c30b27dab --- /dev/null +++ b/docs/design/0163-v18-witnessed-suffix-source-facts/v18-witnessed-suffix-source-facts.md @@ -0,0 +1,130 @@ +--- +cycle: 0163 +task_id: V18_witnessed_suffix_source_facts +status: Complete +sponsors: + human: James + agent: Codex +started_at: 2026-05-22 +completed_at: 2026-05-22 +release_home: v18.0.0 +bearing_task: 15 +--- + +# V18 Witnessed Suffix Source Facts + +## Sponsor Human + +James. + +## Sponsor Agent + +Codex. + +## Hill + +Represent one git-warp sync/export suffix as translated runtime-boundary source +facts without replacing the existing sync protocol or claiming generated +runtime-boundary projection readiness. + +## Playback Questions + +- Can git-warp name graph, source frontier, basis frontier, target frontier, + ordered patch facts, witness reference, bundle digest, and evidence posture + for one exported suffix? +- Does the source-fact object require the `runtime-boundary-family` inventory + row? +- Does it reject empty suffixes so a witnessed suffix cannot silently become a + frontier-only placeholder? +- Does it preserve translated evidence posture and authored-only + runtime-boundary readiness? + +## Accessibility / Assistive Reading Posture + +No UI is introduced. Suffix source facts are explicit named fields and ordered +patch rows, so the artifact remains readable in plain text and test output. + +## Localization / Directionality Posture + +No locale-sensitive UI copy is introduced. Runtime labels and references remain +stable ASCII source-fact identifiers. + +## Agent Inspectability / Explainability Posture + +Later agents can inspect one object to answer: + +- which runtime-boundary family row authorized the source-fact shape; +- what source/basis/target frontier references bound the suffix; +- which ordered patch facts form the suffix; +- whether generated runtime-boundary projection is still blocked. + +## Design + +Add two domain concepts: + +- `GitWarpWitnessedSuffixPatchFact` for one ordered patch reference in a + transported suffix. +- `GitWarpWitnessedSuffixSourceFacts` for the runtime-boundary family row, + translated evidence posture, graph identity, frontier references, ordered + patch facts, witness reference, and bundle digest. + +These are source facts only. They prepare the `WitnessedSuffixShell` and +`CausalSuffixBundle` lanes named by Continuum, but they do not replace +`createSyncRequest`, `processSyncRequest`, or `applySyncResponse`. + +## Non-Goals + +- Do not redesign sync request/response types. +- Do not perform suffix admission. +- Do not emit a generated runtime-boundary `WitnessedSuffixShell` until Wesley + has a profile and fixture for runtime-boundary. +- Do not claim native Continuum witnesshood. + +## RED + +Observed first failure: + +```text +npx vitest run test/unit/domain/continuum/GitWarpWitnessedSuffixSourceFacts.test.ts --reporter=verbose +Error: Cannot find module '../../../../src/domain/continuum/GitWarpWitnessedSuffixPatchFact.ts' +``` + +The test failed because the suffix source-fact nouns did not exist yet. + +## GREEN + +Implemented the source-fact classes, public exports, changelog entry, and +BEARING task 15 closeout. + +## Verification + +```text +npx vitest run test/unit/domain/continuum/GitWarpWitnessedSuffixSourceFacts.test.ts \ + test/unit/domain/index.exports.test.ts --reporter=verbose +npm run typecheck +npx eslint src/domain/continuum/GitWarpWitnessedSuffixPatchFact.ts \ + src/domain/continuum/GitWarpWitnessedSuffixSourceFacts.ts \ + test/unit/domain/continuum/GitWarpWitnessedSuffixSourceFacts.test.ts \ + test/unit/domain/index.exports.test.ts +npx markdownlint-cli2 CHANGELOG.md docs/BEARING.md \ + docs/design/0163-v18-witnessed-suffix-source-facts/v18-witnessed-suffix-source-facts.md +``` + +## Closeout + +Slice 15 creates source facts for future runtime-boundary witnessed suffix +shells while preserving the current sync protocol. Empty patch lists fail +closed, evidence must be translated git-warp evidence, and runtime-boundary +remains authored-only until Wesley adds generated profile and fixture support. + +## SSJS Scorecard + +- Runtime-backed forms: expected green; suffix and patch facts are classes. +- Boundary validation: expected green; raw transport packets are not parsed + here. +- Behavior ownership: expected green; sync keeps its current protocol, while + this slice names source facts for future runtime-boundary projection. +- Message parsing: expected green. +- Ambient time or entropy: expected green. +- Fake shape trust or cast-cosplay: expected green; authored-only inventory + status remains visible. diff --git a/docs/method/backlog/WORKLOADS.md b/docs/method/backlog/WORKLOADS.md index 30502d454..8fec540fd 100644 --- a/docs/method/backlog/WORKLOADS.md +++ b/docs/method/backlog/WORKLOADS.md @@ -42,10 +42,10 @@ dependency graph and likely write-surface overlap. | `3.0` | `B3` | 5 | 25 | Ready-now v17 foundations and independent release hygiene | | `3.1` | `B3` | 2 | 7 | Downstream v17 runtime split work | | `3.x` parked | `B3` | 1 | 6 | Launch-prep proof and package tail | -| `4` | `B4` | 9 | 43 | v18 graph-model cut plus current `up-next/` queue | +| `4` | `B4` | 9 | 42 | v18 graph-model cut plus current `up-next/` queue | | `5` | `B5` | 3 | 11 | v19 observer/admission/runtime convergence | | `6` | `B6` | 10 | 100 | v20/v21 horizon plus speculative orbit | -| Grand total | all | 44 | 371 | Full live backlog | +| Grand total | all | 44 | 370 | Full live backlog | ## Wave 0 — Intake @@ -124,7 +124,7 @@ queue. | `WL-41-upnext-runtime-boundaries` | 9 | `DX_max-file-size-policy`, `DX_trailer-codec-dts`, `NDNM_delete-vv-orset-shims`, `PROTO_cbor-op-hydration`, `PROTO_drop-v5-runtime-nouns`, `PROTO_op-consumer-instanceof-migration`, `PROTO_warpkernel-port-cleanup`, `PROTO_warpruntime-open-options-class`, `PROTO_wire-format-migration-edgepropset` | Runtime boundary cleanup and noun drift removal | none | | `WL-42-upnext-streaming-read-chain` | 5 | `NDNM_defaultcodec-to-infrastructure`, `PERF_stream-read-migration`, `PERF_stream-cleanup`, `PERF_async-generator-traversal`, `PERF_stream-memory-tests` | Streaming read migration and memory witnesses | root `PERF_out-of-core-materialization` | | `WL-43-upnext-merge-contracts` | 8 | `CC_conflict-pipeline-god-context`, `DX_merge-conflict-corpus`, `NDNM_worldline-class-rename`, `PROTO_merge-classifier`, `PROTO_same-writer-concurrent-patch-race`, `PROTO_ttd-merge-inspector`, `PROTO_WESLEY_lane-coordinate-capability-boundary`, `VIZ_cut-git-warp-visualization-surface-in-favor-of-warp-ttd` | Merge, conflict, worldline, and visualization contract cleanup | `PROTO_patch-commit-visibility-contract`; some items also wait on v21 noun work | -| `WL-44-upnext-observer-contracts` | 3 | `DX_observer-first-guide`, `NDNM_observer-full-structural`, `PROTO_tickpatch-tickreceipt-witness-ladder-audit` | Observer teaching, structural observer, and receipt ladder cleanup | relevant v19 observer docs/runtime seams | +| `WL-44-upnext-observer-contracts` | 2 | `DX_observer-first-guide`, `NDNM_observer-full-structural` | Observer teaching and structural observer cleanup | relevant v19 observer docs/runtime seams | | `WL-45-upnext-materialize-strategy` | 1 | `PROTO_materialize-strategy-decomposition` | Materialization strategy decomposition | none | | `WL-46-upnext-trust-security` | 1 | `TRUST_sync-auth-ed25519` | Sync-auth cryptographic model upgrade | none | | `WL-47-upnext-tooling-quality-tail` | 4 | `DX_agent-code-audit`, `DX_dependency-hygiene-audit`, `DX_npm-audit-fix-vite`, `DX_vision-readme-namespace-consistency` | Audit, dependency, and docs/tooling cleanup | none | @@ -169,13 +169,13 @@ The partition is exhaustive and non-overlapping: - Wave `3.0`: `25` - Wave `3.1`: `7` - Wave `3.x`: `6` -- Wave `4`: `43` +- Wave `4`: `42` - Wave `5`: `11` - Wave `6`: `100` Total: -- `5 + 31 + 143 + 25 + 7 + 6 + 43 + 11 + 100 = 371` +- `5 + 31 + 143 + 25 + 7 + 6 + 42 + 11 + 100 = 370` Every live backlog note is covered exactly once. diff --git a/docs/method/backlog/up-next/PROTO_tickpatch-tickreceipt-witness-ladder-audit.md b/docs/method/backlog/up-next/PROTO_tickpatch-tickreceipt-witness-ladder-audit.md deleted file mode 100644 index 7250a84e9..000000000 --- a/docs/method/backlog/up-next/PROTO_tickpatch-tickreceipt-witness-ladder-audit.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -id: PROTO_tickpatch-tickreceipt-witness-ladder-audit -feature: observer-admission-runtime -blocked_by: [] -blocks: - - PROTO_WESLEY_receipt-envelope-boundary -title: TickPatch TickReceipt witness ladder audit -rank: 1 -lane: up-next -cluster: continuum-witness -impact: high -effort: medium -confidence: high ---- - -# TickPatch TickReceipt witness ladder audit - -The Continuum witness packets gave us a cleaner ladder: - -- `R_core` = seam-carrying reintegration core -- `W_core` = purpose-minimal local witness core -- `ReceiptRecord` = witness core plus explanatory shell - -`git-warp` already has strong replay/runtime nouns in `TickPatch` and -`TickReceipt`, but it is still too easy to talk about them as if they were one -undifferentiated witness blob. - -Work: - -- identify what in `TickPatch` is true replay core versus broader witness shell -- identify what in `TickReceipt` is receipt shell versus law-bearing core -- name which fields are substrate-owned and which are debugger/runtime - projection -- record whether reintegration-bearing structure exists explicitly today or is - only implicit in broader receipts -- leave behind one clear mapping that Wesley and `warp-ttd` can target without - reinterpreting `git-warp` from the outside - -Why this matters: - -- keeps substrate truth separate from debugger explanation -- gives Wesley a cleaner contract boundary -- gives `warp-ttd` a better host mapping for neighborhood core and receipt shell - -## Source - -- `docs/design/causal-lifting-and-merge-conflicts.tex` -- Continuum `0010` through `0013` diff --git a/index.ts b/index.ts index f70acdfbf..2fb3598f0 100644 --- a/index.ts +++ b/index.ts @@ -214,14 +214,34 @@ import ContinuumArtifactIngestionPolicy from './src/domain/continuum/ContinuumAr import ContinuumEvidenceClaim from './src/domain/continuum/ContinuumEvidenceClaim.ts'; import ContinuumEvidencePosture from './src/domain/continuum/ContinuumEvidencePosture.ts'; import ContinuumFamilyId from './src/domain/continuum/ContinuumFamilyId.ts'; +import ContinuumGeneratedFamilyInventory from './src/domain/continuum/ContinuumGeneratedFamilyInventory.ts'; +import ContinuumGeneratedFamilyInventoryEntry + from './src/domain/continuum/ContinuumGeneratedFamilyInventoryEntry.ts'; +import ContinuumGeneratedFamilyStatus from './src/domain/continuum/ContinuumGeneratedFamilyStatus.ts'; import ContinuumReceiptFamilyProjection from './src/domain/continuum/ContinuumReceiptFamilyProjection.ts'; +import GitWarpTickPatchReplayCore from './src/domain/continuum/GitWarpTickPatchReplayCore.ts'; +import GitWarpReadingEnvelopePayloadFact from './src/domain/continuum/GitWarpReadingEnvelopePayloadFact.ts'; +import GitWarpReadingEnvelopeSourceFacts from './src/domain/continuum/GitWarpReadingEnvelopeSourceFacts.ts'; +import GitWarpTickReceiptShell from './src/domain/continuum/GitWarpTickReceiptShell.ts'; +import GitWarpTickReceiptWitnessCore from './src/domain/continuum/GitWarpTickReceiptWitnessCore.ts'; +import GitWarpTickWitnessLadder from './src/domain/continuum/GitWarpTickWitnessLadder.ts'; +import GitWarpWitnessedSuffixPatchFact from './src/domain/continuum/GitWarpWitnessedSuffixPatchFact.ts'; +import GitWarpWitnessedSuffixSourceFacts from './src/domain/continuum/GitWarpWitnessedSuffixSourceFacts.ts'; import GitWarpReceiptSourceFacts from './src/domain/continuum/GitWarpReceiptSourceFacts.ts'; +import createCurrentContinuumGeneratedFamilyInventory + from './src/domain/continuum/createCurrentContinuumGeneratedFamilyInventory.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 { ContinuumEvidenceClaimFields } from './src/domain/continuum/ContinuumEvidenceClaim.ts'; import type { ContinuumEvidencePostureValue } from './src/domain/continuum/ContinuumEvidencePosture.ts'; import type { ContinuumFamilyIdValue } from './src/domain/continuum/ContinuumFamilyId.ts'; +import type { + ContinuumGeneratedFamilyInventoryEntryFields, +} from './src/domain/continuum/ContinuumGeneratedFamilyInventoryEntry.ts'; +import type { + ContinuumGeneratedFamilyStatusValue, +} from './src/domain/continuum/ContinuumGeneratedFamilyStatus.ts'; import type { ContinuumDeliveryObservationFact, ContinuumReceiptFact, @@ -230,6 +250,30 @@ import type { ContinuumReceiptWitnessFact, } from './src/domain/continuum/ContinuumReceiptFamilyProjection.ts'; import type { GitWarpReceiptSourceFactsFields } from './src/domain/continuum/GitWarpReceiptSourceFacts.ts'; +import type { + GitWarpReadingEnvelopePayloadFactFields, +} from './src/domain/continuum/GitWarpReadingEnvelopePayloadFact.ts'; +import type { + GitWarpReadingEnvelopeSourceFactsFields, +} from './src/domain/continuum/GitWarpReadingEnvelopeSourceFacts.ts'; +import type { + GitWarpTickPatchReplayCoreFields, +} from './src/domain/continuum/GitWarpTickPatchReplayCore.ts'; +import type { + GitWarpTickReceiptShellFields, +} from './src/domain/continuum/GitWarpTickReceiptShell.ts'; +import type { + GitWarpTickReceiptWitnessCoreFields, +} from './src/domain/continuum/GitWarpTickReceiptWitnessCore.ts'; +import type { + GitWarpTickWitnessLadderFields, +} from './src/domain/continuum/GitWarpTickWitnessLadder.ts'; +import type { + GitWarpWitnessedSuffixPatchFactFields, +} from './src/domain/continuum/GitWarpWitnessedSuffixPatchFact.ts'; +import type { + GitWarpWitnessedSuffixSourceFactsFields, +} from './src/domain/continuum/GitWarpWitnessedSuffixSourceFacts.ts'; import type { ContinuumArtifactJsonLoadContext } from './src/infrastructure/adapters/ContinuumArtifactJsonFileAdapter.ts'; export { @@ -350,8 +394,20 @@ export { ContinuumEvidenceClaim, ContinuumEvidencePosture, ContinuumFamilyId, + ContinuumGeneratedFamilyInventory, + ContinuumGeneratedFamilyInventoryEntry, + ContinuumGeneratedFamilyStatus, ContinuumReceiptFamilyProjection, + GitWarpReadingEnvelopePayloadFact, + GitWarpReadingEnvelopeSourceFacts, + GitWarpTickPatchReplayCore, + GitWarpTickReceiptShell, + GitWarpTickReceiptWitnessCore, + GitWarpTickWitnessLadder, + GitWarpWitnessedSuffixPatchFact, + GitWarpWitnessedSuffixSourceFacts, GitWarpReceiptSourceFacts, + createCurrentContinuumGeneratedFamilyInventory, ContinuumArtifactJsonFileAdapter, // Tick receipts (LIGHTHOUSE) @@ -404,12 +460,22 @@ export type { ContinuumArtifactDescriptorFields, ContinuumEvidenceClaimFields, ContinuumEvidencePostureValue, + ContinuumGeneratedFamilyInventoryEntryFields, + ContinuumGeneratedFamilyStatusValue, ContinuumDeliveryObservationFact, ContinuumReceiptFact, ContinuumReceiptFamilyProjectionFields, ContinuumReceiptOpFact, ContinuumReceiptWitnessFact, GitWarpReceiptSourceFactsFields, + GitWarpReadingEnvelopePayloadFactFields, + GitWarpReadingEnvelopeSourceFactsFields, + GitWarpTickPatchReplayCoreFields, + GitWarpTickReceiptShellFields, + GitWarpTickReceiptWitnessCoreFields, + GitWarpTickWitnessLadderFields, + GitWarpWitnessedSuffixPatchFactFields, + GitWarpWitnessedSuffixSourceFactsFields, ContinuumArtifactJsonLoadContext, ContinuumFamilyIdValue, }; diff --git a/src/domain/continuum/ContinuumGeneratedFamilyInventory.ts b/src/domain/continuum/ContinuumGeneratedFamilyInventory.ts new file mode 100644 index 000000000..795196824 --- /dev/null +++ b/src/domain/continuum/ContinuumGeneratedFamilyInventory.ts @@ -0,0 +1,101 @@ +import ContinuumFamilyId, { CONTINUUM_FAMILY_IDS } from './ContinuumFamilyId.ts'; +import ContinuumGeneratedFamilyInventoryEntry from './ContinuumGeneratedFamilyInventoryEntry.ts'; +import WarpError from '../errors/WarpError.ts'; + +export type ContinuumGeneratedFamilyInventoryFields = { + readonly entries: readonly ContinuumGeneratedFamilyInventoryEntry[]; +}; + +/** Complete generated-family readiness inventory for current Continuum families. */ +export default class ContinuumGeneratedFamilyInventory { + readonly entries: readonly ContinuumGeneratedFamilyInventoryEntry[]; + + constructor(fields: ContinuumGeneratedFamilyInventoryFields) { + const checkedFields = requireFields(fields); + this.entries = freezeAndValidateEntries(checkedFields.entries); + Object.freeze(this); + } + + /** Returns the inventory row for a current Continuum family. */ + requireEntry(familyId: string | ContinuumFamilyId): ContinuumGeneratedFamilyInventoryEntry { + const requested = normalizeFamilyId(familyId); + const found = this.entries.find((entry) => entry.familyId.equals(requested)); + if (found === undefined) { + throw new WarpError(`Continuum family ${requested.toString()} is not present in the inventory`, 'E_VALIDATION'); + } + return found; + } + + /** Returns the row only when the family is ready for generated-family projection. */ + requireProjectionReady(familyId: string | ContinuumFamilyId): ContinuumGeneratedFamilyInventoryEntry { + const entry = this.requireEntry(familyId); + if (!entry.isProjectionReady()) { + throw new WarpError( + `Continuum family ${entry.familyId.toString()} is ${entry.status.toString()}, not projection-ready`, + 'E_VALIDATION', + ); + } + return entry; + } +} + +/** Validates the inventory constructor envelope. */ +function requireFields( + value: ContinuumGeneratedFamilyInventoryFields | null | undefined, +): ContinuumGeneratedFamilyInventoryFields { + if (value === null || value === undefined) { + throw new WarpError('ContinuumGeneratedFamilyInventory fields must be provided', 'E_VALIDATION'); + } + return value; +} + +/** Normalizes a family id carrier. */ +function normalizeFamilyId(value: string | ContinuumFamilyId): ContinuumFamilyId { + if (value instanceof ContinuumFamilyId) { + return value; + } + return new ContinuumFamilyId(value); +} + +/** Freezes entries after enforcing a complete one-row-per-family inventory. */ +function freezeAndValidateEntries( + entries: readonly ContinuumGeneratedFamilyInventoryEntry[], +): readonly ContinuumGeneratedFamilyInventoryEntry[] { + if (!Array.isArray(entries)) { + throw new WarpError('ContinuumGeneratedFamilyInventory entries must be an array', 'E_VALIDATION'); + } + const frozenEntries = freezeEntries(entries); + requireCompleteFamilySet(frozenEntries); + return frozenEntries; +} + +/** Validates entry instances and freezes the inventory row array. */ +function freezeEntries( + entries: readonly ContinuumGeneratedFamilyInventoryEntry[], +): readonly ContinuumGeneratedFamilyInventoryEntry[] { + const checkedEntries: ContinuumGeneratedFamilyInventoryEntry[] = []; + for (const entry of entries) { + if (!(entry instanceof ContinuumGeneratedFamilyInventoryEntry)) { + throw new WarpError('ContinuumGeneratedFamilyInventory entries must be inventory entries', 'E_VALIDATION'); + } + checkedEntries.push(entry); + } + return Object.freeze(checkedEntries); +} + +/** Requires exactly one row for every current Continuum family id. */ +function requireCompleteFamilySet(entries: readonly ContinuumGeneratedFamilyInventoryEntry[]): void { + const seen = new Set(); + for (const entry of entries) { + const familyId = entry.familyId.toString(); + if (seen.has(familyId)) { + throw new WarpError(`Continuum generated family inventory duplicates ${familyId}`, 'E_VALIDATION'); + } + seen.add(familyId); + } + for (const familyId of CONTINUUM_FAMILY_IDS) { + if (!seen.has(familyId)) { + throw new WarpError(`Continuum generated family inventory is missing ${familyId}`, 'E_VALIDATION'); + } + } +} diff --git a/src/domain/continuum/ContinuumGeneratedFamilyInventoryEntry.ts b/src/domain/continuum/ContinuumGeneratedFamilyInventoryEntry.ts new file mode 100644 index 000000000..641a4da9c --- /dev/null +++ b/src/domain/continuum/ContinuumGeneratedFamilyInventoryEntry.ts @@ -0,0 +1,75 @@ +import ContinuumFamilyId from './ContinuumFamilyId.ts'; +import ContinuumGeneratedFamilyStatus from './ContinuumGeneratedFamilyStatus.ts'; +import WarpError from '../errors/WarpError.ts'; + +export type ContinuumGeneratedFamilyInventoryEntryFields = { + readonly familyId: string | ContinuumFamilyId; + readonly version: string; + readonly authoredSchemaPath: string; + readonly status: string | ContinuumGeneratedFamilyStatus; + readonly gitWarpSourceFacts: string; + readonly warpTtdConsumerNeed: string; + readonly openCut: string; +}; + +/** One current Continuum-family readiness row for git-warp v18 planning. */ +export default class ContinuumGeneratedFamilyInventoryEntry { + readonly familyId: ContinuumFamilyId; + readonly version: string; + readonly authoredSchemaPath: string; + readonly status: ContinuumGeneratedFamilyStatus; + readonly gitWarpSourceFacts: string; + readonly warpTtdConsumerNeed: string; + readonly openCut: string; + + constructor(fields: ContinuumGeneratedFamilyInventoryEntryFields) { + const checkedFields = requireFields(fields); + this.familyId = normalizeFamilyId(checkedFields.familyId); + this.version = requireNonEmptyString(checkedFields.version, 'version'); + this.authoredSchemaPath = requireNonEmptyString(checkedFields.authoredSchemaPath, 'authoredSchemaPath'); + this.status = normalizeStatus(checkedFields.status); + this.gitWarpSourceFacts = requireNonEmptyString(checkedFields.gitWarpSourceFacts, 'gitWarpSourceFacts'); + this.warpTtdConsumerNeed = requireNonEmptyString(checkedFields.warpTtdConsumerNeed, 'warpTtdConsumerNeed'); + this.openCut = requireNonEmptyString(checkedFields.openCut, 'openCut'); + Object.freeze(this); + } + + /** Returns true when this row is safe for generated-family projection work. */ + isProjectionReady(): boolean { + return this.status.isProjectionReady(); + } +} + +/** Validates the entry constructor envelope. */ +function requireFields( + value: ContinuumGeneratedFamilyInventoryEntryFields | null | undefined, +): ContinuumGeneratedFamilyInventoryEntryFields { + if (value === null || value === undefined) { + throw new WarpError('ContinuumGeneratedFamilyInventoryEntry fields must be provided', 'E_VALIDATION'); + } + return value; +} + +/** Normalizes a family id carrier. */ +function normalizeFamilyId(value: string | ContinuumFamilyId): ContinuumFamilyId { + if (value instanceof ContinuumFamilyId) { + return value; + } + return new ContinuumFamilyId(value); +} + +/** Normalizes a generated-family status carrier. */ +function normalizeStatus(value: string | ContinuumGeneratedFamilyStatus): ContinuumGeneratedFamilyStatus { + if (value instanceof ContinuumGeneratedFamilyStatus) { + return value; + } + return new ContinuumGeneratedFamilyStatus(value); +} + +/** Validates a required non-empty string. */ +function requireNonEmptyString(value: string, name: string): string { + if (typeof value !== 'string' || value.trim().length === 0) { + throw new WarpError(`${name} must be a non-empty string`, 'E_VALIDATION'); + } + return value; +} diff --git a/src/domain/continuum/ContinuumGeneratedFamilyStatus.ts b/src/domain/continuum/ContinuumGeneratedFamilyStatus.ts new file mode 100644 index 000000000..224011db1 --- /dev/null +++ b/src/domain/continuum/ContinuumGeneratedFamilyStatus.ts @@ -0,0 +1,56 @@ +import WarpError from '../errors/WarpError.ts'; + +const PROFILED_FIXTURE_WITNESSED = 'profiled-fixture-witnessed'; +const AUTHORED_ONLY = 'authored-only'; + +export type ContinuumGeneratedFamilyStatusValue = + | typeof PROFILED_FIXTURE_WITNESSED + | typeof AUTHORED_ONLY; + +export const CONTINUUM_GENERATED_FAMILY_STATUSES: readonly ContinuumGeneratedFamilyStatusValue[] = Object.freeze([ + PROFILED_FIXTURE_WITNESSED, + AUTHORED_ONLY, +]); + +/** Runtime-backed readiness posture for a Continuum family in the v18 inventory. */ +export default class ContinuumGeneratedFamilyStatus { + readonly value: ContinuumGeneratedFamilyStatusValue; + + constructor(value: string) { + this.value = requireContinuumGeneratedFamilyStatus(value); + Object.freeze(this); + } + + /** Returns true when Wesley has profiled and fixture-witnessed this family. */ + isProjectionReady(): boolean { + return this.value === PROFILED_FIXTURE_WITNESSED; + } + + /** Returns true when the family is authored but not yet projection-ready. */ + isAuthoredOnly(): boolean { + return this.value === AUTHORED_ONLY; + } + + /** Returns the stable status string. */ + toString(): string { + return this.value; + } +} + +/** Validates a raw generated-family readiness status. */ +export function requireContinuumGeneratedFamilyStatus(value: string): ContinuumGeneratedFamilyStatusValue { + if (typeof value !== 'string') { + throw new WarpError( + `Continuum generated family status must be one of: ${CONTINUUM_GENERATED_FAMILY_STATUSES.join(', ')}`, + 'E_VALIDATION', + ); + } + const valid = CONTINUUM_GENERATED_FAMILY_STATUSES.find((candidate) => candidate === value); + if (valid === undefined) { + throw new WarpError( + `Continuum generated family status must be one of: ${CONTINUUM_GENERATED_FAMILY_STATUSES.join(', ')}`, + 'E_VALIDATION', + ); + } + return valid; +} diff --git a/src/domain/continuum/GitWarpReadingEnvelopePayloadFact.ts b/src/domain/continuum/GitWarpReadingEnvelopePayloadFact.ts new file mode 100644 index 000000000..e40703acc --- /dev/null +++ b/src/domain/continuum/GitWarpReadingEnvelopePayloadFact.ts @@ -0,0 +1,53 @@ +import WarpError from '../errors/WarpError.ts'; + +export type GitWarpReadingEnvelopePayloadFactFields = { + readonly payloadKind: string; + readonly payloadDigest: string; + readonly stateHash?: string; +}; + +/** Payload identity for a git-warp read result feeding a reading envelope. */ +export default class GitWarpReadingEnvelopePayloadFact { + readonly payloadKind: string; + readonly payloadDigest: string; + readonly stateHash: string | undefined; + + constructor(fields: GitWarpReadingEnvelopePayloadFactFields) { + const checkedFields = requireFields(fields); + this.payloadKind = requireNonEmptyString(checkedFields.payloadKind, 'payloadKind'); + this.payloadDigest = requireNonEmptyString(checkedFields.payloadDigest, 'payloadDigest'); + this.stateHash = optionalNonEmptyString(checkedFields.stateHash, 'stateHash'); + Object.freeze(this); + } + + /** Returns true when the payload carries a materialized state hash. */ + hasStateHash(): boolean { + return this.stateHash !== undefined; + } +} + +/** Validates the payload-fact constructor envelope. */ +function requireFields( + value: GitWarpReadingEnvelopePayloadFactFields | null | undefined, +): GitWarpReadingEnvelopePayloadFactFields { + if (value === null || value === undefined) { + throw new WarpError('GitWarpReadingEnvelopePayloadFact fields must be provided', 'E_VALIDATION'); + } + return value; +} + +/** Validates a required non-empty string. */ +function requireNonEmptyString(value: string, name: string): string { + if (typeof value !== 'string' || value.length === 0) { + throw new WarpError(`${name} must be a non-empty string`, 'E_VALIDATION'); + } + return value; +} + +/** Validates an optional non-empty string. */ +function optionalNonEmptyString(value: string | undefined, name: string): string | undefined { + if (value === undefined) { + return undefined; + } + return requireNonEmptyString(value, name); +} diff --git a/src/domain/continuum/GitWarpReadingEnvelopeSourceFacts.ts b/src/domain/continuum/GitWarpReadingEnvelopeSourceFacts.ts new file mode 100644 index 000000000..97810b077 --- /dev/null +++ b/src/domain/continuum/GitWarpReadingEnvelopeSourceFacts.ts @@ -0,0 +1,107 @@ +import ContinuumEvidencePosture from './ContinuumEvidencePosture.ts'; +import ContinuumFamilyId from './ContinuumFamilyId.ts'; +import ContinuumGeneratedFamilyInventoryEntry from './ContinuumGeneratedFamilyInventoryEntry.ts'; +import GitWarpReadingEnvelopePayloadFact from './GitWarpReadingEnvelopePayloadFact.ts'; +import WarpError from '../errors/WarpError.ts'; + +const RUNTIME_BOUNDARY_FAMILY_ID = 'runtime-boundary-family'; + +export type GitWarpReadingEnvelopeSourceFactsFields = { + readonly family: ContinuumGeneratedFamilyInventoryEntry; + readonly evidencePosture: string | ContinuumEvidencePosture; + readonly observerPlanId: string; + readonly observationRequestId: string; + readonly sourceRef: string; + readonly basisRef: string; + readonly payload: GitWarpReadingEnvelopePayloadFact; + readonly witnessRef: string; + readonly budgetStatus: string; +}; + +/** Translated git-warp source facts for a future runtime-boundary reading envelope. */ +export default class GitWarpReadingEnvelopeSourceFacts { + readonly family: ContinuumGeneratedFamilyInventoryEntry; + readonly evidencePosture: ContinuumEvidencePosture; + readonly observerPlanId: string; + readonly observationRequestId: string; + readonly sourceRef: string; + readonly basisRef: string; + readonly payload: GitWarpReadingEnvelopePayloadFact; + readonly witnessRef: string; + readonly budgetStatus: string; + + constructor(fields: GitWarpReadingEnvelopeSourceFactsFields) { + const checkedFields = requireFields(fields); + this.family = requireRuntimeBoundaryFamily(checkedFields.family); + this.evidencePosture = requireTranslatedPosture(checkedFields.evidencePosture); + this.observerPlanId = requireNonEmptyString(checkedFields.observerPlanId, 'observerPlanId'); + this.observationRequestId = requireNonEmptyString(checkedFields.observationRequestId, 'observationRequestId'); + this.sourceRef = requireNonEmptyString(checkedFields.sourceRef, 'sourceRef'); + this.basisRef = requireNonEmptyString(checkedFields.basisRef, 'basisRef'); + this.payload = requirePayload(checkedFields.payload); + this.witnessRef = requireNonEmptyString(checkedFields.witnessRef, 'witnessRef'); + this.budgetStatus = requireNonEmptyString(checkedFields.budgetStatus, 'budgetStatus'); + Object.freeze(this); + } + + /** Returns true until runtime-boundary has a generated Wesley profile and fixture. */ + requiresGeneratedProfileBeforeProjection(): boolean { + return !this.family.isProjectionReady(); + } +} + +/** Validates the source-facts constructor envelope. */ +function requireFields( + value: GitWarpReadingEnvelopeSourceFactsFields | null | undefined, +): GitWarpReadingEnvelopeSourceFactsFields { + if (value === null || value === undefined) { + throw new WarpError('GitWarpReadingEnvelopeSourceFacts fields must be provided', 'E_VALIDATION'); + } + return value; +} + +/** Requires the runtime-boundary inventory row. */ +function requireRuntimeBoundaryFamily( + value: ContinuumGeneratedFamilyInventoryEntry, +): ContinuumGeneratedFamilyInventoryEntry { + if (!(value instanceof ContinuumGeneratedFamilyInventoryEntry)) { + throw new WarpError('family must be a ContinuumGeneratedFamilyInventoryEntry', 'E_VALIDATION'); + } + if (!value.familyId.equals(new ContinuumFamilyId(RUNTIME_BOUNDARY_FAMILY_ID))) { + throw new WarpError('reading envelope source facts require runtime-boundary-family', 'E_VALIDATION'); + } + return value; +} + +/** Requires translated git-warp evidence posture. */ +function requireTranslatedPosture(value: string | ContinuumEvidencePosture): ContinuumEvidencePosture { + const posture = normalizePosture(value); + if (!posture.isTranslatedGitWarpEvidence()) { + throw new WarpError('reading envelope source facts require translated git-warp evidence', 'E_VALIDATION'); + } + return posture; +} + +/** Normalizes an evidence posture carrier. */ +function normalizePosture(value: string | ContinuumEvidencePosture): ContinuumEvidencePosture { + if (value instanceof ContinuumEvidencePosture) { + return value; + } + return new ContinuumEvidencePosture(value); +} + +/** Validates a reading payload carrier. */ +function requirePayload(value: GitWarpReadingEnvelopePayloadFact): GitWarpReadingEnvelopePayloadFact { + if (!(value instanceof GitWarpReadingEnvelopePayloadFact)) { + throw new WarpError('payload must be a GitWarpReadingEnvelopePayloadFact', 'E_VALIDATION'); + } + return value; +} + +/** Validates a required non-empty string. */ +function requireNonEmptyString(value: string, name: string): string { + if (typeof value !== 'string' || value.length === 0) { + throw new WarpError(`${name} must be a non-empty string`, 'E_VALIDATION'); + } + return value; +} diff --git a/src/domain/continuum/GitWarpTickPatchReplayCore.ts b/src/domain/continuum/GitWarpTickPatchReplayCore.ts new file mode 100644 index 000000000..9aa2710ae --- /dev/null +++ b/src/domain/continuum/GitWarpTickPatchReplayCore.ts @@ -0,0 +1,66 @@ +import VersionVector from '../crdt/VersionVector.ts'; +import WarpError from '../errors/WarpError.ts'; +import Patch from '../types/Patch.ts'; + +export type GitWarpTickPatchReplayCoreFields = { + readonly patch: Patch; + readonly patchSha: string; +}; + +/** Substrate replay facts carried by a git-warp patch at one tick. */ +export default class GitWarpTickPatchReplayCore { + readonly patchSha: string; + readonly writer: string; + readonly lamport: number; + readonly operationCount: number; + readonly contextWriterCount: number; + readonly readCount: number; + readonly writeCount: number; + + constructor(fields: GitWarpTickPatchReplayCoreFields) { + const checkedFields = requireFields(fields); + const patch = requirePatch(checkedFields.patch); + this.patchSha = requireNonEmptyString(checkedFields.patchSha, 'patchSha'); + this.writer = requireNonEmptyString(patch.writer, 'patch.writer'); + this.lamport = requireNonNegativeInteger(patch.lamport, 'patch.lamport'); + this.operationCount = patch.ops.length; + this.contextWriterCount = VersionVector.from(patch.context).size; + this.readCount = patch.reads?.length ?? 0; + this.writeCount = patch.writes?.length ?? 0; + Object.freeze(this); + } +} + +/** Validates the replay-core constructor envelope. */ +function requireFields( + value: GitWarpTickPatchReplayCoreFields | null | undefined, +): GitWarpTickPatchReplayCoreFields { + if (value === null || value === undefined) { + throw new WarpError('GitWarpTickPatchReplayCore fields must be provided', 'E_VALIDATION'); + } + return value; +} + +/** Validates a Patch carrier. */ +function requirePatch(value: Patch): Patch { + if (!(value instanceof Patch)) { + throw new WarpError('patch must be a Patch', 'E_VALIDATION'); + } + return value; +} + +/** Validates a required non-empty string. */ +function requireNonEmptyString(value: string, name: string): string { + if (typeof value !== 'string' || value.length === 0) { + throw new WarpError(`${name} must be a non-empty string`, 'E_VALIDATION'); + } + return value; +} + +/** Validates a non-negative integer. */ +function requireNonNegativeInteger(value: number, name: string): number { + if (!Number.isInteger(value) || value < 0) { + throw new WarpError(`${name} must be a non-negative integer`, 'E_VALIDATION'); + } + return value; +} diff --git a/src/domain/continuum/GitWarpTickReceiptShell.ts b/src/domain/continuum/GitWarpTickReceiptShell.ts new file mode 100644 index 000000000..955a2a09e --- /dev/null +++ b/src/domain/continuum/GitWarpTickReceiptShell.ts @@ -0,0 +1,55 @@ +import WarpError from '../errors/WarpError.ts'; +import { TickReceipt } from '../types/TickReceipt.ts'; + +export type GitWarpTickReceiptShellFields = { + readonly receipt: TickReceipt; +}; + +/** Explanatory receipt shell facts that sit above the local witness core. */ +export default class GitWarpTickReceiptShell { + readonly patchSha: string; + readonly outcomeCount: number; + readonly reasonCount: number; + + constructor(fields: GitWarpTickReceiptShellFields) { + const receipt = requireReceipt(requireFields(fields).receipt); + this.patchSha = receipt.patchSha; + this.outcomeCount = receipt.ops.length; + this.reasonCount = countReasons(receipt); + Object.freeze(this); + } + + /** Returns true when the receipt shell carries explanatory reason text. */ + hasExplanatoryReasons(): boolean { + return this.reasonCount > 0; + } +} + +/** Validates the receipt-shell constructor envelope. */ +function requireFields( + value: GitWarpTickReceiptShellFields | null | undefined, +): GitWarpTickReceiptShellFields { + if (value === null || value === undefined) { + throw new WarpError('GitWarpTickReceiptShell fields must be provided', 'E_VALIDATION'); + } + return value; +} + +/** Validates a TickReceipt carrier. */ +function requireReceipt(value: TickReceipt): TickReceipt { + if (!(value instanceof TickReceipt)) { + throw new WarpError('receipt must be a TickReceipt', 'E_VALIDATION'); + } + return value; +} + +/** Counts explanatory reason fields on receipt outcomes. */ +function countReasons(receipt: TickReceipt): number { + let reasonCount = 0; + for (const op of receipt.ops) { + if (op.reason !== undefined) { + reasonCount += 1; + } + } + return reasonCount; +} diff --git a/src/domain/continuum/GitWarpTickReceiptWitnessCore.ts b/src/domain/continuum/GitWarpTickReceiptWitnessCore.ts new file mode 100644 index 000000000..1796eb675 --- /dev/null +++ b/src/domain/continuum/GitWarpTickReceiptWitnessCore.ts @@ -0,0 +1,71 @@ +import WarpError from '../errors/WarpError.ts'; +import { TickReceipt } from '../types/TickReceipt.ts'; + +export type GitWarpTickReceiptWitnessCoreFields = { + readonly receipt: TickReceipt; +}; + +/** Purpose-minimal local witness core derived from a git-warp TickReceipt. */ +export default class GitWarpTickReceiptWitnessCore { + readonly patchSha: string; + readonly writer: string; + readonly lamport: number; + readonly outcomeCount: number; + readonly appliedCount: number; + readonly supersededCount: number; + readonly redundantCount: number; + + constructor(fields: GitWarpTickReceiptWitnessCoreFields) { + const receipt = requireReceipt(requireFields(fields).receipt); + const counts = countOutcomes(receipt); + this.patchSha = receipt.patchSha; + this.writer = receipt.writer; + this.lamport = receipt.lamport; + this.outcomeCount = receipt.ops.length; + this.appliedCount = counts.applied; + this.supersededCount = counts.superseded; + this.redundantCount = counts.redundant; + Object.freeze(this); + } +} + +type ReceiptOutcomeCounts = { + readonly applied: number; + readonly superseded: number; + readonly redundant: number; +}; + +/** Validates the witness-core constructor envelope. */ +function requireFields( + value: GitWarpTickReceiptWitnessCoreFields | null | undefined, +): GitWarpTickReceiptWitnessCoreFields { + if (value === null || value === undefined) { + throw new WarpError('GitWarpTickReceiptWitnessCore fields must be provided', 'E_VALIDATION'); + } + return value; +} + +/** Validates a TickReceipt carrier. */ +function requireReceipt(value: TickReceipt): TickReceipt { + if (!(value instanceof TickReceipt)) { + throw new WarpError('receipt must be a TickReceipt', 'E_VALIDATION'); + } + return value; +} + +/** Counts validated TickReceipt outcomes. */ +function countOutcomes(receipt: TickReceipt): ReceiptOutcomeCounts { + let applied = 0; + let superseded = 0; + let redundant = 0; + for (const op of receipt.ops) { + if (op.result === 'applied') { + applied += 1; + } else if (op.result === 'superseded') { + superseded += 1; + } else if (op.result === 'redundant') { + redundant += 1; + } + } + return Object.freeze({ applied, superseded, redundant }); +} diff --git a/src/domain/continuum/GitWarpTickWitnessLadder.ts b/src/domain/continuum/GitWarpTickWitnessLadder.ts new file mode 100644 index 000000000..17dd212ae --- /dev/null +++ b/src/domain/continuum/GitWarpTickWitnessLadder.ts @@ -0,0 +1,75 @@ +import GitWarpTickPatchReplayCore from './GitWarpTickPatchReplayCore.ts'; +import GitWarpTickReceiptShell from './GitWarpTickReceiptShell.ts'; +import GitWarpTickReceiptWitnessCore from './GitWarpTickReceiptWitnessCore.ts'; +import WarpError from '../errors/WarpError.ts'; +import Patch from '../types/Patch.ts'; +import { TickReceipt } from '../types/TickReceipt.ts'; + +export type GitWarpTickWitnessLadderFields = { + readonly patch: Patch; + readonly patchSha: string; + readonly receipt: TickReceipt; +}; + +/** Validated witness ladder for one git-warp patch tick and receipt. */ +export default class GitWarpTickWitnessLadder { + readonly replayCore: GitWarpTickPatchReplayCore; + readonly witnessCore: GitWarpTickReceiptWitnessCore; + readonly receiptShell: GitWarpTickReceiptShell; + + constructor(fields: GitWarpTickWitnessLadderFields) { + const checkedFields = requireFields(fields); + const patch = requirePatch(checkedFields.patch); + const receipt = requireReceipt(checkedFields.receipt); + this.replayCore = new GitWarpTickPatchReplayCore({ + patch, + patchSha: checkedFields.patchSha, + }); + this.witnessCore = new GitWarpTickReceiptWitnessCore({ receipt }); + this.receiptShell = new GitWarpTickReceiptShell({ receipt }); + assertAlignedTick(this.replayCore, this.witnessCore); + Object.freeze(this); + } +} + +/** Validates the ladder constructor envelope. */ +function requireFields( + value: GitWarpTickWitnessLadderFields | null | undefined, +): GitWarpTickWitnessLadderFields { + if (value === null || value === undefined) { + throw new WarpError('GitWarpTickWitnessLadder fields must be provided', 'E_VALIDATION'); + } + return value; +} + +/** Validates a Patch carrier. */ +function requirePatch(value: Patch): Patch { + if (!(value instanceof Patch)) { + throw new WarpError('patch must be a Patch', 'E_VALIDATION'); + } + return value; +} + +/** Validates a TickReceipt carrier. */ +function requireReceipt(value: TickReceipt): TickReceipt { + if (!(value instanceof TickReceipt)) { + throw new WarpError('receipt must be a TickReceipt', 'E_VALIDATION'); + } + return value; +} + +/** Requires replay and receipt facts to describe the same tick. */ +function assertAlignedTick( + replayCore: GitWarpTickPatchReplayCore, + witnessCore: GitWarpTickReceiptWitnessCore, +): void { + if (replayCore.patchSha !== witnessCore.patchSha) { + throw new WarpError('tick witness ladder patch SHA must match receipt patch SHA', 'E_VALIDATION'); + } + if (replayCore.writer !== witnessCore.writer) { + throw new WarpError('tick witness ladder writer must match receipt writer', 'E_VALIDATION'); + } + if (replayCore.lamport !== witnessCore.lamport) { + throw new WarpError('tick witness ladder Lamport must match receipt Lamport', 'E_VALIDATION'); + } +} diff --git a/src/domain/continuum/GitWarpWitnessedSuffixPatchFact.ts b/src/domain/continuum/GitWarpWitnessedSuffixPatchFact.ts new file mode 100644 index 000000000..f0867a559 --- /dev/null +++ b/src/domain/continuum/GitWarpWitnessedSuffixPatchFact.ts @@ -0,0 +1,51 @@ +import WarpError from '../errors/WarpError.ts'; + +export type GitWarpWitnessedSuffixPatchFactFields = { + readonly writerId: string; + readonly patchSha: string; + readonly lamport: number; + readonly operationCount: number; +}; + +/** One ordered git-warp patch reference inside a witnessed suffix source fact. */ +export default class GitWarpWitnessedSuffixPatchFact { + readonly writerId: string; + readonly patchSha: string; + readonly lamport: number; + readonly operationCount: number; + + constructor(fields: GitWarpWitnessedSuffixPatchFactFields) { + const checkedFields = requireFields(fields); + this.writerId = requireNonEmptyString(checkedFields.writerId, 'writerId'); + this.patchSha = requireNonEmptyString(checkedFields.patchSha, 'patchSha'); + this.lamport = requireNonNegativeInteger(checkedFields.lamport, 'lamport'); + this.operationCount = requireNonNegativeInteger(checkedFields.operationCount, 'operationCount'); + Object.freeze(this); + } +} + +/** Validates the patch-fact constructor envelope. */ +function requireFields( + value: GitWarpWitnessedSuffixPatchFactFields | null | undefined, +): GitWarpWitnessedSuffixPatchFactFields { + if (value === null || value === undefined) { + throw new WarpError('GitWarpWitnessedSuffixPatchFact fields must be provided', 'E_VALIDATION'); + } + return value; +} + +/** Validates a required non-empty string. */ +function requireNonEmptyString(value: string, name: string): string { + if (typeof value !== 'string' || value.length === 0) { + throw new WarpError(`${name} must be a non-empty string`, 'E_VALIDATION'); + } + return value; +} + +/** Validates a non-negative integer. */ +function requireNonNegativeInteger(value: number, name: string): number { + if (!Number.isInteger(value) || value < 0) { + throw new WarpError(`${name} must be a non-negative integer`, 'E_VALIDATION'); + } + return value; +} diff --git a/src/domain/continuum/GitWarpWitnessedSuffixSourceFacts.ts b/src/domain/continuum/GitWarpWitnessedSuffixSourceFacts.ts new file mode 100644 index 000000000..bb5b9c77a --- /dev/null +++ b/src/domain/continuum/GitWarpWitnessedSuffixSourceFacts.ts @@ -0,0 +1,163 @@ +import ContinuumEvidencePosture from './ContinuumEvidencePosture.ts'; +import ContinuumFamilyId from './ContinuumFamilyId.ts'; +import ContinuumGeneratedFamilyInventoryEntry from './ContinuumGeneratedFamilyInventoryEntry.ts'; +import GitWarpWitnessedSuffixPatchFact from './GitWarpWitnessedSuffixPatchFact.ts'; +import WarpError from '../errors/WarpError.ts'; + +const RUNTIME_BOUNDARY_FAMILY_ID = 'runtime-boundary-family'; + +export type GitWarpWitnessedSuffixSourceFactsFields = { + readonly family: ContinuumGeneratedFamilyInventoryEntry; + readonly evidencePosture: string | ContinuumEvidencePosture; + readonly graphName: string; + readonly sourceFrontierRef: string; + readonly basisFrontierRef: string; + readonly targetFrontierRef: string; + readonly patches: readonly GitWarpWitnessedSuffixPatchFact[]; + readonly witnessRef: string; + readonly bundleDigest: string; +}; + +/** Translated git-warp source facts for a future runtime-boundary suffix shell. */ +export default class GitWarpWitnessedSuffixSourceFacts { + readonly family: ContinuumGeneratedFamilyInventoryEntry; + readonly evidencePosture: ContinuumEvidencePosture; + readonly graphName: string; + readonly sourceFrontierRef: string; + readonly basisFrontierRef: string; + readonly targetFrontierRef: string; + readonly patches: readonly GitWarpWitnessedSuffixPatchFact[]; + readonly patchCount: number; + readonly witnessRef: string; + readonly bundleDigest: string; + + constructor(fields: GitWarpWitnessedSuffixSourceFactsFields) { + const checkedFields = requireFields(fields); + this.family = requireRuntimeBoundaryFamily(checkedFields.family); + this.evidencePosture = requireTranslatedPosture(checkedFields.evidencePosture); + this.graphName = requireNonEmptyString(checkedFields.graphName, 'graphName'); + this.sourceFrontierRef = requireNonEmptyString(checkedFields.sourceFrontierRef, 'sourceFrontierRef'); + this.basisFrontierRef = requireNonEmptyString(checkedFields.basisFrontierRef, 'basisFrontierRef'); + this.targetFrontierRef = requireNonEmptyString(checkedFields.targetFrontierRef, 'targetFrontierRef'); + this.patches = freezePatchFacts(checkedFields.patches); + this.patchCount = this.patches.length; + this.witnessRef = requireNonEmptyString(checkedFields.witnessRef, 'witnessRef'); + this.bundleDigest = requireNonEmptyString(checkedFields.bundleDigest, 'bundleDigest'); + Object.freeze(this); + } + + /** Returns true until runtime-boundary has a generated Wesley profile and fixture. */ + requiresGeneratedProfileBeforeProjection(): boolean { + return !this.family.isProjectionReady(); + } +} + +/** Validates the source-facts constructor envelope. */ +function requireFields( + value: GitWarpWitnessedSuffixSourceFactsFields | null | undefined, +): GitWarpWitnessedSuffixSourceFactsFields { + if (value === null || value === undefined) { + throw new WarpError('GitWarpWitnessedSuffixSourceFacts fields must be provided', 'E_VALIDATION'); + } + return value; +} + +/** Requires the runtime-boundary inventory row. */ +function requireRuntimeBoundaryFamily( + value: ContinuumGeneratedFamilyInventoryEntry, +): ContinuumGeneratedFamilyInventoryEntry { + if (!(value instanceof ContinuumGeneratedFamilyInventoryEntry)) { + throw new WarpError('family must be a ContinuumGeneratedFamilyInventoryEntry', 'E_VALIDATION'); + } + if (!value.familyId.equals(new ContinuumFamilyId(RUNTIME_BOUNDARY_FAMILY_ID))) { + throw new WarpError('witnessed suffix source facts require runtime-boundary-family', 'E_VALIDATION'); + } + return value; +} + +/** Requires translated git-warp evidence posture. */ +function requireTranslatedPosture(value: string | ContinuumEvidencePosture): ContinuumEvidencePosture { + const posture = normalizePosture(value); + if (!posture.isTranslatedGitWarpEvidence()) { + throw new WarpError('witnessed suffix source facts require translated git-warp evidence', 'E_VALIDATION'); + } + return posture; +} + +/** Normalizes an evidence posture carrier. */ +function normalizePosture(value: string | ContinuumEvidencePosture): ContinuumEvidencePosture { + if (value instanceof ContinuumEvidencePosture) { + return value; + } + return new ContinuumEvidencePosture(value); +} + +/** Freezes and validates ordered suffix patch facts. */ +function freezePatchFacts( + values: readonly GitWarpWitnessedSuffixPatchFact[], +): readonly GitWarpWitnessedSuffixPatchFact[] { + if (!Array.isArray(values) || values.length === 0) { + throw new WarpError('witnessed suffix source facts require at least one patch', 'E_VALIDATION'); + } + const checkedValues: GitWarpWitnessedSuffixPatchFact[] = []; + let previous: GitWarpWitnessedSuffixPatchFact | undefined; + for (const value of values) { + const patchFact = requirePatchFact(value); + requireCanonicalPatchOrder(previous, patchFact); + checkedValues.push(patchFact); + previous = patchFact; + } + return Object.freeze(checkedValues); +} + +/** Requires a runtime-backed suffix patch fact. */ +function requirePatchFact(value: GitWarpWitnessedSuffixPatchFact): GitWarpWitnessedSuffixPatchFact { + if (!(value instanceof GitWarpWitnessedSuffixPatchFact)) { + throw new WarpError('patches must be GitWarpWitnessedSuffixPatchFact values', 'E_VALIDATION'); + } + return value; +} + +/** Requires adjacent suffix patch facts to preserve canonical order. */ +function requireCanonicalPatchOrder( + previous: GitWarpWitnessedSuffixPatchFact | undefined, + current: GitWarpWitnessedSuffixPatchFact, +): void { + if (previous !== undefined && comparePatchOrder(previous, current) > 0) { + throw new WarpError('patches must be ordered by lamport, writerId, and patchSha', 'E_VALIDATION'); + } +} + +/** Compares suffix patch facts by deterministic causal-public order. */ +function comparePatchOrder( + left: GitWarpWitnessedSuffixPatchFact, + right: GitWarpWitnessedSuffixPatchFact, +): number { + if (left.lamport !== right.lamport) { + return left.lamport - right.lamport; + } + const writerComparison = compareStrings(left.writerId, right.writerId); + if (writerComparison !== 0) { + return writerComparison; + } + return compareStrings(left.patchSha, right.patchSha); +} + +/** Compares ASCII protocol identifiers without locale-sensitive collation. */ +function compareStrings(left: string, right: string): number { + if (left < right) { + return -1; + } + if (left > right) { + return 1; + } + return 0; +} + +/** Validates a required non-empty string. */ +function requireNonEmptyString(value: string, name: string): string { + if (typeof value !== 'string' || value.length === 0) { + throw new WarpError(`${name} must be a non-empty string`, 'E_VALIDATION'); + } + return value; +} diff --git a/src/domain/continuum/createCurrentContinuumGeneratedFamilyInventory.ts b/src/domain/continuum/createCurrentContinuumGeneratedFamilyInventory.ts new file mode 100644 index 000000000..ad87161df --- /dev/null +++ b/src/domain/continuum/createCurrentContinuumGeneratedFamilyInventory.ts @@ -0,0 +1,70 @@ +import ContinuumGeneratedFamilyInventory from './ContinuumGeneratedFamilyInventory.ts'; +import ContinuumGeneratedFamilyInventoryEntry from './ContinuumGeneratedFamilyInventoryEntry.ts'; + +const CONTINUUM_FAMILY_VERSION = '0.1.0'; +const PROFILED_FIXTURE_WITNESSED = 'profiled-fixture-witnessed'; +const AUTHORED_ONLY = 'authored-only'; + +/** Builds the current v18 Continuum/Wesley generated-family readiness inventory. */ +export default function createCurrentContinuumGeneratedFamilyInventory(): ContinuumGeneratedFamilyInventory { + return new ContinuumGeneratedFamilyInventory({ + entries: [ + receiptFamilyEntry(), + settlementFamilyEntry(), + neighborhoodCoreFamilyEntry(), + runtimeBoundaryFamilyEntry(), + ], + }); +} + +/** Returns the current receipt-family readiness row. */ +function receiptFamilyEntry(): ContinuumGeneratedFamilyInventoryEntry { + return new ContinuumGeneratedFamilyInventoryEntry({ + familyId: 'receipt-family', + version: CONTINUUM_FAMILY_VERSION, + authoredSchemaPath: 'schemas/continuum-receipt-family.graphql', + status: PROFILED_FIXTURE_WITNESSED, + gitWarpSourceFacts: 'TickReceipt, DeliveryObservation, ReceiptShard', + warpTtdConsumerNeed: 'receipt shell summary and delivery facts', + openCut: 'replace fixture vectors with live sibling-runtime receipt publication', + }); +} + +/** Returns the current settlement-family readiness row. */ +function settlementFamilyEntry(): ContinuumGeneratedFamilyInventoryEntry { + return new ContinuumGeneratedFamilyInventoryEntry({ + familyId: 'settlement-family', + version: CONTINUUM_FAMILY_VERSION, + authoredSchemaPath: 'schemas/continuum-settlement-family.graphql', + status: PROFILED_FIXTURE_WITNESSED, + gitWarpSourceFacts: 'PatchDiff, conflict traces, import candidates, writer frontier state', + warpTtdConsumerNeed: 'merge and import inspection', + openCut: 'prove live settlement values from git-warp suffix/import flows', + }); +} + +/** Returns the current neighborhood-core-family readiness row. */ +function neighborhoodCoreFamilyEntry(): ContinuumGeneratedFamilyInventoryEntry { + return new ContinuumGeneratedFamilyInventoryEntry({ + familyId: 'neighborhood-core-family', + version: CONTINUUM_FAMILY_VERSION, + authoredSchemaPath: 'schemas/continuum-neighborhood-core-family.graphql', + status: AUTHORED_ONLY, + gitWarpSourceFacts: 'graph name, writer refs, frontiers, worldline participation facts', + warpTtdConsumerNeed: 'participant catalog and neighborhood focus', + openCut: 'add Wesley profile and fixture witness before projection-ready support', + }); +} + +/** Returns the current runtime-boundary-family readiness row. */ +function runtimeBoundaryFamilyEntry(): ContinuumGeneratedFamilyInventoryEntry { + return new ContinuumGeneratedFamilyInventoryEntry({ + familyId: 'runtime-boundary-family', + version: CONTINUUM_FAMILY_VERSION, + authoredSchemaPath: 'schemas/continuum-runtime-boundary-family.graphql', + status: AUTHORED_ONLY, + gitWarpSourceFacts: 'read basis, materialize results, patch suffixes, import outcomes', + warpTtdConsumerNeed: 'reading envelopes, suffix shells, and admission-chain facts', + openCut: 'add Wesley profile and live witnessed suffix exchange/admission proof', + }); +} diff --git a/test/unit/domain/continuum/ContinuumGeneratedFamilyInventory.test.ts b/test/unit/domain/continuum/ContinuumGeneratedFamilyInventory.test.ts new file mode 100644 index 000000000..edbb8f541 --- /dev/null +++ b/test/unit/domain/continuum/ContinuumGeneratedFamilyInventory.test.ts @@ -0,0 +1,127 @@ +import { describe, expect, it } from 'vitest'; + +import ContinuumFamilyId from '../../../../src/domain/continuum/ContinuumFamilyId.ts'; +import ContinuumGeneratedFamilyInventory from '../../../../src/domain/continuum/ContinuumGeneratedFamilyInventory.ts'; +import ContinuumGeneratedFamilyInventoryEntry from '../../../../src/domain/continuum/ContinuumGeneratedFamilyInventoryEntry.ts'; +import ContinuumGeneratedFamilyStatus from '../../../../src/domain/continuum/ContinuumGeneratedFamilyStatus.ts'; +import createCurrentContinuumGeneratedFamilyInventory + from '../../../../src/domain/continuum/createCurrentContinuumGeneratedFamilyInventory.ts'; +import WarpError from '../../../../src/domain/errors/WarpError.ts'; + +const RECEIPT_SCHEMA_PATH = 'schemas/continuum-receipt-family.graphql'; +const SETTLEMENT_SCHEMA_PATH = 'schemas/continuum-settlement-family.graphql'; +const NEIGHBORHOOD_SCHEMA_PATH = 'schemas/continuum-neighborhood-core-family.graphql'; +const RUNTIME_BOUNDARY_SCHEMA_PATH = 'schemas/continuum-runtime-boundary-family.graphql'; + +function makeEntry(fields: { + readonly familyId: string; + readonly authoredSchemaPath: string; + readonly status?: string; +}): ContinuumGeneratedFamilyInventoryEntry { + return new ContinuumGeneratedFamilyInventoryEntry({ + familyId: fields.familyId, + version: '0.1.0', + authoredSchemaPath: fields.authoredSchemaPath, + status: fields.status ?? 'profiled-fixture-witnessed', + gitWarpSourceFacts: 'test source facts', + warpTtdConsumerNeed: 'test consumer need', + openCut: 'test open cut', + }); +} + +function makeCompleteEntries(): readonly ContinuumGeneratedFamilyInventoryEntry[] { + return Object.freeze([ + makeEntry({ familyId: 'receipt-family', authoredSchemaPath: RECEIPT_SCHEMA_PATH }), + makeEntry({ familyId: 'settlement-family', authoredSchemaPath: SETTLEMENT_SCHEMA_PATH }), + makeEntry({ + familyId: 'neighborhood-core-family', + authoredSchemaPath: NEIGHBORHOOD_SCHEMA_PATH, + status: 'authored-only', + }), + makeEntry({ + familyId: 'runtime-boundary-family', + authoredSchemaPath: RUNTIME_BOUNDARY_SCHEMA_PATH, + status: 'authored-only', + }), + ]); +} + +describe('ContinuumGeneratedFamilyInventory', () => { + it('records current v18 generated-family readiness for every Continuum family', () => { + const inventory = createCurrentContinuumGeneratedFamilyInventory(); + + expect(inventory.entries.map((entry) => entry.familyId.toString())).toEqual([ + 'receipt-family', + 'settlement-family', + 'neighborhood-core-family', + 'runtime-boundary-family', + ]); + expect(inventory.requireEntry('receipt-family').status.isProjectionReady()).toBe(true); + expect(inventory.requireEntry('settlement-family').status.isProjectionReady()).toBe(true); + expect(inventory.requireEntry('neighborhood-core-family').status.isProjectionReady()).toBe(false); + expect(inventory.requireEntry('runtime-boundary-family').status.isProjectionReady()).toBe(false); + }); + + it('requires projection-ready status before later slices emit source facts', () => { + const inventory = createCurrentContinuumGeneratedFamilyInventory(); + + expect(inventory.requireProjectionReady('receipt-family').familyId.toString()).toBe('receipt-family'); + expect(inventory.requireProjectionReady(new ContinuumFamilyId('settlement-family')).familyId.toString()) + .toBe('settlement-family'); + expect(() => inventory.requireProjectionReady('runtime-boundary-family')).toThrow(WarpError); + }); + + it('classifies authored-only generated-family status explicitly', () => { + const authoredOnly = new ContinuumGeneratedFamilyStatus('authored-only'); + const profiled = new ContinuumGeneratedFamilyStatus('profiled-fixture-witnessed'); + + expect(authoredOnly.isAuthoredOnly()).toBe(true); + expect(authoredOnly.isProjectionReady()).toBe(false); + expect(profiled.isAuthoredOnly()).toBe(false); + expect(profiled.isProjectionReady()).toBe(true); + }); + + it('rejects unknown family lookups before returning an inventory row', () => { + const inventory = createCurrentContinuumGeneratedFamilyInventory(); + + expect(() => inventory.requireEntry('not-a-continuum-family')).toThrow(WarpError); + }); + + it('rejects inventories that are missing current Continuum families', () => { + const entries = makeCompleteEntries().filter((entry) => entry.familyId.toString() !== 'runtime-boundary-family'); + + expect(() => new ContinuumGeneratedFamilyInventory({ entries })).toThrow(WarpError); + }); + + it('rejects duplicate family entries', () => { + const entries = Object.freeze([ + ...makeCompleteEntries(), + makeEntry({ familyId: 'receipt-family', authoredSchemaPath: RECEIPT_SCHEMA_PATH }), + ]); + + expect(() => new ContinuumGeneratedFamilyInventory({ entries })).toThrow(WarpError); + }); + + it('rejects invalid status and blank evidence fields', () => { + expect(() => new ContinuumGeneratedFamilyStatus('not-ready')).toThrow(WarpError); + expect(() => new ContinuumGeneratedFamilyInventoryEntry({ + familyId: 'receipt-family', + version: '0.1.0', + authoredSchemaPath: '', + status: 'profiled-fixture-witnessed', + gitWarpSourceFacts: 'test source facts', + warpTtdConsumerNeed: 'test consumer need', + openCut: 'test open cut', + })).toThrow(WarpError); + + expect(() => new ContinuumGeneratedFamilyInventoryEntry({ + familyId: 'receipt-family', + version: '0.1.0', + authoredSchemaPath: ' ', + status: 'profiled-fixture-witnessed', + gitWarpSourceFacts: 'test source facts', + warpTtdConsumerNeed: 'test consumer need', + openCut: 'test open cut', + })).toThrow(WarpError); + }); +}); diff --git a/test/unit/domain/continuum/GitWarpReadingEnvelopeSourceFacts.test.ts b/test/unit/domain/continuum/GitWarpReadingEnvelopeSourceFacts.test.ts new file mode 100644 index 000000000..85ac0260e --- /dev/null +++ b/test/unit/domain/continuum/GitWarpReadingEnvelopeSourceFacts.test.ts @@ -0,0 +1,82 @@ +import { describe, expect, it } from 'vitest'; + +import ContinuumEvidencePosture from '../../../../src/domain/continuum/ContinuumEvidencePosture.ts'; +import GitWarpReadingEnvelopePayloadFact + from '../../../../src/domain/continuum/GitWarpReadingEnvelopePayloadFact.ts'; +import GitWarpReadingEnvelopeSourceFacts + from '../../../../src/domain/continuum/GitWarpReadingEnvelopeSourceFacts.ts'; +import createCurrentContinuumGeneratedFamilyInventory + from '../../../../src/domain/continuum/createCurrentContinuumGeneratedFamilyInventory.ts'; +import WarpError from '../../../../src/domain/errors/WarpError.ts'; + +function makePayload(): GitWarpReadingEnvelopePayloadFact { + return new GitWarpReadingEnvelopePayloadFact({ + payloadKind: 'materialized-state', + payloadDigest: 'sha256:reading-payload', + stateHash: 'state:abc123', + }); +} + +function makeSourceFacts(fields: { + readonly familyId?: string; + readonly posture?: string | ContinuumEvidencePosture; + readonly payload?: GitWarpReadingEnvelopePayloadFact; +} = {}): GitWarpReadingEnvelopeSourceFacts { + const inventory = createCurrentContinuumGeneratedFamilyInventory(); + return new GitWarpReadingEnvelopeSourceFacts({ + family: inventory.requireEntry(fields.familyId ?? 'runtime-boundary-family'), + evidencePosture: fields.posture ?? 'translated-git-warp-evidence', + observerPlanId: 'observer-plan:live-materialize', + observationRequestId: 'observation-request:001', + sourceRef: 'graph:demo/writer:writer-a', + basisRef: 'frontier:writer-a:7', + payload: fields.payload ?? makePayload(), + witnessRef: 'receipt:e'.concat('e'.repeat(39)), + budgetStatus: 'budget-unreported', + }); +} + +describe('GitWarpReadingEnvelopeSourceFacts', () => { + it('records translated runtime-boundary reading-envelope source facts', () => { + const facts = makeSourceFacts(); + + expect(facts.family.familyId.toString()).toBe('runtime-boundary-family'); + expect(facts.family.status.toString()).toBe('authored-only'); + expect(facts.evidencePosture.isTranslatedGitWarpEvidence()).toBe(true); + expect(facts.payload).toBeInstanceOf(GitWarpReadingEnvelopePayloadFact); + expect(facts.payload.payloadKind).toBe('materialized-state'); + expect(facts.payload.hasStateHash()).toBe(true); + expect(facts.requiresGeneratedProfileBeforeProjection()).toBe(true); + }); + + it('rejects source facts for a non-runtime-boundary family', () => { + expect(() => makeSourceFacts({ familyId: 'receipt-family' })).toThrow(WarpError); + }); + + it('rejects native or unproven evidence posture for translated git-warp readings', () => { + expect(() => makeSourceFacts({ + posture: new ContinuumEvidencePosture('native-continuum-evidence'), + })).toThrow(WarpError); + + expect(() => makeSourceFacts({ posture: 'unproven-continuum-shape' })).toThrow(WarpError); + }); + + it('rejects blank payload and source-fact fields', () => { + expect(() => new GitWarpReadingEnvelopePayloadFact({ + payloadKind: '', + payloadDigest: 'sha256:reading-payload', + })).toThrow(WarpError); + + expect(() => new GitWarpReadingEnvelopeSourceFacts({ + family: createCurrentContinuumGeneratedFamilyInventory().requireEntry('runtime-boundary-family'), + evidencePosture: 'translated-git-warp-evidence', + observerPlanId: '', + observationRequestId: 'observation-request:001', + sourceRef: 'graph:demo/writer:writer-a', + basisRef: 'frontier:writer-a:7', + payload: makePayload(), + witnessRef: 'receipt:e'.concat('e'.repeat(39)), + budgetStatus: 'budget-unreported', + })).toThrow(WarpError); + }); +}); diff --git a/test/unit/domain/continuum/GitWarpTickWitnessLadder.test.ts b/test/unit/domain/continuum/GitWarpTickWitnessLadder.test.ts new file mode 100644 index 000000000..e1d6969c7 --- /dev/null +++ b/test/unit/domain/continuum/GitWarpTickWitnessLadder.test.ts @@ -0,0 +1,128 @@ +import { describe, expect, it } from 'vitest'; + +import { Dot } from '../../../../src/domain/crdt/Dot.ts'; +import VersionVector from '../../../../src/domain/crdt/VersionVector.ts'; +import GitWarpTickPatchReplayCore from '../../../../src/domain/continuum/GitWarpTickPatchReplayCore.ts'; +import GitWarpTickReceiptShell from '../../../../src/domain/continuum/GitWarpTickReceiptShell.ts'; +import GitWarpTickReceiptWitnessCore from '../../../../src/domain/continuum/GitWarpTickReceiptWitnessCore.ts'; +import GitWarpTickWitnessLadder from '../../../../src/domain/continuum/GitWarpTickWitnessLadder.ts'; +import WarpError from '../../../../src/domain/errors/WarpError.ts'; +import Patch from '../../../../src/domain/types/Patch.ts'; +import { TickReceipt } from '../../../../src/domain/types/TickReceipt.ts'; +import NodeAdd from '../../../../src/domain/types/ops/NodeAdd.ts'; + +const PATCH_SHA = 'e'.repeat(40); +const WRITER_ID = 'writer-a'; +const LAMPORT = 7; + +function makePatch(): Patch { + const context = VersionVector.empty(); + context.set('writer-prior', 4); + return new Patch({ + schema: 3, + writer: WRITER_ID, + lamport: LAMPORT, + context, + ops: [new NodeAdd('node:a', new Dot(WRITER_ID, LAMPORT))], + reads: ['node:read'], + writes: ['node:a'], + }); +} + +function makeReceipt(fields: { + readonly patchSha?: string; + readonly writer?: string; + readonly lamport?: number; +} = {}): TickReceipt { + return new TickReceipt({ + patchSha: fields.patchSha ?? PATCH_SHA, + writer: fields.writer ?? WRITER_ID, + lamport: fields.lamport ?? LAMPORT, + ops: [ + { + op: 'NodeAdd', + target: 'node:a', + result: 'applied', + reason: 'node admitted', + }, + { + op: 'PropSet', + target: 'node:a\x00name', + result: 'redundant', + }, + ], + }); +} + +describe('GitWarpTickWitnessLadder', () => { + it('splits patch replay core, receipt witness core, and receipt shell facts', () => { + const ladder = new GitWarpTickWitnessLadder({ + patch: makePatch(), + patchSha: PATCH_SHA, + receipt: makeReceipt(), + }); + + expect(ladder.replayCore).toBeInstanceOf(GitWarpTickPatchReplayCore); + expect(ladder.replayCore.patchSha).toBe(PATCH_SHA); + expect(ladder.replayCore.writer).toBe(WRITER_ID); + expect(ladder.replayCore.lamport).toBe(LAMPORT); + expect(ladder.replayCore.operationCount).toBe(1); + expect(ladder.replayCore.contextWriterCount).toBe(1); + expect(ladder.replayCore.readCount).toBe(1); + expect(ladder.replayCore.writeCount).toBe(1); + + expect(ladder.witnessCore).toBeInstanceOf(GitWarpTickReceiptWitnessCore); + expect(ladder.witnessCore.outcomeCount).toBe(2); + expect(ladder.witnessCore.appliedCount).toBe(1); + expect(ladder.witnessCore.supersededCount).toBe(0); + expect(ladder.witnessCore.redundantCount).toBe(1); + + expect(ladder.receiptShell).toBeInstanceOf(GitWarpTickReceiptShell); + expect(ladder.receiptShell.outcomeCount).toBe(2); + expect(ladder.receiptShell.reasonCount).toBe(1); + expect(ladder.receiptShell.hasExplanatoryReasons()).toBe(true); + }); + + it('rejects patch and receipt values that do not describe the same tick', () => { + const patch = makePatch(); + + expect(() => new GitWarpTickWitnessLadder({ + patch, + patchSha: PATCH_SHA, + receipt: makeReceipt({ patchSha: 'f'.repeat(40) }), + })).toThrow(WarpError); + + expect(() => new GitWarpTickWitnessLadder({ + patch, + patchSha: PATCH_SHA, + receipt: makeReceipt({ writer: 'writer-b' }), + })).toThrow(WarpError); + + expect(() => new GitWarpTickWitnessLadder({ + patch, + patchSha: PATCH_SHA, + receipt: makeReceipt({ lamport: 8 }), + })).toThrow(WarpError); + }); + + it('rejects missing constructor carriers at runtime', () => { + expect(() => new GitWarpTickWitnessLadder( + // @ts-expect-error runtime guard for JavaScript callers + undefined, + )).toThrow(WarpError); + + expect(() => new GitWarpTickWitnessLadder({ + // @ts-expect-error runtime guard for JavaScript callers + patch: undefined, + patchSha: PATCH_SHA, + receipt: makeReceipt(), + })).toThrow(WarpError); + + expect(() => new GitWarpTickWitnessLadder({ + patch: makePatch(), + patchSha: PATCH_SHA, + // @ts-expect-error runtime guard for JavaScript callers + receipt: undefined, + })).toThrow(WarpError); + }); +}); diff --git a/test/unit/domain/continuum/GitWarpWitnessedSuffixSourceFacts.test.ts b/test/unit/domain/continuum/GitWarpWitnessedSuffixSourceFacts.test.ts new file mode 100644 index 000000000..e2b7815f4 --- /dev/null +++ b/test/unit/domain/continuum/GitWarpWitnessedSuffixSourceFacts.test.ts @@ -0,0 +1,88 @@ +import { describe, expect, it } from 'vitest'; + +import ContinuumEvidencePosture from '../../../../src/domain/continuum/ContinuumEvidencePosture.ts'; +import GitWarpWitnessedSuffixPatchFact + from '../../../../src/domain/continuum/GitWarpWitnessedSuffixPatchFact.ts'; +import GitWarpWitnessedSuffixSourceFacts + from '../../../../src/domain/continuum/GitWarpWitnessedSuffixSourceFacts.ts'; +import createCurrentContinuumGeneratedFamilyInventory + from '../../../../src/domain/continuum/createCurrentContinuumGeneratedFamilyInventory.ts'; +import WarpError from '../../../../src/domain/errors/WarpError.ts'; + +function makePatchFact(fields: { + readonly writerId?: string; + readonly patchSha?: string; + readonly lamport?: number; + readonly operationCount?: number; +} = {}): GitWarpWitnessedSuffixPatchFact { + return new GitWarpWitnessedSuffixPatchFact({ + writerId: fields.writerId ?? 'writer-a', + patchSha: fields.patchSha ?? 'a'.repeat(40), + lamport: fields.lamport ?? 7, + operationCount: fields.operationCount ?? 2, + }); +} + +function makeSourceFacts(fields: { + readonly familyId?: string; + readonly posture?: string | ContinuumEvidencePosture; + readonly patches?: readonly GitWarpWitnessedSuffixPatchFact[]; +} = {}): GitWarpWitnessedSuffixSourceFacts { + const inventory = createCurrentContinuumGeneratedFamilyInventory(); + return new GitWarpWitnessedSuffixSourceFacts({ + family: inventory.requireEntry(fields.familyId ?? 'runtime-boundary-family'), + evidencePosture: fields.posture ?? 'translated-git-warp-evidence', + graphName: 'demo', + sourceFrontierRef: 'frontier:remote:writer-a:7', + basisFrontierRef: 'frontier:local:writer-a:4', + targetFrontierRef: 'frontier:target:writer-a:7', + patches: fields.patches ?? [makePatchFact()], + witnessRef: 'receipt:a'.concat('a'.repeat(39)), + bundleDigest: 'sha256:suffix-bundle', + }); +} + +describe('GitWarpWitnessedSuffixSourceFacts', () => { + it('records translated runtime-boundary witnessed-suffix source facts', () => { + const facts = makeSourceFacts(); + + expect(facts.family.familyId.toString()).toBe('runtime-boundary-family'); + expect(facts.family.status.toString()).toBe('authored-only'); + expect(facts.evidencePosture.isTranslatedGitWarpEvidence()).toBe(true); + expect(facts.patches).toEqual([makePatchFact()]); + expect(facts.patchCount).toBe(1); + expect(facts.requiresGeneratedProfileBeforeProjection()).toBe(true); + }); + + it('rejects source facts for a non-runtime-boundary family', () => { + expect(() => makeSourceFacts({ familyId: 'settlement-family' })).toThrow(WarpError); + }); + + it('rejects native or unproven evidence posture for translated git-warp suffixes', () => { + expect(() => makeSourceFacts({ + posture: new ContinuumEvidencePosture('native-continuum-evidence'), + })).toThrow(WarpError); + + expect(() => makeSourceFacts({ posture: 'unproven-continuum-shape' })).toThrow(WarpError); + }); + + it('rejects empty suffix patch lists and invalid patch facts', () => { + expect(() => makeSourceFacts({ patches: [] })).toThrow(WarpError); + + expect(() => new GitWarpWitnessedSuffixPatchFact({ + writerId: 'writer-a', + patchSha: '', + lamport: 7, + operationCount: 2, + })).toThrow(WarpError); + }); + + it('rejects suffix patch facts outside canonical order', () => { + expect(() => makeSourceFacts({ + patches: [ + makePatchFact({ patchSha: 'b'.repeat(40), lamport: 8 }), + makePatchFact({ patchSha: 'a'.repeat(40), lamport: 7 }), + ], + })).toThrow(WarpError); + }); +}); diff --git a/test/unit/domain/index.exports.test.ts b/test/unit/domain/index.exports.test.ts index 84464af2a..0ed90a3a2 100644 --- a/test/unit/domain/index.exports.test.ts +++ b/test/unit/domain/index.exports.test.ts @@ -64,8 +64,20 @@ import WarpAppDefault, { ContinuumEvidenceClaim, ContinuumEvidencePosture, ContinuumFamilyId, + ContinuumGeneratedFamilyInventory, + ContinuumGeneratedFamilyInventoryEntry, + ContinuumGeneratedFamilyStatus, ContinuumReceiptFamilyProjection, + GitWarpReadingEnvelopePayloadFact, + GitWarpReadingEnvelopeSourceFacts, + GitWarpTickPatchReplayCore, + GitWarpTickReceiptShell, + GitWarpTickReceiptWitnessCore, + GitWarpTickWitnessLadder, + GitWarpWitnessedSuffixPatchFact, + GitWarpWitnessedSuffixSourceFacts, GitWarpReceiptSourceFacts, + createCurrentContinuumGeneratedFamilyInventory, ContinuumArtifactJsonFileAdapter, } from '../../../index.ts'; @@ -269,8 +281,20 @@ describe('index.ts exports', () => { expect(ContinuumEvidenceClaim).toBeDefined(); expect(ContinuumEvidencePosture).toBeDefined(); expect(ContinuumFamilyId).toBeDefined(); + expect(ContinuumGeneratedFamilyInventory).toBeDefined(); + expect(ContinuumGeneratedFamilyInventoryEntry).toBeDefined(); + expect(ContinuumGeneratedFamilyStatus).toBeDefined(); expect(ContinuumReceiptFamilyProjection).toBeDefined(); + expect(GitWarpReadingEnvelopePayloadFact).toBeDefined(); + expect(GitWarpReadingEnvelopeSourceFacts).toBeDefined(); + expect(GitWarpTickPatchReplayCore).toBeDefined(); + expect(GitWarpTickReceiptShell).toBeDefined(); + expect(GitWarpTickReceiptWitnessCore).toBeDefined(); + expect(GitWarpTickWitnessLadder).toBeDefined(); + expect(GitWarpWitnessedSuffixPatchFact).toBeDefined(); + expect(GitWarpWitnessedSuffixSourceFacts).toBeDefined(); expect(GitWarpReceiptSourceFacts).toBeDefined(); + expect(createCurrentContinuumGeneratedFamilyInventory).toBeDefined(); expect(ContinuumArtifactJsonFileAdapter).toBeDefined(); }); @@ -308,6 +332,16 @@ describe('index.ts exports', () => { expect(claim.isTranslatedGitWarpEvidence()).toBe(true); expect(claim.isNativeContinuumEvidence()).toBe(false); }); + + it('exports the current generated-family readiness inventory', () => { + const inventory = createCurrentContinuumGeneratedFamilyInventory(); + + expect(inventory).toBeInstanceOf(ContinuumGeneratedFamilyInventory); + expect(inventory.requireEntry('receipt-family')).toBeInstanceOf(ContinuumGeneratedFamilyInventoryEntry); + expect(inventory.requireEntry('receipt-family').status).toBeInstanceOf(ContinuumGeneratedFamilyStatus); + expect(inventory.requireEntry('receipt-family').status.isProjectionReady()).toBe(true); + expect(inventory.requireEntry('runtime-boundary-family').status.isProjectionReady()).toBe(false); + }); }); describe('cancellation utilities', () => {