Skip to content

Adopt the three-repository shape: openRepoTools-spec and openRepoTools-code - #344

Merged
brettheap merged 9 commits into
mainfrom
adopt/three-repo-shape
Oct 28, 2026
Merged

brettheap merged 9 commits into
mainfrom
adopt/three-repo-shape

Conversation

@brettheap

Copy link
Copy Markdown
Contributor

Adopt the three-repository shape: openRepoTools-spec and openRepoTools-code

openRepoTools keeps its name, its identity and its full history and becomes the assembly root of the project. The two legs are NEW repositories extracted with git filter-repo, so every moved file keeps the commits that made it. Nothing was deleted: what leaves this tree arrives in a leg at the same path, mounted back here at spec/ and code/.

MOVED TO THE SPEC LEG (openRepoTools-spec @ de756faf8797):
docs/
ideation/
openspec/
specs/

MOVED TO THE CODE LEG (openRepoTools-code @ d9e7d568011b):
.github/
.gitmodules
claude-current
claude-restart-check
commands/
contracts/
lane
lane-end
lane-handoff
lane-rename
lane-start
lane-worktrees
lanes
lanes-edit.sh
lanes-index
link-estates
openRepoTools
park
pyproject.toml
repos.tsv
resume
skills/
src/
status
tests/
upstream/

STAYS IN THE ASSEMBLY ROOT:
.gitattributes
.gitignore
AGENTS.md
CLAUDE.md
LICENSE
README.md

WRITTEN BESIDE, NOT OVER (this repository had the name first):
shape/README.md <- the shape's README.md
shape/AGENTS.md <- the shape's AGENTS.md
shape/CLAUDE.md <- the shape's CLAUDE.md
shape/.gitignore <- the shape's .gitignore
shape/.gitattributes <- the shape's .gitattributes

APPENDED TO .gitattributes, after this repository's own lines, which are unchanged (each pinned copy outside shape/ checks out with LF, as the pin records it):
/scripts/validate-pins.py text eol=lf
/scripts/validate-manifest.py text eol=lf
/scripts/bootstrap.py text eol=lf
/Makefile text eol=lf
/.github/workflows/validate.yml text eol=lf
/AGENTS-shape.md text eol=lf
/scripts/repo_shape.py text eol=lf
/scripts/validate-repository-naming.py text eol=lf
/contracts/repository-naming.yaml text eol=lf

