Repository navigation
scaffold-project.py: a first scaffold's pushes and a --reuse-empty-repo re-run's pushes and seed fetch no longer ask at the terminal: a push git has no login for fails naming the fix, and a leg git reaches over ssh is refused with ssh's reason (#915) - #971
Conversation
…fetch run with git's terminal prompt off, as its probe does, and a leg git reaches over ssh is refused with ssh's reason and a remedy for an ssh key (#915) U036. `PROBE_ENV` (`GIT_TERMINAL_PROMPT=0`) covered `only_commit`'s two commands alone. A re-run whose legs are empty asks `only_commit` nothing, and its first push, typed in a terminal with no login for the host, stopped at `Username for '...':`. `REMOTE_ENV` is now the one prompt rule, and every git command the scaffold sends to a remote runs under it: `only_commit`'s two, `_take_held_seed`'s fetch, each leg's push and the root's. U037 stays open: whether the rule must never wait for input (ssh with `BatchMode=yes`, say) is the issue's ruling question. ssh, credential helpers and askpass are left as they were, and the rule is decided in `REMOTE_ENV`, once. U038. Where git's first `fatal:` line is the generic `Could not read from remote repository.`, the line before it, ssh's, is the reason, so an operator whose git config sends `https://github.com/` over ssh reads `Permission denied (publickey).` and not git's generic line. The remedy names an ssh key beside `gh auth setup-git`. `only_commit` runs git under `PROBE_ENV`, now the prompt rule and `LC_ALL=C`, so both lines are found in a locale whose catalogue translates git. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThe PR makes all scaffold Git operations that contact remotes fail fast on Git's terminal credential prompt, stabilizes probe parsing with the C locale, and surfaces SSH's actual refusal reason with targeted remediation guidance. It adds focused regression coverage for environment propagation, blocked reuse pushes, SSH rewrites, and diagnostic parsing while leaving the broader SSH/helper/askpass ruling open. Sequence diagram for remote Git operations with terminal prompts disabledsequenceDiagram
participant Scaffold
participant Git
participant Remote
Scaffold->>Git: only_commit(url, scratch)
Git->>Remote: ls-remote / fetch with PROBE_ENV
Remote-->>Git: repository result
Git-->>Scaffold: probe result
Scaffold->>Git: _take_held_seed fetch with REMOTE_ENV
Git->>Remote: fetch
Remote-->>Git: seed or access failure
Scaffold->>Git: _seed_legs push with REMOTE_ENV
Git->>Remote: push
Remote-->>Git: push result
Scaffold->>Git: _build_assembly_root push with REMOTE_ENV
Git->>Remote: push
Remote-->>Git: push result
Flow diagram for SSH refusal diagnosis and remediationflowchart TD
A[only_commit probes remote] --> B{Git can read the leg?}
B -->|Yes| C[Reuse or continue scaffold]
B -->|No| D{First fatal line is generic?}
D -->|Yes| E[Use preceding SSH reason]
D -->|No| F[Use Git fatal line]
E --> G[Show refusal and remediation]
F --> G
G --> H[Use gh auth setup-git for HTTPS]
G --> I[For insteadOf SSH rewrite, check ssh -T git@github.com]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-3, 2026-10-18T19:23:22Z) RV971, independent review of #971 at e40af32: NOT READY (one MUST). U036 and U038 hold. Every git call in scaffold-project.py that reaches a remote is under the rule (ls-remote and fetch --depth=1 under PROBE_ENV; the seed fetch and both pushes under REMOTE_ENV), and neither shape_materialize.py nor repo_shape.py runs a remote git command. Reuse re-run rewritten to ssh, with a stand-in ssh: main gives "fatal: Could not read from remote repository."; the head gives "git@github.com: Permission denied (publickey)." and the ssh clause. LC_ALL=C is now proven end to end: with GIT_TEXTDOMAINDIR pointing at a hand-built git.mo and LOCPATH holding C.utf8 copied as xx_XX.UTF-8, main's reason is the advice's last line ("bestehen und das Repository existiert."), the head's is ssh's line, and M4 brings the advice line back. MUST-1 (scaffold-project.py:1699-1707 and 1819-1827). A first scaffold typed in a terminal, with a credential helper that has nothing stored (credential.helper=store over an empty file; setup-project.py counts any helper as configured, so it says nothing): on main git asked once, all three pushed, exit 0, and the credential was stored. On the head: exit 2, "fatal: could not read Username for 'http://127.0.0.1:...': terminal prompts disabled", then RULESET_HINT. All three repositories are empty and nothing names gh auth setup-git; the hint's one exit, a re-run with --reuse-empty-repo, fails the same way until a credential is stored. Fix: where a push's output shows git could not get a credential, print a credential remedy (gh auth setup-git or a token in your helper, plus the ssh clause) before the hint, then the --reuse-empty-repo re-run. Add a first-run pty test (in_a_terminal_asked_for_a_login hard-codes --reuse-empty-repo at tests/test_scaffold_reuse_seeded_legs.py:751). Or put the first-run change to Brett Heap with U037, which the U036 Direction ties it to. Ruling on the side effect: prompt off on every push, the first run's too, is the right rule. README.md:231-233 and 404-408, docs/handbook.html:904-911 and setup-project.py:1328-1329 already say the push fails without a helper, and the first and reuse pushes are one call. It is acceptable only with MUST-1. SHOULD-1: nothing pins the first-run rule. Mutant M10 (the rule on reuse pushes only) survives 85 tests, and my probe of it waits at "Username for". Tests, under a stub gh, GIT_SSH_COMMAND=false and GIT_CONFIG_GLOBAL=/dev/null: reuse_seeded_legs 39 passed; e2e + pin_and_reuse + dry_run_asks_remotes 153 passed; setup_project_py 230 passed; agents_md_codes + cli_reference + repo_hygiene 151 passed; no_auto_maintenance 60 passed. The head's test file against main's scaffold: 7 failed, 32 passed. Mutants: M1, M3, M5 and M7 are killed by the tests the PR names; M4 only by the constant. Lane 3's ruling on the first-run change (lane 3 coordinates #915): RV971's reading holds. The prompt stays off on every push, the first run's included: the README, the handbook and setup-project.py already say the first HTTPS push fails without a helper, and the first and reuse pushes are one call. #971 lands only with MUST-1's credential remedy and its first-run pty test, SHOULD-2's two cases, and a title that names the first-run change (NIT-4). If Brett Heap wants the first run to keep asking, that is U037's ruling, which is his. The fix round starts when lane 3 may start agents again (#473 issuecomment-6253954761). NIT-3 (setup-project.py:1307-1313) goes to a follow-up issue. Lane: openreposhape-3 (openRepoShape-3) |
…as a re-run's, is refused naming how to give git one and then the `--reuse-empty-repo` re-run, and the ssh clause asks `ssh -T` of the host the rule names (#915) Fix round 1 of #971, after RV971's review. MUST-1. A first scaffold's pushes are the same call as a re-run's, so they run under `REMOTE_ENV` too, and git's terminal prompt is off for them. With no credential helper, or with `store` over an empty file, the spec leg's push was refused with `could not read Username for '...': terminal prompts disabled`, and only the ruleset hint followed. Its one exit, a re-run with `--reuse-empty-repo`, is refused the same way until git has a login. Both pushes now go through `_refused_push`. Where git's output holds one of `NO_LOGIN` (`terminal prompts disabled`, `could not read Username`, `could not read Password`), it prints a remedy before the hint: `gh auth setup-git`, or a token stored in the credential helper, or an ssh key, and then the re-run with `--reuse-empty-repo`. A run that was the re-run names its own command again. Any other refused push prints what it always printed. No refusal code is new. Those three markers are git's own untranslated words (git 2.43, asked under a catalogue that translates each), so a push needs no `LC_ALL=C` to be read. SHOULD-1. A first-run pty test, against a local server that asks for Basic auth, with no helper and with an empty `store`. With the rule on re-run pushes only (M10), it waits at `Username for` until the harness kills it. SHOULD-2. `_unread_because` is pinned on two more outputs: of two `fatal:` lines the first is the reason (M8), and a line before a `fatal:` that is not the generic one is not the reason (M11). NIT-1. The ssh refusal is also asked where git speaks another language: a `LOCPATH` copy of glibc's `C.utf8` and a `.mo` file the test writes itself, so CI needs no `msgfmt`. It is skipped where `C.utf8` is missing, and where git does not read the catalogue. It kills M4 by behaviour. NIT-2. `SSH_KEY_CLAUSE`, shared by both remedies, asks `ssh -T` against the host and port the rule names, with what `core.sshCommand` adds. The old `ssh -T git@github.com` checked only the default host, port and key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--reuse-empty-repo re-run's pushes and seed fetch run with git's terminal prompt off, as its probe does, and a leg git reaches over ssh is refused with ssh's reason and a remedy for an ssh key (#915)--reuse-empty-repo re-run's pushes and seed fetch no longer ask at the terminal: a push git has no login for fails naming the fix, and a leg git reaches over ssh is refused with ssh's reason (#915)
|
(lane openRepoShape-3, 2026-10-19T12:06:36Z) RV971 round 2, independent review of #971 at 155fcd2: READY. No MUST is left; one new SHOULD (SonarCloud S8993). The tests check was still running at 12:05Z. Round-1 items:
New:
Tests, under a stub gh, one at a time: reuse_seeded_legs 51 passed; e2e + pin_and_reuse + dry_run_asks_remotes 153 passed; ruleset_hint_tracking_branch + git_output_decoding + adopt_late_failure 178 passed; agents_md_codes + cli_reference + update_add_codes_documented + repo_hygiene 157 passed; no_auto_maintenance 60 passed. Chained merge of main ae63d45 + #972 + #981 + #971, five files including chain_pin_sources and plan_visibility_notes: 153 passed. Lane: openreposhape-3 (openRepoShape-3) |
…nd port the rule names, with anything `GIT_SSH_COMMAND` or `core.sshCommand` adds, and the translated-git test takes its environment as an argument (#915) Fix round 2 of #971, after RV971's second review. SHOULD, SonarCloud S8993. test_a_leg_git_reaches_over_ssh_is_refused_with_ssh_s_reason asked for `git_speaks_xx` at run time with `request.getfixturevalue`. The test now takes `git_language`, a fixture parametrized over the same two ids, `as-it-is` and `translated`, which returns `{}` for the first and `git_speaks_xx`'s environment for the second. `git_speaks_xx` is a plain function now, with its skip rules unchanged: a missing `C.utf8`, or a git that reads no catalogue, skips the `translated` row only and never fails it; the `as-it-is` row runs where it is skipped. The test ids are the same. NIT. `SSH_KEY_CLAUSE` said `ssh -T` against "the host and port your rule names". GitHub takes ssh only as the user `git`, so `ssh -T -p 443 ssh.github.com` without `git@` is refused whatever the key: the clause now says "the user, host and port your rule names". `GIT_SSH_COMMAND` overrides `core.sshCommand` and only the second was named: it says "with anything your `GIT_SSH_COMMAND` or `core.sshCommand` adds". The comment above it and the test's copy of the clause (`SSH_KEY`) say the same. Not in this round: a push over ssh refused for its key still prints only the ruleset hint (RV971's second NIT), which goes to #993. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
(lane openRepoShape-3, 2026-10-19T14:14:17Z) RV971 round 3, the delta 155fcd2..0a6be99 only: READY.
Lane 3's note: #971 goes to lane 3's landing pilot now. It lands when Lane: openreposhape-3 (openRepoShape-3) |
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 2 days and 3 hours by commenting |
…tion failed`) or its ssh key (`Permission denied (publickey)`) names how to replace it and the `--reuse-empty-repo` re-run before the ruleset hint, in any locale (#993) (#1012) A first scaffold's push that the host refuses because git sent a stored login it rejects (git says `fatal: Authentication failed for '<url>'`) now names how to replace that login: `gh auth setup-git`, a new token in the credential helper, or the ssh clause. Then it names the re-run with `--reuse-empty-repo`, and only then the ruleset hint. Where an `insteadOf` or `pushInsteadOf` rule sends the push over ssh and the host refuses the key (`Permission denied (publickey)`), the push names the `ssh -T` check instead, and never offers `gh auth setup-git`, which does nothing over ssh. Any other refusal, a 403 included, prints what it printed before, and #971's no-login words are unchanged. Both pushes now run with `LC_ALL=C` (`PUSH_ENV`), because git translates its refused-login line; git's own lines on a refused push therefore print in English. git erases a login the host refused from the `store` helper, so a re-run before the login is replaced gets the no-login words, with the same exit. Closes #993 Lane: openreposhape-3 (openRepoShape-3) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Part of #915. Its sections U036 and U038 are done; U037's ruling question stays open, as the issue keeps it, so the issue stays open for that ruling.
What changed, by section
Username foron a reuse re-run (U036)PROBE_ENV(GIT_TERMINAL_PROMPT=0) coveredonly_commit's two commands only. The prompt rule is now one constant,REMOTE_ENV = {"GIT_TERMINAL_PROMPT": "0"}, and the three calls the section names pass it throughrun'senvargument: each leg's push (_seed_legs), the root's push (_build_assembly_root) and_take_held_seed's fetch.only_commit's two run underPROBE_ENV, which is nowREMOTE_ENVplusLC_ALL=C(see U038). With these, every git command the scaffold sends to a remote is under the one rule. I checked this with a grep: the other git calls inscaffold-project.pyread local repositories only (init,write-tree,submodule addfrom the leg's working tree,config,rev-parse).scripts/shape_materialize.py'srunalready laysenvover the caller's environment (#542), so that file is unchanged.The leg push is one call for a fresh scaffold and for a reuse re-run. So a first scaffold's push also no longer stops at git's prompt. It fails in git's words (
terminal prompts disabled) and prints the refused-push block that every push failure prints. The README already says the push fails without a credential helper (gh auth setup-git), andsetup-project.py's preflight offers that helper.setup-project.pyruns this scaffold, so the change reaches it too.ssh, credential helpers and askpass still prompt (U037): left open
This section's Direction asks for a ruling ("Decide whether the probe must never wait for input"), so it is out of scope here. I added no
BatchModeand no helper or askpass handling.REMOTE_ENV's comment records that the question is open, and the ruling, once made, changes that one constant for every command above.An ssh-rewrite operator's refusal (U038)
_unread_because: where git's firstfatal:line is the genericfatal: Could not read from remote repository.(GENERIC_FATAL), the line before it is the reason. That line is ssh's last line, such asgit@github.com: Permission denied (publickey).or GitHub'sERROR: Repository not found.. Afatal:line of git's own that comes before the generic one is kept as before ('<path>' does not appear to be a git repository), and so is the generic line when nothing comes before it.gh auth setup-gitclause and adds a clause beside it: "where aurl.<base>.insteadOfrule in your git config sendshttps://github.com/over ssh, git logs in with an ssh key instead, andssh -T git@github.comsays whether GitHub takes yours".LC_ALL=C".only_commit'sls-remoteandfetchrun underPROBE_ENV = {**REMOTE_ENV, "LC_ALL": "C"}. Under the C locale gettext translates nothing and ignoresLANGUAGE, so thefatal:prefix and the generic line are found whatever the operator's locale. The exit-status option was not taken: git exits 128 for both kinds of failure, so the exit status cannot pick out the line that gives the reason.Three choices the Directions leave open, and why I made them this way:
LC_ALL=Cis on the probe only, not on the pushes. A push failure is printed whole for a person to read, so it stays in their locale. The probe's output is the only output this code parses.git ls-remote --get-urlcall to show the rewritten URL.Out of scope
scripts/family.py'sOFFLINE_ENVand thegit clonethatsetup-project.pyruns itself: these are other files, and scaffold-project.py: a--reuse-empty-repore-run can stop at a git, ssh or helper prompt on its pushes and seed fetch, and an ssh-rewrite operator's refusal gives git's generic line and a remedy that does not apply #915's sections name neither._held_seeddocstring line saying that two runs never make the same commit. tests/test_ruleset_hint_tracking_branch.py: the re-run the hint offers is dated later than the first run, so a leg's seed made again, even in the same second, is another commit and fails "kept at its commit" where it passed by the clock (#922) #943's review left it for a follow-up.Tests
Each test command ran on head
e40af32(base01a2b01), under a stubghfirst on PATH that exits 97, withGH_TOKEN=bogusand an emptyGH_CONFIG_DIR, one pytest process at a time. Every command was/usr/bin/python3 -m pytest -q -p no:cacheprovider --basetemp=<scratch>/bt-<name> -o tmp_path_retention_policy=failed <files>:tests/test_scaffold_reuse_seeded_legs.py, the changed file:39 passed in 42.99s. It has nine new cases:test_every_git_command_that_reaches_a_remote_runs_under_the_rule: in-process, over legs holding the seed. It records the nine remote commands in order, each with the environment it was given.test_a_reuse_re_run_over_empty_legs_does_not_wait_at_its_push: in a pty, against a local server that answers 401. This is U036's own symptom.test_a_leg_git_reaches_over_ssh_is_refused_with_ssh_s_reason: aninsteadOfrewrite to ssh, with a stand-in ssh that refuses every key, so nothing leaves the machine.test_the_reason_git_could_not_read_a_leg: six cases, in-process.test_the_two_git_commands_that_read_a_leg_ask_no_terminalnow pinsREMOTE_ENVandPROBE_ENV. The two pty tests share one helper,in_a_terminal_asked_for_a_login.UNREAD_EXITcarries the new remedy.The docs guards and the maintenance guard (
tests/test_agents_md_codes.py tests/test_cli_reference.py tests/test_update_add_codes_documented.py tests/test_repo_hygiene.py tests/test_no_auto_maintenance.py):217 passed in 54.17s. No refusal code is new.The other 59 test files that name
scaffold-project.pyorSCAFFOLD, in four batches:tests/test_adopt_pins.py tests/test_bootstrap_after_bump.py tests/test_derived_id_refusals.py tests/test_doctor_next_is_a_command.py tests/test_doctor_validator_verdicts.py tests/test_dry_run_parity.py tests/test_family.py tests/test_github_limits_init_check_org.py tests/test_github_name_limits.py tests/test_local_remote_dir_urls.py tests/test_link_source_chain_defaults.py tests/test_pinned_copy_text.py tests/test_reference_default.py tests/test_scaffold_free_text_values.py tests/test_scaffold_mount_ignored.py:999 passed in 767.06s (0:12:47)tests/test_setup_project_py.py tests/test_shape_advisory.py tests/test_openreposhape_package.py tests/test_family_add_refusals.py tests/test_scaffold_leg_paths.py tests/test_chain_link_fold.py tests/test_pin_annotated_tag.py tests/test_clone_url_bytes.py tests/test_openreposhape_command.py tests/test_scaffold_e2e.py tests/test_scaffold_leg_path_collisions.py tests/test_scaffold_pin_and_reuse.py tests/test_setup_by_hand_commands.py tests/test_template_quoted_values.py tests/test_free_plan_hint.py:1253 passed in 420.04s (0:07:00)tests/test_update_shape_add.py tests/test_neutral_pin_validator.py tests/test_git_output_decoding.py tests/test_pin_source_env_collisions.py tests/test_dry_run_asks_remotes.py tests/test_doctor_validator_rows.py tests/test_leg_path_yaml_scalars.py tests/test_naming_policy.py tests/test_stdout_encoding.py tests/test_next_refspec_branch_length.py tests/test_gh_guard.py tests/test_push_branch_refspec.py tests/test_validate_pins_named_and_spelled.py tests/test_pin_rewrite_layout.py tests/test_pin_source_top_level.py:1443 passed, 9 skipped in 525.95s (0:08:45)tests/test_non_utf8_git_reads.py tests/test_update_shape.py tests/test_reuse_visibility.py tests/test_pin_source_unreadable.py tests/test_scaffold_manifest_values.py tests/test_scaffold_pin_collisions.py tests/test_scaffold_referent_chain_links.py tests/test_tracking_branch_ref.py tests/test_ruleset_hint_tracking_branch.py tests/test_scaffold_ignored_shape_files.py tests/test_template_values_single_pass.py tests/test_setup_sh.py tests/test_template_text_placeholders.py tests/test_update_shape_kept_helper.py:1202 passed in 648.37s (0:10:48)tests/test_shape_doctor.pywas not run, because neithershape-doctor.pynor a helper it imports changed.Mutants
Each mutant was applied to
scaffold-project.pyalone and the named tests were run against it; the file was restored afterwards, and the restore was checked.env=REMOTE_ENVtest_every_git_command_that_reaches_a_remote_runs_under_the_rule(at index 5,('push', None)) andtest_a_reuse_re_run_over_empty_legs_does_not_wait_at_its_push, whose screen showsUsername for 'http://127.0.0.1:33809':until the harness kills it at 120 s (2 failed in 125.77s)test_every_git_command_that_reaches_a_remote_runs_under_the_rule: "At index 8 diff: ('push', None)"_take_held_seed's fetch without itPROBE_ENVwithoutLC_ALL=Ctest_the_two_git_commands_that_read_a_leg_ask_no_terminalfatal:line kept, as on maintest_a_leg_git_reaches_over_ssh_is_refused_with_ssh_s_reason, andtest_the_reason_git_could_not_read_a_leg[ssh-before-the-generic-line]and firstguard droppedtest_the_reason_git_could_not_read_a_leg[the-generic-line-alone]test_a_leg_git_reaches_over_ssh_is_refused_with_ssh_s_reason, and both cases oftest_a_leg_git_cannot_read_is_refused_as_existing_not_as_a_failureM4 is killed only by the constant's value. This container's git has no translation catalogue, so no test here can run git in a translated locale. The in-process test does show that the probe's two commands get
PROBE_ENV.Merges
git merge-tree --write-tree HEAD <other>with heade40af32:2f5cabf5420c269a040d482bada55f00b23cd108: clean. Main gainedce0245d,450279cand2f5cabfafter this branch was cut from01a2b01.ce0245dtouched onlytests/test_ruleset_hint_tracking_branch.py, outsiderefusingandtemplate, which this test file imports. None of the three touches this PR's files.family.py initand shape_materialize.py: the plan names the tracking branch, and on disk the visibility; an unread root or reused leg is said to be unread, the root before the plan; every holder gets the members' NOTE (#913) #969l3-913-plan-visibility-notese016994872ef6ce64f0f6e5ae6b12d96334448e7(lane 3's W913, which also changesscaffold-project.py): clean.family-manifest-invalidnames the value on the line it refuses,--org is '@x', and gives the reader's remedy, not the validator's (#929) #960fix/929-init-yaml-wordsc6700b7f50a4f3e63ae2b2bbc440f738af3fc965(changesscripts/shape_materialize.py, which this PR does not): clean.l3-907-926-setup-step4-links8c5d605,l3-917-yaml-readerd3a2ee6andl3-939-941-package-standardc1de4c8: clean.Fix round 1
RV971's independent review found this PR NOT READY, with one MUST. Lane 3 ruled that the prompt stays off on every push, a first run's included. This round implements that ruling in one commit on
e40af32:155fcd2.What changed
_seed_legsand_build_assembly_root, now print through_refused_push. Where git's output holds one ofNO_LOGIN(terminal prompts disabled,could not read Username,could not read Password), aRemediation:line comes beforeRULESET_HINT. It namesgh auth setup-gitor a token stored for the host in the credential helper, then the ssh clause, then the same command re-run with--reuse-empty-repoadded, which reuses the repositories this run left empty. A run that already had the flag is told to re-run its own command. Any other refused push prints exactly what it printed before. No refusal code is new.LC_ALL=C. I probed git 2.43 with a catalogue that translates each of those words, and git translated only thefatal:before them. So the markers are found in any locale, and a push's output stays in the person's language.test_a_first_scaffold_s_push_git_has_no_login_for_names_the_fixruns a first scaffold in a pty, against a local server on 127.0.0.1 that asks for Basic auth. It has two cases: no credential helper, andcredential.helper=storeover an empty file. Each must exit 2 without waiting, and print the remedy and then the re-run, before the hint.in_a_terminal_asked_for_a_loginnow takes--reuse-empty-repoas one of its flags, and ahelper.test_the_reason_git_could_not_read_a_leg.test_a_leg_git_reaches_over_ssh_is_refused_with_ssh_s_reason[translated]asks for the ssh refusal where git speaks a test language,xx. The test copies glibc'sC.utf8into aLOCPATHasxx_XX.UTF-8, and writes the.mobytes itself (mo_bytes), so CI needs nomsgfmt. It is skipped on Windows, where/usr/lib/locale/C.utf8is missing, and where git, asked once, does not read the catalogue.SSH_KEY_CLAUSEis shared by both remedies: "ssh -Tagainst the host and port your rule names, with anything yourcore.sshCommandadds, says whether GitHub takes yours".UNREAD_EXITcarries the new words.test_what_a_refused_push_prints, over six outputs, andtest_the_root_s_push_git_has_no_login_for_names_the_fix_too, for the root's push._seed_legs. Placed before_take_held_seed, they conflicted with scaffold-project.py,family.py initand shape_materialize.py: the plan names the tracking branch, and on disk the visibility; an unread root or reused leg is said to be unread, the root before the plan; every holder gets the members' NOTE (#913) #969, which adds a function at that spot.Superseded above
terminal prompts disabled) and prints the refused-push block that every push failure prints". A push git has no login for now also prints the remedy and the re-run.ssh -T git@github.com. It now reads as NIT-2 above.[translated]case now kills it by behaviour.Out of scope in this round
setup-project.py:1307-1313). Lane 3 files it as a follow-up issue.fatal: Authentication failed) is none of the three markers, so it keeps today's words.Tests, fix round 1
On
155fcd2, under a stubghfirst on PATH that exits 97, withGH_TOKEN=bogus, an emptyGH_CONFIG_DIRandGIT_SSH_COMMAND=false, one pytest process at a time. Each command was/usr/bin/python3 -m pytest -q -p no:cacheprovider --basetemp=<scratch>/bt-f1-<name> -o tmp_path_retention_policy=failed <files>:tests/test_scaffold_reuse_seeded_legs.py:51 passed in 44.26stests/test_scaffold_e2e.py tests/test_scaffold_pin_and_reuse.py tests/test_dry_run_asks_remotes.py:153 passed in 49.04stests/test_ruleset_hint_tracking_branch.py tests/test_adopt_late_failure.py tests/test_git_output_decoding.py(the files that pin the hint's words, and the decoding of a push's output):178 passed in 74.96s (0:01:14)tests/test_agents_md_codes.py tests/test_cli_reference.py tests/test_update_add_codes_documented.py tests/test_repo_hygiene.py:157 passed in 37.27stests/test_no_auto_maintenance.py:60 passed in 7.40s155fcd2merged with origin/mainc4dd19d, scaffold-project.py,family.py initand shape_materialize.py: the plan names the tracking branch, and on disk the visibility; an unread root or reused leg is said to be unread, the root before the plan; every holder gets the members' NOTE (#913) #969 included:tests/test_scaffold_reuse_seeded_legs.py tests/test_ruleset_hint_tracking_branch.py tests/test_plan_visibility_notes.py:90 passed in 96.52s (0:01:36)Mutants, fix round 1
Each was applied to
scaffold-project.pyalone, with the named tests run, then the file restored and the restore checked. They ran on this round's code before the helpers moved; the code each mutant changes is the same at155fcd2.env=REMOTE_ENVtest_every_git_command_that_reaches_a_remote_runs_under_the_rule, and the first-run test[no-helper]_take_held_seed's fetch without ittest_every_git_command_that_reaches_a_remote_runs_under_the_rulePROBE_ENVwithoutLC_ALL=C[translated]([as-it-is]passes), andtest_the_two_git_commands_that_read_a_leg_ask_no_terminalfatal:line kept[ssh-before-the-generic-line]test_a_leg_git_cannot_read_is_refused_as_existing_not_as_a_failurefatal:line taken[two-fatal-lines-the-first]Username for 'http://127.0.0.1:...'when the harness killed it at 120 s== GENERIC_FATALguard dropped[a-line-before-a-fatal-not-the-generic-one]NO_LOGINisterminal prompts disabledalonetest_what_a_refused_push_prints[no-terminal]test_what_a_refused_push_prints[a-ruleset]and[ssh-refused-the-key], andtest_the_flag_without_the_operator_is_refused_again_and_changes_nothingtest_the_root_s_push_git_has_no_login_for_names_the_fix_tooMerges, fix round 1
git merge-tree --write-tree 155fcd2 <other>:c4dd19d, with scaffold-project.py,family.py initand shape_materialize.py: the plan names the tracking branch, and on disk the visibility; an unread root or reused leg is said to be unread, the root before the plan; every holder gets the members' NOTE (#913) #969 landed: clean.l3-911-930-scaffold-init-flags0094731: clean.SHAPE_PIN_SOURCE_*two products of one run share answers for no chain, and a first chain link that is a--pinis recorded in the pin's spelling (#909) #981l3-909-chain-pin-sourcesac5b530: clean.Fix round 2
RV971's second review found this PR READY, with one SHOULD (SonarCloud S8993) and two NITs. This round fixes the SHOULD and the first NIT in one commit on
155fcd2:0a6be99.What changed
tests/test_scaffold_reuse_seeded_legs.py:1014.test_a_leg_git_reaches_over_ssh_is_refused_with_ssh_s_reasonasked forgit_speaks_xxat run time withrequest.getfixturevalue. It now takesgit_language, a fixture parametrized over the same two ids,as-it-isandtranslated:{}for the first,git_speaks_xx's environment for the second.git_speaks_xxis a plain function now, and its skip rules are as they were: a missing/usr/lib/locale/C.utf8, or a git that reads no catalogue, skips thetranslatedrow and never fails it, and theas-it-isrow runs where it is skipped. The test ids are unchanged, so the round-1 reports that name[translated]stay true.git diff origin/main...HEAD -- tests | grep getfixturevalueprints nothing at0a6be99, so scaffold-project.py: a first scaffold's pushes and a--reuse-empty-repore-run's pushes and seed fetch no longer ask at the terminal: a push git has no login for fails naming the fix, and a leg git reaches over ssh is refused with ssh's reason (#915) #971 adds no other.SSH_KEY_CLAUSE). GitHub takes ssh only as the usergit, sossh -T -p 443 ssh.github.comwithoutgit@is refused whatever the key. The clause now says "ssh -Tagainst the user, host and port your rule names, with anything yourGIT_SSH_COMMANDorcore.sshCommandadds, says whether GitHub takes yours".GIT_SSH_COMMANDoverridescore.sshCommand, and only the second was named. The comment above the clause and the test's copy of it (SSH_KEY) say the same. A search of the repository for "host and port your rule names",core.sshCommand,GIT_SSH_COMMANDandssh -Tfound no other test, doc, handbook line or template that pins the old words:AGENTS.md,docs/and the templates hold none.scaffold-project.py:1680-1706: git prints theNO_LOGINwords only over https, so a push over ssh refused for its key still prints only the hint. Lane 3 adds it to the follow-up issue scaffold-project.py: a push whose stored login GitHub rejects (fatal: Authentication failed) is refused with only the ruleset hint, where a push git has no login for is told how to give git one #993, and I did not touch that code.Superseded above
core.sshCommandadds". Round 2's words are above.Tests, fix round 2
On
0a6be99, under a stubghfirst on PATH that exits 97, withGH_TOKEN=bogus, an emptyGH_CONFIG_DIRandGIT_SSH_COMMAND=false, one pytest process at a time. Each command was/usr/bin/python3 -m pytest -q -p no:cacheprovider --basetemp=<scratch>/bt-f2-<name> -o tmp_path_retention_policy=failed <files>:tests/test_scaffold_reuse_seeded_legs.py:51 passed in 55.61s. Both rows of the ssh test ran here:[as-it-is]and[translated]passed, none skipped, because this container has/usr/lib/locale/C.utf8and its git reads the catalogue.C_UTF8at a missing path, and thenXX_FATALat a line git never prints, run on the ssh test alone: each gave1 passed, 1 skipped, 49 deselected,[as-it-is]passed and[translated]skipped, with the reason named.tests/test_agents_md_codes.py tests/test_cli_reference.py tests/test_update_add_codes_documented.py tests/test_repo_hygiene.py:157 passed in 41.16stests/test_no_auto_maintenance.py:60 passed in 8.65sMutants, fix round 2
Each was applied to
scaffold-project.pyalone, the named tests run, the file restored and the restore checked.PROBE_ENVwithoutLC_ALL=Ctest_a_leg_git_reaches_over_ssh_is_refused_with_ssh_s_reason[translated]through the new fixture ([as-it-is]passes), andtest_the_two_git_commands_that_read_a_leg_ask_no_terminal:2 failed, 1 passedtest_a_leg_git_cannot_read_is_refused_as_existing_not_as_a_failure, both cases oftest_a_first_scaffold_s_push_git_has_no_login_for_names_the_fix, and the fourNO_LOGINcases oftest_what_a_refused_push_prints:10 failedGIT_SSH_COMMANDdropped from the clauseThe test's
SSH_KEYis a literal copy of the whole clause, which the three remedies' tests compare byte for byte, so no test needed tightening.Merges, fix round 2
git merge-tree --write-tree 0a6be99 <other>:c7a3ff16, with validate-manifest.py, validate-repository-naming.py, repo_shape.py and scaffold-project.py: a chain source that is there and cannot answer is named with why instead of offered, aSHAPE_PIN_SOURCE_*two products of one run share answers for no chain, and a first chain link that is a--pinis recorded in the pin's spelling (#909) #981 landed: clean.l3-911-930-scaffold-init-flags4e44df40: clean.#ends the block as a comment (#917) #974l3-917-yaml-readerdc82a4c4: clean.l3-924-neutral-pin-spelling5f46cd30: clean.Lane: openreposhape-3 (openRepoShape-3)
🤖 Generated with Claude Code