Skip to content

docs(backlog): PR 879 expires #1194's premise and does not move its verdict (BACKLOG #1194) - #906

Open
wshallwshall wants to merge 2 commits into
mainfrom
worktree-agent-a2a9dd9fc7dacda00
Open

docs(backlog): PR 879 expires #1194's premise and does not move its verdict (BACKLOG #1194)#906
wshallwshall wants to merge 2 commits into
mainfrom
worktree-agent-a2a9dd9fc7dacda00

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

What this is

A research pass on BACKLOG #1194 (an honest pass for ASVS 15.2.5). Docs only: one file, one item's block, 13 added lines. No code, no vault write, no cell re-scored.

The item was already researched twice (2026-08-20 and a measured pass on 2026-09-04). This adds what neither pass could know: pull request 879 proposes to change the default every sentence of the item rests on.

The finding, in one line

An honest pass is still not reachable, and PR 879 does not make it reachable -- but the item's premise sentence expires if 879 lands, and one of its disqualifications reads, flatly, as a prohibition on 879 that was never meant.

What PR 879 turned out to mean

PR 879 (claude/backlog-1278-sandbox-default, BACKLOG #1278) sets [sandbox].mode from "off" to "subprocess". That default is this item's whole premise. State when read: OPEN, MERGEABLE, BEHIND, checked twice on 2026-09-05.

It does not claim the cell, and it is right not to. Its own diff corrects the worked example in docs/ASVS-ASSESSMENT-METHOD.md in place, leaves partial standing on the ground that re-scoring is the tracking seat's act against the vault, and states the surviving reason: the confinement is address-space only, the child still reaches os, subprocess and the host. So the flip closes exactly one of this cell's recorded residuals, default-off, and none of the others.

Two things follow, and both are now in the item.

  1. The disqualification needed a precision correction, not a deletion. #1194's "what would NOT be an honest pass" list opens with "Shipping mode='subprocess' as the default". Its two limbs are "it would move the cell" and "silently break the live path", and 879 satisfies neither -- it declines to move the cell in as many words, and it states the live-lookup sacrifice in the [sandbox] docstring, docs/CONFIGURATION.md, docs/DEPLOYMENT.md, docs/PHI.md and three ADRs. What stays disqualified is the re-score, not the flip. Left unamended, two live records contradict each other on the same line of settings.py and the more recent one wins by recency.
  2. A new fact neither record states. [sandbox].mode is engine-global. There is no per-connection or per-handler override: the section hangs off the settings root at config/settings.py:3915, and pipeline/engine.py:681-687 builds one SandboxPolicy and hands it to the runner for the whole graph. PR 879's docstring reads "A Handler needing either must run mode='off'" -- true, and easy to read as a per-Handler escape. Today that ambiguity costs nothing. After the flip it costs the whole process's isolation, spent by one Handler.

The one thing whoever decides PR 879 should read first

BACKLOG #1278's own record says the per-inbound resource cost "is not measured". It is measured, it is on main, and it is absent from PR 879.

  • Measured: 49.9 to 57.0 MiB unique resident (76.8 to 82.5 MiB RSS) per inbound worker child, on a one-router one-handler graph and therefore a floor -- docs/benchmarks/results/2026-09-04-adr0087-sandbox-dispatch/README.md:80-81 and :115-119, landed on main.
  • The committed target is one engine process carrying 1,500 inbound connections (docs/SYSTEM-REQUIREMENTS.md:251-253). A site deploying at that configuration would need roughly 74 GiB of additional resident memory and 1,500 extra operating-system processes.
  • This does not revive a withdrawn figure. ADR 0179 withdrew a per-worker handle number because its denominator was a caught PID count that varied 2, 3, 8, 50 across consecutive ticks. This instrument divides by nothing -- it reads each worker tree's own resident size. It answers the memory half of the question ADR 0179 leaves open, not the handle half.

This PR states the fact and does not decide 879. A memory bill is a product call on #1278, not an ASVS finding, and nothing here asks for the flip to be held. I did not comment on PR 879: publishing to another seat's PR is outside a Builder's lane, so this is routed through the ledger and the report instead.

Also recorded

This item's closing act has no owner in the current roster. The 2026-09-04 pass ends with a handoff to a Tracker seat. CLAUDE.md section 5 retired the ASVS Tracker along with six others, and the roster it leaves -- Console, Builder, Regulator, Steward, Lander -- assigns the vault scorecard to none of them. Stated so the item stops reading as though a Tracker were about to pick it up. That is a roster question for the owner or the Console, not a Builder's to settle.

Anchor discipline

Every anchor was searched as a string across its whole file, so an empty result could not pass as "the code is gone" -- the control this item's own re-anchoring paragraph established. All resolved at base c57903c2c.

One anchor I wrote was stale before I pushed it: citing docs/BACKLOG.md:15318 forgot that this PR's own 13 inserted lines move it to :15331. Corrected, and rewritten to tell the reader to search the string rather than trust the line -- PR 879 adds 95 lines above it, so it will move again.

Checks

Check Result
scripts/docs/backlog_status_check.py --quiet rc=0
scripts/hooks/ledger_check.py rc=0
parse_items status of #1194, base vs branch identical; item count 433 both sides
Added lines are pure ASCII yes, against a positive control of 638 banner-alphabet occurrences elsewhere in the file, so the scan was not a false zero
pre-commit (all wired hooks) passed
Backlog/ledger tests 124 passed -- test_backlog_citation_check, test_backlog_status_check, test_ledger_check, test_backlog_hygiene_claim_extraction, test_backlog_dependency_census
ASVS / docs-guard / encoding tests 436 passed -- test_asvs_rescore_handoff, test_asvs_tally_lint, test_cp1252_console_safety, test_doc_guards_lane, test_docs_cite_no_refused_config_keys, test_link_resolution

Skipped, and why: the full suite (targeted legs run locally, the rest is CI's job); ruff and mypy, which have no changed Python to read; /simplify, for the same reason -- the diff is 13 lines of Markdown prose with zero code, and it reviews changed code. The tests were run with the primary checkout's interpreter from this worktree root, and the import source was confirmed to be this worktree, not the primary.

Merge order

No dependency on PR 879 either way. Different regions of docs/BACKLOG.md (this at ~11582, 879's at ~14694 and beyond), so they merge cleanly in either order, and the text is written to read correctly whether or not 879 lands.

🤖 Generated with Claude Code

…mise (BACKLOG #1194)

Research pass on ASVS 15.2.5. Pull request 879 (BACKLOG #1278) flips
[sandbox].mode to "subprocess", and that default is what every sentence of
#1194 rests on. A finding written without knowing that is wrong on arrival.

A top banner plus six dated paragraphs:

- PR 879 expires the premise sentence and moves the verdict nowhere. It
  declines to claim the cell in its own text and states the surviving reason.
  The flip closes exactly one of this cell's recorded residuals, default-off,
  and none of the others.
- The "what would NOT be an honest pass" list needed a precision correction.
  Read flatly it disqualifies #1278, a separate product item. It was always
  aimed at the re-score, not at the flip, and it is restated rather than cut.
- A fact neither record states: [sandbox].mode is engine-global
  (config/settings.py:3915, pipeline/engine.py:681-687, no per-connection
  override anywhere), so one Handler needing live enrichment turns isolation
  off for every Router and Handler on every inbound. PR 879's "a Handler
  needing either must run mode=off" reads as a per-Handler escape and is not.
- #1278's own record says the per-inbound resource cost "is not measured".
  It is measured, it is on main, and it is absent from PR 879: 49.9 to 57.0
  MiB unique resident per inbound child, roughly 74 GiB and 1,500 extra
  processes at the committed 1,500-connection target. Distinguished from the
  per-worker handle figure ADR 0179 withdrew, which had an unstable
  denominator; this instrument divides by nothing.
- The handoff paragraph addresses the ASVS Tracker, a seat CLAUDE.md section 5
  retired, so this item's closing act has no owner in the current roster.
- Verdict unchanged: an honest pass is not reachable, and 879 does not make it
  reachable. The unaddressed populations are untouched by the flip.

Docs only: one file, one item's block, no code and no vault write. No cell is
re-scored here; that act belongs to whoever holds the scorecard.

Gates: scripts/docs/backlog_status_check.py --quiet and scripts/hooks/
ledger_check.py both rc=0. parse_items confirms #1194's parsed status and the
432-item count are identical before and after. The added lines are pure ASCII,
against a positive control of 637 banner-alphabet occurrences elsewhere in the
file, so the scan was not a false zero.

Tests run: 124 passed across test_backlog_citation_check, test_backlog_status_
check, test_ledger_check, test_backlog_hygiene_claim_extraction and test_
backlog_dependency_census; 436 passed across test_asvs_rescore_handoff, test_
asvs_tally_lint, test_cp1252_console_safety, test_doc_guards_lane, test_docs_
cite_no_refused_config_keys and test_link_resolution. Skipped: the full suite,
and ruff/mypy, which have no changed Python to read.

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

Copy link
Copy Markdown
Collaborator Author

Held by the Lander, not enqueued. It is CLEAN and I would otherwise have landed it. One paragraph carries a figure its own author retracted after this branch was written.

The paragraph

"...a site deploying at that configuration would need roughly 74 GiB of additional resident memory and 1,500 extra operating-system processes" ... "This instrument divides by nothing"

Both clauses were withdrawn by the seat that produced them, in an adversarial pass on its own claim, and the retraction is posted on PR 879. I verified all three defects myself against the files rather than taking the retraction on trust:

  1. The multiplier is not the committed target. ADR 0052 AC-2 reads "1,500 concurrent connections""inbound" does not appear. Sandbox children exist per traffic-carrying inbound; 1,500 is connections in aggregate.
  2. Linearity was never measured. Every result JSON records worker_tree_uss_mb, worker_tree_rss_mb and worker_tree_processessingular, one tree, in all five files, with no scaling series. "Divides by nothing" is true and beside the point: it multiplies by 1,500, unvalidated.
  3. Resident set is bounded by installed RAM. The tier is 16 GB and the bench box 31.7 GiB, so 74 GiB resident is not a reading any sized host can produce. The right quantity is private commit charge — demand, not residency.

Why this matters more for a ledger row than for a comment

A backlog row is a snapshot that every later screen reads as current. A refuted number landing in docs/BACKLOG.md outlives the conversation that refuted it, and the next reader has no way to know. That is the shape CLAUDE.md section 11 names — a record resting on a false premise.

What is right in here and should survive a re-cut

Most of it, and I would rather say so than have this read as a rejection:

  • The engine-global finding is correct and I verified it independently. [sandbox].mode hangs off the settings root, engine.py builds one SandboxPolicy for the whole graph, and config/models.py defines no per-connection field. One Handler spending the whole process's isolation is real, and the phase-scoped mode you point at is the right shape.
  • The premise-expiry warning is exactly the right instinct — flagging that an open PR would invalidate a row's opening sentence, before it does.
  • The roster observation is worth keeping: the closing act is a vault re-score and the current roster assigns that to nobody, so the item reads as though a Tracker were about to pick it up when none exists.

What it needs: that one paragraph re-cut to the surviving objection, which is stronger and needs no extrapolation — ADR 0052 AC-2 requires 1,500 concurrent connections without per-connection-worker exhaustion (fd/socket/worker-task limits), and mode=subprocess holds one never-pooled worker tree per traffic-carrying inbound, two processes each. That is the resource class AC-2 names, and ADR 0052 records the axis as unvalidated with no harness.

I have not edited this PR. Rewriting another seat's ledger prose is authorship on their subject, not queue mechanics.

…he retracted total (BACKLOG #1194)

Re-cut of the paragraph this branch added on 2026-09-05. It asserted "roughly
74 GiB of additional resident memory" at the 1,500-connection target. That claim
was retracted in public at
#879 (comment)
before anyone acted on it, and the ledger must not keep carrying it.

Three defects, each sufficient, each re-verified by hand here:

- Wrong multiplier. ADR 0052 commits "45,000,000 messages/day, 1,500
  connections, and a remote production database" -- connections, inbound and
  outbound (:43). Sandbox children exist per traffic-carrying INBOUND only:
  the session is created per inbound but the child spawns lazily on first
  dispatch (wiring_runner.py:2632-2636, sandbox.py:799-813).
- Linearity was never measured. All five result files under
  docs/benchmarks/results/2026-09-04-adr0087-sandbox-dispatch/ record
  "worker_tree_processes": 2 and one live worker tree. The instrument's
  docstring (scripts/bench/sandbox_dispatch.py:198-212) supplies an argument
  in place of a second data point, and the artifact's own "What this does not
  establish" says it does not measure the sandbox under concurrent lanes.
- Resident set is bounded by installed RAM, so "74 GiB of resident memory"
  names a reading no sized host can produce. The tier is 4 to 8 cores and
  16 GB (docs/SYSTEM-REQUIREMENTS.md) and the bench box has 31.7 GiB. The
  correct quantity is private commit charge: demand, not residency.

What replaces it needs no extrapolation. ADR 0052 AC-2 (:73-74) reads "THE
SYSTEM SHALL support up to 1,500 concurrent connections without
per-connection-worker exhaustion (fd/socket/worker-task limits)". At
mode=subprocess the engine holds one persistent worker tree per
traffic-carrying inbound; nothing pools it and nothing caps it. Each adds a
process tree (two processes under a Windows venv), two parent daemon threads
and three parent pipe fds (sandbox.py:616-655), plus a Windows job-object
handle (:451). That is the resource class AC-2 names.

Both counterweights are carried rather than buried: _spawn uses
subprocess.Popen and not fork (sandbox.py:616), so there is no copy-on-write
sharing of a parent heap and the bulk of that ~50 MiB does replicate; and every
uncertainty the artifact names points up. A labelled extrapolation is kept --
roughly 73 to 74 GiB of private commit DEMAND if 1,500 traffic-carrying
inbounds ran on one host, never validated at two.

Also dropped: the "this instrument divides by nothing" defence against ADR
0179. The figure 0179 withdrew performed no division either, so that sentence
never cleared the bar it claimed to.

Unchanged on this branch: the throughput finding, the engine-global
SandboxPolicy finding, and the disqualification correction (what stays
disqualified is the RE-SCORE, not the flip).

NOT fixed here, and reported instead: ratified ADR 0087 still ships the same
withdrawn figure on main, under "Resident footprint, measured 2026-09-04" --
"Against the committed 1,500-connection target that is roughly 74 GiB". An ADR
is a ratified decision record and correcting it is a separate change, filed
under its own item. Until that lands, the ledger and the ADR disagree.

Docs only: one file, one item's block, five lines replacing one. No code, no
vault write, no ledger number allocated, no cell re-scored.

Gates: scripts/docs/backlog_status_check.py rc=0 (433 live items, 670 total),
dangling_citation_check.py rc=0, link_check.py rc=0 (5065 relative links).
banner_sha_check.py rc=1 and citation_line_check.py rc=1 are both pre-existing
and neither names this diff: the first reports BACKLOG-CLOSED.md:7034, which
this diff does not touch, and 0 of the second's 167 findings name BACKLOG.md.
parse_items reports #1194 identical before and after -- score 8, open banner,
no closed banner -- and zero of the 433 items differ. The five added lines are
pure ASCII against a positive control of 638 banner-alphabet occurrences
elsewhere in the same file.

Tests: 171 passed across test_backlog_citation_check, test_backlog_status_check,
test_ledger_check, test_backlog_hygiene_claim_extraction,
test_backlog_dependency_census and test_dangling_citation_check; 436 passed
across test_asvs_rescore_handoff, test_asvs_tally_lint,
test_cp1252_console_safety, test_doc_guards_lane,
test_docs_cite_no_refused_config_keys and test_link_resolution. Interpreter: the
primary checkout's .venv, run from this worktree root, verified importing this
worktree's messagefoundry. Skipped: the full suite, and ruff and mypy, which
have no changed Python to read.

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

Copy link
Copy Markdown
Collaborator Author

Re-cut in place, one commit on the same branch: 49dcf2014. No new pull request was opened.

This branch's 2026-09-05 paragraph asserted "roughly 74 GiB of additional resident memory" at the
1,500-connection target. That claim was
retracted in public
before anyone acted on it, so it is out of the ledger. Five lines now stand where that one did.

What replaced it needs no extrapolation. ADR 0052 AC-2, verbatim: "THE SYSTEM SHALL support up
to 1,500 concurrent connections without per-connection-worker exhaustion (fd/socket/worker-task
limits)." At mode=subprocess the engine holds one persistent worker tree per traffic-carrying
inbound. Nothing pools it and nothing caps it: created lazily on first dispatch
(wiring_runner.py:2632-2636, sandbox.py:799-813), never evicted, released only at runner stop or
config reload. Each one adds a process tree (two OS processes under a Windows venv), two parent
daemon threads and three parent pipe fds (sandbox.py:616-655), plus a Windows job-object handle
(sandbox.py:451). That is precisely the resource class AC-2 names.

The three defects behind the retraction, each re-verified by hand in this worktree. ADR 0052
commits "1,500 connections" (:43), inbound and outbound, while sandbox children exist per
traffic-carrying inbound only, so the multiplier is smaller by whatever the outbound share is.
Linearity was never measured: all five result files under
docs/benchmarks/results/2026-09-04-adr0087-sandbox-dispatch/ record "worker_tree_processes": 2
and exactly one live worker tree, the instrument's own docstring
(scripts/bench/sandbox_dispatch.py:198-212) supplies an argument in place of a second data point,
and the artifact's "What this does not establish" says it does not measure the sandbox under
concurrent lanes. And resident set is bounded by installed RAM, so "74 GiB of resident memory" names
a reading no sized host can produce -- the tier is 4 to 8 cores and 16 GB
(docs/SYSTEM-REQUIREMENTS.md) and the bench box has 31.7 GiB. The correct quantity is private
commit charge: demand, not residency. A labelled extrapolation is kept in the text: roughly 73 to 74
GiB of private commit demand if 1,500 traffic-carrying inbounds ran on one host, never validated
at two.

Both counterweights are carried in the ledger text rather than buried. _spawn uses
subprocess.Popen and not fork (sandbox.py:616), so there is no copy-on-write sharing of a
parent heap and the bulk of that ~50 MiB is genuinely private and does replicate; and every
uncertainty the artifact names points up (50 MiB is a stated floor, case E measured 57.0 MiB, RSS is
about 27 MiB higher). Also dropped: the "this instrument divides by nothing" defence against ADR
0179, which never cleared the bar it claimed to -- the figure 0179 withdrew performed no division
either.

Unchanged, because all three were verified and stand: the throughput finding (ADR 0087 compares
a per-dispatch cost against a per-message bound when a message pays it twice on one serialized
worker; corrected, a 61 to 66 msg/s per-lane ceiling), the engine-global SandboxPolicy finding
(one policy for the whole graph, no per-connection or per-handler override, so PR 879's mode='off'
line is not a per-Handler escape hatch), and the disqualification correction (what stays
disqualified is the RE-SCORE, not the flip).

Confirmed and NOT fixed here, so a reader knows the two records disagree. Ratified ADR 0087
still carries the same withdrawn figure on main:
docs/adr/0087-sandbox-subprocess-isolation.md:334 and :338, under "Resident footprint, measured
2026-09-04" -- "Against the committed 1,500-connection target that is roughly 74 GiB". Editing a
ratified decision record is a separate change and is not in this diff. BACKLOG #1458 (PR 926, open,
not yet on main) files the underlying AC-2 contradiction and names both records, and PR 932 is
open to retract the ADR 0087 sentence in place. Until one of those lands, the ledger and ADR 0087
disagree on this figure.

Scope and verification. Docs only, one file, one item's block, five lines replacing one. No code,
no vault write, no ledger number allocated, no cell re-scored. docs/BACKLOG.md is the entire diff;
BACKLOG #1278's block is untouched, which PR 879 is rewriting.

Gates: backlog_status_check.py rc=0 (433 live items, 670 total), dangling_citation_check.py
rc=0, link_check.py rc=0 over 5065 relative links. All eleven wired pre-commit hooks passed at
commit time. Two repo-wide reds are pre-existing and neither names this diff: banner_sha_check.py
rc=1 reports BACKLOG-CLOSED.md:7034, a file this diff does not touch, and 0 of
citation_line_check.py's 167 findings name BACKLOG.md (that script runs in no workflow).
parse_items reports #1194 identical before and after -- score 8, open banner, no closed banner --
and zero of the 433 items differ. The five added lines are pure ASCII against a positive control of
638 banner-alphabet occurrences elsewhere in the same file.

Tests: 171 passed across test_backlog_citation_check, test_backlog_status_check,
test_ledger_check, test_backlog_hygiene_claim_extraction, test_backlog_dependency_census and
test_dangling_citation_check; 436 passed across test_asvs_rescore_handoff, test_asvs_tally_lint,
test_cp1252_console_safety, test_doc_guards_lane, test_docs_cite_no_refused_config_keys and
test_link_resolution. Interpreter: the primary checkout's .venv run from this worktree root,
verified importing this worktree's messagefoundry. Skipped: the full suite, and ruff and mypy,
which have no changed Python to read. Legs that only run on a hosted runner have not been read here.

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