Repository navigation
validate-repository-naming.py: every name the gate tables or refuses prints through spelled(), so a name with a line feed or an ESC byte is one row and one naming-unclassified name, with its column worked out from the spelled form (#1267) - #1271
brettheap wants to merge 2 commits into
Conversation
…prints through `spelled()`, so a name with a line feed or an ESC byte is one row and one `naming-unclassified` name, with its column worked out from the spelled form (#1267) With #1266 (#1202) a name that ends in a line feed is refused `naming-unclassified`, but the gate printed it raw: the table row split in two, and `FINDING naming-unclassified: Atlas` was followed by a blank line, which reads as the finding for a valid `Atlas`. A name with an inner line feed split the same way and an ESC byte reached the terminal. Ten print sites now spell the name (`repo_shape.spelled`, in every tag since v1.0.0, so the import is not skewed): the plain listing, whose label column is worked out from the spelled name; the `--explain` NO FAMILY and classified headers, CLAIM note, WARNING and OVERLAP lines; the stderr `WARNING <name>: <sentence>` line; the role finding and the broken-chain finding of `--project`, whose sentence quotes the name, so the whole line is spelled; and the `naming-unclassified` finding. No code, flag or exit status changes, and a name without a control prints byte for byte as it did. tests/test_naming_gate_spelled.py holds the rows (31 of 32 fail on #1266's head). tests/test_naming_policy_line_break.py's two rows that asserted the raw finding now assert the spelled one. Closes #1267 Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reviewer's GuideUISpell repository names in naming-gate output, including complete sentences that quote them, and pad listing rows using the spelled form so control characters cannot split rows or findings.
TestsAdd coverage for spelled names across listing, explain, warning, and finding output, and update line-break tests to expect the escaped finding form.
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-5, 2026-11-08T19:44Z) Independent review of #1271 (#1267) by rv1271, lane 5's opus reviewer, which wrote none of it: READY at Independent review of #1271 (#1267) for lane openRepoShape-5, by rv1271 (opus), at 4b1653d: READY There are no MUST or SHOULD findings. The ten print sites are the right set, and every count the body gives reproduces: rows, mutants, gates and skips. There are 6 NITs, none blocking. CI's Scope: one commit, NITs
What held (measured)
GatesEvery run went through
The predicted-merge set is the core files, the hygiene five, Merge-trees
The body's claims and the commit
My slips
Where everything isEverything is in
I made no commits, pushes or GitHub writes. Lane: openreposhape-5 (openRepoShape-5) |
…cstring says every name the gate classifies or refuses is spelled and a sentence that quotes one is spelled whole, and rows with a CR and a backslash pin `spelled` itself (#1267) Review round on #1271 (rv1271, NITs 1 and 2). NIT 1. The module docstring said a chain link and a caller's words were "printed as given" in one paragraph, and that every repository name was spelled in the next. A sentence that quotes a name is spelled whole (a WARNING, a broken-chain finding, an `--explain` line), so a chain link or a caller's `--link-remedy` words inside it are spelled too, while the CHAIN line, `link-source-unreadable` and the NOTEs print a chain link as given. The docstring now says that and nothing else in the script changes: its syntax tree outside the docstring is equal to 4b1653d's. NIT 2. No row pinned that the gate calls `spelled` and not another escaper: one that escaped only a line feed and an ESC, or one built on `repr`, passed every row. A name ending in a CR (spelled `\r`) and a name holding a backslash and a line feed (a backslash prints as it is, so it is not doubled) are now asked of the finding and the table on the shipped contract: the first fails under the narrow escaper and the second under `repr`. Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
(lane openRepoShape-5, 2026-11-08T20:47Z) Delta check of #1271's NIT round, Delta check of #1271 (#1267) for lane openRepoShape-5, by rv1271b (sonnet), 4b1653d..4fa9f78: READY There are no MUST or SHOULD findings. All six checks hold at Findings
What I checked
GatesAll through
The 10 files hold no skip. The skips named in NIT 4 come from two other files, which I ran separately on both gits:
I did not re-run the first commit's 37-file gate or the full suite. I ran no Windows or macOS leg. Merge-treesRun with
For #1236, I built the body's resolution, My slips
Where everything isEverything is under Lane: openreposhape-5 (openRepoShape-5) |



Goal
#1267:
validate-repository-naming.pyprints every name it tables or refuses throughrepo_shape.spelled(), so a name that holds a line feed or another control byte stays on one line and in one table row, and the finding forAtlasplus a line feed cannot read as the finding for a validAtlas.This PR is stacked on #1266 (#1202) and lands after #1266's squash, by a restack. Its base is
fix/1202-naming-fullmatch(#1266's head09697e9c5), not main. It is a DRAFT. The change is two commits on top of09697e9c5,4b1653d8band the review round4fa9f78a6(see "Review round"): 3 files, +387 and -19 (git diff --shortstat 09697e9c5..HEAD):scripts/validate-repository-naming.py(+28 -15), the newtests/test_naming_gate_spelled.py(+350, 36 rows), andtests/test_naming_policy_line_break.py(+9 -4: two of #1266's rows now assert the spelled form, see "What changed").It is the follow-up lane 5's independent review of #1266 filed as #1267 (NIT 2, #1266 issuecomment-6724498649). #1266 makes
Atlasplus a line feed a name of no family; this one is about how the gate then prints that name.Review round (rv1271: six NITs)
rv1271 (opus, independent) found this PR READY at
4b1653d8bwith six NITs, none blocking (#1271 issuecomment-6726218829). The round is one commit on it,4fa9f78a6: 2 files, +39 and -20 (scripts/validate-repository-naming.py+13 -10,tests/test_naming_gate_spelled.py+26 -10). NITs 1 to 5 are done. NIT 6 is left for the restack.4b1653d8b's, proved withast). It now says one thing: every name the gate classifies or refuses is spelled, and a sentence that quotes one is spelled whole, so a chain link or a caller's--link-remedywords inside it are spelled too, while theCHAINline,link-source-unreadableand the NOTEs print a chain link as given. The old "printed as given" lines no longer list a chain link or the words of a remedyrepr(value)[1:-1])run10.sh's-rs, then the gate's-rf: pytest keeps only the last-r)closingIssuesReferencesis[]while the base is #1266's branchNIT 1, measured. At the round's head, with a chain link
openX<ESC>doxand--link-remedy 'openXdox=run<TAB>make <ESC>[31mfetch'(logs/docstring-claims-head.log,logs/note-claims-head.log): the stderrWARNING <name>: <sentence>and--explain'sWARNINGspell the link (openX\x1bdox) and the remedy words (run\tmake \x1b[31mfetch);--explain'sCHAINline, thelink-source-unreadableWARNING (could not answer for openX<ESC>dox) and the NOTEs that name a link (no name classified here reads openX<ESC>dox's own declaration,links: openX<ESC>dox, openDox) print the link with its ESC byte. The values of--link-sourceand--link-remedygo through_shown, as the docstring's paragraph above the new one says. The "Limits" bullet on chain links still holds as written; the remedy words are the half it did not name.NIT 2, the rows.
SPELLED_ITSELFintests/test_naming_gate_spelled.pyadds two names to the finding row and the table row on the shipped contract (the file is 36 rows now). The expected spellings are written out by hand, as the file's others are:Atlasplus a CR (ends-in-cr)Atlas\rAt\lasplus a line feed (backslash)At\las\nspelledprints it as it is, soreprdoubles it (and an escaper of every non-ASCII character does too); the line feed beside it keeps the row failing on the baseA ZWJ was not used for the table row: lane 3's #1217 pads by terminal columns and a ZWJ (category Cf) takes none, so its row would change on the restack (
ZERO_COLUMN_CATEGORIESat352dcbc96). A backslash is ASCII and one column.tests/test_naming_gate_spelled.py36 passed,logs/new-file-head.log).09697e9c5's script): the 4 fail, the base prints the CR raw and splits the backslash name's row and finding at its line feed (logs/run-base.log,logs/new-names.log). On main (dff8fd4ba): the same 4 fail (logs/run-main-gate-spelled.log).spelledreplaced by a local escaper of a line feed and an ESC): 2 failed, the finding row and the table row ofends-in-cr; the other 40 of the 42 core tests pass. Under X3 (repr(str(value))[1:-1]): 2 failed, the finding row and the table row ofbackslash; the other 40 pass. Each applied alone to the script inn1267-mutand restored from git,restored x1-narrow sha256 ok,restored x3-repr-unquoted sha256 ok(logs/mutants-core.out,logs/mut-core-x1-narrow.log,logs/mut-core-x3-repr-unquoted.log). No other row fails under either mutant.What held at #1266's head
Measured at
09697e9c5(Python 3.12.3, git 2.43.0): the gate printed a name raw.validate-repository-naming.pygivenAtlasplus a line feed: exit 1, stdout' Atlas'and then' NO FAMILY'on the next line (the row split in two), stderrFINDING naming-unclassified: Atlasand then a blank line, which reads exactly as the finding for a validAtlas.At, a line feed,las: the same split.At, ESC,[31mlas: the ESC byte reached the terminal in the row and in the finding.Atlas-specplus a carriage return) printed the same way, the CR raw in the row and in the finding.On main (without #1266)
Atlasplus a line feed is not refused at all: it exits 0, classified asAtlas, with its row split in two (#1266's body has that measurement).The print sites
Every
printof the script, and every string it builds from a name, at the base (09697e9c5), with what became of it. Ten changed:_print_classification(1297)<name padded to 32> <label>_no_family_lines(791)--explain's header for a name of no family_describe(955)--explain's header for a classified name_referent_claim_note(831)[a CLAIM: needs a declared pin on openRouter or openxRouter ...], the referents derived from the name_referent_lines(935)--explain'sWARNING <sentence>; the sentence quotes the name_referent_lines(937)--explain'sOVERLAP <reason>; a broken chain's reason quotes the name_classify_targets(1321)WARNING <name>: <sentence>; the sentence quotes the name again_leg_target(1112)FINDING <name>: declared role 'code' but the name is the 'assembly' form ..._leg_target(1119)FINDING <name>: <broken chain's reason>; the reason quotes the name_emit_reports(1358)FINDING naming-unclassified: <name>, <name>Sites 1 to 4, 8 and 10 spell the name (1 pads the SPELLED name:
f" {spelled(name):<32} ...", so the width is worked out from the printed form). Sites 5, 6, 7 and 9 spell the WHOLE string: the sentenceresolve_referentbuilds says the name again (it ends at openQ, but <name> would need openDox or openxDox), so spelling only the prefix leaves the name raw in the same line. (A mutant that spells only the name, M7b and M9b, is killed.)Not changed, and why (each prints no repository name):
print(f"WARNING {warning}")(1347) andprint(f"FINDING {finding}")(1354),print("\n".join(_describe(...)))(1317): they emit the strings above, spelled where they are built.sources.unread_lines()andunused_lines()(WARNING and NOTE, 1350 and 1352): link-source values and paths through_shown, chain links as given, as the module docstring has said since Pinned shape copies: AGENTS-shape.md'sCONTRACTS_DIRsentence is false for every scaffolded root, and the chain WARNING offers remedies the scaffold lacks (one shape bump before the v1.1.0 tag) #610.print(str(exc))(1457, 1473),no_path_refusal()(1482),REFUSED naming-no-target(1500): the policy or manifest path and fixed words._pins_field_findings,_chain_field_findings,_legs_field_findings,_topic_finding, and_leg_target'smanifest-leganda leg has no repository:lines: values through!ror_shown.--explain'sCHAINline (a chain link is a neutral product's name, printed as given; a manifest's links are checked printable by_not_a_name, validate-repository-naming.py and validate-pins.py: a manifest'slegs:that is not a list ends in a TypeError or waits on stdin, a chain link that is not a name passes, and--explainends in a UnicodeEncodeError on a name that is not UTF-8 #714), the family rows (the policy's ids and patterns) and the policy's notes.What changed
scripts/validate-repository-naming.py:spelledis imported fromrepo_shapeand applied at the ten sites; the module docstring says so beside the_shownparagraph, and that a name with no control prints as it did.spelledis in every kept library:scripts/repo_shape.pyat v1.0.0, v1.0.1, v1.1.0, v1.1.1 and v1.2.0 each define it, with the same text (the sha256 of the AST source ofspelledand of its two constants,ESCAPED_CATEGORIESandESCAPED_BIDI_CONTROLS, is equal in all five and at the head), so the import is not skewed (tests/test_pinned_copy_text.py'sNEWER_THAN_A_KEPT_LIBRARYnames onlyshown_pathandhost_shell_dialect, and passes unchanged).naming-unclassifiedstays; only the name in its words is spelled. A name without a line feed, CR, ESC or other control prints as before (the plain-name row, and the 3,563 gate tests below), with one change in a ROW and none in the name: a name with a lone surrogate (Atl\udcffas, intest_legs_not_a_list.py) prints its own bytes as before (spelledwrites\udcff, as stderr'sbackslashreplacedid), but its label moved from character 40 to character 35, because the column is now worked out from the spelled name and so lines up with every other row's (measured on09697e9c5,4b1653d8band the round's head:logs/surrogate-label.log; rv1271's NIT 3).tests/test_naming_policy_line_break.py(scripts/repo_shape.py:NamingPolicyreads a name withfullmatchat four sites, so a name that ends in a line feed matches no family andvalidate-repository-naming.pyrefuses it, andrepository-naming.yamlsays a pattern is read whole (#1202) #1266's file), two rows updated:test_the_naming_gate_refuses_a_leg_repository_that_ends_in_a_line_feed(3 rows) andtest_the_naming_gate_refuses_an_argv_name_that_ends_in_a_line_feed(3 rows) assertedFINDING naming-unclassified: Atlas, the raw form (the line feed ended the first line). They assertFINDING naming-unclassified: Atlas\nnow (a backslash and ann), which is what the issue asks. The docstrings say so. No other row of the file changed.tests/,README.md,docs/(the handbook included) andAGENTS.mdfornaming-unclassified,NO FAMILY,declared role,also_matchesand the table's columns: README, docs and AGENTS name the command only, never a printed name with a control; the tests that pin the table or the finding use printable names (test_chain_link_fold.py,test_naming_policy.py,test_setup_project_name_edges.pywith its trailing space), whichspelledleaves alone, orAtl\udcffas(test_legs_not_a_list.py), whichspelledwrites as\udcffexactly as stderr'sbackslashreplacedid. The files that run the gate or read its output all pass (Runs).The rows
tests/test_naming_gate_spelled.py(new, 36 rows). The expected spellings are written out by hand (Atlas\n,At\nlas,At\x1b[31mlas,Atlas\r,At\las\n), not computed by the library under test.<spelled padded to 32> NO FAMILY, and--explain's header is<spelled>: NO FAMILYwith every later line indented; one run of all three plus a plainAtlasgives four rows with their labels in one column and one finding naming all three; a name on stdin (an ESC; stdin strips a line feed) is spelled too; and a control row, a run of five plain names, prints exactly as it did. Two more names pinspelleditself (rv1271's NIT 2; the finding row and the table row of each, 4 rows):Atlasplus a CR, whichspelledwrites\r(a gate that escaped only a line feed and an ESC prints it raw), andAt\lasplus a line feed, writtenAt\las\n(a backslash prints as it is;reprwould double it).NamingPolicyreads a name with.matchagainst patterns that end in$, so a name that ends in a line feed is classified as the name without it, andvalidate-repository-naming.pyexits 0 over such a leg repository (#1166 follow-up) #1202), so sites 3 to 9 print a name only when a fork's patterns take an ESC and a line feed. The rows read the shipped contract with its 12 character classes[A-Za-z0-9]widened (loosened()asserts the count), and ask: the listing row and the header of a classified name (3 + 3 rows), the claim note's derived referents (3), the stderr WARNING (2),--explain's WARNING and OVERLAP (2 + 2), the role finding (3) and the broken-chain finding (2). A descendant name ends in its product, which the direct pin reads, so the control is in its stem there (inner line feed and ESC); the other rows ask all three names.Failing on the base
The base is
09697e9c5's script (sha256fc5c9f81c953), swapped inton1267-mut(detached at the round's head4fa9f78a6, so the new file and the updated rows are the branch's) and restored from git with a sha256 check (0b6b187ee256, the branch's script;logs/run-base.log):tests/test_naming_gate_spelled.py: 35 failed, 1 passed. The one that passes is the plain-name control (it must: nothing about a plain name changed). The 35 fail because the base prints the raw bytes: the finding row ends its first line at the line feed (assert ['FINDING naming-unclassified: Atlas', '', ...] == [...]), the table row is' Atlas\n ... NO FAMILY\n'where' Atlas\\n ... NO FAMILY\n'is expected, the ESC rows carry the byte,--explain's header is' Atlas'and the stderr WARNING and the broken-chain finding carry the raw name twice. The 4 rows of the round fail the same way: the base prints the CR raw in the row and the finding, and splits the backslash name's row and finding at its line feed (run-base.log,logs/new-names.log).tests/test_naming_policy_line_break.py: the 6 updated rows fail (6 failed in this run; the file's other 71 rows passed when it was run whole with the first commit, and neither the file nor the base has changed), because the base printsAtlaswhere the spelledAtlas\nis now asserted.dff8fd4ba, fetched at 19:55Z, without scripts/repo_shape.py:NamingPolicyreads a name withfullmatchat four sites, so a name that ends in a line feed matches no family andvalidate-repository-naming.pyrefuses it, andrepository-naming.yamlsays a pattern is read whole (#1202) #1266), what each row does: the new file is 35 failed, 1 passed (logs/run-main-gate-spelled.log), the same 35 as on the base. Rows for the two names without a trailing line feed fail as above. Everyends-in-lfrow fails first on the exit code (assert 0 == 1): main classifiesAtlasplus a line feed asAtlas, exit 0, with its row split in two.tests/test_naming_policy_line_break.pyis 40 failed, 37 passed on main (measured at9729315f1with the first commit; it is scripts/repo_shape.py:NamingPolicyreads a name withfullmatchat four sites, so a name that ends in a line feed matches no family andvalidate-repository-naming.pyrefuses it, andrepository-naming.yamlsays a pattern is read whole (#1202) #1266's file and has not changed since), the number scripts/repo_shape.py:NamingPolicyreads a name withfullmatchat four sites, so a name that ends in a line feed matches no family andvalidate-repository-naming.pyrefuses it, andrepository-naming.yamlsays a pattern is read whole (#1202) #1266's own body gives. The PR needs scripts/repo_shape.py:NamingPolicyreads a name withfullmatchat four sites, so a name that ends in a line feed matches no family andvalidate-repository-naming.pyrefuses it, andrepository-naming.yamlsays a pattern is read whole (#1202) #1266, as its base says.Mutants
Each applied alone to the branch's
scripts/validate-repository-naming.pyinn1267-mut(detached at the round's head4fa9f78a6), run againsttests/test_naming_gate_spelled.py(36 rows) and the 6 updated rows oftests/test_naming_policy_line_break.py(42 tests; the first commit's run over the whole two files, 109 rows, gave the same counts), and restored from git with a sha256 check (restored ... sha256 okafter each, and the script's bytes equal the branch's at the end;logs/mutants-core.out):{name:<32})32 - len(name))--explainNO FAMILY rows--explainWARNING rows)--explainWARNING)--explainWARNING rows--explainOVERLAP)", ".join(unclassified))spelledreplaced byreprspelledis a local escaper of a line feed and an ESC onlyends-in-cr(round)spelledisrepr(str(value))[1:-1]backslash(round)rv1271's other mutants are killed too, with the round's rows: X2 (escape every non-ASCII character; the backslash rows kill it as well) 10, X4 (spell the whole
naming-unclassifiedblock, its line breaks too) 13, X5 (site 9 spells the reason and not the name) 2, X6 (site 7 spells the sentence and not the name) 2, X7 (the claim note spells its first referent only) 3, X8 (pad the raw name, then spell it) 15.X1 and X3 survived every row of the first commit (rv1271's NIT 2); the round's 4 rows kill them, and no other row does. No survivor.
Runs
Python 3.12.3 (CI runs 3.14; the rows assert lists and lines, nothing that depends on a
repror anast.dump), through lane 5'srun10.sh(gh stub first on PATH, bogus token, emptyGH_CONFIG_DIR, blank git identity,GIT_SSH_COMMAND=false,GIT_CONFIG_GLOBAL=/dev/null), git-filter-repo 2.47.0 (CI's pin;git filter-repo --versionprintsa40bce548d2c), at head4b1653d8b, dirty=0. The gate list is the 37 test files that namevalidate-repository-namingor pin its printed forms (grep -l), this PR's three,tests/test_naming_policy.py, and the hygiene five (test_path_guard_hygiene,test_repo_hygiene,test_agents_md_codes,test_cli_reference,test_pinned_copy_text): 3,567 tests in 28 runs, none of more than 100 tests beside another (one exception, below).tools/git247/bin/git, built from kernel.org's tarball)test_adopt_check_throwaway_repos.py:1041, below)tests/test_naming_gate_spelled.pyalone, before the committest_naming_policy, the hygiene five andtest_chain_link_fold, on the PREDICTED merge of current main (51a9c5f10) and the branch (merge-treeandcommit-tree: an object, no ref)The skips, by reason. rv1271 measured them. The first gate's logs hid them because
run10.shpasses-rsand the gate runs then passed-rf, and pytest keeps only the LAST-roption, so no reason was printed. On 2.43.0 the 4 skipped aretests/test_shape_doctor.py:10516x3 ("the PowerShell line is printed, and powershell.exe exists, only on a Windows host") andtests/test_adopt_check_throwaway_repos.py:482x1 ("needs a disk that ignores case:git initwrote nocore.ignorecase=truein the work directory, so that disk keeps case; the rows above write the setting into the throwaway repository by hand instead"). On 2.47.1 the 7 are those 4 andtests/test_adopt_check_throwaway_repos.py:1041x3 ("this git'sgit clonetakesinit.defaultBranch = a..b, as 2.55's does: the split clones there"); 2.43.0's clone does not take it, so its rows run. I ran those two files again at the round's head with-rsalone, on both gits, and the same lines print (logs/skips-243.log,logs/skips-2471.log). A slip in the load rule (first gate): the 106-test run of the #1236 check below ran while the gate's slot A held a run of more than 100 tests; it passed.The review round's runs. Head
4fa9f78a6, dirty=0, Python 3.12.3, git-filter-repo 2.47.0 (a40bce548d2c), throughrun10.shwith its-rsand no other-r, so a skip would name its reason. The files are the brief's ten: the new file,test_naming_policy_line_break,test_naming_policy,test_chain_link_fold,test_legs_not_a_listand the hygiene five (test_path_guard_hygiene,test_repo_hygiene,test_agents_md_codes,test_cli_reference,test_pinned_copy_text): 885 tests in 8 runs per git, none of more than 100 tests beside another./usr/bin/git)tools/git247/bin/git)tests/test_naming_gate_spelled.pyalone, before the round's commitLogs:
gate/gate-243-*.log,gate/gate-2471-*.log,gate/slotA.out,gate/slotB.out. The round changes the script's docstring and the new test file only, so the first commit's 37-file gate above stands for the rest. A slip in the load rule: the resolved-#1236 check (Merges) ran its 154-test chunk while the skip re-run above held a 152-test file; both passed. Not run: the full suite, a real Windows host and macOS.Merges
git merge-tree --write-tree <other> 4fa9f78a6(git 2.43.0) at 2026-11-08T20:07Z, against current main (fetched fresh at 20:07Z), #1266's head and every open PR that touches one of this PR's three files (47 open when listed at 19:56Z; only #1266 and #1236 do;open-prs.json,logs/merge-trees-2.txt; the first read at 19:56Z,logs/merge-trees.txt, gave the same trees for #1266 and #1236 and maindff8fd4baclean at27441e98):40cb8086fb6cfa9c6(clean)tests/test_naming_policy_line_break.py; this PR's parent)09697e9c5264fe0fb, the branch's own tree: the head descends from itscripts/validate-repository-naming.py, lane 3's #1217 display-width pad)352dcbc967c6acc90; stage blobs4f3d7977(base),ff33b440(this branch),4bfafa63(#1236)Main moved since the first commit (
51a9c5f10to40cb8086f); none of its moves touchesscripts/validate-repository-naming.py,scripts/repo_shape.pyor the two test files (git log 4b1653d8b..origin/mainover them is empty). #1236 (352dcbc96) and #1266 (09697e9c5) have not moved, read again at 20:07Z.#1236 still conflicts at
_print_classification'sprintline, and only there: one conflict marker pair in the merged file (lines 1386 to 1392), and the round's docstring reword merges clean with #1236's changes. Whichever lands second takes the refresh. #1236 pads by the columns a terminal gives the name as stdout writes it (_padded(_printed(name), 32)); this PR spells the name ({spelled(name):<32}). The resolution composes them, spell first and then pad what is printed:print(f" {_padded(_printed(spelled(name)), 32)} {label}{also}"). I rebuilt it as a commit object with no ref (2ad9b0832: the branch and #1236's head, theprintline resolved as above) inn1267-mutand ran, on it, this PR's new file withtest_chain_link_fold.py(36 + 19 = 55 tests), #1236'stest_pinned_copy_columns.py(55) andtest_siblings_family_name.py(154), andtest_naming_policy_line_break.py(77): 341 passed in 4 runs (logs/run-resolved-1236-a.logto-d.log). The other hunks of #1236 merge clean. The mut tree was put back at4fa9f78a6, clean, its script's sha256 equal to the branch's.Limits
[A-Za-z0-9]and hyphens, so on the shipped data a name with a control is never classified and only sites 1, 2 and 10 print it. The other sites are spelled for the same reason the first three are (one rule: a repository name the gate prints is spelled), and their rows read a fork's contract. No shipped project sees them change.--pinsvalues are printed as given (the module docstring's standing rule; a manifest's are checked printable, a command line's are the caller's words). So--explain'sCHAINline prints a link raw, while the four sentences that quote it (sites 5, 6, 7, 9) are spelled whole and so escape a control in a link too. Not changed here.role:prints raw in--explain's[ADMITS ...; <role> is declared here and is not admitted]note (--roleis a closed choice; a manifest's value is not). It is a role, not a repository name, so it is outside validate-repository-naming.py: a name that holds a line feed or another control byte is printed raw, in the table and innaming-unclassified, so the finding forAtlasplus a line feed reads like one for a validAtlas#1267. Found, not fixed.validate-repository-naming.pyreaches this throughupdate-shape.py; it needsspelledin itsscripts/repo_shape.py, which every kept library has.shape-doctor.pyand the setup, scaffold and adopt tools run the gate and quote its lines; they print what it prints, so a name with a control is spelled in their rows too. Their tests are in the gate list above.Closes #1267
Lane: openreposhape-5 (openRepoShape-5)
🤖 Generated with Claude Code
Summary by Sourcery
Render every repository name reported by the naming gate through its escaped representation so control characters cannot split output rows or make findings ambiguous.
Bug Fixes:
Enhancements:
Documentation:
Tests: