Repository navigation
Totalize the transport interface: one dispatch in the realization layer, not four arms in six callers - #9057
Conversation
…er, not four arms in six callers gunbc.typed_argv_exec exposed _over_ssh and _over_fleet_ssh and no total entry point, so every caller was forced to write the transport match itself. The repeated match is the symptom; the absence of a total entry point is the producer. Six one-tool domain reads each carried the same four arms. gunbc.host_operation_exec owns them once. A caller says WHERE (a transport it genuinely knows) and WHAT (a HostOperation); it never says HOW. Converted end to end: systemctl_is_active_read, _status_read, _stop_run, _show_read, hostname_read. Each also loses its private copy of the operation's identity, so one operation is named in one place, and its *_matches_authority witness now materializes through the row the read executes. fleet_ssh_leaf_unmigrated_refused is deleted with its three dead imports -- one row off the FleetSsh leaf frontier. Refusals preserved: the Emit arm still names the operation, the shell-safety allowlist stays SshShell-only (the fleet leg rests on a different wall), and stderr is optional because the readonly reads model none. LocalInvocation is declared debt, not a model improvement: a second naming of operations extdeps already declares, admitted only because no .dag-level invoke-by-triple exists. It dissolves when that capability does. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ng is authorable only inside a reviewed function Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…lers gates who may call, not what they may build Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e import dropped with the transport arms Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Fixed in One correction to the finding's mechanism, because it changes what the defect is and I would rather
After the fix: same 53, still none naming That makes it a real defect of a different kind, and worth fixing on its own terms: the file used a — sent from proud-dove-451 |
… its representation The previous shape paired a string-discriminated BoundOperationInvocation with an independently-chosen LocalInvocation, then reconstructed the missing relationship with a hand-maintained name mapping and a predicate that host_operation_exec never consulted -- illegal-states-representable plus a hand-rolled validator standing where construction was available (DESIGN 5). Sealing the record narrowed WHO could author the mismatch; it did not stop the mismatch existing. HostOperation is now one coproduct. The argv identity derives from it via host_operation_invocation, the native call via host_operation_exec_local, so there is no second fact to disagree with the first. The mapping, the predicate, the sole_constructor seal, the mint, its admit list and the witness-module admission are deleted rather than reduced; per 4b(4) the mispairing witnesses dissolve with them, their RED no longer authorable even in a fixture. What stays writable is one derivation arm naming the wrong ref -- covered by the five pre-existing *_matches_authority witnesses, which now materialize through the same derivation the reads execute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Fixed structurally in I had reached for
There is no second fact to disagree with the first, so the cross-transport mismatch has no On the 🟡 bounded owner/lane: the dissolve-on is now stated as a condition on the substrate, not What remains genuinely writable, stated at its real rung: one derivation arm could name the wrong Evidence on the new shape: six production entries compile with 53 hard diagnostics each, none — sent from proud-dove-451 |
|
HOLD — do not merge during the #9102 → #8282 window. Computed against #8282's changed-file set: this PR intersects it on 15 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>
… vocabulary #9057 deleted (#9147) MAIN IS RED AND EVERY OPEN PR INHERITS IT. #9062 added a load-state read to gunbc.systemctl_show_read referencing four names that exist nowhere in the corpus: systemctl_show_property_path, systemctl_show_property_service, systemctl_show_property_read_ssh_argv, systemd_property_capture_from_outcome. Each occurs in exactly one file -- the file referencing it. ROOT CAUSE IS A RACE, NOT UNFINISHED THOUGHT. #9057 (transport totalization) merged first and moved the four transport arms into gunbc.host_operation_exec, deleting the per-transport read helpers every domain read used to hand-roll. #9062 was authored against the pre-#9057 generation. All four missing names belong to that dead vocabulary, which is why the same file's own imports are already the NEW one (host_operation_exec, host_operation_materialize_argv). REWIRE, NOT REMOVE, and one measurement decides it. The block is not orphaned: gunbc.compile_pool_observe matches on systemctl_show_load_state_read and reads the memory limits ONLY under a loaded unit. That is exactly the discrimination the annotation above the block argues for -- `systemctl show --property=MemoryMax --value` answers `infinity` for an ABSENT unit and for an UNBOUNDED one alike, two states whose remedies are opposite (install the slice vs refuse and report the drift). Deleting the block would have removed a correctness distinction and left compile_pool_ensure reading a slice it cannot prove exists. THE REPAIR IS THE ONE #9057 WOULD HAVE PRODUCED HAD THE TWO NOT RACED. SystemctlShowLoadState joins HostOperation with its two derivations -- the argv identity via systemctl_operation_ref("ShowLoadState") and the local leg via systemd.Systemctl.ShowLoadState, an operation extdeps.systemd.systemctl already declares readonly with its own mock. The four broken functions in systemctl_show_read then collapse into ONE host_operation_exec dispatch, byte-for-byte the shape systemctl_show_property_read directly above it already has, and the dead `data systemctl_show_load_state_operation` row goes with them. The module keeps the decoder and nothing else; the four transport arms stay in the realization layer where DESIGN section 3 puts them. NO WITNESS IS DELETED. witness_load_state_operation_argv_matches_its_authority still calls systemctl_show_load_state_operation_argv_matches_transport, which now materializes through the shared invocation -- so it compares the extdeps argv authority against the SAME description the local leg invokes, rather than against a second hand-spelled (path, service, operation) triple. The check got stronger, not weaker: a cross-transport mismatch is now unrepresentable rather than merely detected. Claude-Session: https://claude.ai/code/session_015DhmPmvdPDN3m4ccuQLzys Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This branch's CI red was main's break, not the cut's. #9057 deleted a transport vocabulary together with its call sites; #9062 was authored on an unrebased base and added new call sites against the dead vocabulary. Both were green on their own base and had never compiled together. The four undefined symbols were reported against this PR's synthetic merge head, which is why they read as ours. Two conflicts, both in the repaired file, resolved by rule; local-binder pass re-run over the merged content. Tree still at zero imports. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#9163) Two main reds in one evening, and a third pair before them, all one class: two PRs independently green, jointly broken. #9049 with #9114, #9057 with #9062, #8919 with #8992. No textual conflict in any of them -- the first change altered a semantic API and the second was checked against a base where the old API still existed, so both greens were true of trees that never existed together. The #9057/#9062 pair took the whole fleet red for about an hour and produced four uncoordinated repair PRs plus a fifth branch, two of which proposed deleting live code. No wall inside this repository can close that. The invariant needed is that the commit admitted to main is the exact composed commit that passed the required checks, and nothing in the substrate can constrain what GitHub admits. WHAT THIS CHANGE IS: the in-repository half, and it is INERT ON ITS OWN. witnesses.yml listens only to workflow_dispatch, push and pull_request, so enabling a merge queue today would create a merge-group commit for which the required check never schedules -- pending forever. This adds the trigger so the prerequisite exists BEFORE the setting is changed, with no window in between. With no queue configured the event never fires, so CI behaviour is unchanged by this diff. WHAT IT IS NOT: the repository setting. The ruleset currently carries strict_required_status_checks_policy false, no merge-queue rule, and an always-bypass role. That is an operator decision and this PR does not presume it. MergeGroup already exists in extdeps.github.actions WorkflowTrigger and the YAML emitter already renders it, so this is one row, not new modelling. EVIDENCE, including what I could NOT establish. Evaluating the workflow authority on this branch and on clean origin/main gives byte-identical diagnostic sets (2232 lines, zero diff), so nothing is introduced. I could NOT run the emitter to regenerate witnesses.yml: the local gunbc shim is a Jun 26 build that cannot resolve this corpus. The one yml line was derived by reading the emitter -- `MergeGroup => kv(key: "merge_group", value: YamlNull)` renders exactly as `workflow_dispatch:` does, in the position its entry occupies in the `on:` list. DESIGN records the generated-artifact drift gates as currently unguarded, so nothing will catch that line if I have it wrong, which is why it is stated rather than assumed. Regenerating in CI and diffing is the check I want on this PR. Co-authored-by: Brian Searls <briansearls1@gmail.com>
The finding
gunbc.typed_argv_execexposes_over_sshand_over_fleet_sshand no total entry point, so everycaller is forced to write the match itself. The N x M is manufactured by an interface that declines
to be total.
Stated in the form that catches the next one: the repeated match is the symptom; the absence of a
total entry point is the producer. The shape to look for is not
matchblocks over a realizationtype — by the time those exist the N x M is already manufactured and already has callers. It is
execute_local/execute_ssh/execute_fleet_sshwith noexecute. Every caller-side matchdownstream of that shape is downstream of the one omission, so the omission is what to catch, before
it has any callers at all.
That is DESIGN §3's rule read from the supply side — the dispatch that selects a realization is
itself realization — so an interface offering only per-realization doors pushes its own dispatch
into every caller. Six one-tool domain reads each carried the same four arms (native call, ssh argv,
fleet argv, emit refusal), differing only in an argv row and a decoder. Six copies is what that cost,
and it is a pattern recognisable elsewhere in the tree.
What this changes
gunbc.host_operation_execowns the four arms once. A caller now says WHERE (a transport itgenuinely knows — which host) and WHAT (a
HostOperation). It never says HOW.Converted end to end, so the next author has a template rather than a rule:
systemctl_is_active_readsystemctl_status_readsystemctl_stop_runsystemctl_show_readhostname_readEach seam also loses its private copy of the operation's identity: the
path/service/operationrows and the argv materializer that read them moved into the operation row itself, soone operation is named in one place. The
*_matches_authoritywitnesses now materialize through thesame row the read executes, so they can no longer pass against an identity the read does not use.
fleet_ssh_leaf_unmigrated_refusedis deleted with its three dead imports (systemctl_show_read,hostname_read,systemctl_status_read), which were the only references left. One row off theFleetSsh leaf frontier.
Refusals preserved, deliberately
EmitArtifactThenThinRunrefusal names the operation — which is whyHostOperationcarriesan identity and not merely bindings. A generic "refused" would have erased which operation was
refused; the minimum Y must preserve every required refusal or it has erased a correctness
distinction rather than completed a replacement.
wall (
portable_remote_words, which refuses into a type), so collapsing the two into onepre-check would have silently changed behaviour on both legs. The asymmetry is kept and commented.
stderris optional on the observation.systemd.Systemctl.Stopdeclares a stderr output; thereadonly reads beside it do not.
nonemeans this operation does not model stderr — filling itwith
""to make the record uniform would fabricate an observation nothing produced.The pairing is structural, not reconstructed
HostOperationis one coproduct, and both realizations derive from it — the argv identity byhost_operation_invocation, the native call byhost_operation_exec_local. Nothing pairs twoindependently-chosen facts, so a cross-transport mismatch (ssh runs
systemctl statuswhile localruns
systemctl is-active, both succeeding, answering different questions) has no representation.It is not detected; it cannot be written.
An earlier revision of this PR got this wrong, and the wrong shape looked careful. It carried
HostOperation { invocation, native }— a string-discriminatedBoundOperationInvocationbeside aLocalInvocation, independently chosen — then reconstructed the missing relationship with ahand-maintained name mapping and a predicate that
host_operation_execnever consulted. That isillegal-states-representable plus a hand-rolled validator standing where construction was available,
which §5 names directly: a check that re-states a constraint the model already carries is a second
representation of it, and the fix is a single authority the realization is derived from, not a
check that flags the divergence afterwards. Sealing the record with
sole_constructornarrowed whocould author the mismatch; it did not stop the mismatch existing.
Deriving both legs removes the second fact entirely, so all of it is gone rather than reduced: the
mapping, the predicate, the
sole_constructorseal, the gated mint, its admit list, and thewitness-module admission that departed from
argv_command's no-test-declarations rule. Per §4b(4)the mispairing witnesses dissolve with the machinery, because their RED is no longer authorable
anywhere — not even in a fixture: there is no second field to disagree with the first. That is the
one condition under which evidence may retire, and it is met here rather than assumed.
The declared debt, stated as debt
HostOperation's variants are a nickname:SystemctlIsActive { unit }is a second naming ofsystemd.Systemctl.IsActive(unit:), which extdeps already declares with its own cited transport. Nota modeling improvement, and it should not be read as one.
It exists because the interpreter already dispatches local execution BY REIFIED IDENTITY —
eval_service_callresolves(service, operation)as a string key against itsservice_opsindex,and
shell_materialize_operation_argvtakes that same triple — but there is no.dag-level way toinvoke by that triple. No free-function sibling of
shell_materialize_operation_argvexists, and nogeneric local process-exec service does either (no
service Process, no localExec; the onlyExec/ExecArgvoperations declared anywhere aressh.Session's).The choice is not coproduct-versus-nothing. It is coproduct versus a generic-argv lie or an
opaque-function-value lie: a generic argv would claim the local leg is an argv execution when it is
a modeled operation invocation; a closure would claim it is unknowable, and could not be inspected,
refused, cost-attributed or emitted. The coproduct is the only one of the three that says something
true.
BoundOperationInvocationis not re-minted —v2.std.operation_argvalready declares it as anOperationRefpaired with declaration-owned bindings, andhost_operation_invocationreturns one.Dissolution, phrased as a condition and not a permission: when an invoke-by-triple sibling of
shell_materialize_operation_argvexists, this coproduct is deleted and both derivations collapseinto that one triple. If the v2 self-host program needs invoke-by-triple for its own reasons, the v1
freeze admits it on the purpose test and this dissolution fires without anyone re-litigating transport
ownership.
Checked: no generic local exec was opened
Every variant names an operation declared in extdeps. There is no variant carrying a free-form
command, argv or script, so the coproduct cannot express a local invocation that is not a modeled
operation. The property the corpus currently has — local execution happens only through a declared
operation's transport — survives this cut rather than being quietly opened by it. Adding an operation
requires adding a variant, which keeps growth reviewable and exhaustive rather than incidental.
Rungs, measured rather than inherited
OperationRefor bindings*_matches_authoritywitnesses, now flowing through the derivationThe third row is what remains genuinely writable:
host_operation_invocation's match could pairSystemctlStatuswith theIsActiveref. It is one arm in one function rather than everyconstruction site, and it is covered — each seam's
*_matches_authoritywitness now materializesthrough
host_operation_materialize_argv, comparing against a cited or literal authority ratherthan against a copy of my own row, so it cannot pass against an identity the read does not use.
Bound: the per-transport doors are still public
typed_argv_exec_over_ssh/_over_fleet_ssh/typed_argv_probe_over_sshremain public. Theconverted seams cannot reach them — they no longer import the legs, so the total executor is their
only route — but the second entrance stands for everyone not yet converted. The remaining population,
split because the split is the whole content of the number:
systemd_run_transient(4 calls),roadmap_dashboard_instance_apply(4),
live_deploy/readiness(4),host_reach_identity_probe(4),hostname_set(4),fleet_multi_principal_probe(4),roadmap_verify(2),fleet_receipt_collector(2),roadmap_dashboard_instance(1),ci_spec(1).host_effect_realize(22 calls). It is the realization selector —it occupies the same position
host_operation_execdoes, and calling the per-transport legs is whata dispatch layer is for. Counting it as remaining work would overstate the debt by its largest
single entry.
The program owes that these doors end up private; that is a delete-first cut in its own right and
deserves its own census rather than riding on this one. This diff owes only that its own seams cannot
reach them and that the remainder is named and counted — both discharged above.
A finding worth more than the seam it blocks
hostname_setdoes not fit this shape, and the reason is not a wart in this cut. Its ssh leg builds asudo-privileged argv that is not the modeled operation's argv — which says the modeled operation's
argv is insufficient for privileged execution. That is a gap in the operation model, not in the
executor: privilege is a realization concern the current operation carrier cannot express, so a caller
needing it today must hand-build the elevated argv, which is exactly the class this program exists to
remove.
Recorded here rather than left to be rediscovered by whoever converts that seam. It is a candidate
subject for a later cut, and it is not solved here.
Evidence
Measured on the merged tree (
origin/main@f9963a76— #9024, which adds typecheckrefusals — merged in and the seed binary rebuilt, because a stale compiler cannot see new refusals
and a green taken with one would be stale in the direction that matters).
Compile sweep over the six production entries: 1 hard diagnostic each, not in any file this branch
touches — a
file-transport emission gap ongcloud.Auth.ReadADCinextdeps.cloud.gcp.gcp, identical across all six entries. Pre-merge this figure was 53 (acommand_runnercluster since fixed on main); it is restated rather than left standing, because acount copied from an older base is a false recital, not a conservative one.
Green by execution, full roster (every
test fnin each module, not a sample) — 89 declared,89 PASS, 0 FAIL:
nbd_proxy_serve_transport_witness_testhost_identity_observation_witness_testfleet_show_effective_read_witness_testsystemctl_status_read_scaffold_witness_testhost_converge_slice1_witness_testRe-run in full on the merged tree with the rebuilt binary: same 89/89, so #9024's new refusals do not
reach this change.
Five of those are the
*_matches_authority/*_matches_transportwitnesses that now carry the thirdrung row's coverage, materializing through
host_operation_materialize_argv— the same derivation thereads execute.
(
gunbc runwill not execute witnesses: it refuses a non-ProcessExitreturn with "printing thevalue and exiting 0 would report success for a run whose outcome is unknown". That refusal is the
fail-closed behaviour working, not an obstacle to route around, so these run through the repo's own
witness runner rather than being wrapped to make them print.)
Not in this cut
HostEffectIntent.transport(the field itself) is untouched. Its only branching consumer ishost_effect_realize, which is the realization selector and is legitimate; the other five sitespropagate a transport from a
DeployAccesscontext into the intent they build. It is a writableauthority nothing currently abuses, and removing it re-points ~10 propagation sites in files this
cut does not otherwise open. It lands next, in its own diff — not dropped: while
transportsits onthe intent it is an attractor, and one branch consumer is the cheapest that cut will ever be.
HostOperationOutcomeisthe seam it will need: it is not
SshSessionExecResult(naming a local observation after ssh wouldbe a nickname in the return position), so a sealed
ProcessObservationcan replace it withoutre-pointing any caller.
Worker attestation
gunbc compileover the six production entries (0 diagnostics naming a touched file);claim_batchover the five touched witness modules by full roster (89/89 PASS).cargo testnotrun — removed from CI 2026-07-11 by operator ruling; this branch changes no Rust.
cargo testnot run — removed from CI2026-07-11 by operator ruling; this branch changes no Rust.