Repository navigation
Description
Goal: where a SHAPE_PIN_SOURCE_<LINK> is set and the run guard does not read it because another product of the run shares its name, one line says so, naming the variable and the products that share it, so a person who set it is not left with a warning that reads as if nothing were there.
Filed by lane openRepoShape-3, from RV981's review of #981, its N4 (comment 6254819059), on #981. N4 reads "A set variable the guard passes over is named by no line. #275's existing behaviour; follow-up, not this PR." Lane 3 ruled it a follow-up issue.
State. Read at origin/main ae63d450, and at #981's head 499f7baa (open, a draft; its fix round 1) where it is said.
- scripts/repo_shape.py:
pin_source_env_nameturns every character outside[A-Z0-9_]into_, so the neutral productsopen-glass,open_glassandopen.glassshare oneSHAPE_PIN_SOURCE_OPEN_GLASS, and a project that pins two of them gets aneutral-pin-source-unreadableFINDING (exit 1) for whichever one the variable does not name #275's guard: a variable two products share answers for NEITHER.resolve_link_source(scripts/repo_shape.py:3178) skips rule 2 for such a link:env_value = None if env_shared else os.environ.get(env_name)(:3216). A skipped rule appends no row totried: rows are made only for the candidates that were built (:3225-3228). So every caller that says what was tried has nothing to say about it. Those are the naming gate'sunread_lines(scripts/validate-repository-naming.py:482) and, at 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's head, validate-manifest.py. - The
declared-unverifiedWARNING keeps its offer short for such a link:_chain_link_findingsleaves the variable out of "a sibling checkout ... would let it be read" (scripts/repo_shape.py:1805,:1822-1824). validate-manifest.py's own offer leaves it out the same way (templates/assembly-root/scripts/validate-manifest.py:223-232). That is the only place the guard shows, and it shows by leaving a word out. validate-pins.pydoes say the collision, for two DECLARED PINS: aneutral-pin-source-collidesNOTE (templates/assembly-root/scripts/validate-pins.py:1856,_report_pin_source_collisions), printed whether or not the variable is set. The chain readers print nothing of the kind.- 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, a
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) #981 widens the guard from a chain's own links to every product the run reads (run_products), so the variable is passed over in more runs than at main and is still named nowhere.
What happens. Measured with a stub gh (exit 97) first on PATH, GH_TOKEN=bogus, an empty GH_CONFIG_DIR, GIT_SSH_COMMAND=false. <tree> is a folder holding a project.yaml that declares open_x. The variable is SHAPE_PIN_SOURCE_OPEN_X, which open-x and open_x share.
validate-repository-naming.py MedxGlass --role assembly --pins open-x \
--referent-chain open-x,open_x,openGlass --explain
variable unset: CHAIN open-x -> open_x -> openGlass [declared-unverified]
WARNING declared-unverified: open-x declaring a pin on open_x ... which is not
reachable here (a sibling checkout or --link-source open-x=<path> would let it be read).
variable set to <tree>: the same output, byte for byte (cmp: identical); lines naming SHAPE_PIN_SOURCE: 0
at main ae63d450 and at #981 499f7baa, both.
control, a chain where open-x has the variable to itself (--referent-chain open-x,openY,openGlass), set to <tree>,
at main ae63d450 and at #981 499f7baa, both:
CHAIN open-x -> openY -> openGlass [broken] (the tree was read)
(the tool prints a Unicode arrow in CHAIN lines; it is written -> here)
And the case #981 adds, which shows what the silence costs. --pins open-x,open_x --referent-chain open-x,openGlass, the variable set to a folder whose project.yaml declares openGlass:
main ae63d450: CHAIN open-x -> openGlass [verified]
#981 499f7baa: CHAIN open-x -> openGlass [declared-unverified]
WARNING declared-unverified: open-x declaring a pin on openGlass ... is not reachable here
(a sibling checkout or --link-source open-x=<path> would let it be read).
The variable the person set read a chain as verified before, and now is passed over with no word about it. That is #275's rule working as designed (the variable cannot say which product it holds). What is missing is the line that says it.
Evidence.
- The two probes above, and the code lines.
tests/test_pin_source_env_collisions.pyholds that a shared variable is read for neither (test_a_variable_two_links_share_is_read_for_neither,test_a_shared_variable_is_not_offered_as_the_way_to_read_a_link) and holdsvalidate-pins.py's NOTE; no test holds a line for a set variable that was passed over.- RV981's N4, and 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, a
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) #981's body ("validate-manifest says a source only in the WARNING for a link it could not verify, so it adds no line and no code").
Direction.
- Say it once per shared variable that is SET to something, in both chain readers: one line that names the variable, says it is set, names the products of the run that share it, says it is read for none of them, and names what reads each (
--link-source <link>=<path>in the gate; in validate-manifest.py, which takes no--link-source, a checkout of the link beside the root). An unset variable stays unsaid, as now.--quietprints it no more than it prints the other notes. - Two ways to make it. (a) Put a row for the skip into
triedinresolve_link_source, with the guard's reason, so each caller that already printstriedprints this too.tried's rows are "a source asked" today, so (a) widens what a row means. (b) A separate NOTE built frompin_source_env_collisionsand the environment, likeneutral-pin-source-collides. The author decides; (b) leavestriedalone. - It is a note and fails nothing: no exit code changes, and no finding is added (
validate-pins.py's NOTE says the same, "nothing here fails on it"). - The scaffold is left out: it reads one chain and passes no
run_products(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's body). scripts/repo_shape.py,scripts/validate-repository-naming.pyandtemplates/assembly-root/scripts/validate-manifest.pyare pinned copies, so this re-pins every project once. Land it with the next change to those files, and after 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, which edits the same functions (link_pins_from_trees,_chain_link_findings).
Verify.
- The first probe prints one line naming
SHAPE_PIN_SOURCE_OPEN_Xandopen-xandopen_xwith the variable set, nothing with it unset, and exits as before in both. - The control (variable to itself) prints no such line.
- The second probe prints the line at 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, a
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) #981's head, so the person who relied on the variable is told why it no longer answers. - A row in
tests/test_pin_source_env_collisions.pyortests/test_chain_pin_sources.py(new in 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) for each reader, set and unset;tests/test_pinned_copy_text.pyandtests/test_naming_policy.pypass; run one file at a time under a stubgh.
Size: S.
Lane: openreposhape-3 (openRepoShape-3)
Activity
brettheap commentedon Oct 19, 2026
CLAIMED — lane openRepoShape-3, session b30e8b49-f249-4668-adb1-1dbfa93c09e1@Eagle, 2026-10-19T19:02:17Z, for #990
Logged in
lanes/log/openRepoShape-3.mdatcf51967b21198437b1ef35843e10a1f31294318e.The three reads (lane-collision-protocol Rule 1):
shape_materialize.py: the scaffold's and adopt's in-process read of t…
validate-repository-naming.py and validate-manifest.py: a set `SHAPE_…