Skip to content

shape-doctor.py's two naming rows read only the naming script's exit code, so a crash of the STANDARD's own validate-repository-naming.py is a FINDING about the project (INVALID (naming), exit 1, where #357 makes a crashed template CANNOT ANSWER), and a directory whose name is not UTF-8 gets naming FINDING Atl?as: UnicodeEncodeError: ..., the script's traceback quoted as a fact about the name #410

Closed

Description

@brettheap

Lane: openreposhape-4 (openRepoShape-4)

Found by this lane's planner for the doctor cluster, section 4, item 3: "check_naming ... and check_not_a_root_naming ... also read only the exit code: a crash of the standard's own naming script reads as a finding. R278-1 covers validator_row only." PR #357, which closes #278, adds crashed() for validator_row and leaves both naming rows as they are. Verified on main 3ae0109 and at #357's head 8ad856f, which print the same. Local bare remotes, an empty global git config, GIT_ALLOW_PROTOCOL=file, and a gh that exits 97 (only the machine row calls it). Not claimed: macOS and Windows. Case (1) needs a file name that is not UTF-8, which Windows does not have.

What is wrong. Both naming rows run the STANDARD's own scripts/validate-repository-naming.py, which is this checkout's and never the project's. Both turn any non-zero exit into a FINDING about the repository:

  • check_naming (shape-doctor.py:1256 at 3ae0109) reports "the naming policy is not satisfied (exit N): ";
  • check_not_a_root_naming (:3573) reports the quoted line as the reason.

Neither asks whether the script reached a verdict:

U=<openRepoShape checkout>   # a clean checkout at 3ae0109
# Runs in bash on Linux (GNU coreutils). Nothing leaves the machine: every repository is a local bare one under $R, git
# reads an empty global config, GIT_ALLOW_PROTOCOL=file makes any accidental https/ssh clone fail, and a `gh` that exits
# 97 stands first on PATH.
R=$(mktemp -d); mkdir $R/bin; printf '#!/bin/sh\nexit 97\n' > $R/bin/gh; chmod +x $R/bin/gh; touch $R/gitconfig
export PATH=$R/bin:$PATH GIT_CONFIG_GLOBAL=$R/gitconfig GIT_CONFIG_NOSYSTEM=1 GIT_ALLOW_PROTOCOL=file \
       GIT_AUTHOR_NAME=t GIT_AUTHOR_EMAIL=t@t.invalid GIT_COMMITTER_NAME=t GIT_COMMITTER_EMAIL=t@t.invalid
