Skip to content

the family root's bootstrap.py: run_make's final line is false in three cases, piping make's stderr loses the terminal, the line order and a background child's exit, and five mutants of #876's code survive its tests #884

Closed

Description

@brettheap

Goal: the family root's make bootstrap reports a member's make truthfully in every case it can meet. Passing make's stderr through changes nothing a person relies on. Each new behaviour of #876's is held by a test that fails without it.

Filed by lane openRepoShape-5, from rv876's NIT 2, NIT 3 and NIT 4 on #876 (#839), #876 issuecomment-6246717354. rv876 measured each one at 77f8a6c8, under GNU Make 4.3 on git 2.43.0. #876 has landed as 41baf920, and path:line is at its head 77f8a6c8 in templates/family-root/scripts/bootstrap.py.

Lines

  • Five mutants of the family root's bootstrap.py and validate-family.py: the credential advice and a member's exit code say what happened, and the gate's git rm --cached, guarded mv and section lines type as printed where they failed (#839) #876's code survive its tests, and one docstring claims what no test checks. rv876 made 13 mutants; 7 were caught. The survivors:
    • R7: passed_on's sink.flush() dropped. 253 tests pass. Measured on a pty, the mutant hides a member recipe's stderr prompt (printf 'Continue? ' >&2; read ans) until after the answer is typed; with the flush, the prompt showed at 0.17 s.
    • R8: MAKE_ERROR_RE anchored on ^make: . The line-2 test never runs under a parent make, though the holder's make bootstrap always prints make[1]:.
    • R10 and R11: credential_advice ignoring modules.unreadable, or ignoring modules.refused. In all nine no-advice states, stopper alone drops the advice. With every member checked out and a value git refuses (or an unparsable file), the mutants print the advice right beside "NO MEMBER UPDATED, and not for want of a credential". That is line 1's own contradiction, and head is right there.
    • R1: named[0] in place of named[-1]. No test makes two Error lines. w839's "M6, first Error line" was in fact named[0] + 1.
    • The docstring: test_a_git_mv_staged_with_work_in_the_checkout_is_undone_as_printed says "the checkout's commit is still in the member's store" and never checks it. It is true; rv876 measured it.
    • Direction: one test per survivor: a pty test for the prompt, a run under a parent make, the two all-checked-out states, and two Error lines. Plus the store assert.
  • The final line misleads in three cases (run_member_target, run_make, :822-910).
    • A recursing member. A member whose bootstrap target runs $(MAKE) inner, with the inner command exiting 1, reads "the member's command exited 2", which looks like a refusal. No shape template recurses, but an adopted root may keep its own Makefile.
    • make -k with more than 4 KiB on stderr after the Error line. The line says make "printed no Error <code> line". That is false: the line was there, outside the last 4096 bytes run_make keeps.
    • A command killed by a signal under dash. dash prints Killed and exits 137, so the line reads "exited 137". run_make's docstring says a signal leaves make's code alone.
    • Direction:
      • Keep the last Error line as it streams, not a byte tail.
      • Under a recursing make, name the innermost command's code, or say that make's own command was a make.
      • Make the docstring true of a signal.
  • Piping make's stderr changes what a person sees, and none of it is in the docstring (run_make, passed_on).
    • The member no longer has a terminal on stderr. Base: "stderr is a terminal"; head: "stderr is NOT a terminal". So git's default progress meters and any isatty(2) check go quiet.
    • A member's stdout and stderr lines no longer come out in order. Base alternated out 1|err 1|… three times out of three; head once printed all six out lines before any err line.
    • A background child holds the run. A recipe with sleep 6 & takes 6.1 s on head against 0.1 s on base, because run_make reads stderr until EOF, and a daemon that inherits it keeps it open.
    • On Windows (read only, not run), a member's Python writes stderr to the pipe in the ANSI code page, and passed_on writes those bytes to a console that expects UTF-8, so non-ASCII text comes out garbled.
    • Direction: document each one in run_make's docstring, or stop reading once make has exited and nothing is waiting in the pipe. Decide whether the terminal loss matters enough to give stderr back, for example by reading make's Error line some other way.

Verify:

  • The new tests fail on 41baf920 where a line changes behaviour, and pass after.
  • Each surviving mutant above is caught.
  • For the pty test, a script- or pty-based run asserts the prompt is shown before the answer is typed.
  • tests/test_family_holder_bootstrap_texts.py, tests/test_family_bootstrap_gate_order.py, tests/test_bootstrap_spelling.py, tests/test_pinned_copy_text.py and the trio all pass.
  • If bootstrap.py changes, it is pinned in every holder (FAMILY_COPIED_VERBATIM), and each holder takes one upstream-changed row for it.
  • No Windows or macOS run, since CI is Linux only (ci: run the test suite on Linux only #597).

Overlaps: lane 4's #879 and lane 3's #877 (#856) edit other parts of the same bootstrap.py.

Parent: #459.

Milestone: v1.1.1. The first line is tests only. The other two are a status line wrong in rare cases and changes a person sees only with a prompting, coloured, backgrounding or non-ASCII member recipe. None is a regression of the gate's verdicts.

🤖 Generated with Claude Code

Activity

  1. added this to the v1.1.1 milestone on Oct 18, 2026
  2. brettheap commented on Oct 18, 2026

    @brettheap
    ContributorAuthor

    CLAIMED — lane openRepoShape-5, session 50cede29-ab82-4ca3-9f67-d98e1a58c8d2@Eagle, 2026-10-18T09:00:00Z, for #884

    Logged in lanes/log/openRepoShape-5.md at 00f0c2c609982dc0446397d99e559e59e658f138.

    The three reads (lane-collision-protocol Rule 1):

    1. existing `CLAIMED —` comments on opensoft/openRepoShape#884:
       none
    2. `gh pr list --repo opensoft/openRepoShape --state all --search "#884"`, kept where the row or its body names `#884`:
       none
    3. `git ls-remote --heads git@github.com:opensoft/openRepoShape` branches naming `884` as a whole token:
       none
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions