Skip to content

C2 classification contract — C1 §3 implemented, Erratum 1 filed, C2 row signed - #72

Merged
GodSpeed313 merged 9 commits into
mainfrom
m7-c2-classification-contract
Aug 20, 2026
Merged

C2 classification contract — C1 §3 implemented, Erratum 1 filed, C2 row signed#72
GodSpeed313 merged 9 commits into
mainfrom
m7-c2-classification-contract

Conversation

@GodSpeed313

Copy link
Copy Markdown
Owner

C1 decided what the platform's responses mean; this PR implements that contract, files the one correction C1 needed, and signs C2's checklist row.

Completes C2's engineering half. The structural half (instance-free request/auth seam) landed in #71.

What changed

  • moltbook/transport.py (+245) — the C1 §3 response-condition contract: the eleven-row classification table, detection precedence across the HTTP-status and response-envelope axes, HTTP 409 as AMBIGUOUS, the §4 residual rule (unenumerated responses resolve to AMBIGUOUS, no silent coercion and no exception-raising), and §7's requirement that CaptchaAttemptRecord identify which §3.3 condition matched or that none did.
  • tests/test_moltbook_transport.py (+428) — per-row coverage plus the residual path.
  • docs/m7_c1_wiring_plan_erratum_1_2026-08-17.md (+144) — Erratum 1, LOCKED, operator-signed 2026-08-17 00:43 EDT.
  • TODO.md — the C2 parked items, recorded so each deferral is a decision on the record rather than something a later reviewer reconstructs from a commit body.
  • CLAUDE.md / README.md — test count synced 611 → 647, the deferral C2 seam extraction — instance-free request/auth path for the live captcha wiring #71 filed deliberately.
  • docs/m7_operator_go_checklist.md — C2's row signed.

Erratum 1 — a defect inside locked C1

§3.2 rule 1 cited "§3.3 rows 3–7", but §3.3 also enumerates C1-8 (400) and C1-9 (500). Rule 4 excludes them by its own terms ("not enumerated in §3.3") and rules 2–3 are 2xx-only — so two enumerated rows were matched by no rule of §3.2.

This was implemented under the corrected reading because C1 §8 item 1 admits no other, with the divergence recorded in the code and commit at the time. It is not a §9 stop: stop 1 covers conditions §3.3 does not enumerate. The outcome is unchanged either way — both readings yield AMBIGUOUS — so only recorded provenance differs, which is what §7 exists to protect.

The erratum is filed instead of an edit: C1 is locked and is not reopened to record a correction. Its wording states that §3.2 rule 1's parenthetical should read "rows 3–9"; it does not claim to alter the locked text, change any row's outcome, or touch §3.3 in any respect.

C2's checklist row

Signed as artifacts-exist + suite-green, and the row says so explicitly: it attests that the required artifacts exist at the cited commit and that the suite is green, and does not attest to a reading of the implementation itself.

The row's Evidence is also the only thing in the repo that points at Erratum 1 — a reader of locked C1 §3.2 would otherwise see "rows 3–7" with no sign a signed correction exists.

tools/verify_go_checklist.py: 29 rows — 15 complete, 14 open, 0 defective.

Tests — 647 passed, 7 xfailed

Verified by run at 714f202 with a clean tree.

Scope

No C1 decision introduced or revisited · no governance authority claimed over C1's locked text · the AMBIGUOUS-disposition architecture question carried as C1 §8 item 7, not resolved here · C3–C5 untouched and still open · POST /api/v1/verify was not called.

🤖 Generated with Claude Code

GodSpeed313 and others added 9 commits August 17, 2026 00:23
…fication

C1 decided what things mean; C2 makes the transport structurally capable of
carrying that decision. This commit carries it. Every classification below
traces to a row of C1 §3.3 or to §4's residual rule — no new semantics.

WHAT LANDED

- `CaptchaCondition(str, Enum)` — C1-1..C1-10 plus RESIDUAL. No UNKNOWN
  member: an unclassifiable response is RESIDUAL, a positive classification,
  not a missing one. No fourth `CaptchaOutcome` member (§4 item 1, §7 item 4).
- `CaptchaSubmitResult` replaces the `(CaptchaOutcome, dict | None)` two-tuple,
  which had nowhere to carry the matched condition or the response headers and
  so could satisfy neither §7's recording requirement nor §3.2's two-axis
  detection. Follows Note D's precedent: typed result, visible shape change,
  no compatibility adapter.
- `CaptchaAttemptRecord.condition_id` is REQUIRED — a record that does not
  identify its condition is not constructible (§7 item 2).
