From 52ce64ee7787b62bf015e13cbf663f305513093e Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sat, 3 Oct 2026 18:45:52 +0000 Subject: [PATCH 1/9] docs: preserve triad migration planning drafts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Preserve the existing OpenSpec and Speckit planning files at Brett Heap’s request during repository cleanup. No migration or adoption is executed. --- .../changes/migrate-to-triad/.openspec.yaml | 2 + .../migrate-to-triad/adoption-plan.yaml | 346 ++++++++++++++++++ openspec/changes/migrate-to-triad/design.md | 137 +++++++ openspec/changes/migrate-to-triad/proposal.md | 68 ++++ .../specs/repository-triad-migration/spec.md | 88 +++++ openspec/changes/migrate-to-triad/tasks.md | 22 ++ openspec/config.yaml | 1 + specs/004-migrate-to-triad/data-model.md | 18 + specs/004-migrate-to-triad/inventory.json | 156 ++++++++ specs/004-migrate-to-triad/plan.md | 162 ++++++++ specs/004-migrate-to-triad/quickstart.md | 31 ++ specs/004-migrate-to-triad/research.md | 22 ++ specs/004-migrate-to-triad/spec.md | 85 +++++ specs/004-migrate-to-triad/tasks.md | 124 +++++++ specs/004-migrate-to-triad/verification.md | 42 +++ 15 files changed, 1304 insertions(+) create mode 100644 openspec/changes/migrate-to-triad/.openspec.yaml create mode 100644 openspec/changes/migrate-to-triad/adoption-plan.yaml create mode 100644 openspec/changes/migrate-to-triad/design.md create mode 100644 openspec/changes/migrate-to-triad/proposal.md create mode 100644 openspec/changes/migrate-to-triad/specs/repository-triad-migration/spec.md create mode 100644 openspec/changes/migrate-to-triad/tasks.md create mode 100644 openspec/config.yaml create mode 100644 specs/004-migrate-to-triad/data-model.md create mode 100644 specs/004-migrate-to-triad/inventory.json create mode 100644 specs/004-migrate-to-triad/plan.md create mode 100644 specs/004-migrate-to-triad/quickstart.md create mode 100644 specs/004-migrate-to-triad/research.md create mode 100644 specs/004-migrate-to-triad/spec.md create mode 100644 specs/004-migrate-to-triad/tasks.md create mode 100644 specs/004-migrate-to-triad/verification.md diff --git a/openspec/changes/migrate-to-triad/.openspec.yaml b/openspec/changes/migrate-to-triad/.openspec.yaml new file mode 100644 index 00000000..6a87d2b2 --- /dev/null +++ b/openspec/changes/migrate-to-triad/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-03 diff --git a/openspec/changes/migrate-to-triad/adoption-plan.yaml b/openspec/changes/migrate-to-triad/adoption-plan.yaml new file mode 100644 index 00000000..c32025d1 --- /dev/null +++ b/openspec/changes/migrate-to-triad/adoption-plan.yaml @@ -0,0 +1,346 @@ +# Preliminary adoption mapping; planning only. Execution needs a refreshed baseline and explicit approval. +# Paths are relative to the source checkout. Generated metadata does not authorize repository creation. +schema_version: 1 +kind: adoption-plan +generated_on: "2026-10-03" +tool: "opensoft/openRepoShape adopt-project.py" +source: + repository: opensoft/openRepoTools + local_path: . + default_branch: main + commit: c1bac0dfc99a62583cb62eafc93a192911b2ff22 + files: 58 + bytes: 4202017 + commits: 77 +mode: in-place +project: openRepoTools +org: opensoft +id: openrepotools +visibility: public +tracking_branch: main +adopt_branch: adopt/three-repo-shape +elected_by: "Brett Heap" +elected_on: "2026-10-03" +reference: "openxFactory docs/project-repo-schema.md" +legs: + assembly: openRepoTools + spec: openRepoTools-spec + code: openRepoTools-code + spec_path: spec + code_path: code +pins: [] +seeding: + spec: + seeded_from_template: false + code: + seeded_from_template: false +allow_empty_legs: [] +paths: + - path: .gitattributes + leg: root + confidence: high + rule: root-front-door + reason: "What a person or a forge reads BEFORE deciding which leg to open. The licence and the code of conduct govern all three repositories; the README is the front door of the thing an engineer clones." + files: 1 + bytes: 1857 + review_required: false + - path: .github/ + leg: code + confidence: high + rule: code-ci-workflows + reason: "CI runs the implementation's own gate — build, test, analysis. The assembly root gets its own `validate.yml` from the shape, so a workflow moved here is not a workflow lost." + files: 1 + bytes: 18419 + review_required: false + - path: .gitignore + leg: root + confidence: high + rule: root-front-door + reason: "What a person or a forge reads BEFORE deciding which leg to open. The licence and the code of conduct govern all three repositories; the README is the front door of the thing an engineer clones." + files: 1 + bytes: 38 + review_required: false + - path: .gitmodules + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 121 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Keep existing nested dependency registration with its upstream gitlink in code; assembly spec/code registrations are generated separately." + - path: AGENTS.md + leg: root + confidence: high + rule: root-assistant-instructions + reason: "The AI-assistant instruction files and their one-file directories are the same document ten times over, and it addresses the whole project. An assistant that read it from inside the code leg would be told about specifications it cannot see. Root, ruled 2026-09-02." + files: 1 + bytes: 20968 + review_required: false + - path: CLAUDE.md + leg: root + confidence: high + rule: root-assistant-instructions + reason: "The AI-assistant instruction files and their one-file directories are the same document ten times over, and it addresses the whole project. An assistant that read it from inside the code leg would be told about specifications it cannot see. Root, ruled 2026-09-02." + files: 1 + bytes: 16 + review_required: false + - path: LICENSE + leg: root + confidence: high + rule: root-front-door + reason: "What a person or a forge reads BEFORE deciding which leg to open. The licence and the code of conduct govern all three repositories; the README is the front door of the thing an engineer clones." + files: 1 + bytes: 11357 + review_required: false + - path: README.md + leg: root + confidence: high + rule: root-front-door + reason: "What a person or a forge reads BEFORE deciding which leg to open. The licence and the code of conduct govern all three repositories; the README is the front door of the thing an engineer clones." + files: 1 + bytes: 29806 + review_required: false + - path: commands/ + leg: code + confidence: medium + rule: extension-majority + reason: "no rule names this path; the `.md` extension is classified as spec by the policy's extension table" + files: 3 + bytes: 5146 + review_required: false + resolution: "These Markdown command files are shipped runtime installer payloads with implementation tests, not OpenSpec or Speckit decisions." + - path: contracts/ + leg: code + confidence: high + rule: spec-governance + reason: "Requirements, decisions, contracts and governance prose — what the project has DECIDED, as opposed to what it currently runs. `contracts/` is here by the 2026-09-02 ruling even where the code reads it: the code reads across the assembly-root mount (`spec/contracts` from the root, `../spec/contracts` from `code/`), which is what the legs are mounted for." + files: 1 + bytes: 3299 + review_required: false + resolution: "At this baseline the only contract is openreposhape-pin.yaml, a dependency lock coupled to the code gitlink and installer; future product governance contracts belong in spec and assembly pins are generated at root." + - path: docs/ + leg: spec + confidence: high + rule: spec-governance + reason: "Requirements, decisions, contracts and governance prose — what the project has DECIDED, as opposed to what it currently runs. `contracts/` is here by the 2026-09-02 ruling even where the code reads it: the code reads across the assembly-root mount (`spec/contracts` from the root, `../spec/contracts` from `code/`), which is what the legs are mounted for." + files: 1 + bytes: 182097 + review_required: false + - path: lane + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 83555 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." + - path: lane-end + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 93296 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." + - path: lane-handoff + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 72601 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." + - path: lane-rename + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 17116 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." + - path: lane-start + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 223900 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." + - path: lanes + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 36348 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." + - path: lanes-edit.sh + leg: code + confidence: medium + rule: extension-majority + reason: "no rule names this path; the `.sh` extension is classified as code by the policy's extension table" + files: 1 + bytes: 759480 + review_required: false + - path: link-estates + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 21694 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." + - path: openRepoTools + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 178500 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." + - path: openspec/ + leg: spec + confidence: high + rule: spec-governance + reason: "Requirements, decisions, contracts and governance prose — what the project has DECIDED, as opposed to what it currently runs. `contracts/` is here by the 2026-09-02 ruling even where the code reads it: the code reads across the assembly-root mount (`spec/contracts` from the root, `../spec/contracts` from `code/`), which is what the legs are mounted for." + files: 10 + bytes: 16759 + review_required: false + - path: park + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 50113 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." + - path: repos.tsv + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 2651 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Runtime alias data installed beside the lane commands." + - path: resume + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 63671 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." + - path: skills/ + leg: code + confidence: medium + rule: extension-majority + reason: "no rule names this path; the `.md` extension is classified as spec by the policy's extension table" + files: 3 + bytes: 77708 + review_required: false + resolution: "These three skills are installed product payloads; project workflow skills remain local root bootstrap artifacts." + - path: specs/ + leg: spec + confidence: high + rule: spec-governance + reason: "Requirements, decisions, contracts and governance prose — what the project has DECIDED, as opposed to what it currently runs. `contracts/` is here by the 2026-09-02 ruling even where the code reads it: the code reads across the assembly-root mount (`spec/contracts` from the root, `../spec/contracts` from `code/`), which is what the legs are mounted for." + files: 6 + bytes: 4141 + review_required: false + - path: status + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 347320 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." + - path: tests/ + leg: code + confidence: high + rule: code-source-and-tests + reason: "The implementation, the tests that hold it, and the scripts and images that build and run it. `scripts/` moves whole: a harness that reads `contracts/` from beside it reads them across the root mount afterwards, which is a code change the plan records as a follow-up rather than a reason to split the directory." + files: 12 + bytes: 1880040 + review_required: false + - path: upstream/ + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 0 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Mounted openRepoShape is the implementation/test dependency; preserve its exact gitlink with the original .gitmodules and dependency pin." +follow_ups: + - "code/lanes-edit.sh names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" + - "code/openRepoTools names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" + - "code/tests/run.sh names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" + - "code/tests/test_install_skill_and_hook.py names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" + - "code/tests/test_lane_helpers.sh names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" + - "code/tests/test_repo_hygiene.py names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" + - "code/lanes-edit.sh names `openspec/`, which moves to the spec leg: it must read across the assembly root — `../spec/openspec/` relative to `code/` — or take the path from the environment" + - "code/tests/test_lane_helpers.sh names `openspec/`, which moves to the spec leg: it must read across the assembly root — `../spec/openspec/` relative to `code/` — or take the path from the environment" + - "code/tests/test_park_resume_commands.py names `specs/`, which moves to the spec leg: it must read across the assembly root — `../spec/specs/` relative to `code/` — or take the path from the environment" + - "merge shape/.gitattributes into .gitattributes: the source repository already has that name, so the shape's copy was written beside it and NOTHING was overwritten" + - "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" + - "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" + - "open the split as a pull request against the source's default branch; the legs openRepoTools-spec and openRepoTools-code are seeded before it, so review the root's diff and the two new repositories together" + - "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 58-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." + - "Regenerate this preliminary plan after planning lands and baseline freeze; require current coverage check, local bare-remote rehearsal, prerequisite availability and explicit approval before execute." +summary: + spec: + files: 17 + bytes: 202997 + file_share: 0.2931 + byte_share: 0.0483 + code: + files: 35 + bytes: 3934978 + file_share: 0.6034 + byte_share: 0.9364 + root: + files: 6 + bytes: 64042 + file_share: 0.1034 + byte_share: 0.0152 + unresolved: + files: 0 + bytes: 0 + file_share: 0.0000 + byte_share: 0.0000 +planning_status: proposed-preliminary-baseline +execution_authorized: false diff --git a/openspec/changes/migrate-to-triad/design.md b/openspec/changes/migrate-to-triad/design.md new file mode 100644 index 00000000..691aeee7 --- /dev/null +++ b/openspec/changes/migrate-to-triad/design.md @@ -0,0 +1,137 @@ +# Design: in-place triad adoption + +## Context + +The preliminary source is main `c1bac0dfc99a62583cb62eafc93a192911b2ff22`: +58 tracked paths, 77 commits, one nested standard submodule. The inspected +standard is `39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. There are 12 registered +worktrees including this planning checkout, and four open PRs. Feature 001 has +dirty/untracked work; an extraction of main alone would omit it. The +[inventory](../../../specs/004-migrate-to-triad/inventory.json) is a snapshot. + +## Goals / Non-Goals + +Preserve project identity/history, all user work, installed command behavior +and a reproducible pinned assembly. Separate product decisions from shipped +implementation; migrate unfinished features without restarting specification. +Do not redesign swap, enable its experimental capability, change WIP ownership, +rewrite published source history or remove old worktrees during adoption. + +## Decisions + +### Assembly identity and source baseline + +Use the standard's in-place adoption. `openRepoTools` stays the existing +assembly; `openRepoTools-spec` and `openRepoTools-code` are new legs mounted +at `spec` and `code`. Public visibility is a proposal matching current source +visibility, subject to final approval. A new assembly name would strand +existing issues, URLs and PRs, so is outside this design. + +Run real adoption from a fresh clean default-branch clone with no linked +worktrees attached. Original working clones remain preserved. Freeze the +baseline only after planning and any agreed prerequisite changes land; refresh +the generated plan whenever that commit moves. Approval binds the actual +baseline, standard commit, complete mapping and follow-ups. + +### Placement and dependency custody + +The [resolved adoption plan](adoption-plan.yaml) accounts for all 58 baseline +paths: 17 spec, 35 code, six root, none dropped. It records reasons for all +14 originally unresolved paths and overrides for shipped Markdown payloads +and the dependency lock. Product documentation/OpenSpec/Speckit go to spec. +Root owns front-door guidance, license, project metadata and workflow bootstrap. + +Keep executable files, `repos.tsv`, shipped `commands/` and `skills/`, tests +and implementation CI in code. The original `.gitmodules`, mounted standard +and `contracts/openreposhape-pin.yaml` also stay together in code. At this +baseline that contract is solely a dependency lock, not product governance. +Generated root shape/spec/code pins are distinct. Future governance contracts +belong in spec; regenerate the path plan if the source adds such contracts. + +Keeping the standard in root would move its gitlink away from its dependency +pin and implementation consumers, and mutate the original `.gitmodules` during +the byte-accounted split. Keeping that dependency unit in code preserves its +existing paths. Existing-submodule handling still requires local rehearsal; +plan coverage validation alone is not execution certification. + +### Public installation compatibility + +After successful byte accounting, add a root `openRepoTools` compatibility +entry point. It resolves one assembly revision and its exact code pin, then +loads the implementation at that immutable commit. All install payloads come +from that same code commit. Preserve the existing `gh api .../openRepoTools` +and raw-URL one-liners, local/offline invocation, override/fork behavior, +destination names, artifact counts, permissions, hooks, receipts and atomic +refusal semantics. No request may silently combine root/spec/code main tips. + +The code installer has a self-contained local payload tree; installed commands +must not depend on the developer's assembly checkout. Source/ref resolution +must support the existing monorepo override form, an adopted assembly fork and +explicit code checkout execution. Missing or inconsistent pins refuse before +installation effects. Implementation tests define the compatibility matrix; +this document does not invent a second installer implementation. + +### Development and active work + +The sole executable list is the linked Speckit feature. `.specify/` and agent +links are currently ignored local scaffolding, so baseline blob extraction +does not preserve them. Inventory local configuration and regenerate workflow +at the assembly root with the shape-aware bootstrap. Retain the root guidance +and the named team assignments in feature 001. Leg guidance points back to root. + +Classify each active feature's delta relative to its own reviewed merge base; +translate it into selected spec/code/root changes with old-to-new commit/file +receipts. Include dirty/untracked files in preservation and replay validation. +Preserve original branches and PRs while replacement branches are reviewed. +Paired leg worktrees use the existing feature name under root `worktrees/`. +Do not cherry-pick a mixed monorepo commit wholesale into either leg, or merge +an old feature branch into the adopted assembly. Do not retire/rebind a live +lane until its owner has reached a safe breakpoint and replacement evidence +is reviewed. WIP records remain in their existing private repository and use +the supported estate workflow; public records contain no private backups. + +### CI and exact-pin delivery + +Code CI runs implementation checks; root CI validates manifest/pins and the +exact composed assembly. Supply explicit code/spec/assembly roots to tests +that inspect docs or history; preserve the nested dependency pin assertions +in the code Git repository. Use the canonical serialized `tests/run.sh`, +Bash 3.2 parsing and current platform/fixture timing policies. Missing-spec +context cannot turn a required integration check into a silent skip. + +Land follow-up leg changes before the root pin bump that selects them. Root +gitlinks, leg pins and workflow SHAs move through the standard's bump tool. +The historical upstream dependency remains one-way and unedited; needed +standard fixes land there before a deliberate pin bump here. + +## Risks / Trade-offs + +- Local mapping passes while existing-submodule extraction fails: gate real + execution on an isolated local bare-remote rehearsal with intact gitlink, + module metadata, pin and recursive bootstrap. +- Historical commits/files change or disappear: retain original repository, + bundles/private work backups and migration receipts; require blob accounting + for the frozen baseline and explicit verification of each replayed delta. +- An active lane writes during capture: coordinate a bounded owner-approved + breakpoint, then refresh capture and compare checksums before cutover. +- Installer entry point vanishes or mixes revisions: add compatibility before + root merge; validate exact-ref/fork/offline and all-or-none installation. +- CI cannot read the selected assembly/spec context: publish explicit composed + checkout instructions and preserve the original canonical regression gate. +- New roots make WIP paths stale: use supported park/resume contracts and + reviewed migration records; never overwrite workspace pointers or force a + refusal through with resets. + +## Migration Plan + +The [Speckit plan](../../../specs/004-migrate-to-triad/plan.md) owns sequencing, +gates, active PR decisions, cutover and rollback. This change is planning-only. +`git-filter-repo` is absent in the inspected `py-bench`; make prerequisite +availability an execution gate, without installing it during planning. + +## Approval decisions + +Confirm public new legs, the refreshed frozen baseline/mapping, branch/PR +dispositions, coordinated cutover and repository creation only after the +local rehearsal and compatibility work are reviewable. No approval is inferred +from OpenSpec artifact completeness or the generated election metadata. diff --git a/openspec/changes/migrate-to-triad/proposal.md b/openspec/changes/migrate-to-triad/proposal.md new file mode 100644 index 00000000..8253222b --- /dev/null +++ b/openspec/changes/migrate-to-triad/proposal.md @@ -0,0 +1,68 @@ +# Proposal: migrate openRepoTools to the assembly/spec/code triad + +## Why + +openRepoTools currently keeps product decisions, implementation and project +workflow in one repository. Elect the openRepoShape triad so specifications +and implementation can evolve independently while the existing repository +pins their exact tested combination and preserves its identity and history. + +## What Changes + +- Adopt `opensoft/openRepoTools` in place as the assembly root; extract + `opensoft/openRepoTools-spec` and `opensoft/openRepoTools-code` with history. + Public legs are proposed to match the existing public source; execution + requires explicit approval of the reviewed plan, visibility and follow-ups. +- Put OpenSpec, Speckit specifications and product documentation in `spec/`; + executable tooling, shipped skills/commands, tests, CI and the mounted + standard dependency in `code/`. Root agent guidance and workflow bootstrap + span both legs. No source path is proposed for deletion. +- **BREAKING:** development paths and PR destinations change. Preserve command + names, install artifacts, published one-line installation and installed + behavior through a pinned assembly-to-code compatibility entry point. +- Preserve all open PRs, branch histories, dirty/untracked work, detached + worktrees and workspace records before migration. Continue unfinished + features through explicit paired spec/code branches with migration receipts; + do not merge old monorepo feature branches over the adopted root. +- Validate a local bare-remote rehearsal before any real repository creation. + Keep original clones/worktrees and the installed toolset usable until a + reviewed assembly PR and exact-pin integration checks establish cutover. + +## Capabilities + +### New Capabilities + +- `repository-triad-migration`: history-preserving adoption, placement, + installation compatibility, active-work migration, exact-pin delivery and + evidence-gated cutover/rollback. + +### Modified Capabilities + +None. No canonical capability inventory exists on the inspected source main. +Swap, context, handoff and estate-command semantics remain governed by their +existing changes; this migration does not activate experimental capabilities. + +## Impact + +- `openRepoTools` installer/default source selection and its bare-name payload + lookups; assembly entry point and leg-pin resolution; fork/ref behavior. +- `tests/conftest.py`, pin/status/install/hygiene tests, `tests/run.sh`, + `.github/workflows/tests.yml`, and cross-leg documentation references. +- `.gitmodules`, the mounted `upstream/openRepoShape` dependency and its + `contracts/openreposhape-pin.yaml` lockstep relationship. Keep all three in + code; generated assembly shape/spec/code pins are separate contracts. +- Global OpenSpec/Speckit bootstrap, currently ignored local `.specify/` and + agent links, lane directory bindings, open PR destinations, and WIP records. +- Governing change: this directory. Sole implementation feature: + [004-migrate-to-triad](../../../specs/004-migrate-to-triad/spec.md). + See its [migration plan](../../../specs/004-migrate-to-triad/plan.md) and + executable task list; the OpenSpec checklist records governance only. + +## Planning status + +Requested on October 3, 2026. Planning is authorized; migration execution, +repository creation, software installation, PR merge and lane relocation are +not authorized by this request. The attached adoption plan is a preliminary +mapping of source main `c1bac0dfc99a62583cb62eafc93a192911b2ff22`, not a promise +that this will be the final split commit. Refresh after planning lands and the +cutover baseline is frozen. No runtime or migration success is claimed. diff --git a/openspec/changes/migrate-to-triad/specs/repository-triad-migration/spec.md b/openspec/changes/migrate-to-triad/specs/repository-triad-migration/spec.md new file mode 100644 index 00000000..982c6f73 --- /dev/null +++ b/openspec/changes/migrate-to-triad/specs/repository-triad-migration/spec.md @@ -0,0 +1,88 @@ +# Repository triad migration + +## Purpose + +Convert the existing openRepoTools repository to a pinned assembly/spec/code +project while preserving history, unfinished work and supported installation. + +## ADDED Requirements + +### Requirement: Adoption preserves source identity and accounts for content + +The migration SHALL retain the existing repository name, identity and full +published history. It SHALL freeze a reviewed source commit and account for +each tracked source path by blob or gitlink identity in exactly one leg/root, +or an explicitly approved drop. It SHALL refuse stale/unresolved mappings and +SHALL NOT force-push the original history or weaken verification to pass. + +#### Scenario: Source changes after plan generation +- **WHEN** the default-branch commit differs from the reviewed adoption plan +- **THEN** execution SHALL require regenerated mapping/checks and renewed + approval of the actual baseline before creating real repositories. + +#### Scenario: Existing nested dependency is extracted +- **WHEN** the code leg receives the mounted standard dependency +- **THEN** its registration, exact gitlink and coupled pin SHALL remain valid + and SHALL be demonstrated by local rehearsal and recursive bootstrap. + +### Requirement: Active work is preserved before cutover + +The migration SHALL inventory open PRs, all branches/worktrees, detached work, +dirty/untracked files and local workflow configuration. It SHALL preserve +their contents and recovery evidence before cutover, including work absent +from the default branch. Each continued feature SHALL have a reviewed +old-to-new mapping and validated spec/code/root changes before rebinding. +Original worktrees and installed tools SHALL remain available until acceptance. + +#### Scenario: Swap feature has uncommitted proposal and code work +- **WHEN** source main is extracted without those feature changes +- **THEN** the migration SHALL retain complete original feature contents and + migrate the reviewed delta separately, preserving feature identity and roles +- **AND** absence from the extracted main SHALL NOT imply abandonment. + +### Requirement: Installation retains the public interface and one revision + +Existing one-line installation, command names, payload destinations/modes, +skills/hooks/receipts and supported fork/ref/local invocation SHALL remain +usable. An adopted assembly entry point SHALL resolve one immutable code +revision for the complete installation. Missing/inconsistent pin evidence +SHALL refuse before partial installation. Installed tools SHALL function +without a developer assembly checkout. + +#### Scenario: Assembly code main advances independently +- **WHEN** the operator installs from a selected assembly revision +- **THEN** every payload SHALL come from that revision's selected code commit + rather than whichever code main tip exists at request time. + +#### Scenario: Offline checkout installation +- **WHEN** a complete local adopted checkout is used without network access +- **THEN** installation SHALL use its verified local payload and preserve the + existing all-or-none installation and refusal behavior. + +### Requirement: Project workflow operates across the pinned legs + +The assembly SHALL own project workflow and its manifest/pins; product +OpenSpec/Speckit artifacts SHALL live in spec, implementation in code. +Continued features SHALL use paired leg worktrees with preserved feature IDs. +Delivery SHALL land leg changes and select them through a root pin bump. +CI SHALL test the exact composed state without silently skipping required +cross-leg tests. The standard dependency SHALL remain unedited in place. + +#### Scenario: Implementation and specification land separately +- **WHEN** new leg commits are ready for project delivery +- **THEN** root gitlinks, pin records and workflow commit references SHALL move + together through the established pin-bump procedure and integration checks. + +### Requirement: Execution and cutover require reviewable evidence + +Real execution SHALL require an explicit approved plan, visibility, baseline, +mapping and follow-ups. A local bare-remote rehearsal, preservation receipts, +compatibility checks and required platform/CI checks SHALL precede cutover. +Execution SHALL use a clean isolated source clone without linked worktrees. +Failed verification SHALL retain the source/work and stop landing; rollback +SHALL restore a reviewed prior state without destructive history rewriting. + +#### Scenario: Preparation is complete but approval is absent +- **WHEN** mapping and planning validation have passed without execution approval +- **THEN** the migration SHALL remain a plan and SHALL create no real leg + repositories, move no lanes and change no installed tools. diff --git a/openspec/changes/migrate-to-triad/tasks.md b/openspec/changes/migrate-to-triad/tasks.md new file mode 100644 index 00000000..3738cb5d --- /dev/null +++ b/openspec/changes/migrate-to-triad/tasks.md @@ -0,0 +1,22 @@ +# Governance and Speckit handoff + +The sole executable list is [004-migrate-to-triad/tasks.md](../../../specs/004-migrate-to-triad/tasks.md). +This checklist tracks governance, not duplicate implementation tasks. + +## 1. Proposal and handoff + +- [x] 1.1 Record the migration proposal, design, capability requirements, + preliminary adoption mapping and sole linked Speckit feature; verify all + artifacts exist and planning validation is recorded. +- [ ] 1.2 Approve the frozen source/standard revisions, repository visibility, + final path mapping, follow-ups and active-work dispositions after Gates A/B; + verify approval explicitly names the actual execution plan. + +## 2. Migration evidence and closure + +- [ ] 2.1 Accept exact assembled-head, preservation, feature-transition and + rollback evidence from Speckit Gates C–E; verify required CI and ownership + boundaries without enabling unrelated experimental runtime features. +- [ ] 2.2 Archive only after the adopted assembly and selected leg pins land + and continued work is accounted for; verify root delivery and recorded + old-PR/worktree dispositions before closure. diff --git a/openspec/config.yaml b/openspec/config.yaml new file mode 100644 index 00000000..b4bbeb94 --- /dev/null +++ b/openspec/config.yaml @@ -0,0 +1 @@ +schema: spec-driven diff --git a/specs/004-migrate-to-triad/data-model.md b/specs/004-migrate-to-triad/data-model.md new file mode 100644 index 00000000..b36916ca --- /dev/null +++ b/specs/004-migrate-to-triad/data-model.md @@ -0,0 +1,18 @@ +# Migration records + +These records describe migration evidence; they confer no runtime ownership +and never replace lane claims, workspace pointers or the standard's pins. + +| Record | Required facts | +| --- | --- | +| Adoption plan | Source repository/commit, standard revision evidence, names/visibility, reasoned path owners, follow-ups, coverage result and approval binding. Relative paths only in shared artifacts. | +| Work inventory | Observation time, branch/PR/head identity, detached status, readable/dirty indication and snapshot limits. Public `inventory.json` omits host paths and private work. | +| Private preservation receipt | Exact original worktree/binding, branch refs, staged/unstaged/untracked/ignored content digests, backup location and verified restore result. Stored host-locally or in existing private WIP storage. | +| Feature migration receipt | Original merge base/head and captured content; spec/code/root file mapping; resulting branches/commits/checksums; conflict/replay decisions, roles and approval. | +| Composed candidate evidence | Root/spec/code/standard commits, pin/digest consistency, installer payload revision, platform/CI/rehearsal/rollback results and outstanding findings. | +| Cutover approval | Actual candidate/baseline, visibility, leg names, preservation/rehearsal references, lane breakpoints and authorized actions. | + +Generated `elected_by`/date metadata and `execution_authorized: false` in the +preliminary plan are informational. The adoption script does not enforce the +custom planning flag; the operator must not call real `execute` before the +reviewed approval gate. An inventory snapshot is never proof of backed-up work. diff --git a/specs/004-migrate-to-triad/inventory.json b/specs/004-migrate-to-triad/inventory.json new file mode 100644 index 00000000..77254dad --- /dev/null +++ b/specs/004-migrate-to-triad/inventory.json @@ -0,0 +1,156 @@ +{ + "observed_at": "2026-10-03T17:50:46.499925+00:00", + "source_repository": "opensoft/openRepoTools", + "source_main": "c1bac0dfc99a62583cb62eafc93a192911b2ff22", + "standard_commit": "39d5c986fcfac1a160474bfe91c5f1c37fccc72c", + "worktrees": [ + { + "identity": "main", + "head": "c1bac0dfc99a62583cb62eafc93a192911b2ff22", + "layout": "root", + "readable_here": true, + "dirty_entries": 0, + "locked": false, + "prunable": false + }, + { + "identity": "lclaude-finalize", + "head": "7e4dc31c4571cf8d7b11e24941568edf66f82a94", + "layout": "legacy-sibling", + "readable_here": true, + "dirty_entries": 0, + "locked": false, + "prunable": false + }, + { + "identity": "detached-08dad90a67bc", + "head": "08dad90a67bc6699283589c2222c53833a6603cd", + "layout": "legacy-sibling", + "readable_here": true, + "dirty_entries": 0, + "locked": false, + "prunable": false + }, + { + "identity": "001-separate-swap-ctx-handoff", + "head": "fc5105b5371082c966c151f4bc08bfdabf64539b", + "layout": "legacy-sibling", + "readable_here": true, + "dirty_entries": 22, + "locked": false, + "prunable": false + }, + { + "identity": "002-skip-no-lane-guard", + "head": "a2861bbdf0bf4793108bf3b2da2fc65f38d642bd", + "layout": "legacy-sibling", + "readable_here": true, + "dirty_entries": 0, + "locked": false, + "prunable": false + }, + { + "identity": "003-binary-only-guard-runner", + "head": "42860a93be192f025a725708d7a016083ff82923", + "layout": "legacy-sibling", + "readable_here": true, + "dirty_entries": 0, + "locked": false, + "prunable": false + }, + { + "identity": "004-migrate-to-triad", + "head": "c1bac0dfc99a62583cb62eafc93a192911b2ff22", + "layout": "legacy-sibling", + "readable_here": true, + "dirty_entries": 2, + "locked": false, + "prunable": false + }, + { + "identity": "chore/preserve-local-planning-20261003", + "head": "135c18b258d02136ed834e5a446e552db385d50c", + "layout": "legacy-sibling", + "readable_here": true, + "dirty_entries": 0, + "locked": false, + "prunable": false + }, + { + "identity": "detached-92eab6c4fa2e", + "head": "92eab6c4fa2e68f7e2202d529ee0f2e4bebce874", + "layout": "legacy-nested", + "readable_here": true, + "dirty_entries": 0, + "locked": false, + "prunable": false + }, + { + "identity": "fix/claim-takeover-and-retire-fork", + "head": "3de40b1865d31d3bb96a52e3822c1386c9e52634", + "layout": "legacy-nested", + "readable_here": true, + "dirty_entries": 0, + "locked": false, + "prunable": false + }, + { + "identity": "feat/supervised-context-restart", + "head": "5164ce1535b0f4f21d6b9e5b48318865a3c74295", + "layout": "legacy-nested", + "readable_here": true, + "dirty_entries": 0, + "locked": false, + "prunable": false + }, + { + "identity": "feat/crash-consistent-lane-worktree-recovery", + "head": "64dfd9736b1079c16beda0b1ac53a61e68b21d9e", + "layout": "legacy-nested", + "readable_here": true, + "dirty_entries": 0, + "locked": false, + "prunable": false + } + ], + "open_prs": [ + { + "headRefName": "feat/claude-current", + "headRefOid": "715b7ace2f6eeddcf75014e705535fd5c1965ff7", + "isDraft": true, + "number": 134, + "title": "claude-current and claude-restart-check: every launch starts the newest published Claude Code by absolute path, and a running session is told when its binary moved (opensoft/workBenches#119)", + "url": "https://github.com/opensoft/openRepoTools/pull/134" + }, + { + "headRefName": "feat/supervised-context-restart", + "headRefOid": "5164ce1535b0f4f21d6b9e5b48318865a3c74295", + "isDraft": false, + "number": 121, + "title": "`/ctx` writes its restart to disk before it kills anything, and what tmux starts is a supervisor that stays in the pane \u2014 the fresh session was an environment variable the launcher dropped (#94)", + "url": "https://github.com/opensoft/openRepoTools/pull/121" + }, + { + "headRefName": "feat/crash-consistent-lane-worktree-recovery", + "headRefOid": "64dfd9736b1079c16beda0b1ac53a61e68b21d9e", + "isDraft": false, + "number": 97, + "title": "Crash-consistent lane worktree recovery: a lane now says WHERE its session stopped \u2014 RUNNING \u2192 SWAPPING \u2192 SWAPPED with a generation/operation fence, a machine-readable worktree inventory taken at every handoff, and a resume reconciliation that reports and resets nothing", + "url": "https://github.com/opensoft/openRepoTools/pull/97" + }, + { + "headRefName": "fix/claim-takeover-and-retire-fork", + "headRefOid": "3de40b1865d31d3bb96a52e3822c1386c9e52634", + "isDraft": false, + "number": 61, + "title": "claim --force takes over a dead lane's hold; lane-end --retire ends a duplicate holder", + "url": "https://github.com/opensoft/openRepoTools/pull/61" + } + ], + "limitations": [ + "Snapshot only; refresh before baseline freeze and cutover.", + "Physical paths, credentials, transcript bodies and WIP data are omitted.", + "Dirty/untracked work in feature 001 includes edits from this conversation and concurrent planning; preserve the complete current contents.", + "Raw preservation backups must be host-local or in the designated private WIP repository; public planning records hold identity/digest receipts only." + ] +} diff --git a/specs/004-migrate-to-triad/plan.md b/specs/004-migrate-to-triad/plan.md new file mode 100644 index 00000000..1e17ce07 --- /dev/null +++ b/specs/004-migrate-to-triad/plan.md @@ -0,0 +1,162 @@ +# Migration plan: openRepoTools assembly/spec/code triad + +**Feature**: `004-migrate-to-triad` | **Date**: 2026-10-03 +**State**: Proposed plan; no migration, install or repository creation performed +**Spec**: [spec.md](spec.md) +**Governance**: [proposal](../../openspec/changes/migrate-to-triad/proposal.md) + +## Baseline and deliverables + +The [adoption mapping](../../openspec/changes/migrate-to-triad/adoption-plan.yaml) +is generated from main `c1bac0dfc99a62583cb62eafc93a192911b2ff22` using pinned +openRepoShape `39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. All 58 tracked +paths are resolved: 17 spec, 35 code, six root, zero dropped. There are +21 named follow-ups. This is an inspected preliminary baseline; regenerate +after planning lands and the actual cutover source is frozen. + +The [inventory](inventory.json) records 12 worktrees (including migration +planning), four open PRs and dirty feature 001 work. It omits machine paths, +secrets/transcripts and backup contents. Host-local/private preservation +records must capture actual paths, content and digests at the execution gate. + +## Target placement + +| Owner | Existing paths / generated responsibilities | Reason | +| --- | --- | --- | +| Assembly `openRepoTools` | `AGENTS.md`, `CLAUDE.md`, `README.md`, `LICENSE`, `.gitattributes`, `.gitignore`; generated manifest/Makefile/validators and assembly pins | Front door and project coordination across legs. | +| Spec `openRepoTools-spec` | `openspec/`, `specs/`, `docs/`; later feature ideation/governance artifacts | Product requirements, decisions and manual. | +| Code `openRepoTools-code` | All executable tools, `repos.tsv`, `commands/`, `skills/`, `tests/`, implementation CI | Shipped implementation and its installer payload. | +| Code dependency unit | Original `.gitmodules`, `upstream/openRepoShape`, `contracts/openreposhape-pin.yaml` | Keep module registration, exact gitlink and dependency lock together. | +| Root workflow bootstrap | `.specify/`, project agent links and selected feature pointer | Currently local/ignored; capture config and regenerate shape-aware root scaffolding. | + +After byte-preserving extraction, add a root installation entry point that +resolves the exact code pin. The original installer implementation moves to +code; the root entry point selects and invokes it. Preserve the existing user +one-liner and installed command names. Do not duplicate installer mechanics. + +## Phase 1 — Review and preserve work + +Land the planning artifacts through normal review before freezing execution. +Refresh current estate status using `status --fetch`, open PR metadata and +branch/worktree observations. Verify the two proposed leg names are available +and decide visibility. Agree a bounded cutover with current lane owners. +Capture original history, all branch refs, detached HEADs, staged/unstaged and +untracked content, ignored workflow config and installed receipts/config. +Use host-local backups or the existing private WIP repository; public records +hold only safe identities/digests. Prove restoration before declaring capture +complete. Never edit workspace.yaml or recreate estate parking logic. + +Recommended active-work dispositions are proposals, not merge/closure actions: + +| Work | Proposed treatment | +| --- | --- | +| `001-separate-swap-ctx-handoff` | Preserve full branch delta and dirty/untracked additions; migrate to paired `001` spec/code worktrees. Keep tasks, evidence, roles and activation gates. | +| `002-skip-no-lane-guard`, `003-binary-only-guard-runner` | Inspect remaining deltas versus main; migrate useful unfinished work or retain archived branch receipts if already landed. | +| PR #97 | Preserve branch/review; carry useful diagnostics into feature 001 under its service authority. Do not merge unchanged or auto-close as part of adoption. | +| PR #121 | Preserve/review independently; translate selected context-runtime work to code/spec as needed, with its readiness integration still explicit. | +| PR #134 (draft), PR #61 | Keep current heads and review history; continue selected deltas in new legs after owner review. No automatic merge or abandonment. | +| Other branches/detached worktrees/preservation branch | Inspect individually, assign continue/archive decision, retain recovery receipt; no deletion implied. | +| Migration feature `004` | Carry this plan/spec/tasks into the spec leg; assembly/code follow-ups get separate reviewed changes. Keep its own pre-migration recovery receipt. | + +**Gate A:** every preserved object is attributable and restorable; no active +writer can silently invalidate capture. Baseline, visibility and work +dispositions are ready for review, with any unresolved owner decision explicit. + +## Phase 2 — Rehearse locally and make follow-ups reviewable + +Use a fresh isolated source clone at the chosen source commit with no linked +worktrees. Make `git-filter-repo` available through the approved prerequisite +path. Regenerate the mapping, retain reasoned overrides and run standard check. +Rehearse the standard's adoption against temporary local bare remotes with no +real GitHub repository creation or default-branch movement. + +Verify nested dependency extraction, source `.gitmodules` handling, full +baseline accounting, leg histories and recursive bootstrap. Exercise all +actual file-placement collisions. If the standard refuses or misaccounts +content, relay it and resolve through a reviewed standard change/pin bump; +do not patch the pinned submodule or adjust digests to manufacture success. + +Implement and review the necessary installer entry point/resolver, explicit +test-root context, CI layout, agent pointers, docs paths and workflow bootstrap +in the rehearsed arrangement. Record their patches and resulting commits so +real execution can apply the reviewed changes after source verification. +Add leg root guidance without overwriting original root guidance. Preserve +the Bash 3.2 floor and original no-submodule test behavior, while requiring +complete dependencies for composed acceptance. + +**Gate B:** complete local rehearsal evidence, restore rehearsal, reviewed +follow-up patches, resolved mapping and a passing compatibility test matrix. +Mapping `check` alone does not satisfy this gate. + +## Phase 3 — Freeze and approve real adoption + +Coordinate a bounded main landing hold using applicable lane protocol. Re-read +current main and owner state, finalize source commit, mapping, standard pin, +visibility, follow-ups and preservation receipts. Any source drift requires +plan regeneration and check, not editing the commit field by hand. Present +the final plan and follow-ups for explicit approval before real execution. + +**Gate C:** approval names the actual source/standard revisions, leg repositories, +visibility and final plan; prerequisites, Gate A/B evidence and no-name-collision +checks are current. Do not infer consent from this planning request. + +## Phase 4 — Create the legs and assemble the reviewed candidate + +Run the standard's approved `execute` from the isolated clone. It creates +the legs, extracts histories, mounts/pins them, opens the root split PR and +prints content accounting. Relay its table and refusals. A nonzero verification +stops landing; preserve source and newly created evidence/branches. Never rerun +over live legs or bypass a ruleset. + +After successful source verification, apply reviewed follow-ups through the +new leg/root PRs. Resolve `shape/` collisions explicitly; do not overwrite +pinned shape files. Put `Read AGENTS-shape.md first...` guidance first in root +agent instructions and bootstrap shape-aware OpenSpec/Speckit only when both +legs exist. Land required leg changes; advance root gitlinks, pin digests and +workflow SHAs together using the standard bump tool. + +**Gate D:** exact assembled commits pass manifest/pin checks, canonical code +tests/platform checks, install compatibility and bootstrap. The existing +source/install remain usable until root adoption review and merge complete. + +## Phase 5 — Continue features and cut over lanes + +Translate each approved retained feature delta into reviewed spec/code/root +changes and dirty/untracked replay with checksums. Use paired feature branches +and worktrees under `worktrees//{spec,code}`; preserve feature IDs and +map old commits to new leg commits. Review conflicts semantically. Account +for completed/landed work before replaying it. Retain original worktrees. + +Prove feature 001's amended service-owned inventory requirements and new +planning work are present without claiming runtime acceptance. Test feature +selection, OpenSpec/Speckit prerequisites and estate park/resume in disposable +fixtures. Apply supported WIP record updates only through their established +contracts; relay refusals. Rebind each lane at its reviewed safe breakpoint, +using the existing binding tools and exact session records. + +**Gate E:** every continued feature has a reviewed migration receipt and +preserved source; bindings and workflow resolve the intended new trees. +Obsolete PR disposition and any old-worktree retirement are separate explicit +decisions after verification, never an automatic cleanup step. + +## Rollback + +Before root merge, leave the reviewed candidate unmerged and keep original +development/install active. Newly created legs/branches remain for diagnosis; +do not delete them automatically. After root merge, restore the last working +assembly/code selection through reviewed revert/pin-bump PRs and the preserved +installer, without force push/resetting user work. Preserve any post-cutover +feature edits and uncertain jobs before changing bindings. Return lanes only +through supported tooling at a safe breakpoint; private workspace pointers +and claims are not rewritten to hide a failure. + +## Technical context and checks + +Implementation remains Bash (macOS 3.2), Python standard-library tooling and +Git/GitHub workflows. Run build/test/CLI work in the project bench, with shared +`tests/run.sh` locking. No real repository is created for a test. The local +constitution is an unfilled template; no ratification is inferred. Preserve +the existing root rules, standard pin checks and global workflow protocols. + +Planning checks: [verification.md](verification.md). Executable work and +requirement coverage: [tasks.md](tasks.md). All execution tasks remain open. diff --git a/specs/004-migrate-to-triad/quickstart.md b/specs/004-migrate-to-triad/quickstart.md new file mode 100644 index 00000000..a63a83b8 --- /dev/null +++ b/specs/004-migrate-to-triad/quickstart.md @@ -0,0 +1,31 @@ +# Review this migration plan + +Read [plan.md](plan.md), then the [resolved adoption mapping](../../openspec/changes/migrate-to-triad/adoption-plan.yaml) +and [work inventory](inventory.json). The mapping is for the recorded source +main only; implementation and live feature transfer have not been attempted. + +From the original source checkout, the planning validation is: + +```sh +python3 upstream/openRepoShape/adopt-project.py check \ + --source . \ + --plan ../openRepoTools-worktrees/004-migrate-to-triad/openspec/changes/migrate-to-triad/adoption-plan.yaml +``` + +From the migration worktree, validate governance artifacts: + +```sh +openspec validate migrate-to-triad --strict +git diff --check +``` + +Route project CLIs through the declared bench. If source main changes, +regenerate the plan using `adopt-project.py plan`, retain reviewed resolutions +and follow-ups, then recheck. Do not change the baseline field to suppress a +stale-plan finding. Do not run real `execute` as a planning check. + +Before execution, complete Gates A/B and review the actual frozen baseline, +visibility, mapping, follow-ups and preservation receipts. The standard requires +explicit approval before repository creation. All execution tasks are open in +[tasks.md](tasks.md); the migration does not require recreating feature 001's +specifications or claiming its unfinished runtime work is complete. diff --git a/specs/004-migrate-to-triad/research.md b/specs/004-migrate-to-triad/research.md new file mode 100644 index 00000000..2a2f1e04 --- /dev/null +++ b/specs/004-migrate-to-triad/research.md @@ -0,0 +1,22 @@ +# Planning evidence and decisions + +October 3, 2026 inspection used the pinned standard's `AGENTS.md`, adoption +implementation, generated CLI reference and placement policy; root installer, +CI and fixture code; local branch/worktree state; read-only GitHub PR metadata. +No external writes, repositories, installation or migration occurred. + +| Finding | Decision / execution consequence | +| --- | --- | +| Source main has 58 tracked paths and one standard gitlink | Use exact preliminary mapping and refresh the final baseline. | +| Classifier treats shipped Markdown skills/commands as specification | Override to code with written reasons; these are installation payloads. | +| Original contract is the implementation's upstream dependency lock | Keep code pin/gitlink/.gitmodules together; generated assembly pins are separate. | +| `.specify` and agent directories are ignored local scaffolding | Preserve local config separately and regenerate root workflow after adoption. | +| Twelve worktrees and four open PRs; feature 001 is dirty | Source-main extraction is insufficient; preserve and map retained feature deltas. | +| Installer fetches bare paths from `openRepoTools` and local sibling payloads | Preserve public root entry point and bind every payload to one code pin. | +| Tests conflate repo, spec and dependency paths | Introduce explicit checkout context and composed CI, preserving nested-pin tests. | +| `git-filter-repo` absent in inspected bench | Execution prerequisite; no software installation was performed for planning. | +| Standard plan `check` covers names/paths, not execution correctness | Local bare-remote rehearsal must cover existing submodule and blob accounting. | + +Existing-submodule behavior, exact installation resolver implementation and +live feature replay are pending measured execution tasks, not assumed successes. +The final creation approval also confirms proposed public visibility. diff --git a/specs/004-migrate-to-triad/spec.md b/specs/004-migrate-to-triad/spec.md new file mode 100644 index 00000000..6799586e --- /dev/null +++ b/specs/004-migrate-to-triad/spec.md @@ -0,0 +1,85 @@ +# Feature specification: migrate openRepoTools to the triad + +**Feature**: `004-migrate-to-triad` +**Created**: 2026-10-03 +**Status**: Proposed; planning complete enough for review, execution gated +**Governing change**: [migrate-to-triad](../../openspec/changes/migrate-to-triad/proposal.md) + +## User scenarios + +### US1 — Preserve history and unfinished work (P1) + +The operator reviews an exact adoption mapping and current branch/worktree +inventory, then converts the existing repository without losing unpublished +or dirty work. Validate byte-accounted baseline extraction, nested dependency +preservation, and replay receipts for a mixed spec/code feature and its new files. +Missing content, stale baseline, unavailable backup or an unresolved placement +blocks execution/cutover. Preserve source branches and local work on refusal. + +### US2 — Install through the existing entry point (P1) + +A user runs the existing raw-URL or `gh api` one-liner, or installs from an +offline local checkout. Every payload comes from one selected immutable code +revision; command names, permissions, artifact counts, hooks and receipts are +preserved. Test forks/ref overrides, missing pins and partial fetch refusal. + +### US3 — Continue feature development and deliver exact pins (P2) + +The operator continues existing feature IDs through paired spec/code worktrees +and lands their changes through leg PRs followed by a validated assembly pin +bump. OpenSpec runs in spec; Speckit runs from the assembly with selected +feature paths. A code-only clone cannot falsely claim composed CI success. +The migration preserves feature 001's model-role assignments and open gates. + +## Functional requirements + +- **FR-001**: Keep `opensoft/openRepoTools` as assembly with full original + history; extract spec/code using the reviewed standard procedure. +- **FR-002**: Pin a source commit; resolve every path with reasons, account + for blobs/gitlinks, and reject a stale or unresolved plan. +- **FR-003**: Preserve and verify all active branches/PRs, detached worktrees, + dirty/untracked content and ignored workflow configuration before cutover. +- **FR-004**: Preserve the code dependency unit: `.gitmodules`, + `upstream/openRepoShape` and `contracts/openreposhape-pin.yaml`; verify nested + extraction/recursive bootstrap and retain the standard's exact pin semantics. +- **FR-005**: Keep original installation entry points and one immutable payload + revision, including forks/ref overrides, local/offline behavior and existing + all-or-none ownership/receipt semantics; do not depend on developer checkouts. +- **FR-006**: Put product OpenSpec/Speckit/docs in spec and implementation plus + shipped skills/commands/tests in code; project workflow stays at root. +- **FR-007**: Continue selected feature deltas with old/new identity and + content receipts, paired leg worktrees and unchanged feature/role meanings. + Do not merge old monorepo branches over the adopted assembly. +- **FR-008**: Run canonical serialized tests and existing platform/CI policies + against explicitly selected compatible roots; preserve required checks and + prohibit silent context/dependency skips from claiming composed acceptance. +- **FR-009**: Land leg changes before the root pin bump; move gitlinks, pin + records/digests and applicable workflow SHAs consistently with standard tools. +- **FR-010**: Require a clean isolated source, available prerequisites, local + bare-remote rehearsal and explicit approval before real repository creation. +- **FR-011**: Preserve original worktrees and installed tools until cutover + acceptance; use non-destructive rollback and existing estate/WIP contracts. +- **FR-012**: Treat visibility, baseline, branch disposition and lane rebinding + as reviewed execution decisions; never infer approval from generated metadata. + +## Success criteria + +- **SC-001**: Every frozen baseline source path is accounted for exactly once; + no unapproved drops or content mismatches. +- **SC-002**: Every retained feature's selected spec/code/root delta and + dirty/untracked work has a verified receipt and preserved original. +- **SC-003**: Installation works through original entry points, offline and + fork/ref scenarios, using one code revision with unchanged artifact behavior. +- **SC-004**: Recursive bootstrap, paired feature workflow, canonical tests and + required CI pass for the selected composed project; no hidden skips. +- **SC-005**: A rehearsed rollback restores usable development/installation + without discarding work, altering private workspace pointers or force pushes. + +## Assumptions and approval + +Public new legs are proposed to match source visibility. Their creation is +not approved. `git-filter-repo` must be made available before rehearsal/execution; +it was absent in the inspected bench. The local constitution remains an +unfilled template and is not a ratified migration rule; repository/global +protocols and the pinned standard govern this plan. Refresh all snapshots +before execution. Source token/swap runtime gates are unaffected. diff --git a/specs/004-migrate-to-triad/tasks.md b/specs/004-migrate-to-triad/tasks.md new file mode 100644 index 00000000..4ce3039b --- /dev/null +++ b/specs/004-migrate-to-triad/tasks.md @@ -0,0 +1,124 @@ +# Implementation tasks: migrate openRepoTools to the triad + +The sole executable task list for the governing OpenSpec change. Planning +artifacts and preliminary mapping validation do not complete these tasks. +Every task remains open. Run implementation from the selected feature +worktrees; actual repository creation/cutover require the approval gates. + +## Phase 1 — Preservation and baseline (Gate A) + +- [ ] T001 [US1] Refresh `inventory.json`, current `status --fetch` output and + PR/branch/worktree metadata; resolve continuation/archive decisions for every + object in a host-local/private migration receipt. Verify no branch, detached + worktree or dirty/untracked/local-workflow content is omitted. +- [ ] T002 [US1] Coordinate owner breakpoints and preserve source refs/content, + ignored workflow configuration and installed receipts/config using the + existing estate/private backup contract. Verify restoration and per-object + digests, including all feature 001 edits and new files, without altering + workspace.yaml or broad process actions. +- [ ] T003 [US1] Confirm proposed leg names/visibility and inspect prerequisite + availability; regenerate `adoption-plan.yaml` for the reviewed preliminary + rehearsal source, retaining reasoned overrides. Verify standard `check` + passes and records all source paths with no unapproved drops. + +## Phase 2 — Local adoption rehearsal (Gate B foundation) + +- [ ] T004 [US1] Prepare a clean isolated source clone with no linked + worktrees and approved `git-filter-repo` availability; rehearse the standard + adoption against temporary local bare remotes. Verify source identity/history, + path/blob accounting and recoverability without real GitHub creation. +- [ ] T005 [US1] Exercise the existing nested submodule path, original + `.gitmodules` extraction/removal, pin integrity and recursive bootstrap. + Verify the code gitlink/pin/registration remain coupled. If standard tooling + refuses, record its refusal and resolve upstream before a tested pin bump; + never patch `upstream/openRepoShape` in place or weaken verification. +- [ ] T006 [US3] Resolve materialized root collisions and new leg guidance in + reviewable patches; bootstrap selected root workflow after manifest/legs + exist. Verify first-line shape guidance, relative paths, actual `.specify` + configuration and paired feature selection without copying machine secrets. + +## Phase 3 — Installation and checks (Gate B completion) + +- [ ] T007 [US2] Add the assembly `openRepoTools` entry point and coherent + payload-source resolution in the code installer after source byte accounting. + Verify gh/raw one-liners resolve one code pin and do not duplicate installer + mechanics or depend on developer checkouts after installation. +- [ ] T008 [US2] Cover local/offline, legacy monorepo overrides, adopted forks, + branch/commit refs, immutable code selection, missing/mismatched pins and + failed partial fetch. Verify unchanged command/artifact names, counts, + destinations/modes, hooks, ownership receipts and all-or-none refusal. +- [ ] T009 [US3] Separate code/spec/assembly/dependency roots in fixture and + hygiene consumers, including docs/OpenSpec/Speckit references. Verify the + existing upstream pin checks run against code Git identity and required + composed checks cannot silently skip absent spec/dependency context. +- [ ] T010 [US3] Adapt implementation CI and root exact-pin integration checks + with explicit compatible checkout context; preserve current Linux/macOS/ + Windows/WSL policies, Bash 3.2 parsing and serialized `tests/run.sh` locking. + Verify the exact rehearsed composed state passes required checks and the + compatibility matrix, recording failures rather than deleting assertions. +- [ ] T011 [US1] Rehearse restoration/rollback of the local candidate and + installed payload using preserved artifacts. Verify user work/jobs/claims + survive and no force push, destructive reset or workspace-pointer rewrite + is needed. Publish Gate A/B evidence and follow-up patch/commit inventory. + +## Phase 4 — Reviewed real adoption (Gates C and D) + +- [ ] T012 [US1] Freeze the coordinated source baseline and regenerate/check + the full plan after any prerequisite/planning landings. Obtain explicit + approval naming revisions, leg repositories/visibility, mapping, follow-ups + and work dispositions. Verify current name availability, preservation and + rehearsal evidence; refuse any source drift before execution. +- [ ] T013 [US1] Execute the approved standard adoption from the isolated + clone. Verify and relay the complete content-accounting table and root PR + details; stop landing on any refusal/mismatch, retaining all source/evidence. +- [ ] T014 [US3] Apply reviewed follow-ups through leg/root PRs, land required + leg commits, and advance root gitlinks/pins/workflow SHAs with standard tools. + Verify no in-place edits to copied pinned shape files or the mounted standard. +- [ ] T015 [US2] Validate the real exact assembled head using canonical tests, + required platform/CI, installed/local/raw compatibility, bootstrap and rollback + checks. Verify all Gate D results belong to the selected commits before root + adoption merge; keep original development/install available until then. + +## Phase 5 — Continue features and controlled cutover (Gate E) + +- [ ] T016 [US1] Translate the complete approved feature 001 delta and preserved + dirty/untracked work into paired `001` spec/code branches/worktrees with + reviewed root changes. Verify content/old-to-new receipts, roles, inventory + amendment and open runtime gates; do not replay completed work or claim new + runtime acceptance from migration. +- [ ] T017 [US1] Translate every other approved continued PR/branch/detached + worktree, including this migration feature, with explicit spec/code/root + mappings and conflict review. Verify each object has a complete receipt or + approved archived-original disposition; preserve original PR review history. +- [ ] T018 [US3] Exercise OpenSpec/Speckit feature selection and estate + park/resume in disposable triad fixtures, then perform reviewed binding/WIP + transitions through supported tools at owner breakpoints. Verify correct + feature roots/session bindings and relay refusals without force repairs. +- [ ] T019 [US3] Publish Gate E/cutover evidence and actual PR dispositions; + verify all continued features are usable and recoverable before requesting + separate old-worktree retirement. Do not auto-close old PRs or delete backups. + +## Dependencies + +T001–T003 precede rehearsal. T004–T006 precede integration validation; +T007–T010 may progress within the rehearsed topology after byte accounting. +T011 closes Gate B. T012 requires Gate A/B and is the real-creation approval +boundary. T013–T015 precede root adoption merge. T016–T018 can be prepared in +the candidate after legs exist, but actual lane rebinding follows reviewed +cutover and safe breakpoints. T019 requires validated retained feature receipts. + +## Requirement coverage + +| Requirement | Tasks | +| --- | --- | +| FR-001, FR-002 | T003–T005, T012–T013 | +| FR-003 | T001–T002, T016–T017 | +| FR-004 | T005, T009, T014 | +| FR-005 | T007–T008, T015 | +| FR-006 | T003, T006, T009 | +| FR-007 | T016–T018 | +| FR-008 | T009–T010, T015 | +| FR-009 | T014–T015 | +| FR-010 | T003–T005, T011–T013 | +| FR-011 | T002, T011, T018–T019 | +| FR-012 | T001, T012, T018–T019 | diff --git a/specs/004-migrate-to-triad/verification.md b/specs/004-migrate-to-triad/verification.md new file mode 100644 index 00000000..8d3a138e --- /dev/null +++ b/specs/004-migrate-to-triad/verification.md @@ -0,0 +1,42 @@ +# Migration planning verification — October 3, 2026 + +## Passing planning checks + +- Pinned openRepoShape `adopt-project.py check` passed for the preliminary + source main `c1bac0dfc99a62583cb62eafc93a192911b2ff22` and resolved mapping: + 58 source paths, 17 spec / 35 code / six root, no unresolved path or drop. + Naming recognizes the existing root as neutral-product/assembly and the + proposed repositories as spec/code legs. The plan lists 21 follow-ups. +- `openspec validate migrate-to-triad --strict` passed in `py-bench`. + `openspec status` reports all four governance planning artifacts complete. +- `git diff --check` passed. Supplemental checks cover newly created planning + files, local Markdown targets, host-absolute path exclusion, valid inventory + JSON, unique task IDs and requirement coverage. +- All 19 executable migration tasks remain open. The main checkout is clean; + planning artifacts live only in feature `004-migrate-to-triad`. + +## Snapshot and interpretation + +`inventory.json` records 12 registered worktrees and open PRs #134, #121, #97 +and #61. Feature 001 has dirty/untracked content, including work from this +conversation and concurrent planning. Preserve its complete current contents +at the execution gate; the public snapshot does not contain a backup. + +The generated adoption plan was resolved with written placement reasons and +normalized to relative source paths. It is a preliminary main-only mapping, +not a mapping of all feature work. Regenerate after planning/prerequisite +landings and before frozen-baseline approval. Custom planning/authorization +fields document scope; the standard does not enforce them as execution guards. + +## Not performed or claimed + +No local history extraction/adoption rehearsal, runtime regression suite, +installer changes, real repository creation, software installation, root/leg +PR creation or merge, lane move, WIP pointer rewrite or migration occurred. +`git-filter-repo` was absent in the inspected bench. Existing nested-submodule +handling and actual composed tests remain required Gate B/D evidence. + +Planning validity does not establish execution readiness or approve proposed +public visibility. The standard's procedure requires the reviewed plan and +follow-ups to receive an explicit yes before real adoption. Original work, +experimental swap gates and existing PR review decisions remain preserved. From 8aa119fd5c4286ff836ee2c9c6ee141859cb3710 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sat, 3 Oct 2026 22:14:21 +0000 Subject: [PATCH 2/9] docs: refresh triad migration plan after worktree cleanup --- .../migrate-to-triad/adoption-plan.yaml | 24 +- openspec/changes/migrate-to-triad/design.md | 14 +- openspec/changes/migrate-to-triad/proposal.md | 8 +- specs/004-migrate-to-triad/inventory.json | 227 +++++++++++------- specs/004-migrate-to-triad/plan.md | 40 ++- specs/004-migrate-to-triad/research.md | 7 +- specs/004-migrate-to-triad/tasks.md | 15 +- specs/004-migrate-to-triad/verification.md | 36 ++- 8 files changed, 252 insertions(+), 119 deletions(-) diff --git a/openspec/changes/migrate-to-triad/adoption-plan.yaml b/openspec/changes/migrate-to-triad/adoption-plan.yaml index c32025d1..ec7eb3f9 100644 --- a/openspec/changes/migrate-to-triad/adoption-plan.yaml +++ b/openspec/changes/migrate-to-triad/adoption-plan.yaml @@ -8,10 +8,10 @@ source: repository: opensoft/openRepoTools local_path: . default_branch: main - commit: c1bac0dfc99a62583cb62eafc93a192911b2ff22 + commit: "82ecebee13edaa915b68550170faffdf761754d5" files: 58 - bytes: 4202017 - commits: 77 + bytes: 4339687 + commits: 79 mode: in-place project: openRepoTools org: opensoft @@ -126,7 +126,7 @@ paths: rule: spec-governance reason: "Requirements, decisions, contracts and governance prose — what the project has DECIDED, as opposed to what it currently runs. `contracts/` is here by the 2026-09-02 ruling even where the code reads it: the code reads across the assembly-root mount (`spec/contracts` from the root, `../spec/contracts` from `code/`), which is what the legs are mounted for." files: 1 - bytes: 182097 + bytes: 187100 review_required: false - path: lane leg: code @@ -144,7 +144,7 @@ paths: rule: default reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." files: 1 - bytes: 93296 + bytes: 119587 review_required: false question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." @@ -194,7 +194,7 @@ paths: rule: extension-majority reason: "no rule names this path; the `.sh` extension is classified as code by the policy's extension table" files: 1 - bytes: 759480 + bytes: 807509 review_required: false - path: link-estates leg: code @@ -287,7 +287,7 @@ paths: rule: code-source-and-tests reason: "The implementation, the tests that hold it, and the scripts and images that build and run it. `scripts/` moves whole: a harness that reads `contracts/` from beside it reads them across the root mount afterwards, which is a code change the plan records as a follow-up rather than a reason to split the directory." files: 12 - bytes: 1880040 + bytes: 1938387 review_required: false - path: upstream/ leg: code @@ -324,19 +324,19 @@ follow_ups: summary: spec: files: 17 - bytes: 202997 + bytes: 208000 file_share: 0.2931 - byte_share: 0.0483 + byte_share: 0.0479 code: files: 35 - bytes: 3934978 + bytes: 4067645 file_share: 0.6034 - byte_share: 0.9364 + byte_share: 0.9373 root: files: 6 bytes: 64042 file_share: 0.1034 - byte_share: 0.0152 + byte_share: 0.0148 unresolved: files: 0 bytes: 0 diff --git a/openspec/changes/migrate-to-triad/design.md b/openspec/changes/migrate-to-triad/design.md index 691aeee7..6579bce0 100644 --- a/openspec/changes/migrate-to-triad/design.md +++ b/openspec/changes/migrate-to-triad/design.md @@ -2,11 +2,15 @@ ## Context -The preliminary source is main `c1bac0dfc99a62583cb62eafc93a192911b2ff22`: -58 tracked paths, 77 commits, one nested standard submodule. The inspected -standard is `39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. There are 12 registered -worktrees including this planning checkout, and four open PRs. Feature 001 has -dirty/untracked work; an extraction of main alone would omit it. The +The refreshed preliminary source is main `82ecebee13edaa915b68550170faffdf761754d5`: +58 tracked paths, 79 commits, one nested standard submodule. The inspected +standard is `39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. Cleanup initially left +five registered worktrees; reopening the preserved planning checkout made +six, then concurrent cleanup removed the merged #146 checkout. There are now +five worktrees including planning. The three open PRs are #134, #121 and #97; +#61 and #146 are included in main and need no separate replay. +Feature 001 at `c0b571c` has dirty/untracked work, so main extraction alone +would omit it. Removed worktrees are not an active replay queue. The [inventory](../../../specs/004-migrate-to-triad/inventory.json) is a snapshot. ## Goals / Non-Goals diff --git a/openspec/changes/migrate-to-triad/proposal.md b/openspec/changes/migrate-to-triad/proposal.md index 8253222b..3ffdd9ba 100644 --- a/openspec/changes/migrate-to-triad/proposal.md +++ b/openspec/changes/migrate-to-triad/proposal.md @@ -63,6 +63,12 @@ existing changes; this migration does not activate experimental capabilities. Requested on October 3, 2026. Planning is authorized; migration execution, repository creation, software installation, PR merge and lane relocation are not authorized by this request. The attached adoption plan is a preliminary -mapping of source main `c1bac0dfc99a62583cb62eafc93a192911b2ff22`, not a promise +mapping regenerated from source main `82ecebee13edaa915b68550170faffdf761754d5`, not a promise that this will be the final split commit. Refresh after planning lands and the cutover baseline is frozen. No runtime or migration success is claimed. + +The cleanup refresh records five worktrees before editing, six after reopening +the planning checkout, then five after concurrent cleanup removed the merged +#146 checkout. It removes retired worktrees from active migration and accounts +for both #61 and #146 through main. Feature 001 and remote-only drafts/preservation refs retain +their explicit preservation/review requirements. diff --git a/specs/004-migrate-to-triad/inventory.json b/specs/004-migrate-to-triad/inventory.json index 77254dad..d9719707 100644 --- a/specs/004-migrate-to-triad/inventory.json +++ b/specs/004-migrate-to-triad/inventory.json @@ -1,156 +1,219 @@ { - "observed_at": "2026-10-03T17:50:46.499925+00:00", + "observed_at": "2026-10-03T22:12:05.918851+00:00", "source_repository": "opensoft/openRepoTools", - "source_main": "c1bac0dfc99a62583cb62eafc93a192911b2ff22", + "source_main": "82ecebee13edaa915b68550170faffdf761754d5", + "github_main": "82ecebee13edaa915b68550170faffdf761754d5", + "github_main_matches_local": true, "standard_commit": "39d5c986fcfac1a160474bfe91c5f1c37fccc72c", + "cleanup_summary": { + "previous_snapshot_worktrees": 12, + "worktrees_before_refresh": 5, + "local_branches_before_refresh": 6, + "worktrees_after_reopening_before_concurrent_cleanup": 6, + "registered_worktrees_including_reopened_planning_checkout": 5, + "local_branches_after_concurrent_cleanup": 5, + "planning_checkout_reopened": "004-migrate-to-triad", + "removed_before_refresh": [ + { + "identity": "lclaude-finalize", + "previous_head": "7e4dc31c4571cf8d7b11e24941568edf66f82a94", + "local_branch_present": false + }, + { + "identity": "detached-08dad90a67bc", + "previous_head": "08dad90a67bc6699283589c2222c53833a6603cd", + "local_branch_present": false + }, + { + "identity": "002-skip-no-lane-guard", + "previous_head": "a2861bbdf0bf4793108bf3b2da2fc65f38d642bd", + "local_branch_present": false + }, + { + "identity": "003-binary-only-guard-runner", + "previous_head": "42860a93be192f025a725708d7a016083ff82923", + "local_branch_present": false + }, + { + "identity": "004-migrate-to-triad", + "previous_head": "c1bac0dfc99a62583cb62eafc93a192911b2ff22", + "local_branch_present": true + }, + { + "identity": "chore/preserve-local-planning-20261003", + "previous_head": "135c18b258d02136ed834e5a446e552db385d50c", + "local_branch_present": false + }, + { + "identity": "detached-92eab6c4fa2e", + "previous_head": "92eab6c4fa2e68f7e2202d529ee0f2e4bebce874", + "local_branch_present": false + }, + { + "identity": "fix/claim-takeover-and-retire-fork", + "previous_head": "3de40b1865d31d3bb96a52e3822c1386c9e52634", + "local_branch_present": false + } + ], + "new_worktrees_since_previous_snapshot": [], + "worktrees_after_concurrent_cleanup_excluding_planning_checkout": 4, + "removed_during_refresh": [ + { + "identity": "fix/107-pty-driver-bound-and-lane-per-call-cost", + "previous_head": "da0e5bb257b3b2b60a57ca544b7c75528c97f2a5", + "local_branch_present": false, + "reason": "PR #146 merged into main; concurrent cleanup removed checkout and local branch." + } + ] + }, "worktrees": [ { "identity": "main", - "head": "c1bac0dfc99a62583cb62eafc93a192911b2ff22", + "head": "82ecebee13edaa915b68550170faffdf761754d5", "layout": "root", "readable_here": true, "dirty_entries": 0, + "untracked_entries": 0, "locked": false, "prunable": false }, { - "identity": "lclaude-finalize", - "head": "7e4dc31c4571cf8d7b11e24941568edf66f82a94", - "layout": "legacy-sibling", - "readable_here": true, - "dirty_entries": 0, - "locked": false, - "prunable": false - }, - { - "identity": "detached-08dad90a67bc", - "head": "08dad90a67bc6699283589c2222c53833a6603cd", + "identity": "001-separate-swap-ctx-handoff", + "head": "c0b571c5fa1c1446f143ce4f00f39db7cb7a166b", "layout": "legacy-sibling", "readable_here": true, - "dirty_entries": 0, + "dirty_entries": 18, + "untracked_entries": 1, "locked": false, "prunable": false }, { - "identity": "001-separate-swap-ctx-handoff", - "head": "fc5105b5371082c966c151f4bc08bfdabf64539b", + "identity": "004-migrate-to-triad", + "head": "52ce64ee7787b62bf015e13cbf663f305513093e", "layout": "legacy-sibling", "readable_here": true, - "dirty_entries": 22, + "dirty_entries": 8, + "untracked_entries": 0, "locked": false, "prunable": false }, { - "identity": "002-skip-no-lane-guard", - "head": "a2861bbdf0bf4793108bf3b2da2fc65f38d642bd", - "layout": "legacy-sibling", + "identity": "feat/supervised-context-restart", + "head": "5164ce1535b0f4f21d6b9e5b48318865a3c74295", + "layout": "legacy-nested", "readable_here": true, "dirty_entries": 0, + "untracked_entries": 0, "locked": false, "prunable": false }, { - "identity": "003-binary-only-guard-runner", - "head": "42860a93be192f025a725708d7a016083ff82923", - "layout": "legacy-sibling", + "identity": "feat/crash-consistent-lane-worktree-recovery", + "head": "64dfd9736b1079c16beda0b1ac53a61e68b21d9e", + "layout": "legacy-nested", "readable_here": true, "dirty_entries": 0, + "untracked_entries": 0, "locked": false, "prunable": false + } + ], + "local_branches": [ + { + "identity": "001-separate-swap-ctx-handoff", + "head": "c0b571c5fa1c1446f143ce4f00f39db7cb7a166b" }, { "identity": "004-migrate-to-triad", - "head": "c1bac0dfc99a62583cb62eafc93a192911b2ff22", - "layout": "legacy-sibling", - "readable_here": true, - "dirty_entries": 2, - "locked": false, - "prunable": false + "head": "52ce64ee7787b62bf015e13cbf663f305513093e" }, { - "identity": "chore/preserve-local-planning-20261003", - "head": "135c18b258d02136ed834e5a446e552db385d50c", - "layout": "legacy-sibling", - "readable_here": true, - "dirty_entries": 0, - "locked": false, - "prunable": false + "identity": "feat/crash-consistent-lane-worktree-recovery", + "head": "64dfd9736b1079c16beda0b1ac53a61e68b21d9e" }, { - "identity": "detached-92eab6c4fa2e", - "head": "92eab6c4fa2e68f7e2202d529ee0f2e4bebce874", - "layout": "legacy-nested", - "readable_here": true, - "dirty_entries": 0, - "locked": false, - "prunable": false + "identity": "feat/supervised-context-restart", + "head": "5164ce1535b0f4f21d6b9e5b48318865a3c74295" }, { - "identity": "fix/claim-takeover-and-retire-fork", - "head": "3de40b1865d31d3bb96a52e3822c1386c9e52634", - "layout": "legacy-nested", - "readable_here": true, - "dirty_entries": 0, - "locked": false, - "prunable": false + "identity": "main", + "head": "82ecebee13edaa915b68550170faffdf761754d5" + } + ], + "remote_only_branches": [ + { + "identity": "chore/preserve-local-planning-20261003", + "head": "135c18b258d02136ed834e5a446e552db385d50c", + "currency": "local remote-tracking observation; not fetched during refresh" }, { - "identity": "feat/supervised-context-restart", - "head": "5164ce1535b0f4f21d6b9e5b48318865a3c74295", - "layout": "legacy-nested", - "readable_here": true, - "dirty_entries": 0, - "locked": false, - "prunable": false + "identity": "feat/claude-current", + "head": "715b7ace2f6eeddcf75014e705535fd5c1965ff7", + "currency": "local remote-tracking observation; not fetched during refresh" }, { - "identity": "feat/crash-consistent-lane-worktree-recovery", - "head": "64dfd9736b1079c16beda0b1ac53a61e68b21d9e", - "layout": "legacy-nested", - "readable_here": true, - "dirty_entries": 0, - "locked": false, - "prunable": false + "identity": "takeover/ort-a9-dirty-snapshot-20260913", + "head": "607aee7551a5a7db7e57e294aba193fbc8a8a266", + "currency": "local remote-tracking observation; not fetched during refresh" } ], "open_prs": [ { + "number": 134, + "title": "claude-current and claude-restart-check: every launch starts the newest published Claude Code by absolute path, and a running session is told when its binary moved (opensoft/workBenches#119)", "headRefName": "feat/claude-current", "headRefOid": "715b7ace2f6eeddcf75014e705535fd5c1965ff7", "isDraft": true, - "number": 134, - "title": "claude-current and claude-restart-check: every launch starts the newest published Claude Code by absolute path, and a running session is told when its binary moved (opensoft/workBenches#119)", - "url": "https://github.com/opensoft/openRepoTools/pull/134" + "url": "https://github.com/opensoft/openRepoTools/pull/134", + "state": "open" }, { + "number": 121, + "title": "`/ctx` writes its restart to disk before it kills anything, and what tmux starts is a supervisor that stays in the pane \u2014 the fresh session was an environment variable the launcher dropped (#94)", "headRefName": "feat/supervised-context-restart", "headRefOid": "5164ce1535b0f4f21d6b9e5b48318865a3c74295", "isDraft": false, - "number": 121, - "title": "`/ctx` writes its restart to disk before it kills anything, and what tmux starts is a supervisor that stays in the pane \u2014 the fresh session was an environment variable the launcher dropped (#94)", - "url": "https://github.com/opensoft/openRepoTools/pull/121" + "url": "https://github.com/opensoft/openRepoTools/pull/121", + "state": "open" }, { + "number": 97, + "title": "Crash-consistent lane worktree recovery: a lane now says WHERE its session stopped \u2014 RUNNING \u2192 SWAPPING \u2192 SWAPPED with a generation/operation fence, a machine-readable worktree inventory taken at every handoff, and a resume reconciliation that reports and resets nothing", "headRefName": "feat/crash-consistent-lane-worktree-recovery", "headRefOid": "64dfd9736b1079c16beda0b1ac53a61e68b21d9e", "isDraft": false, - "number": 97, - "title": "Crash-consistent lane worktree recovery: a lane now says WHERE its session stopped \u2014 RUNNING \u2192 SWAPPING \u2192 SWAPPED with a generation/operation fence, a machine-readable worktree inventory taken at every handoff, and a resume reconciliation that reports and resets nothing", - "url": "https://github.com/opensoft/openRepoTools/pull/97" - }, + "url": "https://github.com/opensoft/openRepoTools/pull/97", + "state": "open" + } + ], + "merged_since_previous_snapshot": [ { - "headRefName": "fix/claim-takeover-and-retire-fork", - "headRefOid": "3de40b1865d31d3bb96a52e3822c1386c9e52634", - "isDraft": false, "number": 61, - "title": "claim --force takes over a dead lane's hold; lane-end --retire ends a duplicate holder", - "url": "https://github.com/opensoft/openRepoTools/pull/61" + "state": "MERGED", + "merged_at": "2026-10-03T17:55:42Z", + "merge_commit": "14641fd67cee2985c215fc820219c9906935bdf3", + "disposition": "Included in current source main; no separate feature replay." + }, + { + "number": 146, + "state": "MERGED", + "merged_at": "2026-10-03T22:07:15Z", + "merge_commit": "82ecebee13edaa915b68550170faffdf761754d5", + "disposition": "Included in current source main; no separate feature replay." } ], + "estate_status": { + "command": "status --fetch openRepoTools", + "result": "refused-not-an-estate", + "note": "No project.yaml/family.yaml estate root yet; no fetch performed by this refusal. Local refs and read-only GitHub main/PR APIs were checked instead. Do not run resume to silence it." + }, "limitations": [ "Snapshot only; refresh before baseline freeze and cutover.", "Physical paths, credentials, transcript bodies and WIP data are omitted.", - "Dirty/untracked work in feature 001 includes edits from this conversation and concurrent planning; preserve the complete current contents.", - "Raw preservation backups must be host-local or in the designated private WIP repository; public planning records hold identity/digest receipts only." + "Feature 001 retains dirty/untracked work including current broker/task-recovery planning; preserve its complete current contents.", + "Removed worktrees are cleanup history, not instructions to recreate them; verify retained backup/branch receipts before final cutover.", + "Remote-only preservation refs are local observations, not active worktrees or proof of fresh remote currency.", + "Raw preservation backups belong host-locally or in the designated private WIP repository; public records hold identities/digests only.", + "Inventory captures this planning branch before the refresh commit; its final HEAD is recorded by git." ] } diff --git a/specs/004-migrate-to-triad/plan.md b/specs/004-migrate-to-triad/plan.md index 1e17ce07..27bd12b6 100644 --- a/specs/004-migrate-to-triad/plan.md +++ b/specs/004-migrate-to-triad/plan.md @@ -2,22 +2,33 @@ **Feature**: `004-migrate-to-triad` | **Date**: 2026-10-03 **State**: Proposed plan; no migration, install or repository creation performed +**Refresh**: October 3 cleanup; current source main and active-work queue rechecked **Spec**: [spec.md](spec.md) **Governance**: [proposal](../../openspec/changes/migrate-to-triad/proposal.md) ## Baseline and deliverables The [adoption mapping](../../openspec/changes/migrate-to-triad/adoption-plan.yaml) -is generated from main `c1bac0dfc99a62583cb62eafc93a192911b2ff22` using pinned +is regenerated from main `82ecebee13edaa915b68550170faffdf761754d5` using pinned openRepoShape `39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. All 58 tracked paths are resolved: 17 spec, 35 code, six root, zero dropped. There are 21 named follow-ups. This is an inspected preliminary baseline; regenerate after planning lands and the actual cutover source is frozen. -The [inventory](inventory.json) records 12 worktrees (including migration -planning), four open PRs and dirty feature 001 work. It omits machine paths, -secrets/transcripts and backup contents. Host-local/private preservation -records must capture actual paths, content and digests at the execution gate. +Cleanup initially reduced the original 12-worktree snapshot to five registered +worktrees. Reopening the preserved `004` branch made six; concurrent cleanup +then removed the merged PR #146 checkout. There are now five worktrees, +including this planning checkout, and five local branches. The +[inventory](inventory.json) records those transitions, three open PRs (#134, +#121, #97), remote-only refs and retired-worktree history. Main includes merged +PRs #61 and #146 and matches the read-only GitHub main observation. Feature 001 at +`c0b571c5fa1c1446f143ce4f00f39db7cb7a166b` still has dirty/untracked broker +and task-recovery planning work. Machine paths, secrets/transcripts and backup +contents are omitted; private receipts must capture them at the execution gate. + +Cleanup does not require recreating removed worktrees. Continue only the +retained work below, and verify existing preservation receipts for retired +objects. Snapshot refresh alone does not complete preservation Gate A. ## Target placement @@ -37,8 +48,11 @@ one-liner and installed command names. Do not duplicate installer mechanics. ## Phase 1 — Review and preserve work Land the planning artifacts through normal review before freezing execution. -Refresh current estate status using `status --fetch`, open PR metadata and -branch/worktree observations. Verify the two proposed leg names are available +Refresh GitHub PR/main observations and local branch/worktree state. This +single repository has no estate manifest yet: `status --fetch openRepoTools` +currently refuses with no estate named openRepoTools and performs no fetch. +Relay that refusal; do not run `resume` to silence it. Use the estate status +surface after a valid adopted root exists. Verify the proposed leg names are available and decide visibility. Agree a bounded cutover with current lane owners. Capture original history, all branch refs, detached HEADs, staged/unstaged and untracked content, ignored workflow config and installed receipts/config. @@ -50,13 +64,15 @@ Recommended active-work dispositions are proposals, not merge/closure actions: | Work | Proposed treatment | | --- | --- | -| `001-separate-swap-ctx-handoff` | Preserve full branch delta and dirty/untracked additions; migrate to paired `001` spec/code worktrees. Keep tasks, evidence, roles and activation gates. | -| `002-skip-no-lane-guard`, `003-binary-only-guard-runner` | Inspect remaining deltas versus main; migrate useful unfinished work or retain archived branch receipts if already landed. | +| `001-separate-swap-ctx-handoff` | Continue from current head `c0b571c`, preserving its full delta and dirty/untracked broker/task-recovery additions; migrate to paired `001` worktrees. Keep tasks, evidence, roles and activation gates. | | PR #97 | Preserve branch/review; carry useful diagnostics into feature 001 under its service authority. Do not merge unchanged or auto-close as part of adoption. | | PR #121 | Preserve/review independently; translate selected context-runtime work to code/spec as needed, with its readiness integration still explicit. | -| PR #134 (draft), PR #61 | Keep current heads and review history; continue selected deltas in new legs after owner review. No automatic merge or abandonment. | -| Other branches/detached worktrees/preservation branch | Inspect individually, assign continue/archive decision, retain recovery receipt; no deletion implied. | -| Migration feature `004` | Carry this plan/spec/tasks into the spec leg; assembly/code follow-ups get separate reviewed changes. Keep its own pre-migration recovery receipt. | +| PR #146 / `fix/107-pty-driver-bound-and-lane-per-call-cost` | Merged as `82ecebe`; checkout and local branch removed during concurrent cleanup. Included by baseline extraction; do not replay separately. | +| PR #134 (draft) | Open at `715b7ac`, with a remote branch but no local worktree. Review retained changes from its branch; no checkout recreation is required just to inventory it. | +| PR #61 | Merged as `14641fd`, an ancestor of current main; included by baseline extraction. Do not replay the retired claim-takeover branch separately. | +| Retired `002`, `003`, lclaude and detached worktrees | Removed from current registrations/local branches by cleanup. Verify cleanup/preservation receipts; remove from the active replay queue and do not recreate them from the old snapshot. | +| Remote-only preservation refs | `chore/preserve-local-planning-20261003` and `takeover/ort-a9-dirty-snapshot-20260913` remain locally observed remote-tracking refs with no checkout; review as preservation history, not active writers. | +| Migration feature `004` | Drafts were preserved at `52ce64e`; its checkout was reopened only for this refresh. Carry current plan/spec/tasks into spec; review assembly/code follow-ups separately. | **Gate A:** every preserved object is attributable and restorable; no active writer can silently invalidate capture. Baseline, visibility and work diff --git a/specs/004-migrate-to-triad/research.md b/specs/004-migrate-to-triad/research.md index 2a2f1e04..0cb77876 100644 --- a/specs/004-migrate-to-triad/research.md +++ b/specs/004-migrate-to-triad/research.md @@ -7,11 +7,14 @@ No external writes, repositories, installation or migration occurred. | Finding | Decision / execution consequence | | --- | --- | -| Source main has 58 tracked paths and one standard gitlink | Use exact preliminary mapping and refresh the final baseline. | +| Source main advanced to `82ecebe` (79 commits), still 58 tracked paths and one standard gitlink | Regenerate the mapping from the source; #61 and #146 are included without replay. | | Classifier treats shipped Markdown skills/commands as specification | Override to code with written reasons; these are installation payloads. | | Original contract is the implementation's upstream dependency lock | Keep code pin/gitlink/.gitmodules together; generated assembly pins are separate. | | `.specify` and agent directories are ignored local scaffolding | Preserve local config separately and regenerate root workflow after adoption. | -| Twelve worktrees and four open PRs; feature 001 is dirty | Source-main extraction is insufficient; preserve and map retained feature deltas. | +| Cleanup initially left five worktrees; reopening planning made six, then merged #146's checkout was removed; now five worktrees and open PRs #134/#121/#97 | Reduce the active replay queue; do not recreate retired `002`/`003`, lclaude, detached, claim-takeover or #146 checkouts. | +| Feature 001 at `c0b571c` still has dirty/untracked broker/task-recovery work | Preserve its current complete delta/content, not the previous snapshot. | +| Draft #134 and preservation refs have no local worktrees | Review retained branch history without treating it as an active writer or requiring recreation. | +| Estate status refuses this manifest-free single repository | Relay no-estate refusal; use local Git and read-only GitHub observations now, then estate status after adoption. | | Installer fetches bare paths from `openRepoTools` and local sibling payloads | Preserve public root entry point and bind every payload to one code pin. | | Tests conflate repo, spec and dependency paths | Introduce explicit checkout context and composed CI, preserving nested-pin tests. | | `git-filter-repo` absent in inspected bench | Execution prerequisite; no software installation was performed for planning. | diff --git a/specs/004-migrate-to-triad/tasks.md b/specs/004-migrate-to-triad/tasks.md index 4ce3039b..40cabc07 100644 --- a/specs/004-migrate-to-triad/tasks.md +++ b/specs/004-migrate-to-triad/tasks.md @@ -4,12 +4,18 @@ The sole executable task list for the governing OpenSpec change. Planning artifacts and preliminary mapping validation do not complete these tasks. Every task remains open. Run implementation from the selected feature worktrees; actual repository creation/cutover require the approval gates. +The cleanup refresh updates the snapshot/mapping portion of T001/T003 only; +owner decisions, private preservation/restore receipts and execution gates +remain pending. Retired checkouts are not instructions to recreate worktrees. ## Phase 1 — Preservation and baseline (Gate A) -- [ ] T001 [US1] Refresh `inventory.json`, current `status --fetch` output and - PR/branch/worktree metadata; resolve continuation/archive decisions for every - object in a host-local/private migration receipt. Verify no branch, detached +- [ ] T001 [US1] Refresh `inventory.json`, PR/main/branch/worktree metadata and + applicable estate-status output; distinguish retained work from retired + cleanup records and remote-only drafts. Until an estate manifest exists, + relay `status --fetch openRepoTools`'s no-estate refusal without invoking + `resume`. Resolve continuation/archive decisions for every retained object + in a host-local/private migration receipt. Verify no branch, detached worktree or dirty/untracked/local-workflow content is omitted. - [ ] T002 [US1] Coordinate owner breakpoints and preserve source refs/content, ignored workflow configuration and installed receipts/config using the @@ -90,6 +96,9 @@ worktrees; actual repository creation/cutover require the approval gates. worktree, including this migration feature, with explicit spec/code/root mappings and conflict review. Verify each object has a complete receipt or approved archived-original disposition; preserve original PR review history. + Current candidates are #97/#121/#134 and feature 004. Account for merged + #61/#146 through the baseline and retired checkout history through its receipts; + do not recreate cleaned worktrees or replay integrated changes. - [ ] T018 [US3] Exercise OpenSpec/Speckit feature selection and estate park/resume in disposable triad fixtures, then perform reviewed binding/WIP transitions through supported tools at owner breakpoints. Verify correct diff --git a/specs/004-migrate-to-triad/verification.md b/specs/004-migrate-to-triad/verification.md index 8d3a138e..56574301 100644 --- a/specs/004-migrate-to-triad/verification.md +++ b/specs/004-migrate-to-triad/verification.md @@ -1,5 +1,37 @@ # Migration planning verification — October 3, 2026 +## Cleanup refresh + +The refreshed source main is `82ecebee13edaa915b68550170faffdf761754d5`, +including merged PRs #61 and #146. Read-only GitHub REST main matched local +main; the live open list is #134, #121 and #97. The initial GraphQL list +returned HTTP 503; the REST list succeeded. This refresh performed no merge/closure. + +Cleanup initially left five registered worktrees and six local branches. The preserved +migration draft branch `004-migrate-to-triad` was retained separately at +`52ce64e` without a checkout; reopening it made six worktrees. During validation, +#146 merged and concurrent cleanup removed its checkout and local branch, +leaving five worktrees (including planning) and five local branches. The +refreshed inventory includes those transitions, current +heads/dirty counts, remote-only refs and retired-worktree history. Removed +checkouts are excluded from active replay and are not recreated by the plan. +Feature 001 at `c0b571c` remains dirty with current broker/task-recovery work. + +The mapping was regenerated by the pinned standard, retaining reasoned +resolutions, rather than editing its source commit. The baseline remains 58 +tracked paths: 17 spec, 35 code, six root, zero dropped, with 79 commits and +21 follow-ups. The first standard `check` detected main moving from `14641fd` +to `82ecebe`; the plan was regenerated again. Standard `check`, OpenSpec strict +validation and supplemental document/inventory checks passed; no runtime suite or +local adoption rehearsal is claimed. All 19 execution tasks remain open. + +`status --fetch openRepoTools` refused: no estate named openRepoTools under +the projects directory (there is no estate manifest yet). It fetched nothing. +The refresh used local Git plus read-only GitHub main/PR observations and did +not run `resume`, change workspace pointers or manually fetch to quiet it. + +The original verification and snapshot below are historical evidence only. + ## Passing planning checks - Pinned openRepoShape `adopt-project.py check` passed for the preliminary @@ -17,8 +49,8 @@ ## Snapshot and interpretation -`inventory.json` records 12 registered worktrees and open PRs #134, #121, #97 -and #61. Feature 001 has dirty/untracked content, including work from this +The original inventory snapshot recorded 12 registered worktrees and open PRs +#134, #121, #97 and #61. Feature 001 had dirty/untracked content, including work from this conversation and concurrent planning. Preserve its complete current contents at the execution gate; the public snapshot does not contain a backup. From dda91f11c68818ebeca4264d88908c85818ff630 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sun, 4 Oct 2026 16:41:17 +0000 Subject: [PATCH 3/9] docs: refresh triad plan for October 4 feature updates --- .../migrate-to-triad/adoption-plan.yaml | 4 +- openspec/changes/migrate-to-triad/design.md | 16 +- openspec/changes/migrate-to-triad/proposal.md | 13 +- specs/004-migrate-to-triad/inventory.json | 137 +++++++++++++++--- specs/004-migrate-to-triad/plan.md | 46 +++--- specs/004-migrate-to-triad/research.md | 12 +- specs/004-migrate-to-triad/tasks.md | 17 ++- specs/004-migrate-to-triad/verification.md | 32 +++- 8 files changed, 213 insertions(+), 64 deletions(-) diff --git a/openspec/changes/migrate-to-triad/adoption-plan.yaml b/openspec/changes/migrate-to-triad/adoption-plan.yaml index ec7eb3f9..7baf5e8a 100644 --- a/openspec/changes/migrate-to-triad/adoption-plan.yaml +++ b/openspec/changes/migrate-to-triad/adoption-plan.yaml @@ -2,7 +2,7 @@ # Paths are relative to the source checkout. Generated metadata does not authorize repository creation. schema_version: 1 kind: adoption-plan -generated_on: "2026-10-03" +generated_on: "2026-10-04" tool: "opensoft/openRepoShape adopt-project.py" source: repository: opensoft/openRepoTools @@ -20,7 +20,7 @@ visibility: public tracking_branch: main adopt_branch: adopt/three-repo-shape elected_by: "Brett Heap" -elected_on: "2026-10-03" +elected_on: "2026-10-04" reference: "openxFactory docs/project-repo-schema.md" legs: assembly: openRepoTools diff --git a/openspec/changes/migrate-to-triad/design.md b/openspec/changes/migrate-to-triad/design.md index 6579bce0..b8d27815 100644 --- a/openspec/changes/migrate-to-triad/design.md +++ b/openspec/changes/migrate-to-triad/design.md @@ -4,13 +4,15 @@ The refreshed preliminary source is main `82ecebee13edaa915b68550170faffdf761754d5`: 58 tracked paths, 79 commits, one nested standard submodule. The inspected -standard is `39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. Cleanup initially left -five registered worktrees; reopening the preserved planning checkout made -six, then concurrent cleanup removed the merged #146 checkout. There are now -five worktrees including planning. The three open PRs are #134, #121 and #97; -#61 and #146 are included in main and need no separate replay. -Feature 001 at `c0b571c` has dirty/untracked work, so main extraction alone -would omit it. Removed worktrees are not an active replay queue. The +standard is `39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. The October 4 refresh +found unchanged main, six worktrees including planning and the restored +`feat/claude-current` checkout, and six local branches. The three open PRs +are #134, #121 and #97; #134 is non-draft at `11037f0`. #61 and #146 are +included in main and need no separate replay. Feature 001 is now committed +and clean at `3c26041`, including restored rollover/workspace packets and the +selected local Omnigent launcher contract. Main extraction alone still omits +that branch work. Preserve its contracts, external launcher ownership and +unmet runtime gates during replay. Removed worktrees are not an active replay queue. The [inventory](../../../specs/004-migrate-to-triad/inventory.json) is a snapshot. ## Goals / Non-Goals diff --git a/openspec/changes/migrate-to-triad/proposal.md b/openspec/changes/migrate-to-triad/proposal.md index 3ffdd9ba..5d607d39 100644 --- a/openspec/changes/migrate-to-triad/proposal.md +++ b/openspec/changes/migrate-to-triad/proposal.md @@ -67,8 +67,11 @@ mapping regenerated from source main `82ecebee13edaa915b68550170faffdf761754d5`, that this will be the final split commit. Refresh after planning lands and the cutover baseline is frozen. No runtime or migration success is claimed. -The cleanup refresh records five worktrees before editing, six after reopening -the planning checkout, then five after concurrent cleanup removed the merged -#146 checkout. It removes retired worktrees from active migration and accounts -for both #61 and #146 through main. Feature 001 and remote-only drafts/preservation refs retain -their explicit preservation/review requirements. +The October 4 refresh retains the October 3 cleanup history and records six +current worktrees, including the restored PR #134 checkout. #134 is now +non-draft at `11037f0`; feature 001 is committed and clean at `3c26041` with +restored rollover/workspace packets and the selected local launcher contract. +All checkouts were clean before planning edits. Both #61 and #146 remain +included through unchanged main. Continued feature branches and the two +remote-only preservation refs still require preservation/review receipts; +repository adoption does not implement or activate the feature 001 launch stack. diff --git a/specs/004-migrate-to-triad/inventory.json b/specs/004-migrate-to-triad/inventory.json index d9719707..efdb0aff 100644 --- a/specs/004-migrate-to-triad/inventory.json +++ b/specs/004-migrate-to-triad/inventory.json @@ -1,5 +1,5 @@ { - "observed_at": "2026-10-03T22:12:05.918851+00:00", + "observed_at": "2026-10-04T16:37:26.292668+00:00", "source_repository": "opensoft/openRepoTools", "source_main": "82ecebee13edaa915b68550170faffdf761754d5", "github_main": "82ecebee13edaa915b68550170faffdf761754d5", @@ -64,7 +64,8 @@ "local_branch_present": false, "reason": "PR #146 merged into main; concurrent cleanup removed checkout and local branch." } - ] + ], + "currency": "Historical October 3 cleanup only; use latest_refresh and worktrees for current counts." }, "worktrees": [ { @@ -79,20 +80,30 @@ }, { "identity": "001-separate-swap-ctx-handoff", - "head": "c0b571c5fa1c1446f143ce4f00f39db7cb7a166b", + "head": "3c26041a4444a10bc240cb64a168ece4d9edf6c5", "layout": "legacy-sibling", "readable_here": true, - "dirty_entries": 18, - "untracked_entries": 1, + "dirty_entries": 0, + "untracked_entries": 0, "locked": false, "prunable": false }, { "identity": "004-migrate-to-triad", - "head": "52ce64ee7787b62bf015e13cbf663f305513093e", + "head": "8aa119fd5c4286ff836ee2c9c6ee141859cb3710", + "layout": "legacy-sibling", + "readable_here": true, + "dirty_entries": 1, + "untracked_entries": 0, + "locked": false, + "prunable": false + }, + { + "identity": "feat/claude-current", + "head": "11037f01a45a72fa3fc9a8a2b0e24a64cbf1ed12", "layout": "legacy-sibling", "readable_here": true, - "dirty_entries": 8, + "dirty_entries": 0, "untracked_entries": 0, "locked": false, "prunable": false @@ -121,11 +132,15 @@ "local_branches": [ { "identity": "001-separate-swap-ctx-handoff", - "head": "c0b571c5fa1c1446f143ce4f00f39db7cb7a166b" + "head": "3c26041a4444a10bc240cb64a168ece4d9edf6c5" }, { "identity": "004-migrate-to-triad", - "head": "52ce64ee7787b62bf015e13cbf663f305513093e" + "head": "8aa119fd5c4286ff836ee2c9c6ee141859cb3710" + }, + { + "identity": "feat/claude-current", + "head": "11037f01a45a72fa3fc9a8a2b0e24a64cbf1ed12" }, { "identity": "feat/crash-consistent-lane-worktree-recovery", @@ -146,11 +161,6 @@ "head": "135c18b258d02136ed834e5a446e552db385d50c", "currency": "local remote-tracking observation; not fetched during refresh" }, - { - "identity": "feat/claude-current", - "head": "715b7ace2f6eeddcf75014e705535fd5c1965ff7", - "currency": "local remote-tracking observation; not fetched during refresh" - }, { "identity": "takeover/ort-a9-dirty-snapshot-20260913", "head": "607aee7551a5a7db7e57e294aba193fbc8a8a266", @@ -160,10 +170,10 @@ "open_prs": [ { "number": 134, - "title": "claude-current and claude-restart-check: every launch starts the newest published Claude Code by absolute path, and a running session is told when its binary moved (opensoft/workBenches#119)", + "title": "Launch current Claude Code and report stale running sessions", "headRefName": "feat/claude-current", - "headRefOid": "715b7ace2f6eeddcf75014e705535fd5c1965ff7", - "isDraft": true, + "headRefOid": "11037f01a45a72fa3fc9a8a2b0e24a64cbf1ed12", + "isDraft": false, "url": "https://github.com/opensoft/openRepoTools/pull/134", "state": "open" }, @@ -210,10 +220,99 @@ "limitations": [ "Snapshot only; refresh before baseline freeze and cutover.", "Physical paths, credentials, transcript bodies and WIP data are omitted.", - "Feature 001 retains dirty/untracked work including current broker/task-recovery planning; preserve its complete current contents.", + "Feature 001 is committed and clean at observation; preserve its current full branch delta, restored rollover/workspace packets and October 4 launcher contracts. Clean status is not a backup/restore receipt.", "Removed worktrees are cleanup history, not instructions to recreate them; verify retained backup/branch receipts before final cutover.", "Remote-only preservation refs are local observations, not active worktrees or proof of fresh remote currency.", "Raw preservation backups belong host-locally or in the designated private WIP repository; public records hold identities/digests only.", "Inventory captures this planning branch before the refresh commit; its final HEAD is recorded by git." - ] + ], + "previous_refresh_snapshot": { + "observed_at": "2026-10-03T22:12:05.918851+00:00", + "source_main": "82ecebee13edaa915b68550170faffdf761754d5", + "worktrees": [ + { + "identity": "main", + "head": "82ecebee13edaa915b68550170faffdf761754d5", + "dirty_entries": 0, + "untracked_entries": 0 + }, + { + "identity": "001-separate-swap-ctx-handoff", + "head": "c0b571c5fa1c1446f143ce4f00f39db7cb7a166b", + "dirty_entries": 18, + "untracked_entries": 1 + }, + { + "identity": "004-migrate-to-triad", + "head": "52ce64ee7787b62bf015e13cbf663f305513093e", + "dirty_entries": 8, + "untracked_entries": 0 + }, + { + "identity": "feat/supervised-context-restart", + "head": "5164ce1535b0f4f21d6b9e5b48318865a3c74295", + "dirty_entries": 0, + "untracked_entries": 0 + }, + { + "identity": "feat/crash-consistent-lane-worktree-recovery", + "head": "64dfd9736b1079c16beda0b1ac53a61e68b21d9e", + "dirty_entries": 0, + "untracked_entries": 0 + } + ], + "local_branches": [ + { + "identity": "001-separate-swap-ctx-handoff", + "head": "c0b571c5fa1c1446f143ce4f00f39db7cb7a166b" + }, + { + "identity": "004-migrate-to-triad", + "head": "52ce64ee7787b62bf015e13cbf663f305513093e" + }, + { + "identity": "feat/crash-consistent-lane-worktree-recovery", + "head": "64dfd9736b1079c16beda0b1ac53a61e68b21d9e" + }, + { + "identity": "feat/supervised-context-restart", + "head": "5164ce1535b0f4f21d6b9e5b48318865a3c74295" + }, + { + "identity": "main", + "head": "82ecebee13edaa915b68550170faffdf761754d5" + } + ], + "open_prs": [ + { + "number": 134, + "headRefName": "feat/claude-current", + "headRefOid": "715b7ace2f6eeddcf75014e705535fd5c1965ff7", + "isDraft": true + }, + { + "number": 121, + "headRefName": "feat/supervised-context-restart", + "headRefOid": "5164ce1535b0f4f21d6b9e5b48318865a3c74295", + "isDraft": false + }, + { + "number": 97, + "headRefName": "feat/crash-consistent-lane-worktree-recovery", + "headRefOid": "64dfd9736b1079c16beda0b1ac53a61e68b21d9e", + "isDraft": false + } + ] + }, + "latest_refresh": { + "previous_observed_at": "2026-10-03T22:12:05.918851+00:00", + "previous_worktrees": 5, + "current_worktrees": 6, + "current_local_branches": 6, + "added_worktrees": [ + "feat/claude-current" + ], + "removed_worktrees": [], + "source_main_changed": false + } } diff --git a/specs/004-migrate-to-triad/plan.md b/specs/004-migrate-to-triad/plan.md index 27bd12b6..7dfa99d8 100644 --- a/specs/004-migrate-to-triad/plan.md +++ b/specs/004-migrate-to-triad/plan.md @@ -2,29 +2,32 @@ **Feature**: `004-migrate-to-triad` | **Date**: 2026-10-03 **State**: Proposed plan; no migration, install or repository creation performed -**Refresh**: October 3 cleanup; current source main and active-work queue rechecked +**Refresh**: 2026-10-04; main, feature heads, worktree state and open PRs rechecked **Spec**: [spec.md](spec.md) **Governance**: [proposal](../../openspec/changes/migrate-to-triad/proposal.md) ## Baseline and deliverables The [adoption mapping](../../openspec/changes/migrate-to-triad/adoption-plan.yaml) -is regenerated from main `82ecebee13edaa915b68550170faffdf761754d5` using pinned +was regenerated on October 4 from main `82ecebee13edaa915b68550170faffdf761754d5` using pinned openRepoShape `39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. All 58 tracked paths are resolved: 17 spec, 35 code, six root, zero dropped. There are 21 named follow-ups. This is an inspected preliminary baseline; regenerate after planning lands and the actual cutover source is frozen. -Cleanup initially reduced the original 12-worktree snapshot to five registered -worktrees. Reopening the preserved `004` branch made six; concurrent cleanup -then removed the merged PR #146 checkout. There are now five worktrees, -including this planning checkout, and five local branches. The -[inventory](inventory.json) records those transitions, three open PRs (#134, -#121, #97), remote-only refs and retired-worktree history. Main includes merged -PRs #61 and #146 and matches the read-only GitHub main observation. Feature 001 at -`c0b571c5fa1c1446f143ce4f00f39db7cb7a166b` still has dirty/untracked broker -and task-recovery planning work. Machine paths, secrets/transcripts and backup -contents are omitted; private receipts must capture them at the execution gate. +Main remains at the previous refresh baseline and matches the read-only GitHub +observation; merged PRs #61 and #146 are already included. There are now six +worktrees and six local branches, including this planning checkout and the +newly restored `feat/claude-current` checkout. The three open PRs are #134, +#121 and #97; #134 is now non-draft at `11037f0`. Feature 001 advanced to +`3c26041a4444a10bc240cb64a168ece4d9edf6c5`, committing its broker/task-recovery +changes, restored rollover/workspace packets and October 4 launcher contracts. +Main and all five feature checkouts were clean before this refresh's edits. +The [inventory](inventory.json) keeps the October 3 cleanup as history and +records current heads, dirty counts and the two remote-only preservation refs. +Machine paths, secrets/transcripts and backup contents are omitted; private +receipts must capture them at the execution gate. Clean commits do not replace +verified preservation/restore receipts. Cleanup does not require recreating removed worktrees. Continue only the retained work below, and verify existing preservation receipts for retired @@ -35,7 +38,7 @@ objects. Snapshot refresh alone does not complete preservation Gate A. | Owner | Existing paths / generated responsibilities | Reason | | --- | --- | --- | | Assembly `openRepoTools` | `AGENTS.md`, `CLAUDE.md`, `README.md`, `LICENSE`, `.gitattributes`, `.gitignore`; generated manifest/Makefile/validators and assembly pins | Front door and project coordination across legs. | -| Spec `openRepoTools-spec` | `openspec/`, `specs/`, `docs/`; later feature ideation/governance artifacts | Product requirements, decisions and manual. | +| Spec `openRepoTools-spec` | `openspec/`, `specs/`, `docs/`; feature 001's retained `ideation/` and restored planning packets | Product requirements, decisions and manual; feature-only paths need separate replay accounting. | | Code `openRepoTools-code` | All executable tools, `repos.tsv`, `commands/`, `skills/`, `tests/`, implementation CI | Shipped implementation and its installer payload. | | Code dependency unit | Original `.gitmodules`, `upstream/openRepoShape`, `contracts/openreposhape-pin.yaml` | Keep module registration, exact gitlink and dependency lock together. | | Root workflow bootstrap | `.specify/`, project agent links and selected feature pointer | Currently local/ignored; capture config and regenerate shape-aware root scaffolding. | @@ -64,15 +67,15 @@ Recommended active-work dispositions are proposals, not merge/closure actions: | Work | Proposed treatment | | --- | --- | -| `001-separate-swap-ctx-handoff` | Continue from current head `c0b571c`, preserving its full delta and dirty/untracked broker/task-recovery additions; migrate to paired `001` worktrees. Keep tasks, evidence, roles and activation gates. | +| `001-separate-swap-ctx-handoff` | Continue from committed, clean head `3c26041`; preserve the complete branch delta, restored rollover/workspace packets and launcher/broker contracts. Migrate to paired `001` worktrees; keep tasks, evidence, roles and activation gates. | | PR #97 | Preserve branch/review; carry useful diagnostics into feature 001 under its service authority. Do not merge unchanged or auto-close as part of adoption. | | PR #121 | Preserve/review independently; translate selected context-runtime work to code/spec as needed, with its readiness integration still explicit. | | PR #146 / `fix/107-pty-driver-bound-and-lane-per-call-cost` | Merged as `82ecebe`; checkout and local branch removed during concurrent cleanup. Included by baseline extraction; do not replay separately. | -| PR #134 (draft) | Open at `715b7ac`, with a remote branch but no local worktree. Review retained changes from its branch; no checkout recreation is required just to inventory it. | +| PR #134 / `feat/claude-current` | Non-draft, with a clean sibling worktree at `11037f0`. Preserve/review the resolver and restart checker, installer additions, lane preflight/fencing, timeout containment and tests. Carry implementation/install payloads to code and their manual to spec; if landed before freeze, regenerate the baseline instead of replaying them. | | PR #61 | Merged as `14641fd`, an ancestor of current main; included by baseline extraction. Do not replay the retired claim-takeover branch separately. | | Retired `002`, `003`, lclaude and detached worktrees | Removed from current registrations/local branches by cleanup. Verify cleanup/preservation receipts; remove from the active replay queue and do not recreate them from the old snapshot. | | Remote-only preservation refs | `chore/preserve-local-planning-20261003` and `takeover/ort-a9-dirty-snapshot-20260913` remain locally observed remote-tracking refs with no checkout; review as preservation history, not active writers. | -| Migration feature `004` | Drafts were preserved at `52ce64e`; its checkout was reopened only for this refresh. Carry current plan/spec/tasks into spec; review assembly/code follow-ups separately. | +| Migration feature `004` | Previous refresh committed at `8aa119f`; existing checkout reused for this update. Carry current plan/spec/tasks into spec; review assembly/code follow-ups separately. | **Gate A:** every preserved object is attributable and restorable; no active writer can silently invalidate capture. Baseline, visibility and work @@ -143,8 +146,15 @@ and worktrees under `worktrees//{spec,code}`; preserve feature IDs and map old commits to new leg commits. Review conflicts semantically. Account for completed/landed work before replaying it. Retain original worktrees. -Prove feature 001's amended service-owned inventory requirements and new -planning work are present without claiming runtime acceptance. Test feature +Prove feature 001's service-owned inventory requirements, restored +`add-automatic-context-rollover` governance and lane-set workspace/rollover +brainstorm packets survive the replay. Carry its accepted October 4 launcher +contract and FR-047/T063–T065 without claiming runtime acceptance: workBenches +owns `pclaude`/`oclaude`/`lclaude`, openRepoTools owns LS admission/custody, and +Omnigent owns its local adapter/transport. Required local Omnigent qualification +for the selected lane stack and separate factory-broker admission gates remain +feature 001's delivery conditions. Repository adoption neither implements +those wrappers nor moves their ownership into this repository. Test feature selection, OpenSpec/Speckit prerequisites and estate park/resume in disposable fixtures. Apply supported WIP record updates only through their established contracts; relay refusals. Rebind each lane at its reviewed safe breakpoint, diff --git a/specs/004-migrate-to-triad/research.md b/specs/004-migrate-to-triad/research.md index 0cb77876..acf1eb64 100644 --- a/specs/004-migrate-to-triad/research.md +++ b/specs/004-migrate-to-triad/research.md @@ -1,19 +1,21 @@ # Planning evidence and decisions -October 3, 2026 inspection used the pinned standard's `AGENTS.md`, adoption +October 3 inspection and October 4 refresh used the pinned standard's `AGENTS.md`, adoption implementation, generated CLI reference and placement policy; root installer, CI and fixture code; local branch/worktree state; read-only GitHub PR metadata. No external writes, repositories, installation or migration occurred. | Finding | Decision / execution consequence | | --- | --- | -| Source main advanced to `82ecebe` (79 commits), still 58 tracked paths and one standard gitlink | Regenerate the mapping from the source; #61 and #146 are included without replay. | +| October 4 main still matches GitHub at `82ecebe` (79 commits), 58 tracked paths and one standard gitlink | Regenerate/check the current main mapping; #61 and #146 remain included without replay. | | Classifier treats shipped Markdown skills/commands as specification | Override to code with written reasons; these are installation payloads. | | Original contract is the implementation's upstream dependency lock | Keep code pin/gitlink/.gitmodules together; generated assembly pins are separate. | | `.specify` and agent directories are ignored local scaffolding | Preserve local config separately and regenerate root workflow after adoption. | -| Cleanup initially left five worktrees; reopening planning made six, then merged #146's checkout was removed; now five worktrees and open PRs #134/#121/#97 | Reduce the active replay queue; do not recreate retired `002`/`003`, lclaude, detached, claim-takeover or #146 checkouts. | -| Feature 001 at `c0b571c` still has dirty/untracked broker/task-recovery work | Preserve its current complete delta/content, not the previous snapshot. | -| Draft #134 and preservation refs have no local worktrees | Review retained branch history without treating it as an active writer or requiring recreation. | +| Six current worktrees/local branches; main and all feature checkouts clean before refresh edits; open PRs #134/#121/#97 | Keep retired `002`/`003`, lclaude, detached, claim-takeover and #146 checkouts out of replay. Clean status alone does not complete preservation Gate A. | +| Feature 001 advanced to clean `3c26041`, committing broker/task-recovery work and restoring rollover/workspace packets | Preserve its complete branch delta and restored governance/ideation; remove the stale requirement to capture the previous dirty files as uncommitted work. | +| Feature 001 now selects required local Omnigent for `lclaude → oclaude → profile launch`, with implementation/qualification pending | Carry launcher contract, FR-047/T063–T065, LS custody/input fences and separate factory-broker gates; keep wrapper ownership in workBenches and transport ownership in Omnigent. | +| Non-draft #134 advanced to `11037f0` and has a clean sibling worktree | Carry code installer/resolver/preflight/timeout/test changes and spec manual if unmerged; regenerate baseline if it lands before freeze. | +| Two remaining preservation refs have no local worktrees | Review their history as recovery evidence; do not treat them as active writers or require recreation. | | Estate status refuses this manifest-free single repository | Relay no-estate refusal; use local Git and read-only GitHub observations now, then estate status after adoption. | | Installer fetches bare paths from `openRepoTools` and local sibling payloads | Preserve public root entry point and bind every payload to one code pin. | | Tests conflate repo, spec and dependency paths | Introduce explicit checkout context and composed CI, preserving nested-pin tests. | diff --git a/specs/004-migrate-to-triad/tasks.md b/specs/004-migrate-to-triad/tasks.md index 40cabc07..4c070093 100644 --- a/specs/004-migrate-to-triad/tasks.md +++ b/specs/004-migrate-to-triad/tasks.md @@ -4,7 +4,7 @@ The sole executable task list for the governing OpenSpec change. Planning artifacts and preliminary mapping validation do not complete these tasks. Every task remains open. Run implementation from the selected feature worktrees; actual repository creation/cutover require the approval gates. -The cleanup refresh updates the snapshot/mapping portion of T001/T003 only; +The October 4 refresh updates the snapshot/mapping portion of T001/T003 only; owner decisions, private preservation/restore receipts and execution gates remain pending. Retired checkouts are not instructions to recreate worktrees. @@ -12,7 +12,7 @@ remain pending. Retired checkouts are not instructions to recreate worktrees. - [ ] T001 [US1] Refresh `inventory.json`, PR/main/branch/worktree metadata and applicable estate-status output; distinguish retained work from retired - cleanup records and remote-only drafts. Until an estate manifest exists, + cleanup records and remote-only refs. Until an estate manifest exists, relay `status --fetch openRepoTools`'s no-estate refusal without invoking `resume`. Resolve continuation/archive decisions for every retained object in a host-local/private migration receipt. Verify no branch, detached @@ -87,10 +87,13 @@ remain pending. Retired checkouts are not instructions to recreate worktrees. ## Phase 5 — Continue features and controlled cutover (Gate E) -- [ ] T016 [US1] Translate the complete approved feature 001 delta and preserved - dirty/untracked work into paired `001` spec/code branches/worktrees with +- [ ] T016 [US1] Translate the complete approved feature 001 delta and any + dirty/untracked work captured at cutover into paired `001` spec/code branches/worktrees with reviewed root changes. Verify content/old-to-new receipts, roles, inventory - amendment and open runtime gates; do not replay completed work or claim new + amendment and open runtime gates. Carry the clean `3c26041` checkpoint's + restored rollover/workspace packets, launcher contract and FR-047/T063–T065; + preserve workBenches/Omnigent ownership and required local adapter qualification + separately from factory-broker admission. Do not replay completed work or claim new runtime acceptance from migration. - [ ] T017 [US1] Translate every other approved continued PR/branch/detached worktree, including this migration feature, with explicit spec/code/root @@ -98,7 +101,9 @@ remain pending. Retired checkouts are not instructions to recreate worktrees. approved archived-original disposition; preserve original PR review history. Current candidates are #97/#121/#134 and feature 004. Account for merged #61/#146 through the baseline and retired checkout history through its receipts; - do not recreate cleaned worktrees or replay integrated changes. + do not recreate cleaned worktrees or replay integrated changes. PR #134 is + non-draft at `11037f0` with a clean worktree: account for its resolver/checker, + installer, lane preflight/fencing, timeout containment, manual and tests. - [ ] T018 [US3] Exercise OpenSpec/Speckit feature selection and estate park/resume in disposable triad fixtures, then perform reviewed binding/WIP transitions through supported tools at owner breakpoints. Verify correct diff --git a/specs/004-migrate-to-triad/verification.md b/specs/004-migrate-to-triad/verification.md index 56574301..d2834533 100644 --- a/specs/004-migrate-to-triad/verification.md +++ b/specs/004-migrate-to-triad/verification.md @@ -1,6 +1,34 @@ -# Migration planning verification — October 3, 2026 +# Migration planning verification — October 4, 2026 -## Cleanup refresh +## October 4 repository refresh + +Read-only GitHub main still matches local `82ecebee13edaa915b68550170faffdf761754d5`. +The pinned standard remains `39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. +The current open PRs are #134, #121 and #97; #134 is now non-draft at +`11037f01a45a72fa3fc9a8a2b0e24a64cbf1ed12` with a clean sibling worktree. +There are six registered worktrees and six local branches. Main and every +feature checkout were clean before this refresh's planning edits; the inventory +records its actual observation time and planning edits present when sampled. + +Feature 001 is committed and clean at `3c26041a4444a10bc240cb64a168ece4d9edf6c5`. +The previous dirty broker/task-recovery edits, restored automatic-rollover and +lane-set workspace packets, and October 4 local launcher contract are retained +in its branch. The migration plan now carries those documents and their owning +implementation/runtime gates. It makes no launcher deployment or swap-readiness +claim. The old dirty-content snapshot and cleanup counts below are historical. + +The main-only mapping was regenerated and checked: 58 paths, 17 spec / 35 code / +six root, no unresolved path or drop, 79 commits and 21 follow-ups. OpenSpec +strict validation, document/inventory checks and `git diff --check` passed. +All 19 execution tasks remain open; no adoption rehearsal or runtime suite +was run for this documentation refresh. + +`status --fetch openRepoTools` again refused with no estate named openRepoTools; +the manifest is still absent and no fetch occurred. Git and read-only GitHub +REST supplied the snapshot instead. No `resume`, manual fetch, workspace-pointer +rewrite, PR merge/closure, lane move or migration was performed. + +## October 3 cleanup refresh — historical The refreshed source main is `82ecebee13edaa915b68550170faffdf761754d5`, including merged PRs #61 and #146. Read-only GitHub REST main matched local From 48934f8d4c662be6ed07ad1b7df96dec22028ab2 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sun, 4 Oct 2026 17:42:24 +0000 Subject: [PATCH 4/9] docs: align triad migration with adopted amendment protocol --- .../migrate-to-triad/adoption-plan.yaml | 79 +++++++---- openspec/changes/migrate-to-triad/design.md | 44 ++++-- openspec/changes/migrate-to-triad/proposal.md | 21 ++- .../specs/repository-triad-migration/spec.md | 68 ++++++++- openspec/changes/migrate-to-triad/tasks.md | 5 +- specs/004-migrate-to-triad/data-model.md | 5 +- specs/004-migrate-to-triad/inventory.json | 132 ++++++++++++------ specs/004-migrate-to-triad/plan.md | 92 +++++++++--- specs/004-migrate-to-triad/protocol-review.md | 48 +++++++ specs/004-migrate-to-triad/quickstart.md | 12 +- specs/004-migrate-to-triad/research.md | 11 +- specs/004-migrate-to-triad/spec.md | 43 +++++- specs/004-migrate-to-triad/tasks.md | 73 +++++++--- specs/004-migrate-to-triad/verification.md | 44 +++++- 14 files changed, 550 insertions(+), 127 deletions(-) create mode 100644 specs/004-migrate-to-triad/protocol-review.md diff --git a/openspec/changes/migrate-to-triad/adoption-plan.yaml b/openspec/changes/migrate-to-triad/adoption-plan.yaml index 7baf5e8a..cf1678f3 100644 --- a/openspec/changes/migrate-to-triad/adoption-plan.yaml +++ b/openspec/changes/migrate-to-triad/adoption-plan.yaml @@ -8,10 +8,10 @@ source: repository: opensoft/openRepoTools local_path: . default_branch: main - commit: "82ecebee13edaa915b68550170faffdf761754d5" - files: 58 - bytes: 4339687 - commits: 79 + commit: daed20957f2dd2f22cca24053bb5bc8636ff6b3f + files: 64 + bytes: 4490556 + commits: 80 mode: in-place project: openRepoTools org: opensoft @@ -50,7 +50,7 @@ paths: rule: code-ci-workflows reason: "CI runs the implementation's own gate — build, test, analysis. The assembly root gets its own `validate.yml` from the shape, so a workflow moved here is not a workflow lost." files: 1 - bytes: 18419 + bytes: 18501 review_required: false - path: .gitignore leg: root @@ -76,7 +76,7 @@ paths: rule: root-assistant-instructions reason: "The AI-assistant instruction files and their one-file directories are the same document ten times over, and it addresses the whole project. An assistant that read it from inside the code leg would be told about specifications it cannot see. Root, ruled 2026-09-02." files: 1 - bytes: 20968 + bytes: 20967 review_required: false - path: CLAUDE.md leg: root @@ -100,8 +100,28 @@ paths: rule: root-front-door reason: "What a person or a forge reads BEFORE deciding which leg to open. The licence and the code of conduct govern all three repositories; the README is the front door of the thing an engineer clones." files: 1 - bytes: 29806 + bytes: 30329 review_required: false + - path: claude-current + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 29335 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Executable installer payload added by merged PR #134; keep resolver/checker with commands and their implementation tests in code." + - path: claude-restart-check + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 11880 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Executable installer payload added by merged PR #134; keep resolver/checker with commands and their implementation tests in code." - path: commands/ leg: code confidence: medium @@ -125,8 +145,8 @@ paths: confidence: high rule: spec-governance reason: "Requirements, decisions, contracts and governance prose — what the project has DECIDED, as opposed to what it currently runs. `contracts/` is here by the 2026-09-02 ruling even where the code reads it: the code reads across the assembly-root mount (`spec/contracts` from the root, `../spec/contracts` from `code/`), which is what the legs are mounted for." - files: 1 - bytes: 187100 + files: 2 + bytes: 201648 review_required: false - path: lane leg: code @@ -174,7 +194,7 @@ paths: rule: default reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." files: 1 - bytes: 223900 + bytes: 233030 review_required: false question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." @@ -212,7 +232,7 @@ paths: rule: default reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." files: 1 - bytes: 178500 + bytes: 180034 review_required: false question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." @@ -286,8 +306,8 @@ paths: confidence: high rule: code-source-and-tests reason: "The implementation, the tests that hold it, and the scripts and images that build and run it. `scripts/` moves whole: a harness that reads `contracts/` from beside it reads them across the root mount afterwards, which is a code change the plan records as a follow-up rather than a reason to split the directory." - files: 12 - bytes: 1938387 + files: 15 + bytes: 2022225 review_required: false - path: upstream/ leg: code @@ -300,12 +320,15 @@ paths: question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." resolution: "Mounted openRepoShape is the implementation/test dependency; preserve its exact gitlink with the original .gitmodules and dependency pin." follow_ups: + - "code/claude-current names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" + - "code/lane-start names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" - "code/lanes-edit.sh names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" - "code/openRepoTools names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" - "code/tests/run.sh names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" - "code/tests/test_install_skill_and_hook.py names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" - "code/tests/test_lane_helpers.sh names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" - - "code/tests/test_repo_hygiene.py names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" + - "code/tests/test_lane_start_claude_current.py names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" + - "… and 1 more file(s) under the code leg naming `docs/`" - "code/lanes-edit.sh names `openspec/`, which moves to the spec leg: it must read across the assembly root — `../spec/openspec/` relative to `code/` — or take the path from the environment" - "code/tests/test_lane_helpers.sh names `openspec/`, which moves to the spec leg: it must read across the assembly root — `../spec/openspec/` relative to `code/` — or take the path from the environment" - "code/tests/test_park_resume_commands.py names `specs/`, which moves to the spec leg: it must read across the assembly root — `../spec/specs/` relative to `code/` — or take the path from the environment" @@ -318,25 +341,29 @@ follow_ups: - "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 58-file source tree; capture local configuration and regenerate root workflow using setup-openspeckit after manifest/legs are present." + - "Current .specify and agent links are ignored local bootstrap artifacts, not the 64-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." - "Regenerate this preliminary plan after planning lands and baseline freeze; require current coverage check, local bare-remote rehearsal, prerequisite availability and explicit approval before execute." + - "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." summary: spec: - files: 17 - bytes: 208000 - file_share: 0.2931 - byte_share: 0.0479 + files: 18 + bytes: 222548 + file_share: 0.2812 + byte_share: 0.0496 code: - files: 35 - bytes: 4067645 - file_share: 0.6034 - byte_share: 0.9373 + files: 40 + bytes: 4203444 + file_share: 0.6250 + byte_share: 0.9361 root: files: 6 - bytes: 64042 - file_share: 0.1034 - byte_share: 0.0148 + bytes: 64564 + file_share: 0.0938 + byte_share: 0.0144 unresolved: files: 0 bytes: 0 diff --git a/openspec/changes/migrate-to-triad/design.md b/openspec/changes/migrate-to-triad/design.md index b8d27815..fc1e42ee 100644 --- a/openspec/changes/migrate-to-triad/design.md +++ b/openspec/changes/migrate-to-triad/design.md @@ -2,12 +2,12 @@ ## Context -The refreshed preliminary source is main `82ecebee13edaa915b68550170faffdf761754d5`: -58 tracked paths, 79 commits, one nested standard submodule. The inspected +The refreshed preliminary source is main `daed20957f2dd2f22cca24053bb5bc8636ff6b3f`: +64 tracked paths, 80 commits, one nested standard submodule. The inspected standard is `39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. The October 4 refresh -found unchanged main, six worktrees including planning and the restored -`feat/claude-current` checkout, and six local branches. The three open PRs -are #134, #121 and #97; #134 is non-draft at `11037f0`. #61 and #146 are +found merged PR #134 on main, six worktrees including planning and the retained +`feat/claude-current` checkout, and six local branches. The two open PRs +are #121 and #97. #134, #61 and #146 are included in main and need no separate replay. Feature 001 is now committed and clean at `3c26041`, including restored rollover/workspace packets and the selected local Omnigent launcher contract. Main extraction alone still omits @@ -41,12 +41,18 @@ baseline, standard commit, complete mapping and follow-ups. ### Placement and dependency custody -The [resolved adoption plan](adoption-plan.yaml) accounts for all 58 baseline -paths: 17 spec, 35 code, six root, none dropped. It records reasons for all -14 originally unresolved paths and overrides for shipped Markdown payloads +The [resolved adoption plan](adoption-plan.yaml) accounts for all 64 baseline +paths: 18 spec, 40 code, six root, none dropped, with 28 follow-ups. It records reasons for all +16 originally unresolved paths and overrides for shipped Markdown payloads and the dependency lock. Product documentation/OpenSpec/Speckit go to spec. Root owns front-door guidance, license, project metadata and workflow bootstrap. +Genuine bounded feature amendment records at +`features//openspec/` are the narrow code exception. Review them +explicitly with reasoned resolutions until the upstream rule is qualified. +No such tracked records were observed in current main/retained feature heads; +do not manufacture them or move full feature-branch proposals into code. + Keep executable files, `repos.tsv`, shipped `commands/` and `skills/`, tests and implementation CI in code. The original `.gitmodules`, mounted standard and `contracts/openreposhape-pin.yaml` also stay together in code. At this @@ -96,6 +102,17 @@ lane until its owner has reached a safe breakpoint and replacement evidence is reviewed. WIP records remain in their existing private repository and use the supported estate workflow; public records contain no private backups. +Retain approved original proposal repository/commit/path provenance and verify +correspondence to the extracted spec history; filtered commits can differ. +Repair links and feature/task references without guessing new approval hashes. +Explicitly select the intended spec checkout for full proposals and the code +feature root for bounded amendments. A local root overrides a docs-store pointer; +verified `--store` support is optional, not assumed. Never edit pinned base +mounts by accidental store selection. Keep manual amendment records and update +working Speckit files immediately; runtime/capability/authority/external scope +changes retain full governance. Schema, command routing, workstation distribution +and shape placement automation are pending, not delivered by this migration. + ### CI and exact-pin delivery Code CI runs implementation checks; root CI validates manifest/pins and the @@ -105,6 +122,17 @@ in the code Git repository. Use the canonical serialized `tests/run.sh`, Bash 3.2 parsing and current platform/fixture timing policies. Missing-spec context cannot turn a required integration check into a silent skip. +Prepare code/spec PRs together. At the first bounded amendment create one spec +reconciliation issue; collect net tested effects, all dispositions, dated +departures, as-built record, evidence and landed code identity in one final +spec batch. Preserve archived approval history through linked records. Code +may land first, but completed assembly advancement waits for reconciled spec +and matching code. Close the issue after spec landing; the feature landing +record tracks assembly. Archive only after implementation and assembly landing +with explicit full-spec root selection, and advance later archive commits by +ordinary pinning. Reopen reconciliation for later behavior changes; cancellation +records abandonment. Local archival cannot satisfy reconciliation. + Land follow-up leg changes before the root pin bump that selects them. Root gitlinks, leg pins and workflow SHAs move through the standard's bump tool. The historical upstream dependency remains one-way and unedited; needed diff --git a/openspec/changes/migrate-to-triad/proposal.md b/openspec/changes/migrate-to-triad/proposal.md index 5d607d39..76718921 100644 --- a/openspec/changes/migrate-to-triad/proposal.md +++ b/openspec/changes/migrate-to-triad/proposal.md @@ -17,6 +17,12 @@ pins their exact tested combination and preserves its identity and history. executable tooling, shipped skills/commands, tests, CI and the mounted standard dependency in `code/`. Root agent guidance and workflow bootstrap span both legs. No source path is proposed for deletion. +- Apply the adopted October 4 manual amendment workflow: full proposals and + canonical specs stay in spec; genuine bounded feature records stay in code. + Preserve verified approved-baseline correspondence, update working Speckit + files immediately and reconcile net tested effects once before completed + assembly advancement. Schema/routing/distribution/placement automation remains + pending with workBenches/openRepoShape. - **BREAKING:** development paths and PR destinations change. Preserve command names, install artifacts, published one-line installation and installed behavior through a pinned assembly-to-code compatibility entry point. @@ -63,15 +69,18 @@ existing changes; this migration does not activate experimental capabilities. Requested on October 3, 2026. Planning is authorized; migration execution, repository creation, software installation, PR merge and lane relocation are not authorized by this request. The attached adoption plan is a preliminary -mapping regenerated from source main `82ecebee13edaa915b68550170faffdf761754d5`, not a promise +mapping regenerated from source main `daed20957f2dd2f22cca24053bb5bc8636ff6b3f`, not a promise that this will be the final split commit. Refresh after planning lands and the cutover baseline is frozen. No runtime or migration success is claimed. -The October 4 refresh retains the October 3 cleanup history and records six -current worktrees, including the restored PR #134 checkout. #134 is now -non-draft at `11037f0`; feature 001 is committed and clean at `3c26041` with +The protocol refresh retains the October 3 cleanup history and records six +current worktrees, including the retained merged PR #134 checkout. #134 is +included in the new main baseline; feature 001 is committed and clean at `3c26041` with restored rollover/workspace packets and the selected local launcher contract. -All checkouts were clean before planning edits. Both #61 and #146 remain -included through unchanged main. Continued feature branches and the two +Feature 001/main remain clean; concurrent #121 work now includes dirty/untracked +feature 005 and its full legacy authority decision. Both #61 and #146 remain +included through main. Open PRs are now #97 and #121. Continued feature branches and the two remote-only preservation refs still require preservation/review receipts; repository adoption does not implement or activate the feature 001 launch stack. +The [protocol review](../../../specs/004-migrate-to-triad/protocol-review.md) +records the adopted manual instructions and the working-draft source snapshots. diff --git a/openspec/changes/migrate-to-triad/specs/repository-triad-migration/spec.md b/openspec/changes/migrate-to-triad/specs/repository-triad-migration/spec.md index 982c6f73..f5ff667b 100644 --- a/openspec/changes/migrate-to-triad/specs/repository-triad-migration/spec.md +++ b/openspec/changes/migrate-to-triad/specs/repository-triad-migration/spec.md @@ -62,7 +62,11 @@ without a developer assembly checkout. ### Requirement: Project workflow operates across the pinned legs The assembly SHALL own project workflow and its manifest/pins; product -OpenSpec/Speckit artifacts SHALL live in spec, implementation in code. +Full proposals, archives and canonical OpenSpec/Speckit artifacts SHALL live +in spec, implementation in code. Genuine bounded feature amendment records +SHALL remain in code as the narrow reviewed placement exception, not a second +canonical baseline. A feature-branch origin SHALL NOT change a full proposal's +role. Historical features SHALL NOT require manufactured local amendments. Continued features SHALL use paired leg worktrees with preserved feature IDs. Delivery SHALL land leg changes and select them through a root pin bump. CI SHALL test the exact composed state without silently skipping required @@ -73,6 +77,68 @@ cross-leg tests. The standard dependency SHALL remain unedited in place. - **THEN** root gitlinks, pin records and workflow commit references SHALL move together through the established pin-bump procedure and integration checks. +#### Scenario: Bounded amendment folder exists before extraction +- **WHEN** source content contains genuine `features//openspec/` records +- **THEN** the reviewed plan SHALL explicitly assign them to code until upstream + placement is qualified +- **AND** pinned standard copies/digests SHALL NOT be edited to make placement pass. + +### Requirement: Migration retains provenance and explicit planning roots + +The migration SHALL preserve approved original repository/commit/path identities +and verify correspondence to extracted spec identities when filtering changes +hashes. It SHALL repair cross-leg links, baseline references and feature/task +paths without guessing approved commits. Full proposal operations SHALL select +the intended spec feature checkout; local amendments SHALL select their code +root. A store pointer SHALL NOT override explicit local-root verification or +authorize accidental changes to pinned base mounts. Pending schema/routing, +bootstrap distribution and upstream placement automation SHALL remain distinct +from adopted manual use on a target workstation. + +#### Scenario: Extraction changes an approved proposal commit +- **WHEN** the extracted spec history has a different commit identity +- **THEN** original approved provenance SHALL remain recorded +- **AND** migration evidence SHALL establish the corresponding spec repository, + commit and path rather than substituting a guessed hash. + +#### Scenario: Local amendment root and docs store coexist +- **WHEN** an operation could resolve to either planning root +- **THEN** the operator SHALL verify its intended checkout, branch and root +- **AND** a local amendment archive SHALL NOT prove canonical reconciliation. + +### Requirement: Tested reconciliation precedes completed assembly advancement + +Accepted bounded migration adjustments SHALL record unique feature-local IDs, +governing baseline, affected requirements/tasks, authority/source, verification +impact and dispositions. Working Speckit files SHALL update before dependent +implementation. Larger scope/runtime/authority/external changes SHALL use full +spec governance; unnecessary larger ideas SHALL be linked to future-proposal +issues and excluded from current requirements/tasks. Speckit SHALL remain the +only executable task list. + +At the first amendment, the feature SHALL create one spec reconciliation issue. +After implementation and verification, it SHALL reconcile net tested effects +once into governing specs/contracts/design with dated departures/as-built +evidence, every disposition and the landed code identity. Inactive deltas SHALL +be excluded while approved, archived and amendment history remains traceable. +Spec/code PRs SHALL be prepared together. Code MAY land first, but reconciliation +and matching spec/code pins SHALL precede completed assembly advancement. +The issue SHALL close after spec landing; the feature landing record SHALL track +assembly. Full spec archival SHALL explicitly select its root and wait for +implementation/assembly landing; subsequent archive commits SHALL advance by +ordinary pinning. Later behavior changes SHALL reopen reconciliation; +cancellation SHALL record abandonment rather than as-built completion. + +#### Scenario: Code lands before final spec reconciliation +- **WHEN** the code PR lands while final reconciliation remains open +- **THEN** completed assembly advancement SHALL wait for the matching spec landing +- **AND** the reconciliation SHALL reference the actual landed code commit. + +#### Scenario: Amendment was reverted or deferred +- **WHEN** final reconciliation computes the tested semantic effect +- **THEN** the inactive delta SHALL be excluded from governing behavior +- **AND** its disposition, history and any future-proposal issue SHALL remain linked. + ### Requirement: Execution and cutover require reviewable evidence Real execution SHALL require an explicit approved plan, visibility, baseline, diff --git a/openspec/changes/migrate-to-triad/tasks.md b/openspec/changes/migrate-to-triad/tasks.md index 3738cb5d..858e93fc 100644 --- a/openspec/changes/migrate-to-triad/tasks.md +++ b/openspec/changes/migrate-to-triad/tasks.md @@ -17,6 +17,9 @@ This checklist tracks governance, not duplicate implementation tasks. - [ ] 2.1 Accept exact assembled-head, preservation, feature-transition and rollback evidence from Speckit Gates C–E; verify required CI and ownership boundaries without enabling unrelated experimental runtime features. -- [ ] 2.2 Archive only after the adopted assembly and selected leg pins land +- [ ] 2.2 Accept final tested spec reconciliation, amendment dispositions and + approved-baseline correspondence; verify matched spec/code landings before + completed assembly advancement. Close reconciliation after spec landing. +- [ ] 2.3 Archive only after the adopted assembly and selected leg pins land and continued work is accounted for; verify root delivery and recorded old-PR/worktree dispositions before closure. diff --git a/specs/004-migrate-to-triad/data-model.md b/specs/004-migrate-to-triad/data-model.md index b36916ca..2dc89f10 100644 --- a/specs/004-migrate-to-triad/data-model.md +++ b/specs/004-migrate-to-triad/data-model.md @@ -8,7 +8,10 @@ and never replace lane claims, workspace pointers or the standard's pins. | Adoption plan | Source repository/commit, standard revision evidence, names/visibility, reasoned path owners, follow-ups, coverage result and approval binding. Relative paths only in shared artifacts. | | Work inventory | Observation time, branch/PR/head identity, detached status, readable/dirty indication and snapshot limits. Public `inventory.json` omits host paths and private work. | | Private preservation receipt | Exact original worktree/binding, branch refs, staged/unstaged/untracked/ignored content digests, backup location and verified restore result. Stored host-locally or in existing private WIP storage. | -| Feature migration receipt | Original merge base/head and captured content; spec/code/root file mapping; resulting branches/commits/checksums; conflict/replay decisions, roles and approval. | +| Feature migration receipt | Original merge base/head and captured content; approved original proposal repository/commit/path; verified extracted spec correspondence; spec/code/root mapping; resulting branches/commits/checksums; repaired links/task roots, conflict decisions, roles and approval. | +| Bounded amendment record | Feature-local ID, governing proposal/immutable approved baseline, authority/source, affected requirements/tasks, interpretation, verification impact, implementation/evidence references and disposition. Relative code paths; no second canonical spec/task store. | +| Final spec reconciliation | One issue created at first amendment; all dispositions/net effect, dated departures/as-built record, preserved approval/archive history, verification, landed code identity, deferred future issues and matching spec/code/assembly landing references. | +| Workflow qualification | Explicit root/store/branch selection; manual instruction availability on each target workstation; pending schema/routing/distribution/upstream placement owners and any reviewed local exception. | | Composed candidate evidence | Root/spec/code/standard commits, pin/digest consistency, installer payload revision, platform/CI/rehearsal/rollback results and outstanding findings. | | Cutover approval | Actual candidate/baseline, visibility, leg names, preservation/rehearsal references, lane breakpoints and authorized actions. | diff --git a/specs/004-migrate-to-triad/inventory.json b/specs/004-migrate-to-triad/inventory.json index efdb0aff..656b6cef 100644 --- a/specs/004-migrate-to-triad/inventory.json +++ b/specs/004-migrate-to-triad/inventory.json @@ -1,8 +1,8 @@ { - "observed_at": "2026-10-04T16:37:26.292668+00:00", + "observed_at": "2026-10-04T17:40:54.465251+00:00", "source_repository": "opensoft/openRepoTools", - "source_main": "82ecebee13edaa915b68550170faffdf761754d5", - "github_main": "82ecebee13edaa915b68550170faffdf761754d5", + "source_main": "daed20957f2dd2f22cca24053bb5bc8636ff6b3f", + "github_main": "daed20957f2dd2f22cca24053bb5bc8636ff6b3f", "github_main_matches_local": true, "standard_commit": "39d5c986fcfac1a160474bfe91c5f1c37fccc72c", "cleanup_summary": { @@ -70,7 +70,7 @@ "worktrees": [ { "identity": "main", - "head": "82ecebee13edaa915b68550170faffdf761754d5", + "head": "daed20957f2dd2f22cca24053bb5bc8636ff6b3f", "layout": "root", "readable_here": true, "dirty_entries": 0, @@ -90,11 +90,11 @@ }, { "identity": "004-migrate-to-triad", - "head": "8aa119fd5c4286ff836ee2c9c6ee141859cb3710", + "head": "dda91f11c68818ebeca4264d88908c85818ff630", "layout": "legacy-sibling", "readable_here": true, - "dirty_entries": 1, - "untracked_entries": 0, + "dirty_entries": 14, + "untracked_entries": 1, "locked": false, "prunable": false }, @@ -113,14 +113,14 @@ "head": "5164ce1535b0f4f21d6b9e5b48318865a3c74295", "layout": "legacy-nested", "readable_here": true, - "dirty_entries": 0, - "untracked_entries": 0, + "dirty_entries": 43, + "untracked_entries": 3, "locked": false, "prunable": false }, { "identity": "feat/crash-consistent-lane-worktree-recovery", - "head": "64dfd9736b1079c16beda0b1ac53a61e68b21d9e", + "head": "8f69e96fbd33d84950b1acdaa2f2f3836c081341", "layout": "legacy-nested", "readable_here": true, "dirty_entries": 0, @@ -136,7 +136,7 @@ }, { "identity": "004-migrate-to-triad", - "head": "8aa119fd5c4286ff836ee2c9c6ee141859cb3710" + "head": "dda91f11c68818ebeca4264d88908c85818ff630" }, { "identity": "feat/claude-current", @@ -144,7 +144,7 @@ }, { "identity": "feat/crash-consistent-lane-worktree-recovery", - "head": "64dfd9736b1079c16beda0b1ac53a61e68b21d9e" + "head": "8f69e96fbd33d84950b1acdaa2f2f3836c081341" }, { "identity": "feat/supervised-context-restart", @@ -152,7 +152,7 @@ }, { "identity": "main", - "head": "82ecebee13edaa915b68550170faffdf761754d5" + "head": "daed20957f2dd2f22cca24053bb5bc8636ff6b3f" } ], "remote_only_branches": [ @@ -168,15 +168,6 @@ } ], "open_prs": [ - { - "number": 134, - "title": "Launch current Claude Code and report stale running sessions", - "headRefName": "feat/claude-current", - "headRefOid": "11037f01a45a72fa3fc9a8a2b0e24a64cbf1ed12", - "isDraft": false, - "url": "https://github.com/opensoft/openRepoTools/pull/134", - "state": "open" - }, { "number": 121, "title": "`/ctx` writes its restart to disk before it kills anything, and what tmux starts is a supervisor that stays in the pane \u2014 the fresh session was an environment variable the launcher dropped (#94)", @@ -210,6 +201,13 @@ "merged_at": "2026-10-03T22:07:15Z", "merge_commit": "82ecebee13edaa915b68550170faffdf761754d5", "disposition": "Included in current source main; no separate feature replay." + }, + { + "number": 134, + "state": "MERGED", + "merged_at": "2026-10-04T16:52:38Z", + "merge_commit": "daed20957f2dd2f22cca24053bb5bc8636ff6b3f", + "disposition": "Included in refreshed source main; retained checkout is preservation only, no separate replay." } ], "estate_status": { @@ -224,10 +222,11 @@ "Removed worktrees are cleanup history, not instructions to recreate them; verify retained backup/branch receipts before final cutover.", "Remote-only preservation refs are local observations, not active worktrees or proof of fresh remote currency.", "Raw preservation backups belong host-locally or in the designated private WIP repository; public records hold identities/digests only.", - "Inventory captures this planning branch before the refresh commit; its final HEAD is recorded by git." + "Inventory captures this planning branch before the refresh commit; its final HEAD is recorded by git.", + "PR #121 now contains active staged/unstaged/untracked work including feature 005 legacy-only supervised restart and its full authority decision; preserve current contents and merge hold. Snapshot counts can change while owners continue." ], "previous_refresh_snapshot": { - "observed_at": "2026-10-03T22:12:05.918851+00:00", + "observed_at": "2026-10-04T16:37:26.292668+00:00", "source_main": "82ecebee13edaa915b68550170faffdf761754d5", "worktrees": [ { @@ -238,14 +237,20 @@ }, { "identity": "001-separate-swap-ctx-handoff", - "head": "c0b571c5fa1c1446f143ce4f00f39db7cb7a166b", - "dirty_entries": 18, - "untracked_entries": 1 + "head": "3c26041a4444a10bc240cb64a168ece4d9edf6c5", + "dirty_entries": 0, + "untracked_entries": 0 }, { "identity": "004-migrate-to-triad", - "head": "52ce64ee7787b62bf015e13cbf663f305513093e", - "dirty_entries": 8, + "head": "8aa119fd5c4286ff836ee2c9c6ee141859cb3710", + "dirty_entries": 1, + "untracked_entries": 0 + }, + { + "identity": "feat/claude-current", + "head": "11037f01a45a72fa3fc9a8a2b0e24a64cbf1ed12", + "dirty_entries": 0, "untracked_entries": 0 }, { @@ -264,11 +269,15 @@ "local_branches": [ { "identity": "001-separate-swap-ctx-handoff", - "head": "c0b571c5fa1c1446f143ce4f00f39db7cb7a166b" + "head": "3c26041a4444a10bc240cb64a168ece4d9edf6c5" }, { "identity": "004-migrate-to-triad", - "head": "52ce64ee7787b62bf015e13cbf663f305513093e" + "head": "8aa119fd5c4286ff836ee2c9c6ee141859cb3710" + }, + { + "identity": "feat/claude-current", + "head": "11037f01a45a72fa3fc9a8a2b0e24a64cbf1ed12" }, { "identity": "feat/crash-consistent-lane-worktree-recovery", @@ -287,8 +296,8 @@ { "number": 134, "headRefName": "feat/claude-current", - "headRefOid": "715b7ace2f6eeddcf75014e705535fd5c1965ff7", - "isDraft": true + "headRefOid": "11037f01a45a72fa3fc9a8a2b0e24a64cbf1ed12", + "isDraft": false }, { "number": 121, @@ -305,14 +314,59 @@ ] }, "latest_refresh": { - "previous_observed_at": "2026-10-03T22:12:05.918851+00:00", - "previous_worktrees": 5, + "previous_observed_at": "2026-10-04T16:37:26.292668+00:00", + "previous_worktrees": 6, "current_worktrees": 6, "current_local_branches": 6, - "added_worktrees": [ - "feat/claude-current" - ], + "added_worktrees": [], "removed_worktrees": [], - "source_main_changed": false + "source_main_changed": true, + "protocol": "Shared OpenSpec/Speckit workflow: Triad Feature Amendments, adopted 2026-10-04; manual delivery only.", + "concurrent_work": "PR #121 retains staged/unstaged/untracked work including full legacy authority decision and feature 005; recovery branch advanced during refresh. Snapshot only, not a writer freeze." + }, + "protocol_review": { + "adopted_on": "2026-10-04", + "workflow": "Triad Feature Amendments and Single repository to triad migration", + "manual_use": true, + "automation_qualified": false, + "source_record": "specs/004-migrate-to-triad/protocol-review.md", + "amendment_path_observations": [ + { + "identity": "main", + "head": "daed20957f2dd2f22cca24053bb5bc8636ff6b3f", + "bounded_amendment_paths": [], + "working_bounded_amendment_paths": [] + }, + { + "identity": "001-separate-swap-ctx-handoff", + "head": "3c26041a4444a10bc240cb64a168ece4d9edf6c5", + "bounded_amendment_paths": [], + "working_bounded_amendment_paths": [] + }, + { + "identity": "004-migrate-to-triad", + "head": "dda91f11c68818ebeca4264d88908c85818ff630", + "bounded_amendment_paths": [], + "working_bounded_amendment_paths": [] + }, + { + "identity": "feat/claude-current", + "head": "11037f01a45a72fa3fc9a8a2b0e24a64cbf1ed12", + "bounded_amendment_paths": [], + "working_bounded_amendment_paths": [] + }, + { + "identity": "feat/supervised-context-restart", + "head": "5164ce1535b0f4f21d6b9e5b48318865a3c74295", + "bounded_amendment_paths": [], + "working_bounded_amendment_paths": [] + }, + { + "identity": "feat/crash-consistent-lane-worktree-recovery", + "head": "8f69e96fbd33d84950b1acdaa2f2f3836c081341", + "bounded_amendment_paths": [], + "working_bounded_amendment_paths": [] + } + ] } } diff --git a/specs/004-migrate-to-triad/plan.md b/specs/004-migrate-to-triad/plan.md index 7dfa99d8..7ca2fd2d 100644 --- a/specs/004-migrate-to-triad/plan.md +++ b/specs/004-migrate-to-triad/plan.md @@ -2,27 +2,31 @@ **Feature**: `004-migrate-to-triad` | **Date**: 2026-10-03 **State**: Proposed plan; no migration, install or repository creation performed -**Refresh**: 2026-10-04; main, feature heads, worktree state and open PRs rechecked +**Refresh**: 2026-10-04; adopted amendment protocol and merged PR #134 incorporated **Spec**: [spec.md](spec.md) **Governance**: [proposal](../../openspec/changes/migrate-to-triad/proposal.md) ## Baseline and deliverables The [adoption mapping](../../openspec/changes/migrate-to-triad/adoption-plan.yaml) -was regenerated on October 4 from main `82ecebee13edaa915b68550170faffdf761754d5` using pinned -openRepoShape `39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. All 58 tracked -paths are resolved: 17 spec, 35 code, six root, zero dropped. There are -21 named follow-ups. This is an inspected preliminary baseline; regenerate +was regenerated from main `daed20957f2dd2f22cca24053bb5bc8636ff6b3f` using pinned +openRepoShape `39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. All 64 tracked +paths are resolved: 18 spec, 40 code, six root, zero dropped. There are +28 named follow-ups, including protocol placement/provenance/reconciliation. +This is an inspected preliminary baseline; regenerate after planning lands and the actual cutover source is frozen. -Main remains at the previous refresh baseline and matches the read-only GitHub -observation; merged PRs #61 and #146 are already included. There are now six +Main advanced with merged PR #134 and matches the read-only GitHub observation; +#61 and #146 remain included. There are six worktrees and six local branches, including this planning checkout and the -newly restored `feat/claude-current` checkout. The three open PRs are #134, -#121 and #97; #134 is now non-draft at `11037f0`. Feature 001 advanced to +retained `feat/claude-current` checkout, whose merged changes need no replay. +The two open PRs are #121 and #97. Feature 001 remains at `3c26041a4444a10bc240cb64a168ece4d9edf6c5`, committing its broker/task-recovery changes, restored rollover/workspace packets and October 4 launcher contracts. -Main and all five feature checkouts were clean before this refresh's edits. +Main and feature 001 remain clean. Concurrent work in PR #121's checkout now +includes staged/unstaged/untracked content and `005-supervised-legacy-ctx`; +the recovery branch also advanced. The inventory is an observation, not a +freeze of active writers. The [inventory](inventory.json) keeps the October 3 cleanup as history and records current heads, dirty counts and the two remote-only preservation refs. Machine paths, secrets/transcripts and backup contents are omitted; private @@ -33,6 +37,12 @@ Cleanup does not require recreating removed worktrees. Continue only the retained work below, and verify existing preservation receipts for retired objects. Snapshot refresh alone does not complete preservation Gate A. +The [protocol review](protocol-review.md) records the October 4 manual default +and source snapshots. Full proposals, archives, canonical specs/contracts and +Speckit files stay in spec. Genuine bounded amendments belong in the code +feature folder and require immediate working-spec updates plus final docs +reconciliation. Schema/routing/distribution/placement automation is pending. + ## Target placement | Owner | Existing paths / generated responsibilities | Reason | @@ -41,6 +51,7 @@ objects. Snapshot refresh alone does not complete preservation Gate A. | Spec `openRepoTools-spec` | `openspec/`, `specs/`, `docs/`; feature 001's retained `ideation/` and restored planning packets | Product requirements, decisions and manual; feature-only paths need separate replay accounting. | | Code `openRepoTools-code` | All executable tools, `repos.tsv`, `commands/`, `skills/`, `tests/`, implementation CI | Shipped implementation and its installer payload. | | Code dependency unit | Original `.gitmodules`, `upstream/openRepoShape`, `contracts/openreposhape-pin.yaml` | Keep module registration, exact gitlink and dependency lock together. | +| Code amendment exception | Genuine bounded `features//openspec/` records, deltas and evidence references | Relative changes against an approved spec baseline; explicitly review placement until upstream qualification. No such tracked paths were observed in the current source or retained feature heads. | | Root workflow bootstrap | `.specify/`, project agent links and selected feature pointer | Currently local/ignored; capture config and regenerate shape-aware root scaffolding. | After byte-preserving extraction, add a root installation entry point that @@ -68,14 +79,14 @@ Recommended active-work dispositions are proposals, not merge/closure actions: | Work | Proposed treatment | | --- | --- | | `001-separate-swap-ctx-handoff` | Continue from committed, clean head `3c26041`; preserve the complete branch delta, restored rollover/workspace packets and launcher/broker contracts. Migrate to paired `001` worktrees; keep tasks, evidence, roles and activation gates. | -| PR #97 | Preserve branch/review; carry useful diagnostics into feature 001 under its service authority. Do not merge unchanged or auto-close as part of adoption. | -| PR #121 | Preserve/review independently; translate selected context-runtime work to code/spec as needed, with its readiness integration still explicit. | +| PR #97 | Preserve its current advanced recovery branch, review history and diagnostic role separately from managed LS authority and PR #121. Carry useful diagnostics under feature 001's service authority without merging unchanged or auto-closing through adoption. | +| PR #121 / feature `005-supervised-legacy-ctx` | Preserve complete staged/unstaged/untracked contents and the full legacy authority decision. Continue paired `005` worktrees for confirmed legacy-only supervised restart; managed/unknown ownership refuses. Keep the existing merge hold and PR #97 ownership separate. The full proposal/decision/specs stay in spec, implementation/tests in code; this authority decision is not a bounded local amendment. | | PR #146 / `fix/107-pty-driver-bound-and-lane-per-call-cost` | Merged as `82ecebe`; checkout and local branch removed during concurrent cleanup. Included by baseline extraction; do not replay separately. | -| PR #134 / `feat/claude-current` | Non-draft, with a clean sibling worktree at `11037f0`. Preserve/review the resolver and restart checker, installer additions, lane preflight/fencing, timeout containment and tests. Carry implementation/install payloads to code and their manual to spec; if landed before freeze, regenerate the baseline instead of replaying them. | +| PR #134 / `feat/claude-current` | Merged as `daed209`; the retained clean checkout at `11037f0` is preservation history. Resolver/checker, installer, preflight/fencing, timeout tests and manual are included by the regenerated baseline. Do not replay separately or retire the checkout as part of planning. | | PR #61 | Merged as `14641fd`, an ancestor of current main; included by baseline extraction. Do not replay the retired claim-takeover branch separately. | | Retired `002`, `003`, lclaude and detached worktrees | Removed from current registrations/local branches by cleanup. Verify cleanup/preservation receipts; remove from the active replay queue and do not recreate them from the old snapshot. | | Remote-only preservation refs | `chore/preserve-local-planning-20261003` and `takeover/ort-a9-dirty-snapshot-20260913` remain locally observed remote-tracking refs with no checkout; review as preservation history, not active writers. | -| Migration feature `004` | Previous refresh committed at `8aa119f`; existing checkout reused for this update. Carry current plan/spec/tasks into spec; review assembly/code follow-ups separately. | +| Migration feature `004` | Previous refresh committed at `dda91f1`; existing checkout reused for this update. This full proposal and canonical feature files stay in spec; any genuine bounded implementation amendments belong in code. | **Gate A:** every preserved object is attributable and restorable; no active writer can silently invalidate capture. Baseline, visibility and work @@ -94,6 +105,13 @@ baseline accounting, leg histories and recursive bootstrap. Exercise all actual file-placement collisions. If the standard refuses or misaccounts content, relay it and resolve through a reviewed standard change/pin bump; do not patch the pinned submodule or adjust digests to manufacture success. +Review genuine amendment paths explicitly as code using a reasoned project-local +resolution until the upstream exception is qualified. Full proposals created +on feature branches remain full proposals in spec; do not manufacture amendments +for historical features. Record original approved repository/commit/path and +verified extracted spec correspondence: filtering may change commit hashes. +Repair relative links, selected roots/stores and task paths without guessing +new baseline identities or replacing approval provenance with the split tip. Implement and review the necessary installer entry point/resolver, explicit test-root context, CI layout, agent pointers, docs paths and workflow bootstrap @@ -103,6 +121,15 @@ Add leg root guidance without overwriting original root guidance. Preserve the Bash 3.2 floor and original no-submodule test behavior, while requiring complete dependencies for composed acceptance. +Operate full proposals from the intended feature spec checkout and bounded +amendments from the code feature folder. Verify checkout, branch and root for +each operation; a local root takes precedence over a docs-store pointer. +Use explicit `--store` only if the installed CLI supports it and resolves the +intended feature checkout. Maintain amendments manually until schema/routing +is qualified. Check workBenches protocol/command distribution on each target +workstation; regeneration of root scaffolding alone does not distribute this +decision. Pending automation remains with workBenches/openRepoShape. + **Gate B:** complete local rehearsal evidence, restore rehearsal, reviewed follow-up patches, resolved mapping and a passing compatibility test matrix. Mapping `check` alone does not satisfy this gate. @@ -128,13 +155,33 @@ stops landing; preserve source and newly created evidence/branches. Never rerun over live legs or bypass a ruleset. After successful source verification, apply reviewed follow-ups through the -new leg/root PRs. Resolve `shape/` collisions explicitly; do not overwrite +new leg/root PRs and prepare docs/code review together. For bounded migration +adjustments, record decision authority, approved baseline, unique amendment ID, +affected requirements/tasks and evidence in code; update working Speckit files +in the paired spec worktree before dependent implementation. Create one spec +reconciliation issue at the first amendment and link every later record. Larger +scope, runtime, authority or external-contract changes require full spec +governance; defer unnecessary capabilities to linked future-proposal issues. +Speckit remains the sole executable task list. + +Resolve `shape/` collisions explicitly; do not overwrite pinned shape files. Put `Read AGENTS-shape.md first...` guidance first in root agent instructions and bootstrap shape-aware OpenSpec/Speckit only when both -legs exist. Land required leg changes; advance root gitlinks, pin digests and -workflow SHAs together using the standard bump tool. - -**Gate D:** exact assembled commits pass manifest/pin checks, canonical code +legs exist. Prepare continued-feature translations and disposable workflow +checks from Phase 5 in the candidate before the final migration batch; actual +lane rebinding still waits for reviewed cutover. After implementation and verification, reconcile the net tested +effect in one final spec batch: governing specs/contracts/design, dated departures, +as-built record, all amendment dispositions, evidence, deferred ideas and the +actual landed code commit. Retain superseded/reverted/deferred history while +excluding inactive deltas. Preserve archived approvals through linked records. +Code may land first; completed assembly advancement waits for reconciliation +to land and matching spec/code pins. Close the reconciliation issue after the +spec PR lands; the feature landing record tracks the assembly PR. Advance +gitlinks, pin digests and workflow SHAs together using the standard bump tool. + +**Gate D:** prepared continued-feature receipts and disposable workflow checks +are reviewed; final reconciliation and matching spec/code commits are landed; +exact assembled commits pass manifest/pin checks, canonical code tests/platform checks, install compatibility and bootstrap. The existing source/install remain usable until root adoption review and merge complete. @@ -162,8 +209,15 @@ using the existing binding tools and exact session records. **Gate E:** every continued feature has a reviewed migration receipt and preserved source; bindings and workflow resolve the intended new trees. +Each continued feature retains its governing baseline and reconciliation +obligations; migration does not declare unfinished features implemented. Obsolete PR disposition and any old-worktree retirement are separate explicit decisions after verification, never an automatic cleanup step. +Archive the migration's full spec change only after implementation and assembly +pin landing. Explicitly select that root; a local amendment archive proves no +reconciliation. Pin subsequent archive-record spec commits normally. Reopen +reconciliation if behavior changes afterward; cancellation records abandonment +and dispositions rather than an as-built result. ## Rollback diff --git a/specs/004-migrate-to-triad/protocol-review.md b/specs/004-migrate-to-triad/protocol-review.md new file mode 100644 index 00000000..fe4464df --- /dev/null +++ b/specs/004-migrate-to-triad/protocol-review.md @@ -0,0 +1,48 @@ +# Migration protocol review — October 4, 2026 + +This refresh applies the adopted manual **Triad Feature Amendments** section +of the installed shared OpenSpec/Speckit workflow, including its +single-repository-to-triad migration instructions. Approval/extraction/history, +per-path verification and exact-pin mechanics remain governed by openRepoShape. +The consumer standard remains pinned at `39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. + +## Sources read + +| Source identity | Snapshot SHA-256 | +| --- | --- | +| `$AGENT_PROTOCOL_ROOT/protocols/openspec-speckit-workflow.md` | `867b09a1329a2ba1c8005f2fbe43a4857f4eba7dd69c474c4e03d54bb2ecc20c` | +| `$AGENT_PROTOCOL_ROOT/protocols/project-agent-bootstrap.md` | `39341a7d0958e5efb9af5aa69de6e692d03129047cbcad48ad7f32bb8076ee28` | +| workBenches `docs/triad-feature-amendments.md` | `549d35974d42f474f2c7b2004e900b943c9a4543c479b97c541f47de1035f8d7` | +| workBenches `docs/triad-feature-amendment-templates.md` | `43647f4419dac8e28a3f428469ae27acd1444846736e9d388d967bb93c500a5f` | + +The workBenches documents and governing +`openspec/changes/adopt-triad-feature-amendments/` were read from its working +branch, where they were uncommitted at inspection. These content digests identify +the reviewed drafts; they are not landed commit identities or distribution +evidence. The installed protocol records Brett Heap's October 4 adoption of +manual use. Schema, managed root routing, template distribution and upstream +placement automation remain pending in their owning repositories. + +## Application to this migration + +- Keep full proposals, archives, canonical specs/contracts and Speckit files + in spec, including proposals authored on existing feature branches. Do not + manufacture amendments for historical features. +- Put genuine bounded amendment records in the code feature folder + `features//openspec/`. Review them explicitly in the mapping + until the upstream exception is qualified. No tracked amendment folders were + observed in current main or retained feature heads. +- Preserve approved original repository/commit/path provenance and verify + correspondence to extracted spec identities. Repair links/root selection; + filtered hashes cannot be guessed or substituted for approval evidence. +- Update working Speckit instructions immediately for accepted bounded changes; + record authority, requirements/tasks, evidence and dispositions manually. + New runtime/capability/authority/external obligations use full governance. +- At the first amendment, create one spec reconciliation issue. Reconcile all + net tested effects once at completion, linking landed code and evidence. + Completed assembly advancement waits for matching reconciled spec/code pins. + Archive only after implementation and assembly landing; explicitly select + the full spec root and preserve inactive/archived history. + +Implementation remains in [tasks.md](tasks.md); no external issue, schema, +protocol distribution, installation or migration was executed by this refresh. diff --git a/specs/004-migrate-to-triad/quickstart.md b/specs/004-migrate-to-triad/quickstart.md index a63a83b8..7f225487 100644 --- a/specs/004-migrate-to-triad/quickstart.md +++ b/specs/004-migrate-to-triad/quickstart.md @@ -1,7 +1,9 @@ # Review this migration plan Read [plan.md](plan.md), then the [resolved adoption mapping](../../openspec/changes/migrate-to-triad/adoption-plan.yaml) -and [work inventory](inventory.json). The mapping is for the recorded source +and [work inventory](inventory.json). Read the [protocol review](protocol-review.md) +for canonical spec placement, bounded code amendments and final reconciliation. +The mapping is for the recorded source main only; implementation and live feature transfer have not been attempted. From the original source checkout, the planning validation is: @@ -24,6 +26,14 @@ regenerate the plan using `adopt-project.py plan`, retain reviewed resolutions and follow-ups, then recheck. Do not change the baseline field to suppress a stale-plan finding. Do not run real `execute` as a planning check. +After adoption, full proposal commands select the intended spec feature checkout; +bounded amendments use their explicitly verified code-local root. Newer CLI +store selection is optional and must resolve the intended feature branch. +Use manual records until schema/routing/distribution is qualified. Update working +Speckit instructions immediately for accepted amendments; reconcile the net +tested effect once before completed assembly advancement. A local archive +cannot substitute for full spec reconciliation or assembly landing. + Before execution, complete Gates A/B and review the actual frozen baseline, visibility, mapping, follow-ups and preservation receipts. The standard requires explicit approval before repository creation. All execution tasks are open in diff --git a/specs/004-migrate-to-triad/research.md b/specs/004-migrate-to-triad/research.md index acf1eb64..e38cfa29 100644 --- a/specs/004-migrate-to-triad/research.md +++ b/specs/004-migrate-to-triad/research.md @@ -7,14 +7,19 @@ No external writes, repositories, installation or migration occurred. | Finding | Decision / execution consequence | | --- | --- | -| October 4 main still matches GitHub at `82ecebe` (79 commits), 58 tracked paths and one standard gitlink | Regenerate/check the current main mapping; #61 and #146 remain included without replay. | +| Main advanced to `daed209` with merged #134: 80 commits, 64 tracked paths and one standard gitlink; GitHub matches | Regenerate/check the mapping: 18 spec / 40 code / six root, no drops; #61/#146/#134 are included without replay. | | Classifier treats shipped Markdown skills/commands as specification | Override to code with written reasons; these are installation payloads. | | Original contract is the implementation's upstream dependency lock | Keep code pin/gitlink/.gitmodules together; generated assembly pins are separate. | | `.specify` and agent directories are ignored local scaffolding | Preserve local config separately and regenerate root workflow after adoption. | -| Six current worktrees/local branches; main and all feature checkouts clean before refresh edits; open PRs #134/#121/#97 | Keep retired `002`/`003`, lclaude, detached, claim-takeover and #146 checkouts out of replay. Clean status alone does not complete preservation Gate A. | +| Six current worktrees/local branches; open PRs #121/#97; concurrent #121 staged/unstaged/untracked work and advanced #97 head | Preserve current active contents, not the earlier clean observation. Retain merged #134 checkout without replay; retired checkouts stay out of replay. | +| PR #121 now owns feature 005 confirmed-legacy-only restart and a full authority decision; managed/unknown ownership refuses and merge hold remains | Preserve the entire index/working/untracked delta and continue paired `005`; full authority/runtime governance remains spec, independent of #97 and feature 001 managed authority. | | Feature 001 advanced to clean `3c26041`, committing broker/task-recovery work and restoring rollover/workspace packets | Preserve its complete branch delta and restored governance/ideation; remove the stale requirement to capture the previous dirty files as uncommitted work. | | Feature 001 now selects required local Omnigent for `lclaude → oclaude → profile launch`, with implementation/qualification pending | Carry launcher contract, FR-047/T063–T065, LS custody/input fences and separate factory-broker gates; keep wrapper ownership in workBenches and transport ownership in Omnigent. | -| Non-draft #134 advanced to `11037f0` and has a clean sibling worktree | Carry code installer/resolver/preflight/timeout/test changes and spec manual if unmerged; regenerate baseline if it lands before freeze. | +| #134 merged as `daed209`; clean sibling worktree remains at `11037f0` | Installer/resolver/preflight/timeout/test changes and manual are included in the regenerated main baseline; retain checkout as recovery history. | +| Installed protocol now adopts manual Triad Feature Amendments; workBenches source docs are uncommitted drafts | Record source digests in protocol review; preserve full proposals/canonical files in spec and genuine bounded records in code. Do not claim distribution or automated qualification. | +| Pinned upstream classifier has no qualified amendment exception; no amendment paths observed in current source/retained heads | Explicitly review future genuine paths as code with reasoned local resolutions or a later reviewed upstream rule; do not edit pinned consumers or manufacture historical amendments. | +| Filtered commits may change approved spec identity | Preserve original repository/commit/path and verify extracted correspondence; repair links and selected roots without guessed hashes. | +| Bounded amendments require immediate working files and one final docs reconciliation | Extend the existing 19 tasks; create one spec issue at first amendment, reconcile tested net effect, and block completed assembly advancement until matching reconciled spec/code land. | | Two remaining preservation refs have no local worktrees | Review their history as recovery evidence; do not treat them as active writers or require recreation. | | Estate status refuses this manifest-free single repository | Relay no-estate refusal; use local Git and read-only GitHub observations now, then estate status after adoption. | | Installer fetches bare paths from `openRepoTools` and local sibling payloads | Preserve public root entry point and bind every payload to one code pin. | diff --git a/specs/004-migrate-to-triad/spec.md b/specs/004-migrate-to-triad/spec.md index 6799586e..273cbff1 100644 --- a/specs/004-migrate-to-triad/spec.md +++ b/specs/004-migrate-to-triad/spec.md @@ -27,8 +27,10 @@ preserved. Test forks/ref overrides, missing pins and partial fetch refusal. The operator continues existing feature IDs through paired spec/code worktrees and lands their changes through leg PRs followed by a validated assembly pin -bump. OpenSpec runs in spec; Speckit runs from the assembly with selected -feature paths. A code-only clone cannot falsely claim composed CI success. +bump. Full OpenSpec proposals run in the selected spec checkout; genuine +bounded amendments use the code feature folder with explicit root selection. +Speckit runs from assembly with selected feature paths. A code-only clone +cannot falsely claim composed CI success. The migration preserves feature 001's model-role assignments and open gates. ## Functional requirements @@ -45,8 +47,10 @@ The migration preserves feature 001's model-role assignments and open gates. - **FR-005**: Keep original installation entry points and one immutable payload revision, including forks/ref overrides, local/offline behavior and existing all-or-none ownership/receipt semantics; do not depend on developer checkouts. -- **FR-006**: Put product OpenSpec/Speckit/docs in spec and implementation plus - shipped skills/commands/tests in code; project workflow stays at root. +- **FR-006**: Put full proposals, archives, canonical OpenSpec/Speckit/docs in + spec and implementation plus shipped skills/commands/tests in code; project + workflow stays at root. Genuine bounded feature amendments are the narrow + code placement exception under FR-013, not a second spec baseline. - **FR-007**: Continue selected feature deltas with old/new identity and content receipts, paired leg worktrees and unchanged feature/role meanings. Do not merge old monorepo branches over the adopted assembly. @@ -61,6 +65,30 @@ The migration preserves feature 001's model-role assignments and open gates. acceptance; use non-destructive rollback and existing estate/WIP contracts. - **FR-012**: Treat visibility, baseline, branch disposition and lane rebinding as reviewed execution decisions; never infer approval from generated metadata. +- **FR-013**: Explicitly review genuine bounded + `features//openspec/` records as code until upstream placement + is qualified. Full proposals retain their role regardless of source branch; + do not manufacture local amendments for historical features or change pins + to hide an unqualified placement rule. +- **FR-014**: Preserve approved original repository/commit/path provenance and + verify correspondence to extracted spec identities after filtering. Repair + cross-leg links, baseline references and selected feature roots/stores without + guessing new hashes or editing pinned base mounts. Maintain records manually + until schema/routing/distribution is qualified in its owning repositories. +- **FR-015**: Record accepted bounded adjustments with unique feature-local IDs, + approved baseline, authority/source, affected requirements/tasks, verification + impact and dispositions; update working spec/plan/acceptance/tasks before + dependent implementation. Larger scope, runtime/authority or external-contract + changes require full spec governance; unnecessary larger ideas remain deferred + to linked future-proposal issues. Speckit is the only executable task list. +- **FR-016**: Create one spec reconciliation issue at the first amendment. + Reconcile net tested effects once at completion, retaining all dispositions, + approved history, as-built departures, verification and landed code identity. + Completed assembly advancement requires landed reconciliation and matching + spec/code pins. Close the issue after spec landing; explicitly archive the + full spec root only after implementation and assembly landing, advancing any + archive commit normally. Reopen for later behavior changes; cancellation + records abandonment, not as-built completion. ## Success criteria @@ -74,6 +102,13 @@ The migration preserves feature 001's model-role assignments and open gates. required CI pass for the selected composed project; no hidden skips. - **SC-005**: A rehearsed rollback restores usable development/installation without discarding work, altering private workspace pointers or force pushes. +- **SC-006**: Every continued feature has verified baseline correspondence, + explicit root selection and correct artifact roles; local archive cannot + satisfy canonical reconciliation. Manual workflow and pending automation are + separately evidenced on target workstations. +- **SC-007**: The tested migration has a final spec reconciliation and matching + landed code identity before completed assembly advancement, with every + amendment disposition accounted for and inactive deltas excluded. ## Assumptions and approval diff --git a/specs/004-migrate-to-triad/tasks.md b/specs/004-migrate-to-triad/tasks.md index 4c070093..b1a16859 100644 --- a/specs/004-migrate-to-triad/tasks.md +++ b/specs/004-migrate-to-triad/tasks.md @@ -4,7 +4,7 @@ The sole executable task list for the governing OpenSpec change. Planning artifacts and preliminary mapping validation do not complete these tasks. Every task remains open. Run implementation from the selected feature worktrees; actual repository creation/cutover require the approval gates. -The October 4 refresh updates the snapshot/mapping portion of T001/T003 only; +The protocol refresh updates snapshot/mapping and planned workflow follow-ups; owner decisions, private preservation/restore receipts and execution gates remain pending. Retired checkouts are not instructions to recreate worktrees. @@ -25,7 +25,11 @@ remain pending. Retired checkouts are not instructions to recreate worktrees. - [ ] T003 [US1] Confirm proposed leg names/visibility and inspect prerequisite availability; regenerate `adoption-plan.yaml` for the reviewed preliminary rehearsal source, retaining reasoned overrides. Verify standard `check` - passes and records all source paths with no unapproved drops. + passes and records all source paths with no unapproved drops. Explicitly + classify genuine bounded feature amendment folders as code until upstream + qualification; preserve full proposals/archives/canonical Speckit as spec + regardless of branch origin. Record no amendment paths when none exist; + do not manufacture historical amendments or weaken shape pins. ## Phase 2 — Local adoption rehearsal (Gate B foundation) @@ -42,6 +46,11 @@ remain pending. Retired checkouts are not instructions to recreate worktrees. reviewable patches; bootstrap selected root workflow after manifest/legs exist. Verify first-line shape guidance, relative paths, actual `.specify` configuration and paired feature selection without copying machine secrets. + Verify explicit full-spec/local-amendment roots and intended feature branches, + local-root precedence over store pointers and supported CLI store selection. + Keep manual records until schema/routing is qualified; check protocol/command + distribution per target workstation through owning workBenches delivery, + without treating a scaffold bootstrap as distribution of this manual decision. ## Phase 3 — Installation and checks (Gate B completion) @@ -77,13 +86,23 @@ remain pending. Retired checkouts are not instructions to recreate worktrees. - [ ] T013 [US1] Execute the approved standard adoption from the isolated clone. Verify and relay the complete content-accounting table and root PR details; stop landing on any refusal/mismatch, retaining all source/evidence. -- [ ] T014 [US3] Apply reviewed follow-ups through leg/root PRs, land required - leg commits, and advance root gitlinks/pins/workflow SHAs with standard tools. - Verify no in-place edits to copied pinned shape files or the mounted standard. +- [ ] T014 [US3] Apply reviewed follow-ups and prepare code/spec PRs together. + For accepted bounded adjustments, retain authority/baseline/requirements/tasks + and dispositions in manual code-local records, update working Speckit files + before implementation and create one spec reconciliation issue at the first + amendment. Escalate larger current scope to full governance; defer unnecessary + capabilities to linked future issues. Prepare final reconciliation against + tested behavior; code may land first, but completed assembly advancement waits + for matching reconciled spec. Verify no in-place pinned-shape edits. - [ ] T015 [US2] Validate the real exact assembled head using canonical tests, required platform/CI, installed/local/raw compatibility, bootstrap and rollback - checks. Verify all Gate D results belong to the selected commits before root - adoption merge; keep original development/install available until then. + checks, including prepared continued-feature translations and disposable + workflow evidence from T016–T018. Complete one final spec reconciliation batch with net tested effects, + every amendment disposition, dated departures/as-built record, archived approval + provenance, deferred issues and actual landed code/evidence. Land spec, close + its reconciliation issue and advance matching root gitlinks/pins/workflow SHAs + with standard tools; verify final Gate D results at the actual selected commits + before root adoption merge. Keep original development/install available. ## Phase 5 — Continue features and controlled cutover (Gate E) @@ -93,33 +112,49 @@ remain pending. Retired checkouts are not instructions to recreate worktrees. amendment and open runtime gates. Carry the clean `3c26041` checkpoint's restored rollover/workspace packets, launcher contract and FR-047/T063–T065; preserve workBenches/Omnigent ownership and required local adapter qualification - separately from factory-broker admission. Do not replay completed work or claim new + separately from factory-broker admission. Preserve approved original repository/ + commit/path and verify extracted spec correspondence, repairing links/task roots + without guessed hashes. Full feature-branch proposals remain in spec; preserve + any actual bounded records in code and continued reconciliation obligations. + Do not replay completed work or claim new runtime acceptance from migration. - [ ] T017 [US1] Translate every other approved continued PR/branch/detached worktree, including this migration feature, with explicit spec/code/root mappings and conflict review. Verify each object has a complete receipt or approved archived-original disposition; preserve original PR review history. - Current candidates are #97/#121/#134 and feature 004. Account for merged - #61/#146 through the baseline and retired checkout history through its receipts; - do not recreate cleaned worktrees or replay integrated changes. PR #134 is - non-draft at `11037f0` with a clean worktree: account for its resolver/checker, - installer, lane preflight/fencing, timeout containment, manual and tests. + Current candidates are #97/#121 and feature 004. Account for merged + #61/#146/#134 through the baseline and retired/retained merged checkout history + through receipts; do not recreate cleaned worktrees or replay integrated changes. + Verify approved baseline correspondence and artifact roles for every continued + feature, including this migration's full proposal in spec. + Capture PR #121's complete active index/working files and untracked `005` + feature/full legacy authority decision; carry its confirmed-legacy-only scope, + managed/unknown refusal and merge hold into paired `005` continuation. Keep + PR #97's separately owned diagnostics and current branch head independent. - [ ] T018 [US3] Exercise OpenSpec/Speckit feature selection and estate park/resume in disposable triad fixtures, then perform reviewed binding/WIP transitions through supported tools at owner breakpoints. Verify correct feature roots/session bindings and relay refusals without force repairs. - [ ] T019 [US3] Publish Gate E/cutover evidence and actual PR dispositions; verify all continued features are usable and recoverable before requesting - separate old-worktree retirement. Do not auto-close old PRs or delete backups. + separate old-worktree retirement. Verify each continued feature retains its + outstanding reconciliation/runtime gates rather than declaring it implemented. + Archive the migration's full spec change only after implementation and assembly + pin landing; explicitly select its root and pin any later archive commit normally. + Reopen reconciliation for subsequent behavior changes; record cancellation as + abandonment. Do not auto-close old PRs or delete backups. ## Dependencies T001–T003 precede rehearsal. T004–T006 precede integration validation; T007–T010 may progress within the rehearsed topology after byte accounting. T011 closes Gate B. T012 requires Gate A/B and is the real-creation approval -boundary. T013–T015 precede root adoption merge. T016–T018 can be prepared in -the candidate after legs exist, but actual lane rebinding follows reviewed -cutover and safe breakpoints. T019 requires validated retained feature receipts. +boundary. T013–T015 precede root adoption merge. Prepare T016–T017 translations +and T018 disposable workflow checks in the candidate after legs exist, before +T015's final reconciliation. Actual T018 lane rebinding follows reviewed root +adoption/cutover and safe breakpoints. T019 requires validated retained feature +receipts; later behavior changes reopen reconciliation before another completed +assembly advancement. ## Requirement coverage @@ -136,3 +171,7 @@ cutover and safe breakpoints. T019 requires validated retained feature receipts. | FR-010 | T003–T005, T011–T013 | | FR-011 | T002, T011, T018–T019 | | FR-012 | T001, T012, T018–T019 | +| FR-013 | T003, T006, T016–T017 | +| FR-014 | T006, T016–T018 | +| FR-015 | T014–T015 | +| FR-016 | T014–T015, T019 | diff --git a/specs/004-migrate-to-triad/verification.md b/specs/004-migrate-to-triad/verification.md index d2834533..57c0a9e4 100644 --- a/specs/004-migrate-to-triad/verification.md +++ b/specs/004-migrate-to-triad/verification.md @@ -1,6 +1,48 @@ # Migration planning verification — October 4, 2026 -## October 4 repository refresh +## Updated migration protocol refresh + +Read the installed shared workflow's **Triad Feature Amendments**, including +**Single repository to triad migration**, the revised bootstrap status and +workBenches source workflow/templates/governance. [protocol-review.md](protocol-review.md) +records their identities and snapshot digests, including the uncommitted draft +state of the workBenches source. Manual adoption does not establish delivered +schema/routing/distribution/placement automation. + +The revised proposal, design, capability requirements and Speckit plan/tasks +now distinguish canonical spec content from genuine bounded code amendments, +retain original approved provenance with verified filtered correspondence, +require explicit root selection and working-spec updates, and require one +final tested reconciliation before completed assembly advancement. No existing +tracked bounded amendment paths were observed in main or retained feature heads; +none were manufactured. All 19 implementation tasks remain open, covering 16 +migration requirements. + +Source main advanced to `daed20957f2dd2f22cca24053bb5bc8636ff6b3f` with #134 +merged at `2026-10-04T16:52:38Z`; read-only GitHub main matched. The two open +PRs are #121 and #97. Six worktrees/local branches remain, including the clean +merged #134 checkout and clean feature 001 at `3c26041`. Main extraction now +includes the resolver/checker installer changes, tests and manual. +The final snapshot check detected concurrent #121 staged/unstaged/untracked +work, including feature 005 and its full legacy authority decision, plus an +advanced #97 recovery head. Inventory/dispositions were refreshed to preserve +those active changes, separate ownership and the existing #121 merge hold; +the earlier all-clean observation is historical, not current readiness. + +The pinned standard regenerated the main-only plan: 64 source paths, 18 spec / +40 code / six root, no unresolved path or drop, 80 commits and 28 follow-ups. +Standard `check`, OpenSpec strict validation, supplemental path/link/inventory/ +requirement checks and `git diff --check` passed. Planning checks do not establish +upstream exception qualification, local rehearsal or runtime acceptance. + +Estate `status --fetch` again refused the manifest-free repository and fetched +nothing; local Git and read-only GitHub supplied current observations. This +refresh created no external issue, moved no lane, rewrote no workspace pointer, +changed no pinned standard, distributed no protocol and performed no adoption. + +The previous refresh sections below are historical evidence only. + +## Earlier October 4 repository refresh — historical Read-only GitHub main still matches local `82ecebee13edaa915b68550170faffdf761754d5`. The pinned standard remains `39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. From 5455805d623b6d3e2943606745c4ac812dc40be5 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sun, 4 Oct 2026 21:33:18 +0000 Subject: [PATCH 5/9] docs: record triad preparation and adopter blocker --- .../migrate-to-triad/adoption-plan.yaml | 2 + openspec/changes/migrate-to-triad/design.md | 5 +- openspec/changes/migrate-to-triad/proposal.md | 6 + openspec/changes/migrate-to-triad/tasks.md | 3 +- specs/004-migrate-to-triad/adopter-blocker.md | 68 ++++++ .../004-migrate-to-triad/installer-design.md | 97 ++++++++ specs/004-migrate-to-triad/inventory.json | 207 +++++++++++++++--- specs/004-migrate-to-triad/plan.md | 28 ++- specs/004-migrate-to-triad/preparation.md | 115 ++++++++++ specs/004-migrate-to-triad/quickstart.md | 5 +- specs/004-migrate-to-triad/research.md | 7 + specs/004-migrate-to-triad/tasks.md | 14 +- specs/004-migrate-to-triad/verification.md | 32 +++ 13 files changed, 547 insertions(+), 42 deletions(-) create mode 100644 specs/004-migrate-to-triad/adopter-blocker.md create mode 100644 specs/004-migrate-to-triad/installer-design.md create mode 100644 specs/004-migrate-to-triad/preparation.md diff --git a/openspec/changes/migrate-to-triad/adoption-plan.yaml b/openspec/changes/migrate-to-triad/adoption-plan.yaml index cf1678f3..f9d26fbe 100644 --- a/openspec/changes/migrate-to-triad/adoption-plan.yaml +++ b/openspec/changes/migrate-to-triad/adoption-plan.yaml @@ -1,4 +1,6 @@ # Preliminary adoption mapping; planning only. Execution needs a refreshed baseline and explicit approval. +# Real conversion waits for PRs #97/#121 to land and the pinned-adopter mounting refusal to be resolved upstream. +# Disposable rehearsal evidence: ../../../specs/004-migrate-to-triad/preparation.md; Gates A/B remain open. # Paths are relative to the source checkout. Generated metadata does not authorize repository creation. schema_version: 1 kind: adoption-plan diff --git a/openspec/changes/migrate-to-triad/design.md b/openspec/changes/migrate-to-triad/design.md index fc1e42ee..a51ee15a 100644 --- a/openspec/changes/migrate-to-triad/design.md +++ b/openspec/changes/migrate-to-triad/design.md @@ -63,7 +63,10 @@ belong in spec; regenerate the path plan if the source adds such contracts. Keeping the standard in root would move its gitlink away from its dependency pin and implementation consumers, and mutate the original `.gitmodules` during the byte-accounted split. Keeping that dependency unit in code preserves its -existing paths. Existing-submodule handling still requires local rehearsal; +existing paths. The provisional local rehearsal measured a mounting refusal +after staging deletion of the original `.gitmodules`; see the +[reproduction](../../../specs/004-migrate-to-triad/adopter-blocker.md). +Resolve upstream and repeat before composed acceptance; plan coverage validation alone is not execution certification. ### Public installation compatibility diff --git a/openspec/changes/migrate-to-triad/proposal.md b/openspec/changes/migrate-to-triad/proposal.md index 76718921..1275c04e 100644 --- a/openspec/changes/migrate-to-triad/proposal.md +++ b/openspec/changes/migrate-to-triad/proposal.md @@ -2,6 +2,12 @@ ## Why +Preparation is authorized while PRs #97/#121 are active; real conversion waits +for both to land. The [preparation receipt](../../../specs/004-migrate-to-triad/preparation.md) +records provisional preservation and a local adopter refusal. Resolve that +upstream blocker and refresh the merged baseline, preservation and rehearsal +before the existing real-execution approval gate. + openRepoTools currently keeps product decisions, implementation and project workflow in one repository. Elect the openRepoShape triad so specifications and implementation can evolve independently while the existing repository diff --git a/openspec/changes/migrate-to-triad/tasks.md b/openspec/changes/migrate-to-triad/tasks.md index 858e93fc..6ef4d5bc 100644 --- a/openspec/changes/migrate-to-triad/tasks.md +++ b/openspec/changes/migrate-to-triad/tasks.md @@ -10,7 +10,8 @@ This checklist tracks governance, not duplicate implementation tasks. artifacts exist and planning validation is recorded. - [ ] 1.2 Approve the frozen source/standard revisions, repository visibility, final path mapping, follow-ups and active-work dispositions after Gates A/B; - verify approval explicitly names the actual execution plan. + verify #97/#121 have landed, their content is accounted for through the + refreshed baseline, and approval explicitly names the actual execution plan. ## 2. Migration evidence and closure diff --git a/specs/004-migrate-to-triad/adopter-blocker.md b/specs/004-migrate-to-triad/adopter-blocker.md new file mode 100644 index 00000000..a0b8980e --- /dev/null +++ b/specs/004-migrate-to-triad/adopter-blocker.md @@ -0,0 +1,68 @@ +# Pinned adopter refuses an existing submodule split + +**Upstream:** opensoft/openRepoShape, pinned revision +`39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. +**Consumer:** openRepoTools main +`daed20957f2dd2f22cca24053bb5bc8636ff6b3f`. +**Measured:** October 4, 2026; disposable local remotes only. + +The reviewed mapping moves the existing `.gitmodules` together with +`upstream/openRepoShape` and its dependency contract into code. Plan `check` +passes. Both filtered legs are created, then assembly mounting returns 2: + +```text +REFUSED a git or gh command failed. THE EXACT COMMAND WAS: + git -c protocol.file.allow=always submodule add -q spec + (in ) + exit 128 +--- output --- +fatal: please make sure that the .gitmodules file is in the working tree +--- end output --- +``` + +`adopt-project.py`'s `_mount_the_legs` (line 1201 at this pin) removes moved +paths with `git rm`, then immediately invokes `git submodule add`. `.gitmodules` +is among the moved files; its tracked deletion is still staged when the new +submodule registration is attempted. The root index contains only the six +original front-door files at refusal. The generated assembly registrations +have not been created. Git's missing-file guard is the observed failure; no +assumption about nested dependency correctness substitutes for this evidence. + +## Reproduction for upstream review + +Use a fresh isolated clone of the consumer at the recorded commit, a copy of +the resolved [adoption plan](../../openspec/changes/migrate-to-triad/adoption-plan.yaml), +git-filter-repo 2.47.0, and empty disposable local directories: + +```sh +python3 /adopt-project.py check \ + --source --plan +python3 /adopt-project.py execute \ + --source --plan \ + --local-remote-dir \ + --work-dir --yes +``` + +The yes here covers the reviewed local test only. Never use the live consumer +as `--source` for this reproduction: local mode still pushes an adoption +branch to its source clone if execution reaches that step. + +Expected behavior: preserve the original registration in code, create fresh +assembly registrations for spec/code, complete source accounting, and retain +the dependency gitlink/contract identity. The source repository must remain +unchanged if execution refuses. + +The upstream regression should cover a populated existing submodule, its +registration explicitly assigned to code, the generated assembly registrations, +recursive clone/bootstrap, exact mode/object accounting, and failure recovery. +Also cover partial registration ownership and no-existing-module inputs so a +fix cannot silently erase retained registrations or create duplicate owners. + +No upstream issue or PR was published. No patch was applied to the pinned +checkout or failing candidate. Resolve through the owning repository's normal +review, then bump to a commit on its main and recompute the dependency digest. +Repeat the consumer rehearsal with fresh remotes. Gate B remains blocked by +this refusal even though extraction content was independently verified. + +Private execution log SHA256: +`ca2a11775ca3b6d179fc038436a28090f65b05feb2a1611d4578b315733b5168`. diff --git a/specs/004-migrate-to-triad/installer-design.md b/specs/004-migrate-to-triad/installer-design.md new file mode 100644 index 00000000..4f174789 --- /dev/null +++ b/specs/004-migrate-to-triad/installer-design.md @@ -0,0 +1,97 @@ +# Installer follow-up design and compatibility matrix + +Prepared for [T007–T010](tasks.md), against main `daed20957f2dd2f22cca24053bb5bc8636ff6b3f`. +Implementation patches wait for successful standard split verification. PRs +#97/#121 can change payloads and tests; refresh this inventory after landing. + +## One implementation, two entry contexts + +Keep the current implementation in code. Add a small assembly `openRepoTools` +entry point that selects that implementation; preserve the documented raw and +gh installation one-liners. Installed `openRepoTools` remains the code +implementation so it runs independently of developer worktrees. The assembly +entry point must delegate all installation, merging, ownership and retirement +mechanics to it. + +The root entry point alone is insufficient: subsequent installed `--install`, +`--skill` and WIP template operations currently fetch from the monorepo's +`REPO`/`REF`. Those code-side consumers need the same payload-source resolution. +Separate the selected assembly source from the resolved code payload source; +keep the existing public `OPENREPOTOOLS_REPO`/`OPENREPOTOOLS_REF` meanings. +Do not infer a code repository by appending `-code` to a fork name. + +For an adopted remote, resolve the requested assembly ref once to an immutable +commit. Read `contracts/code-pin.yaml` at that commit, strictly validate the +code role, repository slug, commit, path and digest grammar, and confirm the +assembly gitlink agrees. Fetch the implementation and all its payloads from +that same code repository/commit. Moving branches cannot mix artifact versions. +A present malformed pin must refuse rather than fall back to the legacy layout. + +For a legacy monorepo with no adoption manifest/pin, retain the established +owner/repository/ref override path. Distinguish an authoritative missing path +from authentication, transport and parse errors; those errors are refusals, +not evidence of a legacy repository. Forks use their selected assembly pin. +gh-first and curl fallback apply to the same resolved identities. Reuse jq +where needed; no new general YAML runtime dependency is justified by a fixed +pin grammar. Do not claim whole-tree digest validation from one downloaded +file: the composed validator checks the complete tree, while remote installation +must at least verify the gitlink and immutable identity it actually consumes. + +For a local adopted checkout, require a consistent assembly/code pin and +materialized selected payload set, then copy those local bytes offline. An +explicit code feature checkout must remain a supported development context; +record its actual code identity without pretending it is the assembled pin. +Incomplete local payloads must refuse or fetch only from the established +matching source; arbitrary local siblings plus unrelated remote bytes must not +form a successful adopted install. Define this distinction in the implementation +and user-facing diagnostics before acceptance. Preserve normal legacy sibling +behavior where the legacy installer currently promises it. + +## Code consumers that change + +| Existing consumer | Required adjustment | +| --- | --- | +| `fetch_from_repo` | Use the resolved payload repository/immutable revision for code assets; retain transport fallback and useful refusal output. | +| `collect_commands`, `collect_skills` | Establish coherent local/remote source before collecting; preserve all-before-placement staging. | +| `wip_shape_ref` | Read/fetch code's `contracts/openreposhape-pin.yaml`, distinct from assembly's code pin. Preserve its existing upstream-standard role. | +| `wip_fetch_template_file` | Continue selecting the template from the upstream standard pinned by code; do not repoint templates to spec or assembly. | +| Root entry point | Resolve/select/delegate; forward arguments, exit status and signal/temporary-file cleanup. It adds no installer mechanics. | +| Installed receipts, settings/hook merge and retire functions | Preserve existing ownership, mode, target and refusal behavior; source routing must finish before placement. | + +Current command/data payloads are the 15 names in `INSTALLABLES`, including +`claude-current` and `claude-restart-check`. Three skills and three command +files each have two destinations: 27 placed files plus two hook entries, 29 +artifacts. Derive counts from the lists; source comments with historical counts +are not the contract. Hooks stay in the user's existing settings, preserving +unrelated entries. No machine-specific config enters a payload archive. + +## Compatibility acceptance + +All rows below are pending measured tests in fresh disposable homes/remotes. +They describe coverage for the existing executable tasks, not completion claims. + +| Scenario | Required observation | +| --- | --- | +| Existing raw/gh one-liners | Root delegates to selected code implementation; same names, help and destinations. | +| Offline local legacy / adopted checkout | Legacy behavior retained; complete adopted payload copied without network and pinned identity checked. | +| Explicit local code feature | Intentional feature bytes selected coherently and actual identity recorded. | +| Installed reinstallation and skill-only operation | Works without the developer checkout and resolves the selected assembly/code source. | +| Legacy override / adopted fork | Existing overrides work; fork follows its actual code pin rather than a guessed repository. | +| Branch, tag, commit and slash-containing refs | Ref resolved once; every remote payload uses one immutable code identity. | +| Branch changes between downloads | No mixed versions; subsequent requests use the frozen identity. | +| Missing pin in valid legacy / partial adopted layout | Valid legacy permitted; adopted missing/malformed/mismatched pin refused. | +| Missing or malformed API response / authorization / transport failure | No silent legacy fallback; actionable refusal, no placed files. | +| Changed local gitlink / changed payload / incomplete local payload | Coherence decision enforced before staging/placement; no unrelated-source mix. | +| Missing command, skill, alias or failed settings merge | Existing all-before-placement guarantee holds; prior install remains unchanged. | +| Modes, ownership receipts and custom target paths | Same modes and computed targets; foreign files and unrelated hook entries preserved. | +| WIP init/template lookup | Code dependency pin selects upstream templates; workspace writer restrictions unchanged. | +| Missing nested dependency in standalone code | Existing documented test skips remain; composed acceptance requires complete dependencies. | +| Linux/macOS Bash 3.2 and Windows/WSL policies | Correct parses/platform gates; no new Bash features or PowerShell implementation. | + +Tests and fixtures need explicit **code**, **spec**, **assembly** and +**dependency** roots. In particular, `tests/test_repo_hygiene.py` reads the root +README/install line and agent guidance, while `tests/test_upstream_pin.py` +reads code Git identity and the nested dependency. CI must supply the matching +contexts rather than delete those checks. Keep `tests/run.sh` as the serialized +test entry point, selecting with pytest filters such as `-k upstream_pin`. +The assembly exact-pin integration job must refuse absent required contexts. diff --git a/specs/004-migrate-to-triad/inventory.json b/specs/004-migrate-to-triad/inventory.json index 656b6cef..cb4dd823 100644 --- a/specs/004-migrate-to-triad/inventory.json +++ b/specs/004-migrate-to-triad/inventory.json @@ -1,5 +1,5 @@ { - "observed_at": "2026-10-04T17:40:54.465251+00:00", + "observed_at": "2026-10-04T21:24:50.021151+00:00", "source_repository": "opensoft/openRepoTools", "source_main": "daed20957f2dd2f22cca24053bb5bc8636ff6b3f", "github_main": "daed20957f2dd2f22cca24053bb5bc8636ff6b3f", @@ -90,17 +90,17 @@ }, { "identity": "004-migrate-to-triad", - "head": "dda91f11c68818ebeca4264d88908c85818ff630", + "head": "48934f8d4c662be6ed07ad1b7df96dec22028ab2", "layout": "legacy-sibling", "readable_here": true, - "dirty_entries": 14, - "untracked_entries": 1, + "dirty_entries": 0, + "untracked_entries": 3, "locked": false, "prunable": false }, { - "identity": "feat/claude-current", - "head": "11037f01a45a72fa3fc9a8a2b0e24a64cbf1ed12", + "identity": "feat/supervised-context-restart", + "head": "0e4b311dfa33abca55282a4974cb96c7d1c584ed", "layout": "legacy-sibling", "readable_here": true, "dirty_entries": 0, @@ -109,18 +109,18 @@ "prunable": false }, { - "identity": "feat/supervised-context-restart", - "head": "5164ce1535b0f4f21d6b9e5b48318865a3c74295", - "layout": "legacy-nested", + "identity": "feat/claude-current", + "head": "11037f01a45a72fa3fc9a8a2b0e24a64cbf1ed12", + "layout": "legacy-sibling", "readable_here": true, - "dirty_entries": 43, - "untracked_entries": 3, + "dirty_entries": 0, + "untracked_entries": 0, "locked": false, "prunable": false }, { "identity": "feat/crash-consistent-lane-worktree-recovery", - "head": "8f69e96fbd33d84950b1acdaa2f2f3836c081341", + "head": "07fb3adb8b4ac1476c641685096f00f0d20593c6", "layout": "legacy-nested", "readable_here": true, "dirty_entries": 0, @@ -136,7 +136,7 @@ }, { "identity": "004-migrate-to-triad", - "head": "dda91f11c68818ebeca4264d88908c85818ff630" + "head": "48934f8d4c662be6ed07ad1b7df96dec22028ab2" }, { "identity": "feat/claude-current", @@ -144,11 +144,11 @@ }, { "identity": "feat/crash-consistent-lane-worktree-recovery", - "head": "8f69e96fbd33d84950b1acdaa2f2f3836c081341" + "head": "07fb3adb8b4ac1476c641685096f00f0d20593c6" }, { "identity": "feat/supervised-context-restart", - "head": "5164ce1535b0f4f21d6b9e5b48318865a3c74295" + "head": "0e4b311dfa33abca55282a4974cb96c7d1c584ed" }, { "identity": "main", @@ -170,21 +170,25 @@ "open_prs": [ { "number": 121, - "title": "`/ctx` writes its restart to disk before it kills anything, and what tmux starts is a supervisor that stays in the pane \u2014 the fresh session was an environment variable the launcher dropped (#94)", + "title": "Keep supervised manual ctx as legacy compatibility and refuse managed lanes", "headRefName": "feat/supervised-context-restart", - "headRefOid": "5164ce1535b0f4f21d6b9e5b48318865a3c74295", + "headRefOid": "6c5b74176e9dbf7f9aad5a4ff8ec18ef0cbbb136", "isDraft": false, "url": "https://github.com/opensoft/openRepoTools/pull/121", - "state": "open" + "state": "open", + "merged": false, + "updated_at": "2026-10-04T21:24:31Z" }, { "number": 97, "title": "Crash-consistent lane worktree recovery: a lane now says WHERE its session stopped \u2014 RUNNING \u2192 SWAPPING \u2192 SWAPPED with a generation/operation fence, a machine-readable worktree inventory taken at every handoff, and a resume reconciliation that reports and resets nothing", "headRefName": "feat/crash-consistent-lane-worktree-recovery", - "headRefOid": "64dfd9736b1079c16beda0b1ac53a61e68b21d9e", + "headRefOid": "eada5fff495452cf5da47e52d9550fa4b40a4c96", "isDraft": false, "url": "https://github.com/opensoft/openRepoTools/pull/97", - "state": "open" + "state": "open", + "merged": false, + "updated_at": "2026-10-04T21:05:20Z" } ], "merged_since_previous_snapshot": [ @@ -223,7 +227,8 @@ "Remote-only preservation refs are local observations, not active worktrees or proof of fresh remote currency.", "Raw preservation backups belong host-locally or in the designated private WIP repository; public records hold identities/digests only.", "Inventory captures this planning branch before the refresh commit; its final HEAD is recorded by git.", - "PR #121 now contains active staged/unstaged/untracked work including feature 005 legacy-only supervised restart and its full authority decision; preserve current contents and merge hold. Snapshot counts can change while owners continue." + "#97/#121 have active writers; capture local and remote heads independently and recapture at cutover. Prior dirty snapshots remain historical evidence.", + "Real conversion waits for both PR landings, upstream adopter resolution, refreshed Gates A/B and explicit Gate C approval." ], "previous_refresh_snapshot": { "observed_at": "2026-10-04T16:37:26.292668+00:00", @@ -314,15 +319,19 @@ ] }, "latest_refresh": { - "previous_observed_at": "2026-10-04T16:37:26.292668+00:00", + "previous_observed_at": "2026-10-04T17:40:54.465251+00:00", "previous_worktrees": 6, "current_worktrees": 6, "current_local_branches": 6, "added_worktrees": [], "removed_worktrees": [], - "source_main_changed": true, - "protocol": "Shared OpenSpec/Speckit workflow: Triad Feature Amendments, adopted 2026-10-04; manual delivery only.", - "concurrent_work": "PR #121 retains staged/unstaged/untracked work including full legacy authority decision and feature 005; recovery branch advanced during refresh. Snapshot only, not a writer freeze." + "source_main_changed": false, + "protocol": "Triad Feature Amendments adopted manually October 4; automated delivery remains unqualified.", + "concurrent_work": "Owners continue #97/#121; local heads and remote PR heads can differ. Capture is provisional, not a writer freeze.", + "real_conversion_waits_for_prs": [ + 97, + 121 + ] }, "protocol_review": { "adopted_on": "2026-10-04", @@ -345,28 +354,164 @@ }, { "identity": "004-migrate-to-triad", - "head": "dda91f11c68818ebeca4264d88908c85818ff630", + "head": "48934f8d4c662be6ed07ad1b7df96dec22028ab2", "bounded_amendment_paths": [], "working_bounded_amendment_paths": [] }, { - "identity": "feat/claude-current", - "head": "11037f01a45a72fa3fc9a8a2b0e24a64cbf1ed12", + "identity": "feat/supervised-context-restart", + "head": "0e4b311dfa33abca55282a4974cb96c7d1c584ed", "bounded_amendment_paths": [], "working_bounded_amendment_paths": [] }, { - "identity": "feat/supervised-context-restart", - "head": "5164ce1535b0f4f21d6b9e5b48318865a3c74295", + "identity": "feat/claude-current", + "head": "11037f01a45a72fa3fc9a8a2b0e24a64cbf1ed12", "bounded_amendment_paths": [], "working_bounded_amendment_paths": [] }, { "identity": "feat/crash-consistent-lane-worktree-recovery", - "head": "8f69e96fbd33d84950b1acdaa2f2f3836c081341", + "head": "07fb3adb8b4ac1476c641685096f00f0d20593c6", "bounded_amendment_paths": [], "working_bounded_amendment_paths": [] } ] + }, + "preparation_previous_snapshot": { + "observed_at": "2026-10-04T17:40:54.465251+00:00", + "worktrees": [ + { + "identity": "main", + "head": "daed20957f2dd2f22cca24053bb5bc8636ff6b3f", + "layout": "root", + "readable_here": true, + "dirty_entries": 0, + "untracked_entries": 0, + "locked": false, + "prunable": false + }, + { + "identity": "001-separate-swap-ctx-handoff", + "head": "3c26041a4444a10bc240cb64a168ece4d9edf6c5", + "layout": "legacy-sibling", + "readable_here": true, + "dirty_entries": 0, + "untracked_entries": 0, + "locked": false, + "prunable": false + }, + { + "identity": "004-migrate-to-triad", + "head": "dda91f11c68818ebeca4264d88908c85818ff630", + "layout": "legacy-sibling", + "readable_here": true, + "dirty_entries": 14, + "untracked_entries": 1, + "locked": false, + "prunable": false + }, + { + "identity": "feat/claude-current", + "head": "11037f01a45a72fa3fc9a8a2b0e24a64cbf1ed12", + "layout": "legacy-sibling", + "readable_here": true, + "dirty_entries": 0, + "untracked_entries": 0, + "locked": false, + "prunable": false + }, + { + "identity": "feat/supervised-context-restart", + "head": "5164ce1535b0f4f21d6b9e5b48318865a3c74295", + "layout": "legacy-nested", + "readable_here": true, + "dirty_entries": 43, + "untracked_entries": 3, + "locked": false, + "prunable": false + }, + { + "identity": "feat/crash-consistent-lane-worktree-recovery", + "head": "8f69e96fbd33d84950b1acdaa2f2f3836c081341", + "layout": "legacy-nested", + "readable_here": true, + "dirty_entries": 0, + "untracked_entries": 0, + "locked": false, + "prunable": false + } + ], + "local_branches": [ + { + "identity": "001-separate-swap-ctx-handoff", + "head": "3c26041a4444a10bc240cb64a168ece4d9edf6c5" + }, + { + "identity": "004-migrate-to-triad", + "head": "dda91f11c68818ebeca4264d88908c85818ff630" + }, + { + "identity": "feat/claude-current", + "head": "11037f01a45a72fa3fc9a8a2b0e24a64cbf1ed12" + }, + { + "identity": "feat/crash-consistent-lane-worktree-recovery", + "head": "8f69e96fbd33d84950b1acdaa2f2f3836c081341" + }, + { + "identity": "feat/supervised-context-restart", + "head": "5164ce1535b0f4f21d6b9e5b48318865a3c74295" + }, + { + "identity": "main", + "head": "daed20957f2dd2f22cca24053bb5bc8636ff6b3f" + } + ], + "open_prs": [ + { + "number": 121, + "title": "`/ctx` writes its restart to disk before it kills anything, and what tmux starts is a supervisor that stays in the pane \u2014 the fresh session was an environment variable the launcher dropped (#94)", + "headRefName": "feat/supervised-context-restart", + "headRefOid": "5164ce1535b0f4f21d6b9e5b48318865a3c74295", + "isDraft": false, + "url": "https://github.com/opensoft/openRepoTools/pull/121", + "state": "open" + }, + { + "number": 97, + "title": "Crash-consistent lane worktree recovery: a lane now says WHERE its session stopped \u2014 RUNNING \u2192 SWAPPING \u2192 SWAPPED with a generation/operation fence, a machine-readable worktree inventory taken at every handoff, and a resume reconciliation that reports and resets nothing", + "headRefName": "feat/crash-consistent-lane-worktree-recovery", + "headRefOid": "64dfd9736b1079c16beda0b1ac53a61e68b21d9e", + "isDraft": false, + "url": "https://github.com/opensoft/openRepoTools/pull/97", + "state": "open" + } + ], + "latest_refresh": { + "previous_observed_at": "2026-10-04T16:37:26.292668+00:00", + "previous_worktrees": 6, + "current_worktrees": 6, + "current_local_branches": 6, + "added_worktrees": [], + "removed_worktrees": [], + "source_main_changed": true, + "protocol": "Shared OpenSpec/Speckit workflow: Triad Feature Amendments, adopted 2026-10-04; manual delivery only.", + "concurrent_work": "PR #121 retains staged/unstaged/untracked work including full legacy authority decision and feature 005; recovery branch advanced during refresh. Snapshot only, not a writer freeze." + } + }, + "preparation_evidence": { + "record": "specs/004-migrate-to-triad/preparation.md", + "backup_id": "prep-20261004T211123Z", + "all_ref_restore_verified": true, + "refs": 18, + "dependency_commit_restore_verified": true, + "captured_worktrees": 6, + "snapshot_git_restores_verified": 6, + "git_restore_summary_sha256": "550d1ce96c8a162c3371f95b53be188217f85fac6491e98e628ae307ea35b31e", + "gate_a_complete": false, + "gate_b_complete": false, + "local_adoption_exit": 2, + "reproduction": "specs/004-migrate-to-triad/adopter-blocker.md" } } diff --git a/specs/004-migrate-to-triad/plan.md b/specs/004-migrate-to-triad/plan.md index 7ca2fd2d..f0df01fc 100644 --- a/specs/004-migrate-to-triad/plan.md +++ b/specs/004-migrate-to-triad/plan.md @@ -1,13 +1,20 @@ # Migration plan: openRepoTools assembly/spec/code triad **Feature**: `004-migrate-to-triad` | **Date**: 2026-10-03 -**State**: Proposed plan; no migration, install or repository creation performed +**State**: Preparation and disposable rehearsal performed; real adoption remains on hold **Refresh**: 2026-10-04; adopted amendment protocol and merged PR #134 incorporated **Spec**: [spec.md](spec.md) **Governance**: [proposal](../../openspec/changes/migrate-to-triad/proposal.md) ## Baseline and deliverables +The user authorized preparation while #97/#121 are active and will notify us +when they land. **Wait for both landings before real triad conversion.** The +[preparation receipt](preparation.md) records verified private preservation, +partial local extraction and a measured pinned-adopter refusal. Gates A/B are +still open. The [installer design](installer-design.md) is ready for follow-up +implementation after successful standard split verification. + The [adoption mapping](../../openspec/changes/migrate-to-triad/adoption-plan.yaml) was regenerated from main `daed20957f2dd2f22cca24053bb5bc8636ff6b3f` using pinned openRepoShape `39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. All 64 tracked @@ -23,10 +30,10 @@ retained `feat/claude-current` checkout, whose merged changes need no replay. The two open PRs are #121 and #97. Feature 001 remains at `3c26041a4444a10bc240cb64a168ece4d9edf6c5`, committing its broker/task-recovery changes, restored rollover/workspace packets and October 4 launcher contracts. -Main and feature 001 remain clean. Concurrent work in PR #121's checkout now -includes staged/unstaged/untracked content and `005-supervised-legacy-ctx`; -the recovery branch also advanced. The inventory is an observation, not a -freeze of active writers. +Main and feature 001 remain clean. PR #121's earlier dirty work, including +`005-supervised-legacy-ctx`, has advanced into newer commits; the recovery +branch also advanced. Local and remote PR heads can differ while owners work. +The inventory is an observation, not a freeze of active writers. The [inventory](inventory.json) keeps the October 3 cleanup as history and records current heads, dirty counts and the two remote-only preservation refs. Machine paths, secrets/transcripts and backup contents are omitted; private @@ -134,9 +141,18 @@ decision. Pending automation remains with workBenches/openRepoShape. follow-up patches, resolved mapping and a passing compatibility test matrix. Mapping `check` alone does not satisfy this gate. +The provisional rehearsal passed mapping check and preserved extracted bytes, +then returned 2 mounting the legs because `.gitmodules` was staged for deletion. +See [adopter-blocker.md](adopter-blocker.md). Stop adoption on this refusal; +resolve upstream and repeat into fresh local remotes before treating Gate B +as passed. Installer/topology design can proceed while that resolution is pending. + ## Phase 3 — Freeze and approve real adoption -Coordinate a bounded main landing hold using applicable lane protocol. Re-read +After #97 and #121 land, account for their final merged content through the +new main baseline instead of replaying it as unfinished work. Review any +remaining local deltas separately. Coordinate a bounded main landing hold +using applicable lane protocol. Re-read current main and owner state, finalize source commit, mapping, standard pin, visibility, follow-ups and preservation receipts. Any source drift requires plan regeneration and check, not editing the commit field by hand. Present diff --git a/specs/004-migrate-to-triad/preparation.md b/specs/004-migrate-to-triad/preparation.md new file mode 100644 index 00000000..11259027 --- /dev/null +++ b/specs/004-migrate-to-triad/preparation.md @@ -0,0 +1,115 @@ +# Preparation while PRs #97 and #121 are active + +**Observed:** October 4, 2026. **State:** provisional preservation and local +rehearsal performed; Gates A/B remain open. Real conversion waits for both PRs +to land and the refreshed execution approval. This evidence belongs to +[T001–T011](tasks.md); it is not another executable task list. + +## Preservation evidence + +Private backup ID: `prep-20261004T211123Z`. Exact locations, file lists, +configuration, indexes, binary patches and restore copies remain host-local, +outside the repository. No backup contents are published here. + +- Source bundle: all 18 refs verified against a restored mirror and the live + ref listing before/after capture. SHA256: + `86213b096b6057a1d0fd64b2a950856a6aa8375f87641f49330d205635909c0e`. +- Dependency bundle: verified and restored the pinned standard commit. + SHA256: `2549703215e77353129ebc4d73a5dc7f1f4fd07079b5d86cf259e91a662a20e4`. +- Six worktrees: copied tracked/untracked files and selected ignored workflow + configuration, saved indexes and binary patches, verified restore-copy bytes + and symlinks, and checked source HEAD/status/content stability during capture. + Restored all six captured heads into independent Git clones, reconstructed + the captured index entries, copied captured working/configuration bytes, and + verified matching porcelain status, including #121's working edits. Preserved + repository-local config/excludes separately; restoring the excludes was + necessary to reproduce ignored workflow configuration. This is a restore + rehearsal of the snapshots, not an owner-coordinated freeze or live rollback. + Private Git restore summary SHA256: + `550d1ce96c8a162c3371f95b53be188217f85fac6491e98e628ae307ea35b31e`. +- Installed files/settings/receipts: captured current configured/default and + PATH locations in host and py-bench contexts. Host: 29 locations checked, + 27 present regular-file copies verified. Bench: 41 checked, 39 present copies + verified. These are locations, not the installer artifact count. Additional + profiles/workstations and actual installer rollback still need receipts. + +| Captured worktree identity | HEAD | Restored files verified | +| --- | --- | ---: | +| main | `daed20957f2dd2f22cca24053bb5bc8636ff6b3f` | 130 | +| 001-separate-swap-ctx-handoff | `3c26041a4444a10bc240cb64a168ece4d9edf6c5` | 309 | +| 004-migrate-to-triad | `48934f8d4c662be6ed07ad1b7df96dec22028ab2` | 73 | +| feat/supervised-context-restart | `a9506724c1526a734b2c1b82990ffbb908c4daa4` | 81 | +| feat/claude-current | `11037f01a45a72fa3fc9a8a2b0e24a64cbf1ed12` | 63 | +| feat/crash-consistent-lane-worktree-recovery | `07fb3adb8b4ac1476c641685096f00f0d20593c6` | 74 | + +All six were stable during their own capture. They are active observations; +heads differ from earlier local/remote snapshots and can change again. +Owner dispositions, retired-object preservation receipts and a final capture +at safe breakpoints remain required. This backup is local to this workstation. + +Read-only GitHub lookups returned HTTP 404 for both proposed leg repository +names. That means they were not visible to the current credentials, not a +guarantee of name availability or creation permission. Recheck at Gate C; +public visibility remains a proposal requiring the final decision. + +## Measured local rehearsal + +Used main `daed20957f2dd2f22cca24053bb5bc8636ff6b3f` and unchanged standard +`39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. A fresh single-branch source clone +had no linked worktrees. `git-filter-repo` 2.47.0 was installed into a private +venv for this rehearsal; existing bench/system installations were unchanged. +The package wheel SHA256 is +`2cd04929b9024e83e65db571cbe36aec65ead0cb5f9ec5abe42158654af5ad83`. + +The standard's `check` passed. `execute --local-remote-dir ... --yes` used only +disposable local remotes. Network protocols were disabled except local file +transport; gh/curl shims refused network calls. The source clone and real main +remained at the recorded commit. No real repository, PR or lane was changed. + +Execution returned **2** before an assembly split commit or final verification: + +```text +git -c protocol.file.allow=always submodule add -q spec +fatal: please make sure that the .gitmodules file is in the working tree +``` + +Both extracted legs exist for diagnosis. Independent comparison of path, +mode and object ID accounts for all 64 original entries: 18 spec, 40 code and +six unchanged root entries in the candidate index; zero missing or duplicated +entries. This is partial extraction evidence, not the standard's completed +adoption report or a composed candidate. + +| Temporary extracted leg | Commit | Kept commits | +| --- | --- | ---: | +| spec | `993d1e1d01c9ba28ecee003e518bc444cef7028b` | 25 | +| code | `ba5f25696ba24012731b356ddb91f10c5386a220` | 77 | + +Code retains the original `.gitmodules`, dependency contract and gitlink at +the pinned SHA. Initializing that nested dependency through a local URL rewrite +passed. The pinned standard's own tree-digest helper recomputed +`a44c0165beb9d3b6ddfce46925bf04b8d19934d5327b5a304b5f3f11af699b02`, +matching the unchanged dependency contract. Full recursive assembly bootstrap +is blocked. The canonical +`tests/run.sh -k upstream_pin` remained in its serialization wait; the owned +waiting wrapper was stopped after 45 seconds. No pytest assertion ran and no +test pass is claimed. The preceding unfiltered waiting attempt also never ran +tests. Do not bypass the wrapper to obtain a pass. + +See the [upstream reproduction](adopter-blocker.md) and +[installer design and compatibility matrix](installer-design.md). + +## Resume after the PR landings + +The user's landing notification triggers a fresh read of #97/#121, actual main +and owner state. Landed changes are accounted for through the new baseline; +remaining branch deltas and local edits still need dispositions. Existing +PR holds and review history are preserved until their owners settle them. + +Regenerate the mapping using the standard rather than editing its source SHA. +Resolve the adopter defect through upstream review, a commit on upstream main +and the recomputed pin/gitlink update. Repeat into fresh local remotes, then +prepare and test follow-up patches against the successfully verified split. +Repeat preservation at coordinated breakpoints. Gates A/B and explicit Gate C +approval still precede real repositories, root landing and live lane rebinding. + +No execution task is checked complete by this provisional evidence. diff --git a/specs/004-migrate-to-triad/quickstart.md b/specs/004-migrate-to-triad/quickstart.md index 7f225487..c0b10f9f 100644 --- a/specs/004-migrate-to-triad/quickstart.md +++ b/specs/004-migrate-to-triad/quickstart.md @@ -4,7 +4,10 @@ Read [plan.md](plan.md), then the [resolved adoption mapping](../../openspec/cha and [work inventory](inventory.json). Read the [protocol review](protocol-review.md) for canonical spec placement, bounded code amendments and final reconciliation. The mapping is for the recorded source -main only; implementation and live feature transfer have not been attempted. +main only. A disposable rehearsal extracted both legs and then refused assembly +mounting; [preparation.md](preparation.md) records the measured limits. Live +feature transfer and real conversion remain pending #97/#121 landings and the +execution gates. From the original source checkout, the planning validation is: diff --git a/specs/004-migrate-to-triad/research.md b/specs/004-migrate-to-triad/research.md index e38cfa29..ee1f0958 100644 --- a/specs/004-migrate-to-triad/research.md +++ b/specs/004-migrate-to-triad/research.md @@ -1,5 +1,12 @@ # Planning evidence and decisions +Latest measured preparation is in [preparation.md](preparation.md). Private +git-filter-repo availability now exists for local rehearsal. Existing-submodule +mounting is a measured refusal, documented in [adopter-blocker.md](adopter-blocker.md), +and installer source-routing design is in [installer-design.md](installer-design.md). +The earlier observations below predate that attempt. Both #97/#121 landings +precede real conversion and a new baseline/rehearsal. + October 3 inspection and October 4 refresh used the pinned standard's `AGENTS.md`, adoption implementation, generated CLI reference and placement policy; root installer, CI and fixture code; local branch/worktree state; read-only GitHub PR metadata. diff --git a/specs/004-migrate-to-triad/tasks.md b/specs/004-migrate-to-triad/tasks.md index b1a16859..6a30ed2c 100644 --- a/specs/004-migrate-to-triad/tasks.md +++ b/specs/004-migrate-to-triad/tasks.md @@ -8,6 +8,11 @@ The protocol refresh updates snapshot/mapping and planned workflow follow-ups; owner decisions, private preservation/restore receipts and execution gates remain pending. Retired checkouts are not instructions to recreate worktrees. +Preparation progress is recorded in [preparation.md](preparation.md): partial +T001–T005 evidence, an [upstream blocker reproduction](adopter-blocker.md), +and [T007–T010 installer/test design](installer-design.md). No execution task +is complete. Real conversion waits for the user's #97/#121 landing update. + ## Phase 1 — Preservation and baseline (Gate A) - [ ] T001 [US1] Refresh `inventory.json`, PR/main/branch/worktree metadata and @@ -78,7 +83,9 @@ remain pending. Retired checkouts are not instructions to recreate worktrees. ## Phase 4 — Reviewed real adoption (Gates C and D) -- [ ] T012 [US1] Freeze the coordinated source baseline and regenerate/check +- [ ] T012 [US1] After #97 and #121 land, account for their final merged work + in main and disposition any remaining local deltas. Freeze the coordinated + source baseline and regenerate/check the full plan after any prerequisite/planning landings. Obtain explicit approval naming revisions, leg repositories/visibility, mapping, follow-ups and work dispositions. Verify current name availability, preservation and @@ -146,7 +153,10 @@ remain pending. Retired checkouts are not instructions to recreate worktrees. ## Dependencies -T001–T003 precede rehearsal. T004–T006 precede integration validation; +Reviewed preliminary inventory, mapping and prerequisite availability from +T001–T003 permit disposable provisional rehearsal while writers remain active; +complete Gate A preservation is required before real source freeze. +T004–T006 precede integration validation; T007–T010 may progress within the rehearsed topology after byte accounting. T011 closes Gate B. T012 requires Gate A/B and is the real-creation approval boundary. T013–T015 precede root adoption merge. Prepare T016–T017 translations diff --git a/specs/004-migrate-to-triad/verification.md b/specs/004-migrate-to-triad/verification.md index 57c0a9e4..2812273b 100644 --- a/specs/004-migrate-to-triad/verification.md +++ b/specs/004-migrate-to-triad/verification.md @@ -1,5 +1,37 @@ # Migration planning verification — October 4, 2026 +## Preparation authorized while #97/#121 are active + +[preparation.md](preparation.md) records a verified 18-ref bundle restore, +pinned dependency restore, six worktree file/config captures and verified +installed-file/settings/receipt copies in host and bench contexts. All six +snapshots were also restored into independent Git clones with matching index +entries, captured file bytes and worktree status. Active writers remain +unfrozen; installer/live rollback, retired receipts and final preservation +are pending. Gate A is not complete. + +The unchanged pinned standard passed `check` against an isolated source clone, +then local-only execution returned 2 while adding the spec submodule because +the original `.gitmodules` had been staged for deletion. No assembly adoption +commit or final verification was reached. Independent mode/object accounting +passed for 18 spec, 40 code and six unchanged root index entries. Local nested +dependency initialization and the standard's recomputed dependency digest +check passed. Canonical pin tests stayed in serialization +wait and were stopped before pytest ran; no runtime test pass is claimed. +The [upstream reproduction](adopter-blocker.md) is reviewable, and the +[installer compatibility design](installer-design.md) identifies the required +consumers and pending acceptance scenarios. No pinned-standard changes, +external writes, user-tool installs, real repository creation or lane moves occurred. +Gate B remains open. Both PR landings explicitly precede refreshed real +conversion, which still needs Gates A/B and Gate C approval. + +Final pinned mapping check, OpenSpec strict validation and supplemental +document-link/portable-path/inventory/single-task-list checks passed. +All 19 execution tasks remain open; provisional evidence does not replace +their complete acceptance criteria. + +The following refresh sections are historical observations. + ## Updated migration protocol refresh Read the installed shared workflow's **Triad Feature Amendments**, including From ff6f6a7c7b7921ca1e0b0234e898900f98532366 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Tue, 6 Oct 2026 15:26:03 +0000 Subject: [PATCH 6/9] docs: record successful triad adopter candidate rehearsal --- specs/004-migrate-to-triad/adopter-blocker.md | 16 ++- .../004-migrate-to-triad/adopter-rehearsal.md | 103 ++++++++++++++++++ specs/004-migrate-to-triad/preparation.md | 16 ++- specs/004-migrate-to-triad/tasks.md | 5 +- 4 files changed, 129 insertions(+), 11 deletions(-) create mode 100644 specs/004-migrate-to-triad/adopter-rehearsal.md diff --git a/specs/004-migrate-to-triad/adopter-blocker.md b/specs/004-migrate-to-triad/adopter-blocker.md index a0b8980e..d10580b3 100644 --- a/specs/004-migrate-to-triad/adopter-blocker.md +++ b/specs/004-migrate-to-triad/adopter-blocker.md @@ -6,6 +6,12 @@ `daed20957f2dd2f22cca24053bb5bc8636ff6b3f`. **Measured:** October 4, 2026; disposable local remotes only. +**October 6 update:** a fix is published in draft +[openRepoShape PR #162](https://github.com/opensoft/openRepoShape/pull/162). +The [candidate rehearsal](adopter-rehearsal.md) passes against current consumer +main. The unchanged pinned revision still has the refusal documented below; +upstream landing and a tested consumer pin update remain pending. + The reviewed mapping moves the existing `.gitmodules` together with `upstream/openRepoShape` and its dependency contract into code. Plan `check` passes. Both filtered legs are created, then assembly mounting returns 2: @@ -58,11 +64,13 @@ recursive clone/bootstrap, exact mode/object accounting, and failure recovery. Also cover partial registration ownership and no-existing-module inputs so a fix cannot silently erase retained registrations or create duplicate owners. -No upstream issue or PR was published. No patch was applied to the pinned -checkout or failing candidate. Resolve through the owning repository's normal +At the October 4 measurement no upstream issue or PR had been published and +no patch was applied to the pinned checkout or failing candidate. The October 6 +candidate is maintained in its own upstream worktree. Resolve through normal review, then bump to a commit on its main and recompute the dependency digest. -Repeat the consumer rehearsal with fresh remotes. Gate B remains blocked by -this refusal even though extraction content was independently verified. +Repeat the consumer rehearsal with fresh remotes after repinning. Gate B still +requires the reviewed upstream fix and the remaining integration work, even +though the separate candidate rehearsal now succeeds. Private execution log SHA256: `ca2a11775ca3b6d179fc038436a28090f65b05feb2a1611d4578b315733b5168`. diff --git a/specs/004-migrate-to-triad/adopter-rehearsal.md b/specs/004-migrate-to-triad/adopter-rehearsal.md new file mode 100644 index 00000000..fd8a3ac2 --- /dev/null +++ b/specs/004-migrate-to-triad/adopter-rehearsal.md @@ -0,0 +1,103 @@ +# Adopter fix and disposable rehearsal — October 6, 2026 + +**Task:** T005, with additional T003/T004 rehearsal evidence. +**Upstream candidate:** [openRepoShape PR #162](https://github.com/opensoft/openRepoShape/pull/162), +draft, commit `91d568563f98ddbe159167d82652d8e85214bb93`, based on +`39d5c986fcfac1a160474bfe91c5f1c37fccc72c`. +**Consumer rehearsal source:** `f5444c5e1f3c6c9ed43aeafcc000b0e78b10497a`. + +The [October 4 refusal](adopter-blocker.md) is reproduced by a new regression +on the unchanged upstream revision. The candidate creates and stages a fresh +assembly `.gitmodules` after extraction only when that file is absent. It +preserves registrations retained in assembly. Original registrations remain +with their dependency gitlinks in the extracted leg. Verification is unchanged. + +## Regression evidence + +Ran inside py-bench with the existing private git-filter-repo 2.47.0 venv: + +```sh +python3 -m pytest tests/test_adopt_*.py -q +``` + +Result: **90 passed, 1 skipped**. A final run of +`tests/test_adopt_submodules.py` after adding explicit gitlink-mode and +bootstrap assertions passed all **4** cases. Those cases cover: + +- A populated dependency moved with its original `.gitmodules` into either + code or spec: registration blob, gitlink mode/identity, dependency bytes and + registration history survive; recursive clone, validators and bootstrap pass. +- A retained assembly registration survives mounting alongside a moved + dependency. This exercises the mount boundary; it does not qualify every + mixed-registration plan for complete adoption. +- A mount failure after the first new leg leaves the original source refs, + registration bytes and worktree intact; mounting from a fresh clone succeeds. + +Existing adopter coverage includes sources without submodules. No full +consumer suite or upstream CI pass is claimed by these local runs. + +## Current-main local rehearsal + +Private receipt ID: `adopter-fix-20261006-c2G9Dp`. Clone paths, local remote +URLs, commands and complete logs stay host-local outside the repository. + +Generated a new plan from a clean, single-branch isolated source clone. +Reapplied the previous reasoned ownership overrides; explicitly assigned the +new `lanes-index` executable to code. The new `ideation/` content is spec. +The only existing dependency contract remains with code's registration/gitlink. +All ownership questions were resolved before `check` and execution. +This private plan is preliminary rehearsal input, not the final cutover plan. + +`check` and `execute --local-remote-dir … --yes` both passed with the candidate. +Git transport was restricted to local files. The two leg remotes, assembly and +adoption branch exist only in the disposable rehearsal; no real leg repository +was created. The source remained clean on its original main commit. + +| Source accounting | Original entries | +| --- | ---: | +| Spec | 45 | +| Code | 43 | +| Assembly root | 6 | +| Dropped | 0 | +| Total | 94 | + +The standard reported every source path in exactly one place and 18 added +assembly paths. An independent comparison also checked **path, mode, object +type and object ID** for every original entry: zero missing or duplicate +owners. This includes the existing dependency gitlink. + +| Disposable repository | Commit | Kept commits | +| --- | --- | ---: | +| Spec | `2374d2ec8f8f448c3d086b4546920f875c392ca6` | 29 | +| Code | `af25d6d9094285aa2f1d540c839518e6610adf89` | 81 | +| Assembly split | `52f0c18f239b3fbda1c0656e2aefd9c387164d26` | — | + +Recursive clone and the generated `scripts/bootstrap.py` passed, including +naming, manifest and exact leg/copy pin validation. Assembly and legs were +clean afterwards. Code's nested openRepoShape remained at +`39d5c986fcfac1a160474bfe91c5f1c37fccc72c`; the recomputed tree digest is +`a44c0165beb9d3b6ddfce46925bf04b8d19934d5327b5a304b5f3f11af699b02`, +matching the original dependency contract. Live consumer main, its gitlink and +that contract were unchanged. + +Private log SHA256s: + +- Execute: `0e25dbec207e34694321c604201fb0437d70cbb0e4aba8314d34fbeae11636b3`. +- Bootstrap: `d83e3ca6b76e13bb6f4ec7d99ee64ea159634079d87015c3cc10b5699f042b9a`. + +## Remaining delivery gates + +T005 remains open: review and land the upstream fix, then use a commit on +upstream main for the tested consumer pin/gitlink update and recomputed digest. +The existing consumer pin has not been changed to the candidate branch. + +PRs #97 and #121 have landed. On October 6 the remaining open consumer PRs +were #168, #169, #174 and draft #172. Refresh the final source mapping, +inventory and coordinated preservation after their landings and any other +baseline changes. The October 4 committed adoption plan and preservation +snapshot remain historical inputs. + +Installer adaptations, composed code/spec tests and CI, rollback receipts, +owner dispositions and explicit execution approval remain required. Gates +A/B/C are not completed by this rehearsal. This evidence authorizes no real +conversion, merge, account move or lane rebinding. diff --git a/specs/004-migrate-to-triad/preparation.md b/specs/004-migrate-to-triad/preparation.md index 11259027..89f343a6 100644 --- a/specs/004-migrate-to-triad/preparation.md +++ b/specs/004-migrate-to-triad/preparation.md @@ -1,8 +1,11 @@ -# Preparation while PRs #97 and #121 are active - -**Observed:** October 4, 2026. **State:** provisional preservation and local -rehearsal performed; Gates A/B remain open. Real conversion waits for both PRs -to land and the refreshed execution approval. This evidence belongs to +# Migration preparation evidence + +**Initial observation:** October 4, 2026. The preservation and failed rehearsal +below are historical receipts. **October 6 update:** #97 and #121 have landed; +a candidate upstream fix and successful disposable current-main rehearsal are +recorded in [adopter-rehearsal.md](adopter-rehearsal.md). Gates A/B remain open. +Real conversion waits for the remaining baseline landings, a tested upstream +pin and refreshed execution approval. This evidence belongs to [T001–T011](tasks.md); it is not another executable task list. ## Preservation evidence @@ -100,7 +103,8 @@ See the [upstream reproduction](adopter-blocker.md) and ## Resume after the PR landings -The user's landing notification triggers a fresh read of #97/#121, actual main +PRs #97/#121 are now merged; #168/#169/#174 and draft #172 remained open on +October 6. Their landing notifications trigger a fresh read of actual main and owner state. Landed changes are accounted for through the new baseline; remaining branch deltas and local edits still need dispositions. Existing PR holds and review history are preserved until their owners settle them. diff --git a/specs/004-migrate-to-triad/tasks.md b/specs/004-migrate-to-triad/tasks.md index 6a30ed2c..2e6ead21 100644 --- a/specs/004-migrate-to-triad/tasks.md +++ b/specs/004-migrate-to-triad/tasks.md @@ -11,7 +11,10 @@ remain pending. Retired checkouts are not instructions to recreate worktrees. Preparation progress is recorded in [preparation.md](preparation.md): partial T001–T005 evidence, an [upstream blocker reproduction](adopter-blocker.md), and [T007–T010 installer/test design](installer-design.md). No execution task -is complete. Real conversion waits for the user's #97/#121 landing update. +is complete. The [October 6 candidate rehearsal](adopter-rehearsal.md) passes; +T005 still needs upstream landing and a tested consumer pin update. PRs #97 +and #121 have landed. Real conversion waits for the remaining baseline PRs, +integration/preservation evidence and refreshed execution approval. ## Phase 1 — Preservation and baseline (Gate A) From 5aa40b7068fe84256ccc62e86ccc907ce918b096 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Thu, 8 Oct 2026 20:57:38 +0000 Subject: [PATCH 7/9] Record run C of the Gate B rehearsal at the post-landing main 837928a and regenerate the adoption plan from it. Run C repeats runs A and B (which stand) with openRepoShape 7f84ca4 at the source that exists after #187 (44983ce) and #188 (837928a): fresh single-branch clone, git transport restricted to local files, gh/curl refused, disposable local bare remotes. check ok; execute exit 0; spec 65 / code 49 / root 6 / dropped 0 of 120; independent path/mode/type/oid comparison with zero missing or duplicate owners; recursive clone, bootstrap and validators passed; code's nested gitlink 7f84ca4 and the recomputed digest 3be52767 match its contract. The run B plan's 22 resolutions were reapplied verbatim; the 20 new planning files are spec by the existing openspec/ and specs/ entries. adoption-plan.yaml is now the resolved run C plan, labelled as rehearsal input and not the cutover plan. The arrangement is kept host-local as the read-only input for T006-T010. Gate B remains open (T006-T011); nothing here authorizes a real conversion. Private receipt adopter-fix-20261008-1HztL0. Part of #186. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../migrate-to-triad/adoption-plan.yaml | 152 +++++++++++++----- .../adopter-rehearsal-2026-10-08.md | 135 ++++++++++++++++ 2 files changed, 246 insertions(+), 41 deletions(-) create mode 100644 specs/004-migrate-to-triad/adopter-rehearsal-2026-10-08.md diff --git a/openspec/changes/migrate-to-triad/adoption-plan.yaml b/openspec/changes/migrate-to-triad/adoption-plan.yaml index f9d26fbe..8b360179 100644 --- a/openspec/changes/migrate-to-triad/adoption-plan.yaml +++ b/openspec/changes/migrate-to-triad/adoption-plan.yaml @@ -1,20 +1,38 @@ -# Preliminary adoption mapping; planning only. Execution needs a refreshed baseline and explicit approval. -# Real conversion waits for PRs #97/#121 to land and the pinned-adopter mounting refusal to be resolved upstream. -# Disposable rehearsal evidence: ../../../specs/004-migrate-to-triad/preparation.md; Gates A/B remain open. -# Paths are relative to the source checkout. Generated metadata does not authorize repository creation. +# Rehearsal input, NOT the cutover plan: regenerated 2026-10-08 from source 837928a1029ac53b050a9bdcbc0ef4055b10719b (main) +# with openRepoShape adopt-project.py at 7f84ca42ca86a8902928345109d2bf6bad87bd91. +# Run C: the run B plan's 22 resolutions (the October 4 mapping's 19, plus lane-worktrees and +# lanes-index to code and ideation/ to spec) are reapplied verbatim; no new top-level path. +# Disposable rehearsal evidence: ../../../specs/004-migrate-to-triad/adopter-rehearsal-2026-10-08.md. +# Gate B remains open (T006-T011); execution needs a frozen baseline and explicit Gate C approval. +# Paths are relative to the source checkout (local_path published as "."); generated metadata +# does not authorize repository creation. schema_version: 1 kind: adoption-plan -generated_on: "2026-10-04" -tool: "opensoft/openRepoShape adopt-project.py" + +# Written by `adopt-project.py plan`. IT IS MEANT TO BE EDITED. +# Every entry with `review_required: true` carries a `question:`; a +# human or an AI answers it by setting `leg:` to spec, code, root or +# drop and adding one line of `resolution:` beside it. `check` then +# has to pass, and `execute` REFUSES while any leg is still null. + +generated_on: 2026-10-08 +tool: opensoft/openRepoShape adopt-project.py + source: repository: opensoft/openRepoTools local_path: . default_branch: main - commit: daed20957f2dd2f22cca24053bb5bc8636ff6b3f - files: 64 - bytes: 4490556 - commits: 80 + commit: "837928a1029ac53b050a9bdcbc0ef4055b10719b" + files: 120 + bytes: 6292407 + commits: 92 + +# The adopted repository KEEPS its name, its identity and +# its history and becomes the assembly root. `new-root` — a new +# root with the source reduced to one leg — is not implemented +# in v0.2; the field records which posture this plan took. mode: in-place + project: openRepoTools org: opensoft id: openrepotools @@ -24,19 +42,39 @@ adopt_branch: adopt/three-repo-shape elected_by: "Brett Heap" elected_on: "2026-10-04" reference: "openxFactory docs/project-repo-schema.md" + legs: assembly: openRepoTools spec: openRepoTools-spec code: openRepoTools-code spec_path: spec code_path: code + +# Neutral products this project declares a pin on. A +# `` assembly root is a DESCENDANT only when +# its `open` is listed here (2026-09-02). pins: [] + +# A leg that NO entry below assigns a path to is SEEDED from the +# shape's own leg template as one initial commit, instead of being +# extracted: `git filter-repo` over an empty path list yields an +# empty HISTORY, which is not an empty repository. The split still +# mounts it. This block RECORDS what the entries said when the plan +# was written; `check` and `execute` re-derive it from the entries, +# which are edited afterwards, and the entries always win. seeding: spec: seeded_from_template: false code: seeded_from_template: false + +# Seeding a leg is EXPLICIT HUMAN INTENT, so `execute` refuses to do +# it unless the leg is named here (or on its own command line) with +# `--allow-empty-leg `. A repository that turns out to have no +# code is a fact worth confirming; a plan that lost every code path +# to a bad edit looks exactly the same from here. allow_empty_legs: [] + paths: - path: .gitattributes leg: root @@ -52,7 +90,7 @@ paths: rule: code-ci-workflows reason: "CI runs the implementation's own gate — build, test, analysis. The assembly root gets its own `validate.yml` from the shape, so a workflow moved here is not a workflow lost." files: 1 - bytes: 18501 + bytes: 18865 review_required: false - path: .gitignore leg: root @@ -78,7 +116,7 @@ paths: rule: root-assistant-instructions reason: "The AI-assistant instruction files and their one-file directories are the same document ten times over, and it addresses the whole project. An assistant that read it from inside the code leg would be told about specifications it cannot see. Root, ruled 2026-09-02." files: 1 - bytes: 20967 + bytes: 21092 review_required: false - path: CLAUDE.md leg: root @@ -102,7 +140,7 @@ paths: rule: root-front-door reason: "What a person or a forge reads BEFORE deciding which leg to open. The licence and the code of conduct govern all three repositories; the README is the front door of the thing an engineer clones." files: 1 - bytes: 30329 + bytes: 31198 review_required: false - path: claude-current leg: code @@ -130,7 +168,7 @@ paths: rule: extension-majority reason: "no rule names this path; the `.md` extension is classified as spec by the policy's extension table" files: 3 - bytes: 5146 + bytes: 8906 review_required: false resolution: "These Markdown command files are shipped runtime installer payloads with implementation tests, not OpenSpec or Speckit decisions." - path: contracts/ @@ -148,8 +186,17 @@ paths: rule: spec-governance reason: "Requirements, decisions, contracts and governance prose — what the project has DECIDED, as opposed to what it currently runs. `contracts/` is here by the 2026-09-02 ruling even where the code reads it: the code reads across the assembly-root mount (`spec/contracts` from the root, `../spec/contracts` from `code/`), which is what the legs are mounted for." files: 2 - bytes: 201648 + bytes: 287738 review_required: false + - path: ideation/ + leg: spec + confidence: medium + rule: extension-majority + reason: "no rule names this path; the `.md` extension is classified as spec by the policy's extension table" + files: 12 + bytes: 49688 + review_required: false + resolution: "Feature ideation and brainstorm packets are planning prose about what the project is deciding, not shipped payload; spec beside openspec/, specs/ and docs/, as the October 6 rehearsal assigned it." - path: lane leg: code confidence: review @@ -166,7 +213,7 @@ paths: rule: default reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." files: 1 - bytes: 119587 + bytes: 141860 review_required: false question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." @@ -176,7 +223,7 @@ paths: rule: default reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." files: 1 - bytes: 72601 + bytes: 153546 review_required: false question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." @@ -196,7 +243,17 @@ paths: rule: default reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." files: 1 - bytes: 233030 + bytes: 270707 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." + - path: lane-worktrees + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 277594 review_required: false question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." @@ -206,7 +263,7 @@ paths: rule: default reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." files: 1 - bytes: 36348 + bytes: 45015 review_required: false question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." @@ -216,8 +273,18 @@ paths: rule: extension-majority reason: "no rule names this path; the `.sh` extension is classified as code by the policy's extension table" files: 1 - bytes: 807509 + bytes: 1007136 review_required: false + - path: lanes-index + leg: code + confidence: review + rule: default + reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." + files: 1 + bytes: 114193 + review_required: false + question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." + resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." - path: link-estates leg: code confidence: review @@ -234,7 +301,7 @@ paths: rule: default reason: "No rule matched and the file extensions did not agree. This is the honest answer, not a failure: the tool has said what it does not know, at the exact path where it does not know it." files: 1 - bytes: 180034 + bytes: 193656 review_required: false question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." resolution: "Executable product command; keep with its sibling helpers and installation payload in the code repository." @@ -243,8 +310,8 @@ paths: confidence: high rule: spec-governance reason: "Requirements, decisions, contracts and governance prose — what the project has DECIDED, as opposed to what it currently runs. `contracts/` is here by the 2026-09-02 ruling even where the code reads it: the code reads across the assembly-root mount (`spec/contracts` from the root, `../spec/contracts` from `code/`), which is what the legs are mounted for." - files: 10 - bytes: 16759 + files: 28 + bytes: 185941 review_required: false - path: park leg: code @@ -282,7 +349,7 @@ paths: rule: extension-majority reason: "no rule names this path; the `.md` extension is classified as spec by the policy's extension table" files: 3 - bytes: 77708 + bytes: 80497 review_required: false resolution: "These three skills are installed product payloads; project workflow skills remain local root bootstrap artifacts." - path: specs/ @@ -290,8 +357,8 @@ paths: confidence: high rule: spec-governance reason: "Requirements, decisions, contracts and governance prose — what the project has DECIDED, as opposed to what it currently runs. `contracts/` is here by the 2026-09-02 ruling even where the code reads it: the code reads across the assembly-root mount (`spec/contracts` from the root, `../spec/contracts` from `code/`), which is what the legs are mounted for." - files: 6 - bytes: 4141 + files: 23 + bytes: 123603 review_required: false - path: status leg: code @@ -308,8 +375,8 @@ paths: confidence: high rule: code-source-and-tests reason: "The implementation, the tests that hold it, and the scripts and images that build and run it. `scripts/` moves whole: a harness that reads `contracts/` from beside it reads them across the root mount afterwards, which is a code change the plan records as a follow-up rather than a reason to split the directory." - files: 15 - bytes: 2022225 + files: 22 + bytes: 2637149 review_required: false - path: upstream/ leg: code @@ -321,6 +388,7 @@ paths: review_required: false question: "Which leg owns this path — spec, code, or the assembly root? Answer by editing `leg:` and writing one line into `resolution:` beside it." resolution: "Mounted openRepoShape is the implementation/test dependency; preserve its exact gitlink with the original .gitmodules and dependency pin." + follow_ups: - "code/claude-current names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" - "code/lane-start names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" @@ -331,6 +399,7 @@ follow_ups: - "code/tests/test_lane_helpers.sh names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" - "code/tests/test_lane_start_claude_current.py names `docs/`, which moves to the spec leg: it must read across the assembly root — `../spec/docs/` relative to `code/` — or take the path from the environment" - "… and 1 more file(s) under the code leg naming `docs/`" + - "code/lane-handoff names `openspec/`, which moves to the spec leg: it must read across the assembly root — `../spec/openspec/` relative to `code/` — or take the path from the environment" - "code/lanes-edit.sh names `openspec/`, which moves to the spec leg: it must read across the assembly root — `../spec/openspec/` relative to `code/` — or take the path from the environment" - "code/tests/test_lane_helpers.sh names `openspec/`, which moves to the spec leg: it must read across the assembly root — `../spec/openspec/` relative to `code/` — or take the path from the environment" - "code/tests/test_park_resume_commands.py names `specs/`, which moves to the spec leg: it must read across the assembly root — `../spec/specs/` relative to `code/` — or take the path from the environment" @@ -343,33 +412,34 @@ follow_ups: - "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 64-file source tree; capture local configuration and regenerate root workflow using setup-openspeckit after manifest/legs are present." + - "Current .specify and agent links are ignored local bootstrap artifacts, not the 120-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." - "Regenerate this preliminary plan after planning lands and baseline freeze; require current coverage check, local bare-remote rehearsal, prerequisite availability and explicit approval before execute." - "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." + summary: spec: - files: 18 - bytes: 222548 - file_share: 0.2812 - byte_share: 0.0496 + files: 65 + bytes: 646970 + file_share: 0.5417 + byte_share: 0.1028 code: - files: 40 - bytes: 4203444 - file_share: 0.6250 - byte_share: 0.9361 + files: 49 + bytes: 5579879 + file_share: 0.4083 + byte_share: 0.8868 root: files: 6 - bytes: 64564 - file_share: 0.0938 - byte_share: 0.0144 + bytes: 65558 + file_share: 0.0500 + byte_share: 0.0104 unresolved: files: 0 bytes: 0 file_share: 0.0000 byte_share: 0.0000 -planning_status: proposed-preliminary-baseline +planning_status: rehearsal-input-not-cutover execution_authorized: false diff --git a/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-08.md b/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-08.md new file mode 100644 index 00000000..26f0a537 --- /dev/null +++ b/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-08.md @@ -0,0 +1,135 @@ +# Gate B rehearsal, run C, at the post-landing source — October 8, 2026 + +**Task:** T003–T005 rehearsal evidence under [#186](https://github.com/opensoft/openRepoTools/issues/186). +No task is checked complete by it. +**Tool:** openRepoShape `7f84ca42ca86a8902928345109d2bf6bad87bd91` on its `main` +(`compare main...7f84ca4`: `identical` at measurement). +**Source:** main `837928a1029ac53b050a9bdcbc0ef4055b10719b`, read with `git ls-remote` +at 2026-10-08T20:54:21Z just before cloning. It is `c4864ac` plus #187, the +planning documents squashed as `44983ce`, and #188, the repin to `7f84ca4` +squashed as `837928a`. +**Private receipt ID:** `adopter-fix-20261008-1HztL0`. + +**Run C supersedes nothing.** Runs A (`c4864ac`) and B (the repin candidate +`e43b243`) stand as recorded in +[adopter-rehearsal-2026-10-07.md](adopter-rehearsal-2026-10-07.md). That file +is on branch `004-migrate-to-triad-rehearsal` and reaches main with the +combined evidence PR. Run C is the same rehearsal, with the same tool and the +same resolutions, at the source that now exists after both landings. It is +also the durable rehearsed arrangement that T006–T010 take as input. It is +kept host-local outside every repository so it survives a bench restart. +Clone paths, local remote URLs, exact command lines and complete logs stay in +the private receipt. + +## Setup + +- **filter-repo:** `git-filter-repo` 2.47.0 from a private venv. The rebuilt + bench has no `ensurepip`, and `python3 -m venv` refused with + `The virtual environment was not created successfully because ensurepip is + not available.` So the venv was created with `--without-pip`. The wheel + came from `pip download` with SHA256 + `2cd04929b9024e83e65db571cbe36aec65ead0cb5f9ec5abe42158654af5ad83`, matching + the recorded value. That exact file was installed into the venv with + `--no-index`. The venv's `git-filter-repo` came first on `PATH`, and + `git filter-repo --version` printed `a40bce548d2c`. +- **Tool and source:** a fresh clone of openRepoShape at `7f84ca4`, unedited + and clean afterwards, ignored files included. A fresh `--single-branch` + clone of main, with one worktree and no linked worktrees. Its nested + `upstream/openRepoShape` was not initialized. +- **Transport:** limited to local files (`GIT_ALLOW_PROTOCOL=file`). `gh` and + `curl` shims refused and logged; no call was logged. The recursive clone + reached code's nested openRepoShape through a local mirror of the tool clone. + +## Mapping (T003) + +Since the run B source, the tree gained 20 files and no new top-level path: + +- 13 under `specs/004-migrate-to-triad/`; +- 6 under `openspec/changes/migrate-to-triad/`, including the October 4 + `adoption-plan.yaml` itself; +- `openspec/config.yaml`. + +The pin change is the one run B already carried; its contract blob is +identical. `plan` left the same 18 questions. All 22 resolutions of the run B +plan were reapplied verbatim: the October 4 mapping's 19, plus +`lane-worktrees` and `lanes-index` to code and `ideation/` to spec. Every +other entry kept the tool's leg, unchanged from run B. The 20 new files are +spec through the existing `openspec/` and `specs/` entries. + +The tool's follow-ups, re-derived over the resolved entries, are identical to +run B's. The ten reasoned follow-ups are unchanged except the source file +count, now 120. `check` printed `plan ok` for the executed plan and again +for the published copy. On this branch the +[mapping](../../openspec/changes/migrate-to-triad/adoption-plan.yaml) is that +published copy. It differs from the executed plan only by its header, +`local_path: .`, and the two closing status fields +(`execution_authorized: false`). + +## Run C — main `837928a` + +`execute --local-remote-dir … --work-dir … --yes` exited **0** and printed +`adoption verified: every source path is in exactly one place`. + +| Source accounting (the standard's table) | Original entries | +| --- | ---: | +| Spec | 65 | +| Code | 49 | +| Assembly root | 6 | +| Dropped | 0 | +| Total | 120 | + +The standard reported 18 added assembly paths, the same set as runs A and B. +An independent comparison matched path, mode, object type and object ID for +all 120 entries of `ls-tree -r 837928a`: 65 spec, 49 code and 6 root. It +found zero missing, duplicate or misplaced owners and no leg extras. The +gitlink `upstream/openRepoShape` at `7f84ca4` has exactly one owner, code. + +| Disposable repository | Commit | Kept commits | +| --- | --- | ---: | +| Spec | `593250d600ca291f610671d993c2a6fc36cd690e` | 35 | +| Code | `73b04d637634f897ad24e22ee4c12bed287172d6` | 88 | +| Assembly split | `69b89245029d0339afd3317df7c9b1b3bbaf7fc1` | — | + +Spec gained one kept commit, the planning squash. Code's tree digest +`b4940a13bd8ecbe2d1c6ed49368d1545d1a5f1f7a92623c7fe11ac1cb3617604` has the +same prefix run B recorded. Only the code commit identities differ, because +the repin landed as a different commit. The split commit's only parent is +`837928a`. + +The recursive clone, `scripts/bootstrap.py` (`bootstrap ok`) and the +naming, manifest and pins validators all passed, and every tree was clean +afterwards. Code's nested openRepoShape was checked out at `7f84ca4`, matching +code's `contracts/openreposhape-pin.yaml`. The pinned checkout's own +`scripts/repo_shape.py` recomputed +`3be52767e727d924dd05f9e78b18eb70f1682fe48614fab519f85331eeff5fa8`, which +matches the contract. + +The source clone was unchanged except for the one documented local-mode ref, +`refs/heads/adopt/three-repo-shape` at the split commit. That ref and the +two leg remotes are left in place, read-only, as the input to T006–T010. + +## Private log SHA256s + +- Execute: `f766bc707c88272598b6947ae850d9d42922763f0820940a0202ebcbc1835a46`. +- Bootstrap: `ea2be5dc7455c869a48c7bd36d73024c53ef6f233916cbf8e67ba897b3b732f7`. +- Independent comparison: `d3acbcd6274267eeadd36892bdbe2a08a9b08269d3215b373f84d630b91da007`. + +The receipt holds every log with its SHA256. None was over 2 MB, so none was +trimmed. + +## Remaining gates + +**Gate B is not complete.** T006–T011 remain: + +- root collisions and leg guidance; +- the installer entry point; +- composed tests and CI; +- the restore and rollback rehearsal. + +Gate A preservation must be refreshed at a coordinated breakpoint. Before +Gate C, regenerate the plan from the frozen source that is actually chosen. +Brett Heap's explicit Gate C approval must name the revisions, the leg +repositories, their visibility and the final plan. + +This evidence authorizes no real conversion, repository creation, merge, +default-branch move or lane rebinding. From f06e1af982efcf8eede6a3b9bc665f28e688249a Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:51:07 +0000 Subject: [PATCH 8/9] T006's five rehearsal patches are recorded: the assembly front door with two lockstep pin bumps, and the code and spec legs' own front doors, all made against run C's rehearsed split. Each patch is `git format-patch` output from lane openRepoTools-1's scratch clones of run C (assembly split 69b8924, spec leg 593250d, code leg 73b04d6) under opensoft/openRepoTools#186. Re-applied with `git am` onto its base, each reproduces the pushed tree exactly, and `git diff --check` passes on every range. assembly-0002 and -0003 are bump-leg.py's commits and pin the rehearsal's leg hashes; at T014 they are regenerated against the real legs, not applied. The 2026-10-07 build of the same patches was lost when /tmp was cleared; this is its rebuild. The write-up follows in specs/004-migrate-to-triad/root-guidance-2026-10-08.md. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- ...t-s-front-door-resolves-the-split-s-.patch | 415 +++++++++++++++++ ...leg-to-39c18877e042-in-openRepoTools.patch | 55 +++ ...leg-to-6169e8e96164-in-openRepoTools.patch | 55 +++ ...s-its-own-front-door-the-shape-s-leg.patch | 417 ++++++++++++++++++ ...s-its-own-front-door-the-shape-s-leg.patch | 398 +++++++++++++++++ 5 files changed, 1340 insertions(+) create mode 100644 specs/004-migrate-to-triad/patches/t006/assembly-0001-The-assembly-root-s-front-door-resolves-the-split-s-.patch create mode 100644 specs/004-migrate-to-triad/patches/t006/assembly-0002-Bump-spec-leg-to-39c18877e042-in-openRepoTools.patch create mode 100644 specs/004-migrate-to-triad/patches/t006/assembly-0003-Bump-code-leg-to-6169e8e96164-in-openRepoTools.patch create mode 100644 specs/004-migrate-to-triad/patches/t006/code-0001-The-code-leg-gets-its-own-front-door-the-shape-s-leg.patch create mode 100644 specs/004-migrate-to-triad/patches/t006/spec-0001-The-spec-leg-gets-its-own-front-door-the-shape-s-leg.patch diff --git a/specs/004-migrate-to-triad/patches/t006/assembly-0001-The-assembly-root-s-front-door-resolves-the-split-s-.patch b/specs/004-migrate-to-triad/patches/t006/assembly-0001-The-assembly-root-s-front-door-resolves-the-split-s-.patch new file mode 100644 index 00000000..ec6d9c7b --- /dev/null +++ b/specs/004-migrate-to-triad/patches/t006/assembly-0001-The-assembly-root-s-front-door-resolves-the-split-s-.patch @@ -0,0 +1,415 @@ +From c0a8e1ea7164994fb8ba9fb76b93590ae7bff218 Mon Sep 17 00:00:00 2001 +From: Brett Heap <1513478+brettheap@users.noreply.github.com> +Date: Thu, 8 Oct 2026 21:42:28 +0000 +Subject: [PATCH 1/3] The assembly root's front door resolves the split's + collisions: AGENTS.md opens with the shape's pointer and a 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 opensoft/openRepoTools#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 openRepoShape 7f84ca4's + SHAPE_POINTER_LINE and its adopt follow-up require. The shape's rendered + block from shape/AGENTS.md follows (the three repositories). Then comes a + map of seven entries plus one rehearsal note: spec, code, this root, where + the tests run, where the lane tooling's code and manual are, paired feature + worktrees, and how a path reads. The note: a local rehearsal mounts legs + over the file transport, which git 2.43 refuses without + `-c protocol.file.allow=always`; the real https legs need nothing. The + original AGENTS.md follows unchanged except for 16 path repairs + (docs/ -> spec/docs/, openspec/ -> spec/openspec/, upstream/, contracts/, + tests/ and .github/ -> code/...). +- README.md: a "Three repositories" section (the table, the recursive clone + and `make bootstrap`), 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`. +- .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. + +Lane: openRepoTools-1 +Co-Authored-By: Claude Opus 5.5 +--- + .gitattributes | 43 +++++++++++----------------- + .gitignore | 6 ++++ + AGENTS.md | 78 +++++++++++++++++++++++++++++++++++++++----------- + README.md | 59 +++++++++++++++++++++++++++++++------- + 4 files changed, 133 insertions(+), 53 deletions(-) + +diff --git a/.gitattributes b/.gitattributes +index 3560973..6dc2212 100644 +--- a/.gitattributes ++++ b/.gitattributes +@@ -1,32 +1,21 @@ +-# THIS REPOSITORY'S OWN INSTALLER COMPARES ITS BYTES, so the bytes have to be +-# the same bytes on every platform. ++# THE BYTES A CLONE SEES ARE THE BYTES THIS ROOT'S CHECKS DESCRIBE. + # +-# `openRepoTools --install` decides between `installed at`, `updated at` and +-# `already installed … (unchanged)` with `cmp -s`, and +-# `tests/test_openrepotools_command.py` compares an installed copy to this +-# checkout's file BYTE FOR BYTE. Git for Windows' installer default is +-# `core.autocrlf=true`; a checkout of THIS repository made under it turns every +-# `\n` in `openRepoTools`, `park`, `resume` and `status` into `\r\n`, and every one +-# of those comparisons is about line endings rather than about content — a +-# second `--install` reporting three files `updated at` on a machine where +-# nothing changed, and a colleague whose only sin was cloning on a stock +-# Windows laptop watching the suite fail on lines nobody touched. CI never sees +-# it: the Linux and macOS jobs check out LF by default and the Windows job pins +-# `core.autocrlf=false`. ++# `contracts/shape-pin.yaml` records a sha256 of every shape file copied here, ++# and `scripts/validate-pins.py` recomputes each one from the bytes on disk. ++# Git for Windows' installer default is `core.autocrlf=true`; a clone made under ++# it writes CRLF into the worktree, and `make validate` then goes red on every ++# pinned file for a colleague who did nothing but clone this repository ++# (openRepoShape #51, ruled by Brett Heap on 2026-09-05; the shape's own ++# statement of the rule is `shape/.gitattributes`, pinned and left in place). + # +-# The same rule for a second reason. `tests/test_park_resume_commands.py` +-# asserts that `park`, `resume` and `status` carry the shared estate resolver +-# BYTE FOR BYTE — that is what makes the deliberate duplication safe — and a CRLF +-# checkout would either break the claim or hide a real drift behind it. ++# Before the split into three repositories this file carried the same rule for ++# a second reason: `openRepoTools --install` and its tests compare installed ++# copies with the checkout BYTE FOR BYTE. Those files are the code leg's now, ++# and a superproject's attributes never reach into a submodule's worktree, so ++# that statement moved with them, word for word, to `code/.gitattributes`; the ++# documents in `spec/` carry their own copy of the rule too. + # +-# NO PATH IS LISTED BY NAME. Every file this repository ships is text — bash, +-# Python, YAML, Markdown, a workflow — so there is nothing for git's binary +-# heuristic to misjudge, and `text=auto` leaves that detection where it +-# belongs: with git. The pinned `upstream/openRepoShape` is a gitlink and +-# carries its own copy of this rule. +-# +-# openRepoShape #63 wrote this rule for the standard's own root, and its #51 +-# (2026-09-05) wrote it for every scaffolded project; this is the same rule +-# retold for the repository that ships the two commands. ++# NO PATH IS LISTED BY NAME. Every file here is text, so `text=auto` leaves ++# binary detection with git. + + * text=auto eol=lf +diff --git a/.gitignore b/.gitignore +index a160f49..2d04abf 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -1,3 +1,9 @@ + __pycache__/ + *.py[cod] + .pytest_cache/ ++# Merged from the shape's copy, `shape/.gitignore` (pinned, left in place). ++.venv/ ++.DS_Store ++# Paired feature worktrees, `worktrees//{spec,code}/`: linked ++# worktrees of the two legs, machine plumbing, never committed to this root. ++/worktrees/ +diff --git a/AGENTS.md b/AGENTS.md +index 443880a..d2dd7fb 100644 +--- a/AGENTS.md ++++ b/AGENTS.md +@@ -1,3 +1,49 @@ ++Read AGENTS-shape.md first — the rules of this repository's shape. ++ ++This is the assembly root of **openRepoTools** (`openrepotools`): the repository an ++engineer clones. It holds `project.yaml`, the pins and the gate, and it mounts ++the two legs. ++ ++| role | repository | path | ++|---|---|---| ++| assembly | `opensoft/openRepoTools` | `.` | ++| spec | `opensoft/openRepoTools-spec` | `spec/` | ++| code | `opensoft/openRepoTools-code` | `code/` | ++ ++Everything below this line is openRepoTools's own. The shape wrote the block above ++once and does not pin this file, so how this project is built, tested, reviewed ++and released belongs here and nothing upstream will overwrite it. ++ ++Where things are now that this project is three repositories. The text ++after this list is what this repository said before the split, with each path ++re-pointed at the leg that now holds it. ++ ++- **`spec/`** (`opensoft/openRepoTools-spec`) holds what the project has ++ decided: `openspec/` (full proposals, their specs and archives), `specs/` ++ (the Speckit feature directories), `docs/` (the manuals) and `ideation/`. ++- **`code/`** (`opensoft/openRepoTools-code`) holds what ships: every command ++ `openRepoTools --install` places, `repos.tsv`, `commands/`, `skills/`, ++ `tests/`, its CI, and the pinned standard at `code/upstream/openRepoShape` ++ with `code/contracts/openreposhape-pin.yaml`. ++- **This root** holds the front door (this file, `CLAUDE.md`, `README.md`, ++ `LICENSE`), `project.yaml`, the leg pins and the shape's gate ++ (`make validate`), and no product code. ++- **The tests run in the code leg**: `code/tests/run.sh` from here, or ++ `tests/run.sh` inside `code/`, and the wrapper finds its own root either way. ++- **The lane tooling's code is in `code/` and its manual is in `spec/`**: ++ `spec/docs/README-lanes.md`, which is `docs/README-lanes.md` in ++ `opensoft/openRepoTools-spec` when that leg is cloned on its own. ++- **Feature work** happens in paired worktrees under ++ `worktrees//{spec,code}/`, which this root ignores and never ++ commits: full proposals and Speckit files in the `spec` one, a genuine ++ bounded amendment only in the `code` one's `features//openspec/`. ++- **A path that begins `spec/` or `code/`** in this file or `README.md` is ++ relative to this root. ++- **A local rehearsal** whose `.gitmodules` names bare repositories on disk ++ mounts the legs over git's file transport, which current git refuses by ++ default: there, `git -c protocol.file.allow=always submodule update --init ++ --recursive`. The real legs are https and need no flag. ++ + # Working in openRepoTools + + Three estate commands, `park`, `resume` and `status`; the lane tooling +@@ -19,7 +65,7 @@ person is driving the two INSTALLED commands. + + The lane tooling answers to a different document — the lane collision protocol + at `$AGENT_PROTOCOL_ROOT/protocols/lane-collision-protocol.md` and its +-amendments — and `docs/README-lanes.md` is its manual. **The code is here; the ++amendments — and `spec/docs/README-lanes.md` is its manual. **The code is here; the + data is not.** + + **A LANE HAS ONE BINDING** (Amendment 18, in force 2026-09-14): the `host`, +@@ -41,7 +87,7 @@ handoffs through `$AGENT_PROTOCOL_ROOT/workspace.yaml`'s `repository:` and + unreadable workspace with **2**: only 2 blocks a prompt; 1 would let it through. + Profile-only guard calls carrying exact `CLAUDE_NO_LANE=1` return 0 before input + or identity probes, even with unreadable configuration; other values enforce. +-Brett Heap directed this exception in `openspec/changes/skip-no-lane-guard/`; ++Brett Heap directed this exception in `spec/openspec/changes/skip-no-lane-guard/`; + Shared hooks stay installed. Exit 1 means *registry not found* in lane tooling, + and *findings were printed* in estate status; identify the command before interpreting it. + +@@ -197,13 +243,13 @@ sequence, and relay their per-repository lines rather than summarising them. + reads them all WITHOUT asking, because a read — fetched or not — moves + nothing of yours; `status --all` from anywhere says the same thing. + +-## The pinned standard at `upstream/openRepoShape` ++## The pinned standard at `code/upstream/openRepoShape` + + The suite builds real estates out of eight of openRepoShape's own files, and it +-reads them out of a submodule pinned by `contracts/openreposhape-pin.yaml`. ++reads them out of a submodule pinned by `code/contracts/openreposhape-pin.yaml`. + Three rules, and none of them is negotiable: + +-1. **Never edit anything under `upstream/openRepoShape` in place.** It is ++1. **Never edit anything under `code/upstream/openRepoShape` in place.** It is + somebody else's repository, checked out detached at a commit. A change the + standard needs is a pull request THERE, then a pin bump here. + 2. **Never pin a commit that is not on that repository's `main`.** +@@ -211,17 +257,17 @@ Three rules, and none of them is negotiable: + pull request lands and the next person's fetch of it 404s. Check with + `gh api repos/opensoft/openRepoShape/compare/main...` — `identical` or + `behind`. +-3. **The digest is RECOMPUTED, never adjusted.** `contracts/openreposhape-pin.yaml` ++3. **The digest is RECOMPUTED, never adjusted.** `code/contracts/openreposhape-pin.yaml` + carries the bump command. A `tree_sha256` edited to make +- `tests/test_upstream_pin.py` pass records this checkout as the upstream, ++ `code/tests/test_upstream_pin.py` pass records this checkout as the upstream, + which is the one thing the row exists to prevent. The pin file and the + gitlink move in ONE commit. + + ## Testing your changes to this repository + + ```sh +-git submodule update --init upstream/openRepoShape +-tests/run.sh # the suite, serialized — pass any pytest argument ++git -C code submodule update --init upstream/openRepoShape ++code/tests/run.sh # the suite, serialized — pass any pytest argument + ``` + + Brett Heap's RULING of 2026-09-16 ("do all three", on the coordinator's three +@@ -230,7 +276,7 @@ Linux's 17-22, pull requests drawing 6-12 Copilot review rounds, and this + suite queuing up to 1h20 behind the workstation lock described below): + + 1. **`TESTS-MACOS` LEAVES THE PER-PUSH GATE.** On `pull_request` +- (`.github/workflows/tests.yml`) this workflow runs `tests`, ++ (`code/.github/workflows/tests.yml`) this workflow runs `tests`, + `tests-no-submodule` and `tests-windows` only; `tests-macos` itself runs + there only once the coordinator has put the `ready` label on the PR — the + landing gate, applied once, right before the squash — and otherwise on +@@ -244,10 +290,10 @@ suite queuing up to 1h20 behind the workstation lock described below): + and two are taken; a finding after that is filed as an issue and the PR + lands regardless. + 3. **CI IS THE SUITE OF RECORD.** A writer runs the shell-suite section they +- touched locally — `bash tests/test_lane_helpers.sh` in full is fine when +- the box is quiet — and the full local `tests/run.sh` behind the ++ touched locally — `bash code/tests/test_lane_helpers.sh` in full is fine when ++ the box is quiet — and the full local `code/tests/run.sh` behind the + workstation lock is no longer required before a push: CI's `tests` job is +- the footer of record. `tests/run.sh` stays exactly as it is above: how to ++ the footer of record. `code/tests/run.sh` stays exactly as it is above: how to + run the whole suite locally, serialized, whenever a person wants that + answer anyway, and `python3 -m pytest tests -q` by hand — unserialized + against every other lane on the same workstation — is still the thing it +@@ -282,14 +328,14 @@ while [ "$(pgrep -f "$pat" | awk 'END { print NR + 0 }')" -gt 0 ]; do sleep 20; + PYTHONPYCACHEPREFIX="${XDG_CACHE_HOME:-$HOME/.cache}/openRepoTools/pycache" flock "${TMPDIR:-/tmp}/openrepotools-pytest.lock" python3 -m pytest tests -q -p no:cacheprovider + ``` + +-`awk` AND NOT `pgrep -fc`, which is what `tests/run.sh` does and for the reason ++`awk` AND NOT `pgrep -fc`, which is what `code/tests/run.sh` does and for the reason + it gives: `-c` is not in every `pgrep` this repository runs under, and the + count — never `pgrep`'s exit status — is what decides. The wrapper is the + canonical implementation of this guard; the lines above are it in one place for + a person with no checkout in front of them. + +-`tests/test_lane_helpers.sh` is 122 KB of bash that arrived with the move; +-`tests/test_lane_helpers_suite.py` is what makes `pytest` run it, so it is one ++`code/tests/test_lane_helpers.sh` is 122 KB of bash that arrived with the move; ++`code/tests/test_lane_helpers_suite.py` is what makes `pytest` run it, so it is one + slow test rather than no test at all. Everything shipped here is parsed under + macOS **bash 3.2** in CI, where `${x,,}`, `mapfile`, `declare -A` and + `local -n` are syntax errors — write `tr '[:upper:]' '[:lower:]'` and a loop. +diff --git a/README.md b/README.md +index 18c8f7e..2de8914 100644 +--- a/README.md ++++ b/README.md +@@ -101,6 +101,39 @@ parked. No config, or no record, is a note. + That is all five layers: the local layer, `--fetch`, the fork, the pin and + the record. + ++## Three repositories ++ ++This repository is the ASSEMBLY ROOT of a three-repository project — the shape ++[openRepoShape](https://github.com/opensoft/openRepoShape) calls the Triad. It ++holds this front door, `project.yaml`, the pins and the gate, and mounts the ++other two as submodules; it holds no product code of its own. ++ ++| role | repository | path | holds | ++|---|---|---|---| ++| assembly | `opensoft/openRepoTools` | `.` | the front door, the manifest, the pins, the gate | ++| spec | `opensoft/openRepoTools-spec` | `spec/` | `openspec/`, `specs/`, `docs/` (the manuals), `ideation/` | ++| code | `opensoft/openRepoTools-code` | `code/` | every command below, `repos.tsv`, `skills/`, `commands/`, `tests/`, CI, the pinned standard | ++ ++```sh ++git clone --recurse-submodules https://github.com/opensoft/openRepoTools.git ++cd openRepoTools ++make bootstrap # each leg on main AT its pinned commit, then the validators ++``` ++ ++Nothing a person types changes: the commands keep their names and the install ++one-liner below is the same line. A path in this file that begins `spec/` or ++`code/` is relative to this root. `AGENTS-shape.md` is the shape's rules for an ++agent working here, and electing the shape confers nothing. ++ ++### The lockstep invariant ++ ++For each leg, three things name the same commit and move in ONE commit here: ++the gitlink at the leg's path, `commit:` in `contracts/-pin.yaml`, and ++every `.github/workflows/*.yml` `@` that names the leg. A change to a leg ++lands in that leg first, by pull request; this root then advances its pin with ++openRepoShape's `scripts/bump-leg.py`, and `make pins` refuses when the three ++disagree. ++ + ## The lane tooling + + `lane-start`, `lane-end`, `lanes-edit.sh` and `link-estates` are here too, with +@@ -154,7 +187,7 @@ and 5 put it and where it stays. What moved here is the CODE. A `-wip` is + a private repository whose own rules file says "No secrets and no code", nobody + would look inside somebody else's for a command, and until this move the four + helpers existed in exactly one place in the world: one person's private +-repository. `docs/README-lanes.md` is the manual; `docs/README-claude-current.md` is the one for the Claude Code `lane-start` launches and the RESTART NEEDED line. ++repository. `spec/docs/README-lanes.md` is the manual; `spec/docs/README-claude-current.md` is the one for the Claude Code `lane-start` launches and the RESTART NEEDED line. + + Every one of them finds that data through `$AGENT_PROTOCOL_ROOT/workspace.yaml` + — `repository:` and `path:`, the same pointer file `resume` and `status` +@@ -180,7 +213,7 @@ different hat). + + Then it clones the repository, seeds it from openRepoShape's + `templates/workspace-root/` at the commit pinned in +-`contracts/openreposhape-pin.yaml`, and pushes one commit to `main` — **which is ++`code/contracts/openreposhape-pin.yaml`, and pushes one commit to `main` — **which is + also the check that matters**: a new repository is inside the organisation's + PR-only ruleset until an administrator excludes it, and until it is, every + register write will be refused by that gate. Better to meet it here than at +@@ -237,6 +270,11 @@ gh api repos/opensoft/openRepoTools/contents/openRepoTools \ + -H 'Accept: application/vnd.github.raw' | bash -s -- --install + ``` + ++Both lines are unchanged by the split into three repositories: the ++`openRepoTools` they fetch is this root's entry point, and the installer it ++hands over to is `code/openRepoTools` in `opensoft/openRepoTools-code`, at the ++commit this root pins. ++ + It places SEVENTEEN files into `~/.local/bin` — `openRepoTools`, `park`, `resume`, + `status`, `lane`, `lanes`, `lane-handoff`, `lane-rename`, `lanes-edit.sh`, + `lane-start`, `lane-end`, `link-estates`, the alias table `repos.tsv`, +@@ -260,7 +298,7 @@ that would not take it, and a create needs a directory's search bit as well as + its write bit. A refusal creates none of those directories either. `cp` follows + a symlink, and an install through one leaves the command uninstalled and writes + these bytes into whatever it points at. Then a `17 of 17 placed in ` line, +-and the `export PATH=…` line if that directory is not on your `PATH`. Last, it names this repository's virtual environment, `${XDG_CACHE_HOME:-~/.cache}/openRepoTools/venvs/openRepoTools/` (one per repository, outside the estate; #162), says whether it is there, and prints the two commands that make it: it never makes one, and `tests/run.sh` runs from it once it has `pytest`. ++and the `export PATH=…` line if that directory is not on your `PATH`. Last, it names this repository's virtual environment, `${XDG_CACHE_HOME:-~/.cache}/openRepoTools/venvs/openRepoTools/` (one per repository, outside the estate; #162), says whether it is there, and prints the two commands that make it: it never makes one, and `code/tests/run.sh` runs from it once it has `pytest`. + + It also places **fourteen things that are not files in that directory**: THREE + SKILLS, `/handoff`, `/lane-swap` and `/restart`, each at +@@ -300,7 +338,7 @@ arm keys on the VERB and not on the word anywhere in a path. It + never writes a profile's own `settings.json`: the launcher owns that one. + + Thirty-one artifacts, and the count is the invariant. It was sixteen until A11 +-Addendum 4 ruling 9 gave `--install` a command-file list and `commands/swap.md` ++Addendum 4 ruling 9 gave `--install` a command-file list and `code/commands/swap.md` + in it, at the same pair of paths a skill takes — because `opensoft/workBenches#74` + deletes the launcher's copy and `/swap` would otherwise be installed by nobody; + eighteen until Amendment 12 adoption act 3 added the name guard's entry; and +@@ -414,19 +452,19 @@ the way in is WSL2, exactly as it is for openRepoShape's `setup.sh`. + + **Tools depend on the shape; the shape depends on nothing.** This repository + pins the openRepoShape commit its tests were verified against, as a submodule +-at `upstream/openRepoShape` recorded in `contracts/openreposhape-pin.yaml`. ++at `code/upstream/openRepoShape` recorded in `code/contracts/openreposhape-pin.yaml`. + openRepoShape pins this repository NOWHERE, and knows of it only through one + pointer line its own `--install` prints — text, not a fetch. The reverse would + make the standard unbuildable without its tools. + +-MOUNTED, NOT COPIED. `tests/test_park_resume_commands.py` builds real estates ++MOUNTED, NOT COPIED. `code/tests/test_park_resume_commands.py` builds real estates + out of EIGHT of the standard's own files — the two root Makefiles, their two + `.gitignore`s, the two `bootstrap.py`s, `siblings.py` and + `scripts/repo_shape.py` — and a submodule hands those tests the standard's real + bytes at the pinned commit rather than copies here that could have been edited + into agreeing with a broken expectation. + +-`tests/test_upstream_pin.py` is what says the pin file and the gitlink still ++`code/tests/test_upstream_pin.py` is what says the pin file and the gitlink still + agree. Four of its checks need no submodule at all — the gitlink is read out of + `git ls-tree HEAD` and nothing imports from the checkout — so the lockstep + check bites in a clone that never materialized it. +@@ -434,6 +472,7 @@ check bites in a clone that never materialized it. + ### Bumping the pin + + ```sh ++cd code # the pin and the gitlink are the code leg's + git -C upstream/openRepoShape fetch origin + git -C upstream/openRepoShape checkout <40 hex on openRepoShape's main> + python3 -c "import sys; sys.path.insert(0, 'upstream/openRepoShape/scripts'); from repo_shape import tree_digest; print(tree_digest('upstream/openRepoShape', 'HEAD'))" +@@ -446,7 +485,7 @@ git commit + **The edit comes before the `git add`, and the block is in that order for a + reason.** Staging first and editing after stages the moved gitlink with the OLD + pin file — and that is invisible where you are standing, because +-`tests/test_upstream_pin.py` reads the pin out of the WORKING TREE and the ++`code/tests/test_upstream_pin.py` reads the pin out of the WORKING TREE and the + gitlink out of `git ls-tree HEAD`: green in your checkout, red in CI, which is + the wrong way round. The pin and the gitlink are one fact in two places. The + digest is recomputed with the PINNED CHECKOUT'S OWN `scripts/repo_shape.py` +@@ -461,10 +500,10 @@ orphaned and the next person's fetch of it 404s. + + ```sh + git clone --recurse-submodules https://github.com/opensoft/openRepoTools.git +-cd openRepoTools && python3 -m pytest tests -q ++cd openRepoTools/code && python3 -m pytest tests -q + ``` + +-From a clone that already exists, `git submodule update --init ++From a clone that already exists, `git -C code submodule update --init + upstream/openRepoShape` first. **Without the submodule the suite SKIPS rather + than fails**, naming that command: a fork's first `pytest` going red on a + missing submodule is a fork nobody finishes. The estate tests need `bash`, +-- +2.43.0 + diff --git a/specs/004-migrate-to-triad/patches/t006/assembly-0002-Bump-spec-leg-to-39c18877e042-in-openRepoTools.patch b/specs/004-migrate-to-triad/patches/t006/assembly-0002-Bump-spec-leg-to-39c18877e042-in-openRepoTools.patch new file mode 100644 index 00000000..54f88486 --- /dev/null +++ b/specs/004-migrate-to-triad/patches/t006/assembly-0002-Bump-spec-leg-to-39c18877e042-in-openRepoTools.patch @@ -0,0 +1,55 @@ +From 1c7e74f9902815c764c214d353694af4cbb75598 Mon Sep 17 00:00:00 2001 +From: openRepoShape bump-leg +Date: Thu, 8 Oct 2026 21:42:37 +0000 +Subject: [PATCH 2/3] Bump spec leg to 39c18877e042 in openRepoTools +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +593250d600ca -> 39c18877e042, tree 3a4a84beb20b343390f5bae64cb166df4d0cfd693baff6309fc13c8060d1c866. + +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. + +Lane: openRepoTools-1 +Co-Authored-By: Claude Opus 5.5 +--- + contracts/spec-pin.yaml | 4 ++-- + spec | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/contracts/spec-pin.yaml b/contracts/spec-pin.yaml +index 65f8736..af1d2f7 100644 +--- a/contracts/spec-pin.yaml ++++ b/contracts/spec-pin.yaml +@@ -21,7 +21,7 @@ leg_role: spec + source_repository: opensoft/openRepoTools-spec + submodule_path: spec + +-commit: "593250d600ca291f610671d993c2a6fc36cd690e" ++commit: "39c18877e042e13156849b861d07bc3c92e64db4" + revision_kind: commit + + digest_algorithm: sha256 +@@ -36,7 +36,7 @@ digest_algorithm: sha256 + # `, so a leg's own pin moving is drift too. + digest_definition: sorted-ls-tree-r-v1 + digests: +- tree_sha256: "92da0a8a72f8a9ed3c8827b63e2c39b1734bc1e5418612e2fa41e40b2f148b45" ++ tree_sha256: "3a4a84beb20b343390f5bae64cb166df4d0cfd693baff6309fc13c8060d1c866" + + verify_pin: scripts/validate-pins.py + resync_runbook: "README.md#the-lockstep-invariant" +diff --git a/spec b/spec +index 593250d..39c1887 160000 +--- a/spec ++++ b/spec +@@ -1 +1 @@ +-Subproject commit 593250d600ca291f610671d993c2a6fc36cd690e ++Subproject commit 39c18877e042e13156849b861d07bc3c92e64db4 +-- +2.43.0 + diff --git a/specs/004-migrate-to-triad/patches/t006/assembly-0003-Bump-code-leg-to-6169e8e96164-in-openRepoTools.patch b/specs/004-migrate-to-triad/patches/t006/assembly-0003-Bump-code-leg-to-6169e8e96164-in-openRepoTools.patch new file mode 100644 index 00000000..f5f550d7 --- /dev/null +++ b/specs/004-migrate-to-triad/patches/t006/assembly-0003-Bump-code-leg-to-6169e8e96164-in-openRepoTools.patch @@ -0,0 +1,55 @@ +From 7cde50ac1f7926b243eb0566ae0dc45d1540ab18 Mon Sep 17 00:00:00 2001 +From: openRepoShape bump-leg +Date: Thu, 8 Oct 2026 21:42:38 +0000 +Subject: [PATCH 3/3] Bump code leg to 6169e8e96164 in openRepoTools +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +73b04d637634 -> 6169e8e96164, tree 697a080f3e86d368c31b82daf9088ee834537db6f3f49888dfcc7aa9c9ecfb4f. + +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. + +Lane: openRepoTools-1 +Co-Authored-By: Claude Opus 5.5 +--- + code | 2 +- + contracts/code-pin.yaml | 4 ++-- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/code b/code +index 73b04d6..6169e8e 160000 +--- a/code ++++ b/code +@@ -1 +1 @@ +-Subproject commit 73b04d637634f897ad24e22ee4c12bed287172d6 ++Subproject commit 6169e8e9616425ce93a49447af5df426f4325312 +diff --git a/contracts/code-pin.yaml b/contracts/code-pin.yaml +index 68e06c8..18b44c7 100644 +--- a/contracts/code-pin.yaml ++++ b/contracts/code-pin.yaml +@@ -21,7 +21,7 @@ leg_role: code + source_repository: opensoft/openRepoTools-code + submodule_path: code + +-commit: "73b04d637634f897ad24e22ee4c12bed287172d6" ++commit: "6169e8e9616425ce93a49447af5df426f4325312" + revision_kind: commit + + digest_algorithm: sha256 +@@ -36,7 +36,7 @@ digest_algorithm: sha256 + # `, so a leg's own pin moving is drift too. + digest_definition: sorted-ls-tree-r-v1 + digests: +- tree_sha256: "b4940a13bd8ecbe2d1c6ed49368d1545d1a5f1f7a92623c7fe11ac1cb3617604" ++ tree_sha256: "697a080f3e86d368c31b82daf9088ee834537db6f3f49888dfcc7aa9c9ecfb4f" + + verify_pin: scripts/validate-pins.py + resync_runbook: "README.md#the-lockstep-invariant" +-- +2.43.0 + diff --git a/specs/004-migrate-to-triad/patches/t006/code-0001-The-code-leg-gets-its-own-front-door-the-shape-s-leg.patch b/specs/004-migrate-to-triad/patches/t006/code-0001-The-code-leg-gets-its-own-front-door-the-shape-s-leg.patch new file mode 100644 index 00000000..2d2dc029 --- /dev/null +++ b/specs/004-migrate-to-triad/patches/t006/code-0001-The-code-leg-gets-its-own-front-door-the-shape-s-leg.patch @@ -0,0 +1,417 @@ +From 6169e8e9616425ce93a49447af5df426f4325312 Mon Sep 17 00:00:00 2001 +From: Brett Heap <1513478+brettheap@users.noreply.github.com> +Date: Thu, 8 Oct 2026 21:41:17 +0000 +Subject: [PATCH] The code leg gets its own front door: the shape's leg + AGENTS.md, CLAUDE.md and README.md, and the LF rule, ignore list and licence + it lost when the root kept them. + +Rehearsal patch for opensoft/openRepoTools#186, task T006 of +specs/004-migrate-to-triad/tasks.md, against run C's code leg 73b04d6. The +split left this leg with no AGENTS.md, CLAUDE.md, README.md, LICENSE, +.gitattributes or .gitignore, because all six stayed in the assembly root, +and a superproject's attributes and ignore rules never reach into a +submodule's worktree. + +- AGENTS.md, CLAUDE.md and README.md are openRepoShape 7f84ca4's + templates/code-root/ files, rendered by its own copy_tree with this + project's names, so the leg-clone detector in scripts/shape_advisory.py + recognises them. Each AGENTS.md and README.md then adds one short section: + where the suite runs, the pinned standard, where the manuals and the + specifications now live, and where a feature's code and a bounded + amendment are written. +- .gitattributes is the monorepo's own file, byte for byte (blob 3560973). + Its reason, the installer's byte-for-byte comparisons, is this leg's. + `git add --renormalize .` changes nothing here. +- .gitignore is the shape's templates/code-root/.gitignore, a superset of + the three lines the monorepo ignored. +- LICENSE is the root's Apache-2.0 text, byte for byte (blob 261eeb9). + +No executable, test or workflow is touched. + +Lane: openRepoTools-1 +Co-Authored-By: Claude Opus 5.5 +--- + .gitattributes | 32 ++++++++ + .gitignore | 12 +++ + AGENTS.md | 40 ++++++++++ + CLAUDE.md | 1 + + LICENSE | 201 +++++++++++++++++++++++++++++++++++++++++++++++++ + README.md | 45 +++++++++++ + 6 files changed, 331 insertions(+) + create mode 100644 .gitattributes + create mode 100644 .gitignore + create mode 100644 AGENTS.md + create mode 100644 CLAUDE.md + create mode 100644 LICENSE + create mode 100644 README.md + +diff --git a/.gitattributes b/.gitattributes +new file mode 100644 +index 0000000..3560973 +--- /dev/null ++++ b/.gitattributes +@@ -0,0 +1,32 @@ ++# THIS REPOSITORY'S OWN INSTALLER COMPARES ITS BYTES, so the bytes have to be ++# the same bytes on every platform. ++# ++# `openRepoTools --install` decides between `installed at`, `updated at` and ++# `already installed … (unchanged)` with `cmp -s`, and ++# `tests/test_openrepotools_command.py` compares an installed copy to this ++# checkout's file BYTE FOR BYTE. Git for Windows' installer default is ++# `core.autocrlf=true`; a checkout of THIS repository made under it turns every ++# `\n` in `openRepoTools`, `park`, `resume` and `status` into `\r\n`, and every one ++# of those comparisons is about line endings rather than about content — a ++# second `--install` reporting three files `updated at` on a machine where ++# nothing changed, and a colleague whose only sin was cloning on a stock ++# Windows laptop watching the suite fail on lines nobody touched. CI never sees ++# it: the Linux and macOS jobs check out LF by default and the Windows job pins ++# `core.autocrlf=false`. ++# ++# The same rule for a second reason. `tests/test_park_resume_commands.py` ++# asserts that `park`, `resume` and `status` carry the shared estate resolver ++# BYTE FOR BYTE — that is what makes the deliberate duplication safe — and a CRLF ++# checkout would either break the claim or hide a real drift behind it. ++# ++# NO PATH IS LISTED BY NAME. Every file this repository ships is text — bash, ++# Python, YAML, Markdown, a workflow — so there is nothing for git's binary ++# heuristic to misjudge, and `text=auto` leaves that detection where it ++# belongs: with git. The pinned `upstream/openRepoShape` is a gitlink and ++# carries its own copy of this rule. ++# ++# openRepoShape #63 wrote this rule for the standard's own root, and its #51 ++# (2026-09-05) wrote it for every scaffolded project; this is the same rule ++# retold for the repository that ships the two commands. ++ ++* text=auto eol=lf +diff --git a/.gitignore b/.gitignore +new file mode 100644 +index 0000000..2fb4cee +--- /dev/null ++++ b/.gitignore +@@ -0,0 +1,12 @@ ++__pycache__/ ++*.py[cod] ++.pytest_cache/ ++.mypy_cache/ ++.ruff_cache/ ++*.egg-info/ ++.venv/ ++venv/ ++.DS_Store ++*.swp ++.idea/ ++.vscode/ +diff --git a/AGENTS.md b/AGENTS.md +new file mode 100644 +index 0000000..810eac1 +--- /dev/null ++++ b/AGENTS.md +@@ -0,0 +1,40 @@ ++This is the **code leg** of openRepoTools (`openrepotools`): the implementation ++and its tests. ++ ++**The project's rules are not here.** They are in the assembly root ++`opensoft/openRepoTools`, in `AGENTS-shape.md` — read that before touching anything ++that spans the legs. This leg is mounted there at `code/`, and the other leg, ++`opensoft/openRepoTools-spec`, beside it at `../spec/`, once the root is cloned with ++`--recurse-submodules`. ++ ++Working here is ordinary — an ordinary repository on an ordinary branch. What ++advancing this leg does NOT do is advance the project: that is ONE commit in ++`opensoft/openRepoTools` moving the gitlink, `contracts/code-pin.yaml` and every ++workflow `@` for this leg together. ++ ++Being the code leg confers no authority over the implementation. The split is ++navigation. ++ ++## What is particular to openRepoTools' code leg ++ ++The project's own instructions — driving `park`, `resume` and `status`, the ++lane tooling's protocol, the pinned standard's three rules, how to test and the ++CI policy — are the assembly root's `AGENTS.md`, and they govern here. This ++section adds only what standing in this leg changes. ++ ++- **The suite runs here**: `git submodule update --init upstream/openRepoShape`, ++ then `tests/run.sh`, which serializes every lane on a workstation behind ++ `${TMPDIR:-/tmp}/openrepotools-pytest.lock` and passes any pytest argument on. ++- **The pinned standard is here**: `upstream/openRepoShape`, recorded in ++ `contracts/openreposhape-pin.yaml`. Never edit it in place, never pin a commit ++ that is not on openRepoShape's `main`, and recompute the digest rather than ++ adjust it — the root `AGENTS.md` gives the three rules in full. ++- **The manuals and the specifications are not here.** They are the spec ++ leg's `docs/` (`README-lanes.md`, `README-claude-current.md`), `openspec/` and ++ `specs/`: `../spec/` when this leg is mounted in the assembly root, and ++ `opensoft/openRepoTools-spec` when it is cloned on its own. ++- **A feature's code is written in its code worktree**, ++ `worktrees//code/` under the assembly root, on the feature's ++ branch. A genuine bounded amendment to an approved spec baseline is recorded ++ in that worktree's `features//openspec/`; a full proposal never ++ is, and goes to the spec leg. +diff --git a/CLAUDE.md b/CLAUDE.md +new file mode 100644 +index 0000000..5d30268 +--- /dev/null ++++ b/CLAUDE.md +@@ -0,0 +1 @@ ++Read AGENTS.md. +diff --git a/LICENSE b/LICENSE +new file mode 100644 +index 0000000..261eeb9 +--- /dev/null ++++ b/LICENSE +@@ -0,0 +1,201 @@ ++ Apache License ++ Version 2.0, January 2004 ++ http://www.apache.org/licenses/ ++ ++ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION ++ ++ 1. Definitions. ++ ++ "License" shall mean the terms and conditions for use, reproduction, ++ and distribution as defined by Sections 1 through 9 of this document. ++ ++ "Licensor" shall mean the copyright owner or entity authorized by ++ the copyright owner that is granting the License. ++ ++ "Legal Entity" shall mean the union of the acting entity and all ++ other entities that control, are controlled by, or are under common ++ control with that entity. For the purposes of this definition, ++ "control" means (i) the power, direct or indirect, to cause the ++ direction or management of such entity, whether by contract or ++ otherwise, or (ii) ownership of fifty percent (50%) or more of the ++ outstanding shares, or (iii) beneficial ownership of such entity. ++ ++ "You" (or "Your") shall mean an individual or Legal Entity ++ exercising permissions granted by this License. ++ ++ "Source" form shall mean the preferred form for making modifications, ++ including but not limited to software source code, documentation ++ source, and configuration files. ++ ++ "Object" form shall mean any form resulting from mechanical ++ transformation or translation of a Source form, including but ++ not limited to compiled object code, generated documentation, ++ and conversions to other media types. ++ ++ "Work" shall mean the work of authorship, whether in Source or ++ Object form, made available under the License, as indicated by a ++ copyright notice that is included in or attached to the work ++ (an example is provided in the Appendix below). ++ ++ "Derivative Works" shall mean any work, whether in Source or Object ++ form, that is based on (or derived from) the Work and for which the ++ editorial revisions, annotations, elaborations, or other modifications ++ represent, as a whole, an original work of authorship. For the purposes ++ of this License, Derivative Works shall not include works that remain ++ separable from, or merely link (or bind by name) to the interfaces of, ++ the Work and Derivative Works thereof. ++ ++ "Contribution" shall mean any work of authorship, including ++ the original version of the Work and any modifications or additions ++ to that Work or Derivative Works thereof, that is intentionally ++ submitted to Licensor for inclusion in the Work by the copyright owner ++ or by an individual or Legal Entity authorized to submit on behalf of ++ the copyright owner. For the purposes of this definition, "submitted" ++ means any form of electronic, verbal, or written communication sent ++ to the Licensor or its representatives, including but not limited to ++ communication on electronic mailing lists, source code control systems, ++ and issue tracking systems that are managed by, or on behalf of, the ++ Licensor for the purpose of discussing and improving the Work, but ++ excluding communication that is conspicuously marked or otherwise ++ designated in writing by the copyright owner as "Not a Contribution." ++ ++ "Contributor" shall mean Licensor and any individual or Legal Entity ++ on behalf of whom a Contribution has been received by Licensor and ++ subsequently incorporated within the Work. ++ ++ 2. Grant of Copyright License. Subject to the terms and conditions of ++ this License, each Contributor hereby grants to You a perpetual, ++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable ++ copyright license to reproduce, prepare Derivative Works of, ++ publicly display, publicly perform, sublicense, and distribute the ++ Work and such Derivative Works in Source or Object form. ++ ++ 3. Grant of Patent License. Subject to the terms and conditions of ++ this License, each Contributor hereby grants to You a perpetual, ++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable ++ (except as stated in this section) patent license to make, have made, ++ use, offer to sell, sell, import, and otherwise transfer the Work, ++ where such license applies only to those patent claims licensable ++ by such Contributor that are necessarily infringed by their ++ Contribution(s) alone or by combination of their Contribution(s) ++ with the Work to which such Contribution(s) was submitted. If You ++ institute patent litigation against any entity (including a ++ cross-claim or counterclaim in a lawsuit) alleging that the Work ++ or a Contribution incorporated within the Work constitutes direct ++ or contributory patent infringement, then any patent licenses ++ granted to You under this License for that Work shall terminate ++ as of the date such litigation is filed. ++ ++ 4. Redistribution. You may reproduce and distribute copies of the ++ Work or Derivative Works thereof in any medium, with or without ++ modifications, and in Source or Object form, provided that You ++ meet the following conditions: ++ ++ (a) You must give any other recipients of the Work or ++ Derivative Works a copy of this License; and ++ ++ (b) You must cause any modified files to carry prominent notices ++ stating that You changed the files; and ++ ++ (c) You must retain, in the Source form of any Derivative Works ++ that You distribute, all copyright, patent, trademark, and ++ attribution notices from the Source form of the Work, ++ excluding those notices that do not pertain to any part of ++ the Derivative Works; and ++ ++ (d) If the Work includes a "NOTICE" text file as part of its ++ distribution, then any Derivative Works that You distribute must ++ include a readable copy of the attribution notices contained ++ within such NOTICE file, excluding those notices that do not ++ pertain to any part of the Derivative Works, in at least one ++ of the following places: within a NOTICE text file distributed ++ as part of the Derivative Works; within the Source form or ++ documentation, if provided along with the Derivative Works; or, ++ within a display generated by the Derivative Works, if and ++ wherever such third-party notices normally appear. The contents ++ of the NOTICE file are for informational purposes only and ++ do not modify the License. You may add Your own attribution ++ notices within Derivative Works that You distribute, alongside ++ or as an addendum to the NOTICE text from the Work, provided ++ that such additional attribution notices cannot be construed ++ as modifying the License. ++ ++ You may add Your own copyright statement to Your modifications and ++ may provide additional or different license terms and conditions ++ for use, reproduction, or distribution of Your modifications, or ++ for any such Derivative Works as a whole, provided Your use, ++ reproduction, and distribution of the Work otherwise complies with ++ the conditions stated in this License. ++ ++ 5. Submission of Contributions. Unless You explicitly state otherwise, ++ any Contribution intentionally submitted for inclusion in the Work ++ by You to the Licensor shall be under the terms and conditions of ++ this License, without any additional terms or conditions. ++ Notwithstanding the above, nothing herein shall supersede or modify ++ the terms of any separate license agreement you may have executed ++ with Licensor regarding such Contributions. ++ ++ 6. Trademarks. This License does not grant permission to use the trade ++ names, trademarks, service marks, or product names of the Licensor, ++ except as required for reasonable and customary use in describing the ++ origin of the Work and reproducing the content of the NOTICE file. ++ ++ 7. Disclaimer of Warranty. Unless required by applicable law or ++ agreed to in writing, Licensor provides the Work (and each ++ Contributor provides its Contributions) on an "AS IS" BASIS, ++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or ++ implied, including, without limitation, any warranties or conditions ++ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A ++ PARTICULAR PURPOSE. You are solely responsible for determining the ++ appropriateness of using or redistributing the Work and assume any ++ risks associated with Your exercise of permissions under this License. ++ ++ 8. Limitation of Liability. In no event and under no legal theory, ++ whether in tort (including negligence), contract, or otherwise, ++ unless required by applicable law (such as deliberate and grossly ++ negligent acts) or agreed to in writing, shall any Contributor be ++ liable to You for damages, including any direct, indirect, special, ++ incidental, or consequential damages of any character arising as a ++ result of this License or out of the use or inability to use the ++ Work (including but not limited to damages for loss of goodwill, ++ work stoppage, computer failure or malfunction, or any and all ++ other commercial damages or losses), even if such Contributor ++ has been advised of the possibility of such damages. ++ ++ 9. Accepting Warranty or Additional Liability. While redistributing ++ the Work or Derivative Works thereof, You may choose to offer, ++ and charge a fee for, acceptance of support, warranty, indemnity, ++ or other liability obligations and/or rights consistent with this ++ License. However, in accepting such obligations, You may act only ++ on Your own behalf and on Your sole responsibility, not on behalf ++ of any other Contributor, and only if You agree to indemnify, ++ defend, and hold each Contributor harmless for any liability ++ incurred by, or claims asserted against, such Contributor by reason ++ of your accepting any such warranty or additional liability. ++ ++ END OF TERMS AND CONDITIONS ++ ++ APPENDIX: How to apply the Apache License to your work. ++ ++ To apply the Apache License to your work, attach the following ++ boilerplate notice, with the fields enclosed by brackets "[]" ++ replaced with your own identifying information. (Don't include ++ the brackets!) The text should be enclosed in the appropriate ++ comment syntax for the file format. We also recommend that a ++ file or class name and description of purpose be included on the ++ same "printed page" as the copyright notice for easier ++ identification within third-party archives. ++ ++ Copyright [yyyy] [name of copyright owner] ++ ++ Licensed under the Apache License, Version 2.0 (the "License"); ++ you may not use this file except in compliance with the License. ++ You may obtain a copy of the License at ++ ++ http://www.apache.org/licenses/LICENSE-2.0 ++ ++ Unless required by applicable law or agreed to in writing, software ++ distributed under the License is distributed on an "AS IS" BASIS, ++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ See the License for the specific language governing permissions and ++ limitations under the License. +diff --git a/README.md b/README.md +new file mode 100644 +index 0000000..1084a22 +--- /dev/null ++++ b/README.md +@@ -0,0 +1,45 @@ ++# openRepoTools-code ++ ++The **code leg** of the `openrepotools` project: the implementation and its ++tests. The requirements it implements live in ++[`opensoft/openRepoTools-spec`](https://github.com/opensoft/openRepoTools-spec). ++ ++**Clone the assembly root, not this repository.** This leg is mounted as a ++submodule at `code/` inside ++[`opensoft/openRepoTools`](https://github.com/opensoft/openRepoTools), which ++is what pins the commit of this repository that the project currently is: ++ ++```sh ++git clone --recurse-submodules https://github.com/opensoft/openRepoTools.git ++cd openRepoTools ++make bootstrap ++``` ++ ++Working here directly is fine — it is an ordinary repository with an ordinary ++branch. What advancing this leg does NOT do is advance the project: that is a ++commit in the assembly root moving the gitlink, `contracts/code-pin.yaml` and ++any workflow `@` reference together. ++ ++Being the code leg confers no authority over the implementation. The split is ++navigation; authority travels in grants, and a project that keeps spec and ++code in one repository is reviewed identically. ++ ++Topic: `xf-project-openrepotools`. ++ ++## What is in it ++ ++Every command `openRepoTools --install` places is a file at this leg's root — ++`openRepoTools` itself, `park`, `resume`, `status` and the lane tooling — with ++the alias table `repos.tsv`, the `skills/` and `commands/` it installs, the ++suite under `tests/` (`tests/run.sh` runs it), the CI under ++`.github/workflows/`, and the pinned standard at `upstream/openRepoShape` ++(`contracts/openreposhape-pin.yaml`). Installing is unchanged: the one-liner in ++the assembly root's `README.md` fetches the root's `openRepoTools`, which hands ++over to the installer here at the commit the root pins. ++ ++The manuals — `docs/README-lanes.md` for the lane tooling and ++`docs/README-claude-current.md` for `claude-current` and ++`claude-restart-check` — and every specification are in ++[`opensoft/openRepoTools-spec`](https://github.com/opensoft/openRepoTools-spec). ++ ++Licence: Apache-2.0. See [LICENSE](LICENSE). +-- +2.43.0 + diff --git a/specs/004-migrate-to-triad/patches/t006/spec-0001-The-spec-leg-gets-its-own-front-door-the-shape-s-leg.patch b/specs/004-migrate-to-triad/patches/t006/spec-0001-The-spec-leg-gets-its-own-front-door-the-shape-s-leg.patch new file mode 100644 index 00000000..f8373be7 --- /dev/null +++ b/specs/004-migrate-to-triad/patches/t006/spec-0001-The-spec-leg-gets-its-own-front-door-the-shape-s-leg.patch @@ -0,0 +1,398 @@ +From 39c18877e042e13156849b861d07bc3c92e64db4 Mon Sep 17 00:00:00 2001 +From: Brett Heap <1513478+brettheap@users.noreply.github.com> +Date: Thu, 8 Oct 2026 21:41:43 +0000 +Subject: [PATCH] The spec leg gets its own front door: the shape's leg + AGENTS.md, CLAUDE.md and README.md, the LF rule and licence it lost when the + root kept them, and the claude-current manual's three test paths re-pointed + at the code leg. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Rehearsal patch for opensoft/openRepoTools#186, task T006 of +specs/004-migrate-to-triad/tasks.md, against run C's spec leg 593250d. The +split left this leg with no AGENTS.md, CLAUDE.md, README.md, LICENSE or +.gitattributes, because all of them stayed in the assembly root, and a +superproject's attributes never reach into a submodule's worktree. + +- AGENTS.md, CLAUDE.md and README.md are openRepoShape 7f84ca4's + templates/spec-root/ files, rendered by its own copy_tree with this + project's names, so the leg-clone detector in scripts/shape_advisory.py + recognises them. Each AGENTS.md and README.md then adds one short section: + what this leg holds; that full proposals and canonical Speckit files live + here, and a bounded amendment in the code leg's feature folder; where + `openspec validate` runs; and how to read a pre-split path or commit hash. +- .gitattributes restores the `* text=auto eol=lf` rule the monorepo's root + file applied to these documents. `git add --renormalize .` changes nothing. +- LICENSE is the root's Apache-2.0 text, byte for byte (blob 261eeb9). +- docs/README-claude-current.md:275-276: `tests/test_claude_current.py`, + `tests/test_claude_restart_check.py` and + `tests/test_lane_start_claude_current.py` become `code/tests/…`, relative + to the assembly root. + +No approval record is rewritten. Paths and commit hashes recorded in +openspec/changes/ and specs/ are left as their authors wrote them, under +FR-014 of specs/004-migrate-to-triad/spec.md. docs/README-lanes.md is left +to the lane-tooling analysis. + +Lane: openRepoTools-1 +Co-Authored-By: Claude Opus 5.5 +--- + .gitattributes | 10 ++ + AGENTS.md | 48 ++++++++ + CLAUDE.md | 1 + + LICENSE | 201 ++++++++++++++++++++++++++++++++++ + README.md | 39 +++++++ + docs/README-claude-current.md | 4 +- + 6 files changed, 301 insertions(+), 2 deletions(-) + create mode 100644 .gitattributes + create mode 100644 AGENTS.md + create mode 100644 CLAUDE.md + create mode 100644 LICENSE + create mode 100644 README.md + +diff --git a/.gitattributes b/.gitattributes +new file mode 100644 +index 0000000..974ecca +--- /dev/null ++++ b/.gitattributes +@@ -0,0 +1,10 @@ ++# LF IN EVERY WORKTREE, as these documents had before the split into three ++# repositories: the monorepo's `* text=auto eol=lf` covered them, and a ++# superproject's attributes never reach into a submodule's worktree, so the ++# rule travels with this leg. The code leg's hygiene tests read the manuals in ++# `docs/` and match their lines; a CRLF checkout would make those matches about ++# line endings rather than about content. ++# ++# NO PATH IS LISTED BY NAME. Every file here is text. ++ ++* text=auto eol=lf +diff --git a/AGENTS.md b/AGENTS.md +new file mode 100644 +index 0000000..b5504c1 +--- /dev/null ++++ b/AGENTS.md +@@ -0,0 +1,48 @@ ++This is the **spec leg** of openRepoTools (`openrepotools`): requirements, decisions ++and acceptance criteria. ++ ++**The project's rules are not here.** They are in the assembly root ++`opensoft/openRepoTools`, in `AGENTS-shape.md` — read that before touching anything ++that spans the legs. This leg is mounted there at `spec/`, and the other leg, ++`opensoft/openRepoTools-code`, beside it at `../code/`, once the root is cloned with ++`--recurse-submodules`. ++ ++Working here is ordinary — an ordinary repository on an ordinary branch. What ++advancing this leg does NOT do is advance the project: that is ONE commit in ++`opensoft/openRepoTools` moving the gitlink, `contracts/spec-pin.yaml` and every ++workflow `@` for this leg together. ++ ++Being the spec leg confers no authority over specifications. The split is ++navigation. ++ ++## What is particular to openRepoTools' spec leg ++ ++The project's own instructions are the assembly root's `AGENTS.md`, and they ++govern here. This section adds only what standing in this leg changes. ++ ++- **What is here**: `openspec/` (full OpenSpec proposals with their delta ++ specs, and their archive once archived), `specs/` (the Speckit feature ++ directories, `specs//`), `docs/` (the manuals: ++ `README-lanes.md` for the lane tooling, `README-claude-current.md` for ++ `claude-current` and `claude-restart-check`) and `ideation/` (brainstorm ++ packets). ++- **Full proposals and canonical Speckit files live here**, whichever branch ++ they were written on. The one exception is a genuine bounded amendment to an ++ approved baseline: it is recorded in the code leg's feature worktree, under ++ `features//openspec/`, and reconciled here once, when its ++ feature completes (the shared workflow's "Triad Feature Amendments"). ++- **`openspec` runs here**, never from the assembly root, which has no ++ `openspec/`: `openspec validate --strict` from this leg's root, or ++ from a feature's spec worktree, `worktrees//spec/` under the ++ assembly root, for a change on that feature's branch. A local root takes ++ precedence over a docs-store pointer; pass `--store` only where it resolves ++ to the checkout you mean. ++- **The code these documents describe is the code leg's.** A path such as ++ `tests/…`, `upstream/openRepoShape` or `contracts/openreposhape-pin.yaml` in a ++ document written before the split names a file of ++ `opensoft/openRepoTools-code` (`../code/` when mounted); where such a path has ++ been repaired it reads `code/…`, relative to the assembly root. A commit of this ++ project that such a document cites is a commit of `opensoft/openRepoTools`, ++ the assembly, which kept the whole history and its branches; the split ++ rewrote this leg's copy of that history, so the same change has a different ++ hash here. +diff --git a/CLAUDE.md b/CLAUDE.md +new file mode 100644 +index 0000000..5d30268 +--- /dev/null ++++ b/CLAUDE.md +@@ -0,0 +1 @@ ++Read AGENTS.md. +diff --git a/LICENSE b/LICENSE +new file mode 100644 +index 0000000..261eeb9 +--- /dev/null ++++ b/LICENSE +@@ -0,0 +1,201 @@ ++ Apache License ++ Version 2.0, January 2004 ++ http://www.apache.org/licenses/ ++ ++ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION ++ ++ 1. Definitions. ++ ++ "License" shall mean the terms and conditions for use, reproduction, ++ and distribution as defined by Sections 1 through 9 of this document. ++ ++ "Licensor" shall mean the copyright owner or entity authorized by ++ the copyright owner that is granting the License. ++ ++ "Legal Entity" shall mean the union of the acting entity and all ++ other entities that control, are controlled by, or are under common ++ control with that entity. For the purposes of this definition, ++ "control" means (i) the power, direct or indirect, to cause the ++ direction or management of such entity, whether by contract or ++ otherwise, or (ii) ownership of fifty percent (50%) or more of the ++ outstanding shares, or (iii) beneficial ownership of such entity. ++ ++ "You" (or "Your") shall mean an individual or Legal Entity ++ exercising permissions granted by this License. ++ ++ "Source" form shall mean the preferred form for making modifications, ++ including but not limited to software source code, documentation ++ source, and configuration files. ++ ++ "Object" form shall mean any form resulting from mechanical ++ transformation or translation of a Source form, including but ++ not limited to compiled object code, generated documentation, ++ and conversions to other media types. ++ ++ "Work" shall mean the work of authorship, whether in Source or ++ Object form, made available under the License, as indicated by a ++ copyright notice that is included in or attached to the work ++ (an example is provided in the Appendix below). ++ ++ "Derivative Works" shall mean any work, whether in Source or Object ++ form, that is based on (or derived from) the Work and for which the ++ editorial revisions, annotations, elaborations, or other modifications ++ represent, as a whole, an original work of authorship. For the purposes ++ of this License, Derivative Works shall not include works that remain ++ separable from, or merely link (or bind by name) to the interfaces of, ++ the Work and Derivative Works thereof. ++ ++ "Contribution" shall mean any work of authorship, including ++ the original version of the Work and any modifications or additions ++ to that Work or Derivative Works thereof, that is intentionally ++ submitted to Licensor for inclusion in the Work by the copyright owner ++ or by an individual or Legal Entity authorized to submit on behalf of ++ the copyright owner. For the purposes of this definition, "submitted" ++ means any form of electronic, verbal, or written communication sent ++ to the Licensor or its representatives, including but not limited to ++ communication on electronic mailing lists, source code control systems, ++ and issue tracking systems that are managed by, or on behalf of, the ++ Licensor for the purpose of discussing and improving the Work, but ++ excluding communication that is conspicuously marked or otherwise ++ designated in writing by the copyright owner as "Not a Contribution." ++ ++ "Contributor" shall mean Licensor and any individual or Legal Entity ++ on behalf of whom a Contribution has been received by Licensor and ++ subsequently incorporated within the Work. ++ ++ 2. Grant of Copyright License. Subject to the terms and conditions of ++ this License, each Contributor hereby grants to You a perpetual, ++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable ++ copyright license to reproduce, prepare Derivative Works of, ++ publicly display, publicly perform, sublicense, and distribute the ++ Work and such Derivative Works in Source or Object form. ++ ++ 3. Grant of Patent License. Subject to the terms and conditions of ++ this License, each Contributor hereby grants to You a perpetual, ++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable ++ (except as stated in this section) patent license to make, have made, ++ use, offer to sell, sell, import, and otherwise transfer the Work, ++ where such license applies only to those patent claims licensable ++ by such Contributor that are necessarily infringed by their ++ Contribution(s) alone or by combination of their Contribution(s) ++ with the Work to which such Contribution(s) was submitted. If You ++ institute patent litigation against any entity (including a ++ cross-claim or counterclaim in a lawsuit) alleging that the Work ++ or a Contribution incorporated within the Work constitutes direct ++ or contributory patent infringement, then any patent licenses ++ granted to You under this License for that Work shall terminate ++ as of the date such litigation is filed. ++ ++ 4. Redistribution. You may reproduce and distribute copies of the ++ Work or Derivative Works thereof in any medium, with or without ++ modifications, and in Source or Object form, provided that You ++ meet the following conditions: ++ ++ (a) You must give any other recipients of the Work or ++ Derivative Works a copy of this License; and ++ ++ (b) You must cause any modified files to carry prominent notices ++ stating that You changed the files; and ++ ++ (c) You must retain, in the Source form of any Derivative Works ++ that You distribute, all copyright, patent, trademark, and ++ attribution notices from the Source form of the Work, ++ excluding those notices that do not pertain to any part of ++ the Derivative Works; and ++ ++ (d) If the Work includes a "NOTICE" text file as part of its ++ distribution, then any Derivative Works that You distribute must ++ include a readable copy of the attribution notices contained ++ within such NOTICE file, excluding those notices that do not ++ pertain to any part of the Derivative Works, in at least one ++ of the following places: within a NOTICE text file distributed ++ as part of the Derivative Works; within the Source form or ++ documentation, if provided along with the Derivative Works; or, ++ within a display generated by the Derivative Works, if and ++ wherever such third-party notices normally appear. The contents ++ of the NOTICE file are for informational purposes only and ++ do not modify the License. You may add Your own attribution ++ notices within Derivative Works that You distribute, alongside ++ or as an addendum to the NOTICE text from the Work, provided ++ that such additional attribution notices cannot be construed ++ as modifying the License. ++ ++ You may add Your own copyright statement to Your modifications and ++ may provide additional or different license terms and conditions ++ for use, reproduction, or distribution of Your modifications, or ++ for any such Derivative Works as a whole, provided Your use, ++ reproduction, and distribution of the Work otherwise complies with ++ the conditions stated in this License. ++ ++ 5. Submission of Contributions. Unless You explicitly state otherwise, ++ any Contribution intentionally submitted for inclusion in the Work ++ by You to the Licensor shall be under the terms and conditions of ++ this License, without any additional terms or conditions. ++ Notwithstanding the above, nothing herein shall supersede or modify ++ the terms of any separate license agreement you may have executed ++ with Licensor regarding such Contributions. ++ ++ 6. Trademarks. This License does not grant permission to use the trade ++ names, trademarks, service marks, or product names of the Licensor, ++ except as required for reasonable and customary use in describing the ++ origin of the Work and reproducing the content of the NOTICE file. ++ ++ 7. Disclaimer of Warranty. Unless required by applicable law or ++ agreed to in writing, Licensor provides the Work (and each ++ Contributor provides its Contributions) on an "AS IS" BASIS, ++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or ++ implied, including, without limitation, any warranties or conditions ++ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A ++ PARTICULAR PURPOSE. You are solely responsible for determining the ++ appropriateness of using or redistributing the Work and assume any ++ risks associated with Your exercise of permissions under this License. ++ ++ 8. Limitation of Liability. In no event and under no legal theory, ++ whether in tort (including negligence), contract, or otherwise, ++ unless required by applicable law (such as deliberate and grossly ++ negligent acts) or agreed to in writing, shall any Contributor be ++ liable to You for damages, including any direct, indirect, special, ++ incidental, or consequential damages of any character arising as a ++ result of this License or out of the use or inability to use the ++ Work (including but not limited to damages for loss of goodwill, ++ work stoppage, computer failure or malfunction, or any and all ++ other commercial damages or losses), even if such Contributor ++ has been advised of the possibility of such damages. ++ ++ 9. Accepting Warranty or Additional Liability. While redistributing ++ the Work or Derivative Works thereof, You may choose to offer, ++ and charge a fee for, acceptance of support, warranty, indemnity, ++ or other liability obligations and/or rights consistent with this ++ License. However, in accepting such obligations, You may act only ++ on Your own behalf and on Your sole responsibility, not on behalf ++ of any other Contributor, and only if You agree to indemnify, ++ defend, and hold each Contributor harmless for any liability ++ incurred by, or claims asserted against, such Contributor by reason ++ of your accepting any such warranty or additional liability. ++ ++ END OF TERMS AND CONDITIONS ++ ++ APPENDIX: How to apply the Apache License to your work. ++ ++ To apply the Apache License to your work, attach the following ++ boilerplate notice, with the fields enclosed by brackets "[]" ++ replaced with your own identifying information. (Don't include ++ the brackets!) The text should be enclosed in the appropriate ++ comment syntax for the file format. We also recommend that a ++ file or class name and description of purpose be included on the ++ same "printed page" as the copyright notice for easier ++ identification within third-party archives. ++ ++ Copyright [yyyy] [name of copyright owner] ++ ++ Licensed under the Apache License, Version 2.0 (the "License"); ++ you may not use this file except in compliance with the License. ++ You may obtain a copy of the License at ++ ++ http://www.apache.org/licenses/LICENSE-2.0 ++ ++ Unless required by applicable law or agreed to in writing, software ++ distributed under the License is distributed on an "AS IS" BASIS, ++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ See the License for the specific language governing permissions and ++ limitations under the License. +diff --git a/README.md b/README.md +new file mode 100644 +index 0000000..89beb3b +--- /dev/null ++++ b/README.md +@@ -0,0 +1,39 @@ ++# openRepoTools-spec ++ ++The **spec leg** of the `openrepotools` project: requirements, decisions and ++acceptance criteria. The implementation lives in ++[`opensoft/openRepoTools-code`](https://github.com/opensoft/openRepoTools-code). ++ ++**Clone the assembly root, not this repository.** This leg is mounted as a ++submodule at `spec/` inside ++[`opensoft/openRepoTools`](https://github.com/opensoft/openRepoTools), which ++is what pins the commit of this repository that the project currently is: ++ ++```sh ++git clone --recurse-submodules https://github.com/opensoft/openRepoTools.git ++cd openRepoTools ++make bootstrap ++``` ++ ++Working here directly is fine — it is an ordinary repository with an ordinary ++branch. What advancing this leg does NOT do is advance the project: that is a ++commit in the assembly root moving the gitlink, `contracts/spec-pin.yaml` and ++any workflow `@` reference together. ++ ++Being the spec leg confers no authority over specifications. The split is ++navigation; authority travels in grants, and a project that keeps spec and ++code in one repository is reviewed identically. ++ ++Topic: `xf-project-openrepotools`. ++ ++## What is in it ++ ++`openspec/` holds the project's full OpenSpec proposals, `specs/` its Speckit ++feature directories, `docs/` the manuals — `docs/README-lanes.md` for the lane ++tooling and `docs/README-claude-current.md` for `claude-current` and ++`claude-restart-check` — and `ideation/` the brainstorm packets the proposals ++grew from. The commands, their tests and their CI are in ++[`opensoft/openRepoTools-code`](https://github.com/opensoft/openRepoTools-code). ++`openspec validate --strict` runs from this leg's root. ++ ++Licence: Apache-2.0. See [LICENSE](LICENSE). +diff --git a/docs/README-claude-current.md b/docs/README-claude-current.md +index 03687b9..36818dc 100644 +--- a/docs/README-claude-current.md ++++ b/docs/README-claude-current.md +@@ -272,6 +272,6 @@ The status line that calls it is workBenches' + + Both commands parse and run under bash 3.2, which is what macOS ships. Neither + uses `mapfile`, associative arrays, `readlink -f` or GNU-only flags. +-`tests/test_claude_current.py`, `tests/test_claude_restart_check.py` and +-`tests/test_lane_start_claude_current.py` are their suites, and every one of ++`code/tests/test_claude_current.py`, `code/tests/test_claude_restart_check.py` and ++`code/tests/test_lane_start_claude_current.py` are their suites, and every one of + them runs against fakes. None reaches npm or runs a real Claude Code. +-- +2.43.0 + From 465fa6d33a011f573e6ada66b7874d6cfd806953 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 00:34:55 +0000 Subject: [PATCH 9/9] T006's run C rebuild is written up: the collision inventory, 32 repaired links, the leg front doors, the 35-commit provenance correspondence and every bootstrap, validator, OpenSpec and Speckit result, with the assembly patches replaced by leaner ones that respect the root files' length caps. specs/004-migrate-to-triad/root-guidance-2026-10-08.md records T006 for opensoft/openRepoTools#186, rebuilt on run C (split 69b8924, spec 593250d, code 73b04d6) after the 2026-10-07 build was lost with /tmp. The assembly patches are replaced: code/tests/test_repo_hygiene.py caps AGENTS.md and README.md at today's 316 and 486 lines and admits lines only for a rule. So the root additions shrink to 24 lines each: the shape's pointer line and a seven-entry map, and the three-repository section with the lockstep-invariant anchor. The leg patches are unchanged. All five patches re-apply to identical trees, and `git diff --check` passes. Findings for lane 3, T007, T009, T010, T014 and T017 are in its section 10. Nothing lands on main from this task. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- ...t-s-front-door-resolves-the-split-s-.patch | 194 ++++---- ...leg-to-39c18877e042-in-openRepoTools.patch | 4 +- ...leg-to-6169e8e96164-in-openRepoTools.patch | 4 +- .../root-guidance-2026-10-08.md | 432 ++++++++++++++++++ 4 files changed, 514 insertions(+), 120 deletions(-) create mode 100644 specs/004-migrate-to-triad/root-guidance-2026-10-08.md diff --git a/specs/004-migrate-to-triad/patches/t006/assembly-0001-The-assembly-root-s-front-door-resolves-the-split-s-.patch b/specs/004-migrate-to-triad/patches/t006/assembly-0001-The-assembly-root-s-front-door-resolves-the-split-s-.patch index ec6d9c7b..f9b6d7e5 100644 --- a/specs/004-migrate-to-triad/patches/t006/assembly-0001-The-assembly-root-s-front-door-resolves-the-split-s-.patch +++ b/specs/004-migrate-to-triad/patches/t006/assembly-0001-The-assembly-root-s-front-door-resolves-the-split-s-.patch @@ -1,34 +1,29 @@ -From c0a8e1ea7164994fb8ba9fb76b93590ae7bff218 Mon Sep 17 00:00:00 2001 +From 33618121e18b4b0d720324a3b747033adde80f07 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> -Date: Thu, 8 Oct 2026 21:42:28 +0000 +Date: Fri, 9 Oct 2026 00:29:16 +0000 Subject: [PATCH 1/3] The assembly root's front door resolves the split's - collisions: AGENTS.md opens with the shape's pointer and a map of the three - repositories, README.md gains them and the lockstep invariant, and every path - that crossed into a leg names that leg. + collisions: AGENTS.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 opensoft/openRepoTools#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 openRepoShape 7f84ca4's - SHAPE_POINTER_LINE and its adopt follow-up require. The shape's rendered - block from shape/AGENTS.md follows (the three repositories). Then comes a - map of seven entries plus one rehearsal note: spec, code, this root, where - the tests run, where the lane tooling's code and manual are, paired feature - worktrees, and how a path reads. The note: a local rehearsal mounts legs - over the file transport, which git 2.43 refuses without - `-c protocol.file.allow=always`; the real https legs need nothing. The - original AGENTS.md follows unchanged except for 16 path repairs - (docs/ -> spec/docs/, openspec/ -> spec/openspec/, upstream/, contracts/, - tests/ and .github/ -> code/...). -- README.md: a "Three repositories" section (the table, the recursive clone - and `make bootstrap`), and "The lockstep invariant", which - contracts/{spec,code}-pin.yaml's `resync_runbook: +- 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`. + 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 @@ -37,14 +32,18 @@ contracts/shape-pin.yaml is touched. - 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. + Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- - .gitattributes | 43 +++++++++++----------------- - .gitignore | 6 ++++ - AGENTS.md | 78 +++++++++++++++++++++++++++++++++++++++----------- - README.md | 59 +++++++++++++++++++++++++++++++------- - 4 files changed, 133 insertions(+), 53 deletions(-) + .gitattributes | 43 +++++++++++++++----------------------- + .gitignore | 6 ++++++ + AGENTS.md | 56 +++++++++++++++++++++++++++++++++++--------------- + README.md | 44 ++++++++++++++++++++++++++++++--------- + 4 files changed, 96 insertions(+), 53 deletions(-) diff --git a/.gitattributes b/.gitattributes index 3560973..6dc2212 100644 @@ -114,60 +113,38 @@ index a160f49..2d04abf 100644 +# worktrees of the two legs, machine plumbing, never committed to this root. +/worktrees/ diff --git a/AGENTS.md b/AGENTS.md -index 443880a..d2dd7fb 100644 +index 443880a..f2509a2 100644 --- a/AGENTS.md +++ b/AGENTS.md -@@ -1,3 +1,49 @@ +@@ -1,3 +1,27 @@ +Read AGENTS-shape.md first — the rules of this repository's shape. + -+This is the assembly root of **openRepoTools** (`openrepotools`): the repository an -+engineer clones. It holds `project.yaml`, the pins and the gate, and it mounts -+the two legs. -+ -+| role | repository | path | -+|---|---|---| -+| assembly | `opensoft/openRepoTools` | `.` | -+| spec | `opensoft/openRepoTools-spec` | `spec/` | -+| code | `opensoft/openRepoTools-code` | `code/` | -+ -+Everything below this line is openRepoTools's own. The shape wrote the block above -+once and does not pin this file, so how this project is built, tested, reviewed -+and released belongs here and nothing upstream will overwrite it. -+ -+Where things are now that this project is three repositories. The text -+after this list is what this repository said before the split, with each path -+re-pointed at the leg that now holds it. ++This is the assembly root of a three-repository project. The text after ++this list is what this repository said before the split, each path re-pointed ++at the leg that now holds it; a path beginning `spec/` or `code/` is relative ++to this root. + -+- **`spec/`** (`opensoft/openRepoTools-spec`) holds what the project has -+ decided: `openspec/` (full proposals, their specs and archives), `specs/` -+ (the Speckit feature directories), `docs/` (the manuals) and `ideation/`. -+- **`code/`** (`opensoft/openRepoTools-code`) holds what ships: every command ++- **`spec/`**, `opensoft/openRepoTools-spec`: `openspec/`, `specs/`, `docs/` ++ (the manuals) and `ideation/`; `openspec` runs there. ++- **`code/`**, `opensoft/openRepoTools-code`: every command + `openRepoTools --install` places, `repos.tsv`, `commands/`, `skills/`, -+ `tests/`, its CI, and the pinned standard at `code/upstream/openRepoShape` -+ with `code/contracts/openreposhape-pin.yaml`. -+- **This root** holds the front door (this file, `CLAUDE.md`, `README.md`, -+ `LICENSE`), `project.yaml`, the leg pins and the shape's gate -+ (`make validate`), and no product code. -+- **The tests run in the code leg**: `code/tests/run.sh` from here, or -+ `tests/run.sh` inside `code/`, and the wrapper finds its own root either way. -+- **The lane tooling's code is in `code/` and its manual is in `spec/`**: -+ `spec/docs/README-lanes.md`, which is `docs/README-lanes.md` in -+ `opensoft/openRepoTools-spec` when that leg is cloned on its own. -+- **Feature work** happens in paired worktrees under -+ `worktrees//{spec,code}/`, which this root ignores and never -+ commits: full proposals and Speckit files in the `spec` one, a genuine -+ bounded amendment only in the `code` one's `features//openspec/`. -+- **A path that begins `spec/` or `code/`** in this file or `README.md` is -+ relative to this root. ++ `tests/`, its CI, and the pinned standard at `code/upstream/openRepoShape`. ++- **This root**: the front door, `project.yaml`, the leg pins and the gate ++ (`make validate`); no product code. ++- **Tests**: `code/tests/run.sh` from here, or `tests/run.sh` inside `code/`. ++- **The lane tooling**: its code is in `code/`; its manual is ++ `spec/docs/README-lanes.md` (`docs/README-lanes.md` in the spec leg). ++- **Feature work**: paired worktrees `worktrees//{spec,code}/`, ++ never committed here; full proposals and Speckit files in `spec`, a genuine ++ bounded amendment only in `code`'s `features//openspec/`. +- **A local rehearsal** whose `.gitmodules` names bare repositories on disk -+ mounts the legs over git's file transport, which current git refuses by -+ default: there, `git -c protocol.file.allow=always submodule update --init -+ --recursive`. The real legs are https and need no flag. ++ needs `git -c protocol.file.allow=always submodule update --init ++ --recursive`; the real legs are https and need no flag. + # Working in openRepoTools Three estate commands, `park`, `resume` and `status`; the lane tooling -@@ -19,7 +65,7 @@ person is driving the two INSTALLED commands. +@@ -19,7 +43,7 @@ person is driving the two INSTALLED commands. The lane tooling answers to a different document — the lane collision protocol at `$AGENT_PROTOCOL_ROOT/protocols/lane-collision-protocol.md` and its @@ -176,7 +153,7 @@ index 443880a..d2dd7fb 100644 data is not.** **A LANE HAS ONE BINDING** (Amendment 18, in force 2026-09-14): the `host`, -@@ -41,7 +87,7 @@ handoffs through `$AGENT_PROTOCOL_ROOT/workspace.yaml`'s `repository:` and +@@ -41,7 +65,7 @@ handoffs through `$AGENT_PROTOCOL_ROOT/workspace.yaml`'s `repository:` and unreadable workspace with **2**: only 2 blocks a prompt; 1 would let it through. Profile-only guard calls carrying exact `CLAUDE_NO_LANE=1` return 0 before input or identity probes, even with unreadable configuration; other values enforce. @@ -185,7 +162,7 @@ index 443880a..d2dd7fb 100644 Shared hooks stay installed. Exit 1 means *registry not found* in lane tooling, and *findings were printed* in estate status; identify the command before interpreting it. -@@ -197,13 +243,13 @@ sequence, and relay their per-repository lines rather than summarising them. +@@ -197,13 +221,13 @@ sequence, and relay their per-repository lines rather than summarising them. reads them all WITHOUT asking, because a read — fetched or not — moves nothing of yours; `status --all` from anywhere says the same thing. @@ -202,7 +179,7 @@ index 443880a..d2dd7fb 100644 somebody else's repository, checked out detached at a commit. A change the standard needs is a pull request THERE, then a pin bump here. 2. **Never pin a commit that is not on that repository's `main`.** -@@ -211,17 +257,17 @@ Three rules, and none of them is negotiable: +@@ -211,17 +235,17 @@ Three rules, and none of them is negotiable: pull request lands and the next person's fetch of it 404s. Check with `gh api repos/opensoft/openRepoShape/compare/main...` — `identical` or `behind`. @@ -224,7 +201,7 @@ index 443880a..d2dd7fb 100644 ``` Brett Heap's RULING of 2026-09-16 ("do all three", on the coordinator's three -@@ -230,7 +276,7 @@ Linux's 17-22, pull requests drawing 6-12 Copilot review rounds, and this +@@ -230,7 +254,7 @@ Linux's 17-22, pull requests drawing 6-12 Copilot review rounds, and this suite queuing up to 1h20 behind the workstation lock described below): 1. **`TESTS-MACOS` LEAVES THE PER-PUSH GATE.** On `pull_request` @@ -233,7 +210,7 @@ index 443880a..d2dd7fb 100644 `tests-no-submodule` and `tests-windows` only; `tests-macos` itself runs there only once the coordinator has put the `ready` label on the PR — the landing gate, applied once, right before the squash — and otherwise on -@@ -244,10 +290,10 @@ suite queuing up to 1h20 behind the workstation lock described below): +@@ -244,10 +268,10 @@ suite queuing up to 1h20 behind the workstation lock described below): and two are taken; a finding after that is filed as an issue and the PR lands regardless. 3. **CI IS THE SUITE OF RECORD.** A writer runs the shell-suite section they @@ -247,7 +224,7 @@ index 443880a..d2dd7fb 100644 run the whole suite locally, serialized, whenever a person wants that answer anyway, and `python3 -m pytest tests -q` by hand — unserialized against every other lane on the same workstation — is still the thing it -@@ -282,14 +328,14 @@ while [ "$(pgrep -f "$pat" | awk 'END { print NR + 0 }')" -gt 0 ]; do sleep 20; +@@ -282,14 +306,14 @@ while [ "$(pgrep -f "$pat" | awk 'END { print NR + 0 }')" -gt 0 ]; do sleep 20; PYTHONPYCACHEPREFIX="${XDG_CACHE_HOME:-$HOME/.cache}/openRepoTools/pycache" flock "${TMPDIR:-/tmp}/openrepotools-pytest.lock" python3 -m pytest tests -q -p no:cacheprovider ``` @@ -266,50 +243,36 @@ index 443880a..d2dd7fb 100644 macOS **bash 3.2** in CI, where `${x,,}`, `mapfile`, `declare -A` and `local -n` are syntax errors — write `tr '[:upper:]' '[:lower:]'` and a loop. diff --git a/README.md b/README.md -index 18c8f7e..2de8914 100644 +index 18c8f7e..8a65e41 100644 --- a/README.md +++ b/README.md -@@ -101,6 +101,39 @@ parked. No config, or no record, is a note. +@@ -101,6 +101,25 @@ parked. No config, or no record, is a note. That is all five layers: the local layer, `--fetch`, the fork, the pin and the record. +## Three repositories + -+This repository is the ASSEMBLY ROOT of a three-repository project — the shape -+[openRepoShape](https://github.com/opensoft/openRepoShape) calls the Triad. It -+holds this front door, `project.yaml`, the pins and the gate, and mounts the -+other two as submodules; it holds no product code of its own. -+ -+| role | repository | path | holds | -+|---|---|---|---| -+| assembly | `opensoft/openRepoTools` | `.` | the front door, the manifest, the pins, the gate | -+| spec | `opensoft/openRepoTools-spec` | `spec/` | `openspec/`, `specs/`, `docs/` (the manuals), `ideation/` | -+| code | `opensoft/openRepoTools-code` | `code/` | every command below, `repos.tsv`, `skills/`, `commands/`, `tests/`, CI, the pinned standard | -+ -+```sh -+git clone --recurse-submodules https://github.com/opensoft/openRepoTools.git -+cd openRepoTools -+make bootstrap # each leg on main AT its pinned commit, then the validators -+``` -+ -+Nothing a person types changes: the commands keep their names and the install -+one-liner below is the same line. A path in this file that begins `spec/` or -+`code/` is relative to this root. `AGENTS-shape.md` is the shape's rules for an -+agent working here, and electing the shape confers nothing. ++This is the ASSEMBLY ROOT of a three-repository project, the shape ++[openRepoShape](https://github.com/opensoft/openRepoShape) calls the Triad: it ++holds this front door, `project.yaml`, the pins and the gate, and mounts ++`opensoft/openRepoTools-spec` at `spec/` (`openspec/`, `specs/`, the manuals in ++`docs/`, `ideation/`) and `opensoft/openRepoTools-code` at `code/` (every ++command below, `repos.tsv`, `skills/`, `commands/`, `tests/`, CI and the pinned ++standard). Clone it with `git clone --recurse-submodules` and run ++`make bootstrap`. Nothing a person types changes, and a path in this file that ++begins `spec/` or `code/` is relative to this root. + +### The lockstep invariant + -+For each leg, three things name the same commit and move in ONE commit here: -+the gitlink at the leg's path, `commit:` in `contracts/-pin.yaml`, and -+every `.github/workflows/*.yml` `@` that names the leg. A change to a leg -+lands in that leg first, by pull request; this root then advances its pin with -+openRepoShape's `scripts/bump-leg.py`, and `make pins` refuses when the three -+disagree. ++A leg's gitlink, `commit:` in `contracts/-pin.yaml` and every workflow ++`@` naming that leg move in ONE commit here, after the leg's own pull ++request lands: openRepoShape's `scripts/bump-leg.py` writes it, and ++`make pins` refuses when the three disagree. + ## The lane tooling `lane-start`, `lane-end`, `lanes-edit.sh` and `link-estates` are here too, with -@@ -154,7 +187,7 @@ and 5 put it and where it stays. What moved here is the CODE. A `-wip` is +@@ -154,7 +173,7 @@ and 5 put it and where it stays. What moved here is the CODE. A `-wip` is a private repository whose own rules file says "No secrets and no code", nobody would look inside somebody else's for a command, and until this move the four helpers existed in exactly one place in the world: one person's private @@ -318,7 +281,7 @@ index 18c8f7e..2de8914 100644 Every one of them finds that data through `$AGENT_PROTOCOL_ROOT/workspace.yaml` — `repository:` and `path:`, the same pointer file `resume` and `status` -@@ -180,7 +213,7 @@ different hat). +@@ -180,7 +199,7 @@ different hat). Then it clones the repository, seeds it from openRepoShape's `templates/workspace-root/` at the commit pinned in @@ -327,19 +290,18 @@ index 18c8f7e..2de8914 100644 also the check that matters**: a new repository is inside the organisation's PR-only ruleset until an administrator excludes it, and until it is, every register write will be refused by that gate. Better to meet it here than at -@@ -237,6 +270,11 @@ gh api repos/opensoft/openRepoTools/contents/openRepoTools \ +@@ -237,6 +256,10 @@ gh api repos/opensoft/openRepoTools/contents/openRepoTools \ -H 'Accept: application/vnd.github.raw' | bash -s -- --install ``` -+Both lines are unchanged by the split into three repositories: the -+`openRepoTools` they fetch is this root's entry point, and the installer it -+hands over to is `code/openRepoTools` in `opensoft/openRepoTools-code`, at the -+commit this root pins. ++Both lines are unchanged by the split: the `openRepoTools` they fetch is this ++root's entry point, which hands over to the installer `code/openRepoTools` at ++the commit this root pins. + It places SEVENTEEN files into `~/.local/bin` — `openRepoTools`, `park`, `resume`, `status`, `lane`, `lanes`, `lane-handoff`, `lane-rename`, `lanes-edit.sh`, `lane-start`, `lane-end`, `link-estates`, the alias table `repos.tsv`, -@@ -260,7 +298,7 @@ that would not take it, and a create needs a directory's search bit as well as +@@ -260,7 +283,7 @@ that would not take it, and a create needs a directory's search bit as well as its write bit. A refusal creates none of those directories either. `cp` follows a symlink, and an install through one leaves the command uninstalled and writes these bytes into whatever it points at. Then a `17 of 17 placed in ` line, @@ -348,7 +310,7 @@ index 18c8f7e..2de8914 100644 It also places **fourteen things that are not files in that directory**: THREE SKILLS, `/handoff`, `/lane-swap` and `/restart`, each at -@@ -300,7 +338,7 @@ arm keys on the VERB and not on the word anywhere in a path. It +@@ -300,7 +323,7 @@ arm keys on the VERB and not on the word anywhere in a path. It never writes a profile's own `settings.json`: the launcher owns that one. Thirty-one artifacts, and the count is the invariant. It was sixteen until A11 @@ -357,7 +319,7 @@ index 18c8f7e..2de8914 100644 in it, at the same pair of paths a skill takes — because `opensoft/workBenches#74` deletes the launcher's copy and `/swap` would otherwise be installed by nobody; eighteen until Amendment 12 adoption act 3 added the name guard's entry; and -@@ -414,19 +452,19 @@ the way in is WSL2, exactly as it is for openRepoShape's `setup.sh`. +@@ -414,19 +437,19 @@ the way in is WSL2, exactly as it is for openRepoShape's `setup.sh`. **Tools depend on the shape; the shape depends on nothing.** This repository pins the openRepoShape commit its tests were verified against, as a submodule @@ -380,7 +342,7 @@ index 18c8f7e..2de8914 100644 agree. Four of its checks need no submodule at all — the gitlink is read out of `git ls-tree HEAD` and nothing imports from the checkout — so the lockstep check bites in a clone that never materialized it. -@@ -434,6 +472,7 @@ check bites in a clone that never materialized it. +@@ -434,6 +457,7 @@ check bites in a clone that never materialized it. ### Bumping the pin ```sh @@ -388,7 +350,7 @@ index 18c8f7e..2de8914 100644 git -C upstream/openRepoShape fetch origin git -C upstream/openRepoShape checkout <40 hex on openRepoShape's main> python3 -c "import sys; sys.path.insert(0, 'upstream/openRepoShape/scripts'); from repo_shape import tree_digest; print(tree_digest('upstream/openRepoShape', 'HEAD'))" -@@ -446,7 +485,7 @@ git commit +@@ -446,7 +470,7 @@ git commit **The edit comes before the `git add`, and the block is in that order for a reason.** Staging first and editing after stages the moved gitlink with the OLD pin file — and that is invisible where you are standing, because @@ -397,7 +359,7 @@ index 18c8f7e..2de8914 100644 gitlink out of `git ls-tree HEAD`: green in your checkout, red in CI, which is the wrong way round. The pin and the gitlink are one fact in two places. The digest is recomputed with the PINNED CHECKOUT'S OWN `scripts/repo_shape.py` -@@ -461,10 +500,10 @@ orphaned and the next person's fetch of it 404s. +@@ -461,10 +485,10 @@ orphaned and the next person's fetch of it 404s. ```sh git clone --recurse-submodules https://github.com/opensoft/openRepoTools.git diff --git a/specs/004-migrate-to-triad/patches/t006/assembly-0002-Bump-spec-leg-to-39c18877e042-in-openRepoTools.patch b/specs/004-migrate-to-triad/patches/t006/assembly-0002-Bump-spec-leg-to-39c18877e042-in-openRepoTools.patch index 54f88486..c0d9ea5d 100644 --- a/specs/004-migrate-to-triad/patches/t006/assembly-0002-Bump-spec-leg-to-39c18877e042-in-openRepoTools.patch +++ b/specs/004-migrate-to-triad/patches/t006/assembly-0002-Bump-spec-leg-to-39c18877e042-in-openRepoTools.patch @@ -1,6 +1,6 @@ -From 1c7e74f9902815c764c214d353694af4cbb75598 Mon Sep 17 00:00:00 2001 +From 1024f186cce1b0c03a295ae6514c43ce6fd52a31 Mon Sep 17 00:00:00 2001 From: openRepoShape bump-leg -Date: Thu, 8 Oct 2026 21:42:37 +0000 +Date: Fri, 9 Oct 2026 00:29:17 +0000 Subject: [PATCH 2/3] Bump spec leg to 39c18877e042 in openRepoTools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/specs/004-migrate-to-triad/patches/t006/assembly-0003-Bump-code-leg-to-6169e8e96164-in-openRepoTools.patch b/specs/004-migrate-to-triad/patches/t006/assembly-0003-Bump-code-leg-to-6169e8e96164-in-openRepoTools.patch index f5f550d7..a7eb0ad2 100644 --- a/specs/004-migrate-to-triad/patches/t006/assembly-0003-Bump-code-leg-to-6169e8e96164-in-openRepoTools.patch +++ b/specs/004-migrate-to-triad/patches/t006/assembly-0003-Bump-code-leg-to-6169e8e96164-in-openRepoTools.patch @@ -1,6 +1,6 @@ -From 7cde50ac1f7926b243eb0566ae0dc45d1540ab18 Mon Sep 17 00:00:00 2001 +From 3e87e3941ff947d8160fa7d8b6204670af7926df Mon Sep 17 00:00:00 2001 From: openRepoShape bump-leg -Date: Thu, 8 Oct 2026 21:42:38 +0000 +Date: Fri, 9 Oct 2026 00:29:18 +0000 Subject: [PATCH 3/3] Bump code leg to 6169e8e96164 in openRepoTools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/specs/004-migrate-to-triad/root-guidance-2026-10-08.md b/specs/004-migrate-to-triad/root-guidance-2026-10-08.md new file mode 100644 index 00000000..ed610850 --- /dev/null +++ b/specs/004-migrate-to-triad/root-guidance-2026-10-08.md @@ -0,0 +1,432 @@ +# T006: root and leg guidance, collisions, and workflow bootstrap (run C rebuild) + +**Task**: T006 of [tasks.md](tasks.md), for opensoft/openRepoTools#186. +**Writer**: lane openRepoTools-1, on 2026-10-08 and 2026-10-09. +**Evidence branch**: `004-migrate-to-triad-root-guidance`. +**State**: rehearsal evidence only. Nothing here lands on `main`, and today's layout is untouched. + +The 2026-10-07 build of this task ran against run B. It was lost when the bench +restart cleared `/tmp`: clones, patches and logs alike. Nothing from it was ever +committed. This document is its rebuild on **run C**, the rehearsal regenerated +on 2026-10-08. Everything below was measured again on run C, and none of it is +carried over from run B. + +## Inputs and outputs + +| what | identity | +|---|---| +| source (`main` when run C cloned it) | `837928a` (includes #187's planning files and #188's openRepoShape repin) | +| rehearsed assembly split (`adopt/three-repo-shape`) | `69b8924` | +| spec leg / code leg (run C bare remotes) | `593250d` (35 commits) / `73b04d6` (88 commits) | +| code's nested `upstream/openRepoShape`, and the tool | `7f84ca4` (opensoft/openRepoShape `main`) | +| **code leg patch** (my bare copy, branch `t006/leg-guidance`) | `6169e8e` | +| **spec leg patch** (my bare copy, branch `t006/leg-guidance`) | `39c1887` | +| **assembly patch** (my bare copy, branch `t006/root-guidance-c2`) | `3361812`, then `bump-leg.py`'s `1024f18` (spec) and `3e87e39` (code) | + +All work happened in host-local private scratch: my own bare copies of the +three run C remotes and of the openRepoShape mirror. Nothing was written into +the rehearsal directory, and nothing reached a real remote except this +evidence branch. + +The patches are in [patches/t006/](patches/t006/), as `git format-patch` +output: + +| patch | sha256 | +|---|---| +| `assembly-0001-…front-door…` | `cd69c2a9afb7bb11…` | +| `assembly-0002-Bump-spec-leg-to-39c18877e042…` | `574c0df16dddda7d…` | +| `assembly-0003-Bump-code-leg-to-6169e8e96164…` | `a7f8521bb7dbb9b5…` | +| `code-0001-…own-front-door…` | `74b98ff97b0b9b6b…` | +| `spec-0001-…own-front-door…` | `c11070f05a9e4611…` | + +Each was applied with `git am` onto its base in a fresh clone, and each +reproduced the pushed tree exactly: + +| patch set | `git am` | tree check | `git diff --check` on the range | +|---|---|---|---| +| assembly | exit 0 | `6eb8145e…` = `6eb8145e…` | exit 0 | +| code | exit 0 | `ca65c3b9…` = `ca65c3b9…` | exit 0 | +| spec | exit 0 | `10256135…` = `10256135…` | exit 0 | + +`assembly-0002` and `-0003` are commits written by openRepoShape's +`bump-leg.py`, and they pin the rehearsal's leg hashes. At T014 they are +**regenerated against the real legs with the same tool, not applied**. +`assembly-0001`, `code-0001` and `spec-0001` are the portable patches. + +The evidence branch's first commit, `f06e1af`, recorded an earlier `assembly-0001`. +Its root additions were 46 and 39 lines. The hygiene caps in section 7 made +those too long, so this commit replaces the three assembly patches with the +c2 versions. The legs' patches are unchanged. + +## 1. Collision inventory at the assembly root + +The root tree after run C's split, path by path. The front-door blobs are the +monorepo's own: `AGENTS.md` 443880a, `README.md` 18c8f7e, `.gitattributes` +3560973 and `.gitignore` a160f49, all equal to `main`'s. + +| path | class | resolution in this patch set | +|---|---|---| +| `AGENTS.md` | **collision** (shape wrote `shape/AGENTS.md`) | Line 1 is the shape's exact pointer, then a short map, then the original text with 16 path repairs. This is the shape's own follow-up: "add the line … to the existing AGENTS.md rather than replacing it". | +| `CLAUDE.md` | **collision** (shape wrote `shape/CLAUDE.md`) | Unchanged. `Read AGENTS.md.` is byte-identical to the shape's own rendered `shape/CLAUDE.md` (`cmp` equal), so the pointer reaches it through `AGENTS.md` line 1. The shape's generic follow-up text ("add the line to the existing CLAUDE.md") is satisfied the way the shape's own scaffold satisfies it. | +| `README.md` | **collision** (shape wrote `shape/README.md`) | Merged. A "Three repositories" section and "The lockstep invariant" are added. `contracts/{spec,code}-pin.yaml` name `resync_runbook: "README.md#the-lockstep-invariant"`, an anchor that only `shape/README.md` had. Plus 12 path repairs and a re-rooted pin-bump block. | +| `.gitignore` | **collision** (shape wrote `shape/.gitignore`, pinned) | The project's file absorbs the shape's two extra lines, `.venv/` and `.DS_Store`, plus `/worktrees/`, the line the three-leg bootstrap requires. Nothing was lost: the root kept `__pycache__/`, `*.py[cod]` and `.pytest_cache/`. | +| `.gitattributes` | **collision** (shape wrote `shape/.gitattributes`, pinned) | Same rule, `* text=auto eol=lf`, with the comment rewritten for this root's pinned copies. The monorepo's reason (the installer's byte-for-byte comparisons) moved, word for word, to `code/.gitattributes`. | +| `LICENSE` | front door | Unchanged. A byte-identical copy is added to each leg, which had none. | +| `shape/.gitignore`, `shape/.gitattributes` | shape-generated, **pinned** (rows in `contracts/shape-pin.yaml`) | Left in place. Deleting or editing them is drift. `update-shape.py` knows a collision copy by its `shape/` row. | +| `shape/AGENTS.md`, `shape/CLAUDE.md`, `shape/README.md` | shape-generated, **not pinned** (no row; verified) | Left in place under this task's rule. The shape's own follow-up says to delete `shape/AGENTS.md` and `shape/CLAUDE.md` once the pointer line is in `AGENTS.md`, which this patch does, and to merge `shape/README.md`, which this patch also does. Removing all three is drift-free. **Decision for T014's reviewer.** `shape/README.md` also carries the rehearsal's local clone path, an artifact of rehearsing with local remotes. | +| `AGENTS-shape.md`, `Makefile`, `scripts/{bootstrap,repo_shape,validate-manifest,validate-pins,validate-repository-naming}.py`, `contracts/repository-naming.yaml`, `.github/workflows/validate.yml` | shape-generated, **pinned** | Untouched. 11 rows; all 11 digests match before and after. | +| `project.yaml`, `contracts/{spec,code,shape}-pin.yaml`, `.gitmodules` (the two mounts) | shape-generated, rendered | Untouched. The leg pins move only through `bump-leg.py`. | +| `spec`, `code` gitlinks | shape-generated | Moved only by `bump-leg.py`, each together with its pin. | +| `contracts/`, `.github/`, `.gitmodules` as **names** | not collisions | The source's `contracts/openreposhape-pin.yaml`, `.github/workflows/tests.yml` and `.gitmodules` left whole for the code leg. The root's same-named files are the shape's. No source path exists at `shape/`, `scripts/`, `Makefile`, `project.yaml`, `worktrees/` or `.specify/` (checked at `837928a`). | +| (lost by the split) | **legs' front door** | Neither leg has `AGENTS.md`, `CLAUDE.md`, `README.md`, `LICENSE`, `.gitattributes` or `.gitignore`. A superproject's attributes and ignore rules never reach into a submodule, so the code leg lost the LF rule its installer depends on. Section 4 restores them. | + +## 2. Root agent instructions + +- **`AGENTS.md`**, 316 → 340 lines. + - Line 1 is `Read AGENTS-shape.md first — the rules of this repository's shape.`, the exact `SHAPE_POINTER_LINE` of openRepoShape 7f84ca4. + - A seven-entry map follows: + - `spec/`, with `openspec` run there; + - `code/`, with the pinned standard; + - this root; + - where the tests run; + - the lane tooling's code (`code/`) and its manual (`spec/docs/README-lanes.md`, or `docs/README-lanes.md` in `opensoft/openRepoTools-spec` standalone); + - paired feature worktrees, with full proposals in spec and bounded amendments in code; + - the local-rehearsal file-transport flag (the real https legs need none). + - The original text follows, unchanged except for the 16 repairs in section 3. + - The shape's 14-line rendered repository table is **not** repeated: the map names the same repositories, and the repository's length cap (section 7) admits lines only for a rule. +- **`README.md`**, 486 → 510 lines. + - The front door is kept. + - "Three repositories" says what the root is, what each leg holds, the recursive clone and `make bootstrap`, that nothing a person types changes, and how paths read. + - "The lockstep invariant" is the anchor the leg pins name. + - The install section adds: "Both lines are unchanged by the split: the `openRepoTools` they fetch is this root's entry point, which hands over to the installer `code/openRepoTools` at the commit this root pins." The entry point itself is T007's. +- **`CLAUDE.md`**: unchanged, as briefed. + +## 3. Repaired links (32) + +Line numbers refer to the original blobs at `69b8924`. Every repair was +asserted to match exactly once. + +| file:line | was | now | +|---|---|---| +| AGENTS.md:22 | `docs/README-lanes.md` | `spec/docs/README-lanes.md` | +| AGENTS.md:44 | `openspec/changes/skip-no-lane-guard/` | `spec/openspec/changes/skip-no-lane-guard/` | +| AGENTS.md:200 | heading `upstream/openRepoShape` | `code/upstream/openRepoShape` | +| AGENTS.md:203 | `contracts/openreposhape-pin.yaml` | `code/contracts/openreposhape-pin.yaml` | +| AGENTS.md:206 | `upstream/openRepoShape` | `code/upstream/openRepoShape` | +| AGENTS.md:214 | `contracts/openreposhape-pin.yaml` | `code/contracts/openreposhape-pin.yaml` | +| AGENTS.md:216 | `tests/test_upstream_pin.py` | `code/tests/test_upstream_pin.py` | +| AGENTS.md:223 | `git submodule update --init upstream/openRepoShape` | `git -C code submodule update --init upstream/openRepoShape` | +| AGENTS.md:224 | `tests/run.sh` | `code/tests/run.sh` (the wrapper `cd`s to its own root, so it runs from here) | +| AGENTS.md:233 | `.github/workflows/tests.yml` | `code/.github/workflows/tests.yml` | +| AGENTS.md:247 | `bash tests/test_lane_helpers.sh` | `bash code/tests/test_lane_helpers.sh` (it derives `SRC_DIR` from its own path) | +| AGENTS.md:248, 250, 285 | `tests/run.sh` | `code/tests/run.sh` | +| AGENTS.md:291 | `tests/test_lane_helpers.sh` | `code/tests/test_lane_helpers.sh` | +| AGENTS.md:292 | `tests/test_lane_helpers_suite.py` | `code/tests/test_lane_helpers_suite.py` | +| README.md:157 | `docs/README-lanes.md`, `docs/README-claude-current.md` | `spec/docs/…` (2) | +| README.md:183 | `contracts/openreposhape-pin.yaml` | `code/contracts/openreposhape-pin.yaml` | +| README.md:263 | `tests/run.sh` | `code/tests/run.sh` | +| README.md:303 | `commands/swap.md` | `code/commands/swap.md` | +| README.md:417 | `upstream/openRepoShape`, `contracts/openreposhape-pin.yaml` | `code/…` (2) | +| README.md:422 | `tests/test_park_resume_commands.py` | `code/tests/test_park_resume_commands.py` | +| README.md:429 | `tests/test_upstream_pin.py` | `code/tests/test_upstream_pin.py` | +| README.md:437–442 | the pin-bump block (`upstream/openRepoShape` ×4, `contracts/openreposhape-pin.yaml` ×2) | re-rooted with one added line, `cd code`, because the pin and gitlink are a commit in the code leg | +| README.md:449 | `tests/test_upstream_pin.py` | `code/tests/test_upstream_pin.py` | +| README.md:464 | `cd openRepoTools && python3 -m pytest tests -q` | `cd openRepoTools/code && …` | +| README.md:467 | `git submodule update --init upstream/openRepoShape` | `git -C code submodule update --init upstream/openRepoShape` (the command the suite's skip message names, run in the code leg) | +| spec `docs/README-claude-current.md`:275–276 | `tests/test_claude_current.py`, `tests/test_claude_restart_check.py`, `tests/test_lane_start_claude_current.py` | `code/tests/…` (3) | + +That is 16 repairs in `AGENTS.md`, 13 in `README.md` (12 rewrites and the +re-rooted block) and 3 in the spec leg. All six markdown links in the spec leg +that use relative `](…)` targets stay inside the spec leg, so none crossed a leg. + +**Deliberately unchanged:** +- `AGENTS.md`'s `python3 -m pytest tests -q …` lines describe the command line `tests/run.sh` itself runs, inside the code leg, which is what every other lane's `pgrep` counts. +- `contracts/shape-pin.yaml` (AGENTS.md:118, README.md:46) names every estate's shape pin, not a path into a leg. +- README.md:242 `repos.tsv` is a file installed into `~/.local/bin`. +- README.md:41 `upstream/` is a git remote. + +## 4. Leg guidance added + +The shape does provide leg guidance. `templates/code-root/` and +`templates/spec-root/` carry `AGENTS.md`, `CLAUDE.md`, `README.md` and +`.gitignore`. `scaffold-project.py` writes them, and `adopt-project.py` seeds them +only into an empty leg, so an extracted leg gets none. The leg files below are those +templates, rendered by the shape's own `shape_materialize.copy_tree` with this +project's values; no placeholder is left. Each `AGENTS.md` and `README.md` then +appends one short openRepoTools section. + +| leg | files | openRepoTools section says | +|---|---|---| +| code | `AGENTS.md`, `CLAUDE.md` ("Read AGENTS.md."), `README.md`, `.gitattributes` (monorepo blob 3560973, byte for byte), `.gitignore` (the shape's code-root template, a superset of the monorepo's three lines), `LICENSE` (blob 261eeb9) | The root's guidance governs. The suite runs here: `git submodule update --init upstream/openRepoShape`, then `tests/run.sh` behind `${TMPDIR:-/tmp}/openrepotools-pytest.lock`. The pinned standard's three rules, by pointer. The manuals and specifications are `../spec/` when mounted and `opensoft/openRepoTools-spec` standalone. A feature's code goes in its code worktree, a bounded amendment in `features//openspec/`. | +| spec | `AGENTS.md`, `CLAUDE.md`, `README.md`, `.gitattributes` (the LF rule the monorepo's root file applied to these documents), `LICENSE` | It holds `openspec/`, `specs/`, `docs/` and `ideation/`. Full proposals and canonical Speckit files live here; bounded amendments live in the code leg's feature folder, under the shared workflow's "Triad Feature Amendments". `openspec validate --strict` runs here, never at the root, and a local root takes precedence over a store pointer. A pre-split path names a code-leg file (`code/…` once repaired), and a cited commit is the assembly's. | + +`git add --renormalize .` changes nothing in either leg. Every file is LF: +code 54 + 1 gitlink, spec 70. + +The spec leg gets no `.gitignore`. Nothing ignorable was lost there, and the +doctor's row for it is a `note`, which the shape's own agent rules say is +"not a finding and you do not 'fix' it". + +**Shape agreement, measured:** +- **Doctor `leg shape files`.** Before: 6 files missing (AGENTS/CLAUDE/.gitignore × 2). After: "spec: AGENTS.md rendered, CLAUDE.md identical, .gitignore absent; code: AGENTS.md rendered, CLAUDE.md identical, .gitignore identical". +- **`scripts/shape_advisory.py`'s leg-clone reader on standalone clones.** Before: recognised by name only (`openRepoTools-code` … "is the code-leg form"). After: "its AGENTS.md is the code leg's, as templates/code-root/ writes it", with the assembly `opensoft/openRepoTools` named. The spec leg reads the same way. +- **Doctor `placement` finding.** Identical before and after: the same 21 flagged paths, `misplaced: 3`, `review_required: 18`. It comes from the adoption plan's reasoned overrides for `commands/`, `skills/` and `contracts/`, and none of the 11 added leg files is flagged. Per the shape's rule 4 it is relayed, not answered. + +## 5. Cross-leg references this task does not repair, with owners + +- **Spec records (approval provenance, FR-014): kept as written.** + - Pre-split code paths appear in 17 files under `openspec/` and `specs/`. + - In completed features (002, 003 and the four other changes) they record acts done against the monorepo at a commit. Example: specs/002 tasks.md:9, "`bash tests/run.sh -k …` passed 165 checks … on implementation commit `64f4e93`". Rewriting such a line would falsify the record. + - **Feature 004's own working files** carry 23 mentions in 9 files: plan.md 4, installer-design.md 4, proposal.md 4, design.md 3, adoption-plan.yaml 3, tasks.md 2, adopter-rehearsal.md 2, and spec.md, quickstart.md, preparation.md and adopter-blocker.md with 1 each. These are T017's to translate with the feature. + - 005's `analysis.md`:13 is likewise T016/T017's. +- **`docs/README-lanes.md` (lane 3: the manual's content).** 21 lines name code-leg files: + - lines 72, 572, 3601, 4163: `tests/test_lane_helpers.sh`; + - lines 573, 576, 594, 3777, 3979, 3997: `tests/run.sh`; + - line 3601 (again): `tests/test_lanes_index.py`; + - lines 74, 637, 1013, 1062, 1236, 2369, 2477, 4162: `repos.tsv` (some of these mean the installed table); + - lines 2551–2553: `skills/handoff/SKILL.md`, `skills/lane-swap/SKILL.md`, `commands/{ctx,handoff,swap}.md`. +- **Code-leg citations of the manuals: 23 lines in 8 files.** + - `lane-start` 2 and `lanes-edit.sh` 1, plus `tests/test_lane_helpers.sh` 4 (lines 3730, 3738, 6222, 6228), go to **lane 3**. + - `openRepoTools` 1 (line 1218, the `venv_note` comment) goes to **T007**, which patches that file. + - `tests/test_repo_hygiene.py` 11, `tests/test_lane_start_claude_current.py` 1 and `tests/run.sh` 1 (comment, line 36) go to **T009/T010**. + - `claude-current` 2: line 89 is a comment, and line 123 is `--help` output, so changing it changes the shipped command. Its owner is whoever owns `claude-current`'s text; it is not changed here. + - A comment-only repair ahead of the functional change beside it would leave the comment disagreeing with the code. +- **Code-leg OpenSpec object keys (lane 3).** `lanes-edit.sh` (12 lines, e.g. 280, 2770, 2814–2827, 2895–2897) and `lane-handoff`:2439 key a claim as `owner/repo:openspec/changes/`. After the split a change lives in `opensoft/openRepoTools-spec`, not the assembly. +- **External referrers (lane 2's reads; follow-ups, not edited here).** From lane 2's `consumer-reads-2026-10-07.md` on `004-migrate-to-triad-reads` (76dcb93, which contains ae70dc5); line numbers are as lane 2 recorded them. + - **brett-wip:** + - `lanes/LANES.md`:36 and :1305; + - 12 `docs/README-lanes` lines in 9 files (first: `handoffs/openRepoTools/brief-lane-openRepoTools-1-2026-09-16.md`:9, `session-handoff-2026-09-16-lane-openRepoTools-1.md`:33, `lanes/log/openRepoTools-1.md`:22); + - `lanes/repos.tsv`:52, which has no leg aliases. + - **`~/.agents`:** + - `protocols/lane-collision-protocol-amendment-9.md`:735 and 943, `-11.md`:1511, `-12.md`:315, `-14.md`:185, `-17.md`:175 and `-18.md`:222, all naming the manual; + - `AGENTS.md`:273-278 ("places from `opensoft/openRepoTools`"); + - `AGENTS.md`:327 and others ("the code lands in `opensoft/openRepoTools`"). + - **openRepoShape 7f84ca4:** + - `templates/workspace-root/AGENTS.md`:32, 71-76, 91-95, plus `README.md`:16,18,31,58, `handoffs/README.md`:24, `lanes/LANES.md`:19,40, `lanes/log/README.md`:4,6 and `.gitattributes`:8, all in that template; + - `scripts/shape_advisory.py`:509; + - the `--install` pointer at `openRepoShape`:279-280. + - **workBenches 7fd01e3:** + - `README.md`:188 and 190; + - `docs/claude-multi-account-profiles.md`:314; + - the vendored openrepotools tree and its `upstream-pin.yaml` rows. + - **Installed handoff skill:** `~/.claude/skills/handoff/SKILL.md`:329, whose writer-poll globs do not cover `/worktrees/`. + +## 6. Provenance correspondence (FR-014) + +From run C's own filter-repo `commit-map`, copied read-only. For every +spec-leg commit, every path that commit changed was compared blob for blob +with its original commit: **35 of 35 match, 0 mismatches**. + +Correspondence is deterministic. The first 34 spec commits have exactly the +hashes run B produced (for example `30d7bf7`, `b4e1cb7`, `558a12b`). Only +`593250d`, from #187's `44983ce`, is new. Re-extracting the same history +reproduces the same leg identities. + +| # | spec leg (run C) | original (assembly history) | date | paths | of which openspec/ or specs/ | blob check | +|---|---|---|---|---|---|---| +| 1 | `af9806736072` | `3fc62156e798` | 2026-09-10 | 1 | 0 | ok=1 bad=0 | +| 2 | `7ab0b61163ec` | `9e3e241f4f3c` | 2026-09-10 | 1 | 0 | ok=1 bad=0 | +| 3 | `1a12f36f7b5e` | `792a9dede1ec` | 2026-09-10 | 1 | 0 | ok=1 bad=0 | +| 4 | `a1ebef63f4a6` | `3179f50d4d6c` | 2026-09-10 | 1 | 0 | ok=1 bad=0 | +| 5 | `46ef1be6a9af` | `e6044d3a4189` | 2026-09-12 | 1 | 0 | ok=1 bad=0 | +| 6 | `780b66740b1a` | `2108ce3ab6f7` | 2026-09-12 | 1 | 0 | ok=1 bad=0 | +| 7 | `e26759dffc6d` | `bd27fc346682` | 2026-09-13 | 1 | 0 | ok=1 bad=0 | +| 8 | `422f7f8b6ef4` | `9000e86796b2` | 2026-09-13 | 1 | 0 | ok=1 bad=0 | +| 9 | `f53e46c45137` | `f97530bb936e` | 2026-09-13 | 1 | 0 | ok=1 bad=0 | +| 10 | `cfae3d330bae` | `63a74af1b741` | 2026-09-14 | 1 | 0 | ok=1 bad=0 | +| 11 | `f245d9f16c86` | `8345d2f1dc42` | 2026-09-14 | 1 | 0 | ok=1 bad=0 | +| 12 | `7a76cad941fe` | `a95443591ab7` | 2026-09-14 | 1 | 0 | ok=1 bad=0 | +| 13 | `52acec4817a2` | `2faa883db081` | 2026-09-14 | 1 | 0 | ok=1 bad=0 | +| 14 | `17ba18660c6a` | `fcccbe08b1bd` | 2026-09-14 | 1 | 0 | ok=1 bad=0 | +| 15 | `6d8aa95504bb` | `f98d7347b8b3` | 2026-09-15 | 1 | 0 | ok=1 bad=0 | +| 16 | `21c1e268489d` | `9449557495b3` | 2026-09-15 | 1 | 0 | ok=1 bad=0 | +| 17 | `30d7bf7dc109` | `f9e63f44dc37` | 2026-09-15 | 6 | 5 | ok=6 bad=0 | +| 18 | `6a9f75c6f1a9` | `4c2ffefdec64` | 2026-09-16 | 1 | 0 | ok=1 bad=0 | +| 19 | `219184864a87` | `4218e6dacdaf` | 2026-09-23 | 1 | 0 | ok=1 bad=0 | +| 20 | `b4e1cb7a7797` | `a7d125716829` | 2026-09-30 | 9 | 8 | ok=9 bad=0 | +| 21 | `4fe91d309f75` | `cbb59813ceea` | 2026-09-30 | 3 | 3 | ok=3 bad=0 | +| 22 | `ac36505644f4` | `6faed35f418c` | 2026-10-02 | 1 | 0 | ok=1 bad=0 | +| 23 | `525a95dd752d` | `c1bac0dfc99a` | 2026-10-03 | 1 | 0 | ok=1 bad=0 | +| 24 | `03818f50c581` | `14641fd67cee` | 2026-10-03 | 1 | 0 | ok=1 bad=0 | +| 25 | `993d1e1d01c9` | `daed20957f2d` | 2026-10-04 | 1 | 0 | ok=1 bad=0 | +| 26 | `fa21860f9063` | `b1015f4672f9` | 2026-10-04 | 12 | 5 | ok=12 bad=0 | +| 27 | `705058619ba5` | `fea9a2904ce0` | 2026-10-05 | 1 | 0 | ok=1 bad=0 | +| 28 | `75ada176939b` | `00971b929f58` | 2026-10-06 | 18 | 10 | ok=18 bad=0 | +| 29 | `2374d2ec8f8f` | `f5444c5e1f3c` | 2026-10-06 | 1 | 0 | ok=1 bad=0 | +| 30 | `0c2b499dc1b4` | `2080f3d83520` | 2026-10-06 | 1 | 0 | ok=1 bad=0 | +| 31 | `74e48d3b605b` | `500687c4b746` | 2026-10-06 | 1 | 0 | ok=1 bad=0 | +| 32 | `659087420e44` | `5eb3d5f816f8` | 2026-10-06 | 1 | 0 | ok=1 bad=0 | +| 33 | `c9b870270cf5` | `2fb854f12855` | 2026-10-06 | 1 | 0 | ok=1 bad=0 | +| 34 | `558a12b67400` | `c4864ac5e59d` | 2026-10-07 | 1 | 0 | ok=1 bad=0 | +| 35 | `593250d600ca` | `44983ce250ab` | 2026-10-08 | 20 | 20 | ok=20 bad=0 | + +**Per document.** These are the 51 files under `openspec/` and `specs/` at +`593250d`. They were computed with plain `git log -- `. `--follow` falsely +linked the tiny `.openspec.yaml` files across changes. + +| documents | files | introduced (spec <- original) | last changed (spec <- original) | +|---|---|---|---| +| `openspec/changes/add-crash-consistent-lane-worktree-recovery/` | 5 | `fa21860<-b1015f4` | `fa21860<-b1015f4` | +| `openspec/changes/add-supervised-context-restart/` | 6 | `75ada17<-00971b9` | `75ada17<-00971b9` | +| `openspec/changes/interactive-lane-name-repair/` | 5 | `30d7bf7<-f9e63f4` | `30d7bf7<-f9e63f4` | +| `openspec/changes/migrate-to-triad/` | 6 | `593250d<-44983ce` | `593250d<-44983ce` | +| `openspec/changes/skip-no-lane-guard/` | 5 | `b4e1cb7<-a7d1257` | `b4e1cb7<-a7d1257` | +| `openspec/config.yaml` | 1 | `593250d<-44983ce` | `593250d<-44983ce` | +| `specs/002-skip-no-lane-guard/` | 3 | `b4e1cb7<-a7d1257` | `b4e1cb7<-a7d1257` | +| `specs/003-binary-only-guard-runner/` | 3 | `4fe91d3<-cbb5981` | `4fe91d3<-cbb5981` | +| `specs/004-migrate-to-triad/` | 13 | `593250d<-44983ce` | `593250d<-44983ce` | +| `specs/005-supervised-legacy-ctx/` | 4 | `75ada17<-00971b9` | `75ada17<-00971b9` | + +**Where cited hashes resolve.** The spec documents at `593250d` contain 85 +distinct hex tokens. Each was resolved against the assembly's history (run C's +source, `main` only) and against the original checkout's branches: + +| resolves | tokens | examples | +|---|---|---| +| assembly `main` history | 12 | `daed209`, `b1015f4`, `14641fd`, `82ecebe`, `c1bac0d`, `f5444c5` | +| original repository's branches or PR objects only | 35 | `3c26041` (001), `5164ce1`/`b20882a` (`feat/supervised-context-restart`), `11037f0` (`feat/claude-current`), `dda91f1`/`52ce64e` (004 planning branch), `64f4e93`/`708395e` (no local branch: PR heads) | +| openRepoShape | 2 | `39d5c98`, `91d5685` | +| not an openRepoTools commit | 36 | session uuids, pids, dates, examples (`abc1234`), commits of other repositories | + +None of them resolves in the spec leg, whose history filter-repo rewrote. All +47 openRepoTools citations resolve in `opensoft/openRepoTools`, which keeps its +whole history and its branches because the adoption is in place. So the +recorded citations stay valid as written and are not rewritten. The spec +`AGENTS.md` says so. + +One consequence: a citation of a squash-merged branch commit resolves only +while that branch or its PR ref survives. For example, feature 004's +`dda91f1` and `52ce64e` predate the #187 squash. + +## 7. Bootstrap, validators, OpenSpec and Speckit: results + +Every check ran against a fresh clone of my bare copies, with +`-c protocol.file.allow=always`. On git 2.43.0 the file transport is refused +without that flag: on run C, plain `submodule update` exits 1 and the flagged +one exits 0. + +| check | before (split `69b8924`) | after (c2 `3e87e39`) | +|---|---|---| +| `python3 scripts/bootstrap.py` | exit 0; legs on `main` at their pins | exit 0; spec `39c1887`, code `6169e8e`, both "branch tip == pin" | +| `make validate` (naming, manifest, pins) | exit 0 | exit 0. Gitlinks equal their pins, tree digests recompute (spec `3a4a84be…`, code `697a080f…`), all 11 copied shape files match | +| `bump-leg.py --dry-run`, then the real bump (spec, code) | — | exit 0 ×4; the tool's own validators "pins ok" | +| `shape-doctor.py` | exit 1, `MISPLACED (3 paths)` (pre-existing; section 4) | exit 1, the same `MISPLACED (3 paths)`; `leg shape files` note down to 1 missing file | +| `setup-openspeckit --shape on --integration codex --base-branch main --no-global-agent-pointers`, in a disposable copy with an isolated HOME and agent root | — | exit 0. **Line 1 unchanged**; the managed block landed before the first `## ` heading (line 72, before line 94), as the protocol prescribes for an adopted root; `git-config.yml` reads `checkout_mode: worktree`, `base_branch: main`, `worktree_root: worktrees`; ".gitignore unchanged" (`/worktrees/` already present); the spec leg gains `openspec/README.md`, `changes/archive/.gitkeep` and `specs/.gitkeep`, and its tracked `openspec/config.yaml` is kept. No file in the real agent root is newer than the run. The only workBenches files newer than it are another writer's QA evidence under `sysBenches/cloudBench/devcontainer.example/tmp/qa1-*`; the bootstrap writes only to its `--repo`. | +| paired selection: the overlay's `create-new-feature.sh --number 4 --short-name migrate-to-triad` (dry run, then real) | — | exit 0 ×2. `004-migrate-to-triad` branched in both legs from `origin/main`; worktrees at `worktrees/004-migrate-to-triad/{spec,code}`; `.specify/feature.json` = `{"feature_directory":"worktrees/004-migrate-to-triad/spec/specs/004-migrate-to-triad"}`; `git status --ignored` at the root shows only `!! worktrees/` | +| `.specify/scripts/bash/check-prerequisites.sh --json --paths-only` from the root | — | exit 0; `BRANCH 004-migrate-to-triad`, `FEATURE_DIR worktrees/004-migrate-to-triad/spec/specs/004-migrate-to-triad` | +| `$SPECKIT_GIT_WORKTREE_ROOT=wt-override` (dry run) | — | exit 0; `WORKTREE_PATH wt-override/009-probe`. The override wins, but a root other than `worktrees/` is **not** covered by the root's ignore line, so point it at `worktrees/` or outside the root. | +| `openspec validate migrate-to-triad --strict` in the **spec leg** (`main`, `39c1887`) | — | exit 0, "Change 'migrate-to-triad' is valid" | +| the same in the feature's **spec worktree** | — | exit 0, valid | +| `openspec validate --changes --strict` in the spec leg | — | exit 0, 5 passed, 0 failed | +| the same command from the **assembly root** | — | exit 1, "Unknown item"; `openspec context`: "No OpenSpec root found" | +| store precedence (a disposable spec clone registered as store `ort-spec-main` in the isolated config) | — | spec worktree without `--store` resolves `source: nearest`, the worktree itself (**local root wins**). With `--store` it resolves `source: store`, the store clone (explicit selection overrides, so check where it points). The assembly root without `--store` gives exit 1, naming the store rather than picking it; with `--store` it is valid. | +| `git diff --check`, every patch range | — | exit 0 ×3 | +| code-leg hygiene subset, `PATH=/usr/bin:$PATH code/tests/run.sh -k repo_hygiene` (serialized: it waited for a live suite, then held the lock) | 17 failed, 135 passed | 13 failed, 139 passed (exit 1 both times; section 10, finding 6) | + +**Tool versions on this bench, 2026-10-08:** +- git 2.43.0; `/usr/bin/openspec` 1.6.0; `specify` 0.12.12.dev0. +- `setup-openspeckit` is now a shim that runs workBenches' `devBenches/base-image/files/openspeckit/setup-openspeckit` (last changed in d86ba59, #140). Under an isolated HOME it needs `WORKBENCHES_ROOT`. +- The `openspec` first on PATH (`~/.npm-global/bin`, 1.2.0) has no `--store`. The checks above name `/usr/bin/openspec`. +- `python3` on PATH has no pytest, so locked runs need `PATH=/usr/bin:$PATH`. +- `~/.agents` is a symlink; an isolated copy must dereference it (`cp -rL`). `cp -a` copies the link, and that copy then writes to the real agent root. + +## 8. `.specify/` today, and what the triad root needs + +**Today, on `main`'s shared checkout:** +- `.specify/` is local and ignored. It is listed in `.git/info/exclude` (`/.specify/`, beside `.claude/`, `/.agents/`, `/.codex/` and `/handoffs`), not in the tracked `.gitignore`. +- Its values are machine-local: integration `codex`; Speckit `1.0.7.dev0`; `git-config.yml` with `worktree_root: ../openRepoTools-worktrees` (the single-repository layout); and a `feature.json` pointing into a sibling worktree. +- No agent file in this repository's history has ever carried the bootstrap's managed block (`git log -S'OPENSPEC-SPECKIT-GLOBAL'`: none). + +**What the shape and protocol prescribe for a three-leg root:** +- `.specify/` at the root; +- `git-config.yml` with `worktree_root: worktrees`; +- `/worktrees/` in the root's own `.gitignore`; +- paired worktrees `worktrees//{spec,code}`; +- `$SPECKIT_GIT_WORKTREE_ROOT` as an override. + +**The root scaffolding patch carries only `/worktrees/`** (plus the shape's +two ignore lines), and the bootstrap leaves it unchanged. It copies no +machine-specific configuration or secret: `feature.json`, `init-options.json`, +`integration.json`, `git-config.yml` and the skill links are all regenerated +by the bootstrap on each workstation. + +It deliberately does **not** commit `.specify/`, the managed blocks, or the +twenty generated agent files. That keeps today's posture: local scaffolding +excluded per clone. The in-place checkout keeps its `.git/info/exclude` +through the adoption. On a fresh clone elsewhere these files show as untracked +after the bootstrap, exactly as they do on a fresh clone of today's +monorepo. Changing that posture, by ignoring them in the tracked +`.gitignore` or by committing `.specify/`, is Brett Heap's decision and is +not made here. The root `AGENTS.md` map carries what the managed block would +otherwise say about the split. + +## 9. Distribution is not bootstrap + +Running the shape-aware bootstrap in a root writes that root's scaffolding. +It does not distribute the October 4 manual decision ("Triad Feature +Amendments") to any workstation. + +The installed `project-agent-bootstrap.md` says so itself: "Existing global +protocol files are written only when missing … Do not run a whole bootstrap +merely to apply this manual decision". The fixture run confirms it: it logged +"kept existing" for the agent root's `AGENTS.md` and both protocol files. + +On this workstation (Eagle) the installed protocol files carry the "Triad +Feature Amendments" section. Their sha256 (`f9dd6464…`, `bbbc2b2c…`) are newer +than the October 4 snapshots recorded in protocol-review.md. Nothing here +speaks for any other workstation. + +What workBenches delivery would carry to each target workstation: +- the updated `openspec-speckit-workflow.md` and `project-agent-bootstrap.md`; +- the amendment documents and templates (`docs/triad-feature-amendments.md`, `docs/triad-feature-amendment-templates.md`); +- the opsx command and Speckit skill guidance with its shape blocks; +- the `setup-openspeckit` installer and the speckit-worktree overlay templates in the bench image. + +Each of these is verified per workstation through workBenches' own delivery, +under its governing `openspec/changes/adopt-triad-feature-amendments/`. +Lane 2 is reading that side. + +## 10. Findings for the other writers + +1. **T010.** The root `.github/workflows/validate.yml` has a row in `contracts/shape-pin.yaml` (sha256 `fb8c0360…`). A job added there is drift, and `validate-pins.py` will refuse it. The root integration job needs its own, unpinned workflow file. +2. **Legs lose their front door.** Neither leg has `AGENTS.md`, `CLAUDE.md`, `README.md`, `LICENSE`, `.gitattributes` or `.gitignore` after the split. The root's attributes never reach a submodule, so the code leg loses the LF rule behind its installer's byte-for-byte comparisons. `code-0001` and `spec-0001` restore them. +3. **Root README anchor.** `contracts/{spec,code}-pin.yaml` name `README.md#the-lockstep-invariant`, a section only `shape/README.md` had. `assembly-0001` adds it. +4. **`shape/` copies.** Keep `shape/.gitignore` and `shape/.gitattributes` (pinned). `shape/AGENTS.md`, `shape/CLAUDE.md` and `shape/README.md` are unpinned, and the shape's own follow-up says to delete the first two. Their deletion is left to T014's reviewer. +5. **Doctor placement.** `MISPLACED (3 paths)` (`code/commands/`, `code/contracts/`, `code/skills/`) is the adoption plan's reasoned overrides, identical before and after. Relay it; do not move files. +6. **T009: the hygiene tests read the wrong front door after the split.** + - 17 test functions read `README.md`, `AGENTS.md`, `CLAUDE.md`, `.gitattributes` or `docs/README-lanes.md` at the **code leg's** root: 16 in `test_repo_hygiene.py`, 1 in `test_openrepotools_command.py`. + - Measured on the code leg alone: before `code-0001`, 17 failed (all `FileNotFoundError`). After it, 13 fail (4 `FileNotFoundError` on `docs/README-lanes.md`, 9 assertions on the short leg README and AGENTS). + - **4 now pass only because they read the leg's own files:** `test_agents_md_is_short_enough_to_be_read`, `test_readme_is_short_enough_to_be_read`, `test_claude_md_points_at_agents_md` and `test_the_root_carries_the_line_ending_rule`. A green there is about the wrong file. + - Pointed back at the assembly root, the two length caps (`<= 316`, `<= 486`, today's counts) must rise to 340 and 510, each in a dated entry naming the rule its lines buy: + - AGENTS.md: the shape's first-line pointer and the triad map the bootstrap protocol requires. + - README.md: the lockstep-invariant anchor the leg pins name, and the three-repository front door. + - `test_lane_helpers.sh`:3738 and :6228 read the manual through `cat … || :`, so a missing file reads as empty rather than failing. +7. **Lane 3 (lane tooling).** + - `lanes-edit.sh` and `lane-handoff` key an OpenSpec claim as `owner/repo:openspec/changes/`; the triad's changes live in `opensoft/openRepoTools-spec`. + - The manual has 21 lines naming code-leg files (section 5). + - `lane-worktrees`' and `lanes-edit.sh`'s lane roots (`.claude/worktrees`, `.lane-worktrees/`) do not include `/worktrees//{spec,code}`. + - `repos.tsv` has no `openRepoTools-spec` or `openRepoTools-code` aliases. + - The installed handoff skill's writer-poll globs miss `/worktrees/`. + - The checkout's `/handoffs` symlink and `.git/info/exclude` persist through the in-place adoption. +8. **T017.** Feature 004's own working files in the spec leg carry 23 pre-split path mentions. Some cited 004 commits (`dda91f1`, `52ce64e`) resolve only while the 004 branch or #187's PR ref survives. +9. **T007.** `assembly-0001`'s README sentence assumes T007's design: the root `openRepoTools` hands over to `code/openRepoTools` at the pinned commit. `code/openRepoTools`:1218 (`venv_note`) still cites `docs/README-lanes.md`. + +## 11. What this task did not do + +- It patched no executable, test, workflow or lane-tooling file in the code leg. Lane 3, T007, T009 and T010 own those. +- It did not edit `docs/README-lanes.md`'s content (lane 3), any external referrer, any pinned shape copy, or any approval record. +- It did not delete the three unpinned `shape/*.md` copies (T014's reviewer decides). +- It did not commit `.specify/`, managed blocks or generated agent files. +- It did not change the `.specify/` posture. +- It did not run the full suite. Only the serialized `repo_hygiene` subset ran. +- It did not write anything into the rehearsal directory or any real remote except this evidence branch. + +## 12. Nothing lands until the real legs exist + +These patches apply only to run C's rehearsed arrangement. Today's layout on +`main` is unchanged. At T014, after Gate C and the real `execute`, the +portable patches (`assembly-0001`, `code-0001`, `spec-0001`) are applied +through the real leg and root pull requests. The pin bumps are then +regenerated with `bump-leg.py` against the real leg commits, and the +reviewer settles the `shape/*.md` deletion and the hygiene cap raises with +T009.