Repository navigation
Open a 0003-era database with the CareerSignal that exists now - #40
Merged
Merged
Conversation
Seven migrations, and every test of them starts from nothing: create a fresh database, apply all seven, assert the result. That proves the SQL executes. It says nothing about the thing an operator depends on, which is that a database holding months of their decisions still means what it meant after the upgrade runs over it. So this builds a genuine 0003-era database -- the real historical migration resources, applied by the real runner with the file list narrowed, carrying rows written the way that schema allowed -- and lets the current code open it. No fixture .db is committed: a checked-in binary is one nobody reviews, and it would drift from the migrations it claims to predate. What is asserted is semantic rather than structural: old facts survive an opportunity recorded then reads now 0004 backfills honestly the row says a migration wrote it 0005 invents no authority an old approval authorizes no draft 0006 invents no target no addressing appears for one 0007 keeps one-attempt an old intent still bars a second The three "invents nothing" claims are why this exists. 0005, 0006 and 0007 each added columns binding an approval to what the operator read, and each deliberately left old rows unmatched, because the database has no record of what those approvals were for. A migration that backfilled a plausible value would authorize an outward write nobody approved -- and every structural test here would still pass. Mutations, each alone: 0004 backfills actor='operator' FAIL 0007 defaults provider to 'controlled' FAIL the fixture builds a CURRENT database FAIL (7 errors) the reviews share one opportunity again FAIL The third and fourth are vacuity guards, and the fourth is there because I wrote the defect it catches. The first version of this fixture pointed one opportunity's current_review at one of three reviews, so the other two were stale -- and an authorization check reads its binding through current_review, refusing a stale review before any 0005/0006/0007 column is consulted. All seven tests passed. Both outward tests were green about staleness while claiming to be about migrations. It surfaced by testing the test: I fabricated a complete, correct binding for the old approval, as a dangerous migration would, and asked whether draft() then created one. It did not -- it raised "Review is missing or stale", which is the wrong refusal and meant the test could not have caught a fabricating migration at all. With each review current for its own opportunity, the same experiment creates a real draft: provider.calls = 1, intent confirmed. That is what the semantic test asserts against, and only now does asserting it mean anything. The staleness precondition is now asserted in the vacuity test rather than left to hold by luck. Gates: 1679 -> 1686 tests, ruff clean, verify_tree 100/5/1, verify_secrets and verify_wheel PASS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B8kjhqYENShpeZ16QDnsyo
Review is right, and the old assertions proved a weaker property than the
PR's own claim. `pytest.raises(Exception)` passes for the intended refusal
-- Repository.claim() raising ValueError("Approval predates draft
authorization binding...") or workflow.py raising ValueError("Requested
provider does not match the draft intent already reserved...") -- but it
would also pass, silently, if a future unrelated failure in _binding(),
draft_material(), provider identity resolution, or SQLite access threw
first. provider.calls == 0 and intent is None would still hold, and the
regression test would stay green while proving nothing about the
historical-upgrade contract it exists to guard -- the exact class of
defect this file was written to catch.
Both assertions now name the exact exception and match its message, so a
different failure earlier in draft() fails the test instead of passing it
for the wrong reason.
Gates: 1686 tests (both narrowed assertions still pass against the real
refusal), ruff clean, verify_tree 100/5/1, verify_secrets and verify_wheel
PASS.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KR2F1vgfq8mygoDtcSaSyp
The Windows 3.12 cell failed on test_an_outward_command_refuses_an_unknown _query_parameter with WinError 10053, which is the unread-body reset race #39 was written to remove: `_outward` settles accepted() on the query before it reaches for _body(), so a declared body goes unread, and closing on unread bytes is a reset rather than a clean shutdown on Windows. Whether the 400 or the reset reaches the client first is then a coin toss. #39 fixed five call sites and claimed in its own docstring to have removed "the last instance ... from this file". That claim was wrong when I wrote it. This call site arrived in #35 and I missed it; the sweep behind #39 was not a sweep. An AST pass over all 112 client call sites in the file now says this was the only one left: every other send carrying a body either declares transmit=False already or is an address that genuinely reads -- malformed JSON, the charset 200, and the provenance-in-order half of the ordering test -- and the 39 calls through outward()/decide() never override provenance, so they always reach the read. The whole-file count in that docstring is replaced by the mechanism, which cannot rot the same way: the file's rule is that an address refusing before reading declares its body and sends none, written down on Client.send. Withholding is the stronger fix rather than tolerating the race with unread(), and it turns out to strengthen the test itself: drop accepted() from _outward FAIL (TimeoutError) check the query AFTER _body() FAIL (TimeoutError) the same reorder, body transmitted PASS <- what the old form allowed The third line is the point. With the body sent, reading first and judging the query second still answers 400, so the old assertion held while the ordering it is named for had been inverted. Withheld, the refusal can only arrive if the query was judged first, so the test now proves its own title instead of a status code. Gates on this commit: 1686 tests, ruff check and format clean, verify_tree 100 publishable text files / 5 reviewed renders / 1 reviewed screenshot, verify_secrets and verify_wheel PASS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B8kjhqYENShpeZ16QDnsyo
Deep-Sixed
marked this pull request as ready for review
September 23, 2026 00:34
Deep-Sixed
added a commit
that referenced
this pull request
Sep 25, 2026
Brings in #40 (the 0003-era upgrade regression). Its change to test_web_surface.py withholds the body on the one refuse-before-read call site that still sent it; the new leading-empty-component test added here already does the same, for the same reason. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ViK1h7GSCZtShZqeknnVJ
Deep-Sixed
added a commit
that referenced
this pull request
Sep 26, 2026
Two findings from the review of #36-#40, both in the intake code #36 added. A refusal decided on the envelope -- 413, 415, the namespace 400 -- is answered before a byte of the body is read, which is right, and then the socket was closed on whatever the client had sent, which is not. Closing on unread bytes is a reset, and a reset can overtake the response; on Windows it discards a response the client has not read yet. The page always sends the file it was given, so an operator choosing a 3 MB message could see "Failed to fetch" instead of the ceiling that refused it. The surface now settles every response the same way: write the answer, shut its own send side, then read and discard whatever the client is still sending, bounded at 16 MiB and five seconds, and only then close. Nothing about what is decided, or when, changes -- the answer is written before the drain begins and the drained bytes are never parsed, stored or kept. The socket is reached as `self.request`, socketserver's own name for it, because this package is mechanically forbidden the word `connection` and that guard is right to be. The suite could not see this. Every pre-read refusal was asserted with transmit=False, which proves the surface decides before reading and cannot prove what a browser sees, because a browser always sends the body. That was my choice in #39 and #40, and I did not say what it left untested. These tests send it: unfixed surface, deterministic orderly-close test FAIL 20/20 runs unfixed surface, over-ceiling full-body test FAIL 10/10, then 1 pass in 3 drain with no time bound FAIL (thread held) drain without shutting the send side first FAIL (no early end-of-stream) The small-body 415 and 400 full-body cases never fail on Linux; they are there for Windows, where the reset destroys the response rather than trailing it. `gmail-ingest --limit 0` reported "the mailbox could not be read". The service checked label_ids before its try and let limit fall inside it, where every failure becomes SourceUnreadable -- and only after the identity read had spent the credential. The limit is now checked where label_ids is, against the reader's own rule, and the CLI refuses a non-positive --limit before Gmail or the database is touched. service limit check removed FAIL (4 cases) CLI limit check removed FAIL Finding #1 of the review, a malformed read token stopping serve, is not in this change: whether that launch should refuse or start without Gmail is a decision still open. Gates: 1686 -> 1696 tests, ruff check and format clean, verify_tree 100/5/1, verify_secrets and verify_wheel PASS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B8kjhqYENShpeZ16QDnsyo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Item 2 of the career-ops queue: a semantic upgrade regression, not "the SQL executed."
Head:
d5676b1Base:
main @ a50e83cThree commits, two files.
ccf5c9asrc/system/tests/test_historical_upgrade.py, +326a107781pytest.raises(Exception)to the exact exception and messaged5676b1No production code changes in any of the three.
Why
Every existing migration test starts from nothing — fresh database, apply all seven, assert the result. What none of them covers is a database that genuinely predates the later migrations being opened by current code.
It lives in
src/system/tests/rather thansrc/data/tests/because the semantic assertions run the real outward workflow, andverify_tree.pyallowsdata → recruitingonly.How the old database is built
monkeypatch.setattr(store, "migration_files", ...)narrows the list to<= 0003, then the real runner applies them. The old database therefore carries the sameschema_migrationsdigests a real operator's would, and the laterstore.migrate()treats it as an ordinary upgrade rather than a special case.No fixture
.dbis committed — a checked-in binary is one nobody reviews, and it would drift from the migrations it claims to predate.What is asserted
actor='migration', not an operatorThe three "invents nothing" claims are the point. 0005–0007 each added columns binding an approval to what the operator read, and each deliberately left old rows unmatched because the database has no record of what those approvals were for. A migration that backfilled a plausible value would authorize an outward write nobody approved — and every structural test in this repo would still pass.
Refusals are asserted on
provider.calls == 0, following the repo's rule that a refusal and a rejection read alike to a person and only the call count says whether anything was contacted.I wrote the defect this catches
The first version of the fixture pointed one opportunity's
current_reviewat one of three reviews, leaving the other two stale. An authorization check reads its binding throughcurrent_review, so a stale review refuses before any 0005/0006/0007 column is consulted. All seven tests passed — and both outward tests were green about staleness while claiming to be about migrations.It surfaced by testing the test: I fabricated a complete, correct binding for the old approval, as a dangerous migration would, and asked whether
draft()then created one.The semantic test asserts
calls == 0andintent is None. Only after the fix does asserting that mean anything. The staleness precondition is now asserted in the vacuity test rather than left to hold by luck.a107781— the same vacuity class, one level upReview was right that
pytest.raises(Exception)proved a weaker property than this file's own claim. It passes for the intended refusal, but would also pass silently if an unrelated failure in_binding(),draft_material(), provider identity resolution or SQLite access threw first —provider.calls == 0andintent is Nonewould still hold, and the test would stay green while proving nothing. Both assertions now name the exception and match its message.d5676b1— the Windows failure, and a claim of mine that was wrongWindows 3.12 failed on
test_an_outward_command_refuses_an_unknown_query_parameterwithWinError 10053. That is the unread-body reset race #39 was written to remove:_outwardsettlesaccepted()on the query before it reaches for_body(), so a declared body goes unread, and closing on unread bytes is a reset rather than a clean shutdown on Windows.#39's docstring claimed it had removed "the last instance ... from this file". That claim was wrong when I wrote it. This call site arrived in #35 and I missed it — the sweep behind #39 was not a sweep. An AST pass over all 112 client call sites now says this was the only one remaining: every other
sendcarrying a body either already declarestransmit=False, or is an address that genuinely reads (malformed JSON, the charset 200, the provenance-in-order half of the ordering test), and the 39 calls throughoutward()/decide()never override provenance. The whole-file count in that docstring is replaced by the mechanism, which cannot rot the same way.Withholding is the stronger fix than tolerating the race with
unread()— and it strengthens the test:accepted()from_outwardTimeoutError_body()TimeoutErrorThe third row is the point. With the body sent, reading first and judging the query second still answers 400, so the old assertion held while the ordering the test is named for had been inverted. Withheld, the refusal can only arrive if the query was judged first — the test now proves its own title rather than a status code.
Mutation coverage —
ccf5c9a0004backfillsactor='operator'0007defaults provider to'controlled'The last two are vacuity guards. Note the honest limit: mutation 2 is caught by the structural assertion, not the semantic one —
0005's empty digests refuse independently, so the end-to-end test cannot attribute a refusal to any single column. The per-column claim is the structural test's job; the semantic test is the end-to-end backstop, and the fabricated-binding experiment above is what proves it is not vacuous.Verification on
d5676b11686 tests (1679 → 1686), Ruff check and format clean,
verify_tree100 publishable text files / 5 reviewed renders / 1 reviewed screenshot,verify_secretsPASS,verify_wheelPASS.🤖 Generated with Claude Code
https://claude.ai/code/session_01B8kjhqYENShpeZ16QDnsyo
Generated by Claude Code