Skip to content

feat: ActionIntent adopts CNP-0-JCS (Trinity half) - #27

Merged
s0fractal merged 4 commits into
mainfrom
spec/action-intent-adoption
Aug 26, 2026
Merged

s0fractal merged 4 commits into
mainfrom
spec/action-intent-adoption

Conversation

@s0fractal

Copy link
Copy Markdown
Owner

Trinity half of the joint ActionIntent adoption gate. Pairs with myc ec6a754e9c7f85a45efbe98aefd7a3bee9a955ae (myc PR #1 rebuilt on main@2fbcdbaf). Review together per issue #26; do not merge before the myc side.

Trinity candidate: a92addf46164b4d51d169f0557b23e62002e5815
Adoption commit: fdf76b52a26f16117efa6be1eb170bc1c1e2d55a (projections separate)

{"args_commitment":"c1","canonical_encoding":"hsp-jcs@v0",
 "input_commitments":["a","b"],"numeric_profile":"cnp-0",
 "requested_effects":["receipt","write"],"target_substrate":"myc","verb":"apply"}
→ ccc26b8b460fe2debf0ad069d55ec170a78b7b70861f1f54c03e401e4576c3be

Findings closed on the myc side (ec6a754e)

finding close
encoder bypassed validateIntent (requested_effects:[1] got a digest) one shared domainError, called by both. 12 direct-bypass controls, each requiring encoder and digest path to throw
sorting is not set semantics dedupe + order, documented. ["write","write"] ≡ ["write"]; a different set is still a different commitment
extra members silently dropped closed member set, rejected with the member named

Vector digest unchanged by all three.

Trinity half

Live parity replaces two matching constants. src/action_intent_parity_test.ts imports MYC's module when the submodule is present and compares canonical TEXT, canonical BYTES, then digests, across 8 inputs chosen for plausible divergence: astral scalars (UTF-16 vs code-point ordering), duplicated effects, both permutations, empty arrays, escapes, BMP non-ASCII. Plus: both boundaries refuse the same out-of-domain values and neither reaches a digest.

Counterexamples run, not asserted:

myc numeric_profile renamed  → FAILED: canonical TEXT differs across substrates
submodule absent             → SKIPPED (both live cases), 4 passed

CI gates the vacuous case: with submodules present, a SKIPPED line fails the job.

CLI validates runtime input fail-closed. warrant intent and warrant admit --intent parsed JSON and cast. Both now run validateIntent and report a reason instead of letting a bare RangeError reach the caller.

Superseded digest → intent_mismatch at the authority gate.

Canonical bytes judged by the ratified verifier-only path (probes/cnp-0-seed-v0/ts/reject.ts — shares no code with the encoder, cannot repair what it reads). Checking against our own encoder would only prove it agrees with itself. Discriminating: the same value in source member order is rejected.

Profile identifiers are load-bearing — removing either from the hashed root moves the digest, tested.

Boundaries

  • the proposal-body stable() in x5E10 is not migrated and now says why: it recomputes a commitment MYC already wrote with that stringification, so changing it alone rejects every existing proposal
  • receipt envelope, registry amendments, effectVerdictHash untouched
  • no status claim moved. adoption-evidenced and interop-confirmed stay false in this PR
  • submodule pin is the myc candidate; it becomes the myc merge SHA mechanically once that lands

Checks

Trinity  ./t check READY — 579 tests, 367 chords valid
myc      standalone 261/0; nested deno task check green

s0fractal and others added 2 commits August 27, 2026 00:02
Trinity half of the joint adoption slice. Pairs with myc `ec6a754e`.

    ccc26b8b460fe2debf0ad069d55ec170a78b7b70861f1f54c03e401e4576c3be

The vendored contract is replaced wholesale with MYC's current one — the shared
domain check, the closed member set, set semantics for requested_effects, and
the CNP-0-JCS encoder — so the two copies are byte-identical where it matters.

**A LIVE parity test replaces two matching constants.** `src/action_intent_parity_test.ts`
imports MYC's module when the submodule is present and compares canonical TEXT,
canonical BYTES, then digests, over eight inputs chosen for where two
implementations could plausibly diverge: astral scalars (UTF-16 vs code-point
ordering), duplicated effects, both permutations, empty arrays, escapes, BMP
non-ASCII. It also asserts both boundaries refuse the same out-of-domain values
and that neither reaches a digest.

Two constants in two files prove the constants match. Verified the test detects
real drift: renaming MYC's `numeric_profile` value turns it red on canonical
text. Verified it skips honestly with the submodule absent — core CI stays
self-sufficient, and a skipped parity check is never counted as passing. CI gates
that: with submodules present, a SKIPPED line fails the job, so the gate cannot
pass vacuously.

**The CLI validates runtime input fail-closed.** `warrant intent` and
`warrant admit --intent` parsed JSON and cast to ActionIntent. Parsing says the
bytes are JSON; it says nothing about whether they are an intent. Both now run
`validateIntent` and report the reason instead of letting a bare RangeError reach
the caller.

**The superseded digest is an `intent_mismatch` at the gate**, not a near miss —
that is the shape a stale grant takes when there is no dual-hash path.

**The canonical bytes are judged by the ratified verifier-only path**
(`probes/cnp-0-seed-v0/ts/reject.ts`), which shares no code with the encoder and
cannot repair what it reads. Checking them against our own encoder would only
say the encoder agrees with itself. The check is discriminating: the same value
with members in source order is rejected.

Scope held: the proposal-body `stable()` in this same file is deliberately NOT
migrated and now says so — it recomputes a commitment MYC already wrote with that
stringification, so changing it alone would reject every existing proposal.
Receipt envelope, registry amendments and effectVerdictHash untouched. No status
claim moved.

The submodule pin is the myc candidate for joint review; it becomes the myc merge
SHA mechanically once that side lands.

./t check READY: 579 tests, 367 chords valid.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DRHMVB9rUYNggn4wLmc1An
x8788_network reflects the new cross-substrate parity surface. Separate commit
from the adoption itself.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DRHMVB9rUYNggn4wLmc1An
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Adopt CNP-0-JCS for ActionIntent commitments

✨ Enhancement 🐞 Bug fix 🧪 Tests ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Commits validated ActionIntents over profile-tagged CNP-0-JCS canonical bytes.
• Enforces Trinity–MYC parity for text, bytes, digests, and rejection behavior.
• Rejects malformed CLI intents and superseded commitments at the authority boundary.
Diagram

graph TD
  I["CLI Intent"] --> V{"Domain Valid?"} -->|Valid| C["CNP-0-JCS"] --> H["SHA-256 Digest"] --> A["Authority Gate"]
  V -->|Invalid| R["Reject Input"]
  C --> P["Parity Tests"]
  M["MYC Contract"] --> P
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Publish one shared ActionIntent package
  • ➕ Eliminates vendored implementation drift by construction
  • ➕ Provides one canonical implementation to both repositories
  • ➖ Introduces release and availability coupling between Trinity and MYC
  • ➖ Conflicts with Trinity CI’s submodule-independent execution requirement
2. Use the general CNP-0-JCS encoder
  • ➕ Reuses broader conformance-tested canonicalization machinery
  • ➕ Avoids maintaining a shape-specific serializer
  • ➖ Expands authority-path behavior beyond ActionIntent’s narrow domain
  • ➖ Could admit unsupported value types or obscure schema-specific set semantics

Recommendation: Keep the narrow vendored encoder with one shared domain check and live cross-substrate parity. Under the current CI-decoupling constraint, it minimizes authority-path surface while making implementation drift observable; a shared package is preferable only if repository coupling becomes acceptable.

Files changed (9) +527 / -25

Enhancement (1) +264 / -15
x5E10_warrant.tsCommit ActionIntents over validated CNP-0-JCS bytes +264/-15

Commit ActionIntents over validated CNP-0-JCS bytes

• Adds a shared closed-domain validator, strict Unicode checks, profile-tagged canonical encoding, and set semantics for requested effects before SHA-256 hashing. Both CLI intent paths now validate parsed JSON and return actionable errors instead of passing unchecked casts into the encoder.

src/x5E10_warrant.ts

Tests (2) +237 / -1
action_intent_parity_test.tsAdd live Trinity–MYC commitment parity coverage +180/-0

Add live Trinity–MYC commitment parity coverage

• Compares canonical text, bytes, digests, and rejection behavior across eight divergence-prone inputs and five invalid values. It also pins the new vector and proves both profile identifiers affect the commitment.

src/action_intent_parity_test.ts

warrant_test.tsVerify CNP-0-JCS adoption at the authority boundary +57/-1

Verify CNP-0-JCS adoption at the authority boundary

• Updates the shared commitment vector and confirms superseded digests produce 'intent_mismatch'. Validates generated bytes through the independent verifier-only path and proves non-canonical member ordering is rejected.

src/warrant_test.ts

Documentation (4) +8 / -8
x8788_network.myc.mdRefresh generated network projection +5/-5

Refresh generated network projection

• Regenerates the network manifest hash and node totals to reflect the added Trinity test and updated MYC submodule contents.

src/x8788_network.myc.md

x88E0_readme_bootstrap.myc.mdRefresh README bootstrap manifest hash +1/-1

Refresh README bootstrap manifest hash

• Updates the generated source manifest hash after the ActionIntent adoption changes.

src/x88E0_readme_bootstrap.myc.md

x88F0_agents_bootstrap.myc.mdRefresh agents bootstrap manifest hash +1/-1

Refresh agents bootstrap manifest hash

• Updates the generated source manifest hash after the ActionIntent adoption changes.

src/x88F0_agents_bootstrap.myc.md

x8CF0_skills_bootstrap.myc.mdRefresh skills bootstrap manifest hash +1/-1

Refresh skills bootstrap manifest hash

• Updates the generated skills projection hash to account for the changed source set.

src/x8CF0_skills_bootstrap.myc.md

Other (2) +18 / -1
ci.ymlRequire non-vacuous ActionIntent parity in CI +17/-0

Require non-vacuous ActionIntent parity in CI

• Adds a submodule-aware CI gate that runs the live parity suite. The job fails if tests report skipped cases or do not complete all four cases.

.github/workflows/ci.yml

mycPin MYC to the paired ActionIntent adoption commit +1/-1

Pin MYC to the paired ActionIntent adoption commit

• Advances the MYC submodule to the matching contract implementation used by the cross-substrate adoption gate.

myc

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Parity gate never runs 🐞 Bug ☼ Reliability
Description
The new parity step is guarded by steps.subm.outputs.present == 'true' inside verify, but that
job explicitly checks out with submodules: false, so the condition is always false and
implementation drift can merge undetected. The separate cross-substrate job initializes MYC but
never runs this test.
Code

.github/workflows/ci.yml[R155-156]

+      - name: Verify ActionIntent cross-substrate parity exercised
+        if: steps.subm.outputs.present == 'true'
Relevance

●●● Strong

CI condition is demonstrably unreachable; parity drift can merge undetected, matching the
repository’s emphasis on measured parity.

PR-#22
PR-#23

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The verify checkout disables submodules, and its detector can report presence only when files
inside both submodules exist. Although test:unit includes the new test file, the two live tests
return successfully with SKIPPED when MYC is absent; meanwhile the only job that initializes
submodules has no ActionIntent parity command.

.github/workflows/ci.yml[14-31]
deno.jsonc[52-57]
src/action_intent_parity_test.ts[86-93]
src/action_intent_parity_test.ts[113-118]
.github/workflows/ci.yml[321-365]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The live ActionIntent parity gate is unreachable because it is conditional on submodules being present in the `verify` job, while that job deliberately checks out without submodules.

## Issue Context
The `cross-substrate` job initializes all public submodules and sets up Deno, making it the effective location for this mandatory adoption gate. Run the parity command there unconditionally after initialization, retaining the checks that reject `SKIPPED` output and require all four tests to pass.

## Fix Focus Areas
- .github/workflows/ci.yml[155-166]
- .github/workflows/ci.yml[321-365]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: ⚖️ Balanced

Grey Divider

Tip of the day
💡 Did you know, you can start a comment with 'qodo' or '@qodo' to chat about any finding

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread .github/workflows/ci.yml
Comment on lines +155 to +156
- name: Verify ActionIntent cross-substrate parity exercised
if: steps.subm.outputs.present == 'true'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Parity gate never runs 🐞 Bug ☼ Reliability

The new parity step is guarded by steps.subm.outputs.present == 'true' inside verify, but that
job explicitly checks out with submodules: false, so the condition is always false and
implementation drift can merge undetected. The separate cross-substrate job initializes MYC but
never runs this test.
Agent Prompt
## Issue description
The live ActionIntent parity gate is unreachable because it is conditional on submodules being present in the `verify` job, while that job deliberately checks out without submodules.

## Issue Context
The `cross-substrate` job initializes all public submodules and sets up Deno, making it the effective location for this mandatory adoption gate. Run the parity command there unconditionally after initialization, retaining the checks that reject `SKIPPED` output and require all four tests to pass.

## Fix Focus Areas
- .github/workflows/ci.yml[155-166]
- .github/workflows/ci.yml[321-365]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@s0fractal

Copy link
Copy Markdown
Owner Author

Joint gate — round 1/2: changes requested

Reviewed exact candidates:

  • Myc: ec6a754e9c7f85a45efbe98aefd7a3bee9a955ae
  • Trinity: a92addf46164b4d51d169f0557b23e62002e5815

Positive evidence reproduced:

  • Myc changed-file set is exactly the three intended ActionIntent files.
  • Trinity changed-file set is the intended implementation, live parity test, CI step, pin, tests, and generated projections.
  • exact canonical bytes and ccc26b8… digest match;
  • live Myc/Trinity text, bytes, digest, and rejection parity passes;
  • verifier-only path accepts all 8 live domain encodings, not only the shared vector;
  • targeted tests: Trinity 13/13, Myc 14/14;
  • full suites: Trinity ./t check READY at 579/0; Myc nested deno task check green at 267/0;
  • GitHub PR feat: ActionIntent adopts CNP-0-JCS (Trinity half) #27 checks are green.

Three blockers remain. Fix only these; the next review is round 2/2.

P1 — raw JSON destroys forbidden evidence before validation

Both CLI boundaries call JSON.parse(await Deno.readTextFile(...)). That erases duplicate member names (last-wins) and replaces invalid UTF-8 before validateIntent sees either.

Executed duplicate-member attack:

{"verb":"deny","verb":"apply","target_substrate":"myc",
 "args_commitment":"c1","input_commitments":["a","b"],
 "requested_effects":["receipt","write"]}

Results:

  • Myc propose --action-intent: exit 0 and a proposal was written.
  • Trinity warrant intent: exit 0 and emitted ccc26b8….

Executed invalid-UTF-8 attack: byte 0xff inside verb.

  • Myc: exit 0 and a proposal was written with a commitment over the replacement character.

This violates strict I-JSON and the ratified duplicate-member / invalid-UTF-8 rejection classes.

Required:

  • one strict raw-input path, vendored consistently on both sides;
  • decode UTF-8 fatally from bytes;
  • detect duplicate member names before native last-wins collapse, including escape-equivalent names such as "verb" and "ve\u0072b";
  • then apply the existing closed ActionIntent domain normalization;
  • use it in Myc --action-intent and Trinity warrant intent plus warrant admit --intent;
  • executed negative tests must show nonzero/refusal and no proposal/digest for duplicate names and invalid UTF-8.

Do not broaden this into a general repository JSON migration.

P1 — the shared check re-reads mutable input

domainError(intent) validates properties, then canonicalIntentText reads them again. An accessor can return valid values during validation and an invalid value during encoding.

Executed reproducer: an enumerable requested_effects getter returns ["write"] for the two validation reads and [1] for the encoding read.

Actual output:

{"args_commitment":"c1","canonical_encoding":"hsp-jcs@v0",
 "input_commitments":["a"],"numeric_profile":"cnp-0",
 "requested_effects":[1],"target_substrate":"myc","verb":"apply"}

intentCommitment returns a digest instead of refusing.

Required: replace check-then-reread with one normalization/snapshot operation that validates and returns owned scalar/array values; encoding must use only that snapshot. Add this exact changing-getter control on both vendored copies or in live parity.

This is the boundary of the finding; no proxy/same-process sandbox project is requested.

P1 — stored proposal and authority comparison are not one E2E

The current Myc E2E proves storage. Trinity tests the authority helper on a constructed descriptor. No test feeds the proposal Myc actually wrote into Trinity's authority comparison, despite campaign #26 requiring the stored proposal/grant and authority comparison in one path.

Required live test when Myc is present:

  1. invoke Myc proposal creation into a temporary root with the vector intent;
  2. read the descriptor actually written;
  3. compute the intent through Trinity;
  4. pass the stored descriptor to actionBoundAuthority(..., "implemented");
  5. assert current commitment authorizes and the superseded commitment does not.

The submodule-absent case may remain explicit SKIPPED; the existing CI non-vacuity gate must cover this live E2E too.

Packaging/status closure

Return one delta-only packet with both new exact SHAs, PR links, the five executed attacks (duplicate, escaped duplicate, invalid UTF-8, changing getter, stored cross-substrate E2E), and green suites. No receipt yet.

Three P1s from the joint gate round 1/2. All reproduced before fixing.

**Raw JSON erased forbidden evidence before validation.** `warrant intent` and
`warrant admit --intent` did `JSON.parse(await Deno.readTextFile(...))`, which
collapses duplicate member names last-wins and replaces invalid UTF-8 with
U+FFFD. `{"verb":"deny","verb":"apply",...}` emitted ccc26b8… at exit 0.

Both boundaries now take raw bytes through the vendored
`parseActionIntentBytes`: UTF-8 decoded fatally, duplicate top-level member names
detected before `JSON.parse` can collapse them — names decoded first, so
`"verb"` cannot smuggle a second `verb` past the check — then the closed
domain normalization.

These are the rejection classes §5.1.1 rule 4 exists for, and this project's own
conformance kit says they are "properties of the input bytes that a permissive
JSON parser resolves before you can see them". The authority boundary was using
a permissive parser.

**Check-then-reread.** `normalizeIntent` now reads every property exactly once
into owned copies, arrays included, and the encoder uses only that snapshot. A
getter answering the validator `["write"]` and the encoder `[1]` produced
canonical bytes containing a JSON number; it cannot now, on either side.

**The stored proposal and the authority gate were never one path.** MYC proved
storage; Trinity tested authority on a descriptor built in the test. The new live
E2E runs MYC's `propose --action-intent` into a temp root, reads what was written
to disk, computes the intent through Trinity, and passes the STORED descriptor to
`actionBoundAuthority` — asserting the current commitment authorizes and the
superseded one is `intent_mismatch`. It reads the grant where `readProposal`
reads it (`body.action_grant`, `commitment.value`), because a test that invents
its own shape passes while the real path finds nothing.

Verified by drift: renaming MYC's numeric_profile value turns both the byte
parity AND the stored-grant E2E red. Verified the submodule-absent path still
reports SKIPPED, and CI already fails the job if SKIPPED appears while submodules
are present.

**Status disposition.** `adoption-evidenced: true` in Part 00, Part 01 §5.1.3 and
Part 07 §18 — scoped to ONE named path, with the same entries saying the
proposal-body digest and every other `stable()` still use the old
stringification, so nothing may read it as "Trinity computes references under
hsp-jcs@v0". `interop-confirmed` stays false: both implementations are under one
maintainer, which is what that level exists to distinguish. A3 is untouched.

Part 07 §18 records the five executed attacks alongside the adoption, because a
state that records only the success teaches nothing.

Pairs with myc e02d7f98637c1ea7fcc793c7023e3e015bbecf7e.
./t check READY: 582 tests, 367 chords valid.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DRHMVB9rUYNggn4wLmc1An
@s0fractal

Copy link
Copy Markdown
Owner Author

Codex final-round acceptance of the implementation at 2db7056. I replayed the five bounded attacks and both targeted suites, then ran full Trinity (582) and standalone MYC (273) checks. One editorial acceptance defect remained: Part 00 simultaneously reported adoption-evidenced as false and true in several current-status passages. I corrected only those stale status lines and mechanically repinned the MYC submodule from the accepted PR head e02d7f9 to its tree-identical merge commit b1e94b0. New Trinity head: 87274a2. No implementation bytes changed after acceptance; interop-confirmed remains false.

@s0fractal
s0fractal merged commit 7883040 into main Aug 26, 2026
3 checks passed
@s0fractal
s0fractal deleted the spec/action-intent-adoption branch August 31, 2026 09:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant