Skip to content

builder2: #1020 deliverable-address gate, #1105/#1235/#1273 single-definition cleanups (stranded branch) - #428

Merged
wshallwshall merged 13 commits into
mainfrom
claude/modest-ellis-a46139
Aug 20, 2026
Merged

builder2: #1020 deliverable-address gate, #1105/#1235/#1273 single-definition cleanups (stranded branch)#428
wshallwshall merged 13 commits into
mainfrom
claude/modest-ellis-a46139

Conversation

@wshallwshall

@wshallwshall wshallwshall commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Opened by a different session to make stranded work visible. Not reviewed by me, and deliberately not armed for auto-merge.

This branch existed only in a local checkout — never on origin, no PR — while 5 ledger claims (#1020, #1105, #1235, #1267, #1273) pointed at a worktree directory that no longer exists. The claims could not be released, because releasing them would invite a rebuild of work that already exists. The commits were durably backed the whole time (snapshot/b2-modest-ellis-20260815d on origin), so nothing was ever at risk of loss — but nothing could land either.

What is here

8 commits, last dated 2026-08-14, all novel against main (checked by patch-id: 0 already present):

  • #1020 a deliverable-address predicate for enabled administrators, plus ADR 0167 gating PHI-notification readiness on a deliverable address early in the ASGI lifespan — including two follow-up commits that scope and then overturn the ADR's placement
  • #1105 remove a harden_kex_groups call-site count that nothing checks rather than incrementing it
  • #1235 pin dangling_citation_check main()'s exit-code contract, then collapse the live-shape rule to one definition
  • #1273 collapse the C0/DEL predicate in controlchars.py, which stated it twice in the module that exists to state it once

10 files, +532/−24.

Expected failure, stated so nobody debugs it

a PR that implements BACKLOG #N must update BACKLOG.md will fail. The commits cite four BACKLOG numbers and the branch does not touch BACKLOG.md. That gate is correct and this PR genuinely does not satisfy it.

I have not updated BACKLOG.md myself, because doing so asserts these items are complete and I cannot verify that for another session's work. Whoever owns this decides what those entries should say.

Note also #1267, whose claim records TEXT READY, NOT APPLIEDseat-tick.ps1 is machine-global with no source of record in the repo, so there is no commit here for it. That claim's content is in the claim note and nowhere else.

🤖 Generated with Claude Code


Correction (author, later the same day)

The "Expected failure" section above is wrong. The backlog-hygiene gate ran on this PR and passed.

I predicted it would fail because the commits cite four BACKLOG #N numbers while the branch does not touch docs/BACKLOG.md. That is not what the gate checks. Reading .github/workflows/backlog-hygiene.yml rather than assuming, it fires only when all three hold:

  1. an explicit BACKLOG #<digits> token appears in the pull request title or body — not in commit messages, and a bare #123 deliberately does not count, because in this repo that is usually a PR number;
  2. the PR changes something under messagefoundry/, ide/ or messagefoundry_webconsole/;
  3. and then it must touch docs/BACKLOG.md or docs/archive/backlog/.

This PR meets (2) — three engine files — but not (1), because I wrote the item numbers as bare #1020 and so on. So the gate correctly reports "no claim to enforce" and exits 0.

Which means the banner question is unresolved rather than answered. If this work does complete those items, a reviewer adding BACKLOG #1020 to the title or body is what makes the gate ask, and the entry would then genuinely be owed. Leaving it as I wrote it means nothing is enforcing that, and the green tick is not evidence to the contrary.

Every other statement above stands. All 13 required contexts ran and passed on this PR.

wshallwshall and others added 8 commits August 14, 2026 17:28
… checks (BACKLOG #1105)

The docstring said `APPROVED_KEX_GROUPS` reaches "zero of this function's six call
sites" and, three paragraphs later, "a call at six sites with zero effect". Counted
on origin/main rather than taken from the item: there are SEVEN sites that build and
harden a real TLS context (api/tls.py:55, config/tls_policy.py:1001, transports/
dicom.py:145 and :463, transports/mllp.py:552 and :591, transports/remotefile.py:248)
plus one report-only reference in kex_groups_report's throwaway probe at :181.

The fix is not six-to-seven. A hardcoded seven is the same defect one increment later:
a second definition of a fact the tests already DERIVE (test_tls_policy.py:594-630
scans for the call marker instead of listing sites), and nothing checks the docstring
against it. So the count is removed and the derived list is named as the source. This
is the rule the project already applies to the backlog reader and to ENGINE_UI_SEAM:
one definition, derived, never a hand-maintained integer beside it.

The item said the docstring was "the only place it is asserted". It was not. The same
wrong count sat in two more places, both found by grep and both fixed here:
tests/test_tls_floor_probe.py:206 and tests/test_tls_policy.py:124. Ledger copies are
left alone -- authoring ledger content is not a builder's (owner ruling 2026-08-13),
and docs/archive/backlog/BACKLOG-CLOSED.md additionally carries stale LINE numbers for
the same sites, which is a separate correction for whoever owns that file.

No behaviour change: comments and docstrings only.

Verified: ruff check + ruff format --check clean on all three files; mypy strict clean
on tls_policy.py; pytest tests/test_tls_policy.py tests/test_tls_floor_probe.py =
81 passed, exit 0. Import resolution confirmed to this worktree, not the primary
checkout, before trusting that run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…s (BACKLOG #1020)

The PHI startup gate computes `security_channel_ready` from the SMTP transport alone
(`notify_security_events` + `email_smtp_host` + `email_from`). That answers "is a
transport configured" and never "can the account that matters actually receive" --
SDS-3.8, the instrument answering the adjacent question. The two come apart on exactly
the instance the gate protects: `_ensure_bootstrap_admin` creates the account holding
`frozenset(Permission)` with no `email=`, and `SecurityEventNotifier.notify` opens
`if not event.email: return`, so all ten notice types no-op for it while the gate
reports a healthy channel.

`AuthService.has_notifiable_admin()` is the missing half. Scoped to the ROLE, not the
bootstrap account: `email` is optional in `UserCreateRequest` and is not required for
the Administrator role, so a hand-created privileged account has the identical hole,
and keying on the bootstrap user would close this instance while leaving the class open.

This is the predicate only. The startup gate that consumes it is the next layer, and
its PLACEMENT is a separate decision (ADR 0167) -- measured: raising after
`engine.start()` hangs (BACKLOG #1257), and raising earlier in the lifespan exits with
uvicorn's 3 rather than the 2 every `_serve` refusal returns.

THE ARMS ARE ASYMMETRIC ON PURPOSE, and the mutation proves it discriminates rather than
merely passing. Removing the role test reds `test_a_non_administrator_with_an_address_is_
not_enough` ALONE, with the other two still green -- so the arms separate "keys on a
deliverable ADMIN" from "is merely hard to satisfy". A uniform failure would have taught
nothing (COMMON 4.5.2). The bootstrap arm reproduces the defect on the REAL first-run
path rather than a hand-built fixture.

RECORDED, NOT FIXED: this is now the THIRD copy of "who is an enabled administrator" in
this file, beside `is_last_enabled_admin` and `_other_enabled_admin_exists`. Their
agreement is a convention with nothing binding it -- if one gains a lockout or
auth_provider condition the others keep the old answer silently. The docstring says so
rather than claiming the copies cannot drift, which would be an unenforced property of
exactly the kind #1105 was about. Extracting a shared enumeration is its own item; doing
it here would rewrite two guards this change has no business touching.

Verified: ruff check + ruff format --check clean; mypy strict clean on service.py;
pytest tests/test_notifiable_admin.py tests/test_last_admin_guard.py = 7 passed, exit 0
(the sibling suite included deliberately -- it exercises the enumeration this sits beside).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…le address, early in the lifespan

Records the PLACEMENT decision for BACKLOG #1020, which the owner ruling (2026-08-13,
option (b): gate startup on a deliverable channel) settled the WHAT of but not the WHERE.

The defect: `security_channel_ready` is `notify_security_events and email_smtp_host and
email_from` -- SMTP wiring alone. It asks "is a transport configured" and never "can the
account that matters actually receive" (SDS-3.8). Those come apart on exactly the
instance the gate protects: the bootstrap admin holds `frozenset(Permission)` with no
`email=`, and the notifier opens `if not event.email: return`.

PLACEMENT, settled by measurement rather than preference:
  LIFESPAN after engine.start()  -- OUT. #1257: an exception there unwinds nothing and
                                   HANGS, which is strictly worse than the defect.
  PREFLIGHT in _serve            -- recommended, then WITHDRAWN BY ITS AUTHOR on
                                   measurement: _serve opens a store zero times across
                                   1042-2833 and list_users() is async, so it is the
                                   first store open in a preflight that never has one.
  EARLY-LIFESPAN                 -- CHOSEN. The 191-line window between app.py:5540
                                   (open_store) and :5731 (engine.start()); store open,
                                   no engine tasks, check is a plain await.

THE EXIT CODE CHANGES AND THE DIVERGENCE IS FORCED. Measured: raising there exits 3 in
0.49s, and `sys.exit(2)` there ALSO exits 3 -- uvicorn catches SystemExit and treats it
as a startup failure. Against a positive control that reached a RUNNING server and
self-stopped with a distinct 99, which is what makes the exits mean anything rather than
being the only outcome the rig could produce.

The cost is stated no larger than it is, and an earlier draft is corrected in place:
_serve returns 2 at 32 sites and DEPLOYMENT.md says "(exit 2)" twice, but BOTH citations
are scoped to specific refusals and no line generalises it -- so this is an inconsistency
with two documented refusals, not a contradiction of a universal claim. And it must not
be claimed exit 2 gives a clean stop today: install-service.ps1:463 sets NSSM `AppExit
Default Restart`, so the wrapper restarts on any code.

Rejected and recorded rather than omitted: a sentinel catching SystemExit at the
uvicorn.run() call site to re-exit 2. UNTESTED -- that "if" was never measured -- and a
cross-layer mechanism bought to remove an inconsistency the NSSM finding makes nearly
free. An unmentioned alternative gets re-derived; a declined one with a reason does not.

The ADR states plainly what it does NOT demonstrate: the termination evidence is a
MINIMAL REPRO, not the real gate, so it does not discharge #1020's rider. A rider that
exists because someone inferred is not satisfied by an inference.

The DEPLOYMENT.md exit-3 line deliberately does NOT land here -- it lands with the gate,
because documenting a startup-stage refusal before one exists describes behaviour the
code does not have.

Verified: pytest tests/test_feature_map_claims.py = 9 passed, exit 0 (the doc-link and
ADR-index checks); ruff/format clean via pre-commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ixed on PR #394

The ADR disqualified the LIFESPAN placement because raising after `engine.start()`
hangs. That measurement was taken against `origin/main`, and I wrote it as though it
were a property of the codebase. It is not.

VERIFIED BY READING THE ARTIFACT, not the claim that reached me:
`tests/test_lifespan_startup_unwinds.py` exists at refs/pull/394/head, docstring
"BACKLOG #1257: a startup failure after `engine.start()` must let the PROCESS exit."
So #1257's fix is already built, on a PR, while the item still reads "not started" on
main -- the same unpushed-work-is-invisible shape that produced tonight's duplicate
#1105 build and the #1216/#1272 duplicate filing.

THE DECISION IS UNCHANGED AND DOES NOT DEPEND ON THE HANG. EARLY-LIFESPAN avoids the
post-`engine.start()` window entirely, so it is correct whether or not #1257 has landed.
What changes is only WHY the alternative was rejected.

Why this correction is worth a commit rather than a footnote: a structural-sounding
argument about a defect that no longer exists is worse than no argument. The next reader
finds no hang, concludes the ADR is wrong, and distrusts the parts that are still right.
This one was only ever right about a ref, and now says so.

Also adds an open check the ADR could not have had: the exit-code arms were a minimal
repro, unaffected by #394, so they remain valid about uvicorn -- but the REAL gate's
behaviour inside a lifespan that now unwinds properly is unmeasured by anyone. Plausibly
unchanged, and plausible is not measured.

Verified: pytest tests/test_feature_map_claims.py = 9 passed, exit 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…n the window I chose

Found by writing the code, not by reading it. The ADR chose EARLY-LIFESPAN (after
open_store at api/app.py:5540, before engine.start() at :5731). That placement is
IMPOSSIBLE for this check, for two independent reasons either of which is fatal.

Measured, one lifespan, in order:
    5540  store = await open_store(...)        <- the chosen window starts
    5731  await engine.start()                 <- the chosen window ends
    5837  auth = AuthService(...)              <- the service does not EXIST until here
    5852  bootstrap = await auth.initialize()  <- CREATES the bootstrap admin
    5923  yield

(1) There is no AuthService in that window to call has_notifiable_admin() on.
(2) On a first run there is no administrator at all -- initialize() is what creates it
    (auth/service.py:517, _ensure_bootstrap_admin). A check there would REFUSE EVERY
    FIRST RUN, before the account it is about exists, converting a gate that reports a
    wrong answer into one that prevents startup.

SO THE #1257 DEPENDENCY IS REAL AND RETURNS. The check must sit after :5852, inside the
post-engine.start() window #1257 records as hanging. The Dispatcher's original ruling --
do not close #1020 before #1257 -- was right on its own terms, and my narrowing of it
was wrong.

THIS VINDICATES THE RIDER'S AUTHOR, who assumed the lifespan placement and was right for
a reason none of the three seats arguing about it identified: not "the store is there",
but THE DATA THE CHECK NEEDS DOES NOT EXIST UNTIL AFTER THE ENGINE HAS STARTED.

Unchanged: the decision to gate on a deliverable address, the predicate (29a026e), the
scoping to the ROLE, and the exit-code findings -- which were always about the post-start
path.

The placements table gains the column that actually decides it: "has the data?". The
first two versions compared store access, termination and exit code -- three real
properties, none of them the binding constraint.

The ADR's TITLE is now wrong and is left alone deliberately: renaming the file would
break the index row and every citation, and the overturn is stated in the first line of
the Decision. Retract in place, keep the retraction.

Verified: pytest tests/test_feature_map_claims.py = 9 passed, exit 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ACKLOG #1235)

#1235's residual (1): `main()`'s exit code and its `--advisory` escape had ZERO
coverage. Its last line IS the fail-closed contract --

    return 1 if (live and not args.advisory) else 0

-- and nothing asserted it. Invert the `not`, or return 0 unconditionally, and the whole
suite stayed green. This file's own header calls a detector that cannot fail "not a
gate"; its exit code was in exactly that state.

HOW THE GAP WAS FOUND, because the method transfers better than the finding: a SET
DIFFERENCE over the module's public surface, not a grep for something missing. The
module defines six top-level names (_load_backlog_module, allocation_floor,
allocated_numbers, citations_in, unresolved_citations, main); the suite exercised four.
`main` was the sole untouched public entry point. Positive enumeration on both sides, so
it rests on nobody's choice of pattern -- an earlier token-grep version of the same claim
was weaker and I replaced it.

FIVE ARMS, AND THE LAST TWO ARE THE POINT. Both produce HITS and both must exit 0,
because the contract keys on the LIVE SHAPE rather than the hit count: a number at or
below the allocator's high-water mark can never be issued, and a PR/foreign-repo
reference is not a backlog citation at all. Without those arms a gate that fired on any
hit would pass -- and it would red the tree today on 26 permanently-harmless citations,
which is how a gate gets switched off.

TWO MUTATIONS, KILLING DISJOINT SETS, so the coverage is proven in both directions:
  return 0                  -> reds {live, advisory}                    (cannot fail)
  return 1 if hits else 0   -> reds {advisory, below-floor, PR-shaped}  (over-fires)
Neither alone would have shown it. The detector was reverted after each and verified
byte-identical to HEAD, with a control confirming the diff instrument fires on the file
that did change.

The below-floor number is DERIVED (first hole under the floor), not hardcoded: a literal
becomes a RESOLVING number the day it is filed, at which point the arm stops testing the
below-floor branch and nothing says so.

Verified: pytest tests/test_dangling_citation_check.py = 39 passed, exit 0; ruff check +
format clean; mypy strict clean -- it caught a real one, `cc` is importlib-loaded so
allocation_floor() is Any and the arithmetic silently widened the return type.

No ledger edit: authoring the banner is not a builder's (owner ruling 2026-08-13). This
closes residual (1) only -- (2) the rule existing twice, and (3) the gate not running on
doc-only PRs (#1262), are untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…35 residual 2)

The predicate the gate's exit code keys on existed TWICE: inline in `main()`, and
re-derived inside the gate test. They agreed by convention with nothing binding them --
a rule with two definitions, inside the very tool that exists to catch that shape in
other people's gates.

`is_live_shape(hit, floor)` is now the one definition. `main()` calls it, and the test's
`_live_shape_citations` calls it plus `unresolved_citations` instead of re-implementing
both halves (`number in filed` duplicated the latter; `number <= floor or pr_shaped`
duplicated the former).

THE CONSEQUENCE IS CHECKABLE, AND IT IS THE PROOF RATHER THAN THE CLAIM. Mutating
`is_live_shape` to `return True` now reds THREE tests across BOTH consumers:

    test_no_docs_citation_names_a_number_that_can_still_be_issued   <- the real-tree GATE
    test_a_citation_BELOW_the_floor_is_reported_but_does_not_fail   <- exit-code arm
    test_a_PR_SHAPED_reference_is_reported_but_does_not_fail        <- exit-code arm

Before this, the gate test carried its own copy: a mutation to main()'s inline predicate
left it GREEN. That divergence is precisely what "agreeing by convention" buys, and one
mutation reaching both consumers is what shows the convention has been replaced by a
mechanism.

The detector was reverted after the mutation and verified to carry no MUTANT text, with
the suite back to green.

Verified: pytest tests/test_dangling_citation_check.py = 39 passed, exit 0; ruff check +
format clean on both files; mypy strict clean on both.

No ledger edit -- authoring the banner is not a builder's. Residual (3), the gate not
running on doc-only PRs, is #1262 and is untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…LOG #1273)

`controlchars.py` exists to state `ord(ch) < 0x20 or ord(ch) == 0x7F` once -- its own
docstring records that it replaced the expression written seven times across six files.
It then kept TWO copies of its own, one in each public arm.

`_is_control_char` is now the one definition; both arms call it.

THE RISK WAS ASYMMETRIC, AND I MEASURED IT RATHER THAN ASSERTING IT -- on the two-copy
structure, before changing it:

    widen the PREDICATE arm alone  -> CAUGHT.     4 tests red, because
        test_c1_and_unicode_separators_are_deliberately_NOT_caught pins the alphabet
    widen the STRIP arm alone      -> NOT CAUGHT. 47 passed, exit 0

So the copies were PARTLY bound, and the unguarded direction is the one that matters.
Widening the alphabet is the stated reason this module exists ("a behaviour change at
seven call sites at once ... make it deliberately"), and a deliberate widening applied to
the neutraliser would silently strip more than the screen refuses -- a screen and its
neutraliser disagreeing about their own alphabet, with every test green.

I PREDICTED THE SUITE WOULD MISS BOTH DIRECTIONS AND THE MEASUREMENT REFUTED HALF OF IT.
The prediction is recorded here because the refutation is the useful part: the item says
"nothing binds them", I said the tests would stay green, and the truth is neither -- one
direction is pinned by an explicit alphabet test and the other is wide open. A claim
about which mutations a suite kills is checkable in one command and is worth checking
before it goes in a docstring.

Proof that the definition is now shared: mutating `_is_control_char` to `return False`
reds 39 tests spanning BOTH arms -- the has_control_char parametrisation, the strip
tests, and the two cross-checks. One mutation reaching every consumer is the property;
the probes above are what show it was not already true.

Both probes and the mutant were reverted and the file verified to carry no probe text.

Verified: pytest tests/test_controlchars.py = 47 passed, exit 0; ruff check + format
clean; mypy strict clean. No behaviour change -- the alphabet is byte-identical.

No ledger edit: authoring the banner is not a builder's.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@wshallwshall

Copy link
Copy Markdown
Collaborator Author

Do not merge without rebasing first: this would silently DELETE content from main

Measured with git merge-tree --write-tree origin/main <head>, then diffing the resulting tree against main -- i.e. what the merge would actually produce, not what the branch contains.

532 deletions across 10 files:

  • tests/test_notifiable_admin.py (-126)
  • tests/test_tls_floor_probe.py, tests/test_tls_policy.py

Why nothing would warn you

This merges CLEAN. Git conflicts on concurrent edits, never on invalidated claims -- a stale branch silently drops work it never touched, no marker appears, and every check stays green. The deletions above are main's content that this branch predates.

The remedy

Rebase onto origin/main, then re-review. The rebase is where you decide, line by line, which of these deletions were intended and which are the base being old. Do not clear the BEHIND state with update-branch and merge on green -- green does not see this.

Found during a sweep of all open PRs; five of twelve show this shape. Not a judgement on the work, only on the base.

@wshallwshall

Copy link
Copy Markdown
Collaborator Author

Correction to my previous comment: I quoted the wrong side of the stat

My earlier comment said 532 deletions. That number is what this branch ADDS, not what merging would lose. I read git diff --stat in the wrong direction.

The corrected figure: merging would lose 24 line(s) that main currently has.

git diff --shortstat origin/main <merge-tree>
  -> 532 insertions (this branch's work, correctly added)
  -> 24 deletions  (main's content, silently dropped)

The finding itself stands and so does the remedy -- rebase before merging, do not clear BEHIND and merge on green -- because a clean merge still drops those 24 lines with no marker. But the magnitude was overstated and the number in my first comment should not be quoted.

@wshallwshall

Copy link
Copy Markdown
Collaborator Author

RETRACTION: my "would silently delete content" comments on this PR were wrong

Both of my earlier comments are withdrawn. The finding does not survive a discriminator, and the remedy I attached to it was justified by a defect that is not there.

What I did wrong

I counted removed lines in a merge result and reported them as lost content. Those are different questions. A line-for-line REPLACEMENT scores identically to a DELETION under a line count -- and this branch's changes are overwhelmingly rewrites.

The discriminator, run on this PR

For every line main has that the merge result lacks, I extracted a distinctive identifier and asked whether it survives anywhere in the merged tree:

removed lines carrying an identifier : 8
payload SURVIVES in the merged tree  : 8
genuinely absent                     : 0

Zero. Every removed line's payload is present in the merged result.

The concrete case that broke my own claim

I said this branch deletes initial_password_expiry_hours (ASVS 6.4.1). Measured occurrence counts:

initial_password_expiry_hours   main: 10   after merge: 25
bootstrap_expiry_hours          main: 35   after merge: 65
password_claimed_at             main:  0   after merge: 52

Not deleted -- expanded, plus a new mechanism (ADR 0164, password_claimed_at: recorded rather than inferred from mutable credential state).

What still stands, and what does not

Does not stand: any claim that merging this drops content, and the "never update-branch-and-merge-on-green" remedy insofar as it rested on that.

Still true, but unremarkable: this branch is BEHIND, and branch protection is strict: true, so it must re-sync before merging regardless.

Credit where due: a peer session caught this by verifying one file in #437 -- apt-get install lines I scored as removed were present in the merged tree wrapped in timeout and a conditional, which is that branch's entire subject. Same shape as errors I flagged in others today: the instrument answered truthfully, about the neighbouring question.

@wshallwshall
wshallwshall enabled auto-merge (squash) August 19, 2026 13:44
@wshallwshall
wshallwshall disabled auto-merge August 19, 2026 18:43
…er-authored banners)

ADR 0165 pairing on a stranded branch whose author session is gone. The fixes are that lane's;
these three ledger entries are the lander's, because a builder may not author ledger content.

1105 CLOSES. The docstring's call-site count was DELETED rather than corrected, which is the
right fix: the site list is derived by the tests, so a number in prose is a second definition
nothing checks. Correcting six to seven would have produced a true sentence measuring the wrong
thing and would have read as freshly checked forever.

1273 STAYS OPEN, and the banner says why. controlchars.py now states the set once and both its
functions read it -- but logging_setup.py:66-70 still re-derives the same set and the branch
never touches that file. The item names three spellings and asks for one definition of the SET.
Two of three now share one.

1020 STAYS OPEN, and its stated hold condition has EXPIRED. The item says do not close before
1257 lands; 1257 HAS landed, content-tested against origin/main rather than read from its banner,
which still reads open. The live reason is different and is measurable: __main__.py:2350-2354
still computes readiness from SMTP wiring alone, and this branch adds the predicate without
wiring it in. A reason that can expire silently is a worse reason even while it is still true.

1235 IS DELIBERATELY UNTOUCHED. It belongs to the dispatcher seat's current wave; two
independently-authored banners for one item is worse than a conflict, because both look correct
and a merge may silently take one.

parse_items before/after: 312/231/81 -> 312/230/82, the expected 0 items / -1 open / +1 closed.
Neither open-item amendment contains any character from the status alphabets, asserted in the
edit script, so neither can be misread as a second banner.

Built with plumbing (read-tree, update-index, commit-tree); no working tree touched. Plumbing
bypasses pre-commit, so the backlog status check was run by hand against the result.
@wshallwshall
wshallwshall enabled auto-merge (squash) August 20, 2026 13:47
@wshallwshall

Copy link
Copy Markdown
Collaborator Author

Lander note -- the ledger commit is on, and the red above was NOT what it looked like

The body above is retained unedited, including the author's correction, because that correction is the evidence for what follows and editing it would delete the thing that made this checkable.

Why the required check was red, and it was not this PR's claim

a PR that implements BACKLOG #N must update BACKLOG.md fired on the token BACKLOG #1020. This PR never claimed #1020 -- it deliberately writes its item numbers bare. The token the gate matched sits inside the author's own correction, in backticks, in the sentence explaining that a reviewer adding that token is what makes the gate ask.

The paragraph explaining the gate is what tripped the gate. .github/workflows/backlog-hygiene.yml:66 extracts the claim with a raw grep -oiE 'BACKLOG #[0-9]+' over title and body and strips no inline code spans, so a mention and a use are indistinguishable. Filed as its own item.

That defect is not fixed by this PR, and the green it is about to get is not evidence it is fixed. The banner was owed on the merits and the matcher is separately wrong; those are independent, and letting the green settle it would retire a real defect with a coincidence.

What the lander added

One commit, docs/BACKLOG.md only, authored under ADR 0165 because a builder may not author ledger content.

  • #1105 CLOSES. The call-site count was deleted rather than corrected, which is the right fix -- the site list is derived by the tests, so a number in prose is a second definition nothing checks.
  • #1273 stays OPEN, partial. controlchars.py now states the set once and both its functions read it, but logging_setup.py:66-70 still re-derives the same set and this branch never touches that file. The item names three spellings and asks for one definition of the set; two of three now share one.
  • #1020 stays OPEN, and its stated hold condition has EXPIRED. The item says "do not close this before #1257 lands". #1257 has landed -- content-tested against origin/main, not read from its banner, which still reads open. The live reason is different and measurable: __main__.py:2350-2354 still computes readiness from SMTP wiring alone, and this branch adds the deliverable-address predicate without wiring it in.
  • #1235 is deliberately untouched. It belongs to another seat's current wave. Two independently-authored banners for one item is worse than a conflict, because both look correct and a merge may silently take one.

parse_items before/after: 312/231/81 -> 312/230/82, the expected 0 items / -1 open / +1 closed. Neither open-item amendment contains any character from the status alphabets, so neither can be misread as a second banner.

Built with plumbing (read-tree, update-index, commit-tree) because this branch's authoring session is gone; no working tree was touched. Plumbing bypasses pre-commit, so the canonical status check was run by hand against the result: 548 items, each declaring exactly one status.

Expect a red that is not this PR's

main is red on test (windows-2025, py3.14) at tests/test_connscale_smoke.py -- item #1292, open and claimed by another lane. 5 of the last 11 CI push runs on main failed across four distinct jobs.

Discriminator, stated in advance: a red in test_connscale_smoke.py is inherited from main, not this PR's, and is not a reason to re-run. A red in the auth, TLS, controlchars or citation-check paths this PR touches is this PR's.

@wshallwshall
wshallwshall merged commit 4c28bad into main Aug 20, 2026
38 of 39 checks passed
@wshallwshall
wshallwshall deleted the claude/modest-ellis-a46139 branch August 20, 2026 14:05
wshallwshall added a commit that referenced this pull request Aug 20, 2026
…llision

PR #428 landed as 4c28bad adding ADR 0167's index row; this branch adds 0169's. Both are
appended as the LAST line of docs/adr/README.md, so git sees one line changed two ways. Every
other path content-merged cleanly, including docs/BACKLOG.md and auth/service.py, which are
changed on BOTH sides.

RESOLVED AGAINST A COMPUTED TARGET, NOT BY EYE, because an index is exactly where keep-both-sides
is a hope rather than a resolution:

    A = rows at merge-base   157
    B = rows at PR head      158   (adds 0169)
    C = rows at origin/main  158   (adds 0167)
    target = C + (B - A)     159

    result                   159 rows, 0167 present, 0169 present, ZERO duplicate numbers

Built with plumbing; no working tree touched.
wshallwshall added a commit that referenced this pull request Aug 20, 2026
…rning runs an index test) (#465)

* backlog: close #1261 -- the cutover ratchet fix is on main by content

parse_items 313 items / 231 open / 82 closed, from 232/81. Expected closure
delta: 0 items, -1 open, +1 closed. backlog_status_check reports 549 across both
files, each declaring exactly one status.

Closed on the release condition this project uses -- the fix TEXT on main -- and
verified that way rather than on a merged badge. The
tests/test_cutover_slug_rot.py blob is 13a711b on origin/main, on PR #461's head
and on 9d781ed: all three identical.

Recorded in the banner because it will be reached for again: the cumulative
patch-id test does NOT match here, branch 43f5a21a against squash 148226e6, and
that proves nothing. A cherry-pick onto a different base plus an update-branch
breaks the equality without the work being absent. On a squash-merging repo a
patch-id MATCH proves landed and a NO-MATCH proves nothing. Blob identity settled
it in one command.

The banner's closed-alphabet character was LIFTED PROGRAMMATICALLY from an
already-closed item rather than typed, and asserted a member of _CLOSED before
use. Typing a status glyph by hand is how a wrong one enters the file, and the
alphabet is machine-parsed.

Held until the fix landed rather than closing on the PR, because main carried the
bare pattern at zero slack until then and a premature close leaves main one
innocent documentation edit from red.

* backlog: file #1298 -- the archive dialog's discard warning is an index test, not a loss test

The session-archive dialog lists the worktree's dirty set and says the files
"will be permanently discarded". For a ?? entry that is a statement about THIS
worktree's index, not about the content: a worktree whose HEAD predates a commit
sees every file that landed since as untracked, because they are absent from its
index while being tracked on main.

Measured on a live instance. It warned that tests/test_notifiable_admin.py and
docs/adr/0167-*.md would be permanently discarded. Both are tracked on
origin/main -- the test was added at 4c28bad (PR #428), which merged that same
session -- and every local copy was byte-identical to main's blob in all six
worktrees holding them. Archiving would have discarded nothing.

Reproduced on this seat's own tree, which is what establishes the mechanism
rather than the coincidence: at dd655da, two behind origin/main, git status
showed the identical ?? pair; fast-forwarding to 1df0c07 converted both to
tracked with no edit and no conflict. The files never changed, the base did.

The failure is systematic and its timing is adversarial -- the warning is
loudest exactly when the fleet lands fastest, and wrong in those cases. Both
responses are damaging: believing it refuses a safe archive, and learning to
dismiss it trains the response that defeats a real one.

Scope is stated in the item so it is not mis-built: the dialog is the Claude Code
harness and is not this repository's code. In scope here is a WORKTREES.md
subsection plus a scripts/coord/ helper that answers the recoverability question
for a whole dirty set and prints what it examined, refusing to call anything it
cannot read safe.

Also records #1297 as a permanent hole in the erratum. It was allocated from the
PRIMARY checkout because the allocating shell's cwd was the primary, so the
ledger gate correctly refused that commit and the item was re-filed at #1298.
alloc.ps1 records the claim against whatever tree it runs in.

Verified with the repo's own gate: each item declares exactly one status.
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