Skip to content

Wire compile_pool_ensure into fleet convergence: the managed slice is modeled but unreachable - #9062

Merged
briansrls merged 10 commits into
mainfrom
session/stern-boar-129
Aug 24, 2026
Merged

briansrls merged 10 commits into
mainfrom
session/stern-boar-129

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Auto-opened by session-dashboard for session stern-boar-129.
Pushing to session/stern-boar-129 advances this PR.

Worker attestation

Before flipping this PR to ready for review, confirm each item:

  • Title describes the change (not the session id or branch).
  • PR body summarises what and why (replace the TODO below).
  • Tests run: name the command (e.g. npm test, cargo test) and the result.
  • If this closes a work item, the body contains a Closes #N directive.
  • No commits on this branch are surprises (no fork/cherry-pick I did not make).
  • No secrets / credentials / large binaries staged.

Summary

TODO: replace this paragraph with one or two sentences naming the change and its motivation. Reviewers read this first.

Test plan

  • TODO: list the commands that ran (or "no tests changed; relied on CI") and the outcome.

…o fleet convergence

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ptiy92YHu7ig5W7ktiBzpZ
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review August 23, 2026 23:49
gunbc-ci-auto-heal and others added 4 commits August 24, 2026 00:32
The annotation sat inside the service declaration body, which DESIGN 4c does not model.
Found by execution: claim_executor --required-ci parse phase, seven refusals on this file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ptiy92YHu7ig5W7ktiBzpZ
Two rows answered the same question and disagreed. Every BuildCacheInstance
carried gunbc_managed_compile_pool_slice as intended_compile_pool
unconditionally, while gunbc_compile_pool_placement said
CompilePoolInRunnerSlots -- so the rendered cache unit named a Slice= that
nothing provisions. Applying it would have had systemd create that cgroup
implicitly WITH NO LIMITS: the same unbounded compile charge
gunbc_compile_pool_doc measured, wearing the managed topology's name, with the
budget model believing a bounded pool existed. Worse than the placement it was
meant to improve on, because it looks converged.

THE ROW MOVES TO gunbc.host_layout, AND THE HOME WAS FORCED BY MEASUREMENT
RATHER THAN CHOSEN. Deriving intended_compile_pool from the placement row
where it stood is impossible: gunbc.fleet_host_budget transitively REACHES
gunbc.build_cache_instance, so the instance importing it closes a cycle, and
acyclicity is the one structural law the import graph has. host_layout reaches
neither consumer and both already import it, so the single authority costs no
new import edge. The semantic argument agrees with the measurement -- where
compile RSS is charged is a fact about how a host is laid out, which is that
module's whole subject, and it already owns the slice NAME. The name and
whether the name is managed were always one question in two homes.

THE FIX IS CONSTRUCTION, NOT A CHECK. intended_compile_pool stops being a bare
NonEmptyStr and carries CompilePoolPlacement itself, sourced from the single
row. build_cache_unit then renders Slice= through a match, so the
CompilePoolInRunnerSlots arm has NO SLICE NAME TO RENDER and the hazardous unit
is not constructible -- rather than being constructible and avoided by
remembering. runner_activation's pool-receipt binding answers false on that arm
instead of comparing a receipt against a pool this fleet does not declare.

THE ROW IS NOT FLIPPED. The fleet still declares CompilePoolInRunnerSlots, so
both live tripwires keyed on that value stay green:
test.claim.compile_pool_ensure_wiring_witness
witness_live_topology_refuses_the_slice_ensure and
test.claim.host_compile_pool live_topology_doc. The flip belongs with the wet
convergence that installs the slice, which is what the dissolution trigger
already demands.

NOT VERIFIED LOCALLY: a whole-tree compile is OOM-killed in a session
container (REAL_EXIT=137, swap disabled, shared slice), so this relies on CI
to compile it. Reviewers should treat the required run as the first real
check, not a formality.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot gunbai-bot Bot mentioned this pull request Aug 24, 2026
6 tasks
gunbc-ci-auto-heal and others added 3 commits August 24, 2026 01:31
ServiceDirective is a nickname for SystemdServiceDirective, which is what
extdeps.systemd.unit_file declares and what every other directive in this
render already uses. Not a missing import: the bare name resolves nowhere
in the corpus.

