Skip to content

shape-doctor.py's manifest row says ok ... passes when the project's own validate-manifest.py exits 0 after printing WARNING naming-referent-chain: ... not reachable here, and the warning appears nowhere in the text or --json report, so a referent chain nothing on this disk could check reads as checked under COMPLIANT: the class of the skip #357 makes a note, in the form #357 still drops #448

Closed

Description

@brettheap

Lane: openreposhape-4 (openRepoShape-4)

Found by this lane's adversarial review of #278, #297 and #298 (PR #357, head 2bf402d, its N2): "WARNING lines on exit 0 are still dropped ... It is the same class as the skip, but no issue or ruling covers it." PR #357's body carries it as follow-up N2 at its head 8ad856f. #357 landed as 5678f68. Before it, main 3ae0109 dropped these lines and the skip lines both. #357 turned a skip into a note and still drops a WARNING. Verified on main at 5678f68. Local bare remotes, an empty global git config, GIT_ALLOW_PROTOCOL=file, and a gh that exits 97 (only the machine row calls it; the validator's own gh api for the openXdox pin gets the same answer, so since #357 landed the pins row is a note here as well). Not claimed: macOS and Windows, which were not run.

What is wrong.

  • validate-manifest.py prints a WARNING line and exits 0 when a recorded referent chain has a link whose tree is not on this disk. Its words: "A WARNING is a note about what could not be CHECKED here ... It is printed and then forgotten by the exit code, deliberately" (templates/assembly-root/scripts/validate-manifest.py:720-722 at 5678f68).
  • The doctor's validator_row (shape-doctor.py:1392-1398 at 5678f68) returns ok ... passes for exit 0 unless skipped() found skip blocks. A WARNING line ends a skip block (REPORT_ITEM, :1104-1105) and is never collected itself:
U=<openRepoShape checkout>   # a clean checkout of main at 5678f68, the squash #357 landed as
# 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]))
for r in d["rows"]:
    if r["id"] in sys.argv[2].split(","):
        det = r.get("detail", {})
        print("  " + r["id"] + ":", r["status"], "|", r["reason"][:120], "| crashed:", det.get("crashed"), "environment:", det.get("environment"))
print("  verdict:", d["verdict"] + ", exit", d["exit"])' "$1" "$2"; }
# openXdox is a layer that pins openDox; codexDox pins openXdox and names openDox as its referent through that chain
git init -q -b main $R/src/openXdox
printf 'schema_version: 1\nkind: project-manifest\nneutral_product_pins:\n  - openDox\n' > $R/src/openXdox/project.yaml
git -C $R/src/openXdox add -A && git -C $R/src/openXdox commit -qm seed; X=$(git -C $R/src/openXdox rev-parse HEAD)
python3 $U/scaffold-project.py --org testorg --project codexDox --elected-by 'Test Human' --elected-on 2026-10-12 \
    --pin openXdox@$X --pin-source $R/src/openXdox --referent-chain openXdox,openDox \
    --local-remote-dir $R/remotes --work-dir $R/work >/dev/null
C=$R/away/codexDox; git clone -q --recurse-submodules $R/remotes/codexDox.git $C     # no openXdox checkout beside it
(cd $C && python3 scripts/validate-manifest.py 2>&1 | cut -c1-150; echo "the root's own validate-manifest.py: exit ${PIPESTATUS[0]}")
python3 $U/shape-doctor.py --root $C --json > $C.json; echo "doctor --json: exit $?"; row $C.json manifest
python3 -c 'import json, sys; print("  WARNING anywhere in the --json report:", "WARNING" in open(sys.argv[1]).read())' $C.json
python3 $U/shape-doctor.py --root $C > $C.txt 2>&1; echo "  WARNING lines in the text report: $(grep -c WARNING $C.txt)"
echo "== the same root with a FINDING after that WARNING: the spec leg's naming.role says 'code'"
D=$R/away/codexDox2; git clone -q --recurse-submodules $R/remotes/codexDox.git $D
sed -i '/form: project-leg/{n;s/role: spec/role: code/}' $D/project.yaml
(cd $D && python3 scripts/validate-manifest.py 2>&1 | grep -E '^(WARNING|FINDING)' | cut -c1-150; echo "the root's own validate-manifest.py: exit ${PIPESTATUS[0]}")
python3 $U/shape-doctor.py --root $D --json > $D.json; echo "doctor --json: exit $?"; row $D.json manifest

