Skip to content

Model the spawn-coherence relation: producer-absence and never-observed are different states - #9268

Merged
briansrls merged 2 commits into
mainfrom
toolchain-coherence-per-spawn
Aug 26, 2026
Merged

briansrls merged 2 commits into
mainfrom
toolchain-coherence-per-spawn

Conversation

@briansrls

@briansrls briansrls commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds gunbc.toolchain_coherence: the relation by which spawn observations are classified as coherent, divergent, or unevaluated. Model-only, by operator ruling.

The question it answers is not digest A == digest B. It is: two spawns claiming to inhabit one tool role, inside one pass, reached the same executable bytes. The subject is ONE SPAWN, not one job — a job that spawns rustfmt eleven times has eleven opportunities to exec different bytes, so a job-grained receipt would report a coherence it never established. The digest is taken at spawn and travels with the observation, because resolving a pathname and re-launching it afterwards is precisely the defect the carrier exists to detect.

What this PR does NOT claim

Stated first, because the ruling turns on it:

  • No production spawn has been observed. Nothing in this repository observes a spawn.
  • No host is established to have used the same executable twice.
  • Toolchain coherence is not guaranteed by anything here, and this carrier must not be cited as coverage that successive spawns reached the same bytes. They are unmeasured.

The relation is terminal; its production standing is a stall. gunbc_toolchain_coherence_disposition records both, so the declaration is not named as though coherence had been achieved (DESIGN §4b(1) rung inflation).

Three states the design turns on

SpawnObservationProducerAbsent ≠ ToolchainNeverObserved. Before this split the fold returned NeverObserved for an empty list, so the first producer wired to the carrier would have rendered its own absence as "we checked and found no comparable pair" — the execution-provenance loss DESIGN names, where a masked run and a clean run render identically. NeverObserved is now reserved for an observation transaction that RAN and found no comparable pair. The fold cannot return ProducerAbsent, which is correct rather than a dead arm: a fold speaks only about the list it was handed; producer absence is a claim about the world.

ToolchainIdentityIncomparable is its own cause. std.content_hash refuses to collapse a cross-family pair. Reporting that as "differed" would manufacture a finding; reporting it as "equal" would manufacture a pass.

Differed is a finding beside NotEvaluated, never inside it. "We could not tell" and "we checked and they differed" have opposite remedies. No outcome projects to Bool.

Grain: the finding must pick the remedy

ToolchainSpawn carries pass, spawn identity, slot, resolved executable, and the observed digest. Without the resolved path, two spawns diverging at ONE path (the binary was replaced under us) and at DIFFERENT paths (we ran two different binaries) collapse into one verdict — opposite repairs. The Differed arm carries both spawns so the consumer can tell them apart. std.types.FilePath is reused rather than minting a path type.

Not a nickname for ObservedToolIdentityDuplicate

That arm folds one ResolvedBuildContext roster and carries only a count, discarding the identities — it cannot answer whether the duplicates agreed. It is a name-multiplicity fact over one snapshot; this is a byte-agreement fact over a spawn sequence.

Next-rung trigger

A spawn operation emits tool identity, resolved executable, executable digest, pass identity and spawn identity at the moment of spawn; two such observations sharing one tool role within one pass are handed to toolchain_coherence_of. That producer belongs to the shared spawn/execution substrate the aggregate-demand instrument will also read — not a capacity-only producer, which would give this fact a second home.

Test plan

claim_batch --source-root dag --source-root src/v2 --hermetic --entry dag/test/claim/toolchain_coherence_witness_test.dag — 6/6 PASS:

witness establishes
no_observation_is_not_evaluated empty folds to NotEvaluated, never agreement
same_bytes_across_passes_is_single three agreeing spawns are Single
differing_bytes_mid_run_is_a_finding discriminating RED — divergence is a finding, not NotEvaluated
one_spawn_is_single one spawn cannot disagree with itself
producer_absent_is_not_never_observed the two absence states are distinct constructions
divergence_carries_the_paths_that_pick_the_remedy asserted in BOTH directions — same-path and different-path divergence are distinguishable

Focused green is preflight; required CI is the completion receipt. The witnesses establish the classifier's distinctions and nothing about any host.

Brian Searls and others added 2 commits August 26, 2026 01:25
…Evaluated, never to agreement