FOLLOW-UPS, which this commit does NOT do:

  • .github/workflows/release.yml in the code leg names docs/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to docs/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • claude-current in the code leg names docs/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to docs/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • lane-start in the code leg names docs/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to docs/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • lanes-edit.sh in the code leg names docs/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to docs/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • openRepoTools in the code leg names docs/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to docs/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • tests/conftest.py in the code leg names docs/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to docs/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • tests/run.sh in the code leg names docs/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to docs/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • tests/test_install_skill_and_hook.py in the code leg names docs/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to docs/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • … and 9 more file(s) under the code leg naming docs/
  • tests/test_release_workflow.py in the code leg names ideation/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to ideation/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • .github/workflows/release.yml in the code leg names openspec/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to openspec/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • lane-handoff in the code leg names openspec/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to openspec/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • lanes-edit.sh in the code leg names openspec/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to openspec/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • tests/conftest.py in the code leg names openspec/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to openspec/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • tests/test_lane_helpers.sh in the code leg names openspec/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to openspec/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • tests/test_release_workflow.py in the code leg names openspec/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to openspec/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • tests/test_test_roots.py in the code leg names openspec/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to openspec/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • .github/workflows/release.yml in the code leg names specs/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to specs/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • openRepoTools in the code leg names specs/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to specs/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • tests/conftest.py in the code leg names specs/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to specs/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • tests/test_install_payload_source.py in the code leg names specs/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to specs/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • tests/test_package_stamp.py in the code leg names specs/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to specs/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • tests/test_park_resume_commands.py in the code leg names specs/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to specs/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • tests/test_release_workflow.py in the code leg names specs/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to specs/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • tests/test_repo_hygiene.py in the code leg names specs/, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's legs.code_path mounts it, to specs/ in the spec leg, where legs.spec_path mounts it — or take the path from the environment
  • … and 1 more file(s) under the code leg naming specs/
  • keep the block at the end of .gitattributes that opens with # openRepoShape (#471): the pinned shape copies below check out with LF everywhere, as contracts/shape-pin.yaml records their bytes: the source repository already has a .gitattributes, so the shape's copy was written beside it as shape/.gitattributes, which governs only its own directory, and the split appends one text eol=lf line to .gitattributes for each pinned copy outside it. That block is what makes every checkout, Windows included, write those copies with the bytes contracts/shape-pin.yaml records; without it a Windows clone reports shape-copy-drift on each one. None of the source's own lines is changed. Merge the rest of shape/.gitattributes into .gitattributes only if the whole project should check out with LF, and only then remove shape/.gitattributes: update-shape.py check reports its pin row merged-away, and apply drops the row
  • merge shape/.gitignore into .gitignore: the source repository already has that name, so the shape's copy was written beside it and NOTHING was overwritten. Then remove shape/.gitignore: update-shape.py check reports its pin row merged-away, and apply drops the row
  • add the line Read AGENTS-shape.md first — the rules of this repository's shape. to the existing AGENTS.md rather than replacing it with shape/AGENTS.md: AGENTS.md is what this project's agents ALREADY read, and contracts/path-classification.yaml keeps it at the root (root-assistant-instructions) because it addresses the whole project. Then delete shape/AGENTS.md. Nothing was overwritten.
  • add the line Read AGENTS-shape.md first — the rules of this repository's shape. to the existing CLAUDE.md rather than replacing it with shape/CLAUDE.md: CLAUDE.md is what this project's agents ALREADY read, and contracts/path-classification.yaml keeps it at the root (root-assistant-instructions) because it addresses the whole project. Then delete shape/CLAUDE.md. Nothing was overwritten.
  • merge shape/README.md into README.md: the source repository already has that name, so the shape's copy was written beside it and NOTHING was overwritten
  • review the root's diff together with openRepoTools-spec and openRepoTools-code, the two new repositories execute creates and pushes before the split; against GitHub it opens the split's pull request itself, whose base is the plan's tracking_branch, the branch the assembly root and both legs track
  • Preserve original .gitmodules, upstream/openRepoShape gitlink and contracts/openreposhape-pin.yaml together in code; rehearse existing-submodule extraction/removal and recursive bootstrap before real execution. Never weaken blob verification or edit the standard in place.
  • Add an assembly openRepoTools compatibility entry point after byte-preserving split verification; resolve one immutable code pin for all installer artifacts and preserve OPENREPOTOOLS_REPO/REF forks, raw/gh and offline checkout behavior.
  • Separate code, assembly and spec roots in test fixtures and hygiene checks; code CI must supply the exact compatible spec/assembly context where a test reads product documentation.
  • Current .specify and agent links are ignored local bootstrap artifacts, not the 200-file source tree; capture local configuration and regenerate root workflow using setup-openspeckit after manifest/legs are present.
  • Preserve all branch/PR/worktree and dirty/untracked work; source main extraction does not transport unfinished feature branches. Migrate selected patches to paired feature branches with old-to-new receipts before rebinding lanes.
  • Review genuine bounded features//openspec/ amendment records explicitly as code; canonical/full proposals and archives remain spec even when created on a feature branch. No existing bounded amendment paths were observed; do not manufacture them for historical features.
  • Preserve approved original repository/commit/path provenance and verify correspondence to extracted spec identities, repairing relative links, selected OpenSpec roots/stores and paired feature paths without guessing filtered commit hashes.
  • Use the adopted manual amendment workflow with immediate working Speckit updates and one docs reconciliation issue at the first amendment. Schema, root routing, bootstrap distribution and upstream placement automation are pending in their owners; do not patch pinned shape copies or assume other workstations are updated.
  • Reconcile the tested migration net effect and all amendment dispositions in one final docs batch before completed assembly advancement; prepare docs/code PRs together, link landed code/evidence, close reconciliation after docs landing, and archive only after implementation and assembly pins land.
  • The wheel declares license-files = ["LICENSE"] relative to pyproject.toml, which moves to the code leg while LICENSE stays in the root: apply T006 code-0001 (the code leg's byte-identical LICENSE, .gitattributes and front door) in openRepoTools-code inside the cutover, right after execute and before the code leg's first package or release run. At the cutover, as the owner's acts: create the testpypi and pypi environments and the tag ruleset in openRepoTools-code, add a test.pypi.org trusted publisher for openRepoTools-code, and add a pypi.org pending publisher naming openRepoTools-code, release.yml and the pypi environment (Releases: annotated tags, release/vX.Y branches, and openrepotools on TestPyPI and PyPI from v1.1.0 #228).

…s-code

openRepoTools keeps its name, its identity and its full history and becomes the assembly root of the project. The two legs are NEW repositories extracted with `git filter-repo`, so every moved file keeps the commits that made it. Nothing was deleted: what leaves this tree arrives in a leg at the same path, mounted back here at spec/ and code/.

MOVED TO THE SPEC LEG (openRepoTools-spec @ de756faf8797):
  docs/
  ideation/
  openspec/
  specs/

MOVED TO THE CODE LEG (openRepoTools-code @ d9e7d568011b):
  .github/
  .gitmodules
  claude-current
  claude-restart-check
  commands/
  contracts/
  lane
  lane-end
  lane-handoff
  lane-rename
  lane-start
  lane-worktrees
  lanes
  lanes-edit.sh
  lanes-index
  link-estates
  openRepoTools
  park
  pyproject.toml
  repos.tsv
  resume
  skills/
  src/
  status
  tests/
  upstream/

STAYS IN THE ASSEMBLY ROOT:
  .gitattributes
  .gitignore
  AGENTS.md
  CLAUDE.md
  LICENSE
  README.md

WRITTEN BESIDE, NOT OVER (this repository had the name first):
  shape/README.md  <- the shape's README.md
  shape/AGENTS.md  <- the shape's AGENTS.md
  shape/CLAUDE.md  <- the shape's CLAUDE.md
  shape/.gitignore  <- the shape's .gitignore
  shape/.gitattributes  <- the shape's .gitattributes

APPENDED TO .gitattributes, after this repository's own lines, which are unchanged (each pinned copy outside shape/ checks out with LF, as the pin records it):
  /scripts/validate-pins.py text eol=lf
  /scripts/validate-manifest.py text eol=lf
  /scripts/bootstrap.py text eol=lf
  /Makefile text eol=lf
  /.github/workflows/validate.yml text eol=lf
  /AGENTS-shape.md text eol=lf
  /scripts/repo_shape.py text eol=lf
  /scripts/validate-repository-naming.py text eol=lf
  /contracts/repository-naming.yaml text eol=lf

FOLLOW-UPS, which this commit does NOT do:
  - .github/workflows/release.yml in the code leg names `docs/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `docs/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - claude-current in the code leg names `docs/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `docs/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - lane-start in the code leg names `docs/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `docs/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - lanes-edit.sh in the code leg names `docs/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `docs/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - openRepoTools in the code leg names `docs/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `docs/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - tests/conftest.py in the code leg names `docs/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `docs/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - tests/run.sh in the code leg names `docs/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `docs/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - tests/test_install_skill_and_hook.py in the code leg names `docs/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `docs/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - … and 9 more file(s) under the code leg naming `docs/`
  - tests/test_release_workflow.py in the code leg names `ideation/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `ideation/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - .github/workflows/release.yml in the code leg names `openspec/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `openspec/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - lane-handoff in the code leg names `openspec/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `openspec/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - lanes-edit.sh in the code leg names `openspec/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `openspec/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - tests/conftest.py in the code leg names `openspec/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `openspec/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - tests/test_lane_helpers.sh in the code leg names `openspec/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `openspec/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - tests/test_release_workflow.py in the code leg names `openspec/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `openspec/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - tests/test_test_roots.py in the code leg names `openspec/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `openspec/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - .github/workflows/release.yml in the code leg names `specs/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `specs/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - openRepoTools in the code leg names `specs/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `specs/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - tests/conftest.py in the code leg names `specs/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `specs/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - tests/test_install_payload_source.py in the code leg names `specs/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `specs/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - tests/test_package_stamp.py in the code leg names `specs/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `specs/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - tests/test_park_resume_commands.py in the code leg names `specs/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `specs/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - tests/test_release_workflow.py in the code leg names `specs/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `specs/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - tests/test_repo_hygiene.py in the code leg names `specs/`, which moves to the spec leg: it must read across the assembly root — from the code leg, where the plan's `legs.code_path` mounts it, to `specs/` in the spec leg, where `legs.spec_path` mounts it — or take the path from the environment
  - … and 1 more file(s) under the code leg naming `specs/`
  - keep the block at the end of .gitattributes that opens with `# openRepoShape (#471): the pinned shape copies below check out with LF everywhere, as contracts/shape-pin.yaml records their bytes`: the source repository already has a .gitattributes, so the shape's copy was written beside it as shape/.gitattributes, which governs only its own directory, and the split appends one `text eol=lf` line to .gitattributes for each pinned copy outside it. That block is what makes every checkout, Windows included, write those copies with the bytes contracts/shape-pin.yaml records; without it a Windows clone reports shape-copy-drift on each one. None of the source's own lines is changed. Merge the rest of shape/.gitattributes into .gitattributes only if the whole project should check out with LF, and only then remove shape/.gitattributes: `update-shape.py check` reports its pin row `merged-away`, and `apply` drops the row
  - merge shape/.gitignore into .gitignore: the source repository already has that name, so the shape's copy was written beside it and NOTHING was overwritten. Then remove shape/.gitignore: `update-shape.py check` reports its pin row `merged-away`, and `apply` drops the row
  - add the line `Read AGENTS-shape.md first — the rules of this repository's shape.` to the existing AGENTS.md rather than replacing it with shape/AGENTS.md: AGENTS.md is what this project's agents ALREADY read, and `contracts/path-classification.yaml` keeps it at the root (`root-assistant-instructions`) because it addresses the whole project. Then delete shape/AGENTS.md. Nothing was overwritten.
  - add the line `Read AGENTS-shape.md first — the rules of this repository's shape.` to the existing CLAUDE.md rather than replacing it with shape/CLAUDE.md: CLAUDE.md is what this project's agents ALREADY read, and `contracts/path-classification.yaml` keeps it at the root (`root-assistant-instructions`) because it addresses the whole project. Then delete shape/CLAUDE.md. Nothing was overwritten.
  - merge shape/README.md into README.md: the source repository already has that name, so the shape's copy was written beside it and NOTHING was overwritten
  - review the root's diff together with openRepoTools-spec and openRepoTools-code, the two new repositories `execute` creates and pushes before the split; against GitHub it opens the split's pull request itself, whose base is the plan's `tracking_branch`, the branch the assembly root and both legs track
  - Preserve original .gitmodules, upstream/openRepoShape gitlink and contracts/openreposhape-pin.yaml together in code; rehearse existing-submodule extraction/removal and recursive bootstrap before real execution. Never weaken blob verification or edit the standard in place.
  - Add an assembly openRepoTools compatibility entry point after byte-preserving split verification; resolve one immutable code pin for all installer artifacts and preserve OPENREPOTOOLS_REPO/REF forks, raw/gh and offline checkout behavior.
  - Separate code, assembly and spec roots in test fixtures and hygiene checks; code CI must supply the exact compatible spec/assembly context where a test reads product documentation.
  - Current .specify and agent links are ignored local bootstrap artifacts, not the 200-file source tree; capture local configuration and regenerate root workflow using setup-openspeckit after manifest/legs are present.
  - Preserve all branch/PR/worktree and dirty/untracked work; source main extraction does not transport unfinished feature branches. Migrate selected patches to paired feature branches with old-to-new receipts before rebinding lanes.
  - Review genuine bounded features/<NNN-feature>/openspec/ amendment records explicitly as code; canonical/full proposals and archives remain spec even when created on a feature branch. No existing bounded amendment paths were observed; do not manufacture them for historical features.
  - Preserve approved original repository/commit/path provenance and verify correspondence to extracted spec identities, repairing relative links, selected OpenSpec roots/stores and paired feature paths without guessing filtered commit hashes.
  - Use the adopted manual amendment workflow with immediate working Speckit updates and one docs reconciliation issue at the first amendment. Schema, root routing, bootstrap distribution and upstream placement automation are pending in their owners; do not patch pinned shape copies or assume other workstations are updated.
  - Reconcile the tested migration net effect and all amendment dispositions in one final docs batch before completed assembly advancement; prepare docs/code PRs together, link landed code/evidence, close reconciliation after docs landing, and archive only after implementation and assembly pins land.
  - The wheel declares license-files = ["LICENSE"] relative to pyproject.toml, which moves to the code leg while LICENSE stays in the root: apply T006 code-0001 (the code leg's byte-identical LICENSE, .gitattributes and front door) in openRepoTools-code inside the cutover, right after execute and before the code leg's first package or release run. At the cutover, as the owner's acts: create the testpypi and pypi environments and the tag ruleset in openRepoTools-code, add a test.pypi.org trusted publisher for openRepoTools-code, and add a pypi.org pending publisher naming openRepoTools-code, release.yml and the pypi environment (#228).
@sourcery-ai

sourcery-ai Bot commented Oct 26, 2026

Copy link
Copy Markdown

Sorry @brettheap, your pull request is larger than the review limit of 150,000 diff characters

brettheap and others added 8 commits October 26, 2026 21:05
…S.md opens with the shape's pointer and a short map of the three repositories, README.md gains them and the lockstep invariant, and every path that crossed into a leg names that leg.

Rehearsal patch for #186, task T006 of
specs/004-migrate-to-triad/tasks.md, against run C's rehearsed split
69b8924. Nothing under shape/ and no file with a row in
contracts/shape-pin.yaml is touched.

- AGENTS.md: line 1 is the exact pointer that openRepoShape 7f84ca4's
  SHAPE_POINTER_LINE and its adopt follow-up require. A seven-entry map
  follows: spec, code, this root, the tests, the lane tooling's code and
  manual, paired feature worktrees, and the local-rehearsal file-transport
  flag. The original AGENTS.md comes after it, unchanged except for 16 path
  repairs (docs/ -> spec/docs/, openspec/ -> spec/openspec/, and upstream/,
  contracts/, tests/ and .github/ -> code/...). 316 -> 340 lines.
- README.md: a "Three repositories" section and "The lockstep invariant",
  which contracts/{spec,code}-pin.yaml's `resync_runbook:
  README.md#the-lockstep-invariant` names and which only shape/README.md
  carried. The install section now says the one-liner is unchanged and that
  the installer is code/openRepoTools. 12 path repairs, and the pin-bump
  block re-rooted with `cd code`. 486 -> 510 lines.
- .gitignore: the shape's `.venv/` and `.DS_Store` merged in, and
  `/worktrees/`, the line the three-leg bootstrap requires.
- .gitattributes: the same `* text=auto eol=lf` rule, re-explained for this
  root's pinned copies. The installer reason moved with its files to
  code/.gitattributes.
- CLAUDE.md is unchanged: "Read AGENTS.md." is byte-identical to the
  shape's own rendered shape/CLAUDE.md.

The two length caps in code/tests/test_repo_hygiene.py, 316 and 486, are
today's counts. Once T009 points those tests at the assembly root, each cap
is raised in a dated entry naming the rule its lines bought.

Re-cut on 2026-10-19 (lane openRepoTools-1) against the root files
#228's series leaves, 006-release-pypi-t025 b3aab89 with
006-release-pypi-pr3fix c6cea2a merged in (neither main
55aae16 nor 006-release-pypi d10cfb1 changes the four), and the
.gitattributes block openRepoShape 190133d's split appends, headed
`# openRepoShape (#471): ...`. Seven hunks had stopped applying where
#227, #237 and #228 rewrote the lines around them: AGENTS.md's
skip-no-lane-guard repair and its three under CI ruling 1, ruling 3 and
the bash 3.2 paragraph, and README.md's install sentence and its
`code/tests/run.sh` and `code/commands/swap.md` repairs. Each keeps its
own lines, re-anchored one for one, so the repairs are still 16 and 13:
ruling 1's `.github/workflows/tests.yml`, in the sentence #227 and #228
rewrote, is one of them, and no other line added since names a path
that crossed into a leg. .gitattributes' last paragraph no longer says
no path is listed by name, because the #471 block after the rule names
nine; it says the block stays last. AGENTS.md 330 -> 354 lines and
README.md 505 -> 529; the assembly-root caps move with them in
t009-assembly-caps.patch.

Lane: openRepoTools-1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ch runs the code leg's suite composed and refuses any absent context.

validate.yml is a pinned shape copy, so the job lives in composed.yml.
T010 of specs/004-migrate-to-triad/tasks.md (#186).

Lane: openRepoTools-1
…e runs, so the suite's zsh cases run instead of skipping (part of #186)

Root follow-up, part of #186, applied at T014 of
specs/004-migrate-to-triad/tasks.md. Gate C's sentence names it "the
zsh install in composed.yml", right after T010's exact-pin job, the
commit before this one, which creates the file.

What changes. One step in the exact-pins job of
.github/workflows/composed.yml, after the manifest and pin checks and
before the test runner's install: apt-get update and apt-get install
zsh, then command -v zsh and zsh --version, so the run's log names the
zsh the suite ran with. No other step and no other file changes.

Why. On ubuntu-24.04 a composed run skips 66 conditional cases: 60
that need zsh, 1 byte-count case and 5 wheel cases. Of the three ways
offered, accept the skips, fail the job on any skip, or install zsh,
Brett Heap chose to install it. The 60 cases then run, and 6 skips
remain, as in rehearsal D's composed run on a bench that has zsh; the
suite's -rs still lists each one.

Not pinned. The job already takes its runner image and python 3.x as
the runner provides them, and pins the actions, the test runner and the
commits this root records; the zsh step prints the version it got.

Lane: openRepoTools-1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ation the code pin names and nothing else.

T007 of specs/004-migrate-to-triad/tasks.md (#186). The
documented one-liners fetch openRepoTools from this repository's root, and
after the split the installer is the code leg's. This file keeps that line
working and adds no installer mechanics.

In a checkout it requires the pin, the gitlink and the implementation's
bytes to name one commit, and then execs code/openRepoTools with every
argument. That is the same process, so the exit status and signals are the
implementation's.

Anywhere else it resolves the ref once and reads contracts/code-pin.yaml at
that commit. It checks that the gitlink at the pin's submodule_path in that
commit is the pinned commit, so the lockstep rule is enforced here and not
left to whichever code a stale pin selects. Only then does it fetch
openRepoTools from the pinned repository at the pinned commit, and run it
from stdin with OPENREPOTOOLS_REF set to that commit, so the implementation
reads the same pin.

The pin reader is a deliberate copy of the implementation's, guarded by the
code leg's tests/test_install_payload_source.py.

Lane: openRepoTools-1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
de756faf8797 -> 3c3816210b91, tree bc2d1813f9c60bc6119d2f724a947276527b46b4bf4c8c8b4068cc6a8eac8089.

No workflow reference names this leg, so the gitlink and the pin file are the whole of it.

The gitlink at spec, `commit:` and `digests.tree_sha256` in contracts/spec-pin.yaml, and every workflow reference to opensoft/openRepoTools-spec move in ONE commit because `scripts/validate-pins.py` refuses when they disagree — a gitlink advanced alone is red on the next pull request, and on somebody else's change.

Written by openRepoShape's `scripts/bump-leg.py`; the digest is recomputed from the leg's own objects, not adjusted.

Cutover: #186
d9e7d568011b -> 2a057a6d84e5, tree 31123e2d08efe36583e72956ed8b8dbbe4b968c75402b59af5b8952be83ef6d5.

No workflow reference names this leg, so the gitlink and the pin file are the whole of it.

The gitlink at code, `commit:` and `digests.tree_sha256` in contracts/code-pin.yaml, and every workflow reference to opensoft/openRepoTools-code move in ONE commit because `scripts/validate-pins.py` refuses when they disagree — a gitlink advanced alone is red on the next pull request, and on somebody else's change.

Written by openRepoShape's `scripts/bump-leg.py`; the digest is recomputed from the leg's own objects, not adjusted.

Cutover: #186
…UDE.md and shape/README.md, and its CLAUDE.md now carries the line that points to AGENTS-shape.md, as AGENTS.md already does (part of #186)

Root follow-up, part of #186 and the last
follow-up in Gate C's sentence ("the shape/*.md deletion"), applied at
T014 of specs/004-migrate-to-triad/tasks.md. It deletes three files,
adds one line to a fourth, and changes no other path.

What changes. shape/AGENTS.md, shape/CLAUDE.md and shape/README.md are
deleted, and CLAUDE.md gains the line that points to AGENTS-shape.md,
the line AGENTS.md already carries. It is CLAUDE.md's new first line,
where T006's assembly-0001 puts it in AGENTS.md, and the file's old
line follows it unchanged.

Why the three copies can go. The adoption plan's follow-ups 29, 30 and
31 ask for that line to be added to AGENTS.md and to CLAUDE.md, with
the shape's copy of each then deleted, and for shape/README.md to be
merged into README.md. T006's assembly-0001, already in this root,
added the line to AGENTS.md ahead of a short map of the three
repositories, and gave README.md the sections on the three repositories
and the lockstep invariant, which only shape/README.md carried. It
leaves CLAUDE.md alone, so CLAUDE.md gets its line here, and with the
three deletions all three follow-ups are done. None of the three copies
is a pinned shape file: contracts/shape-pin.yaml has no row for them,
and this commit does not touch it.

What stays. shape/.gitignore, its row in contracts/shape-pin.yaml and
the .gitignore comment that calls that copy pinned and left in place
are as they were, and nothing else under shape/ changes. The plan's
follow-up 28, which merges shape/.gitignore into .gitignore and then
removes it, is its own root change after the root-split pull request
merges, made with update-shape.py apply, which also drops the pin row.

Why it is made on the root and not as a patch. shape/README.md carries
the root's clone URL, a local path in rehearsal D and the GitHub URL in
the real split, so a deletion patch cut on rehearsal D's bytes would
not apply to the real split. Made on the real root clone, the commit
needs no such base: git rm takes the three files whatever their bytes,
and the one added line does not depend on the URL.

Why this slot. As the cutover's last follow-up it goes on the root after
the two commits that bump the assembly root's pins of the spec and code
legs, which Gate C's order puts before it, and before the root's push to
the split's pull request branch, so that the checks run on the root
before that push already see the three deletions and the new line.

Lane: openRepoTools-1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3c3816210b91 -> 64a1a989fe7b, tree 0aa2803cf44fc7494369144ff85aa93d79ac713314ea0c9fdcbfdb3ced1fb055.

No workflow reference names this leg, so the gitlink and the pin file are the whole of it.

The gitlink at spec, `commit:` and `digests.tree_sha256` in contracts/spec-pin.yaml, and every workflow reference to opensoft/openRepoTools-spec move in ONE commit because `scripts/validate-pins.py` refuses when they disagree — a gitlink advanced alone is red on the next pull request, and on somebody else's change.

Written by openRepoShape's `scripts/bump-leg.py`; the digest is recomputed from the leg's own objects, not adjusted.

Cutover: #186
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
95.8% Duplication on New Code (required ≤ 3%)
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@brettheap

Copy link
Copy Markdown
Contributor Author

LANDING — lane openRepoTools-1, session df82f553-7803-49c4-8ddf-3f9acdcb8ee0@Eagle, 2026-10-28T19:06:41Z, PR #344 into opensoft/openRepoTools main → into main; head a9bde65; the root-split merge (#186) after Gate D, on Brett Heap's own word (S15); squash

@brettheap
brettheap merged commit 2a99dd4 into main Oct 28, 2026
3 of 4 checks passed
@brettheap

Copy link
Copy Markdown
Contributor Author

LANDED — lane openRepoTools-1, session df82f553-7803-49c4-8ddf-3f9acdcb8ee0@Eagle, 2026-10-28T19:08:22Z, PR #344 into opensoft/openRepoTools main → main 2a99dd4 (squash of a9bde65; the root-split merge, #186)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant