Skip to content

docs(backlog): record that the 2026-08-16 ruling settles ASVS 2.4.1 (BACKLOG #1114) - #862

Merged
wshallwshall merged 1 commit into
mainfrom
worktree-agent-a27f3dc1d321be8c3
Sep 5, 2026
Merged

wshallwshall merged 1 commit into
mainfrom
worktree-agent-a27f3dc1d321be8c3

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

What this changes

One row in docs/BACKLOG.md (item #1114). No engine code, and no document
outside that row. The ASVS scorecard lives in the vault clone and is untouched.

BACKLOG #1114 asks whether ASVS 2.4.1 can reach an honest pass. Its original
claim was falsified once already, on 2026-08-12. This pass found and confirmed a
second staleness, then recorded the answer the item's own hardened question
already had.

The second staleness, measured with a control on every reading

The pacing keys reach four of the nine externally-facing inbound factories.
MLLP, Tcp, X12 and Http accept max_messages_per_second and
message_burst and carry both into the connection spec's settings. DICOM,
File, Sftp, Ftp and DatabasePoll construct normally WITHOUT the keys and
then raise TypeError on each, so the rejection is a fact about the key and not
about a bad signature. Every row ran that no-key control leg first; three rows
failed it on the first run, reported themselves uninterpretable rather than
absent, and were re-run against the real signatures.

By symbol, _MessagePacer and _pacing_settings appear in transports/mllp.py,
tcp.py, x12.py and http_listener.py and in no other transport module. Two
positive controls fire against that zero: source_ip_allowlist returns 12 in
dicom.py, the very file the pacer scan calls zero, and register_source
returns 2 in every inbound transport module.

So the re-score banner's clause "for non-MLLP inbounds there is no opt-in bound
at all, so no workaround exists there" is FALSE at HEAD. It is struck in place,
not deleted. Its difficulty clause is marked spent for two reasons: the raw-TCP
limb it priced was delivered on 2026-09-03, and the ruling it priced was written
on 2026-08-16, before the score was set.

The numbers are left as the scoring pass recorded them. Re-scoring is that
pass's act, not a builder's, and changing them here would desync the quadrant
census elsewhere in the file.

What did not change is the half that decides the cell.
DEFAULT_MAX_MESSAGES_PER_SECOND is still None, each of the four factories
still defaults both keys to None, and max_messages_per_second appears nowhere
in messagefoundry/ outside those four transport modules and config/wiring.py
-- run with the exclusion inverted as its own control, returning 4 and 11 in the
two files that do carry it. Nothing reads the key at startup and no gate refuses
to start without it. A default install WOULD still take messages at an unbounded
rate on first deployment, on every intake.

The 2026-08-16 ruling settles the cell on the shipped default

Walking docs/ASVS-ASSESSMENT-METHOD.md section 1 in order: rule 3 takes the
cell off fail because the control exists reachable by some configuration; rule
4 denies pass because the control ships off and no gate refuses to start; and
section 1.1a records the owner ruling that rule 4 is strict, that an
off-by-default control can NEVER be graded pass, and that the vacuity argument
is pre-rejected by name. Rule 5 then selects partial on two independent limbs
at once -- ships off, AND covers part of the surface.

That is why the 2026-09-03 port did not move the verdict and why finishing the
port would not move it either: widening the surface answers rule 5's third limb
while rule 4 goes on asking about the default. transports/mllp.py reached the
same conclusion first, in the comment above DEFAULT_MAX_MESSAGES_PER_SECOND.

The row now tells a builder not to build IN ORDER TO MOVE THIS CELL. Other
subjects on its proposed-work list stay buildable on their own merits, but none
of them changes the verdict, so none of them closes the item. The two changes
that could carry the cell to pass each need an owner ruling first: flipping the
default, which the 2026-08-11 ruling set OFF as a deliberate deviation; and the
refuse-to-start gate of rule 4's second limb, a new gate class.

That distinction is deliberate: an earlier draft of this row said "nothing on
this item's own build list moves it", which was false -- the refuse-to-start gate
IS on that list and would move it. Caught and corrected before commit.

Citation re-pointed by content

docs/SECURITY.md:1582 now lands on account-lockout prose about an assertion
leg, so that anchor is dead. The live sentence is the "Ingest plane" row of the
table under the "Business-logic limits (ASVS 2.1.3)" heading. The row cites it
that way rather than by line, because this anchor has now drifted twice.

One residual is recorded and deliberately left unedited: that same SECURITY.md
row concludes "the code-first and the TOML surface both express them" across all
four named factories, and for X12 the second half is false -- _TRANSPORTS in
config/connections_file.py carries no x12 key. The gap is already pinned with
its own positive control in tests/test_ingress_message_pacing.py. A security
sentence deserves a change a reviewer can see on its own.

One contradiction inside the row, narrowed

The Severity line said "MLLP has neither intake authentication nor rate
limiting". Intake authentication holds in no configuration, but an opt-in pacer
has existed on MLLP since 2026-08-11, so the flat claim contradicted this item's
own body -- the same defect the 2026-08-13 amendment was raised to fix. Narrowed
in place, struck rather than deleted. The asymmetry the line exists to carry is
unchanged and now sharper: no intake authentication at all, and no rate bound on
the shipped default, so there is still no actor to charge a budget against.

Checks run

  • ruff check . -- All checks passed
  • ruff format --check . -- 1241 files already formatted
  • mypy messagefoundry (strict) -- Success: no issues found in 267 source files
  • scripts/docs/backlog_status_check.py -- OK, 664 backlog items, each declaring
    exactly one status
  • pytest over the ledger and doc-guard files that read docs/BACKLOG.md
    (test_backlog_status_check, test_backlog_citation_check, test_ledger_check,
    test_asvs_tally_lint, test_link_resolution, test_cp1252_console_safety,
    test_doc_guards_lane) -- see the comment below for the summary line of the run
    against the final text.

Not run: the full suite. The diff is one Markdown row and the box was under
heavy concurrent load -- seven test files took 7m26s. The legs above are the ones
that read this file. /simplify had no target: no code changed.

Please read the hosted legs after merge; a builder's process exits before they
report.

What I deliberately did not do

  • Did not flip the pacer default. It is a recorded deliberate deviation with a
    ruling behind it, and inverting it silently is the move this item most needs
    nobody to make.
  • Did not claim the cell passes because a control exists. The verb asks about the
    shipped configuration.
  • Did not touch the ASVS scorecard, which lives in the vault clone.
  • Did not re-score the item's own value or difficulty numbers.

🤖 Generated with Claude Code

…BACKLOG #1114)

The #1114 re-score banner rested on two premises that no longer hold at a2eef0f,
and the row now says so rather than carrying them forward.

Measured at HEAD, with a control on every reading:

- The pacing keys reach four of the nine externally-facing inbound factories.
  MLLP, Tcp, X12 and Http accept max_messages_per_second and message_burst and
  carry both into the connection spec; DICOM, File, Sftp, Ftp and DatabasePoll
  construct without the keys and then raise TypeError on each. Every row ran that
  no-key control leg first, so a rejection is a fact about the key and not about a
  bad signature.
- By symbol, _MessagePacer and _pacing_settings appear only in mllp.py, tcp.py,
  x12.py and http_listener.py. Two positive controls fire against that zero:
  source_ip_allowlist returns 12 in dicom.py, the very file the pacer scan calls
  zero, and register_source returns 2 in every inbound transport module.
- DEFAULT_MAX_MESSAGES_PER_SECOND is still None and nothing reads the key at
  startup, so a default install WOULD still take messages at an unbounded rate on
  first deployment.

So the banner's "for non-MLLP inbounds there is no opt-in bound at all" is struck
as falsified by the 2026-09-03 port, and its difficulty clause is marked spent:
the raw-TCP limb shipped, and the ruling it priced was written on 2026-08-16,
before the score was set. The numbers are left as the scoring pass recorded them,
because re-scoring is that pass's act and not a builder's.

The cell's verdict is settled by standing rule rather than by a build. Rule 3
takes it off fail; rule 4 denies pass because the control ships off and no gate
refuses to start; section 1.1a pre-rejects the vacuity argument by name. Rule 5
then selects partial on two independent limbs, so finishing the port would not
move it either. The row now tells a builder who reaches it not to build.

Also re-points the dead docs/SECURITY.md:1582 citation by content, to the "Ingest
plane" row under "Business-logic limits (ASVS 2.1.3)", and records one residual:
that row's "both surfaces express them" clause overstates the TOML surface for
X12. Left for its own diff.

And narrows the Severity line's "MLLP has neither intake authentication nor rate
limiting". Intake authentication holds in no configuration, but an opt-in pacer
has existed on MLLP since 2026-08-11, so the flat claim contradicted this item's
own body -- the same defect the 2026-08-13 amendment was raised to fix. The
asymmetry the line exists to carry is unchanged and now sharper: no intake auth
at all, no rate bound on the shipped default.

No code changed. The ASVS scorecard is untouched; it lives in the vault clone.

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

Copy link
Copy Markdown
Collaborator Author

Local verification against the committed tree (0832d5883), as the description promised.

pytest, its own summary line:

192 passed in 423.95s (0:07:03)

Files: test_backlog_status_check, test_backlog_citation_check, test_ledger_check,
test_asvs_tally_lint, test_link_resolution, test_cp1252_console_safety,
test_doc_guards_lane. Zero failures, zero errors. An earlier run of the same seven files
against the pre-correction text also read 192 passed; this one is the run against the text
that is actually committed here.

scripts/docs/backlog_status_check.py on the same tree: OK - 664 backlog items, each declaring exactly one status. The pre-commit hooks that read this file passed at commit time:
ledger gate, backlog ledger parses, forbidden-content, control characters, gitleaks.

One further control, run after the push, on the claim this row turns on.

The row says the pacer reaches four intakes and no others. A symbol grep can be defeated by an
aliased import, so I checked every import from mllp across transports/:

$ grep -n "from messagefoundry.transports.mllp import\|from .mllp import" messagefoundry/transports/*.py
database.py:72      from messagefoundry.transports.mllp import InsecureHopGuard
dicom.py:92         from messagefoundry.transports.mllp import InsecureHopGuard
email.py:69         from messagefoundry.transports.mllp import InsecureHopGuard
remotefile.py:98    from messagefoundry.transports.mllp import InsecureHopGuard
http_listener.py:57 from messagefoundry.transports.mllp import (
tcp.py:46           from messagefoundry.transports.mllp import (
x12.py:48           from messagefoundry.transports.mllp import (

The four uncovered inbound modules that import from mllp at all take InsecureHopGuard and
nothing else; file.py imports nothing from it. So no alias hides a pacer in the five intakes
the row calls uncovered, and the same grep proves it can reach those files -- a zero there is a
fact about the module, not about the query.

Not run: the full suite. The diff is one Markdown row, and the box was carrying concurrent
work from other sessions -- seven test files took just over seven minutes. ruff check,
ruff format --check and mypy messagefoundry (strict, 267 files) were all green before the
commit. The hosted legs still need reading by whoever picks this up; a builder's process exits
before they report.

@wshallwshall wshallwshall added the reviewed A reviewer has read this. Removed automatically when new commits arrive. label Sep 4, 2026
@wshallwshall

Copy link
Copy Markdown
Collaborator Author

The reviewed label is now applied, by me.

Disclosure per the Manager playbook section 6. I am the seat that commissioned this diff, so the label records my read of work I asked for, not an independent one. The builder deliberately held off labelling because its brief's precondition was unmet -- the review-gate run was queued rather than completed -- and because a stand-down I sent it said not to. That stand-down was my error and is retracted; the label is mine to apply and to disclose. The reviewer role and its tag requirement were disabled by owner ruling today.

What the label stands on. The pacing keys reach four of the nine externally-facing inbound factories -- MLLP, TCP, X12, HTTP -- and are rejected by DICOM, File, SFTP, FTP and DatabasePoll. Every probe row constructed the factory without the keys first as a control; three rows failed that control on the first attempt and reported themselves uninterpretable rather than as absent, and were re-run against the real signatures. A separate alias check confirmed the uncovered modules import only InsecureHopGuard from mllp, so a symbol grep defeated by aliasing was ruled out rather than assumed.

DEFAULT_MAX_MESSAGES_PER_SECOND is still None and the key appears nowhere outside those four modules and config/wiring.py, so no gate reads it at startup. That is what makes the 2026-08-16 ruling settle the cell at partial rather than pass.

pytest, quoting its own summary line: 192 passed in 423.95s. The full suite was not run; the diff is one Markdown row and the box was saturated. That is stated as a gap, not a pass.

A correction the builder made in its own draft, which I want on the record because it cuts against the finding. It first wrote that nothing on this item's build list moves the cell. That is false -- the refuse-to-start gate is on that list and would move it. It corrected to "no further widening of the pacer's surface moves it" before committing. The distinction matters: this row records that a build exists which would change the verdict, and it is not being claimed as unreachable.

It also re-pointed the dead docs/SECURITY.md citation by content rather than by line, and narrowed a Severity sentence that contradicted the item's own body.

@wshallwshall
wshallwshall added this pull request to the merge queue Sep 5, 2026
Merged via the queue into main with commit 8440da9 Sep 5, 2026
42 of 43 checks passed
@wshallwshall
wshallwshall deleted the worktree-agent-a27f3dc1d321be8c3 branch September 5, 2026 08:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

reviewed A reviewer has read this. Removed automatically when new commits arrive.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant