Repository navigation
Refuse email addresses in the publication scan - #1002
seathatflowsinourveins wants to merge 10 commits into
Conversation
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 308033f6c1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
308033f to
bc41822
Compare
|
@codex review |
bc41822 to
e0862d8
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bc41822cae
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
e0862d8 to
51f45b5
Compare
Claude Code puts the signed-in account's email address in every session's context, and nothing in scripts/validate.py refused one, so it could reach a public evidence file. PRIVATE_CONTENT gains an "email address" rule: an RFC 5322 addr-spec, case-insensitive, that exempts GitHub noreply addresses, the Anthropic and GitHub noreply commit identities, the RFC 2606 reserved names with example.* under any TLD, the git@ SSH user, systemd instance units and decorator calls. The tree scan found 86 tracked files with public upstream or vendor addresses or address-shaped text (signing keys, package maintainers, upstream captures, fixtures). scripts/public_email_paths.json lists them with a reason per group; validate.py's publication scan and host_receipts.py's receipt scan waive only this rule for them. A test keeps every entry live. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Codex's GitHub review of 308033f (1 P1, 3 P2): - P1: a path waiver passed any later address added to a listed file (reproduced at 308033f). scripts/public_email_matches.json now lists the 64 reviewed matches; a text passes the email rule only when every match in it is listed. The loader refuses a non-lower-case or repeated entry, and a test fails on an entry no tracked file carries. - P2: the rule no longer skips a git local part; [email protected], the only Git-host form in the tree, is a reviewed entry. - P2: the last label may be an IDNA A-label (RFC 5890 2.3.2.1). - P2: native_token_ci.py redacts addresses as <EMAIL>; its copies of validate.py's rules sit in REDACTIONS, held equal by a test. host_receipts.py judges the raw receipt and each decoded JSON string on its own, so an escaped address is not waived by the raw bytes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…mes, safe digests
The command center's Claude read and the co-op's GPT read (P1 B1, B2, B4;
P2 B3, C1; C2, C3), in one commit as the command center ruled:
- B1: a "(" after a match exempts nothing; the 7 tree matches it hid,
decorator calls after an escaped newline, are reviewed entries.
- B2: the local part and its left boundary take every RFC 5322 atext
character (rfc5322.txt:679-690), so a match is the whole dot-atom and
private!<listed> is a different, unlisted match. A local part needs a
letter or a digit (a code span of an @import names no mailbox).
- B3: the dead-entry check leaves the registry out.
- B4 (correction #161): native_token_ci.py publishes digests of sanitized
text only; raw_sha256 and the stdout, stderr and MCP raw digests are gone.
- C1: RFC 2606's three names exactly, with their subdomains; example.*
under other TLDs and chained labels are no longer exempt.
- C2: target is a TLD (IANA 2026101001), so it is no longer an exempt
unit type.
- The registry is re-reviewed at main 3a8a846: 78 values, including
#1003's URL-userinfo fixture; the record's counts are measured here.
Each regression fails on e0862d8 and passes here.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
…case The GPT delta read at 51f45b5, as the command center ruled it: - P1-2: Run.publish() cleans each decoded key and string value of JSON or JSON Lines output, nested JSON too, for every published argument, stream, artifact and MCP digest, so a @-escaped address is removed. - P2-3: the waiver keys a match by its local part as written and its domain in lower case (RFC 5321 2.4, rfc5321.txt:869); the registry lists the tree's spellings (79 entries). - P1-1: the letter-or-digit guard stays; "names no mailbox" is restated as the residual it is (CPython 3.13.16 _header_value_parser.py accepts one). - P2-4: the counts are measured: 532/454 at 51f45b5, 533/454 here. The new tests fail on 51f45b5 (3 failures, 6 errors) and pass here. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…decode depth The command center's terminal adjudication at 4c916e1: - B1: publish() split lines with str.splitlines(), which also breaks at U+2028, U+2029 and U+0085 (CPython cbc944f4 unicodetype_db.h:6418-6430); RFC 8259 7 allows them raw in a string, so such a line was never decoded and its \u0040-escaped address was published. A line now ends only at a line feed. - B2: JSON text nested in strings deeper than PUBLISH_DEPTH (4) is refused with the fixed PUBLISH_REFUSED message, and nothing is published. - B3-B5, B7-B9: the registry's spelling, the table (39), the respelling count (7 and 1), the symbol-only figure (125 in 76 files at 900835c, method named; correction #182), the limitation text, and the escape's sources (RFC 8259 7 rfc8259.txt:433; json/decoder.py:60, :118). The new tests fail on 4c916e1 (16 failures) and pass here. Co-Authored-By: Claude Opus 5.5 <[email protected]>
4c916e1 to
adc2adb
Compare
…e scanned snapshot (#1027 P1) Astra's P1 at d6be1b0 (CHANGES_REQUESTED, reproduced on this PR's own EndToEndTests harness): nas_land.sh and uet_land.sh checked that SQUASH_BODY_FILE was a readable, non-empty regular file and nothing more, so a body holding a home path, an account name, an address that is not a no-reply one or a token shape went into main's permanent history through gh pr merge --body-file (her four synthetic payloads each gave exit 0 and one merge call). gh reads the file when the merge call starts (cli/cli v2.102.0 pkg/cmd/pr/merge/merge.go:146-153; :292-293 and http.go:72-75 send it as CommitBody; pkg/cmdutil/file_input.go:8-16), up to an hour or more after any check, so the scanned bytes must also be the bytes gh reads. When SQUASH_BODY_FILE is set, both scripts now, before the lock and every gh and git call: - copy the file once to a snapshot (mktemp in TMPDIR, mode 0600; an EXIT trap removes it on every exit, SIGTERM and SIGHUP included); - scan the snapshot with the new tools/landing/squash_body_scan.py (the repository's PRIVATE_CONTENT table from scripts/validate.py, a WSL UNC home path that table misses, an address that is not a no-reply one, and the names of the CC's landing-prohibited-names list; the list is parsed, never sourced) and with scripts/validate.py --scan-file (the scan the pre-push hook runs); - stop with exit 2 and one fixed line on a match, a copy or scan that fails, a scanner or names file that is missing or unreadable, and any answer that is not a scan's own clean line (fail closed); nothing of the body, no path and no scanner output is printed; - give gh the same snapshot, never the caller's file. New variables: LANDING_BODY_SCAN, LANDING_SCANNER (default: scripts/validate.py of LANDING_NAS_REPO, which also fits the CC's live copies) and LANDING_NAMES_FILE (default: the file under the coordination tree). Three allowances, the CC's rulings of 2026-10-12, each with its tests: - a names-list entry equal to the running host's own name (compared with hostname when the scan runs, never printed) is a warning count, not a refusal; every other name still refuses; - a systemd unit instance, <name>@<instance>.service|timer|socket|target|path|mount (systemd/systemd man/systemd.unit.xml:121-135), is not an address; - a short list of vendor co-author trailer addresses, each with the vendor source it comes from, passes: [email protected], as the vendor's own repository carries it (openai/codex@db22c91, message line 38). #1002's email registry replaces the list. A real-provider address is still refused (the control). Tests (22 new or changed; at d6be1b0 they give 140 failures and 5 errors, here they pass; the allowance tests also fail on the scan without the allowance): - a body with a home path of each form (Linux, macOS, Windows with either slash, the WSL mount, the WSL UNC forms with one or doubled backslashes, the dash-encoded and graph-id forms, percent-encoded), a name of a synthetic names file, an address that is not a no-reply one (with look-alikes) or a token shape (seven kinds) is refused by both scripts with exit 2 before any call; - a missing or failing scanner, names file or validate.py, a scanner that prints nothing or another line, and every answer of validate.py --scan-file but its clean one refuse the landing; - the valid-body control lands (exit 0, one merge call) with the body's bytes read from a mode-0600 snapshot, after both scans ran on it; - a file rewritten after the scan does not reach the merge; the snapshot exists during the run and is gone after exits 0, 3 and 7 and after SIGTERM and SIGHUP; the defaults of the new variables; the real validate.py --scan-file where Betterleaks is installed; - the allowances: a body naming the host lands with the count printed and the name not, and another listed name still refuses; unit instances land, their look-alikes and a provider address do not; the vendor address lands, another local part at the vendor's domain, the vendor's local part at another domain and a provider address do not; - squash_body_scan.py by itself: the names file's formats and that no line of it runs, the address, unit and vendor rules, the host name, a name as a whole token, and its one output line. Measured on the last 600 commit messages of main at 3a8a846 (counts only): 8 refused: 0 names, 5 addresses, 3 home paths; 26 messages name the host (a warning); 402 carry the vendor noreply trailer, which passes. main's history keeps 3 messages with a home path and 3 with an address at a mail provider; history is immutable, as for CC correction #177, and nothing here rewrites it. The README records the scan, the variables, the exit-2 cases, the known gaps and the sources: cli/cli v2.102.0, MicrosoftDocs/WSL, github/docs, systemd/systemd, openai/codex, the bash manual, CPython's -I and socket. The README digests and manifests/evidence.json follow the changed files. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…reader The command center's addenda 01 and 02 at 4c916e1: - Duplicate names (the GPT read's P1-2): json.loads keeps only the last member, so an earlier member's escaped address went out uninspected. publish() parses through an object_pairs_hook that refuses such an object (RFC 8259 4, rfc8259.txt:322). - N1 (the micro at adc2adb): splitting only at line feeds left JSON beside a lone CR, or after RS, FF, VT, FS, GS, U+0085, U+2028 or U+2029, undecoded, while universal newlines (Lib/_pyio.py:153-154), splitlines() and jq read the address back. publish() now verifies its own output (recoverable()): every reader's splitting, a stream decoder, all members, nested JSON to the same depth and numbers kept as text (the micro's N2); if any reader recovers private content, PUBLISH_REFUSED, no output. Clean output keeps every byte. The new tests fail on adc2adb (35 failures) and pass here. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 66036a74ad
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…n every framing The command center's addendum 03 at 66036a7 (a shadow review, reproduced by the co-op): a pretty-printed object after a lone CR, or behind RS framing (RFC 7464 2), was cut by every split view, so its escaped address went out while readline-then-json.load and jq --seq read it back. The verifier now also decodes every RFC 8259 7 string escape across the whole output, framing aside, up to PUBLISH_DEPTH times, and runs the sanitizer's rules on each projection. Its decoder refuses a duplicate name too, so RS-framed duplicates and duplicates after a long number are refused. The record's stale scope sentence is corrected; a test's argv no longer carries the escaped address. From the GPT read at 66036a7 (ACK): the MCP session closes the parent's pipe ends once its readers finish (ResourceWarnings 24 to 0). The new tests fail on 66036a7 (13 failures) and pass here. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6aaa204a74
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The command center's addendum 04, from the GPT read and its Claude micro at 6aaa204: - P1-1, D5: an address escaped five or more times, or JSON nested four or more times, behind a lone CR or RS framing was published: the escape projection stopped after PUBLISH_DEPTH passes and the record views cut the multiline object. publication_views() now refuses when a further pass would still decode something, reads RS records whole, skips a byte order mark (RFC 8259 8.1) and resumes at the next line where no JSON text starts; text past the bound is refused whatever it holds. - P1-2: host_receipts.py decoded a receipt once, so JSON in an output_excerpt kept its escaped address. It imports the same views, and a receipt they cannot finish is refused. native_token_ci.py joins the verdict-review gate's TRUST_PATHS. - P2-3, F1: a duplicate name after a CR, an RS, a byte order mark or a progress line, nested, or beside a long number is refused. - P2-4, P2-5: a rewrite that would merge two names or change a number as written is refused. - Codex: the noreply exemption keeps the local part's case (RFC 5321 2.4). - P3s: JSON too deep for Python's decoder is refused, not raised; the surrogate-pair locator; the pipe close's CPython citation and a closure test; command tests read their payloads from files; the record's stale sentences, the 765 figure and the warning count. The new tests fail on 6aaa204 (46 failures, 4 errors) and pass here. Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 167eb00a98
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The command center's Claude micro at 167eb00: - N1 (Codex's thread 4247416401): Run.observe() recorded the names a tool wrote under its state directory through clean() alone, so a file name that is JSON holding an escaped address reached receipt.json. flush() now verifies every string of the receipt, names and values, as publish() verifies its output; one that fails is replaced by the fixed refusal text and the run fails. - Thread 4247416418: an address right after a JSON escape took the escape's last character, leaving invalid JSON. A document clean() changed is read as written and its decoded values are cleaned, at each level of JSON in strings. - Thread 4247416409: a rewrite holding a lone surrogate is refused, not raised as UnicodeEncodeError. - FC2: re-reading of JSON text left unclosed is bounded (RESCANS); past it the text is refused. - FC1, FC3, FC4: the nesting bound applies in the framings the views read, not "whatever it holds"; the byte order mark's attribution; the addendum-04 label. The duplicate residual is disclosed. The new tests fail on 167eb00 (6 failures, 23 errors) and pass here. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Both reads at 171b081: the claim that the decoding work is bounded by a multiple of the length was false. Every failed decode builds a JSONDecodeError that counts line breaks from the start of what it decodes (CPython Lib/json/decoder.py:31-32), and the read across lines decoded the whole piece from each line. It now decodes growing slices that start where the text does, and every slice's length counts against RESCANS, so the work is a multiple of the length. In this delta's code: refused names are numbered instead of merged and the report's own names are verified; a document holding NaN that clean() changes is published cleaned again; the unread framings are worded as measured; the lone-CR too-deep case no longer depends on the interpreter; the surrogate refusal cites RFC 3629 3. The new tests fail on 171b081 (4 failures) and pass here. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Scope
scripts/validate.pyPRIVATE_CONTENTgains anemail addressrule. Claude Code puts the signed-in account's address in every session's context, and nothing refused one before it reached a public evidence file.scripts/public_email_matches.jsonlists the 79 reviewed matches, each public upstream or vendor text, in four groups with a reason; an entry keeps its local part's case and lowers its domain. A text passes the rule only when every match in it is listed. The waiver applies to this rule alone, invalidate.py's publication scan and inhost_receipts.py's receipt scan.scripts/native_token_ci.pynow redacts addresses in its sanitized fixture evidence.2a330b0a0(main after Rule text: work compounds through reviewed, cited repository records #1020). Rebased from8bffa2c41, fromf1f6c0c20and, with the terminal commit, from3a8a8461e, each time for a manifest-only conflict (withoutmanifests/evidence.jsonthe four earlier commits' patch-ids are identical;4c916e14is now6db753db). The deletedscripts/public_email_paths.json's manifest row is dropped in the commit that deletes the file.lane:foundationscripts/validate.py,scripts/public_email_matches.json,scripts/host_receipts.py,scripts/native_token_ci.py,tests/test_validate.py,tests/test_host_receipts.py,tests/test_native_token_ci.py,docs/decisions/2026-10-11-email-address-publication-rule.md,manifests/evidence.json(re-registration);scripts/verdict_review_gate.py(oneTRUST_PATHSentry, addendum 04)Review rounds:
308033f6(one P1, three P2s) is answered ine0862d8d:308033f6: an operator-shaped address appended to a listed file gave no email finding. The list now holds the reviewed matches, so the same edit gives one finding on that file, and the address is never printed.gitlocal part at any domain. The tree's only Git-host form, thegituser at github.com (7 matches in 6 files), is a reviewed entry.xn--, RFC 5890).native_token_ci.pyredacts addresses as<EMAIL>. Its copies ofvalidate.py's rules sit inREDACTIONS, and a test holds each copy equal to its original.host_receipts.pynow judges the raw receipt and each decoded JSON string on its own, so an address a JSON escape hides is not waived by the raw bytes. A test covers this. (It decoded one level until addendum 04 below, which judges every view.)e0862d8d(three P1, three P2 and two false claims) are answered in one commit,51f45b5d, as the command center ruled:(after a match exempts nothing. The 7 tree matches it hid, decorator calls after an escaped newline, are reviewed entries.atextcharacter, so a match is the whole dot-atom andprivate!<listed>is a different, unlisted match. The registry is re-reviewed. A local part needs a letter or a digit; the command center kept that guard (measured at main900835c6with this rule minus its guard over every tracked blob: 125 symbol-only local parts in 76 files, each a backtick, quote or slash before an@-name, none an address; the first figure, 139, came from an ad-hoc search and does not reproduce). Residual: local parts made only of RFC 5322 atext symbols are not detected, though they are valid; no observed instance is identity-bearing.native_token_ci.pypublishes digests of sanitized text only;raw_sha256and the raw stdout, stderr and MCP digests are gone.example.*under other TLDs, and chained labels, are no longer exempt.targetis a TLD, so it is no longer an exempt unit type.3a8a8461brought Verify the code citations in a PR's SOTA sources (W1b phase A, report-only) #1003's URL-userinfo fixture, reviewed in the same commit (78 entries).51f45b5d(two P1s, two P2s, seven P3s) is answered in one commit,4c916e14, as the command center ruled (fix round 2):Lib/email/_header_value_parser.py:1219,:1360,:1486,:1682atcbc944f4). Both copies' comments, the record and this description now state the residual above.@a JSON escape hid (\u0040) passedRun.clean(), and the published receipt or artifact decoded back to it.Run.publish()cleans each decoded key and string value of JSON or JSON Lines output, nested JSON four levels deep (the terminal commit refuses deeper nesting and splits lines at line feeds only), for every published argument, stream, artifact and MCP digest; each digest is of the published bytes. (This saidhost_receipts.pyjudges decoded values the same way; it decoded one level until addendum 04, the micro's C1.) Codex's thread 4242033284 is answered and resolved.email_keykeeps the local part (RFC 5321 2.4, rfc5321.txt:869) and lowers the domain, in the loader, the dead-entry check and the waiver. The registry lists the tree's spellings: 7 entries respelled and 1 added, where one public agreement carries a contact in two spellings (79 entries). Codex's thread 4242033279 is answered and resolved.51f45b5dwere 532 and 454, not 533 and 455; they are measured below at4c916e14.51f45b5d(3 failures, 6 errors) and pass at4c916e14.4c916e14(CHANGES_REQUESTED; fix round 2 closed 21 of the 36 encoded leaks it probed) is answered in one terminal commit,adc2adb2(written on4c916e14asa94c07d5, rebased with the branch onto main2a330b0a; the reviewed delta is6db753db..adc2adb2), as the command center ruled:publish()split JSON Lines withstr.splitlines(), which also breaks at U+2028, U+2029 and U+0085 (CPythoncbc944f4Objects/unicodetype_db.h:6418-6430); RFC 8259 7 allows them raw in a string, so such a line was never decoded and its escaped address was published. A line now ends only at a line feed.publish()now refuses it with the fixedPUBLISH_REFUSEDmessage and publishes nothing (in the framingspublish()parses; behind a lone CR or RS it went out until addendum 04, the micro's D5); a command, artifact or MCP session that meets one fails. Real receipts nest at most two levels.Lib/json/decoder.py:60,:118).\u0040); it had been decoded when typed.4c916e14(16 failures) and pass atadc2adb2.adc2adb2(fix3): ACK. The command center's addenda 01 and 02 (its micro atadc2adb2and the GPT read at4c916e14) are answered in one commit,66036a74:json.loadskept only the last member, so an earlier member's escaped address went out uninspected.publish()parses through anobject_pairs_hookthat refuses such an object (RFC 8259 4, rfc8259.txt:322).splitlines()and jq read the address back.publish()now verifies its own output (recoverable()): each reader's line splitting, a stream decoder, every member, nested JSON to the same depth, numbers as text (which also covers the micro's N2, a number past CPython's 4,300-digit limit). A recovery by one of these readers isPUBLISH_REFUSED, with no output (addendum 04 adds the readers this lacked and refuses what they cannot finish).adc2adb2(35 failures) and pass at66036a74; byte-for-byte controls pass on both.66036a74(fix4): ACK, three P3s. The command center's addendum 03 (a shadow review the co-op reproduced at66036a74) is answered in one commit,6aaa204a:json.loadandjq --seqread it back. The verifier now also decodes every RFC 8259 7 string escape across the whole output, framing aside, up toPUBLISH_DEPTHtimes over, and runs the sanitizer's rules on each projection.:316-318) is corrected.ResourceWarnings in the module's tests: 12 before, printed on 24 lines; 0 after); the refusal and recovery ordering is unchanged.66036a74(13 failures: the two framings and a doubly escaped address throughpublish(); the two framings through an artifact, a command and an MCP answer; three duplicate shapes; the receipt's own check) and pass at6aaa204a. Over the committed native-token receipts, the published streams pass unchanged (this said 765, a selector not recorded; addendum 04 states its census).6aaa204a(fix5, CHANGES_REQUESTED: two P1s, three P2s) and the command center's Claude micro at6aaa204a(CHANGES_REQUESTED: D5, F1, C1) are answered in one commit,167eb00a, under the command center's addendum 04:171b0811: behind a prefix on the same line, a form feed, a vertical tab, U+0085 or U+2028, clean text nested deeper is not read as JSON, the micro's FC1.)output_excerptholding JSON with an escaped address passed the receipt scan, which decoded once. One verifier,publication_views()innative_token_ci.py, now servesRun.publishand the receipt scan, and a receipt it cannot finish is refused.json.loaddoes.(?-i:noreply), RFC 5321 2.4).Popen.__exit__(Lib/subprocess.py:1106-1113atcbc944f4) and a test asserts both ends are closed; the census selectors are stated in the record.native_token_ci.py, not a module of its own: the push gate's read analysis counts a call into a non-stdlib module as running its argument, so a module added an advisory location.native_token_ci.pyjoins the verdict-review gate'sTRUST_PATHSbecausehost_receipts.pyimports it.6aaa204a(46 failures, 4 errors) and pass at167eb00a; 14 of 14 weakened copies fail them (named in the record).167eb00a(fix6): ACK, six P3s (in Follow-ups). The command center's Claude micro at167eb00a(CHANGES_REQUESTED: one new P1, three Codex threads, four false or loose claims) is answered in one commit,171b0811:Run.observe()recorded the names a tool wrote under its state directory throughclean()alone, so a file name that is JSON holding an escaped address reachedreceipt.jsonand a reader decoded it back.flush()now verifies every string of the receipt, names and values, aspublish()verifies its output; one that fails is replaced by the fixed refusal text and the run fails. This covers every field recorded throughclean()alone (error messages, doctor lines, environment values, working directories). Over the 6 committed receiptsRunwrote (16,224 strings), none is refused.clean()changed is now read as written, its decoded values are cleaned, andclean()'s text is kept only if it reads as the same document, at each level of JSON in strings.UnicodeEncodeErrorin a command or an artifact; it is refused.167eb00a(6 failures, 23 errors) and pass at171b0811; 13 of 13 weakened copies fail them. Census A and B are unchanged; the push gate's advisory counts are unchanged.171b0811(both CHANGES_REQUESTED, on one ground: the claim that the work is bounded by a multiple of the length, P2, availability) are answered in one commit,6ba4206a:JSONDecodeErrorthat counts line breaks from the start of what it decodes (python/cpython@cbc944f4Lib/json/decoder.py:31-32, also from the C scanner,Modules/_json.c:299-312), and the read across lines decoded the whole piece from each line, so each failure cost its position: the decoder was handed 1,250 to 20,000 times the length of the PR's own control. The read across lines now decodes growing slices that start where the text does, and every slice's length counts againstRESCANS: 1.92 times the length at every size, 0.38 s at 960,002 characters (9.6 s before).NaNthatclean()changes is published cleaned again; the unread framings are worded as measured (U+2029 included); the lone-CR too-deep case no longer depends on the interpreter; two tests close the CC's test gaps; the surrogate refusal cites RFC 3629 3.171b0811(4 failures) and pass at6ba4206a; 8 of 8 weakened copies fail them. Census A and B are unchanged at both heads.Follow-ups (not in this change):
171b0811:Lib/json/decoder.py:351-364, the encoder);tools/sota-convergence/landscape-sweep/convert.pyon main now calls the native publication scanner, which cannot collect host names in the GPT read's sandbox; verify those modules in a worktree with the host-name source at the landing rebase;flush()refuses a string, it rebuilds that entry's nested containers, so a reference a fixture holds goes stale in a run that already fails.167eb00a(fix6, ACK):str.splitlines()breaks and rejoins the rest still reads a pretty duplicate object after U+2028 (no private content is recovered); support that composed reader or keep the boundary explicit, as the record now states it;scripts/public_email_matches.json(PUBLIC_EMAIL_REGISTRY) as a rule input of the verdict-review gate and add it to the named-input derivation test, with a registry change beside a verdict change;re);RecursionErrorcorrections (python/cpython@cbc944f4Lib/json/decoder.py:216,:307-329,Lib/json/encoder.py:226-241,Modules/_json.c:1033,:1041);167eb00a:host_receipts.py validatenow takes about 3.5 s over the 183 host receipts the micro measured (2.5 s before the shared views).`,',/,*) and keep the rest ofatext. It belongs with the command center's research-queue row for one maintained, cited email detector shared with Local pages: a command-center page for lanes, PR waves, tokens, skills, manifests and owner items #1012, which is meant to replace this hand-written rule (rule 4).@; non-ASCII local parts, U-label domains, and an address glued to a non-ASCII word character; a fullwidth at sign or a zero-width space; percent-encoding (%40in a captured login or OAuth URL first), HTML entities, base64, JWT payloads, quoted-printable and line wrapping; an address followed by_,-or a digit; a domain ending.com.service.native_token_ci.py, the RTK exactness digests (rtk_exactness_fixture) and the Serena parity digests (serena_fixture), which the receipt's limitation names (the locators:1356,:1384,:1515given here before no longer point at them); the off-host app-state and restore runs;usage_record.py'sraw_output_sha256;bundle.py:133,:158. Committed evidence holds 16 JSON files withstdout_raw_sha256and 70 withraw_sha256.native-token-e2e-resultsartifacts are retained on GitHub. They come from hosted runners with the runner's home and a fixture configuration, and no owner address appears in them. They expire by 2026-10-25 (14-day retention), so none is deleted.e0862d8d: non-receipt JSON decoding stays a stated boundary; the PR-body gate carries the email rule at this PR's landing rebase after Scan the pull request description for home paths and credentials #991; adjacent consumers differ on the waiver by design; some unpinned prose locators moved; machine-name references in the manifest's existing content.\x40, a fullwidth at sign,%40) go to the shared email-detector research row (rule 4) and the unified-detector follow-up. (For an address, the escape projection reads all four of two values on one line, a BOM, a trailing comma and a log prefix; the JSON reading covers the first three since addendum 04, so a duplicate name behind a log prefix on the same line is not refused.)66036a74(fix4): retain the twelve named source captures (RFC 8259 and RFC 7464 among them) and the timing inputs (the census selectors are now stated in the record); keep the refusal and recovery ordering; carry the locator, strict-consumer and hostname follow-ups below.6aaa204a:@, a Markdown renderer decodes entities, a followed link decodes%40) stay with the shared email-detector row;Run.publishrewrites only JSON it reads whole or by line feed; a CR- or RS-framed document that needs cleaning is refused rather than rewritten (none of the committed streams is);51f45b5d:--scan-file,host_requests.py,hardware_profile.py,component_matrix.py, the handbook builder, the OpenHands guards) do not read the reviewed list, by design: a capture carrying a public contact needs redaction there;native_token_ci.py, now named in the receipt's limitation), and historical evidence;SOTA sources
Read 2026-10-11 at 02:08Z unless noted. The decision record lists each page's byte count or sha256.
addr-spec = local-part "@" domain, and 3.2.3atext(rfc5322.txt:679-690) anddot-atom(:695): https://www.rfc-editor.org/rfc/rfc5322.txt.test,.example,.invalid,.localhostand exactlyexample.com/.net/.org(rfc2606.txt:106-121): https://www.rfc-editor.org/rfc/rfc2606.txt7d560edd...): https://www.rfc-editor.org/rfc/rfc5321.txtcbc944f4):Lib/email/_header_value_parser.py:1219,:1360,:1486,:1682(a symbol-only local part is a valid dot-atom) andLib/email/headerregistry.py:50,:53,:62,:100(the local part kept and compared as written)xn--and at most 59 more characters (read 14:22Z): https://www.rfc-editor.org/rfc/rfc5890.txtXN--label, 151 of themXN--,TARGETat line 1111 (read 14:22Z): https://data.iana.org/TLD/tlds-alpha-by-domain.txt[email protected]form: https://docs.github.com/en/account-and-profile/reference/email-addresses-referencegituser: https://docs.github.com/en/authentication/connecting-to-github-with-ssh/testing-your-ssh-connection@path/to/import: https://code.claude.com/docs/en/memoryre(installed 3.13.16), fixed-width lookbehind andIGNORECASE: https://docs.python.org/3.13/library/re.htmlEvidence-class table
validate.py: 0 email findings at308033f6, 1 on that file at this head (address not printed)tests/test_validate.pyandtests/test_native_token_ci.pyon308033f6: 7 failures, 6 errorsgit@exemption, no or unbounded A-label, raw text deciding the decoded receipt scan, the sanitizer's email copy drifting or skipped)adc2adb2, 533 matches in 106 files, 454 in 105 outside the registry (at51f45b5d: 532 and 454)e0862d8dis fixede0862d8d's code (rc 1) and pass at51f45b5d(rc 0): the annotated address and the chainedexampleand.targetnames in the rule's cases; the 15 gluedatextcharacters in the rule, the waiver andvalidate(); the registry-only orphan; the whole-token redaction; no published digest confirming a removed addressPRIVATE_CONTENTor the manifest: 1225 tests OK (4 skips) before the rebase; 337 OK after it51f45b5dis fixed51f45b5d's code (3 failures, 6 errors) and pass at4c916e14: a JSON-escaped address, nested too, in a command's output, an artifact and an MCP answer; a local part's case variant in the waiver,validate(), the dead-entry check and the loader4c916e14is fixed4c916e14's code (16 failures) and pass atadc2adb2: a JSON line holding U+2028, U+2029 or U+0085, alone and among other lines; five to seven levels of nesting in values and keys, throughpublish(), an artifact and a command6aaa204ais fixed6aaa204a's code (46 failures, 4 errors) and pass at167eb00a; 14 of 14 weakened copies fail them; census A (26native_cli_e2efiles, 1,518 strings) unchanged, B (198 files, 4,867 strings) none refused, at both heads167eb00ais fixed167eb00a's code (6 failures, 23 errors) and pass at171b0811; 13 of 13 weakened copies fail them; census A and B unchanged at both heads; every string of the 6 committed receiptsRunwrote passes the flush check171b0811are answered171b0811's code (4 failures) and pass at6ba4206a; 8 of 8 weakened copies fail them; the decoder is handed 1.92 times the length of the control dump at 60,002 to 960,002 characters (1,251 to 20,001 times at171b0811)python3 scripts/validate.pyatadc2adb2: passed, 11744 hashed filesLocal commands run
At
6ba4206a(on main2a330b0a; it merges cleanly with maind8f7e2051):At
171b0811(on main2a330b0a; it merges cleanly with maind8f7e2051):At
167eb00a(on main2a330b0a; it merges cleanly with main66584736c):At
6aaa204a(on main2a330b0a; it merges cleanly with main83cec12ee):At
adc2adb2(on main2a330b0a):Decision record
docs/decisions/2026-10-11-email-address-publication-rule.mdcovers the rule, its exemptions, the reviewed matches, Codex's review, both reads ate0862d8d, the GPT delta read at51f45b5d, the command center's micro at4c916e14and its addenda 01 to 06 (the reads at6aaa204a, the Claude micro at167eb00aand both reads at171b0811). Consequences: the sweep,convert.py,bundle.pyandcounter_readings.pyapply the rule, andnative_token_ci.pyredacts it. #991 adds a test that fails whenvalidate.pyhas a rule the PR-body gate does not carry. #991 lands first, and this PR's landing rebase carries the email rule into the gate. To undo, revert the commits.Host evidence
Not applicable: no receipt changes.
Checklist
version comment (no floating tags). (none changed)
permissions: {}and grant each jobonly what it needs (
contents: read, or an explicitly justified addition). (none changed)added without a documented owner.
moved or overwritten).
🤖 Generated with Claude Code