Repository navigation
validate-repository-naming.py (_pins_from_project :302, _chain_from_leg :318), validate-manifest.py (_declared_pins :85) and validate-pins.py (PinSources :390, called at :698) iterate the project's own neutral_product_pins: and a leg's naming.referent_chain: without asking they are lists, so 5 (or true, 1.5) in either field ends the naming gate in TypeError: 'int' object is not iterable, exit 1, neutral_product_pins: 5 ends the other two validators the same way before validate-manifest.py reaches the manifest-neutral-product-pins finding it has for it, and make validate stops in a traceback at its first command; a scalar openXdox in neutral_product_pins: is read as the characters X, d, e, n, o, p, x by the naming gate, which classifies and exits 0 #443
Description
Lane: openreposhape-5 (openRepoShape-5)
(lane openRepoShape-5, 2026-10-12T18:05:40Z)
Found by lane 5's review of fix/link-source-values (the fix for #338) at 40762ca, whose SHOULD-1 was this class in a LINK's manifest (neutral_product_pins: 5 out of link_declarations; fixed at 4b21263), and then by the writer of that fix round, who ran the same value on the project's OWN manifest at 4b21263 and listed it as its candidate D: --project on a manifest with neutral_product_pins: 5 gave the traceback, exit 1, and a scalar openXdox read as characters and classified, exit 0. The writer did not check what the project's own validate-manifest.py says first. A second lane-5 review of 4b21263 did, and found that validate-manifest.py ends in the same TypeError, and that a leg's naming.referent_chain: 5 crashes the naming gate the same way; both are in this issue. It is pre-existing and outside #338, which is about the OTHER trees a chain reads (a link's manifest, link_declarations) and not the project's own declarations. The reproduction below was run on origin/main 45e01f4 (the neutral_product_pins: rows were run earlier on cd1fc79, which differs from it only by an added tests/test_agents_md_codes.py, with the same answers) and on a merge of 4b21263 with main: _pins_from_project and _chain_from_leg are unchanged there (:507 and :523, the second reached first, from _project_chain_links :593). Not claimed.
What is wrong. The project's own declarations are read by four unguarded iterations in three validators, each of the form for x in (value or []), which iterates whatever is there:
scripts/validate-repository-naming.py:302,_pins_from_project, readsneutral_product_pins:(called from_targets_from_project,:377).scripts/validate-repository-naming.py:315-318,_chain_from_leg, reads a leg'snaming.referent_chain:(called from_leg_target,:337). It has the rule for a string (:316-317, one link) and none for anything else that is not a list.templates/assembly-root/scripts/validate-manifest.py:85,_declared_pins, readsneutral_product_pins:(called from_findings,:658).templates/assembly-root/scripts/validate-pins.py:697-698:products = manifest.get("neutral_product_pins") or []and thenPinSources(pin_source_values, products), whose__init__iterates it (:390).
The naming gate is both the standard's scripts/validate-repository-naming.py and the project's own copy of it.
A value that is not iterable (5, true, 1.5) raises TypeError. A string in neutral_product_pins: is iterable, so openXdox is read one character at a time.
neutral_product_pins:, the manifest validator's finding is never reached.validate-manifest.pyhas the refusal for both kinds of value:_neutral_product_pins_type_finding(:501-512) saysFINDING manifest-neutral-product-pins: neutral_product_pins is <value>, expected a list of neutral product namesfor anything that is notNoneand not a list. But_findingscalls_declared_pins(manifest)(:658) one line BEFORE it calls that function (:659), so for the values that crash, the finding built for them is never reached and the project's own manifest validator ends in the same traceback.tests/test_manifest_validator.py:286(test_neutral_product_pins_must_be_a_list) pins only the stringopenChart, the value that does not crash.referent_chain:, the manifest validator is already right and is not reached._recorded_chain(validate-manifest.py:88-100) reads a string as a one-link chain and anything that is not a list as(), and_naming_chain_field_finding(:119-131) saysFINDING naming-referent-chain: leg 'assembly': referent_chain is 5, expected a list of neutral-product names(tests/test_manifest_validator.py:441,test_a_chain_record_that_is_not_a_list_is_a_finding, value7). The naming gate's_chain_from_legis the copy of the same reader that has the string rule and not the list rule.
The order the gates run in decides what a person sees first, and it is the naming gate: make validate runs naming, manifest, pins (templates/assembly-root/Makefile:46-49) and stops at the first failure; validate.yml (:187, :190, :200) and bootstrap.py's step (b) (VALIDATORS, :76-79) run them in the same order. So for either field the first thing a person reads is the naming gate's traceback, exit 2 from make, and the validator that has a sentence for the field (validate-manifest.py) is never run by make validate. bootstrap.py runs all three and prints every traceback before bootstrap FAILED: ....
# usage: bash non-list-fields.sh "$U" ($U: a checkout of openRepoShape)
# Local remotes only; the real `gh` is kept out of reach.
U=$1
STUB=$(mktemp -d); printf '#!/bin/sh\nexit 97\n' > "$STUB/gh"; chmod +x "$STUB/gh"
export PATH="$STUB:$PATH" GH_TOKEN=bogus GH_CONFIG_DIR=$(mktemp -d) PYTHONDONTWRITEBYTECODE=1
export GIT_AUTHOR_NAME=T GIT_AUTHOR_EMAIL=t@example.invalid GIT_COMMITTER_NAME=T GIT_COMMITTER_EMAIL=t@example.invalid
D=$(mktemp -d)
python3 "$U/scaffold-project.py" --org testorg --project Atlas --elected-by 'T H' --elected-on 2026-09-02 \
--local-remote-dir "$D/remotes" --work-dir "$D/work" > /dev/null; echo "scaffold exit $?"
git -c protocol.file.allow=always clone -q --recurse-submodules "$D/remotes/Atlas.git" "$D/Atlas" && cd "$D/Atlas" || exit 1
grep -n '^neutral_product_pins' project.yaml
set_field() { # set_field pins|chain VALUE: reset project.yaml, then put VALUE in the field
git checkout -q -- project.yaml
if [ "$1" = pins ]; then sed -i "s/^neutral_product_pins: \[\]\$/neutral_product_pins: $2/" project.yaml
else sed -i "0,/^ also_matches: \[\]\$/s// also_matches: []\n referent_chain: $2/" project.yaml; fi
}
for field in pins chain; do
for value in '[]' 5 openXdox; do
set_field $field "$value"
[ $field = pins ] && name=neutral_product_pins || name="the assembly leg's naming.referent_chain"
echo "=== $name: $value"
for cmd in "scripts/validate-repository-naming.py --project project.yaml" scripts/validate-manifest.py scripts/validate-pins.py; do
python3 $cmd > "$D/o" 2> "$D/e"; rc=$?
echo " $cmd: exit $rc, Traceback: $(grep -c '^Traceback' "$D/e"), last line: $(cat "$D/e" "$D/o" | grep -v '^$' | tail -1 | cut -c1-110)"
done
make validate > "$D/o" 2> "$D/e"; echo " make validate: exit $?, stopped in: $(grep '^python3' "$D/o" | tail -1)"
done
done
for value in openXdox 5; do # the doctor, over each field with each value
for field in pins chain; do
set_field $field "$value"
python3 "$U/shape-doctor.py" --root "$D/Atlas" --json 2>/dev/null | python3 -c '
import json, sys
p = json.load(sys.stdin); rows = {x["id"]: x for x in p["rows"]}
print("doctor,", sys.argv[1], "=", sys.argv[2] + ":", p["verdict"], "exit", p["exit"])
for k in ("naming", "manifest", "pins"): print(" ", k, rows[k]["status"], rows[k]["reason"][:120])' "$field" "$value"
done
doneObserved on origin/main 45e01f4, and unchanged on 3ef059c, which adds #314's changes to scaffold-project.py and adopt-project.py (Linux, git 2.43.0, Python 3.12.3; <T> is the temporary directory):
scaffold exit 0
97:neutral_product_pins: []
=== neutral_product_pins: []
scripts/validate-repository-naming.py --project project.yaml: exit 0, Traceback: 0, last line: Atlas-code project-leg/code
scripts/validate-manifest.py: exit 0, Traceback: 0, last line: manifest ok: Atlas (atlas), 3 legs
scripts/validate-pins.py: exit 0, Traceback: 0, last line: pins ok
make validate: exit 0, stopped in: python3 scripts/validate-pins.py
=== neutral_product_pins: 5
scripts/validate-repository-naming.py --project project.yaml: exit 1, Traceback: 1, last line: TypeError: 'int' object is not iterable
scripts/validate-manifest.py: exit 1, Traceback: 1, last line: TypeError: 'int' object is not iterable
scripts/validate-pins.py: exit 1, Traceback: 1, last line: TypeError: 'int' object is not iterable
make validate: exit 2, stopped in: python3 scripts/validate-repository-naming.py --project project.yaml
=== neutral_product_pins: openXdox
scripts/validate-repository-naming.py --project project.yaml: exit 0, Traceback: 0, last line: Atlas-code project-leg/code
scripts/validate-manifest.py: exit 1, Traceback: 0, last line: 1 finding(s) in <T>/Atlas/project.yaml
scripts/validate-pins.py: exit 2, Traceback: 0, last line: Remediation: `scaffold-project.py --pin o@<commit>` writes that file. A declaration in project.yaml with no pi
make validate: exit 2, stopped in: python3 scripts/validate-manifest.py
=== the assembly leg's naming.referent_chain: []
scripts/validate-repository-naming.py --project project.yaml: exit 0, Traceback: 0, last line: Atlas-code project-leg/code
scripts/validate-manifest.py: exit 0, Traceback: 0, last line: manifest ok: Atlas (atlas), 3 legs
scripts/validate-pins.py: exit 0, Traceback: 0, last line: pins ok
make validate: exit 0, stopped in: python3 scripts/validate-pins.py
=== the assembly leg's naming.referent_chain: 5
scripts/validate-repository-naming.py --project project.yaml: exit 1, Traceback: 1, last line: TypeError: 'int' object is not iterable
scripts/validate-manifest.py: exit 1, Traceback: 0, last line: 1 finding(s) in <T>/Atlas/project.yaml
scripts/validate-pins.py: exit 0, Traceback: 0, last line: pins ok
make validate: exit 2, stopped in: python3 scripts/validate-repository-naming.py --project project.yaml
=== the assembly leg's naming.referent_chain: openXdox
scripts/validate-repository-naming.py --project project.yaml: exit 0, Traceback: 0, last line: Atlas-code project-leg/code
scripts/validate-manifest.py: exit 0, Traceback: 0, last line: manifest ok: Atlas (atlas), 3 legs
scripts/validate-pins.py: exit 0, Traceback: 0, last line: pins ok
make validate: exit 0, stopped in: python3 scripts/validate-pins.py
doctor, pins = openXdox: INVALID (manifest, pins) exit 1
naming ok every leg name classifies, and each declared role agrees with its form
manifest FINDING the project's own scripts/validate-manifest.py reports a finding (exit 1): FINDING manifest-neutral-product-pins: neutra
pins FINDING the project's own scripts/validate-pins.py refuses (exit 2): REFUSED neutral-pin-missing: project.yaml declares a pin on
doctor, chain = openXdox: COMPLIANT exit 0
naming ok every leg name classifies, and each declared role agrees with its form
manifest ok the project's own scripts/validate-manifest.py passes
pins ok the project's own scripts/validate-pins.py passes
doctor, pins = 5: INVALID (naming, manifest, pins) exit 1
naming FINDING the naming policy is not satisfied (exit 1): TypeError: 'int' object is not iterable
manifest FINDING the project's own scripts/validate-manifest.py reports a finding (exit 1): TypeError: 'int' object is not iterable
pins FINDING the project's own scripts/validate-pins.py reports a finding (exit 1): TypeError: 'int' object is not iterable
doctor, chain = 5: INVALID (naming, manifest) exit 1
naming FINDING the naming policy is not satisfied (exit 1): TypeError: 'int' object is not iterable
manifest FINDING the project's own scripts/validate-manifest.py reports a finding (exit 1): FINDING naming-referent-chain: leg 'assembly'
pins ok the project's own scripts/validate-pins.py passes
The traceback of the naming gate, for each field (the standard's copy and the project's copy are the same at the same lines):
File ".../scripts/validate-repository-naming.py", line 377, in _targets_from_project
pins = _pins_from_project(data)
File ".../scripts/validate-repository-naming.py", line 302, in _pins_from_project
return {str(pin) for pin in (data.get("neutral_product_pins") or []) if pin}
TypeError: 'int' object is not iterable
File ".../scripts/validate-repository-naming.py", line 337, in _leg_target
target = Target(name, declared, pins, _chain_from_leg(leg), root)
File ".../scripts/validate-repository-naming.py", line 318, in _chain_from_leg
return tuple(str(link) for link in (recorded or []) if str(link).strip())
TypeError: 'int' object is not iterable
and the one in the project's own validate-manifest.py for neutral_product_pins: 5, which has the finding for the value and dies first:
File ".../scripts/validate-manifest.py", line 658, in _findings
pins = _declared_pins(manifest)
File ".../scripts/validate-manifest.py", line 85, in _declared_pins
return {str(pin) for pin in (manifest.get("neutral_product_pins") or []) if pin}
TypeError: 'int' object is not iterable
What the readers that take the dict do with each value, on the pure functions (no project, no files):
# usage: python3 values.py "$U" ($U: a checkout of openRepoShape; pure functions, no files, no network, no gh)
import importlib.util, sys
from pathlib import Path
U = Path(sys.argv[1]).resolve()
sys.dont_write_bytecode = True
sys.path.insert(0, str(U / "scripts"))
sys.path.insert(0, str(U / "templates" / "assembly-root" / "scripts"))
def load(name, path):
spec = importlib.util.spec_from_file_location(name, path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
naming = load("naming_gate", U / "scripts" / "validate-repository-naming.py")
manifest = load("manifest_gate", U / "templates" / "assembly-root" / "scripts" / "validate-manifest.py")
VALUES = [("5", 5), ("true", True), ("1.5", 1.5), ("openXdox", "openXdox"), ("{a: b}", {"a": "b"}),
("0", 0), ("false", False), ("''", ""), ("[]", []), ("null", None), ("[openXdox]", ["openXdox"])]
def show(read, value, key="neutral_product_pins", nested=False):
data = {key: value}
try:
return "reads " + repr(sorted(read({"naming": data} if nested else data)))
except Exception as exc: # the point of the probe
return f"raises {type(exc).__name__}: {exc}"
print(f"{'neutral_product_pins:':23}{'validate-repository-naming.py _pins_from_project':58}validate-manifest.py _declared_pins")
for label, value in VALUES:
print(f"{label:23}{show(naming._pins_from_project, value):58}{show(manifest._declared_pins, value)}")
print()
print(f"{'referent_chain:':23}{'validate-repository-naming.py _chain_from_leg':58}validate-manifest.py _recorded_chain")
for label, value in VALUES:
print(f"{label:23}{show(naming._chain_from_leg, value, 'referent_chain', True):58}{show(manifest._recorded_chain, value, 'referent_chain')}")Observed on origin/main 45e01f4, and identical on a merge of 4b21263 with it:
neutral_product_pins: validate-repository-naming.py _pins_from_project validate-manifest.py _declared_pins
5 raises TypeError: 'int' object is not iterable raises TypeError: 'int' object is not iterable
true raises TypeError: 'bool' object is not iterable raises TypeError: 'bool' object is not iterable
1.5 raises TypeError: 'float' object is not iterable raises TypeError: 'float' object is not iterable
openXdox reads ['X', 'd', 'e', 'n', 'o', 'p', 'x'] reads ['X', 'd', 'e', 'n', 'o', 'p', 'x']
{a: b} reads ['a'] reads ['a']
0 reads [] reads []
false reads [] reads []
'' reads [] reads []
[] reads [] reads []
null reads [] reads []
[openXdox] reads ['openXdox'] reads ['openXdox']
referent_chain: validate-repository-naming.py _chain_from_leg validate-manifest.py _recorded_chain
5 raises TypeError: 'int' object is not iterable reads []
true raises TypeError: 'bool' object is not iterable reads []
1.5 raises TypeError: 'float' object is not iterable reads []
openXdox reads ['openXdox'] reads ['openXdox']
{a: b} reads ['a'] reads []
0 reads [] reads []
false reads [] reads []
'' reads [] reads []
[] reads [] reads []
null reads [] reads []
[openXdox] reads ['openXdox'] reads ['openXdox']
Together with the CLI runs above and validate-manifest.py measured on each value in a scaffolded project, that gives:
neutral_product_pins: |
_pins_from_project (naming) |
_declared_pins (manifest) |
_neutral_product_pins_type_finding |
|---|---|---|---|
5, true, 1.5 |
TypeError ('int', 'bool', 'float') |
the same | would be a finding; never reached |
openXdox |
reads {'X', 'd', 'e', 'n', 'o', 'p', 'x'} |
the same | FINDING manifest-neutral-product-pins (reached) |
{a: b} |
reads {'a'} |
the same | a finding (reached) |
0, false, '' |
reads set() |
the same | a finding (0 is not None; reached) |
[], null |
set() |
the same | none |
[openXdox] |
{'openXdox'} |
the same | none |
referent_chain: |
_chain_from_leg (naming) |
_recorded_chain (manifest) |
_naming_chain_field_finding |
|---|---|---|---|
5, true, 1.5 |
TypeError |
reads () |
FINDING naming-referent-chain (reached) |
openXdox |
one link, openXdox |
the same | none |
{a: b} |
reads the KEY a as a one-link chain |
reads () |
FINDING naming-referent-chain |
0, false |
() |
() |
FINDING naming-referent-chain |
'', [], null |
() |
() |
none |
[openXdox] |
openXdox |
the same | none |
On validate-pins.py a string in neutral_product_pins: is read the same way and the first character is the finding: REFUSED neutral-pin-missing: project.yaml declares a pin on 'o' but contracts/o-pin.yaml ...; it does not read referent_chain:. The doctor reports each crash as "reports a finding (exit 1)" with the TypeError line as the quoted finding (PR #357 teaches the manifest and pins rows to say a validator crashed, #278; the naming row reads only the exit code, #410). With referent_chain: 5 it says INVALID (naming, manifest): the naming row quotes the TypeError, and the manifest row quotes the right sentence.
Why. YamlError's docstring is the standard's contract for these files: "Raising rather than guessing is the whole point: a validator that silently mis-reads its own contract file is worse than one that will not start." A traceback is a validator that will not start without saying why, and a manifest the project's own gate calls a list of characters, or a mapping whose keys are a chain, is the silent misreading. The standard already decided the first half: validate-manifest.py has a finding for a non-list neutral_product_pins: and one for a non-list referent_chain:, and #338's fix gives the same value in a LINK's manifest the answer "a tree that cannot answer, named with its reason" (link_declarations). The project's own declarations are the places the idiom is left, and the order of the gates makes the naming gate's traceback the first thing read.
What it should do. The author decides; a value that is not a list must end in a sentence naming the field and no traceback, in every validator that reads it, and the sentence a person reads first should be the right one:
validate-manifest.pyreaches its own finding forneutral_product_pins:._declared_pinsreturns an empty set for a value that is not a list (or_findingscalls_neutral_product_pins_type_findingfirst and returns early), so5givesFINDING manifest-neutral-product-pins: neutral_product_pins is 5, expected a list of neutral product namesasopenXdoxdoes.referent_chain:needs nothing here:_recorded_chainand its finding are the model.- The naming gate asks the same of both fields, and says so. Either it treats a non-list as declaring nothing (no pins, no chain, as
_recorded_chaindoes) and leaves the finding to the manifest validator (a name that needs the pin or the chain then classifies without it, exit as the classification gives it, and the project still failsmake validateat its second command), or it refuses the project with the same sentence so that the FIRST command ofmake validatesays it. The second is the one the order of the gates favours; the exit (1, a finding, or 2, a refusal, asmanifest-unreadableis) is the author's. Either way a string inneutral_product_pins:must not be read as characters and a mapping inreferent_chain:must not be read as its keys. validate-pins.pyasks it before it buildsPinSources, and refuses with the same sentence (it already refuses withneutral-pin-missingfor the first character of a string, which names'o'and no field).- One reader, one place. The four iterations (five with
_recorded_chain) share two questions, "what does this manifest declare?" and "what chain does this leg record?"; a helper inscripts/repo_shape.py(which every project carries, and whichvalidate-manifest.py,validate-pins.pyand the naming gate already import) that returns the list, with_recorded_chain's string rule, or raises the refusal removes the idiom from all of them.templates/assembly-root/scripts/validate-manifest.py,validate-pins.py,scripts/validate-repository-naming.pyandscripts/repo_shape.pyare all digest-pinned copies in a scaffolded root'scontracts/shape-pin.yaml, so the change reaches existing projects throughupdate-shape.py apply.
How to test. In tests/test_manifest_validator.py, widen test_neutral_product_pins_must_be_a_list (:286) from the string to 5, true, 1.5, {a: b}, 0, false and '', each asserting manifest-neutral-product-pins in stderr, exit 1, and no Traceback; the chain test at :441 already covers 7 and should cover true, 1.5 and {a: b} too. In tests/test_naming_policy.py, beside the --project tests, run validate-repository-naming.py --project on each of those values for each field and on openXdox for neutral_product_pins:, asserting no Traceback, the chosen answer, that a string never classifies as characters (no pin o) and that {a: b} never reads as a chain a. In tests/test_neutral_pin_validator.py or tests/test_pin_validator.py, run validate-pins.py on each and assert the sentence and no neutral-pin-missing naming 'o'. In the doctor's tests, one row per field: 5 gives INVALID with the rows quoting a sentence and not a TypeError. Controls: [], a missing key, null, [openXdox], and the one-link string openXdox for referent_chain: keep their answers. Reverting the fix fails each. A test must not pin [5] or [[a]] (entries that are not names), which this issue did not examine.
Not in this issue.
- A LINK manifest's non-list
neutral_product_pins:(the other tree a chain reads): scripts/validate-repository-naming.py:_link_sources_from_argssplits every--link-sourcevalue at its first=and nothing reports a value no chain link used, so a link tree under a directory nameda=b, or behind a mistyped key, is silently skipped (declared-unverified, exit 0), andrepo_shape.link_manifest_pathlets a source this user cannot enter (keyed,SHAPE_PIN_SOURCE_<LINK>or a sibling checkout) end the naming gate in aPermissionErrortraceback before Python 3.14 #338 and its fix, which names it as a tree that cannot answer. - The same idiom on other fields: a shape pin's
files:(update-shape.pyread_pinand the pinned validate-pins.py and validate-family.py: iteratepin.get("files") or []without asking for a list, so a shape pin withfiles: 5orfiles: trueends shape-doctor.py,update-shape.py checkandapplyand the project's own validator inTypeError: ... is not iterable, exit 1, with no refusal naming the pin #290, PR update-shape.pyread_pinrefusesupdate-pin-row, and the pinned validate-pins.py and validate-family.py reportshape-pin-row, when a shape pin'sfiles:is not a list, sofiles: 5,trueor1.5no longer endscheck,apply, shape-doctor.py and the project's own validator inTypeError: ... is not iterable(#290) #343), an adopt plan'spins:,allow_empty_legs:,paths:andlegs:(adopt-project.py:Plan.__init__,allowed_empty_legs,namesandopen_sourceiterate or call.geton a hand-edited plan'spins:,allow_empty_legs:,paths:,legs:andsource:without asking their type, so a scalar endscheckandexecutein a TypeError or AttributeError traceback, exit 1, andpins: openGlasspassescheck, adopts, and writes eight one-letter neutral-product pins that the project's own validate-pins.py refuses #386) and itsfollow_ups:(adopt-project.py:checkandexecuteiterate a plan'sfollow_ups:without asking for a list, so a hand-edited plan withfollow_ups: 5(ortrue) endscheckin aTypeError: ... is not iterabletraceback, andexecutein the same one after both leg repositories are created and pushed #293). They are different files and different fields, found separately. - The doctor reading a validator's crash as "reports a finding" (shape-doctor.py:
validator_rowreads a validator's exit 1 as "reports a finding" whatever caused it, so a validator that CRASHED (a:in the standard's path splits thePYTHONPATHthe template fallback runs under, andvalidate-manifest.pydies onimport repo_shape) is a FINDING row quotingModuleNotFoundError, and a root whose manifest is valid goes fromCOMPLIANT, SHAPE BEHINDtoINVALID (manifest)#278, PR 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 thePYTHONPATHseparator no longer runs at all; a neutral product's digestvalidate-pins.pyskipped is anotenaming the product, from a child that can no longer reachghor a proxy; and a root whose manifest does not load and whosescripts/repo_shape.pyis gone is told to restore that file before its gate (#278, #297, #298) #357) and thenamingrows reading only the exit code (shape-doctor.py's twonamingrows read only the naming script's exit code, so a crash of the STANDARD's ownvalidate-repository-naming.pyis 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 getsnaming FINDING Atl?as: UnicodeEncodeError: ..., the script's traceback quoted as a fact about the name #410). - Entries inside a list that are not names (
[5],[[a]]), whichstr()turns into text: not examined.
Related. #338 and fix/link-source-values (4b21263; _pins_from_project and _chain_from_leg are untouched there), and the two other follow-ups from the review of that branch, #441 and #442, which are about how a --link-source value is read and not about these fields. #132 (the split of validate-manifest.py's _findings, where _neutral_product_pins_type_finding was written). #290, #293, #386 (the same class on other fields). #278, #410 (how the doctor shows the crash).
Activity
validate-repository-naming.py, repo_shape.py: a `--link-source` value…
docs/release-notes/v1.0.0.md: brought up to main b8b7062, so section …
brettheap commentedon Oct 15, 2026
(lane openRepoShape-2, 2026-10-15T17:27:31Z)
A fact this issue does not yet hold, from the checklist triage of 2026-10-15 (Brett Heap approved its recommendations at 2026-10-15T16:49:38Z): since #557, the doctor answers this crash
CANNOT ANSWER, exit 3, which outranks the manifest's own finding. At main26c1600, where #557 isa2243f1f, all three validators still end in theTypeError(the triage's probe).The checklist line that reported it on #459 (comment 6175509630) is ticked there as a duplicate of this issue.
brettheap commentedon Oct 15, 2026
CLAIMED — lane openRepoShape-4, session 11c6c0ef-495a-4a05-94cf-811b7b0a1012@Eagle, 2026-10-15T23:09:49Z, for #443
Logged in
lanes/log/openRepoShape-4.mdat569360fc96bf4e6e4bf83ac7c326c9c3ab2e6a80.The three reads (lane-collision-protocol Rule 1):
docs/release-notes/v1.1.0.md: refreshed at main 4cf5fb0 (49 pull requ…
tests/test_non_list_project_fields.py: #631 fix round 1: a chain that…
validate-repository-naming.py, validate-manifest.py and validate-pins…