Skip to content

feat(spec): Capability Kit manifest identity, OperatorBindingDTO and OpportunityDTO v0 contracts (interface-only; not before wave D) - #397

Merged
LamaSu merged 18 commits into
masterfrom
feat/kits-spec-contracts
Oct 4, 2026
Merged

LamaSu merged 18 commits into
masterfrom
feat/kits-spec-contracts

Conversation

@LamaSu

@LamaSu LamaSu commented Sep 24, 2026 •

Copy link
Copy Markdown
Owner

Summary

These are interface-only @pcc/spec contracts for the kits lane (ledger R5/R6/R7/R8/R41/R45, product PX-13). The PR adds no routes and no storage. It is not for merge before wave D (steward ruling #3058).

The contracts are v0, frozen for consumers: adk (R4), readmodels (PX-7), operator-ux (PX-11) and refvertical (R46) build against these shapes, and any change needs their ack on the bus first.

types/capability-kit.ts: Capability Kit manifest and identity

  • The manifest references existing artifacts by digest and inlines none. CapabilityKitManifestV1 is strict zod, and its references are:
    • a CSD pinned by capabilityContractDigest, because a url alone is a mutable pointer;
    • artifacts named by the sha256 of their bytes: adapter or method, tests, install and provenance recipes, and machine-native labware, PLR and CAD files;
    • economics-lane terms hashes, which only economics compiles.
  • Identity. computeKitDigest = sha256(canonicalize(normalized manifest)). That is the same construction as capabilityContractDigest and uses the single @pcc/spec canonicalizer. List order never changes the digest.
  • Lineage. parentKitDigest records forks and revisions.
  • Golden vectors are pinned. The minimal one was re-derived independently in Python and matches sha256:e05bb524…2527. Re-run them when N15 / fix(spec): canonicalize refuses values that have no JSON form #359 changes the canonicalizer.
  • Reusable vs one-off. validateKitCompleteness separates a reusable kit from a one-off listing. A reusable kit has an implementation, tests, an install recipe, a provenance recipe and a license. This is the L1 acceptance rule for kit-build bounties.

types/operator-binding.ts: OperatorBindingDTO v0

The shape was agreed with operator-ux (bus #2896 / #2944):

  • Principal: principal.identityStatus is self_asserted | proven.
  • Server-derived fields: executorKinds, the bindings (each with a kitDigest and a server-capped tier), and claim rights.
  • No money authority: moneyAuthority is the literal "none". Execution authority is derived from bindings, never from the key's scopes.
  • Payee: a masked read of the server-resolved destination (N21). The schema refuses an unmasked address, and a binding grants no payout or spend authority (R41).

types/opportunity.ts: OpportunityDTO v0

There are three kinds: funded_offer, kit_build_request and demand_aggregate.

  • Funding: funded needs an authoritative, server-verified source.
  • Demand aggregates carry exactly painpoints' toPublicOpportunityAggregate fields (type, band, as-of): no location, evidence or deadline, and no raw intents, priors or requester identities.
  • Hygiene: amounts are base-unit strings, titles are single-line and server-templated, and the schema is strict.

Tests

Command (run on the Spark in /mnt/sparkbulk/pcc-lanes/wt-kits-spec) Result
packages/spec: npx vitest run src/__tests__/kits-contracts.test.ts 21/21
packages/spec: npx vitest run 818/818 (39 files)
packages/spec: npx tsc --noEmit exit 0

The tests cover:

  • digest determinism across key and list order;
  • that every content change moves the digest;
  • that invalid manifests get no identity;
  • fork lineage, the golden vectors and completeness;
  • the money-authority and masking invariants;
  • the funding and aggregate invariants.

Merge conditions (steward #3058)

  1. The OpportunityDTO exposes only the k-suppressed public aggregate. The steward runs a boundary check before any merge.
  2. Golden vectors are re-run when fix(spec): canonicalize refuses values that have no JSON form #359 lands.
  3. OperatorBindingDTO carries no money or payout authority.
  4. The shapes are v0, frozen for consumers.

Merging is the operator's call, and only after wave D opens.

Refs: kits reconciliation §0.4 K1/K3/K4 and §0.5.1; bus #2780 (refvertical manifest mapping), #2896/#2944 (operator-ux), #2607 (painpoints demand policy).

Round 3: astra 112b fixes, at a173743

Every open finding was reproduced at 072f9a1 first. The fixes are in 8 commits, written by a Sonnet implementer from my spec, then reviewed and mutation-checked by me:

  • CRITICAL 1 (demand privacy): a demand_aggregate's capabilityType must be IN an approved public set (opportunityDTOSchemaFor; the default is the built-in CSDs, and ACTIVE kits come via publicCapabilityUrls). It carries a required releaseDigest, and demandAggregatesFromRelease checks the record's digest, its approvedSetDigest (same bytes as feat(spec,demand-intel): kit-demand signal and fixed periodic public release layer (R44, PX-13) #365), its periods and its membership.
  • License: isAllowedLicenseExpression is an honest allow-list (renamed from isSpdxLicenseExpression).
  • HIGH 4: executable means evidenceIsExecutable: tier-eligible under evidence's computeCsdEligibility, with live verifiers. validatePrimitiveParams fails closed.
  • Masking and describe: at most 25% of a destination is revealed, and describe refuses scheme:// text.
  • Read times: asOf and lastSeenAt may not run more than 5 minutes ahead of the parsing clock.
  • Versioning: a semantic corpus per literal (refinements included) is pinned with the shape fingerprint.

Evidence: spec 885/885, both typechecks clean, mutations 10/10 killed in my independent pass. Review pack: 112c-k-397-r3-a173743e.

At merge with master, one pinned test (the built-in public list) gains pcc://capabilities/document-print-and-mail/v1 (2d80818) by design.

Round 4: astra 112c fixes, at cbc1c14

Master was merged first (3f16596). It brings painpoints' #365 (types/kit-demand.ts) and the 9th built-in CSD. Every open finding was reproduced at a173743 first. The fixes are mine:

Evidence: spec 1185/1185, both typechecks 0, mutations 8/8. Review pack: 112d-k-397-r4-cbc1c149.

Round 5: astra 112d fixes, at 79a3330

Both open findings were reproduced at cbc1c14 first (3 failing cases, 2 controls). The fixes are mine:

  • CRITICAL 1: the release reader requires feat(spec,demand-intel): kit-demand signal and fixed periodic public release layer (R44, PX-13) #365's canonical order (capabilityType strictly increasing in code units, as buildPublicRelease sorts), so a reordered record is refused and the order carries no information.
  • HIGH 4: every named primitive must sit in some tier, or the requirement is not executable. A primitive with an unmet dependency (for example confirm.execution_mode without receipt.kernel_signed) is no longer silently dropped. The test that pinned that hole now expects false.
  • LOW: the module header names every external input.

Evidence: spec 1187/1187, both typechecks 0, mutations 11/11, opportunity corpus 97 cases (LOCK re-pinned). Review pack: 112e-k-397-r5-79a3330d.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj

LamaSu and others added 17 commits September 24, 2026 15:27
…OpportunityDTO contracts

These are interface-only contracts for the kits lane (ledger R5/R6/R7/R8/R41/
R45, PX-13). They add no routes and no storage. adk (R4), readmodels (PX-7),
operator-ux (PX-11) and refvertical (R46) build against them.

- capability-kit.ts: CapabilityKitManifestV1 (strict zod). The kit references
  existing artifacts by digest: a CSD pinned by its
  capabilityContractDigest; adapter, method, tests, install and
  provenance recipes; machine-native labware, PLR and CAD files; and
  economics-lane terms hashes. computeKitDigest =
  sha256(canonicalize(normalized)), the same construction as
  capabilityContractDigest; list order never changes the digest.
  parentKitDigest records fork and revision lineage.
  validateKitCompleteness separates a reusable kit from a one-off listing
  (implementation, tests, install recipe, provenance recipe, license).
- operator-binding.ts: OperatorBindingDTO v0, the shape agreed with
  operator-ux (#2896/#2944). moneyAuthority is the literal "none"; claim
  rights must come from bindings; payee destinations are masked
  (maskPayoutDestination) and a schema refuses an unmasked address.
- opportunity.ts: OpportunityDTO v0. There are three kinds: funded_offer,
  kit_build_request and demand_aggregate. "funded" requires an
  authoritative, server-verified source. Aggregates are banded signals
  with no reward. Amounts are base-unit strings, titles are single-line
  and server-templated, and there are no requester fields.

Tests: kits-contracts.test.ts (18) covers digest determinism across key
and list order; every content change moves the digest; invalid
manifests get no identity; fork lineage; completeness; money-authority
and masking invariants; and funding and aggregate invariants.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj
…s (#3058)

- All three contracts are marked "v0, frozen for consumers": adk,
  readmodels, operator-ux and refvertical ack any change on the bus
  first.
- computeKitDigest documents that it uses @pcc/spec's single
  canonicalizer and pins two golden vectors (a minimal kit and the
  full lab/workcell kit). The minimal vector was re-derived
  independently in Python, and both are re-run when the canonicalizer
  changes (N15 / PR #359).
- OperatorBindingDTO states the payout boundary: the payee is a masked
  read of the server-resolved destination (N21), and a binding sets,
  changes and authorizes no payout and grants no spend authority (R41).
- A demand_aggregate OpportunityDTO carries only painpoints' public
  projection fields; location, evidence and deadline are refused.

Tests: kits-contracts 21/21 (3 new); spec 818/818; tsc clean.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj
The consumers acked this on the bus: readmodels (#3623), adk (#3785) and
refvertical (#3571, #3792).

- A2: OpportunityDTO.capabilityContractDigest pins the exact CSD revision.
  It is REQUIRED on a funded funded_offer, because the accepted plan pins
  it; optional on kit_build_request; forbidden on demand_aggregate.
- A3: evidence.requiredEventClasses: string[] becomes
  evidence.requiredPrimitives: CsdEvidencePrimitiveRef[], the same grammar
  as a CSD's evidence refs and setup's EmitterDecl emits[]. Each id must be
  active in EVIDENCE_PRIMITIVES, so the ADK's provenance planner compares
  supply against demand with no mapping table.
- A4: the manifest header states two conventions. The provenance-recipe
  mediaType is application/vnd.pcc.provenance-recipe+json;v=1, and
  compatibility.interfaces uses the kernel's AdapterType names, never
  "mock". There is no shape change.
- A5: a demand_aggregate carries releasePeriod (YYYY-MM), required there
  and forbidden elsewhere. asOf stays the READ time for every kind
  (readmodels' counter), so it never says when an intent happened (#365 F4).
- A6: artifact names must be safe relative POSIX paths. Before, the schema
  accepted '../../etc/passwd', a path traversal for any installer that
  writes artifacts by name. Refvertical's real R46 kit already complies,
  so its kitDigest is unchanged.
- One CSD_CAPABILITY_URL_PATTERN is now shared by the kit manifest and
  OpportunityDTO.

Golden kit digests are unchanged (A6 only validates). kits-contracts
passes 26/26 (5 new). With the source changes reverted, 8 fail. The full
spec suite passes 823/823 and tsc is clean.

A1, the OperatorBindingDTO part, follows separately: operator-ux, its last
acker, is paused (steward #4029).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj
OperatorBindingDTO capability types become CSD urls (adk's ask (a) in
#3152, with A1b acked in #3785).
- bindings[].capabilityType and executionAuthority.canClaimCapabilityTypes
  must match CSD_CAPABILITY_URL_PATTERN, the same pattern as OpportunityDTO
  and the kit manifest. A Work Inbox or the ADK's match compares them
  directly, so a legacy '3d-printing' can no longer silently match nothing.
- NEW unmappedCapacity: {kind, id, legacyType}[] (required, may be empty)
  lists capacity whose legacy type resolves to no CSD. It is never
  claimable, and it shows the operator why that capacity matches nothing.

operator-ux acked A1 (#3997) and asked that claim rights stay derived only
from mapped bindings. The superRefine already does that, and the test
"unmapped capacity can never become claimable" pins it. Steward #4111:
land it normally with A2-A6.

Tests: kits-contracts passes 30/30 (4 new). Against the old
operator-binding.ts, 2 of the 4 fail (the CSD-url binding and the required
unmappedCapacity). The other two are defense in depth: the old schema
already refused them, by claim binding and by strictness. The spec suite
passes 827/827 and tsc is clean.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj
…fact roles)

KIT_ARTIFACT_ROLES gains "intake-schema" (the filled human-intake record
schema, ADK-track item 6) and "safety-envelope" (sensors' R8 operational
envelope). A device kit can then ship the onboarding artifacts the ADK
produces. The change is additive: every existing manifest and golden
digest is unchanged, and an unknown role is still refused.

adk acked as a consumer (#4099). refvertical, the other manifest producer,
is asked on the bus before this is pushed.

Tests: kits-contracts passes 32/32 (2 new), the spec suite 829/829, and
tsc is clean.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj
Each finding was reproduced on b9f3491 before it was fixed.

- CRITICAL 1: OpportunityDTO is a strict discriminated union. A
  demand_aggregate carries no free text: its id and title are derived
  (demandAggregateId / demandAggregateTitle), its capability slug must be
  publishable (isPublicCapabilityUrl), and kitRef, location, evidence,
  deadline, reward and digest are refused.
- HIGH 2: validateKitCompleteness is documented as STRUCTURAL only. It
  counts only a valid SPDX expression (isSpdxLicenseExpression) or a
  rights-terms hash as a license, and refuses one artifact standing in for
  several required roles.
- HIGH 3: a funded opportunity names its authoritative funding record
  (fundingRef); a funded kit_build_request needs authority and the record.
- HIGH 4: evidence requirements carry executable, true exactly when every
  primitive has a live verifier; funded work must be executable; params
  are validated against each primitive's paramsSchema.
- MEDIUM 5: payee masks match exactly the two shapes maskPayoutDestination
  produces; availability is a closed typed summary.
- MEDIUM 6: duplicate set entries and non-NFC text are refused; lists sort
  by Unicode code point.
- MEDIUM 7: asOf, deadline and binding timestamps are ISO timestamps; a
  release period must have closed before asOf.
- MEDIUM 8: every shape or enum change bumps the schema literal, pinned by a
  structural fingerprint test.

Spec suite 39 files, 841 passed; tsc clean. Reverting any of 19 fix sites
fails its reproduction test.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ease (astra 112b A)

Slug syntax is not a privacy boundary ("alice-smith" passes
isPublicCapabilityUrl), so a demand_aggregate now has to name a capability in
an approved public set, and has to name the release record it came from.

- BUILTIN_PUBLIC_CAPABILITY_URLS: the CSD urls compiled into @pcc/spec
  (loadBuiltinCsds), filtered, sorted by code unit, frozen. That is 8 urls:
  document-print-and-mail/v1 is a draft workflow CSD that loadBuiltinCsds does
  not register and the package build does not ship, so it is not in the set.
- publicCapabilityUrls(activeKitCsdUrls): built-ins plus ACTIVE kits' csd urls;
  throws on any entry that is not a public capability url.
- approvedSetDigest(urls): same construction as #365's approvedSetDigest, pinned
  by a golden vector (also checked once against #365's real buildPublicRelease).
- DemandAggregateDTO.releaseDigest is required.
- opportunityDTOSchemaFor(urls) snapshots the set once; demand_aggregate needs
  membership. OpportunityDTOSchema is built from the built-ins.
- demandAggregatesFromRelease(release, approvedUrls, asOf) rebuilds the DTOs and
  throws unless the record's digest, approved-set digest, periods and
  membership all hold.
- isPublicCapabilityUrl is documented as a syntax backstop, not a boundary.

The aggregate() fixture moves to a built-in capability, period 2026-08 and past
instants; the opportunity shape fingerprint is re-pinned (releaseDigest).

Agent: implementer-kilo

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj
… fail closed (astra 112b B)

An empty primitive set was vacuously executable, and decl.self_attested (tier 0
only) could be presented as an executable tier-3 requirement, because
executability only asked for live verifiers.

- evidenceIsExecutable(tier, refs) runs the evidence lane's computeCsdEligibility
  with requireImplementedVerifier on the single tier: non-empty from tier 1,
  tierSupport, dependency closure, a Family-G primitive from tier 2, a non-decl
  primitive from tier 1, and live verifiers. Tier 0 with no primitives is the
  executable floor.
- EvidenceRequirementSchema checks executable === evidenceIsExecutable(...), in
  both directions. Funded kinds still need executable evidence.
- primitivesAreExecutable stays exported, documented as necessary, not
  sufficient; nothing in the schema calls it.
- validatePrimitiveParams fails closed: only type (one string), enum, items (an
  object schema), properties, required and additionalProperties (false, true or
  an object schema, now validated) are evaluated; annotations are ignored; any
  other keyword, an unknown or missing type, a malformed keyword, or a schema
  that would mean "any value" returns false. required and properties use own
  keys only.
- PRIMITIVE_PARAMS_KEYWORDS and PRIMITIVE_PARAMS_ANNOTATIONS are exported so the
  test walks every active primitive against the same set the validator uses.
  One active stub, telemetry.envelope_conformance, uses oneOf today; the test
  pins it, requires every live primitive to stay inside the grammar, and shows
  that stub fails closed.

The evidence() test helper now states evidenceIsExecutable. Ports the 112b
reproductions for HIGH 4a, 4b and 4c.

Agent: implementer-kilo

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj
…r (astra 112b C)

isSpdxLicenseExpression claimed SPDX validity but accepts only PCC's curated
list, so real SPDX ids such as EUPL-1.2 and DocumentRef references were refused
while the name said otherwise.

- Rename it isAllowedLicenseExpression and remove the old name (pre-release).
  The doc now says: SPDX expression syntax over PCC's allow-list of license and
  exception ids plus LicenseRef-<id>; not a general SPDX validator; a real SPDX
  id outside the list and DocumentRef references are refused by policy; extend
  the list by PR.
- validateKitCompleteness, KitCompleteness.missing and spdxLicense docs say
  "allowed license expression". The "license" gap id is unchanged.
- Tests: EUPL-1.2 is refused as a policy, "MIT OR Apache-2.0" is allowed, and
  the module no longer exports isSpdxLicenseExpression. Ports the 112b
  reproduction for finding 2, fixed to the allow-list semantics.

Agent: implementer-kilo

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj
…of describe (astra 112b D)

maskPayoutDestination("AB") returned the whole value, and the schema could not
tell a mask from a reconstructed short value. availability.describe could carry
endpoint text while the docs said availability "never" carries an endpoint.

- maskPayoutDestination trims, then: under 8 characters returns the mask alone,
  under 40 the mask and the last 2, otherwise the first 6, the mask and the last
  4. It reveals at most 25%; an EVM address (42) shows 6+4.
- MASKED_DESTINATION also accepts the bare mask. Its doc says the schema checks
  the FORM only and cannot know the source length; the helper guarantees the
  ratio.
- availability.describe refuses ASCII scheme:// text (any case) as a backstop.
  The docs now say what is enforced: no endpoint or authority FIELD (strict),
  describe is display text a consumer must never parse, fetch or execute, and
  the other availability strings are length-bounded typed values.
- The operator-binding shape fingerprint is re-pinned (the mask regex changed).

Ports the 112b reproductions for MEDIUM 5a, 5b and the 5b control.

Agent: implementer-kilo

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj
A future demand period paired with a forged future asOf passed, because the
only check was "the period closed before asOf" and asOf is caller-supplied.

- MAX_AS_OF_SKEW_MS (5 minutes) is exported from opportunity.ts, with one shared
  helper, readTimeIsNotInFuture. Every OpportunityDTO kind refuses an asOf later
  than now plus the skew; so does OperatorBindingDTO for asOf and for every
  binding's lastSeenAt. An unparseable instant is refused too. A deadline is not
  restricted.
- The docs say what this is: a bound on a forged future time, not proof that a
  time is true; the producer assigns read times from its own clock.
- Fixtures already use past instants (the aggregate fixture moved in the
  public-demand commit). Times in the new tests are computed from Date.now().

Ports the 112b reproduction for MEDIUM 7 (the verdict's 2099-12 case).

Agent: implementer-kilo

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj
…ded (astra 112b F)

The structural fingerprint unwraps ZodEffects, so a change to a superRefine (demand
privacy, funding rules, duplicate or NFC rejection, tier validation) moved no pin.

- Three corpora under __tests__/kits-corpus/, one per literal, each
  {literal, cases: [{name, expect, value}]} of wire JSON: 90 opportunity, 67
  operator-binding and 53 capability-kit cases. Every refinement rule has a reject
  case that fails only because of it, plus accept cases; the rules from A to E are
  included. Timestamps are in the past, except the cases that exist to prove a rule
  refuses the future (year 2100) and one far-future deadline.
- The MEDIUM 8 block now checks: every case's verdict equals its expectation; one
  lock table pins {literal, shape, corpus} per literal (corpus = first 16 hex of
  sha256 over the parsed file); each corpus names its schema's literal; wrapping a
  schema in a superRefine that refuses its first accept case flips a verdict; and
  every refinement message a corpus reaches has a case that produces exactly that
  one issue.
- The comment above the lock table says changing a pin under an unchanged literal
  is the review-blocking act and that no in-repo test can stop a PR rewriting its
  own pins: the merge-gate review is the control.
- The Versioning paragraphs in opportunity.ts, operator-binding.ts and
  capability-kit.ts name the corpus.

Ports the 112b reproduction for MEDIUM 8: a superRefine-only change leaves the
fingerprint unchanged but moves corpus verdicts. The shape pins were re-pinned in
the commits that changed those shapes (releaseDigest, the mask regex).

Agent: implementer-kilo

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj
…r (astra 112b A)

The pinned built-in list has 8 urls because this branch is behind master's
2d80818 (board N64), which registers document-print-and-mail/v1 in
loadBuiltinCsds. The set follows the registry, so the pin fails by design when
this branch merges master; the comment now says so and says what to add.

Agent: implementer-kilo

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj
…ra 112b E)

The tests accepted asOf = now + MAX_AS_OF_SKEW_MS, which only holds while the
clock never steps backwards between computing the instant and parsing it. They
now use 1 s inside the boundary; the refusal cases stay a minute beyond it.

Agent: implementer-kilo

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FT8WYWkfig4KNcxtMqvMuj
…he release contract)

Master carries painpoints' #365 (kit-demand.ts: PUBLIC_RELEASE_POLICY,
isReleasePeriodClosed and PublicOpportunityRelease), which astra 112c asks
demandAggregatesFromRelease to reuse. It also carries 2d80818 (board N64),
which registers document-print-and-mail/v1 in loadBuiltinCsds.

The one conflict-free consequence is planned: the built-in public set
follows the registry, so its pinned list gains that url, in sorted
position, here (the test said to do it in the merge commit).

Spec 1181/1181 (48 files); package tsc and the contract test file's tsc
exit 0.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zYzsSSFUHbPV5DNxssX4A
…e executability, module digests

All three open findings were reproduced at a173743 first (scratchpad
repro-112c: three failing tests plus a passing control, and a mutation
trace for MEDIUM 8).

CRITICAL 1 (demand privacy). demandAggregatesFromRelease now enforces
#365's exact release contract, reused from types/kit-demand.ts (on this
branch since the master merge):
- the record's policy must be PUBLIC_RELEASE_POLICY's k and evidence
  floor exactly, and every aggregate must be counted at that floor
  (strict literals; the verdict's k 0 / query record is refused);
- the period must be releasable under #365's rule, isReleasePeriodClosed:
  ended at least the 24-hour grace ago by this clock;
- PublicOpportunityReleaseRecord is now #365's PublicOpportunityRelease.
A demand_aggregate DTO's asOf must also fall after the period's close
plus that grace, since no release can exist earlier.

HIGH 4 (executability). evidenceIsExecutable checks the CUMULATIVE
program, tier 0 through the target. Each tier gets the refs that support
it and whose dependencies are satisfiable there (fixpoint), so it is the
best assignment the refs allow. computeCsdEligibility's eligibleTier
must then reach the target, with live verifiers. A ref that can
contribute at no tier up to the target fails it. Payer approval alone is
no longer executable at tier 2 or 3, because its tier 1 set is empty
(the verdict's reproduction is a test). Positive tier 1-3 cases mark the
stub ident.registered_key live inside a try/finally, because no tier
1-3 set is executable with today's registry.

MEDIUM 8 (versioning). The lock table also pins the digest of each
literal's own module, so any edit there fails CI until it is
re-pinned. That includes a refinement whose effect lies outside the
finite corpus (the verdict's reproduction now fails on exactly that
pin). The Versioning docs list the inputs that live outside each module.

Corpus: the payer-only accept cases become honest non-executable cases.
New rejects cover payer-only executable claims and reads inside the
grace; the demand accept cases move past the grace. The generator is
deterministic: the binding and kit corpora came out byte-identical.

Tests: spec 1185/1185 (48 files); package tsc and the contract
test-file tsc exit 0. Mutations: 8 of 8 killed (grace check, policy,
counted evidence, DTO grace, single-tier executability, non-contributing
ref, dependency fixpoint, and the out-of-corpus refinement).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zYzsSSFUHbPV5DNxssX4A
…irement

CRITICAL 1 (reproduced at cbc1c14): demandAggregatesFromRelease accepted a
self-consistent release whose aggregates were in reverse code-unit order,
a record #365's buildPublicRelease can never write. The reader now requires
capabilityType strictly increasing in code-unit order (the order #365 sorts
into), so the order carries no information.

HIGH 4 (reproduced): the dependency pass silently removed a named primitive
and then judged the reduced program, so confirm.execution_mode alone was
executable at tier 0, and registered_key + execution_mode (no receipt) at
tier 1 with the key live. Every named ref must now sit in some tier;
otherwise the requirement is not executable. That rule subsumes the old
"supports some tier" pre-check, which is removed. The tier-1 expectation
that pinned the hole is now false.

LOW: the header names every external input (csd/schema.ts, capability-kit.ts).

Corpus: 3 new cases (97). LOCK re-pinned: opportunity corpus 4fb26628cccab46e,
source 4deb00fd1332e7d0; shape unchanged. Spec 1187/1187, both tsc 0,
mutations 11/11 killed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zYzsSSFUHbPV5DNxssX4A
#348 (merged 15:23) and #397 both add exports to packages/spec/src/types/index.ts
after ./ui-artifact.js, which is the only conflict. It is resolved as the union
of both sides, verbatim: #397's capability-kit, operator-binding and
opportunity exports, then #348's render-provenance export. Every other path
that differs from 79a3330 (450 of them) equals master's blob exactly, so no
file was auto-merged. Spec: 73 files, 2255 tests pass; both typechecks are
clean.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zYzsSSFUHbPV5DNxssX4A
@LamaSu
LamaSu marked this pull request as ready for review October 4, 2026 02:25
@LamaSu
LamaSu merged commit ae69fa6 into master Oct 4, 2026
9 checks passed
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