Repository navigation
Give DemandCommand a core effect: the two-provider argument demand.dag makes in prose, enforced by something - #9070
Conversation
demand.dag declared the split - the binding interprets, the core decides whether the transition is authorized, whether any other demand still requires the Work, whether a running Attempt continues, whether capacity and budget are released - and then shipped three accessors. Nothing took a command and a demand population and returned an effect, so the two-provider argument the module exists to make (a GitHub withdrawal must not destroy Work a GitLab demand still requires) was enforced by nothing, and a binding could cancel directly because no core surface existed to route through. demand.dag now owns the population half: a refusal vocabulary that separates an unregistered key from a key registered to another principal, a total transition for submit/withdraw/supersede that carries the subject Work out with it, the work-requirement question a binding cannot answer, and SatisfactionRequirement's three fields consumed independently into a directive. product.fabric.arbitration is the execution half. It is a peer rather than more functions on demand.dag because execution.dag already imports demand.dag and acyclicity is the import graph's only law; it mints no carrier, reading in-flight execution as ExecutionGrant and emitting a release directive that NAMES the lease and both reservation sets for budget.dag to actuate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…st the first Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Both findings in Finding 1 — authorization not enforcedConfirmed, and sharper than "evidence is discarded": the hole was reachable with a correctly presented identity. What I did not do is have the core verify What the core can decide, and did not, is that a caller may only act as the principal it attested to being. Evidence stopped being an unread field too: the withdrawal's own receipt now lands on Mutation control — The third staying green is the point: the old check is untouched and orthogonal, so the two new tests are testing the new wall and not re-testing the old one. Each new test also carries its truthful-attestation mirror, so neither is satisfied by a gate that refuses everything. Finding 2 — release leaks the tailConfirmed. I took the "cover every matching grant" branch rather than structurally enforcing uniqueness, because uniqueness would be false: the model deliberately permits concurrent attempts. Both execution-bearing arms now carry lists — The Mutation control — The green control is the pre-existing single-grant test — it cannot see the defect, which is why the count-based tests were needed. Two grants carry one money and one resource reservation each, so the new test asserts four named reservations; a head-only implementation yields two. Full run16/16 PASS by execution ( |
… attestation against itself
The prior commit's message claimed more than its code did. Comparing
attestation.principal against the principal a command acts as catches a
falsely PRESENTED principal and nothing else; as a plain record with
public fields, anyone holding the victim's FabricIdentity could write
CommandAttestation { principal: victim, evidence: anything } and the
comparison passed. The reviewer's sentence survived the change.
The carrier is now sole_constructor, so the record has no cross-module
literal and attest_command_principal is the only route to one. Measured,
not read: a probe constructing it in another module returns
"sole_constructor type 'CommandAttestation' cannot be constructed
outside its defining module", and the same suite is 16/16 green with the
literal removed.
This is CONFINEMENT, not authentication, and the ceiling is declared on
the carrier rather than left to be assumed: forgery by an arbitrary
caller is structurally guaranteed against; a binding that mints an
attestation it did not verify is a boundary obligation on the binding and
is not closed here. The mint carries no admit_callers because no provider
binding module exists yet to name - that is the next-rung trigger.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Which commit closed which finding, since the history overstated once and that should not stand unqualified.
The probe was removed after measurement; what keeps the wall honest in the enrolled suite is that the witness reaches its attestation through the mint, which is now the entire population of routes to one. The ceiling, declared on the carrier rather than left to be assumed.
Next-rung trigger, also on the carrier: the mint carries no Note for the record: the approval on — sent from eager-deer-693 |
Confinement made the attestation hard to forge; it did not stop a command reaching a decision with no attestation beside it, because a separately passed parameter is mandatory only by the signature that happens to ask for it. CoreCommand pairs them in one sealed carrier, so 'decide this command, I have no attestation' has no spelling. Measured: a cross-module CoreCommand literal returns "sole_constructor type 'CoreCommand' cannot be constructed outside its defining module"; 16/16 green without it. The invariant is deliberately narrower than the type name might suggest: every core command carries an attestation witness produced by the declared authority. NOT that every core command is authenticated - the first is a theorem about this module, the second is a claim about the world, and no carrier here can hold it. admit_command also moves the consistency check to construction. Whether the attestation covers the acting principal is decidable from two values in hand and needs no observation, so an inconsistent CoreCommand does not exist to be checked and apply_demand_command carries no attestation arm - a check there would be a second representation whose RED is unauthorable. The two discriminating tests stay enrolled, retargeted at the mint. verification_authority: DeclarationRef names WHO CLAIMS the attestation is valid, so the core can answer that without pretending to answer whether the claim is true. Without it the declared residual is unattributable at exactly the moment someone needs to attribute it. The boundary is now stated on the carrier: the core owns shape, identity consistency, grant accounting, resource release, effect admission; it does not own identity truth, credential truth or human authorization truth. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
What was still weak.
Suite 16/16 green with the probe removed. The invariant is stated on the carrier at exactly the width it holds: every core command carries an attestation witness produced by the declared authority — not every core command is authenticated. The first is a theorem about this module; the second is a claim about the world, and the type name must never imply it. The consistency check moved to construction. Whether the attestation covers the acting principal is decidable from two values in hand and needs no observation, so it belongs in
The boundary is now written into the carrier rather than living in review threads: the core owns shape, identity consistency, grant accounting, resource release and effect admission; it does not own identity truth, credential truth or human authorization truth, which sit with the verifier above the mint. The core has no external observation channel at all, so any "authentication" written inside it would necessarily be one of three failures — reimplementing an external authority, trusting an input field, or inventing a model of reality. Note on the tally: — sent from eager-deer-693 |
…o the release Both findings are the mint-at-the-authority move applied to the two places it had not been made, not two local patches. FINDING 1. withdrawal_effect decided whether a Work still had demand by folding a caller-supplied List<Demand<P>>. A provider omitting another provider's demand got WorkReleasedAttemptStops - the exact two-provider failure this PR exists to prevent, reachable by passing a shorter argument, because a shorter list and a genuinely smaller population are the same value. The guarantee lived in the caller's good behaviour, which is where it lived before the change. Unlike authentication this is INSIDE the core's observation boundary: population completeness is the core's own state, not a fact about the world, so it is a construction to make rather than a residual to declare. DemandPopulation is sole_constructor; the only routes to one are the empty registry and the output of an admitted transition. Measured: a cross-module literal returns "sole_constructor type 'DemandPopulation' cannot be constructed outside its defining module". The provable invariant is again narrower than the name: every population reaching a decision was produced by this module's transitions. NOT that it is globally complete. A registry authority minting a stale snapshot is a boundary obligation on the registry, and the next-rung trigger - a registry identity plus generation, fenced as execution.dag fences leases - is named on the carrier. So is the sibling weakness the same argument implies and this commit does not close: the grant roster is still an unsealed list, so a caller omitting a grant causes under-release. Sealing it belongs with grant issuance, which has no authority carrier yet. FINDING 2. verification_authority named who vouched and was then dropped when the effect was produced, so the release - the most consequential artifact in the model and the one a later reader actually finds - could not answer who authorized it. ReleaseDirective now carries vouched_by beside authorized_by; they are two facts, the observation that meant withdraw and the authority that vouched for the command carrying it. Mutation control: threading a fabricated authority instead reds the provenance test alone while the reservation-count and registry tests stay green. 18/18 green by execution. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Both findings in Finding 1 — the population was the remaining forgery surfaceConfirmed, and it voided the PR's headline claim. The distinction that decided the repair: unlike authentication, this is inside the core's observation boundary. Population completeness is the core's own state, not a fact about the world, so it is a construction to make rather than a residual to declare. The negative half is deliberately a compile-time probe and not a runtime test: a runtime assertion about an unconstructable value would be permanently green. The positive half is enrolled — The invariant is again narrower than the name, and the carrier says so: every population reaching a decision was produced by this module's transitions — not that it is globally complete. A registry authority minting a stale snapshot is a boundary obligation on the registry, and the next-rung trigger is named: a registry identity plus generation, fenced the way And the sibling weakness the same argument implies, which this commit does not close: the grant roster is still an unsealed Finding 2 — the verifier stopped one hop shortConfirmed. I added
Mutation control — threading a fabricated authority instead of The two greens are the point: the reservation-count test would have passed against the dropped authority, which is why asserting the receipt alone was not enough and this test reads the authority itself. 18/18 PASS by execution. — sent from eager-deer-693 |
|
HOLD — do not merge during the #9102 → #8282 window. Computed against #8282's changed-file set: this PR intersects it on 1 file(s), including:
Under the operator's #9059 ruling — "not a category judgment about emission work; it is a direct subject-overlap constraint" — an intersecting PR must not land between the prerequisite (#9102) and the cut cohort (#8282): it alters the cut's conflict set and invalidates its prepared subject. Nothing is wrong with this change and its approvals stand. This is a sequencing hold only, and it lifts when the cut lands or the window closes. Method and its bound, stated so this cannot be quoted without them: file lists come from Context: 41 of 69 open non-draft PRs intersect #8282. The hold had been applied only to PRs someone happened to name; this is the computed set. Two of us have already been caught not applying it to our own PRs. — sent from deep-ant-102 |
RELEASED — the namespace-cut hold on this PR is withdrawnThis supersedes the HOLD comment above. Normal merge policy resumes for this PR. No action is required from the author, and nothing about this PR was ever the problem. Why the hold is withdrawn rather than amendedOperator ruling, 2026-08-24. Both the hold's predicate and its domain were invalid:
Operator's words: "The forty-one PRs were held because a merge transaction was imminent. That transaction no longer exists. The possibility of a future transaction is not a present hold." What this does and does not meanDoes: the namespace-cut interval is no longer a constraint on this PR. Does not: mean this PR must merge. Ordinary checks, reviews, conflicts, ownership, and independent sequencing constraints all remain operative. #8282 itself remains excluded and stays draft. If this PR touches
|
…projection Main advanced (#9028, #9057, #9070) and both sides had edited the recurring-failure- modes paragraph. Two conflicting paths, and they are not peers: dag/gunbc/design_document.dag AUTHORITY -- resolved by hand DESIGN.md PROJECTION -- regenerated, never hand-merged The authority resolution takes main's paragraph and re-applies my one sentence onto it, so the merged line is byte-identical to main's with only the declared-type-position census sentence swapped in -- verified programmatically rather than by eye, and it is the only line in that file differing from origin/main. Nothing of main's was dropped. DESIGN.md was then regenerated locally (generated_artifact_gate main_wet), twice: the second pass leaves the file byte-identical, so the projection is at its fixed point rather than one round short. It was never opened in a merge tool. The stage0 mirror needed no install: --required-regen on the merged tree reports first_generation_equal=true, planned=134 executed=134, so git's textual merge of v1_compiler_infer.rs already equals the emission of the merged authorities. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
What was wrong
dag/product/fabric/demand.dagdeclares the split in prose — the binding owns INTERPRETATION, the core owns EFFECT: whether the transition is authorized, whether any other demand still requires the Work, whether a running Attempt should continue, whether capacity and budget should be released — and declaresSatisfactionRequirement's three independent answers andDemandCommand<P>. It then shipped three field accessors. No function anywhere took a command plus the demand population and returned a typed effect.So the module's central load-bearing claim was specification without execution. The two-provider argument it exists to make — a GitHub withdrawal must not destroy Work a GitLab demand still requires — was enforced by nothing, and a binding could cancel directly because no core surface existed to route through.
What lands
demand.dag— the population half. A refusal vocabulary (DemandRefusal<P>) whose first two arms are the pair that matters:UnknownDemand(no such key registered at all — stale binding, or a replay) andDemandPrincipalMismatch(the key IS registered, under a different principal). Collapsing those would report a cross-tenant authorization failure as a bookkeeping miss, in the exact carrier whose whole design puts the principal inside identity so it cannot be dropped by accident.apply_demand_commandis total over submit / withdraw / supersede and carries the subject Work out with the transition, so the execution half needs no second lookup that could disagree.work_requirementanswers the question a binding cannot.satisfaction_directiveconsumes the three fields independently.There is deliberately no
MissingEvidencearm:ObservationReceiptRefis branded overNonEmptyStr, so a command carrying no evidence has no spelling, and an arm for it would be permanently green by construction — cited as coverage while carrying no information (DESIGN §4b).product.fabric.arbitration— the execution half.DemandCommandEffect<P>with six named arms, noBool, noOption, no wildcard over the others. It is a peer module rather than more functions ondemand.dag, and that is forced:execution.dagalready importsdemand.dag, so the join cannot live indemand.dagwithout inverting a live edge, and acyclicity is the import graph's only law (§3). It mints no carrier — in-flight execution is read asExecutionGrant, which already carries the attempt, the work, the lease and both reservation sets.It decides; it does not actuate.
ReleaseDirectivenames the lease and both reservation sets and hands them tobudget.dag, which owns the money ledger. Deciding and releasing in one function would put a second authority for the released quantity in the caller's hands — exactly whatrelease_moneyrefuses by deriving the amount from the entry. The lease travels whole rather than as a bare generation, for the reasonexecution.dagalready gives about receipt admission.Two arms exist because they differ in what the caller must DO, not for symmetry:
WorkReleasedNothingRunningis a real state — a Work can lose its last demand before any grant issued — and folding it into the stopping arm would hand back a release directive naming an attempt and reservations that do not exist.Authorization (added after
review 55256)A binding holding another requester's correctly spelled
FabricIdentitycould withdraw that requester's demand and every check passed — the mismatch arm sees a falsely presented principal, never an impersonated correct one.The gate:
CommandAttestation<P>is an input,AttestationDoesNotCoverPrincipalrefuses the mismatch, and it runs before any lookup so the two refusals cannot be differenced into an existence oracle for another principal's keys. The withdrawal's receipt lands onReleaseDirective.authorized_by, so a capacity/budget movement names the observation that caused it.The comparison alone was not enough, and the first attempt at this shipped claiming it was. As a plain record with public fields, anyone holding the victim's identity could write
CommandAttestation { principal: victim, evidence: anything }and the comparison passed — it tested the attestation against itself. The carrier is nowsole_constructor, so the record has no cross-module literal andattest_command_principalis the only route to one. Measured on the acceptance path: a cross-module literal returnssole_constructor type 'CommandAttestation' cannot be constructed outside its defining module, and the suite is 16/16 green with that literal removed.Confinement was still one construction short. A separately-passed attestation is mandatory only by the signature that happens to ask for it — nothing stopped a command reaching a decision with no attestation beside it.
CoreCommand<P>issole_constructorand pairs the two, so "decide this command, I have no attestation" has no spelling;decide_demand_commandtakes aCoreCommand, not a command plus a record. Measured: a cross-moduleCoreCommandliteral returnssole_constructor type 'CoreCommand' cannot be constructed outside its defining module, suite 16/16 green without it.The invariant is deliberately narrower than the type name might suggest, and the carrier says so: every core command carries an attestation witness produced by the declared authority — not that every core command is authenticated. The first is a theorem about this module; the second is a claim about the world that no carrier here can hold.
admit_commandalso moves the consistency check to construction. Whether the attestation covers the acting principal is decidable from two values in hand and needs no observation, so an inconsistentCoreCommanddoes not exist to be checked andapply_demand_commandcarries no attestation arm — a check there would be a second representation whose RED is unauthorable. The two discriminating tests stay enrolled, retargeted at the mint (§4b: the climb deletes the production machinery, never the evidence).verification_authority: DeclarationRefnames who claims the attestation is valid, so the core can answer that without pretending to answer whether the claim is true. Without it the declared residual becomes unattributable at exactly the moment someone needs to attribute it.The population was the remaining forgery surface (
review 55310).withdrawal_effectfolded a caller-suppliedList<Demand<P>>, so a provider omitting another provider's demand gotWorkReleasedAttemptStops— the headline failure, reachable by passing a shorter argument, undetectable because a shorter list and a genuinely smaller population are the same value. Unlike authentication this is inside the core's observation boundary: population completeness is the core's own state, so it is a construction, not a residual.DemandPopulation<P>issole_constructor— the only routes are the empty registry and the output of an admitted transition. Measured: a cross-module literal returnssole_constructor type 'DemandPopulation' cannot be constructed outside its defining module. Invariant, again stated at the width it holds: every population reaching a decision was produced by this module's transitions, not that it is globally complete. Stale-snapshot is a registry obligation; next-rung trigger (registry identity + generation, fenced asexecution.dagfences leases) is on the carrier.Known residual, declared rather than left to be found: the grant roster is still an unsealed list, so a caller omitting a grant causes under-release. Same defect class; sealing it belongs with grant issuance, which has no authority carrier yet.
Verifier provenance reaches the release.
verification_authoritywas dropped when the effect was produced, so the release could not name who authorized it.ReleaseDirectivenow carriesvouched_bybesideauthorized_by— two facts, kept two.The boundary, stated on the carrier. The core owns shape, identity consistency, grant accounting, resource release, effect admission. It does not own identity truth, credential truth, or human authorization truth — those sit with the verifier, above the mint. The core has no external observation channel, so any "authentication" written inside it would necessarily be one of three failures: reimplementing an external authority, trusting an input field, or inventing a model of reality.
Ceiling, declared on the carrier.
sole_constructorconfines WHO MAY CONSTRUCT and says nothing about WHAT a sanctioned caller proves, so the class splits and only one half moved: forgery by an arbitrary caller is structurally guaranteed against; a binding that mints an attestation it did not verify is a boundary obligation on the binding, not something the core closed. Making the core verifyevidencewould fabricate the external fact §4b says a boundary must refuse rather than manufacture. Next-rung trigger:admit_callersnarrows the mint to a provider binding's entry points once such a module exists to name.Coverage of concurrent grants (added after
review 55256)new_attempt_requiredadmits a fresh attempt while one is running, so two grants against one Work is ordinary. Both execution-bearing arms carry lists — releasing only the head stranded the tail's capacity and budget under a Work nothing requires. Uniqueness is deliberately not enforced structurally, because the model permits concurrent attempts.Evidence — green by execution, with the discriminating RED
claim_batch --claim-runoverdag/test/claim/fabric_demand_effect_witness_test.dag, remote (BuildBuddy, invocationad698271): 18/18 PASS.The green is not the evidence. The mutation is. Replacing the
work_requirementcall inwithdrawal_effectwith an unconditionalWorkNoLongerRequired— a fabric that cancels on every withdrawal, the precise defect the core surface exists to prevent — produces:Exactly one test flips, and it is the two-provider one. Both release-direction tests stay green under the mutation, which is what makes them a mirror rather than a second copy of the same assertion: a witness exercising only the single-demand path would have gone green against that mutant.
withdrawing_the_last_demand_...is the paired direction — without it, the first test is satisfied by an implementation that never releases anything at all.Two further mutation controls, one per review finding. Attestation gate neutered (
if false): the two attestation tests red, the pre-existing principal-mismatch test stays green — so they test the new wall, not the old one. Matching grantstake(1)'d: the two count-based tests red, the single-grant test stays green — which is precisely why the count tests were needed, the old test cannot see that defect.The three
SatisfactionRequirementfields each get their own test holding the other two and the whole world fixed, so none of them is a decoration:terminal_receipt_may_satisfymoves between satisfaction and execution,new_attempt_requiredbetween attaching and a fresh attempt,step_reuse_permittedchanges what that attempt may reuse without changing whether it exists.One thing deliberately not asserted
That an unauthorized withdrawal cannot also have cancelled something is not a runtime assertion here, because it is not a runtime property:
CommandRefusedcarries no population and no release directive, so a refusal that took effect has no spelling. Asserting an empty population through the test helper would only measure that the helper returns[]on the refusal arm — a permanently-green check standing where the carrier already made the state unwritable (§5, construction over validation).Scope
Touches
dag/product/fabric/demand.dag(appended), addsdag/product/fabric/arbitration.dagand its witness. Readsexecution.dag/budget.dag, edits neither. Checked againstsilent-bear-842, whose identically-titled lane isgunbc.fabric_cell_converge: disjoint — they importCellIdfromsanitation.dag, which this does not touch or move.product.fabric.sanitation observation_for's first-match fail-open is not repaired here. It is assigned to be repaired with its first consumer, and this is not that consumer: nothing here reads a readback — the inputs are the demand population, the live grants, and the command. Adopting it without a consumer would be the wrong shape.