Repository navigation
setup-project.py's run and the assembly root's bootstrap.py _run start Python children with no environment, so a cp1252 stdout gets two encodings from setup and a name it cannot hold ends make bootstrap in validate-manifest.py #836
Description
Goal: every Python child that setup-project.py and the assembly root's bootstrap.py start writes in the encoding the parent's own stdout has, so a stdout that cannot hold a character ends in neither a stream of two encodings nor a traceback in a validator.
Filed by lane openRepoShape-5, from the checklist candidates of #759's PR (#808, writer w759: candidates 1 to 3, and the finding its body measures), and from lane 2's review of that PR (rv808, SHOULD 1: "there are six Python children, not five"). #759 itself (the guards) is not reopened: it holds the setup-project.py row as a strict xfail (NOT_YET) and changes no tool. The first two lines below were measured by w759 and are not re-run here; the third is read from source.
Lands after: lane 5's #759 (PR #808, fix/759-quoting-launcher-guards, head 84ce67c9), which adds the LAUNCHERS rows and NOT_YET (tests/test_validator_child_encoding.py:616 and :628 at that head). The assembly bootstrap.py is also edited by #764 (PR #815) and #710 (PR #824); whichever lands second re-gates against the others. path:line is at main 6626a3c5.
Lines
-
setup-project.pyreconfigures its own stdout to UTF-8 and gives each Python child an environment of its own, so the run writes two encodings (w759, measured).mainreconfigures both streams to UTF-8 (setup-project.py:2788-2791), andrun(:269-300) callssubprocess.runwith noenv=(:288-291), so a child inherits the stdout and writes in the code page its environment gives. Six children start throughrun: the re-run of itself in a checkout (:696), the naming validator (:2198), the plan (:2330) and its re-run for the diagnosis (:2312, captured), the scaffold (:2395) andscripts/bootstrap.py(:2570). tests/: the quoting-sentence inventory sees a quoter handed on as a value and pins each function's host-dialect calls, and the launcher guard readsrun_childandrun([PYTHON, ...]), with rows forcli.pyandsetup-project.py(#759) #808's body measuredsetup-project.py --project Sample --yes --org demoorg --local-remote-dir <dir> --into <dir>with aghstub: underPYTHONIOENCODING=cp1252:strictand undercp1252:surrogateescape, exit 0 and 9 of 129 stdout lines that are not valid UTF-8 (the naming validator's threefamily ...rows with their em dash, the scaffold's fourshape ...,specandcodelines with their ellipsis, and two lines of the validatorsbootstrap.pyruns); underutf-8:strict, none. Nothing raises, sincescaffold-project.pyandscripts/bootstrap.pyescape what their stdout cannot hold; the stream is two encodings. rv808 found the sixth child (:2312at its base,again = run(plan, ...)), which tests/: the quoting-sentence inventory sees a quoter handed on as a value and pins each function's host-dialect calls, and the launcher guard readsrun_childandrun([PYTHON, ...]), with rows forcli.pyandsetup-project.py(#759) #808's guard cannot see, becauseplanis a parameter. Direction:runtakes the environment:inherited_child_environmentfor a child that inherits stdout (soPYTHONIOENCODINGis the stream's own encoding, after the reconfigure, and a handler that never raises) andcaptured_child_environmentforcapture=True, whichrundecodes as UTF-8 (scripts/shape_materialize.py:266and:286;bump-leg.py,update-shape.pyandfamily.pyalready pass them).setup-project.pycannot import them: it runs before there is a checkout to import from (w759), so it holds a local copy of each. Then take thesetup-project.pyrow out ofNOT_YET; rv808 asked that the guard read theenv=of the function a tool defines as its starter (def run), so a fix insiderunflips the row and:2312is covered. - The assembly root's
bootstrap.pystarts the validators with no environment, so a project name the code page cannot hold endsmake bootstrap(w759, measured: it is bump-leg.py (a real bump) and update-shape.pyapplyrun the project's own validators as children that cannot print its display name on a cp1252 stdout, so a project named日本is refused and rolled back asbump-leg-validators-redorupdate-validators-redunderPYTHONIOENCODING=cp1252:surrogateescape, and the refusal blames the validators #254's defect in a pinned copy)._run(templates/assembly-root/scripts/bootstrap.py:143-150, its one caller at:1088) callssubprocess.run([sys.executable, *args], cwd=str(root), check=False); the parent has calledescape_unencodable_stdout()(:1096), the child has not. w759 scaffolded a project locally, setproject.yamlname:to two CJK characters, and ranPYTHONIOENCODING=cp1252:strict python3 scripts/bootstrap.py:validate-manifest.pyraisedUnicodeEncodeErroratprint(f"manifest ok: {manifest.get('name')} ...")(templates/assembly-root/scripts/validate-manifest.py:1037at main) and bootstrap endedbootstrap FAILED: manifest (exit 1), exit 1. Withutf-8:strictit printedmanifest ok: <name> (sample), 3 legsand exited 0.make bootstrapandsetup-project.pystep (7) both run it. Direction:_runpassesenv=built as the line above says.scripts/shape_materialize.pyis not inCOPIED_VERBATIM, so a project does not carry it; both roots'bootstrap.pyimport their shared code fromscripts/repo_shape.py(templates/family-root/scripts/siblings.py:183-189), so the helper moves there, or the script holds a copy. Either is a shape bump: the copy ofbootstrap.pyis re-pinned in every project, andrepo_shape.pyis too if the helper moves. Bundle it with bootstrap.py (the assembly root's): aSHAPE_UPSTREAM_PATHdirectory nobody may enter ends in a traceback, a validator's refusal is reported asFAILED, exit 1, a reftable leg's cherry-pick goes unseen, anddocs/cli.mdsays bootstrap places each leg AT the pin with no condition #764's and bootstrap.py and repo_shape.py: a leg on a branch whose name is not UTF-8 makes every latermake bootstrapend in a UnicodeDecodeError, and a character the filesystem encoding lacks still ends step (a) in a traceback #710's edits to the same file. - The guard has no row for the two pinned scripts, and the family root's
siblings.pystarts a child the same way, with no defect found.LAUNCHERS(tests/test_validator_child_encoding.py:611at main) lists tools by file; the survey tests/: the quoting-sentence inventory sees a quoter handed on as a value and pins each function's host-dialect calls, and the launcher guard readsrun_childandrun([PYTHON, ...]), with rows forcli.pyandsetup-project.py(#759) #808 made with its newinterpreter_runsover every shipped file found the assemblybootstrap.pyandtemplates/family-root/scripts/siblings.py(place,:567-568:subprocess.run([sys.executable or PYTHON, str(script)], cwd=str(path), check=False), the member's ownbootstrap.py) with no environment, and no row for either, because both are pinned copies. Forsiblings.pythe parent callsescape_unencodable_stdout()(:1351) and the child is the member's own bootstrap, which does too, so the two agree on the code page; nothing was measured to fail there. The survey also listedshape-doctor.py:1697(the doctor setsPYTHONIOENCODINGfor its own children) andsrc/openreposhape/standard.py:560(a digest program with captured output and anenvhanded in), which need nothing. Direction: once the second line lands, add the assemblybootstrap.pyrow; addsiblings.py's row only if the guard can read a pinned copy without making the next edit to it a re-pin of the test. If it can, say what the row pins (that the child inherits an environment the code page agrees with), as thecli.pyrow does.
Verify: tests/test_validator_child_encoding.py, tests/test_setup_project_py.py, tests/test_setup_sh.py, tests/test_bootstrap_without_git.py, tests/test_bootstrap_after_bump.py and tests/test_repo_hygiene.py pass on Linux. For the first line, #808's probe as a test: setup-project.py --yes --local-remote-dir under PYTHONIOENCODING=cp1252:strict and cp1252:surrogateescape, a gh stub first on PATH, asserting exit 0 and that every stdout line decodes as the encoding the parent wrote (none is "not valid UTF-8" where the parent says UTF-8), and the NOT_YET row removed. For the second, a scaffolded project whose name: the code page cannot hold, run under cp1252:strict: exit 0 and manifest ok, with the validator's name printed through the escape. One mutant per env= put back to nothing, each caught. No Windows run (CI is Linux only, #597): the code page is emulated with PYTHONIOENCODING, as #254's and #808's tests do.
Overlaps. #808 edits tests/test_validator_child_encoding.py. #756, #811 and #821 edit setup-project.py (#811 the name checks near :350-470, #821 the Git Bash dialect), far from run and the children. #815 and #824 edit the assembly bootstrap.py; #772, #775, #788 and #794 (lane 3's siblings.py and doctor branches) edit templates/family-root/scripts/siblings.py. #817, #821, #823 and #827 edit scripts/repo_shape.py, which a moved helper would also edit. The assembly bootstrap.py and repo_shape.py are pinned copies in every project.
Parent: #459.
Milestone: v1.1.1. Each case needs a stdout whose code page cannot hold a character the run prints (Windows' ANSI page on a redirected stdout): the stream is mixed, or make bootstrap refuses a project that is fine. Nothing is lost, #254's fix covers bump-leg.py and update-shape.py apply, and v1.0.0 shipped with it as v1.1.0 will.
🤖 Generated with Claude Code
Activity
brettheap commentedon Oct 17, 2026
CLAIMED — lane openRepoShape-2, session 81650e29-c906-4fe8-96c9-55580bfe0c44@Eagle, 2026-10-17T17:12:27Z, for #836
Logged in
lanes/log/openRepoShape-2.mdat26916efbf103209c95be8c0aacdaeb74e86ceb8c.The three reads (lane-collision-protocol Rule 1):