C2 seam extraction — instance-free request/auth path for the live captcha wiring - #71
Merged
Merged
Conversation
… path
C1 decided what things mean; C2 is making the transport structurally capable
of carrying that decision, not making any new decisions. This commit is the
structural half only — no classification logic, no C1 semantics.
The live captcha wiring must build `submit_captcha_fn` BEFORE
MoltbookHTTPTransport exists, because the Note E fail-closed presence check
takes it as a constructor argument. It therefore cannot reach a bound method,
and `_real_request` was trapped behind an instance it does not yet have. The
factory shape was never the problem; the trapped default was.
Extraction only:
- `real_request(base_url, method, path, body, headers)` at module level,
containing `_real_request`'s prior body with ONLY the Note D comment
relocated into the docstring. No logic, HTTP semantics, redirect posture,
error handling, or timeout change.
- Deliberately no leading underscore. This module uses underscore at module
level to mean "internal, do not import" (`_collapse_letter_runs`,
`_normalize_captcha_prompt`, `_word_number_value`); one here would signal
the opposite of why it was extracted.
- `real_request` is NOT itself a `request_fn` — the seam signature is
(method, path, json_body, headers); callers partially apply the base_url.
- `auth_headers(api_key)` at module level, transport-scoped.
- `_real_request()` / `_auth_headers()` reduced to one-line delegations,
each carrying a guard comment stating that instance-specific behavior does
not belong in the wrapper.
One HTTP path, not two: the write path and the live verify path now build on
the same function, so retry/redirect/timeout posture cannot diverge between
them (C1 §5's concern).
Tests: 613 passed, 7 xfailed. Two added. The existing two `_real_request`
tests are deliberately untouched — they now cover the delegation, which is
the regression evidence that extraction changed no behaviour. The new
module-level test pins the one property they cannot: callable with no
transport instance in existence. Those two would still pass if an instance
dependency were reintroduced, because they hold an instance; this one would
not, which makes the "pure delegation" guard mechanical rather than a comment
asking the next editor to remember.
DELIBERATE DEFERRALS — recorded as policy, not omissions:
1. Redirect posture. `_real_request`/`request_fn` follow redirects via
urllib's default opener. Verified on CPython 3.12.10: on POST it redirects
for 301/302/303 only and rebuilds the Request WITHOUT `data`, so the
captcha answer body is never resubmitted and C1 §5 is not violated; 307/308
raise HTTPError and fall to C1 §4 residual, which is correct. The open
concern is classification fidelity — a followed redirect means the status
reaching the classifier may not be `/verify`'s own (a 303 landing on 404
would classify as C1-4 CONFIRMED_FAILURE, a C1 §9 stop condition, from a
response `/verify` never gave). Also note `docs/moltbook_api_spec.md` §1:
a bare-domain request triggers a redirect that STRIPS the Authorization
header, so redirect behaviour here carries a documented credential-safety
dimension too. PARKED, unresolved, not touched in this commit.
2. `moltbook/client.py:144` `_auth_header()` duplicates
`f"Bearer {api_key}"`, and its comment calls itself "the ONLY place the
key is touched", which `transport.py` has made inaccurate. The DRY helper
here is deliberately transport-scoped and does NOT consolidate it; fixing
it would reach outside C2 and ripple into
tests/test_moltbook_credential_integrity.py:196. PARKED alongside (1).
3. Test-count sync. CLAUDE.md, TODO.md and README.md all still read 611.
613 is an intermediate count inside active C2 work, not a completed state
the docs failed to follow — unlike PR #69's situation. Syncing now would
only be resynced when the classification tests land. DEFERRED by decision
to the C2-complete commit, to be synced once.
Structurally scoped throughout: no C1 decision introduced, no
`submit_captcha_fn` policy beyond making the live path authenticable, no
`TransportResult.rate_limit` change, no Note E constructor invariant touched,
no write-path behaviour changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The redirect finding and the client.py auth duplication were tracked nowhere — they existed only in the review conversation and in 203c016's commit body. That is the state where a later reviewer has to reconstruct a deferral by noticing a mismatch, which is what PR #69 had to do for the test counts. Adds a new "M7 — §C / C2 parked items" subsection. Deliberately NOT filed under "post-GO engineering debt (NOT prerequisites for GO-1)": GO-1 is already granted, so that heading's qualifier is spent, and the redirect item may bear on GO-2. Classifying it as post-GO would assert a readiness decision that belongs to the operator and to docs/m7_operator_go_checklist.md, not to this tracker — which its own readiness-authority note at the top already says. Three items: 1. Redirect posture — filed as open, with the verified facts separated from the unresolved ones: C1 §5 is provably not violated (POST redirects drop `data` on 301/302/303; 307/308 raise and fall to §4 residual), while classification fidelity and a documented credential dimension (moltbook_api_spec §1: bare-domain redirect strips Authorization) remain open. Whether it gates GO-2 is explicitly not decided here. 2. client.py:144's duplicate `f"Bearer {api_key}"` and its stale "the ONLY place the key is touched" comment — recorded with why C2's helper is transport-scoped rather than consolidating it. 3. Test-count sync — recorded as deferred to the C2-complete commit by decision, with the reason it differs from #69's case. Contains no test count. Item 3 states that the documented count has moved without restating a number, so this commit cannot itself become a stale count needing a later correction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
C1 decided what things mean; C2 is making the transport structurally capable of carrying that decision, not making any new decisions.
This PR is the structural half only. No classification logic, no C1 semantics, no new governance authority.
Why
The live captcha wiring must build
submit_captcha_fnbeforeMoltbookHTTPTransportexists, because Note E's fail-closed presence check takes it as a constructor argument. It therefore cannot reach a bound method, and_real_requestwas trapped behind an instance it does not yet have. The factory shape was never the problem; the trapped default was.What changed
real_request(base_url, method, path, body, headers)at module level, containing_real_request's prior body with only the Note D comment relocated into the docstring. No logic, HTTP semantics, redirect posture, error handling, or timeout change._collapse_letter_runs,_normalize_captcha_prompt,_word_number_value). One here would signal the opposite of why it was extracted.real_requestis not itself arequest_fn— the seam signature is(method, path, json_body, headers); callers partially apply thebase_url.auth_headers(api_key)at module level, transport-scoped._real_request()/_auth_headers()reduced to one-line delegations, each with a guard comment stating instance-specific behavior does not belong in the wrapper.One HTTP path, not two: the write path and the live verify path now build on the same function, so retry/redirect/timeout posture cannot diverge between them (C1 §5's concern).
Tests — 613 passed, 7 xfailed
Two added. The existing two
_real_requesttests are deliberately untouched — they now cover the delegation, which is the regression evidence that extraction changed no behaviour.The new module-level test pins the one property they cannot: callable with no transport instance in existence. Those two would still pass if an instance dependency were reintroduced, because they hold an instance. This one would not — which makes the "pure delegation" guard mechanical rather than a comment asking the next editor to remember.
Deliberate deferrals — recorded as decisions, now tracked in
TODO.mdThe second commit exists because these were tracked nowhere — only in review conversation and a commit body, which is the state where a reviewer reconstructs a deferral by noticing a mismatch (what #69 had to do for test counts).
dataon 301/302/303; 307/308 raise and fall to §4 residual). Open: classification fidelity (a followed redirect means the status reaching the classifier may not be/verify's own, and C1 §3.2 makes status authoritative), plus a documented credential dimension —moltbook_api_spec.md§1 records that a bare-domain redirect stripsAuthorization.client.py:144duplicatesf"Bearer {api_key}", and its comment still calls itself "the ONLY place the key is touched". C2's helper is transport-scoped by decision; consolidating would reach outside C2 and ripple intotest_moltbook_credential_integrity.py:196.TODO.md's entry states the count has moved without restating a number, so it cannot itself become a stale count.Scope
No C1 decision introduced · no
submit_captcha_fnpolicy beyond making the live path authenticable · noTransportResult.rate_limitchange · no Note E constructor invariant touched · no write-path behaviour changed · no redirect change ·CLAUDE.md/README.mduntouched.🤖 Generated with Claude Code