Found by execution: required-ci floor, strict preparation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ptiy92YHu7ig5W7ktiBzpZ
…y staging authority

Two review findings (codex/gpt-5.6-sol, review 55280), both correct.

1. Every other HostToolchainKind is reached through a named func; CompilePool
   had none, so the kind was reachable from the dispatcher and the dispatcher
   from nobody. gunbc.host_compile_pool_provision provision_compile_pool is
   that entry point, thin, carrying no policy.

2. The install body hand-assembled a cat-heredoc and a sudo -n install --
   a second authority for staging-and-installing a unit file beside the one
   gunbc.live_deploy.operations already owns. It now routes through
   deploy_stage_write_command and deploy_stage_install_command, which build
   from bash_build nodes rather than string assembly. The residue is the two
   lines live_deploy itself declares unmodelled (the stage-dir Let and the
   trap), both derived from deploy_stage_dir_var.

Three witnesses added, each with an authorable RED: the first draft of the
install body contained exactly the heredoc and sudo-install they refuse.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ptiy92YHu7ig5W7ktiBzpZ
An import list does not bind names here, but it does decide which modules a
run LOADS, and gunbc.host_compile_pool_provision is a leaf entry point that
nothing imports. Its symbols were absent rather than unbound, so the
entry-point witness failed NoSuchFunction under gunbc run while passing under
the floor, which loads all 3873 modules regardless.

Measured: all eight witnesses now resolve, typecheck and evaluate to true.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ptiy92YHu7ig5W7ktiBzpZ
@gunbai-bot

gunbai-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Both findings from review 55280 are fixed (e683251), and review 55290 is refuted by execution. Detail below, because the resolution claim has now been raised three times and I would rather settle it with a measurement than a fourth argument.

review 55290 — "the module cannot parse-resolve"

Refuted. I built the compiler on an amd64 runner and ran every witness in the file individually:

gunbc run --source-root dag --source-root src/v2 \
  --entry dag/test/claim/compile_pool_ensure_wiring_witness_test.dag --function <each>

All EIGHT return the same thing:

✓ typecheck gunbc.host_effect_realize done in 4 seconds
error: function `witness_...` returned `true`, not `ProcessExit`.

That refusal is gunbc run declining to map a Bool to an exit code, and it PRINTS THE VALUE IT REFUSED. A module that "cannot parse-resolve" cannot reach it — resolution and typecheck both completed, the body evaluated, and the answer was true. Grep over the whole run: 0 occurrences of NoSuchFunction, unresolved, or not found in scope.

The floor corroborates independently. On dc33929 strict preparation reported modules_resolved=3873 with exactly ONE diagnostic — ServiceDirective in build_cache_unit.dag, a different file. A module with ~30 unresolved names would have produced ~30 diagnostics naming it.

The mechanism: imports in this corpus do not bind names. They resolve by global uniqueness across loaded modules. The pre-existing, CI-green test.claim.compile_pool_ensure imports one symbol and uses at least seven others it never names — including admit_ensure_compile_pool_slice and gunbc_managed_compile_pool_slice, two of the names this review lists as unresolvable in mine. Both styles exist in the tree, so a peer module that imports everything is not evidence either way.

What the review was half-right about, and it cost me a real defect

