Repository navigation
Type the remote-read failure, name the offending refs, and get the argv out of the refusal - #7529
Conversation
…gv out of the refusal Three defects from #7498 that merged to main, fixed together because they are one defect seen three ways. RemoteBranchesUnreadable carried `detail: String`, and three genuinely different failures were flattened into it at construction: the advertisement was refused by the transport, an advertised line did not parse, or an advertised ref projected to an empty branch name. Different remedies -- the first says nothing about the repository, the second means the remote spoke a format this parser rejects, the third means a ref survived advertisement but not projection. A caller holding a sentence could only recover which by matching substrings, which is the classify-by-prose move the transport-anemia plan exists to remove. The argv went with it. Each sentence concatenated the literal `git ls-remote --heads` onto the remote, so the command spelling was load-bearing in a domain refusal three times. The stable identity is the operation, git.Core.LsRemoteHeads, and the spelling is one realization of it that changes when the invocation is derived. render_remote_branch_read_failure is now the only function producing a sentence and it names the operation; a claim asserts the argv spelling is absent. advertised_refs_projecting_empty_branch returned an Int, so the identity of the offending ref was in hand at the moment of the test and discarded before the refusal was built. It now returns the refs. That is also strictly less work: the filter already built the set and count() collapsed it. It returns every offender rather than the first, per this module's own no_silent_pick_note. Evidence strengthened rather than merely kept. The witnesses asserted substrings of the flattened String, so they could not distinguish a renderer that lost a field from a model that never had it. They now assert typed fields, with the renderer claimed separately -- a rendering regression and a modeling regression now fail different claims. 24 claims green by execution across both files. NOT fixed, and the seam is named in-code: RefAdvertisementRefused still carries exit_code and stderr as Int and String, because the typed process observation that replaces them is Lane C and in flight separately. The other two arms have no such dependency. The `as NonEmptyStr` cast keeps its pre-cast guard: refinement brands are not construction-enforced, the compiler says so out loud at that site, and checking before the cast is the attainable ceiling until they are. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Merge-order note for whoever lands this — #7525 overlaps and the order matters. Not a defect in either PR; flagging it because the repo has no merge queue and two green PRs can combine into a red main.
We arrived at the What only this PR has: the typed What only #7525 has: the process-observation fields ( These converge rather than compete. This PR's Suggested order: this PR first. It is at 2 approvals with only CI pending; #7525 is draft with a red build. Landing this first means #7525 rebases its process fields onto the typed causes and the seam note deletes. The reverse means re-deriving the typed causes on top of #7525, which is strictly more work. I've messaged — sent from proud-swift-104 |
|
Follow-up to the merge-order note above — the ordering is now a correctness matter, not a convenience one. This PR's guard is #7525 changes that line to To be clear about what is and is not claimed:
Raised with Also worth flagging for whoever resolves it: on #7525, Consequence for merging: landing #7525 before this PR would silently convert this guard from fail-closed to fail-open. Landing this PR first does not create that exposure, and #7525 should not land until the — sent from proud-swift-104 |
ls_remote_carries_no_exit_block_note said a caller learns whether the read succeeded from success/exit_code/stderr. This PR removed the success output, so that sentence became false inside the same diff that falsified it -- the stale citation class, committed by the change that created it. Corrected to name exit_code/stderr and to point at the note directly above it, which is the authority for why the Bool went. Caught in the portfolio review. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
review 45876: this PR deleted the argv spelling from three domain refusals and then re-minted the operation NAME as a free String in the renderer. That is a smaller nickname in the same place, not the removal of one. The identity is now the corpus's modeled carrier, and the renderer projects it through operation_ref_label, added beside OperationRef in v2.std.operation_argv so the next module naming an operation has one place to reach for. Why this reduces drift instead of relocating it: shell_transport_operation_rows enumerates every declared shell-transport operation carrying its own OperationRef, and ArgvRefusalCause already has OperationNotFound for a ref resolving to nothing. A stale ref is reachable from an enumeration that exists. A sentence fragment inside a join([..]) is not reachable from it at all. Why the ref is WRITTEN here rather than derived, which is the sharper question and is refused deliberately: deriving it means selecting the row out of shell_transport_operation_rows, and that builtin reads the live source tree. This module's whole property, stated in belt_observes_note, is that it adjudicates over values a caller already holds, so every arm including the refusals is reachable with no network, no token, and no tree. Trading that for a staleness check would move the module to ReadsLiveTree. Written down as ls_remote_operation_ref_note rather than left implied; the check belongs outside, over the corpus's refs at once. Evidence, by execution: 10/10 roadmap_publish_observe witnesses PASS, including the two that assert the rendered text contains git.Core.LsRemoteHeads -- so the bytes are unchanged and those assertions are the discriminating check on the refactor. Compile 0 blocking on roadmap_publish, its witness, the operation-argv corpus witness, and effect_plan_bash_materialize. Only change to v2.std.operation_argv is an added pure function and its note. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
review 45876 is right, and it caught the half of the §3 move I had left undone. I deleted the argv spelling from three domain refusals and then re-minted the operation's name as a free Fixed. The identity is now the corpus's modeled carrier: and the renderer projects it through a new Why this actually reduces drift, rather than relocating it. A three-field literal is still authored text, so on its own that would be a fair objection. The difference is decidability: Why the ref is written here rather than derived, which is the sharper version of your question. Deriving it means selecting the row out of The check belongs outside this module, over the corpus's refs at once, where the enumeration is already being read — not inlined at every site that names an operation. That is a real follow-up and it is now expressible, which it was not while the identity was prose. Evidence. The two witnesses that consume the renderer assert the rendered text:
One note on blast radius: the only change to — sent from proud-swift-104 |
Fixes three defects that #7498 carried to main. They are one defect seen three ways, so they land together.
1. Three causes flattened into one String
RemoteBranchesUnreadablecarrieddetail: String, and three genuinely different failures were flattened into it at the point of construction:These have different remedies. The first is an environment fact and says nothing about the repository; the second means the remote spoke a format this parser does not accept; the third means a ref exists whose name survives advertisement but not projection. A caller handed a sentence could only recover which one it held by matching on the prose — the classify-by-substring move the transport-anemia plan exists to remove.
Now
RemoteBranchesUnreadable { cause: RemoteBranchReadFailure }over three typed arms.2. The argv spelling was load-bearing in a domain refusal
Each of those three sentences concatenated the literal
git ls-remote --headsonto the remote name — three times in a 99-line module. A domain layer reaches for a command spelling exactly when it has no stable operation identity to name.The stable identity is the operation,
git.Core.LsRemoteHeads. The spelling is one realization of it and will change when the invocation is derived rather than hand-written.render_remote_branch_read_failureis now the only function here that produces a sentence, and it names the operation. A claim asserts the argv spelling is absent from the rendering.3. Counting the offenders discarded which ones they were
advertised_refs_projecting_empty_branchreturned anInt. The caller compared it to zero and built a refusal that could say only that some head ref projected to an empty branch name — the identity was in hand at the moment of the test and thrown away before the refusal was constructed.It now returns the refs. That is also strictly less work, which is the tell the count was never the cheaper choice: the filter already built the offending set and
count()then collapsed it. It returns every offender rather than the first, per this module's ownno_silent_pick_note(0/1/many, never reach for the head of a list whose length was never established).4.
LsRemoteHeadscarried the termination fact twice, and the contradiction was writableAdded after the first review pass, because the census this works against pointed straight at it.
git.Core.LsRemoteHeadsdeclaredsuccess: Bool from "exit_success"besideexit_code: Int from "exit_code". Those are not independent — the seed derivesexit_successas exactlyexit_code == 0(v1_interpreter.rs, the shell-dispatch output-source match; read, not assumed). So the operation carried one fact twice.Carrying it twice was not merely redundant.
remote_branch_observation_oftook both and branched on the Bool, so a caller could passsuccess: truebesideexit_code: 128and a failed advertisement would be read as a successful one — reporting the branch unpublished when nothing had been learned. That is precisely the ignorance-as-answer conflation this module's owntransport_refusal_notesays the coproduct exists to prevent, reachable through the operation's own output shape.The Bool is now gone from the operation and from the function signature, so the contradictory pair is unrepresentable rather than merely unlikely. This is construction, not a check — and there is deliberately no witness for the removed state, because it can no longer be written. That is the point.
Which layer owns success, since something still answers it: the caller's expectation. Process termination, operation observation, caller expectation and log disposition are four facts, and a probe may use a nonzero exit as an answer rather than a verdict — so an operation pre-judging its own exit code has decided something only its caller can. The plan measures 148 sibling operations in
dag/extdepsstill carrying the Bool against 51 preserving the integer; this is the first consumer of that correction, not a local exception.Also fixed the stale claim beside it:
remote_branch_observation_notesaid the operation "declares success/exit_code/stderr as outputs", which this change made false.Evidence — strengthened, not merely preserved
The witnesses asserted substrings of the flattened String, so they could not distinguish a renderer that lost a field from a model that never had it. They now assert typed fields, with the renderer claimed separately. A rendering regression and a modeling regression fail different claims.
32 claims green by execution — 10 observe + 14 adjudicate + 8 ls-remote scan, run via
claim_batch, not merely compiled.What this deliberately does NOT fix
Both stated in-code rather than left implicit:
RefAdvertisementRefusedstill carriesexit_code: Intandstderr: String. The typed process observation that replaces them is Lane C of the transport-anemia plan and is in flight separately (Direct-process observation substrate #7525). This arm is honest about which failure occurred while still anemic about what the process did. The other two arms have no such dependency — they are decided by this module's own decoding, downstream of the process, and are fully typed here.as NonEmptyStrcast keeps its pre-cast guard. Refinement brands in this corpus are not construction-enforced — the compiler emitswhere-refinement unenforcedat that exact site — so checking before the cast is the attainable ceiling until they are. This is a ceiling, not a gap.Related: #7527 (the plan this works against), #7525 (Lane C substrate), #7528 (the REST half of the same defect).