Skip to content

tls: first-party contexts inherited an unasserted suite list (#1317), repair main's census tests, file #1319 and #1322 - #525

Merged
wshallwshall merged 5 commits into
mainfrom
claude/mercycare-account-cleanup-1160a8
Aug 22, 2026
Merged

tls: first-party contexts inherited an unasserted suite list (#1317), repair main's census tests, file #1319 and #1322#525
wshallwshall merged 5 commits into
mainfrom
claude/mercycare-account-cleanup-1160a8

Conversation

@wshallwshall

@wshallwshall wshallwshall commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Work recovered from a cancelled account, plus a repair to main that this branch was the first to hit. Four commits, four layers.

bd37e233 files #1319 — the demote-teardown timing assert cannot discriminate its own mutation
3fad32ee #1317 — contexts the engine built but never named inherited an unasserted suite list
2b6dc1be repairs main — the summary_access census assertion is stale on two backends
748675a6 files #1322 — the serverdb path gate selects on the tests, not on the sources they assert against

This PR repairs main

The three database legs were red on arrival and not caused by this branch.

#514 added a masked key to the summary_access audit detail and updated the coalescer, but not the Postgres and SQL Server twins of the census test. It merged green, and every PR afterward that runs those legs inherits the break. This one was first; #516 was second.

Why it merged green is the root cause, and it is filed as #1322 in this branch. ci.yml's serverdb path-gate alternation lists store/, three pipeline modules, config/, transports, a tests/test_(...) list and ci.yml itself — but not messagefoundry/api/. Verified independently here: zero occurrences of messagefoundry/api in the filter region against a positive control of one for messagefoundry/store. So a change to api/app.py selects those legs on neither a pull request nor a push. They were never chosen, rather than skipped past.

The stated invariant is one level too narrow. ci.yml says the alternation must list every file the store pytest steps run — the test files. It says nothing about the source files those tests assert against, and test_postgres_store.py asserts on a dict produced by api/app.py::_emit.

Not filed as "main skips the legs on push": that is a deliberate billed-minutes decision stated in ci.yml, and the nightly runs them unconditionally, so the blind window is a day rather than open-ended.

The detail worth keeping: the SQLite twin stayed green because it asserts '"count": 5' in detail — substring containment, which cannot see an added key. The two store twins assert dict equality, which can. The weaker assertion survived the change the stronger ones caught, and the stronger ones were the ones not being run.

What #1317 closes, and what it does not

Its residual said the earlier work covered the operator knob and the shipped context builder, did not enumerate every SSLContext construction, and does not reach suite sets chosen inside ldap3, hvac or ODBC Driver 18.

This closes the first-party enumeration half — the OIDC identity-provider opener, the syslog TLS forwarder on both arms including tls_verify=False, the Postgres store, and the REST/SOAP/FHIR transports. The library half stays open and the item now says so.

The change also records a residual rather than papering over it: on the default store path True hands asyncpg the job of building the context, so no context exists in engine code to assert on. Asserting a look-alike built locally would grade an object the connection never uses.

Verification

  • All three database legs green — postgres, SQL Server 2022, SQL Server 2025. These cannot run locally (this repo silently skips them), so CI was the only authority available and it has now spoken.
  • ruff check, ruff format --check, mypy --strict clean; 141 tests across the TLS and SOAP files.
  • Full suite 13,183 passed, 834 skipped, exit 0 — scoped honestly by its author: launched at 12:11 against 3fad32ee, so it validates the TLS commit as written and covers neither the later merge nor the two commits after it. The green CI on the current head is what covers those.

Provenance

3fad32ee was built by a workflow child of the asvs-honest-pass seat, whose account was cancelled at 16:56Z mid-verification, leaving 680 lines uncommitted in a worktree locked by a dead pid. The mercycare-account-cleanup seat recovered it, replayed it onto a main that had moved two commits underneath, and re-verified against the newer three-property harden_cipher_suites rather than the one it was written against.

…iminate its own mutation under load

Recovered from a session that was measuring repo health when its Claude account
was cancelled mid-command. It had found the CI failure and never reported it.

THE OBSERVED FAILURE. tests/test_adr0157_demote_teardown.py:106 asserts
elapsed < 1.5 and reported 1.92s on main at 2d1c89e, CI run 32580332076, leg
test (ubuntu-latest, py3.14). That commit was docs-only, so nothing in the tree
under test moved the number.

WHAT MAKES IT MORE THAN A FLAKE, and it is measured rather than argued. Four
implementations, 200 sources at 0.4s under a 3.0s budget:

  shipped, max-shaped      0.40s  200/200 finished   timing PASS
  MUTANT sequential        3.01s    7/200 finished   timing FAIL
  MUTANT semaphore(8)      3.00s   56/200 finished   timing FAIL
  MUTANT semaphore(64)     1.63s  200/200 finished   timing FAIL

The semaphore(64) row is the item. Every source finishes, so all(stop_finished)
passes and _pending_source_stops is empty -- the wall-clock bound is the ONLY
assertion that catches it. So the threshold cannot simply be raised: the
discriminating band is (0.40, 1.63) and the bound sits at 1.50.

CI measured 1.92s for the SHIPPED implementation, which is above the 1.63s the
mutant produces. On that run the correct code was slower than the defect the
assertion exists to detect, so no threshold in the band could have separated
them. That is the same class as #1290 -- an instrument that goes silent on its
own subject exactly when it is under stress.

LOCAL CONTROL. Seven consecutive runs on Windows while the full suite ran
concurrently: 0.40 0.41 0.40 0.41 0.41 0.41 0.41. The floor is the 0.4s sleep
plus ~10ms, so CI's 1.92s is runner latency, not the tree.

The item records the fix direction (_Source already carries stop_started, and
concurrency width separates all four rows with no timing dependence) and states
plainly what is NOT established: this is at least one observed failure, with no
rate measured and no same-head re-run recorded.

Number allocated through scripts/coord/alloc.ps1. Backlog parser clean at 567
items, each declaring exactly one status.
…serted suite list (BACKLOG #1317)

Closes the first-party half of the residual #1317 recorded as open. That item
covered the operator-facing knob and the shipped context builder; it did not
enumerate every SSLContext the engine constructs.

RECOVERED, NOT REDONE. This was built by a workflow whose parent session's Claude
account was cancelled on 2026-08-22 at 16:56Z while its full-suite run was in
flight. The tree was left uncommitted in a locked worktree, three commits behind.
It is replayed onto current main -- which had moved two commits, including #1317's
own build -- and re-verified there. The claim on #1317 was taken over from that
dead seat via claim.ps1 -Force, recorded in claims/.history.

THE DEFECT IS INHERITANCE WITHOUT ASSERTION, not a missing context. Every hop
below always had a TLS context; the engine simply never held a reference to it,
so harden_cipher_suites never ran on it. Sites now asserted:

  auth/oidc_http.py        identity provider token + JWKS hop
  logging_setup.py         syslog TLS forwarder, BOTH arms incl tls_verify=False
  store/postgres.py        pinned-CA and verification-disabled arms
  transports/soap.py       mutual-TLS opener
  transports/rest.py       shared REST/FHIR/DICOMweb opener family
  transports/fhir.py       digest-auth rebuilt opener
  pipeline/alert_sinks.py  webhook opener

IT ASSERTS ON URLLIB'S OWN CONTEXT RATHER THAN SUBSTITUTING ONE, and that is
load-bearing. Measured on CPython 3.14.6 / OpenSSL 3.5.7: urllib's context adds
set_alpn_protocols(["http/1.1"]) and post_handshake_auth=True, which a hand-built
ssl.create_default_context() has neither of. Passing a look-alike would silently
drop ALPN and TLS 1.3 post-handshake auth from every default HTTP-family hop -- a
handshake change on a control whose whole point is to change nothing about the
connection. build_asserted_https_handler reads the handler's private _context and
FAILS CLOSED if absent, rather than shrugging and reporting success forever.

TWO FIRST-PARTY SITES ARE DELIBERATELY NOT ASSERTED, each with its reason in the
code so a later reader does not "fix" them. The TLS floor probe builds
ALL:@SECLEVEL=0 on purpose -- asserting there would empty the offer and turn a
probe that can fail into one that cannot. And the Postgres ssl=True default arm
hands asyncpg the job, so no engine object exists to assert on; grading a
look-alike would grade an object the connection never uses.

THE INSTRUMENT THAT GUARDED THIS RESIDUAL COULD NOT SEE IT, which is why the new
test file is the other half rather than more of the same. test_tls_policy.py
derives its call-site list from the presence of harden_kex_groups( in a file and
its guard is `if kex and assertions < len(kex)`, so a file with ZERO kex-pin
sites is skipped entirely. Every site above called neither helper, so the scan
passed over all of them in silence. test_tls_cipher_assertion_sites.py names each
construction and proves the assertion is REACHED, not merely present: contexts
that are built are checked by patching _is_forward_secret to report every suite
weak and requiring a ValueError naming that site's connector; contexts handed
through an opener are checked by requiring the context the opener will actually
use to be the SAME OBJECT the assertion ran on. It carries its own positive
control, so a test seeing no raise reports a missing call rather than an inert
instrument.

VERIFICATION, stated exactly. ruff check, ruff format --check and mypy --strict
all clean. 141 passed across test_tls_cipher_assertion_sites, test_tls_policy and
test_soap_wssecurity -- run against current main, so the work is confirmed
compatible with #1317's newer three-property harden_cipher_suites (forward
secrecy plus encryption plus peer authentication), which landed after it was
written. THE FULL SUITE IS STILL RUNNING and its result is NOT in this message;
it is committed now rather than held because the pool that killed the first
attempt is running hot again, and a branch commit is recoverable where an
uncommitted worktree is not. CI is the authority.
…d only a skipped leg hid it

NOT A DEFECT IN THIS BRANCH. #514 (BACKLOG #1187, merged as fdd89b4) added a
`masked` key to the summary_access audit detail and updated the coalescer, but not
the Postgres or SQL Server twins of the census test. This branch merely ran the
legs that hid it.

  api/app.py _emit, on main:
    detail=json.dumps({"count": count, "masked": masked, "window_start": hour*3600})

  tests/test_postgres_store.py:3409  asserted {"count": 5, "window_start": 0}
  tests/test_sqlserver_store.py:3435 asserted the same

MAIN IS NOT RED, IT IS BLIND -- which is the worse half and the reason this needs
saying. Both DB legs were SKIPPED on main's CI at fdd89b4 and at ae72f58 before
it, so main reports green while carrying two failing tests. They run on a pull
request, so the first PR to touch anything after #514 inherits three red required-
adjacent legs it did not cause. This one did: postgres store, sql server 2022 and
sql server 2025.

WHY THE LEG THAT DID RUN STAYED GREEN, and it is not luck. The SQLite twin at
tests/test_api.py:587 asserts `'"count": 5' in detail` -- a SUBSTRING containment
check, which cannot see an added key. The two store twins assert DICT EQUALITY,
which can. So the weaker assertion survived a change the stronger ones caught,
and the stronger ones were the ones not being run.

VERIFIED AT RUNTIME, not read off the source. Driving the real
_SummaryAuditCoalescer end to end against a live store:

    ACTUAL detail: {'count': 5, 'masked': 0, 'window_start': 0}
    OLD assertion holds  : False
    FIXED assertion holds: True

The coalescer is backend-agnostic -- it calls store.record_audit and nothing in the
shape is per-backend -- so the SQLite run is evidence for the Postgres and SQL
Server paths, which is exactly the substitution that makes this checkable here.

WHAT THIS RUN DOES NOT PROVE, stated because a local pass would be misleading:
this repo SILENTLY SKIPS both DB legs locally. Measured on this tree, 152 skipped
and zero run. CI is the only authority for the two lines themselves.

LEFT ALONE DELIBERATELY: test_api.py:587's substring assertion. Tightening it to
equality is the right change and it belongs to #1187's owner, not to a branch
recovering unrelated TLS work.
@wshallwshall
wshallwshall enabled auto-merge (squash) August 22, 2026 17:48
…ot on the sources they assert against

Root cause of PR #525's three red DB legs. Found by the liaison; every claim below
re-verified against origin/main by this seat before filing.

NOT "MAIN SKIPS THE LEGS". That is deliberate and filing it would be filing a
billed-minutes decision: ci.yml:1521 is `schedule || workflow_dispatch ||
serverdb=='true'` and its own comment names the no-per-merge-run guard. The defect
is one level down, in the PR arm -- the only arm that can catch a change BEFORE it
lands -- whose producer set has a hole.

THE HOLE. ci.yml:1358's alternation lists messagefoundry/store/, three pipeline
modules, config/(settings|wiring), a transports list, a tests/test_(...) list and
ci.yml. It does not list messagefoundry/api/.

THE INVARIANT IS WRITTEN ONE LEVEL TOO NARROW. ci.yml:1348 requires the alternation
list "every file the sqlserver-store / postgres-store pytest steps below run" --
the TEST files. Not the SOURCE those tests assert against. test_postgres_store.py
asserts on a dict built by api/app.py::_emit, so the rule cannot see this class.

IT HAS ALREADY FIRED. #514 touched api/app.py, serverdb evaluated false, the legs
never ran on its PR, push never runs them, and it merged green leaving two store
tests asserting a stale shape. PR #525 selected the legs and inherited three reds
it did not cause.

SECOND WEAKNESS NAMED ON THE ROW, not folded into the fix: the SQLite twin at
test_api.py:587 asserts substring containment and is blind to an added key. The
assertion strong enough to catch the change was the one not being run; the one
being run was too weak to notice. Tightening it belongs to #1187's owner.

Severity bounded in the same line that causes it -- the schedule arm runs these
legs nightly, so the blind window is about a day. What it does not do is stop the
merge.

Number allocated and claimed through scripts/coord. Backlog parser clean at 568
items, each declaring exactly one status.
@wshallwshall wshallwshall changed the title tls: first-party contexts inherited an unasserted suite list (BACKLOG #1317), and file #1319 tls: first-party contexts inherited an unasserted suite list (#1317), repair main's census tests, file #1319 and #1322 Aug 22, 2026
@wshallwshall
wshallwshall merged commit 4e9bf38 into main Aug 22, 2026
39 of 40 checks passed
@wshallwshall
wshallwshall deleted the claude/mercycare-account-cleanup-1160a8 branch August 22, 2026 18:09
wshallwshall added a commit that referenced this pull request Aug 22, 2026
…only

#525 landed 1319 and 1322 at the same append point this branch adds 1318, 1320 and
1321 to, so both sides grew the BACKLOG tail. Kept both, main's side first, no
re-sorting. A second smaller conflict in one banner block kept main's SHIPPED-BUT-OPEN
note above the Verdict and Closing-act fields this branch added.

The merge routes here rather than to the lander because 1318, 1320 and 1321 are
allocated to this worktree and the ledger gate accepts a BACKLOG.md carrying them only
from the tree that holds the allocation.

Result is 335 items, not the 334 forecast: main carries 332, this branch 333, and the
three unique here are 1318, 1320 and 1321 against 1319 and 1322 there. The forecast
predates the 1321 filing. Verified as a set union rather than an arithmetic guess.

Line endings verified rather than assumed. The file is CRLF throughout, 13386 of 13386,
zero bare LF introduced. My first check reported it as all-LF, which was a false zero
from a broken shell quote, and acting on that would have rewritten every line.
Conflict markers checked anchored at line start, because the file legitimately quotes
one in item prose and a bare substring test fails on a correctly resolved file.
wshallwshall added a commit that referenced this pull request Aug 22, 2026
Two seats filed the same root cause within minutes. #1322 landed on main first via
#525; #1321 is mine and arrives by merge, so neither the ledger gate nor either
author could see the collision. #1322 is the better record and carries everything
#1321 does, including the substring-versus-equality point about the SQLite twin.

The lander caught it before the merge was pushed, not me. I filed #1321 after
correcting its headline twice and never checked whether the defect was already on
main.

The banner is deliberately not flipped. Closing is the lander's act per
BUILDER.md:253 and :148, so the row says so and the closure is handed over. Until
then it is an open duplicate and the row warns a reader who reaches it first.

CRLF preserved, zero bare LF, item count and every status asserted unchanged.
wshallwshall added a commit that referenced this pull request Aug 22, 2026
… refuses (#524)

* backlog: file #1318 -- init writes a config the loader rejects

messagefoundry/scaffold.py emits an uncommented host key under [api]. That key is
in _RELOCATED_TO_SECURITY, and _reject_relocated_keys runs during load_settings.
So the product generates a file the loader that reads it refuses, and the first
serve after the first init fails. Present in the published release at v0.3.2, not
only on main.

The test gap is the part worth keeping: the two scaffold test modules call
load_settings zero times, against a positive control of 58 test files that do.
They assert string presence in the generated TOML, so a test asserting a string
the loader rejects certifies the defect rather than catching it.

Three sibling keys sit commented in the same template with instructions to
uncomment them, each of which becomes this same failure when an operator follows
the line beside it.

Measured by the ASVS Tracker, verified independently by the Liaison, re-verified
here before filing. One citation corrected in that chain: the emitting file is
messagefoundry/scaffold.py, not messagefoundry/config/scaffold.py -- my own first
read returned empty against the cited path, which is what surfaced it.

Conditional per CLAUDE.md section 0: zero deployments, so nobody is hitting this
today. A new adopter would hit it on their first two commands.

* backlog: file #1320 and amend #1318's writer-set framing

#1320: messagefoundry check catches ValueError from load_settings at three sites --
posture, build-check, alert-smtp-tls -- and returns ok with skipped. So the gate
loads a refused config, receives the refusal, prints it, and exits zero. The
existing test asserts rc == 0 and therefore passes on a config the loader rejects.

That is larger than #1318 and independently true: fix the scaffold template and no
config error of this class is failable. The fix is a distinction rather than a
catch list -- absent is a legitimate skip, present-but-refused is not, and the
except clause renders them identically. Same two-states-one-signal shape this repo
already records for goalPromptedAt.

#1318 amended: I filed it as though scaffold.py wrote one template. It writes
three. The other two carry zero relocated keys for a structural reason -- they are
flat env() value files, not settings sections, so no (section, key) pair can match.
Population is 1 active and 3 armed over the complete writer set.

The one-line diagnosis in the amendment is a builder's: the scaffold did not
change, the loader did. ADR 0118's relocation swept the docs and the settings and
never swept the one place that writes a config.

Both found by builders while sizing work I had scoped narrowly. Conditional per
CLAUDE.md section 0 throughout.

* backlog: correct #1320 to five catch sites, and name the fifth as a different defect

Filed as three. A builder found a fourth. Reading every catch of the tuple in
messagefoundry/checks.py finds five.

The fifth is worse in kind, not just one more of the same. Four return CheckResult
with skipped=True and detail "settings did not load", so the reason survives even
though the exit code discards it. The fifth returns PipelineSettings().snapshot_on_send
-- a shipped default substituted for a config that was refused, with no CheckResult,
no detail, and nothing printed. A reader cannot tell it fired.

Also corrects the file: messagefoundry/checks.py at 1761 lines, not
messagefoundry/verify/checks.py at 251, which holds none of them. My first read went
to the wrong path and returned an empty result, which is the same wrong-path trap
that #1318 already carries.

The count moving 3 to 4 to 5 across three seats in twenty minutes is recorded in the
item as the lesson: each measured honestly and each stopped at the first population
their instrument could see.

* backlog: correct #1320 to four checks, scoping the fifth out as a resolver

I amended this item to five sites and said fix all five. Two seats independently
classified the fifth as a value resolver rather than a check: the builder fixing it
and a second seat measuring the same file, neither prompted by the other. This
ledger was the outlier, so it moves.

A check owes a CheckResult; a resolver owes a value. Widening the resolver's
contract is a different change with a different blast radius, and with the four
checks now failing on a refused config the gate fails anyway.

The resolver still swallows silently and the item now says so explicitly, so that
"four" is not read as the whole surface.

* backlog: promote Verdict and Closing-act into the banner block on 192 open items

The field that decides whether a seat can finish an item has never been readable
by any tool. Verdict sits below the line where parse_items stops, so nothing has
ever read it, and a 93-item wave went out whose every item carried a verdict that
has closed zero times in 330 closed items. It closed zero.

This moves the machine-readable state into the banner blockquote, the only region
the parser reads, in the exact form the new field parser matches: a blockquote line
of key, colon, value, restricted to verdict, research and closing-act.

Closing-act is derived only where the derivation is evidenced. Verdict build means
the closing act is code. Verdict research means a vault scorecard re-score, which is
what the zero-closure measurement actually showed. The 26 demand-gate items and the
20 with no declaration are deliberately left alone: a missing field reads as a
refusal, which is the safe direction, and inventing a closing act for them would be
the same guess that caused this.

192 items carry the fields now, 98 closing by code and 94 by re-score. Verified by
asserting the item count, the item numbers and every open or closed status are
unchanged across the rewrite, and that the diff is 384 insertions with no other
line touched.

* backlog: record two owner rulings on the items they govern, and stop #1243 dispatching

Both rulings were made today and lived only in the owner queue record, so the items
they govern did not carry them. A builder found the first one the expensive way,
while about to take the item.

#1243 is blocked pending a real FHIR server. It is #1107 clause 3, and the encoding
question cannot be settled against our own suite because that suite pins %7C in five
places and so cannot disagree with itself.

Its Closing-act moves from code to blocked. I set it to code an hour ago in the field
migration, which would have let a dispatch gate hand it to a builder. The migration
derived the closing act from the verdict, and a verdict cannot see a block.

The item also carries an OWNER-RULED banner for a different ruling of 2026-08-13, so
a search for the phrase already hits and returns a reader who then stops looking. The
note says so, because that is what nearly sent this item out twice in one hour, once
from this seat.

#332 now carries the sigstore ruling: pin sigstore==4.4.0 and hash-lock it, not 4.5.0.
That row already names release.yml:255 and the OIDC identity it runs under.

Verified by asserting the item count, numbering and every status are unchanged.

* backlog: file #1321 -- main skips the two DB legs that would catch a break

A change can merge to main breaking the Postgres and SQL Server suites and main
still reports green, because neither leg runs on a push. Their if: has no push
case and the guard comment above them says so outright. The first pull request
afterwards inherits the red.

It has already fired. #1187 added a masked key to the summary-access coalescer.
Both database twins assert dict equality and break on it; the SQLite twin asserts
substring containment and cannot see an added key at all.

The generalisable half is the assertion strength rather than the skip. The leg that
ran passed honestly because its assertion was too weak to observe the change, and
the two assertions strong enough to catch it were the two not being run. A skipped
strong check and a running weak check produce the same green and only one is
evidence.

Verified at origin/main rather than this worktree, which was one commit behind and
initially showed no masked key at all. Whether the SQLite twin should assert
equality is #1187's call and is named as out of scope here.

* backlog: correct #1321 -- the defect is the producer set, not the push skip

I filed this an hour ago with the wrong headline. That the container legs do not
fire on a push is intended, and ci.yml:1317-1319 says so in as many words. Filing
that would have been filing a decision as a bug.

A peer refuted it and I re-measured at origin/main. The real hole is that the
serverdb path alternation at ci.yml:1358 does not list messagefoundry/api/, so a
change confined to api/ selects those legs on neither a pull request nor a push.
Counted in that one line with a control: messagefoundry/api appears zero times,
messagefoundry/store once. The break did not slip past the legs; they were never
selected.

The invariant is already written one level too narrow. ci.yml:1348 requires the
alternation to list every file the server-DB pytest steps run, which covers the test
files and not the sources they assert against. test_postgres_store.py:3409 asserts on
a dict produced by api/app.py::_emit.

Severity is bounded and now says so: the nightly runs these legs unconditionally, so
the blind window is about a day.

* backlog: mark the 21 demand-gate items with an owner-ruling closing act

These declare Verdict: demand-gate and none carried a machine-readable field, so a
dispatch gate saw nothing and a missing field refuses by default. That was the safe
answer but not an informative one.

Their closing act is an owner ruling: a demand-gated item is finished by deciding to
build it or to drop it, not by writing code, and the standing protocol is to pause
and ask before building one. Recording owner-ruling makes the gate refuse them for
the stated reason rather than for an absent field.

The 20 items with no verdict declaration anywhere are deliberately left alone. They
need a per-item read, not a derivation, and guessing a closing act for them is the
error this whole migration exists to stop.

* backlog: classify the last 20 undeclared items, and refuse the one that is contested

These 20 declared no verdict anywhere, so nothing was derivable and each needed a
read. Ten agents read them in five batches and an independent pass re-read every one
and tried to refute the classification. It changed five: 320, 1038, 1072, 1309, 1311.

Nineteen are applied as returned. #320 is not.

#320 is the only low-confidence result and it moved toward code, which is the one
direction that costs something. A wrong code hands an unfinishable item to a builder
with a machine-readable blessing on it, which is what a derivation did to #1243
earlier today. The argument for the change is real and is recorded on the item: it
mentions ASVS and scorecard zero times against a control of 112 items that mention
both, so there is no cell to re-score. That is good enough to doubt the old verdict
and not good enough to assert the new one, so the item carries the verdict, no
closing act, and the reason. A gate refuses it until someone reads it.

Every open item now resolves: 107 close by code, 94 by a vault re-score, 28 by an
owner ruling, 4 are blocked, and 6 carry no act and therefore refuse.

Verified by asserting the item count, numbering and every status unchanged.

* backlog: mark #1321 superseded by #1322 -- I filed a duplicate

Two seats filed the same root cause within minutes. #1322 landed on main first via
#525; #1321 is mine and arrives by merge, so neither the ledger gate nor either
author could see the collision. #1322 is the better record and carries everything
#1321 does, including the substring-versus-equality point about the SQLite twin.

The lander caught it before the merge was pushed, not me. I filed #1321 after
correcting its headline twice and never checked whether the defect was already on
main.

The banner is deliberately not flipped. Closing is the lander's act per
BUILDER.md:253 and :148, so the row says so and the closure is handed over. Until
then it is an open duplicate and the row warns a reader who reaches it first.

CRLF preserved, zero bare LF, item count and every status asserted unchanged.

* backlog: file #1323 -- the crypto-inventory gate has a store-only seam set

The required crypto-inventory gate is green while structurally unable to see a
first-party TLS seam. All five entries in CRYPTO_SEAM_MODULES are
messagefoundry.store.*, and pipeline/alert_sinks.py imports smtplib and
config.tls_policy, so no import it makes can trigger a review.

The file is correctly inventoried in the doc, so a reader checking the record finds
it present. The gate is what cannot see it, and a merge-blocking context blind to a
surface reports the same green whether that surface is sound or not.

The one-line fix newly implicates at least 26 importers, each needing an inventory
row or the gate reds the build. That backlog is the work, which is why this is filed
rather than fixed in passing. Stated as a floor: a seat measured 26 by import
statement, a broader pattern here finds 28.

Scoped so it does not absorb the prose correction on the two ASVS cells, which is a
separate authorised act belonging to the tracker.

Found by a builder sizing #1164, routed here by the liaison carrying the owner ruling.

* backlog: file #1327 and #1328 -- the two items this seat owed

Both were deferred to this seat to number and neither can be built quickly, so they
go to the ledger rather than staying in a handoff nobody is required to read.

#1327: settings models carry extra=ignore on 35 of 38, so a deleted setting passes
silently and the test named after it establishes nothing. The row states the hazard
and refuses the population claim behind it. One seat said the suite is full of these;
another swept and found zero beyond the single instance already fixed. The honest
form is that the next deletion will do this silently. The sweeping seat's own blind
spots are recorded beside that zero, because a zero from an instrument that cannot
see the failing shape is worth less than the shape itself.

#1328: nothing maps an ASVS cell to the items it governs, so a re-score cannot be
handed to the seat that must flip the banner. Three re-scores in one day produced
zero flips. The row also records that re-scores are not one-directional: one moved a
cell from pass to partial, so any item closed on that earlier pass must re-open, and
a closed banner resting on a withdrawn pass is a control on a false premise.

Item count asserted +2 and bare-LF asserted zero BEFORE the write, after an earlier
commit today checked afterwards and shipped one bare LF.

* backlog: put the #1227 design on its own row so nobody re-derives it

A builder decided the shape, refused two alternatives with reasons, and wrote it to
a handoff rather than half-building it under a live stop rung. That work is only
useful if the next seat finds it, and a handoff is not where anyone looks first.

The row now carries the pointer, the chosen shape, and the refusal that most needs
recording: the bounded-exception option is out because the item's own premise shows
nothing enforces the arrival path, so documenting it harder does not make it true.

The deciding argument is on the row too, because it is the part a re-reader would
otherwise relitigate. Stash-and-replay would store a message body across a re-auth,
creating PHI at rest in a new place with a new deletion question, to save one click.
The confirm page's own URL is the stash instead.

Structure asserted unchanged and bare-LF asserted zero before the write.

* backlog: mark #1327 superseded by #1326 -- my second duplicate this evening

A builder and I filed the same defect within minutes. They had deferred it to me to
number and then filed it themselves, as the seat whose own commit contained the only
instance. Theirs is the better row.

The lesson is about the tool I have been trusting. Both numbers came from alloc.ps1,
which is doing its job: it guarantees two seats never take the SAME number. It cannot
notice that two numbers describe one defect. Atomic allocation prevents a collision,
not a duplicate, and I have now hit the second failure mode twice in one evening
after citing the first at other people all day.

#1326 carries what this row does not: these models also load from TOML and MEFOR_
env, so extra="forbid" turns an unknown config key into a load refusal. That makes it
a posture change rather than test hygiene, which is a decision rather than a one-line
edit.

Banner not flipped; closing is the lander's act.

* backlog: reverse my #1327 supersede -- we cross-superseded and neither row survived

A builder and I filed one defect as #1326 and #1327. Then each of us marked our own
row superseded by the other's: they withdrew #1326 to a stub pointing here, I wrote a
supersede note pointing there. For a few minutes both rows pointed at each other and
a reader following either went in a circle.

Mutual deference produced the duplicate and then nearly destroyed both records of it.
That is the sharper failure and it is now recorded on the row, because the duplicate
is ordinary and this is not.

Theirs is already committed as a withdrawn stub, so this row is the survivor and the
supersede note is removed. Lifted from #1326 before it went to a stub, at their
request: these models also load from TOML and MEFOR_ env, so extra="forbid" turns an
unknown config key into a load refusal rather than a test failure. That makes it a
posture change and a decision, not a one-line edit.

Structure asserted unchanged, bare-LF asserted zero before the write.

* backlog: #1327 is the stub, #1326 the survivor -- the lander's call, complied with

The lander holds the closure call and made it. I had reversed my own supersede
fifteen minutes earlier and told the fleet the opposite, so this is a compliance, not
a concurrence, and the measurement that changed my mind is on the row.

#1327 never carried the two named blind spots in the population sweep, including that
the direct scan would not have found the bug that prompted it. #1326 does. That makes
theirs the fuller body regardless of which was handed over first, which was the
lander's stated ground.

It also removes a hazard I created. My previous note said THIS ROW SURVIVES A
CROSS-SUPERSEDE, which contains the word supersede in a sentence meaning the
opposite. Three seats had already read this row by grepping for that word. A marker
that reverses its own meaning in context is worse than no marker.

The near-miss stays on the row because it is worth more than the duplicate: two seats
each marked their own row superseded by the other's, two seconds apart, and briefly
left the defect filed nowhere. Mutual deference has no fixed point unless somebody
states a direction instead of yielding one.

* backlog: #1327 lands live -- matching the lander's landing plan, not overruling it

The lander re-armed #524 on the basis that #1327 lands live as its own row, and
decided #1326 is in no PR tonight. They measured 10f8ab5; I had pushed 73eb78d
after it, which stubbed #1327. With their plan and my pushed state combined, #524
would have merged with #1327 a stub pointing at a row that never lands, and the
defect would have been filed nowhere.

That is the zero-row outcome three cycles of this had been trying to avoid, and it
was one armed auto-merge away.

This is compliance with the deciding seat's stated intent rather than a fourth
reversal of my own. They hold the closure call by position, being the only seat that
sees both branches at merge time.

The row now records the whole structure, because it generalises past this item:
mutual deference has no fixed point and neither does mutual assertion. Stating a
direction only converges if exactly one seat states it, and neither seat can tell
whether it is the second.

It also records the near-miss that nearly shipped: the deciding seat read SUPERSEDED
BY in this row's prose and took it for a status. That is presence-equals-meaning, the
exact misreading CLAUDE.md section 11 forbids for banner glyphs, reproduced with a
phrase instead of a pictograph. A status marker must not be a word that also appears
in narration.
wshallwshall added a commit that referenced this pull request Aug 22, 2026
…tranded sentence in #1327 (#528)

* backlog: close #1317 -- the TLS cipher gate now refuses NULL and anonymous suites

Fixed on main by PR 519 and PR 525. Closed here because the LANDER performs the
closing act; a builder may not conclude an item closed.

VERIFIED BY RUNNING THE SHIPPED CODE, which is the item's own stated method and
the bar roles/LANDER.md sets -- a re-read, not a prose match. Against main at
76ee9a7, both functions the item names:

  validate_tls_ciphers    harden_cipher_suites
  ECDHE-RSA-NULL-SHA           REFUSE   REFUSE   does not encrypt
  ADH-AES256-GCM-SHA384        REFUSE   REFUSE   does not authenticate the peer
  ECDHE-RSA-AES256-GCM-SHA384  ACCEPT   ACCEPT   POSITIVE CONTROL

The positive control is the part that makes this a measurement. Without it a
validator that refused everything reads identically to one that discriminates.

The item warned that its own heading invites the wrong fix -- tightening the
prefix match on _is_forward_secret, which is not the bug. The landed fix did
what the item asked instead: it added the missing PAIR of predicates,
_is_encrypting and _is_peer_authenticated, and left _is_forward_secret alone.

WHY THIS ONE AND NOT THE THREE BESIDE IT. Today merged 31 PRs, filed 13 items
and closed none; open went 225 to 238 while the closed count never moved off
94. Seven items were named in merged code-PR titles, but a title mixes "fixes
#N" and "files #N" freely: #486 FILED #1306, and #525 FILED #1319 and #1322
while fixing only #1317. Of the rest, #1166, #1179 and #1187 carry Verdict
research, whose closing act is a vault re-score and cannot complete here.

One genuine candidate, verified, closed. Open 244 to 243, closed 94 to 95,
total unchanged at 338 and no item lost.

* backlog: delete a stranded sentence in #1327 that invites closing the only live row

docs/BACKLOG.md:13466 read, directly beneath the heading:

  > assigns it). *Until it is flipped this is an open duplicate.*

An unmatched paren, and it asserts the row is a duplicate awaiting a flip while
its own heading four lines above now reads THIS IS THE LIVE ROW FOR THIS DEFECT.

IT WAS NOT INTRODUCED BY THE REVERT. Present at 73eb78d, fa98579 and 3f35be8
alike, with a nonsense-token negative control returning zero and "THIS ROW IS A
STUB" as a positive control that goes 1 to 0 across the same span, so the grep
discriminates and the heading really did move. Under the old STUB heading the
sentence was COHERENT. fa98579 flipped the heading and left it. The sentence
never moved; its context moved out from under it.

WHY IT HAD TO GO NOW. #1326 is the same finding and is on no origin remote --
ls-remote returns zero heads against a control branch returning one -- so it
cannot land and #1327 is the only row this defect has. A leftover sentence
reading "until it is flipped this is an open duplicate" is a standing invitation
to whoever next runs a reconcile to close the only live row, which is the
zero-row outcome six seats spent the evening avoiding, reached by a new route.

THE PLACEMENT IS THE WHOLE LESSON. It sat three lines under the row's own rule:
"A STATUS MARKER MUST NOT BE A WORD THAT ALSO APPEARS IN NARRATION." The row
states the rule and then breaks it. A note describing a trap does not protect a
reader already inside it.

Found while verifying the merge of PR 524; no pre-merge edit was legal, since
the ledger gate keys on the allocating worktree. Amended on main instead.
Status unchanged: #1327 stays open, which is the point.

* backlog: correct #1317 from CLOSED to PARTIAL -- it declares part of itself open

I closed this item two commits ago and that was wrong. Correcting it here rather
than force-pushing, so the error and its cause stay in the record.

WHAT I MISSED. #1317 runs 105 lines. I read the first 26, verified the two
functions its HEADING names by running them against main, and closed it. Further
down, the item says of itself:

  The library half is unchanged and still open: ldap3, hvac and ODBC Driver 18
  choose their own suites and no engine object exists to assert on.

An item that declares part of itself open must not be closed. Closing it would
have deleted the only open record of that half, which is the half-a-record defect
7e names.

THE CODE VERIFICATION WAS SOUND AND IS UNCHANGED. Both functions still refuse
both named suites with a positive control accepting the good one; the founding
claim is genuinely false in the code now. That is why this is PARTIAL and not a
revert. The build shipped and the residual is real.

THE LESSON, and it is narrower than "read more". A code re-read is NECESSARY AND
NOT SUFFICIENT. I applied the bar roles/LANDER.md sets -- verify against the code,
not the prose -- and skipped the prose entirely. The item's own scope statement
outranks a green measurement of the part it named first.

SO TODAY STILL CLOSES ZERO. My earlier "first closure of the day" was wrong. The
count is 31 PRs merged, 13 filed, 0 closed, open 225 to 244 -- and this PR leaves
open unchanged at 244 rather than moving it to 243.

Surfaced because builder-1 mailed about PR 458 conflicting under this very item
and quoted a BUILT paragraph I had not read. I went to check their claim and
found my own error instead.
wshallwshall added a commit that referenced this pull request Aug 23, 2026
…d not take

#1321 and #1322 record one root cause: the `serverdb` path alternation at
`ci.yml:1358` does not list `messagefoundry/api/`, so an api-only change
deselects the SQL Server and Postgres legs on the pull request that would
catch it. Two seats filed it within minutes. #1322 landed on `main` first via
PR #525; #1321 was filed on a branch and arrived by merge, so neither the
ledger gate nor either author could see the collision.

#1321's own row already declared itself the duplicate and handed the closure
over, because `BUILDER.md:253` forbids a filer from closing their own row and
`:148` assigns it here. This discharges that hand-over.

Verified before flipping rather than relayed: #1322 carries every point
#1321 does, including the `tests/test_api.py:587` substring-versus-equality
half, and adds a fired-in-practice case (#514 / BACKLOG #1187, `fdd89b49`)
and the nightly-arm severity bound.

THE GLYPH IS A DELIBERATE CHOICE WITH NO PRECEDENT TO FOLLOW. The live ledger
carried no prior close-as-duplicate-of-an-OPEN-item, so the alphabet had to be
read rather than copied. The other two closed banners would each assert
something false: the check would say the gate hole is fixed, and it is not,
it is open at #1322; the no-entry would say an owner declined the work, and
none did. The tombstone says this ROW is void, which is the true statement,
and the banner text says so in words so no reader has to infer it from a
pictograph.

parse_items before 341 items / 246 open, after 341 / 245. The status gate
reports 577 items across both files, each declaring exactly one status.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.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