THE SUBJECT IS ONE SPAWN, NOT ONE JOB. A job that spawns rustfmt eleven times has
eleven opportunities to exec different bytes, so a job-grained receipt would report
a coherence it never established. The digest is taken at spawn and travels with the
observation rather than being resolved once and looked up later, because resolving
a pathname and re-launching it afterwards is the defect the carrier exists to detect.

THREE CAUSES, NOT COLLAPSED. "we could not tell" and "we checked and they differed"
have opposite remedies, so ToolchainIdentitiesDiffered is a finding beside
NotEvaluated rather than inside it. Cross-family comparison is its own cause because
std.content_hash refuses to collapse an fnv1a64 fingerprint against a sha256 digest:
reporting that as "differed" would manufacture a finding, reporting it as "equal"
would manufacture a pass.

WHAT THIS IS NOT. It does not nickname ObservedToolIdentityDuplicate. That arm folds
one ResolvedBuildContext roster and carries only a `count`, discarding the identities,
so it cannot answer whether the duplicates agreed -- it is a name-multiplicity fact
over one snapshot, where this is a byte-agreement fact over a spawn sequence.

RUNG, STATED HONESTLY: NOTHING CONSTRUCTS A ToolchainSpawn YET. No producer observes
spawns, so the carrier's live answer today is ToolchainNeverObserved, and it says so
rather than reporting agreement. This is a modeled domain type landing ahead of its
producer, not a closed loop, and it must not be cited as coverage that successive
spawns reached the same bytes -- they are unmeasured.

Four witnesses, executed green via claim_batch --hermetic. The discriminating red is
differing_bytes_mid_run_is_a_finding: one pass reaching different bytes must return
a finding, not NotEvaluated. The empty-list arm is asserted first and deliberately --
if it ever returns Single, absence of evidence has started rendering as a pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…erved, and carry the grain that picks the remedy

Operator ruling: land this as a standalone model-only PR, do not serialize the
session-capacity program behind it. The ruling set a merge bar; three of its
conditions already held (no authored population passed off as a production read,
no Bool collapse of the outcomes, no producer). Three did not.

1. PRODUCER-ABSENT IS NOT NEVER-OBSERVED. The fold returned ToolchainNeverObserved
for an empty list, so the first producer wired to this carrier would have rendered
its OWN ABSENCE as "we checked and found no comparable pair". That is the
execution-provenance loss DESIGN names -- a masked run and a clean run rendering
identically. SpawnObservationProducerAbsent is now its own cause.
The fold cannot RETURN it, and that is correct rather than a dead arm: a fold speaks
only about the list it was handed, while producer absence is a claim about the world
that only a declaration or a real observation transaction may make.

2. THE GRAIN COULD NOT PICK THE REMEDY. ToolchainSpawn carried no resolved
executable and no spawn identity, so two spawns diverging at ONE path (the binary
was replaced under us) and two spawns diverging at DIFFERENT paths (we ran two
different binaries) collapsed into one verdict. They have opposite repairs. The
Differed arm now carries both spawns. std.types.FilePath is reused rather than
minting a path type.

3. THE NEXT-RUNG TRIGGER IS A SEAM, NOT "OBSERVE LATER". The disposition names what
a spawn must emit and where the producer belongs -- the shared spawn/execution
substrate the aggregate-demand instrument will also read, not a capacity-only
producer, which would give this fact a second home.

WHAT THIS PR DOES NOT CLAIM, stated plainly because the ruling turns on it: no
production spawn has been observed, no host is established to have used the same
executable twice, and toolchain coherence is NOT guaranteed by anything here. The
carrier classifies observations it is given. Nothing gives it any. The witnesses
establish the classifier's distinctions and nothing about any host.

Six witnesses via claim_batch --hermetic, all PASS, two of them new:
producer_absent_is_not_never_observed pins the split in (1);
divergence_carries_the_paths_that_pick_the_remedy asserts (2) in both directions,
so the evidence does the work rather than the arm's name.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gunbai-bot gunbai-bot Bot changed the title srv4 memory upgrade Model the spawn-coherence relation: producer-absence and never-observed are different states Aug 26, 2026
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review August 26, 2026 01:38
@briansrls
briansrls merged commit 900135e into main Aug 26, 2026
4 of 6 checks passed
@briansrls
briansrls deleted the toolchain-coherence-per-spawn branch August 26, 2026 02:52
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