- `classify_captcha_response()` applies §3.2 precedence in order, then §3.3,
  then §4. Never keyed on status alone, never on the `error`/`hint` strings
  (unpinned human-facing text); C1-2 is `success is False` on a 2xx.
- `live_submit_captcha_fn(request_fn, api_key)` builds the live seam on the
  module-level `real_request` extracted in 0fdbf88. Exactly one request per
  submission, no loop, no backoff, no resubmission on any outcome (§5).
- `verify()` is a CARRIER: it copies every field onto the record and derives
  nothing. Re-deriving classification here would put a second, drifting
  interpretation of the same response into the record.

C1 §10(b) BOUNDARY, HELD STRUCTURALLY

Verify-path `rate_limit`/`platform_headers` are preserved on the attempt
record. They are never written to `TransportResult.rate_limit`, which keeps
its Note D meaning (the WRITE response's state) — `resp_meta` stays bound to
the write response and `verify()` still returns a bare `CaptchaOutcome`, so
verify-path metadata is unreachable from `_classify_created_content` by
construction rather than by discipline. Preservation confers no authority:
nothing reads these fields, and the kill-switch trigger is still keyed on
`outcome` alone, its signature still closed keyword-only so evidence cannot
silently become an escalation input.

FINDING — AN INCONSISTENCY INSIDE LOCKED C1, FLAGGED NOT RESOLVED

C1 §3.2 rule 1 names "rows 3-7" as the authoritative enumerated non-2xx
statuses, but §3.3 also enumerates C1-8 (400) and C1-9 (500), which are
likewise non-2xx. Rule 4 covers only statuses "not enumerated in §3.3", so on
a literal reading 400 and 500 fall through §3.2 entirely despite having their
own rows, and could never be recorded as themselves.

This implementation treats ALL enumerated non-2xx statuses as authoritative,
because §8 item 1 requires "every enumerated condition classified as
specified" — under the literal reading, rows 8 and 9 are dead letters, which
§8 item 1 cannot mean. The runtime outcome is IDENTICAL either way (both
readings yield AMBIGUOUS); only the recorded provenance differs. Recorded in
code at the mapping and reported to the operator 2026-08-17. Not treated as a
§9 stop: §9 stop 1 covers conditions §3.3 does NOT enumerate, and these are
enumerated.

Related, and disclosed rather than smoothed over: a non-JSON body raises
inside `real_request` before a status is available, so such a response reaches
C1-10 rather than §3.2 rule 3 even when it was a 2xx. Both are AMBIGUOUS; only
provenance differs. Documented at the C1-10 handler.

TESTS — 647 passed, 7 xfailed (33 added)

One per §3.3 row plus C1-R; §3.2 precedence including an enumerated status
beating a contradicting body; residual-vs-enumerated distinguishability;
mandatory `condition_id` on both types; no-retry proofs on C1-5/6/9/10 by
counting calls at the seam; urllib's own no-retry posture evidenced
behaviourally rather than asserted (§8 item 4); and the boundary proof — write
and verify responses carrying DIFFERENT rate-limit values, asserting
`TransportResult` reports the write's and the record the verify's. All
fake-driven; `POST /api/v1/verify` is never really issued.

C1 §8 ITEM 7 — CARRIED, NOT RESOLVED

The disposition of an AMBIGUOUS outcome after `verify()` returns stays
unresolved, now recorded as an explicit TODO item rather than left implicit:
the outcome is recorded but nothing consumes it, and because AMBIGUOUS never
increments the confirmed-failure counter, a systematic ambiguous condition
will never engage `captcha_suspension_risk`. C1 §6 records this as needing its
own ruling before unattended operation, and as not blocking GO-2.

Test-count sync (CLAUDE.md, TODO.md, README.md -> 647) performed here, once,
at the C2-complete count, per the deferral recorded in 9ab4d10; its TODO entry
is marked done rather than left reading as open.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ce note

Alongside 55fd620 rather than after it: the classifier commit records the
divergence from §3.2's literal text, and this is the artifact that resolves
what the plan means. Filed as its own dated document rather than as an edit
to the locked plan, per the precedent that a locked document is not reopened
to record a correction.

DRAFT — corrects nothing until signed.

Scope is deliberately one cross-reference. §3.2 rule 1 cites "§3.3 rows 3-7"
as the authoritative enumerated non-2xx statuses, but §3.3 also enumerates
C1-8 (400) and C1-9 (500); rule 4 excludes them by its own terms ("not
enumerated in §3.3"), and rules 2-3 are conditioned on 2xx. So two enumerated
rows are matched by no rule of §3.2. Corrected to "rows 3-9".

The erratum states at length what it does NOT do, because the failure mode
here is a scope-word fix reading as a re-decision: it changes no row's
outcome, amends no other section, and decides nothing undecided. §3.3 assigns
outcomes; §3.2 describes the order in which rows are reached. The defect is
in the second.

Outcome-neutral and says so: C1-8 and C1-9 are AMBIGUOUS as signed, and C1-R
is AMBIGUOUS under §4, so no CaptchaOutcome, publication_status,
verification_status, or counter differs either way. What differs is the
recorded condition identifier — which is the reason to correct it rather than
tolerate it, since §7 exists to keep a documented-ambiguous outcome
distinguishable from one that falsifies the model, and the uncorrected
reading would record a documented 400 or 500 as a falsification.

It also states that signing it is NOT review of the C2 code: it records what
the plan means; whether C2 implements the plan is the C2 row's question,
answered by its own evidence and its own signature.

Every citation verified against the plan before drafting (rule 1 at :125,
rule 4 at :131-134, §8 item 1 at :338-339, §7 at :304-305, §2 at :81, the
C1-8/C1-9 outcomes at :153-154). One defect found in my own draft and fixed:
the §8 item 1 quotation carried bold emphasis the source does not have, which
is the same paraphrase-fidelity error class as borrowing a narrower
conclusion sentence. Now quoted plainly and marked as such.

Separately, the non-JSON observation is kept as a PARKED note at the C1-10
handler rather than escalated: a body that fails to decode inside request_fn
loses its status before classification, so it lands as C1-10 rather than
§3.2 rule 3 even when it was a 2xx. Both rows are AMBIGUOUS, so no outcome
differs; only provenance does. Resolving it would mean changing what
request_fn returns, which is Note D's contract and outside C2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ce in §4

Two operator-directed changes to the unsigned draft.

1. §4's phrasing. "is corrected to" was the active voice of an amendment, on a
   document that is locked — it implicitly claimed this erratum has authority
   to alter the plan's text, contradicting §1's own premise that it is filed
   INSTEAD of editing the locked document, precisely because it cannot. Now
   reads "should read, correcting the citation to ...": it describes what the
   plan's text ought to say without asserting that this adjacent, unsigned
   document is the instrument doing it. Line 75 was the only occurrence; §1,
   §5 and §6 were already in the weaker states-what-the-plan-means register
   and are untouched.

2. A fourth piece of internal evidence in §3, independent of the first three:
   §7's own illustrative list contrasts "a documented-ambiguous response
   (C1-6, C1-9, C1-10)" with "a response C1 never enumerated (C1-R)"
   (plan :302). A section written after §3.2 therefore already assumes row 9
   is recorded as itself rather than folded into residual.

   Recorded with its limit rather than at full strength: that list names C1-9
   and NOT C1-8, so point 4 is direct evidence for row 9 only. Row 8's
   reachability continues to rest on points 1-3 and on its identical
   structural position in §3.3. Writing it as covering both rows would have
   been the widening this erratum exists to avoid.

Staleness sweep caught the consequence of the addition: §3's own preamble
still read "Three features", and the fourth is not symmetric with the other
three. Preamble now states the count and the asymmetry together.

Still DRAFT. Corrects nothing until signed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Updated correction details with operator name and date.
Updated the correction date for the wiring plan erratum.
Updated correction timestamp from 00:42 to 00:43 EDT.
Post-signature staleness pass. The header still read "DRAFT — awaiting
operator signature. Corrects nothing until signed." on a document that had
been signed — the same self-contradiction the GO-1 record carried when its
header said it authorized nothing over a signed authorization.

Replaced with the locked status, the recorded signature time, and a pointer
to where the signature lives. The original line did two jobs: it stated
status AND it bounded the document's effect. Only the first was stale, so the
second is preserved rather than dropped — the replacement still says the
effect is the §4 citation-scope correction and nothing beyond it, and still
points at §1 for what the erratum does not do. Dropping that clause would
have quietly widened the document at the moment it became operative.

Touches the header line only. The signature block is not modified.

Full sweep of the document found no other stale statement: §1, §4, §5 and §6
were written in the states-what-the-plan-means register throughout, and §6's
"signing it does not constitute review of the C2 code" remains true after
signing rather than becoming a past-tense claim.

Signature verified before this commit: all three cells present
(LOCKED / Kevin Brown / 2026-08-17 00:43 EDT), the timestamp consistent with
the signing commit's author date, and the cumulative diff across the operator's
three commits touching ONLY those three cells — Statement and document body
byte-identical to the drafted text.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@GodSpeed313
GodSpeed313 merged commit c1eb9ba into main Aug 20, 2026
2 checks passed
@GodSpeed313
GodSpeed313 deleted the m7-c2-classification-contract branch August 28, 2026 01:56
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