Output on main at 5678f68:

WARNING naming-referent-chain: leg 'assembly': declared-unverified: openXdox declaring a pin on openDox is a fact in openXdox's own tree, which is not
manifest ok: codexDox (codexdox), 3 legs
the root's own validate-manifest.py: exit 0
doctor --json: exit 0
  manifest: ok | the project's own scripts/validate-manifest.py passes | crashed: None environment: None
  verdict: COMPLIANT, exit 0
  WARNING anywhere in the --json report: False
  WARNING lines in the text report: 0
== the same root with a FINDING after that WARNING: the spec leg's naming.role says 'code'
WARNING naming-referent-chain: leg 'assembly': declared-unverified: openXdox declaring a pin on openDox is a fact in openXdox's own tree, which is not
FINDING naming-role: leg 'spec': naming.role is 'code', but 'codexDox-spec' classifies as 'spec'
the root's own validate-manifest.py: exit 1
doctor --json: exit 1
  manifest: FINDING | the project's own scripts/validate-manifest.py reports a finding (exit 1): WARNING naming-referent-chain: leg 'assembly' | crashed: None environment: None
  verdict: INVALID (manifest), exit 1

The validator says the chain's middle link could not be read here: "openXdox declaring a pin on openDox is a fact in openXdox's own tree, which is not reachable here (a sibling checkout, SHAPE_PIN_SOURCE_OPENXDOX, or --link-source openXdox= would let it be read)". The doctor says the gate passes, under COMPLIANT, and the word appears nowhere in its report, as text or as --json.

The same blind spot has a red twin, found by this lane's plan for the next doctor change (its R08-4): a row that is already a FINDING can quote the WARNING as its finding. run_validator quotes the first line of the output that starts FINDING, REFUSED or WARNING (shape-doctor.py:1230-1237), and validate-manifest.py prints leg by leg, so a WARNING from an earlier leg comes before a later leg's FINDING, and the row's reason is then the note that moved nothing and not the finding that failed the gate. The last lines of the repro change the spec leg's naming.role to code, a true naming-role finding, on the same root: the validator prints the WARNING and then the FINDING, and the manifest row is FINDING with the WARNING as its quoted line.

Why. #297 ruled that a check the validator could not make, which it reports and which leaves its exit at 0, is a note in the doctor's row: "passes, but did not recheck: ...", with detail.skipped. PR #357 implements that for the skip form validate-pins.py prints. validate-manifest.py reports the same kind of thing in another form, WARNING <code>: ..., and nothing reads it.

What it should do. An exit 0 that came with a WARNING line is a note naming it, as a skip is. The verdict does not move. The decisions are the author's:

  1. The form. Its own detail.warnings and wording ("passes, with a warning: ..."), or folded into 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 skipped note.
  2. Which children. validate-manifest.py is the validator that prints WARNING lines on exit 0 today. The naming script prints WARNING lines on stderr for an unread link tree (scripts/validate-repository-naming.py:480-485), so the naming row has the same gap. The project-mode run in this repro printed none, so that row was not shown here.
  3. NEXT. None, as for a skip, since the warning names its own remedies (a sibling checkout, SHAPE_PIN_SOURCE_<LINK>, --link-source).
  4. The quoted line of a red row. A suggestion from the same plan (its R08-4): run_validator quotes the first FINDING or REFUSED line, else the first WARNING, else the output's last line, so a row that fails never names a note as its reason.

How to test. A descendant by chain, scaffolded with --pin openXdox@<commit> --pin-source <stand-in> --referent-chain openXdox,openDox and cloned with no openXdox beside it. The manifest row is a note whose reason quotes naming-referent-chain, and the verdict is still COMPLIANT, exit 0. The control has the link's tree beside it (or SHAPE_PIN_SOURCE_OPENXDOX): the row is ok. For decision 4, the same root with a naming-role finding added: the row's reason quotes the FINDING naming-role line, not the WARNING.

Verified on main at 5678f68, the squash #357 landed as (shape-doctor.py sha256 c206264b…, validate-manifest.py 50f56a3e…).

Related: #297 / PR #357 (a skip becomes a note), #278, #447 (the same PR's other follow-up), #40 / PR #42 (the referent chain).

Activity

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

    @brettheap
    ContributorAuthor

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

    Logged in lanes/log/openRepoShape-4.md at 8f3123e4e63a58b5a44767b381948dad9d807daa.

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

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