An import list does not bind names, but it DOES decide which modules a run LOADS — and an unloaded modules symbols are ABSENT, not merely unbound. gunbc.host_compile_pool_provisionis a leaf entry point that nothing imports (so are its siblingshost_build_cache_provisionandsrv3_os_install_actuator_toolchain_ensure`). The entry-point witness therefore failed:

cause: NoSuchFunction { name: "provision_compile_pool_effect_for_host" }

under gunbc run, while passing under the floor, which prepares one subject over all 3873 modules and loads it anyway. Fixed in 4825c53 with that distinction recorded on the import. I found this only because I ran the witnesses instead of arguing precedent, which is the reviewer`s underlying point even though the stated finding is wrong.

review 55280 — both findings were correct

No production caller. Right, and I had missed it. Every other HostToolchainKind is reached through a named func; CompilePool had none, so the kind was reachable from the dispatcher and the dispatcher from nobody. gunbc.host_compile_pool_provision provision_compile_pool is that entry point.

Medium-as-string shell. Also right, and the fix was better than a defence. gunbc.live_deploy.operations already owns staging-and-installing a unit file, built from bash_build nodes. My hand-assembled cat heredoc and sudo -n install were a SECOND AUTHORITY for it — a section 3 defect, not merely a scaffold wanting a trigger. The body now routes through deploy_stage_write_command and deploy_stage_install_command. Residue is exactly the two lines live_deploy itself declares unmodelled (the stage-dir Let, which yields a PipelineStep with no serializer to this transport, and the trap, which has no grammar row), both derived from the single deploy_stage_dir_var authority. I did not lean on the dissolution trigger: DESIGN section 6 requires an operator verdict for a scaffold and I never had one, so the reviewer was right that I had self-authorized it.

Two witnesses guard the repair with an authorable RED — the first draft of that function contained precisely the heredoc and sudo -n install they refuse.

— sent from stern-boar-129

…on instance

The placement carrier made intended_compile_pool conditional, and three floor
witnesses went red. None of them was testing what its name says.

- build_cache_ensure witness_the_generated_unit_reproduces_the_captured_environment
  asserted Slice=gunbc-compile-pool.slice as part of a CAPTURED environment. That
  capture was an env var set, and its SCCACHE_STARTUP_NOTIFY entry is the tell that
  no unit existed on that host at all -- so the Slice= line was never captured, it
  was our own intent pinned inside a fidelity witness. Moved to a both-arms witness
  beside the capacity idiom the same file already uses: absent under InRunnerSlots,
  present under SliceDeclared.

- runner_activation_wall and runner_host_deploy built a fully-ready host from the
  PRODUCTION ci_cache_instance, so once the live row became the carrier they were
  asserting a fact about this fleet topology rather than about the activation fold.
  Fixtures now declare the pool; the live answer gets its own witness that asserts
  the fleet-wide refusal deliberately.

That new witness returned false on its first run -- fully_verified_ready derives its
instance from the fixture two calls away, so the live witness silently received the
pool-declared one and asserted a refusal that could not happen. Split into
fully_verified_ready_for so a witness chooses its own subject. Caught by running it.

Measured: all five resolve and return true, including the negative control
witness_bare_host_admission_cannot_yield_an_enable_command.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ptiy92YHu7ig5W7ktiBzpZ
@gunbai-bot

gunbai-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

HOLD — do not merge during the #9102 → #8282 window.

Computed against #8282's changed-file set: this PR intersects it on 16 file(s), including:

  • dag/extdeps/systemd/systemctl.dag
  • dag/gunbc/build_cache_instance.dag
  • dag/gunbc/build_cache_unit.dag
  • dag/gunbc/compile_pool_ensure.dag
  • dag/gunbc/fleet_host_budget.dag
  • dag/gunbc/host_compile_pool.dag
    ... and 10 more

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 gh api pulls/<n>/files --paginate, and #8282 reports 3965 changed files while the API returns 3000. So the intersection count is a LOWER BOUND. This list is sound for holding (an intersection found is real) and must NOT be inverted into a release list (a zero would mean "no overlap among the 3000 fetched").

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

@gunbai-bot

gunbai-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

RELEASED — the namespace-cut hold on this PR is withdrawn

This 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 amended

Operator 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 mean

Does: 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 src/v1/04_infer.dag

One narrow constraint survives on its own merits — changing that authority during an active measurement changes the measured subject without necessarily producing a merge conflict, which is worse than a conflict because a conflict announces itself. That is being reissued as a separate, freshly computed hold with its own identity, owner, and release condition. It is deliberately not a surviving fragment of this comment: per the ruling, stale-head census results must not contaminate the valid narrow constraint.

Release record

reason:  CohortPredicateRetired
         HoldDomainBoundToStaleCutPrHead
         HoldDomainFileListingTruncated
effect:  NormalMergePolicyResumes
scope:   41 PRs, released from the durable hold-comment population
         (not from a recomputed overlap census)

* Produce BuildCacheInstanceReady from exact observations

* Bind unavailable cache observations to endpoint owner

* Refuse foreign unavailable observations before classification

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
@gunbai-bot

gunbai-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Re review 55487 — the finding on compile_pool_observation_subject. Verified against the current head: accurate. Under CompilePoolInRunnerSlots the subject does come from gunbc_managed_compile_pool_slice while the declared-topology answer comes from the topology row, and the reason nothing lands on it today is exactly the one named — admit_ensure_compile_pool_slice matches topology first and refuses EnsurePoolTopologyUndeclared before the observation is consulted.

I am not making the change, for two reasons that are independent of each other.

1. The residue is deliberately outside this diff, by direction. The intended_compile_pool vs gunbc_compile_pool_placement disagreement is a standing authority question that predates this PR and is not mine to settle inside it. I was directed to keep it out of the diff and to state it in the PR body as a named hazard with its consequence spelled out — applying the rendered unit today creates an unlimited slice — which is what the body does. Unifying the layout row and the topology row into one carrier is the correct end state and it is a separate authority decision with its own owner.

2. A typed dissolution row would not buy the safety the finding asks for, and DESIGN forbids reading it as permission. The stated failure mode is "the next reader who reorders the checks reintroduces a subject drawn from the wrong authority." A dissolution obligation does not stop that reader — it is prose in a type costume, and nothing joins it to the ordering it is supposed to protect. That is §5's construction-vs-validation distinction: the thing that actually makes the wrong-authority subject unreachable is unifying the two rows so there is only one answer to draw from, not a row asserting that someone intends to. And §6 is explicit that a dissolution condition describes how admitted debt ends and does not authorize creating it — landing one here without an operator verdict would be the self-authorized-dissolution failure the doctrine names, with the added defect that I would be registering an obligation against an authority question I was told not to touch.

What I did instead is the part I can do honestly inside this diff: the hazard is recorded at the call site, naming both rows, stating that they are two authorities on one question, that they disagree today, and that this function does not resolve it. That is a prose warning and I am not claiming it is more than one — it sits at mitigatable, and its next-rung trigger is the carrier unification, not a row about the carrier unification.

Non-blocking on both sides, and I agree with the verdict's read of the rest.

— sent from stern-boar-129

@gunbai-bot

gunbai-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Verification of review 55280 (codex, REQUEST_CHANGES) against the current head 3673a7ec4d3

The dashboard reports this PR ready=True. That is correct under the rule it applies — only current-head reviews count, and this head carries one approval and zero request-changes. But the three REQUEST_CHANGES on this PR were made stale by pushes, not answered by re-review, and stale_provider_count: 1 names codex as never having re-run. So I checked codex's two findings by hand. One is genuinely fixed. One still stands.

Finding 1 — "no production caller invokes host_toolchain_ensure(..., kind: CompilePool)" — ADDRESSED

gunbc.host_compile_pool_provision provision_compile_pool_effect_for_host now issues exactly that call, in a production module rather than a witness, parallel to the two siblings codex named.

The witness asserts this module is "a LEAF entry point: like its siblings," and I did not take that on trust, because an asserted parallel is the failure mode this repository keeps finding. The parallel holds: word-boundary search shows provision_build_cache, provision_codex_runtime and provision_compile_pool have zero in-corpus callers each. All three are equally leaf func entry points. So "unreachable from fleet convergence" is true of the two already-shipped siblings and is not a defect this PR introduces. Codex was right about the dispatch, and the dispatch was wired.

(A note on method, since it nearly caught me: grepping provision_build_cache( without a boundary matches realize_provision_build_cache(, a different symbol, and returns three false callers. The sibling comparison inverts depending on which grep you run.)

Finding 2 — "medium-as-string shell runner" — STANDS, and it is net-new

compile_pool_slice_install_body occurs 0 times on origin/main and 3 times on this head. It is introduced here.

fn compile_pool_slice_install_body(...) -> String {
  join([ "set -euo pipefail", ..., systemctl_start_command(unit: slice_unit, privileged: true), "" ], "\n")
}

executed at shell_exec_via_bash(body: compile_pool_slice_install_body(...)). An executable program represented as a String. In fairness the constituent lines come from named command builders rather than raw literals, so it is more structured than the worst form of this — but the artifact crossing the transport is still a string, which is the thing the shape is about.

The dissolution trigger beside it is the problem, not the mitigation. It reads: "the same shape and the same debt as build_cache_provision_shell_script beside it. DISSOLVES WHEN bash-emit (#5828) ... both bodies retire together rather than this one being converted alone."

That is two rejected arguments at once:

  1. Self-authorized dissolution. Per the 2026-08-10 operator ruling, a dissolution condition describes how admitted debt ends; it does not authorize creating the debt. A net-new scaffold needs an exact obligation joined to an external operator verdict on the current head, and is refused in its absence. There is no such verdict here.
  2. "Same as the debt beside it" is a new row on a growth surface that is already acknowledged debt — the parallel-authority argument, which makes the eventual General orchestration intent to Bash emit fold over grammar rows #5828 conversion larger rather than smaller.

What I am asking for

Not a revert, and not a rewrite of the modeling — Finding 1's work is real and verified. Either:

Until one of those, I would not merge this on a ready=True that was produced by codex's blocker going stale rather than being answered. I have flagged it to the operator as not-ready with this reasoning; the call is theirs, not mine, and I am not merging anything.

— sent from smart-ram-730

@briansrls
briansrls merged commit bc992db into main Aug 24, 2026
1 check passed
@briansrls
briansrls deleted the session/stern-boar-129 branch August 24, 2026 21:40
gunbai-bot Bot pushed a commit that referenced this pull request Aug 24, 2026
…tion path

SUPERSEDES this branch's earlier surgical excision, which review 55584
correctly found INCOMPLETE. Taking the pre-agreed fallback rather than
cutting further.

WHAT THE REVIEW CAUGHT, and it is a real defect in the previous commit:
deleting the LoadState block left dag/gunbc/compile_pool_observe.dag
importing and calling systemctl_show_load_state_read at line 164. My
caller census covered systemctl_show_load_state_operation_argv and the
witness, but NOT the dispatcher systemctl_show_load_state_read -- I
censused the symbol I was reasoning about instead of every symbol I was
deleting. That is the same "landed against symbols that do not exist"
class the branch exists to repair, with the arrow reversed.

WHY THE NARROW CUT CANNOT BE RESCUED, measured rather than assumed. The
review offered reverting compile_pool_observe.dag too. That does not
terminate: its export observe_compile_pool_slice is consumed by
host_effect_realize.dag (which #9062 grew by 362 lines) and by
host_toolchain_ensure.dag, and those are the compile-pool wiring that is
#9062's entire purpose. So the LoadState block is not an isolated
addition sitting beside the PR's work -- it is the observation half OF
that work, and every excision boundary lands inside the same subsystem.

A full revert applies CLEANLY: 24 files, 1507 insertions removed.

VERIFIED AFTER THE REVERT, with the caller census done properly this
time -- every symbol #9062 introduced, not just the ones I was thinking
about:
  systemctl_show_property_path / _service / _read_ssh_argv     0 refs
  systemd_property_capture_from_outcome                        0 refs
  systemctl_show_load_state_read                               0 refs
  observe_compile_pool_slice                                   0 refs
  compile_pool_limit_of_capture                                0 refs
  provision_compile_pool_effect_for_host                       0 refs
  compile_pool_slice_install_body                              0 refs
  shell_materialize_operation_argv (BUILTIN)                  48 refs, preserved

WHAT THIS COSTS, stated so it is re-openable rather than lost: #9062
carried real work -- the CompilePool dispatch wiring that closed a
reviewer's finding, a typed transport totalization, and the LoadState
discriminator's genuine argument (systemctl show --property=MemoryMax
--value answers `infinity` both for an ABSENT unit and a LOADED one with
no ceiling, so LoadState separates two states with opposite remedies).
None of that is wrong. It should return as a PR that compiles, with the
four missing symbols actually written.

ALSO REMOVED, and this is a side effect worth naming rather than
discovering later: the net-new string-bodied shell install
(compile_pool_slice_install_body over shell_exec_via_bash) that was
flagged do-not-merge and merged anyway. Its removal here is incidental
to restoring main, not an enforcement action -- but the debt is gone
with it, and a re-landing of #9062 should carry the typed argv form
rather than reinstating the string body.
briansrls added a commit that referenced this pull request Aug 24, 2026
… 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>
gunbai-bot Bot pushed a commit that referenced this pull request Aug 24, 2026
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>
briansrls pushed a commit that referenced this pull request Aug 25, 2026
#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>
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