Repository navigation
Prefer the Triad: the work-start advisory, the optional single-repository.yaml, and the posture text (prefer-triad-project-shape 5.2) - #164
Conversation
…schema and template, and the doctor's advisory row Partial realization of openxFactory prefer-triad-project-shape task 5.2 (Refs #163). Checkpointed mid-task for an account swap; NOT complete. In this commit: - scripts/shape_advisory.py: the one module that says the advisory and decides where it is silent (elected Triad, family holder, leg clone, <user>-wip workspace, a single-repository.yaml of the right kind), from declared facts only; the only reader of single-repository.yaml. Not a shape copy, so no project's pinned repo_shape.py moves. - contracts/single-repository-record.yaml: the record's schema, as data. - templates/single-repository/single-repository.yaml: its template. - shape-doctor.py: an `advisory` status and a `triad-advisory` row (absent where silent; n/a with the move-to-root instruction for a leg clone; a note for a silent record missing a field). Never moves the verdict; NOT A SHAPE ROOT stays exit 2. Not yet done: scaffold and adopt advisory lines, README / AGENTS.md / AGENTS-shape.md / project.yaml template posture text, handbook regeneration, hygiene caps and module lists, new tests and the two updated row-set assertions. Tests are expected to be red at this commit. Lane: codeXfactory-5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… optional single-repository.yaml, and the posture text Realizes task 5.2 of openxFactory's ratified prefer-triad-project-shape (Refs #163): the Triad -- an assembly root with a spec leg and a code leg -- is the PREFERRED project shape, it stays elective and confers nothing, and a person starting work outside one is told so once and never stopped. - scaffold-project.py: the plan names the Triad, the only thing it creates, as the preferred shape, with the posture, above the revision. - adopt-project.py: plan and check print the advisory once in the conversion's own words; nothing of it is written into the plan. - shape-doctor.py: the module docstring and the `what is here` row name the new row and the record. - README posture box (ruled by Brett Heap on 2026-10-06, "ratify 1249 as recommended"), the advisory's scope and the optional record; AGENTS.md "What you do tell them" beside "What you must not tell them", which stays; the assembly-root AGENTS-shape.md and project.yaml posture comment; docs/handbook.html regenerated with its README blob. - tests: test_shape_advisory.py (31), the hygiene module lists and the AGENTS.md 446 -> 465 and README 1376 -> 1416 caps with dated reasons, and the two doctor row sets that now carry the advisory row. Suite: 1191 passed, 33 skipped on this tree; clean main 39d5c98 is 1159 passed, 33 skipped. The skips are environmental on both: 32 for a missing git-filter-repo, 1 for the MedxEHR-backed plan test. Lane: codeXfactory-5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 3 days and 2 hours by commenting |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Reviewer's GuideThis PR implements the Triad as the preferred but strictly elective project shape by centralizing a declaration-based, non-blocking advisory, integrating it into scaffolding, adoption, and doctor reporting, adding an optional single-repository record, and updating guidance, generated documentation, and regression coverage without changing review gates or doctor exit semantics. Sequence diagram for the non-blocking work-start advisorysequenceDiagram
actor Person
participant Tool as scaffold/adopt/doctor
participant Advisory as shape_advisory.py
participant Repo as Repository facts
Person->>Tool: Start planning or checking work
Tool->>Advisory: read(repository)
Advisory->>Repo: Read declared manifests, naming, workspace, record
Repo-->>Advisory: Declared facts
alt Triad, family, leg, workspace, or valid record
Advisory-->>Tool: Silent or leg move-to-root instruction
else Unclassified repository
Advisory-->>Tool: Triad preference and posture
end
Tool-->>Person: Print once and continue
Tool-->>Person: Preserve plan, verdict, and exit status
State diagram for advisory silence and reportingstateDiagram-v2
[*] --> ReadRepository
ReadRepository --> Silent: Triad root / family holder / workspace
ReadRepository --> LegInstruction: Leg clone
ReadRepository --> Silent: Valid single-repository record
ReadRepository --> Advisory: No declared class
ReadRepository --> AdvisoryWithReport: Invalid or wrong-kind record
Silent --> [*]
LegInstruction --> [*]
Advisory --> [*]
AdvisoryWithReport --> [*]
note right of Advisory
Non-blocking advisory row or message
end note
note right of AdvisoryWithReport
Record problem reported; nothing fails
end note
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
… leg, read the planned commit The #164 review round (F1-F9): - F1: the advisory can no longer crash a run or change an exit status. shape_advisory.read() answers NOT_ANSWERED (silent) on any Exception; the doctor's Triad row is dropped rather than raised; adopt flattens its lines to ASCII and swallows the rest. KeyboardInterrupt and SystemExit still propagate. Tests: an undecodable record, a record nested past the recursion limit, a NUL byte in workspace.yaml's path, and a non-ASCII kind on an ascii console -- exit unchanged, no traceback. - F2: the single-repository doctor test now also runs the text report and asserts no REFUSED / CANNOT ANSWER, which kills the detail["environment"] mutation; a broken assembly root's verdict and exit are asserted identical with and without a valid record. - F3: `the way in` offers no adoption for a leg clone, and a leg known by its name alone is told it is "named as" a leg, "if it is one". - F4: the Triad row is asked only where there is no manifest; a project.yaml short of a Triad keeps its manifest finding instead. - F5: README and handbook say the Triad row names no command in any status. - F6: a leading UTF-8 BOM is dropped before the record's first key and before a leg AGENTS.md's opening sentence. - F7: "What you DO tell them instead" now sits inside AGENTS.md's "What you must not tell them" (Triad preferred, a single repository reviewed identically, the shape confers nothing); AGENTS.md cap 465 -> 464. - F9: adopt-project.py plan/check read the record, manifests and leg AGENTS.md from the commit being planned, not the working tree. Handbook regenerated for the README change (README.md blob 095a1b9). Refs #163 Lane: codeXfactory-5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
LANDED — lane codeXfactory-5, session ed23f049-7e99-4601-8a6d-760b6aeb5f26@Eagle, 2026-10-06T22:52:05Z, PR #164 into opensoft/openRepoShape main → squash Merged on Brett Heap's word "merge 164 when green", at head Copilot review not requested (Brett Heap, 2026-10-06). Lane: codeXfactory-5 |
…ged evidence Records the realization lane's own merged acts in tasks.md, on 5.1's pattern (PR #1254). Edits only the 5.2-5.5 lines; 5.6 stays unticked, and 5.7 is left exactly as #1255 merged it. - 5.2: opensoft/openRepoShape#164 -> 1a9fc537 (squash, 2026-10-06T22:52:05Z); tests, tests-macos, tests-windows and SonarCloud green; full suite 1159 -> 1200 passed, 33 skipped. - 5.3: this PR (#1260) -> 9af2207, the pin advance; openreposhape-pin-gate green on it (run 37550154276). - 5.4: opensoft/openBenches#139 -> 9fbe609c and #140 -> d86ba59b; post-merge main CI green; bootstrap suite 1042 PASS; workBenches#138 closed completed. - 5.5: the private brettheap/new-workstation, cited by merged sha and file name only: #52 -> e081c5ab, #53 -> cb411460; no CI there; the live ~/.agents files are byte-identical to cb411460. Each tick holds on main only through this pull request's merge, which waits on Brett Heap's merge word and its required checks. Lane: codeXfactory-5 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…adopter fix for a source with an existing submodule and opensoft/openRepoShape#164's prefer-triad advisory; none of the eight mounted files moved (#188) The triad migration (#186, first act; the plan's T005) rehearses adopt-project.py against this repository, which carries a submodule of its own. At 39d5c98 the adopter refused that source at the first leg mount (adopter-blocker.md on 004-migrate-to-triad); opensoft/openRepoShape#162 fixed it and landed on that repository's main as 7f84ca4. The rehearsal must run the tool at a commit this repository pins, so the pin moves first. On openRepoShape's main, as AGENTS.md's rule 2 requires: gh api repos/opensoft/openRepoShape/compare/main...7f84ca42ca86a8902928345109d2bf6bad87bd91 --jq .status -> identical (7f84ca4 is main's head) 39d5c98...7f84ca4: ahead 2, behind 0, 15 files (1a9fc53 is opensoft/openRepoShape#164, 7f84ca4 is opensoft/openRepoShape#162): AGENTS.md, README.md, adopt-project.py, contracts/single-repository-record.yaml, docs/handbook.html, scaffold-project.py, scripts/shape_advisory.py, shape-doctor.py, templates/assembly-root/AGENTS-shape.md, templates/assembly-root/project.yaml, templates/single-repository/single-repository.yaml, tests/test_adopt_submodules.py, tests/test_repo_hygiene.py, tests/test_shape_advisory.py, tests/test_shape_doctor.py. None of the eight files this suite mounts (the two root Makefiles, their two .gitignores, the two bootstrap.py, siblings.py, scripts/repo_shape.py) is among them; `git diff --stat 39d5c98 7f84ca4 -- <the eight>` in the submodule is empty. Bump run exactly as contracts/openreposhape-pin.yaml states: git -C upstream/openRepoShape fetch origin git -C upstream/openRepoShape checkout 7f84ca42ca86a8902928345109d2bf6bad87bd91 python3 -c "import sys; sys.path.insert(0, 'upstream/openRepoShape/scripts'); from repo_shape import tree_digest; print(tree_digest('upstream/openRepoShape', 'HEAD'))" Old commit: 39d5c986fcfac1a160474bfe91c5f1c37fccc72c Old tree_sha256: a44c0165beb9d3b6ddfce46925bf04b8d19934d5327b5a304b5f3f11af699b02 New commit: 7f84ca42ca86a8902928345109d2bf6bad87bd91 New tree_sha256: 3be52767e727d924dd05f9e78b18eb70f1682fe48614fab519f85331eeff5fa8 The digest is recomputed, not adjusted. The same 7f84ca4 helper, asked for 39d5c98's tree, prints the old row a44c0165... back, so sorted-ls-tree-r-v1 means the same thing on both sides of the bump. The pin file and the gitlink move together in this one commit. Part of #186 Lane: openRepoTools-1 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>



Lane: codeXfactory-5
Refs #163
What this realizes
Task 5.2 of openxFactory's ratified change
prefer-triad-project-shape, "opensoft/openRepoShape, the mechanics", as it reads inopenspec/changes/prefer-triad-project-shape/tasks.mdon openxFactorymaine63809650d39586134c4ec4fdb6e9effc87a7860. The Triad, an assembly root with a spec leg and a code leg, is now this standard's PREFERRED project shape. It stays elective and confers nothing. A person starting work in a repository that has not elected it is told so once and is never stopped.The advisory.
scripts/shape_advisory.py(new; deliberately NOT a shape copy, so no project's pinnedrepo_shape.pymoves) says it and decides where it is silent. It reads only declared facts:project.yaml,kind: project-manifest,schema: project-repo-schema, aspecand acodeleg;AGENTS.md(which names its assembly root) or, failing that, the-spec/-codeform ofcontracts/repository-naming.yaml(told it is "named as" a leg, "if it is one");<user>-wipworkspace, by the naming policy'sworkspaceform on the directory's name ororigin's, or by the person's ownworkspace.yaml;single-repository.yamlof the rightkind.No other class is guessed.
scaffold-project.pyhas no single-repository mode, so "offers the Triad first" is the tool itself. Its plan now names the Triad as the preferred shape, with the posture, on two lines above the shape revision.adopt-project.py planandcheckprint the advisory once, in the conversion's own words, and write none of it into the plan file. They read the record, the manifests and a leg'sAGENTS.mdout of the commit being planned, not the working tree. A leg clone gets the move-to-root note instead. The hunks are clear of fix(adopt): preserve existing gitmodules through leg extraction #162's_mount_the_legshunk, and fix(adopt): preserve existing gitmodules through leg extraction #162's patch still applies cleanly on this tree.shape-doctor.pygains oneTriadrow with the new statusadvisory, asked only of a directory with no manifest. The row is absent where the rule makes the advisory silent. A leg clone getsn/awith the move-to-root instruction, andthe way inoffers it no adoption. A silent record that leaves a field out gets anote. The verdict never reads the row: NOT A SHAPE ROOT stays exit 2, as the ratified D3 says ("This packet changes nothing about the doctor's exits").The optional record.
contracts/single-repository-record.yamlis its schema (data, with a validated example) andtemplates/single-repository/single-repository.yamlits template. The fields areschema_version: 1,kind: single-repository-record,decided_by,decided_on,reasonand an optionalrevisit_on.kind:decides: an unreadable record, or one of the wrong kind, silences nothing and is reported beside the advisory, failing nothing. The advisory is its only reader, so it has no validator script of its own.Posture text, preference beside posture everywhere.
[!IMPORTANT]box: "elective and confers nothing" stays, and the ruling is recorded as "Ruled by Brett Heap on 2026-10-06: "ratify 1249 as recommended"". It is followed by the advisory's scope and the record.templates/assembly-root/AGENTS-shape.md, and the header comment oftemplates/assembly-root/project.yaml.docs/handbook.htmlregenerated for all of it, with its README blob hash.Tests.
tests/test_shape_advisory.py(40 tests): a single repository hears it, and every exit is unchanged; silence for a Triad root, a family holder, a<user>-wip(four ways), and a valid record; the move-to-root instruction for a leg clone; advisory plus report for a wrong-kind, unparsable, non-mapping or directory record; no class guessed; schema, template and module agree; only the three tools import the advisory; nothing is written; and the review round's cases below.tests/test_repo_hygiene.py: new module lists, plus the AGENTS.md cap (446 to 464) and the README cap (1376 to 1416), each raised with a dated reason.tests/test_shape_doctor.py: two exact row-set assertions gain the new row.docs/cli.mddoes not change, because no--helptext moved:render-cli-reference.py --checkreports[ok] docs/cli.md is what the generator produces.Review round (F1-F9)
An independent review of
ce2d84draised nine findings. All nine are taken; F8 is a disclosure rather than a code change.shape_advisory.read()wraps its body inexcept Exceptionand answersNOT_ANSWERED, which is silent.KeyboardInterruptandSystemExitare notExceptions and still propagate. The doctor'scheck_triad_advisoryreturns no row rather than raising.adopt-project.pyflattens every advisory line to ASCII, as the doctor already does for its rows, and swallows anything left. New tests, each asserting the exit is unchanged (doctor 2,adopt-project.py plan0) and that no traceback is printed: an undecodable record; a record nested 600 deep (RecursionError); a NUL byte inworkspace.yaml'spath:; and a non-ASCIIkind:underPYTHONIOENCODING=ascii. On the old code, the last three failed: the doctor crashed (--jsonwas not JSON),read()raisedValueError: embedded null byte, andadopt-project.py planexited 1 with aUnicodeEncodeError. The undecodable case was already handled, so it now guards against a regression.REFUSEDon stderr and noCANNOT ANSWERon stdout. A new test on aproject.yamlroot that is short of a Triad asserts the verdict and exit are identical with and without a valid record, and that there is no Triad row.detail["environment"] = Trueon the advisory row: killed bytest_a_single_repository_hears_it_once_and_its_exit_is_unchanged. 1 failed, 39 passed.UnicodeDecodeErrorfromread_record's except: the clause is nowexcept Exception, so the literal mutation no longer exists. The equivalent narrowing,except (Refusal, OSError, YamlError), is killed bytest_a_record_nobody_can_read_fails_nothing[undecodable]and[nested-past-the-recursion-limit]. 2 failed, 38 passed.the way inno longer offersadopt-project.py planto a leg clone: it has no NEXT, and its reason sends the person to the Triad row. A leg known by its name alone is told "<name>is named as the leg of<X>under contracts/repository-naming.yaml ...; if it is one, work from its assembly root ...". A leg whose ownAGENTS.mdsays so keeps the plain wording. TheAGENTS.mdroute is checked first. Tests cover the name-only wording (Atlas-spec,billing-code), theAGENTS.mdroute, and the absence of the adopt NEXT on both.project.yamlthat is short of a Triad keeps itsmanifestfinding and is never called "a single repository". F2's new test asserts the row is absent and the verdict and exit are unchanged.n/aand the record'snoterows lack one too.AGENTS.md's opening sentence. One test covers both.adopt-project.py planandchecknow read the record, both manifests and a leg'sAGENTS.mdfrom the commit the plan is written against (git cat-file), not from the working tree. The doctor still reads the disk, which is its contract. A test covers an uncommitted record:adoptstill advises, the doctor is silent, and both are silent once the record is committed.Left unchanged on purpose:
adopt-project.py's ownambiguous-top-level-yamlquestion about asingle-repository.yaml.--jsonbehaviour.originURL. It is present at base39d5c986, it is not this pull request's, and it is out of scope here.F8 disclosure: existing Triads will show
AGENTS-shape.mdasupstream-changedThis pull request edits
templates/assembly-root/AGENTS-shape.md, a shape copy. Until each project runsupdate-shape.py applyon its own word, every existing Triad will report that file asupstream-changedinupdate-shape.py checkand in the doctor's shape-currency row. That covers openDox, openXdox and MedxEHR. It is anote, not a finding: it moves no verdict, and nothing here touches those projects.Merge method
Squash-merge this pull request. The branch carries the account-swap WIP checkpoint
d8331ea, andmainis squash-style.Copilot review not requested (Brett Heap, 2026-10-06). Codex review not requested either: it is out of quota.
Brett Heap's words, verbatim
None of these is a merge word for this pull request.
Validation evidence
python3 -m pytest tests -q -rs:main39d5c986: 1159 passed, 33 skipped (1192), exit 0;ce2d84d(before the review round): 1191 passed, 33 skipped (1224), exit 0;ff73958(after the review round): 1200 passed, 33 skipped (1233), exit 0. The 41 more passing thanmainare the 40 advisory tests, plus one new parametrized case in the hygiene suite forscripts/shape_advisory.py.git-filter-reponot being installed on the measuring machine, and 1 for the MedxEHR-backed plan test, which needs a read-only clone.tests/test_shape_advisory.py, 40 passed. Against the pre-round code atce2d84d, the same file fails 11 of them: the F1, F3, F4, F6 and F9 cases.actionlinton.github/workflows/tests.ymland the two templatevalidate.ymlfiles exits 0. CI does not run pyflakes. Run locally on every touched Python file, it reports nothing thatmaindoes not already report.scripts/render-cli-reference.py --checkexits 0.git apply --checkof PR fix(adopt): preserve existing gitmodules through leg extraction #162's diff on this tree is clean.test_no_committed_file_names_a_host_absolute_pathpasses.What follows, and is not this pull request's act
openxFactory's pin advance,
contracts/openreposhape-pin.yaml(task 5.3), follows after this lands. It is not this pull request's act.The edit to
templates/assembly-root/AGENTS-shape.mdreaches existing projects only throughupdate-shape.py checkandapply, on each project's own word. No project is touched here.Held for Brett Heap's merge word; do not merge.
🤖 Generated with Claude Code
Summary by Sourcery
Introduce the Triad as the preferred but elective project shape with a non-blocking work-start advisory and an optional single-repository declaration.
New Features:
Enhancements:
Documentation:
Tests: