Skip to content

v0.26.0: Spec v0.3.0 and Runtime v0.39.0 pins; dialect document checks aligned (#86); dispatch admission acceptance (#147); the 0.3.0 recording set (#174) - #187

Merged
kikashy merged 4 commits into
mainfrom
release-v0.26.0-final
Oct 29, 2026
Merged

kikashy merged 4 commits into
mainfrom
release-v0.26.0-final

Conversation

@kikashy

@kikashy kikashy commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

The v0.26.0 final's pull request: the Spec v0.3.0 and Runtime v0.39.0 pins, and the rows #86, #147 and #174 of the milestone. Release notes and the tag are separate.

Pins

  • Spec v0.3.0. The installed retained-column/1 tuple moves from the v0.3.0-rc.2 candidate (bcac15b7…) to the published v0.3.0 at its signed tag's commit 27cb29bf13e4a2d58d69fe19c7ebfeb762b014fc. Schema sha256:0cb49f23… and corpus sha256:17b16ff7… are byte-identical to rc.2. The leaf corpus is re-imported from the verified archive (sha256:6469a866…) into internal/retainedcolumns/testdata/v0.3.0/, with the archive's release-identity.json and releases/v0.3.0.retained-column.json (file sha256:c8e489b2…) as release-inventory.json. The rc.2 directory is removed: no test needs the candidate's bytes. A new handwritten selection vector names the final tuple; the rc.2 one is kept as history.
  • Runtime v0.39.0. The retained-column companion pin in scripts/checks/build-pinned.sh moves from v0.39.0-alpha.2 (57c854ab…) to v0.39.0 (12793d2d…, the signed tag's commit), whose selector is retained-column/1@27cb29bf…. Both selected real-Runtime lifecycles pass against it. The ordinary, earlier-Runtime, old-Runner and Gateway pins do not move.
  • Tests that named RC2 are renamed TestSelectedLeaf*; the unit partitions, the chain-test must-run list, tools/test_ci_evidence.py and .github/ci-evidence.json follow. New: TestSelectedLeafRefusesTheCandidateIdentity holds that the rc.2 identity is refused by the transport selector, by a capability report and by the public route before any body is read.

This is a change to a public surface: /v1/health's and verify-preparation's reported conversion capability now name Spec 0.3.0 at 27cb29bf…; a caller still selecting the candidate tuple is refused capability-unsupported.

#86 — dialect document checks aligned with the Runtime's tools (bounded)

  • internal/runner/dialect_tools_test.go, run in chain-tests (must run and pass) against the pinned Runtime v0.39.0:
    • every dialect document of the shared fixtures (vectors/preparation, which the Spec publishes as its dialect vectors: 47 vectors, version 2, version 3 bound, version 3 published unbound) is document-valid to the Runtime and read by the Runner, with identical canonical bytes and digest; that digest is each fixture's recorded identity (the preparation's mappingDigest, the binding's published digest), and a named dialect's is the release identity the Runner records. Existing v2 and bound/unbound v3 fixtures keep their identities.
    • five malformed changes (duplicate member, unknown version, fractional number, unknown source member, unknown kind) are refused by both, each with the Runtime's diagnostic code named.
    • document validity is kept apart from readiness: http, sql and web sources are encoded by the Runtime, refused by its validator and refused by name by the Runner; valid operation and research documents are not prepared without installed profiles, binding at import or source records.
  • The boundary is recorded in docs/design/dialect-document-checks.md: the Runner keeps its in-process reader and calls no Runtime document tool when it runs anything (no dependency cycle, no per-stage subprocess, no second validator); CI holds the two together. The Runtime's document tools are pinned to Spec b7cc8906… (not v0.3.0); the tests name that revision and fail if the Runtime moves it.
  • No production code changes for this row.

#147 — dispatch admission acceptance

internal/runner/manual_admission_acceptance_test.go, six tests named for the acceptance cases, against the real Runtime, required by the manual-gate step: omitted-dependency substitution, withdrawal/enqueue/dispatch races, lost admission response, worker restart and fencing, identity revocation, pre-withdrawal restore (20 subtests). No case revealed a missing mechanic, so no production change. The fence is described in ADR-0025's names (owner, expiry, epoch, fence {resource, owner, epoch}) in docs/design/manual-admission-gate.md; the Runner reads an epoch only as a canonical decimal string, and a JSON number is refused (tested). The agent package is not a dependency.

#174 — the 0.3.0 recording set: not published

The set is not generated in this pull request. All 47 scenarios are ordinary mapped-source runs (mapping v2, dialect v3 bound/imported, research, endpoint scope). The Runtime v0.39.0 evaluates Core 0.3.0, but the Runner admits Core 0.3.0 only on the selected retained-column path: an ordinary release is evaluated, and its report checked, under 0.2.0-draft (supportedEvaluatorSpec, expectedEvaluatorSpec). Producing the 47 recordings under 0.3.0 needs an ordinary Core 0.3.0 admission in the Runner first, an admission change no row of this milestone names, so all 47 scenarios are blocked for that one reason. No claim of 0.3.0 producer coverage is made. The historical 0.2.0-draft set is unchanged.

Also

docs/releasing.md names the two prerelease forms: -rc.N a candidate of the complete milestone, -alpha.N a scoped cut for a named consumer that never graduates.

Safeguards and mutants

Each new safeguard test was run against a broken copy of the code it guards and failed:

  • installed pin set back to rc.2 → TestSelectedLeafRefusesTheCandidateIdentity and the installed-identity test fail;
  • the Runner's web kind refusal removed → the readiness test fails; the dialect digest taken without canonicalization → the agreement test fails;
  • the attempt-journal replay barrier removed → lost-start and restart/fencing fail; the reservation's release-digest check removed → release-dependency substitution fails; the eligibility check at submission removed → withdrawn-before-enqueue fails; a recovered reservation allowed to continue → pre-withdrawal restore fails.

Checks

jpdev check --changed origin/main (devkit v0.11.0), locally:

== check summary (dev.json) ==
handover: boundary 2 paths, shared 2 paths; see AGENTS.md "Before you hand over"
PASS     local-paths              0s
PASS     agent-docs               0s
    sync-agent-docs: CLAUDE.md matches AGENTS.md
PASS     diff-check               0s
PASS     go-fmt                   0s
PASS     go-vet                   19s
PASS     build-pinned             31s
PASS     manual-gate              31s
    19 lines matched; last: ok  	github.com/Judgment-Pack/judgment-pack-runner/internal/runner	23.942s
PASS     unit-tests               1349s
    60 lines matched; last: ok  	github.com/Judgment-Pack/judgment-pack-runner/internal/runner	228.908s
PASS     chain-tests              42s
    ok  	github.com/Judgment-Pack/judgment-pack-runner/internal/runner	15.474s
    ok  	github.com/Judgment-Pack/judgment-pack-runner/cmd/jpack-runner	5.848s
PASS     build                    124s
PASS     ci-evidence              6s, python 3.8
    Ran 15 tests in 6.612s
    OK
PASS     third-party-notices      0s, python 3.8
== 12 passed, 0 failed, 0 skipped, 0 not run ==

Builder review: same-vendor (Claude Opus build); not a cross-vendor claim.

🤖 Generated with Claude Code

kikashy and others added 4 commits October 29, 2026 05:19
…ntime v0.39.0

The installed retained-column/1 tuple moves from the v0.3.0-rc.2 candidate
(bcac15b7) to the published v0.3.0 (27cb29bf, the signed tag). Schema and
corpus are byte-identical; the leaf corpus moves to testdata/v0.3.0 with the
final archive's release identity and retained-column inventory. The rc.2
directory is removed: no test needs the candidate's bytes. The candidate's
handwritten selection vector stays, and a new test holds that its identity is
refused by the selector, by a capability report and by the public route.

The retained-column companion pin moves from v0.39.0-alpha.2 to v0.39.0
(12793d2d), whose selector is retained-column/1@27cb29bf. Tests that named RC2
are renamed TestSelectedLeaf*; the partitions, must-run lists and the CI
evidence configuration follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>
The shared fixtures (vectors/preparation, the specification's dialect
vectors) go through the pinned Runtime v0.39.0 document tools and the
Runner's reader: every document, version 2, version 3 bound and version 3
published unbound, is valid to both with the same canonical bytes and digest,
and that digest is the identity each fixture records (the preparation's
mappingDigest, the binding's published digest, a named dialect's release
identity). Five malformed changes are refused by both. Document validity is
kept apart from readiness: kinds http, sql and web are encoded by the Runtime,
refused by its validator and refused by name by the Runner; valid operation
and research documents are not prepared without installed profiles, binding
at import or source records.

The Runner calls no Runtime document tool at run time; the boundary and its
alternatives are recorded in docs/design/dialect-document-checks.md. The three
tests must run and pass in chain-tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>
Six tests, each named for an acceptance case, run governed manual runs
against the real Runtime with a transport peer standing in for the anchored
authority: omitted-dependency substitution (a caller-supplied closure, a
substituted release dependency, an omitted lock/config/Pack, a substituted
Runtime), withdrawal/enqueue/dispatch races (before enqueue, between enqueue
and dispatch, before the start commitment, after it with a sibling queued),
lost admission responses (reserve and start), worker restart and fencing (a
new epoch fences the old reservation; another epoch's reply, stale-epoch and
an epoch written as a JSON number are refused), identity revocation, and
pre-withdrawal restore (a restored intent answered recovered or refused, a
restored queue after withdrawal). No case needed a new mechanic.

The fence is described in ADR-0025's names ({resource, owner, epoch}) in
docs/design/manual-admission-gate.md; the manual-gate step requires the six
tests to run and pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>
-rc.N is a candidate of the complete milestone; -alpha.N is a scoped cut for
a named consumer that never graduates, as the development kit's release
policy defines them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>
@kikashy

kikashy commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Verdict: MERGE AND RELEASE — 0 HIGH, 1 MEDIUM (cured by the v0.26.0 notes, no code change needed), 6 LOW. Depth 2: every material PR in v0.25.0..8a1ef95 has its own review record.

Clean-room review: runner PR #187 (v0.26.0 final prep), plus the pre-release pass over v0.25.0..8a1ef95

Depth 1: PR #187

1. Pins

  • Installed tuple. installedConversionSpec = {0.3.0, 27cb29bf…, schema 0cb49f23…, corpus 17b16ff7…}. The Spec tag v0.3.0 (annotated tag f4535372) dereferences to 27cb29bf13e4a2d58d69fe19c7ebfeb762b014fc. EXACT.

  • Fixture bytes. The archive sha256 6469a866… was verified. I compared the archive entries to the files in internal/retainedcolumns/testdata/v0.3.0/. Seven are byte-identical: capabilities, cases, examples, manifest, preparations, contract.schema.json, release-identity.json (sha256 9432a9f5…) and releases/v0.3.0.retained-column.json → release-inventory.json (sha256 c8e489b2…, as the brief states). I also recomputed all 27 hashes in the inventory against the archive, with 0 mismatches.

  • The fixture README. testdata/v0.3.0/README.md is a note the Runner wrote. It is not the archive's conformance/retained-column/1/README.md. See L-5.

  • rc.2 removal. The rc.2 directory is gone. The remaining rc.2 mentions are:

    • historical docs: the alpha notes and the design note;
    • the published preparations.json synthetic installation, used only in tests through leafSyntheticInstallation(), a _test.go function;
    • the refusal assertions;
    • vectors/retained-column-selection-rc2.json, kept as history.

    No production path assigns installedConversionSpec. I found no path that admits rc.2.

  • Entry points of TestSelectedLeafRefusesTheCandidateIdentity. It covers three: the selector (ParseConversionSelector), the capability report (strictJSON → ConversionCapability.validate) and the HTTP route (refused before any body read). A fourth class it does not drive is the body-embedded selection:

    • validateStoredInput, which runs on release and run writes and on release(id) reads;
    • supportedEvaluatorSpec at release creation;
    • ConversionExportLimit and exportWithinLimits, used by verify and verify-preparation.

    The --conversion-contract flags of those two CLI commands go through ParseConversionSelector. Every path ends in the same exact ConversionContract.validate. Probe P1 shows each one refuses rc.2 as capability-unsupported, and mutant M1 kills the probe too. See L-1.

  • Companion pin. scripts/checks/build-pinned.sh pins the companion at 12793d2dd24d…. The tag v0.39.0 (annotated tag 5bdf2352) dereferences to that commit. EXACT. The binary reports jpack 0.39.0 and JPS 0.3.0.

  • Unmoved pins. All are deliberate and consistent with their comments:

    • The ordinary Runtime 09206e74 (0.33.0-dev; the binding and signing tests need it). It has been unchanged since v0.24.0, and vectors/preparation/README.md cites the same commit.
    • The earlier Runtime 7a2fcfe2, which the tag v0.27.1 dereferences to exactly.
    • The Gateway 5b49e1e0, which v0.9.1 dereferences to exactly.
    • The old Runner 1cc6b298, test-only.

    The partitions (TestSelectedLeaf), the chain and manual-gate must-run lists, tools/test_ci_evidence.py and .github/ci-evidence.json all followed the rename consistently.

2. #86, the dialect document checks

  • The pin difference is correct and documented. It is not stale in the Runner. The built v0.39.0 binary's jpack dialect contract reports specRevision b7cc8906c23d…. That pin belongs to the Runtime, the Runner cannot move it, and the test fails if the Runtime moves it. The design note and the test constant both name it. However, spec/dialect.md is not the same at b7cc8906 (sha256 4a4c3ebc…) and at v0.3.0 (729e491a…). Between them are 60 commits, including spec #239 (ceiling profiles), #241 (data quality and typed research results) and #248 (the 0.3.0 re-pin). See L-4: the notes must not claim dialect agreement at Spec v0.3.0.
  • Both sides are compared. The Runtime side is a real subprocess of the pinned binary. The test checks specRevision, status, the canonical document and the digest. The Runner side is production code: strictJSON into InputMapping, mappingV2Hash, releaseDialectIdentity and prepareV2. The digest must equal both digest(canon) and the Runner's mappingV2Hash, and the fixture's recorded mappingDigest or binding digest. A stubbed Runtime would have to re-implement canonicalization and match the pinned revision. A stubbed Runner side is not possible, because the test calls production functions.
  • Counts. The run gave 47 vector subtests and 5×2 refusals.
  • Refusal reasons. The Runner-side refusal assertion accepts any error. Probe P4 shows the actual reasons do correspond: duplicate key, non-integer maxAge and unknown field are refused at decode; version 4 and an unknown kind are refused in prepareV2. See L-6.

3. #147, the dispatch admission acceptance cases

  • The races are sequential stand-ins. They are deterministic interleavings injected at the authority peer: the callback flips "withdrawn" between reserve and commitStart. The dispatcher is disabled and there are no concurrent goroutines. This is a sound way to pin orderings, but it is not real concurrency.

    I added two real-concurrency probes:

    • P2: 4 goroutines × 5 iterations run dispatchNext on one governed run, under -race. Each time the result was exactly 1 reserve, 1 commitStart and a completed run. The race detector was clean.
    • P3: a withdrawal committed from another goroutine during the reservation. It was never launched after a blocked decision.

    The claim CAS that protects concurrent dispatch is pinned by TestStoreKindsConform, not by the Retain governing policy adoption and enforce current run admission at dispatch #147 tests (mutant M5).

  • Subtest count. The PR body says 20 subtests; the run gives 21. See L-2.

  • The fencing vocabulary is consistent with agent PR #251 (ADR-0025, open, head 67e25ed). It uses the names owner, expiry, epoch, fence {resource, owner, epoch}, and epoch as a canonical base-10 string, never a JSON number. The Runner reads an epoch as a string field, so a JSON number fails to decode, and manualPositive requires the canonical decimal form. The epoch-as-number-refused subtest covers activate.

    The domains differ. ADR-0025 bounds the epoch at 1..999999999999999999 (18 digits) and refuses an overflowed epoch. The Runner accepts 1..2^64-1. The note's word "contains" is accurate, but the Runner admits epochs the ADR refuses. See L-3.

4. docs/releasing.md

  • The sentence matches the devkit policy (devkit docs/policy.md): -rc.N is a candidate of the complete milestone, and -alpha.N is a scoped cut for a named consumer that never graduates.
  • The policy text is in kit v0.12.0 only. Kit v0.11.0 has no alpha.N text, and the Runner vendors v0.11.0. See L-2.
  • Release-notes obligation. The same policy requires the final's notes to list what the alphas carried.

Probes

All the probe code was a test-only file in <scratch>/probes/ and was removed from the clone after the runs.

# Probe Result
P1 rc.2 embedded in a body: validateStoredInput, supportedEvaluatorSpec("0.3.0", rc2), ConversionExportLimit, decode then validate. Also: the final tuple and the new retained-column-selection-v0.3.0.json selection are admitted All rc.2 paths refuse capability-unsupported. The final tuple and the v0.3.0 vector are admitted
P2 Real concurrency: 4 dispatchers race for one governed queued run, 5 iterations, also under -race Exactly 1 reserve, 1 commitStart and 1 completed run each time. Race detector clean
P3 Real concurrency: withdrawal commits from another goroutine during the reservation, 5 iterations Always blocked and never launched. The invariant "no launch after a blocked decision" holds
P4 The Runner's reason for each of the 5 malformed changes Duplicate key, fractional number and unknown field are refused at decode. Version 4 and an unknown kind are refused in prepareV2
P5 Pin and tag dereference: 12793d2d=v0.39.0, 7a2fcfe2=v0.27.1, 5b49e1e0=gateway v0.9.1, 27cb29bf=spec v0.3.0 All exact
P6 The 7 leaf files plus identity plus inventory against the archive, and the 27 inventory hashes against the archive Identical, 0 mismatches
P7 The companion's dialect contract specRevision b7cc8906…, tool version 0.39.0
P8 The new tests on the head (baseline) 11/11 top-level PASS: selected leaf ×2, dialect ×3, governed ×6 (21 subtests)

Mutants

Each mutant was applied to the room's clone and reverted. The tests ran against the pinned Runtimes.

# Origin Mutation Expected killer Result
M1 builder installedConversionSpec set back to rc.2 (0.3.0-rc.2, bcac15b7…) TestSelectedLeafRefusesTheCandidateIdentity, installed-identity test KILLED (both, plus P1)
M2 builder eligibility check at submission removed (if false && !observation.Eligible) …WithdrawalEnqueueDispatchRaces/withdrawn-before-enqueue KILLED
M3 builder reservation release-digest check removed (manualReserve returns true) …OmittedDependencySubstitution/release-dependency-substituted KILLED
M4 own (race/fence) out.Epoch != c.epoch removed from the Reserve reply check …WorkerRestartAndFencing/reply-of-another-epoch SURVIVED. The later commitStart check (request.Epoch != c.epoch, client line 93) still blocks the start. M4b (both removed) is KILLED. See L-3
M5 own (race) dispatcher claim CAS removed (changeQueuedRun expects anyState) the #147 race tests Not killed by #147. KILLED by P2 and by TestStoreKindsConform (a run read queued is claimed once, concurrent claimers grant every run once)

Findings

ID Sev Finding Disposition
M-1 MEDIUM Undocumented upgrade effect for the alpha consumer. Stored releases and runs created under v0.26.0-alpha.1/.2 carry the rc.2 selection. Under the final, release(id) reads fail as faultUnreadable (validateStoredRelease → validate → capability-unsupported), while lists still show them. Alpha-era selected exports cannot be verified, because --conversion-contract with rc.2 is refused and the embedded selection fails exportWithinLimits. The PR states only that a caller selecting rc.2 is refused Cure in the v0.26.0 notes: state that alpha-era selected releases, runs and exports are not carried and must be re-created under the final. No code change
L-1 LOW The rc.2 refusal test does not drive a selection embedded in a body (stored input, release evaluator admission, export ceiling). They all share the one exact validate, M1 is killed, and P1 confirms each path refuses Optional: add a body-embedded rc.2 row
L-2 LOW Wording and counts: the PR body says 20 subtests and the run gives 21. docs/releasing.md cites "the development kit's release policy", but the forms appear only in kit v0.12.0 while the repository vendors v0.11.0 Fix the count in the notes. Name kit v0.12.0 or keep the sentence free-standing
L-3 LOW Fence and epoch. (a) The Reserve-reply epoch check is not pinned at its own point (M4 survives). The end property, no start under a foreign epoch, is held by the redundant commitStart check. (b) The Runner's epoch domain is 1..2^64-1, a superset of ADR-0025's 18-digit bound; documented as "contains" (a) Optional: assert that Reserve itself refuses and that no epoch-2 reservation is journaled. (b) Accept as documented, or bound to 18 digits when ADR-0025 merges
L-4 LOW The #86 agreement is at Spec b7cc8906, not at v0.3.0, and spec/dialect.md differs between the two (4a4c3ebc vs 729e491a). The design note names the revision but does not say it predates v0.3.0's dialect text Notes: say "agreement with the Runtime v0.39.0 document tools (Spec b7cc8906)", never "at Spec v0.3.0"
L-5 LOW testdata/v0.3.0/README.md is a note the Runner wrote and has the same filename as the inventory's conformance/retained-column/1/README.md (dc4ee78b) without being those bytes. No test reads it, and the same pattern was in rc2 Optional rename, for example IMPORT.md
L-6 LOW #147 races are deterministic interleavings, and the #86 refusal test accepts any Runner error. P2, P3 and P4 show the claims hold under real concurrency and the reasons correspond Describe them as "ordered interleavings" in the notes. No code change

No HIGH. All the pinned bytes are exact and verifiable, rc.2 is admitted nowhere, every #147 safeguard held under the race it names (including real-concurrency probes), and the range has no security-boundary defect.

Depth 2: the pre-release pass over v0.25.0 (604c1b7)..8a1ef95

PR Title (short) Material? Own review record Notes
#181 Re-vendor devkit scripts at v0.10.0 No: 3 header-comment lines none Diff read: comments only
#182 Release: reuse main's exact-commit CI evidence at the tag (#175) Yes: the release gate Yes: a clean-room record, same-vendor, no HIGH, 2 MEDIUM fixed in-PR, confirmations at eef5b29 and fcd15ed The merged head 4b901b3 is one commit past the last confirmed head. That delta is docs, vendored-header and one kit-hash line; tag.prerelease: true was already in the reviewed head. Not material
#183 Selected retained-column Base consumer for Agent v0.11 (alpha.1) Yes: record formats, public surface, security boundary Yes: an independent review with 2 MEDIUM, a finding-specific confirmation (both CLOSED) at b684369, which equals the merged head —
#184 Bounded CI headroom (alpha.2) Config/docs Yes: a bounded delta confirmation at 455331a, which equals the merged head, plus a publication check —
#187 This PR Yes This record —

No material PR in the range is unreviewed, so no extra depth-2 probes were needed.

Public changes for the release notes

What was not established

@kikashy
kikashy merged commit 1acb8b2 into main Oct 29, 2026
5 checks passed
@kikashy
kikashy deleted the release-v0.26.0-final branch October 29, 2026 10:22
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