row() { python3 -c '
import json, sys
d = json.load(open(sys.argv[1]))
r = [r for r in d["rows"] if r["id"] == "naming"][0]
print("  naming:", r["status"], "|", r["reason"])
print("  verdict:", d["verdict"] + ", exit", d["exit"])' "$1"; }
python3 $U/scaffold-project.py --org testorg --project Atlas --elected-by 'Test Human' --elected-on 2026-10-12 \
    --local-remote-dir $R/remotes --work-dir $R/work >/dev/null
git clone -q --recurse-submodules $R/remotes/Atlas.git $R/Atlas
echo "(1) the stock standard, a directory that is not a root and whose name is not UTF-8"
D=$(printf '%s/elsewhere/Atl\xffas' $R); mkdir -p "$D"
python3 $U/shape-doctor.py --root "$D" --json > $R/1.json; row $R/1.json
echo "(2) a checkout of the standard whose own naming script crashes, asked about a compliant project"
git clone -q $U $R/std
printf 'raise RuntimeError("boom")\n' > $R/std/scripts/validate-repository-naming.py
python3 $U/shape-doctor.py --root $R/Atlas --json > $R/2a.json; echo " the stock standard:"; row $R/2a.json
python3 $R/std/shape-doctor.py --root $R/Atlas --json > $R/2b.json; echo " the broken one:"; row $R/2b.json

Output at 3ae0109 (8ad856f prints the same):

(1) the stock standard, a directory that is not a root and whose name is not UTF-8
  naming: FINDING | Atl?as: UnicodeEncodeError: 'utf-8' codec can't encode character '\udcff' in position 5: surrogates not allowed
  verdict: NOT A SHAPE ROOT, exit 2
(2) a checkout of the standard whose own naming script crashes, asked about a compliant project
 the stock standard:
  naming: ok | every leg name classifies, and each declared role agrees with its form
  verdict: COMPLIANT, exit 0
 the broken one:
  naming: FINDING | the naming policy is not satisfied (exit 1): RuntimeError: boom
  verdict: INVALID (naming), exit 1

The script's own crash on such a name is a separate defect of validate-repository-naming.py, named here for whoever fixes it. #385 is the doctor's own traceback on an origin URL that is not UTF-8, which is another read of another name.

Why. The naming rows were written when the exit code was the whole protocol: 0 classifies, 1 does not. That is the reading #278 reports in validator_row, and PR #357 replaces there. The naming rows call run_validator directly and never go through validator_row, so #357 does not reach them.

What it should do. A naming script that did not reach a verdict is never a FINDING about the repository. The decisions are the author's:

  1. The test. Reuse shape-doctor.py: a validator that crashed says it crashed instead of "reports a finding", and is CANNOT ANSWER when it is the standard's template, which a standard whose path holds the PYTHONPATH separator no longer runs at all; a neutral product's digest validate-pins.py skipped is a note naming the product, from a child that can no longer reach gh or a proxy; and a root whose manifest does not load and whose scripts/repo_shape.py is gone is told to restore that file before its gate (#278, #297, #298) #357's crashed() once it has landed, in both rows. A negative exit, from a signal, belongs in the same test (shape-doctor.py, after #357's crashed(), still reads a validator killed by a signal (a negative exit, -9 or -15) or one that left by os._exit(1) with nothing printed as one that "reports a finding", because crashed() needs a traceback line, so a root's own gate ended by the OOM killer or a timeout's SIGTERM is a FINDING about the project, and the standard's template killed the same way is not CANNOT ANSWER #447).
  2. What a crash becomes. In an assembly root or a holder: detail.environment, so CANNOT ANSWER, exit 3, with "crashed before reaching a verdict" and the quoted last line, as shape-doctor.py: a validator that crashed says it crashed instead of "reports a finding", and is CANNOT ANSWER when it is the standard's template, which a standard whose path holds the PYTHONPATH separator no longer runs at all; a neutral product's digest validate-pins.py skipped is a note naming the product, from a child that can no longer reach gh or a proxy; and a root whose manifest does not load and whose scripts/repo_shape.py is gone is told to restore that file before its gate (#278, #297, #298) #357 does for a template. In a directory that is not a root, the verdict is fixed, so only the row's status and wording change.
  3. The script itself. Whether this issue also makes validate-repository-naming.py print a name it cannot encode (backslashreplace) or refuse it by name, or that goes in an issue of its own.

How to test. Beside #357's crash tests:

  • A copy of the standard whose naming script raises, asked about a compliant project: the naming row carries detail.environment and the verdict is CANNOT ANSWER, exit 3.
  • POSIX only (WINDOWS_SKIP): a directory that is not a root, whose name holds \xff. The naming row is not a FINDING quoting UnicodeEncodeError.

Verified at main 3ae0109 (shape-doctor.py sha256 f1731ff4…, scripts/validate-repository-naming.py 362efbbd…) and at #357's head 8ad856f (shape-doctor.py 9ceb97d7…).

Related: #278 / PR #357 (crashed() for validator_row), #447 (a validator killed by a signal), #408, #385 (the doctor's own crash on a non-UTF-8 origin), #104 (the naming row's -- before names), #338 (the naming script's --link-source).

Activity

  1. added this to the v1.1.0 milestone on Oct 14, 2026
  2. brettheap commented on Oct 14, 2026

    @brettheap
    ContributorAuthor

    v1.1.0 (triage of 2026-10-14): one pull request with #448, the doctor's reading of validator output, under one rule: a verdict is a printed FINDING or REFUSED, anything else is a crash, and WARNING and note lines become a note. #447 and the pins row's dropped note lines (#460, comment 6136245436) can ride along (backlog).

  3. brettheap commented on Oct 14, 2026

    @brettheap
    ContributorAuthor

    CLAIMED — lane openRepoShape-4, session 11c6c0ef-495a-4a05-94cf-811b7b0a1012@Eagle, 2026-10-14T16:51:29Z, for #410

    Logged in lanes/log/openRepoShape-4.md at f1b127570ad607f4dc6e96c905faf9ea769ce2fa.

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

    1. existing `CLAIMED —` comments on opensoft/openRepoShape#410:
       none
    2. `gh pr list --repo opensoft/openRepoShape --state all --search "#410"`, kept where the row or its body names `#410`:
       none
    3. `git ls-remote --heads git@github.com:opensoft/openRepoShape` branches naming `410` 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