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 01/42] 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 02/42] 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 03/42] 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 04/42] 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 05/42] 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 06/42] 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 07/42] 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 08/42] 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 74e4eb73efc0bef5cb39be79851e9d156088be96 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 00:27:49 +0000 Subject: [PATCH 09/42] Record T010's two workflow patches for the triad: the code leg's standalone tests workflow and the assembly's exact-pin integration job in its own file. Checkpoint of the CI evidence for #186. The legs do not exist on GitHub yet (Gate C), so these are patches against the rehearsed run C arrangement: the code leg's tests.yml at 73b04d6 (its six jobs and trigger policy kept, the standalone checkout context named) and a new .github/workflows/composed.yml for the assembly at 69b8924, its own file because validate.yml is a pinned shape copy. Both `git apply --check` cleanly against run C. The measurements and local proofs follow in test-roots-and-ci-2026-10-08.md. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../patches/t010-assembly-exact-pin-job.patch | 128 ++++++++++++++++++ .../t010-code-leg-tests-workflow.patch | 88 ++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 specs/004-migrate-to-triad/patches/t010-assembly-exact-pin-job.patch create mode 100644 specs/004-migrate-to-triad/patches/t010-code-leg-tests-workflow.patch diff --git a/specs/004-migrate-to-triad/patches/t010-assembly-exact-pin-job.patch b/specs/004-migrate-to-triad/patches/t010-assembly-exact-pin-job.patch new file mode 100644 index 00000000..2b51c625 --- /dev/null +++ b/specs/004-migrate-to-triad/patches/t010-assembly-exact-pin-job.patch @@ -0,0 +1,128 @@ +From 386be81959ea32836be0777c1af8547cac044e2d Mon Sep 17 00:00:00 2001 +From: openRepoTools CI +Date: Thu, 8 Oct 2026 21:52:59 +0000 +Subject: [PATCH] Add the assembly's exact-pin integration job as its own + workflow, which runs the code leg's suite composed and refuses any absent + context. + +validate.yml is a pinned shape copy, so the job lives in composed.yml. +T010 of specs/004-migrate-to-triad/tasks.md (#186). + +Lane: openRepoTools-1 +--- + .github/workflows/composed.yml | 103 +++++++++++++++++++++++++++++++++ + 1 file changed, 103 insertions(+) + create mode 100644 .github/workflows/composed.yml + +diff --git a/.github/workflows/composed.yml b/.github/workflows/composed.yml +new file mode 100644 +index 0000000..ef28e08 +--- /dev/null ++++ b/.github/workflows/composed.yml +@@ -0,0 +1,103 @@ ++name: composed ++ ++# THE EXACT-PIN INTEGRATION GATE (opensoft/openRepoTools#186, T010 of ++# `specs/004-migrate-to-triad/tasks.md`; FR-008). `validate` beside this file ++# says the three records of each leg agree — gitlink, pin, digest. This says ++# the code those records name PASSES ITS OWN SUITE, composed with the spec leg ++# and the dependency it was pinned beside: the whole arrangement at the exact ++# commits this root records, and nothing allowed to skip its way to green. ++# ++# WHY A SIBLING AND NOT A JOB IN `validate.yml`: that file is a COPY of ++# openRepoShape's, digest-pinned in `contracts/shape-pin.yaml`. An edit to it ++# is drift `scripts/validate-pins.py` and `status` both report. This file is ++# this project's own, and it references no leg by `/@`, so the ++# lockstep rule has nothing in it to move. ++# ++# LINUX ONLY, on purpose. The platform matrix — macOS's bash 3.2 parse and its ++# `ready`-label suite, Windows and the no-submodule skip path — is the code ++# leg's own `tests` workflow, and it stays there. What only this root can run ++# is the COMPOSED suite: `OPENREPOTOOLS_COMPOSED=1` turns every absent root ++# into a refusal and every check that would have skipped for want of one into ++# a failure, so a green run here is a claim about all three legs at once. ++# ++# IT REFUSES ABSENT CONTEXT TWICE OVER. The step below fails on any missing ++# leg, pin or dependency file before a test runs; and the suite's own ++# `tests/conftest.py` refuses a composed session (pytest's exit 4) when the ++# assembly or spec root is absent, the dependency is not initialized, or the ++# code or spec leg is not at the commit this root records for it. A leg this ++# job cannot fetch — a private leg with no credential — fails the checkout, ++# which is the same refusal: composed acceptance with a leg missing is the one ++# thing this job must never report. The legs are fetched over https, as ++# `.gitmodules` names them; nothing here allows a `file://` transport. ++ ++on: ++ pull_request: ++ push: ++ branches: [main] ++ workflow_dispatch: ++ ++jobs: ++ exact-pins: ++ runs-on: ubuntu-latest ++ steps: ++ # PINNED BY COMMIT, the same two actions the code leg pins. RECURSIVE: ++ # the legs, and the dependency nested in the code leg. ++ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 ++ with: ++ submodules: recursive ++ fetch-depth: 1 ++ persist-credentials: false ++ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 ++ with: ++ python-version: '3.x' ++ ++ - name: every context the composed run claims is here ++ run: | ++ set -euo pipefail ++ missing=0 ++ for need in project.yaml contracts/code-pin.yaml contracts/spec-pin.yaml \ ++ code/openRepoTools code/tests/run.sh code/tests/conftest.py \ ++ code/contracts/openreposhape-pin.yaml \ ++ code/upstream/openRepoShape/scripts/repo_shape.py \ ++ code/upstream/openRepoShape/templates/workspace-root/README.md; do ++ if [ ! -f "$need" ]; then ++ echo "::error::the composed run needs $need, and this checkout has none" ++ missing=1 ++ fi ++ done ++ if [ ! -d spec/openspec ] && [ ! -d spec/specs ] && [ ! -d spec/docs ]; then ++ echo "::error::the spec leg is not checked out at spec/" ++ missing=1 ++ fi ++ git submodule status --recursive ++ exit "$missing" ++ ++ # The same two the `validate` workflow runs, here as the gate's FIRST ++ # half: a suite that passes at a gitlink the pin does not name passes ++ # at the wrong commit. ++ - name: the manifest and the lockstep pins ++ run: | ++ python3 scripts/validate-manifest.py ++ python3 scripts/validate-pins.py ++ ++ - name: install the test runner ++ run: python3 -m pip install --disable-pip-version-check pytest==8.3.4 ++ ++ - name: git identity for the fixture commits (env, not global config) ++ run: | ++ { ++ echo "GIT_AUTHOR_NAME=openRepoTools CI" ++ echo "GIT_AUTHOR_EMAIL=ci@openrepotools.invalid" ++ echo "GIT_COMMITTER_NAME=openRepoTools CI" ++ echo "GIT_COMMITTER_EMAIL=ci@openrepotools.invalid" ++ } >> "$GITHUB_ENV" ++ ++ # THE CODE LEG'S OWN ENTRY POINT, serialized as it is everywhere: the ++ # roots are named, not inferred, and `-rs` prints any skip that remains ++ # (a platform one; none for want of a root can reach this far). ++ - name: the code leg's suite, composed, at the exact pins ++ env: ++ OPENREPOTOOLS_COMPOSED: '1' ++ OPENREPOTOOLS_ASSEMBLY_ROOT: ${{ github.workspace }} ++ OPENREPOTOOLS_SPEC_ROOT: ${{ github.workspace }}/spec ++ run: bash code/tests/run.sh -rs +-- +2.43.0 + diff --git a/specs/004-migrate-to-triad/patches/t010-code-leg-tests-workflow.patch b/specs/004-migrate-to-triad/patches/t010-code-leg-tests-workflow.patch new file mode 100644 index 00000000..199bcd65 --- /dev/null +++ b/specs/004-migrate-to-triad/patches/t010-code-leg-tests-workflow.patch @@ -0,0 +1,88 @@ +From b95d667339a5cef54605a949cdcae2d264dd3b6a Mon Sep 17 00:00:00 2001 +From: openRepoTools CI +Date: Thu, 8 Oct 2026 21:53:33 +0000 +Subject: [PATCH] Name the code leg's standalone checkout context in its tests + workflow (T010, #186). + +--- + .github/workflows/tests.yml | 36 ++++++++++++++++++++++++++++++++++-- + 1 file changed, 34 insertions(+), 2 deletions(-) + +diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml +index e27bc64..f078694 100644 +--- a/.github/workflows/tests.yml ++++ b/.github/workflows/tests.yml +@@ -21,6 +21,25 @@ name: tests + # job. A suite nothing runs is not a suite (act 3, obligation 6). It is bash + # and it skips on Windows, like every other bash claim here; the macOS + # `/bin/bash -n` step below parses it under bash 3.2 whatever it does. ++# ++# THIS IS THE CODE LEG'S WORKFLOW, AND EVERY JOB IN IT IS STANDALONE ++# (opensoft/openRepoTools#186, T010 of `specs/004-migrate-to-triad/tasks.md`). ++# The leg is cloned at its own root: `actions/checkout` puts the code root at ++# `$GITHUB_WORKSPACE`, named below as `OPENREPOTOOLS_CODE_ROOT`, and ++# `submodules: true` initializes the one submodule the leg has, the dependency ++# `upstream/openRepoShape`. There is no assembly above it and no spec leg ++# beside it, so `tests/conftest.py` resolves neither: the checks of the ++# documents those two own (`README.md`, `AGENTS.md`, `CLAUDE.md`, ++# `docs/README-lanes.md`) SKIP here naming the root they need, and `-rs` ++# prints every one. `OPENREPOTOOLS_COMPOSED: '0'` says in the file what the ++# jobs are: nothing here is composed acceptance. That claim is the assembly ++# root's `composed` workflow, which checks out all three at their exact pins ++# and runs this same suite with nothing allowed to skip. ++# ++# The platform policy is unchanged by the move: `tests`, `tests-no-submodule`, ++# `tests-windows`, `parse-macos` and `guard-launch-mode` on every ++# `pull_request` push, `tests-macos` on the `ready` label, on main, nightly ++# and on dispatch — Brett Heap's RULING of 2026-09-16 travels with the code. + + # `pull_request` names `labeled` beside GitHub's own three defaults + # (`opened`, `synchronize`, `reopened`): the coordinator applying `ready` to +@@ -45,6 +64,13 @@ on: + - cron: '17 4 * * *' # 04:17 UTC, nightly + workflow_dispatch: + ++# THE CHECKOUT CONTEXT, NAMED (#186, T010): the code root is the checkout's ++# own root, and the run is standalone. `github.workspace` is the same root on ++# every runner this file uses, Windows' `D:\a\...` included. ++env: ++ OPENREPOTOOLS_CODE_ROOT: ${{ github.workspace }} ++ OPENREPOTOOLS_COMPOSED: '0' ++ + jobs: + # Keep the process launch contract independently visible while the full + # lane-helper suite runs. Each GitHub job has its own workstation runner; +@@ -110,15 +136,19 @@ jobs: + echo "GIT_COMMITTER_EMAIL=ci@openrepotools.invalid" + } >> "$GITHUB_ENV" + ++ # `-rs`: in the leg the document checks SKIP for want of the assembly and ++ # spec roots, and a skip a person cannot read is a skip nobody audits. + - name: tests +- run: python3 -m pytest tests -q ++ run: python3 -m pytest tests -q -rs + + # THE SKIP PATH, PROVED. A fork's first command is `git clone` without + # `--recurse-submodules` and then `pytest`, and if that goes RED the fork is + # one nobody finishes. So one job checks out with NO submodules and asserts + # the suite still exits 0: the estate tests skip, naming + # `git submodule update --init upstream/openRepoShape`, and the hygiene and +- # installer tests — which need nothing but this repository — still run. ++ # installer tests — which need nothing but this repository — still run. In ++ # the leg the document checks skip too, naming the assembly or spec root, ++ # exactly as they do in `tests` above: no submodule changes nothing there. + # + # `pytest -q` exits 0 on skips and 1 on failures, so the job's own status IS + # the assertion; `-rs` prints the skip reasons, which is what a person +@@ -151,6 +181,8 @@ jobs: + # THE SAME SUITE ON WINDOWS, because a person on Windows still CLONES this + # repository and reads it. What this job proves is the checkout — that the + # `.gitattributes` rule holds and the three bash files are LF in the index — ++ # (in the leg that is the LEG'S OWN `.gitattributes`: git never reads the ++ # assembly's into a submodule, so the leg must carry the rule itself) — + # and the hygiene tests, which need no shell. The estate and installer tests + # carry `WINDOWS_SKIP` in `tests/conftest.py`: these files are bash, there is + # no PowerShell twin to run instead, and on Windows the way in is WSL2. +-- +2.43.0 + From cc7c7a156953eacc66755efc5bf9f49289d2cf40 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 00:32:48 +0000 Subject: [PATCH 10/42] Record what the triad split breaks in the test suite, the root rules and composed contract PR #190 answers it with, and the local proofs of both T010 workflow patches. Measured on the rehearsed run C code leg 73b04d6 in three contexts (mounted in the assembly, standalone, standalone without the dependency): the same 17 hygiene tests fail on files the split moves out of the code leg, and nothing else in the measured selectors. PR #190 is red at origin/main in 18 tests and 188 passed with nothing skipped on today's layout; CI green per push. In a composed candidate pinned with bump-leg.py, the validators pass, the composed job refuses a missing spec leg (exit 1 at its context step, exit 4 from the suite), and the composed and standalone leg runs fail only on the code leg's missing .gitattributes (a T006 finding) and two lane-suite stamp assertions the long run-root path pushes past the row cut. Hosted runs wait for real legs (Gate C). Part of #186. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../test-roots-and-ci-2026-10-08.md | 255 ++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 specs/004-migrate-to-triad/test-roots-and-ci-2026-10-08.md diff --git a/specs/004-migrate-to-triad/test-roots-and-ci-2026-10-08.md b/specs/004-migrate-to-triad/test-roots-and-ci-2026-10-08.md new file mode 100644 index 00000000..df58888a --- /dev/null +++ b/specs/004-migrate-to-triad/test-roots-and-ci-2026-10-08.md @@ -0,0 +1,255 @@ +# Explicit test roots and the triad's CI — October 8, 2026 + +**Tasks:** T009 and T010 of [tasks.md](tasks.md), under +[#186](https://github.com/opensoft/openRepoTools/issues/186). No task is +checked complete by this record; Gate B stays open. +**Code change:** [PR #190](https://github.com/opensoft/openRepoTools/pull/190) +(`feat/test-roots-for-the-triad`, on `main` `c4864ac`). +**CI patches:** [patches/t010-code-leg-tests-workflow.patch](patches/t010-code-leg-tests-workflow.patch) +and [patches/t010-assembly-exact-pin-job.patch](patches/t010-assembly-exact-pin-job.patch). +**Rehearsed arrangement:** run C of 2026-10-08 (private prep +`work-20261008/rehearsal`): source `main` `837928a`, assembly split +`69b8924`, spec leg `593250d`, code leg `73b04d6`, code's nested +openRepoShape `7f84ca4`. Every clone here was taken *out* of it, with +`-c protocol.file.allow=always` (git 2.43 refuses `file://` submodule +transport by default) and an offline `insteadOf` for the nested +openRepoShape URL; nothing was written in it. + +The first scratch of this work, and the October 7 run B arrangement it was +measured against, were lost when the bench restart cleared `/tmp`. An interim +reproduction of run B into disposable local remotes (adopter `check` and +`execute` exit 0, `gh`/`curl` shims never called) gave the same leg commits as +the October 7 receipt — spec `558a12b`, code `6d52162` — and is superseded by +run C for everything below. + +## 1. What the split breaks — measured before the change + +The suite at run C's code leg `73b04d6` (that is, `main` without PR #190), run +with `tests/run.sh -k 'repo_hygiene or upstream_pin or wip_init or park_resume' -rfEs` +in three contexts: + +| Context | Result | Log SHA256 | +| --- | --- | --- | +| M1 — the code leg mounted in the recursive assembly clone (`assembly/code`) | **17 failed**, 350 passed | `e1658c99…eed0e` | +| M2 — the code leg cloned on its own, dependency initialized | **17 failed**, 350 passed | `442588dd…c8dd5` | +| M3 — the code leg cloned on its own, dependency **not** initialized | **17 failed**, 141 passed, 209 skipped | `59027b3c…08238` | + +The 17 are the same tests, failing the same way, in all three: every one is +`FileNotFoundError` on a file the split moved out of the code leg. M1 equals +M2 because the suite never looked above its own root. + +| Test (`tests/test_repo_hygiene.py`) | Missing file | +| --- | --- | +| `test_the_readme_prints_the_install_line_the_pointer_prints`, `…_also_carries_the_gh_api_form`, `…_carries_the_two_line_onboarding_chain`, `test_no_document_offers_a_windows_powershell_twin`, `test_the_documents_say_what_bare_park_does_now`, `…_what_status_is_and_is_not`, `…_the_sweep_skips_a_root_without_the_overlay`, `…_what_a_bare_lanes_lists`, `test_readme_is_short_enough_to_be_read` | `README.md` | +| `test_claude_md_points_at_agents_md` | `CLAUDE.md` | +| `test_agents_md_names_the_pin_rules`, `test_agents_md_is_short_enough_to_be_read`, `test_the_suite_wrapper_takes_one_lock_and_names_it_where_agents_read_it` | `AGENTS.md` | +| `test_the_directory_precedence_states_every_rung_it_implements`, `test_the_exit_3_documentation_agrees_with_the_die_message_it_describes`, `test_the_manual_offers_the_fork_act_and_not_a_kill` | `docs/README-lanes.md` | +| `test_the_root_carries_the_line_ending_rule` | `.gitattributes` | + +What passed in all three is as informative: `tests/test_upstream_pin.py` +already read the code leg's own gitlink and pin, so it needed no rewiring to +stay correct in a leg — only names and a guard (below). The dependency-reading +estate, `wip init` and pin checks skipped in M3 with today's reason, as they +do today. + +Two more readers are outside those selectors and were found by reading: +`tests/test_openrepotools_command.py`'s receipt-mode check reads `README.md`, +and two sections of `tests/test_lane_helpers.sh` (14 assertions) read +`docs/README-lanes.md` with `cat … 2>/dev/null || :`, so in a leg they would +have run against an empty string rather than fail on a missing file. PR #190 +covers all of them: the 16 hygiene document tests, the installer's README +check, the two lane-suite sections, and the `.gitattributes` rule (kept on the +code root, section 2). The T006 writer's independent count of code-leg tests +reading those documents was 17 functions, two of them in the lane suite. + +## 2. The root rules (PR #190) + +| Root | Named by | Otherwise | +| --- | --- | --- | +| code | `OPENREPOTOOLS_CODE_ROOT` | the tree the suite was loaded from | +| assembly | `OPENREPOTOOLS_ASSEMBLY_ROOT` | the nearest directory above the code root that carries `contracts/code-pin.yaml` and `project.yaml`, is a repository's top level, and records the code root as the gitlink its code pin's `submodule_path` names; none on today's layout | +| spec | `OPENREPOTOOLS_SPEC_ROOT` | the assembly's spec leg (`contracts/spec-pin.yaml`'s `submodule_path`) when it is checked out; none otherwise | +| dependency | — | `/upstream/openRepoShape`, initialized or not | + +A file is read from the root that owns it: `README.md`, `AGENTS.md` and +`CLAUDE.md` from the assembly; anything under `docs/`, `openspec/`, `specs/` +or `ideation/` from the spec root; everything else from the code root. +Today's single repository carries `openspec/` and `specs/` beside the code, so +it answers for all three roles, and every reader reads exactly the file it +read before. The layout is decided by those trees and never by the documents, +so a README deleted from today's tree is still a failure, not a skip. + +`.gitattributes` stays the **code root's** in every layout, against the +placement table, because git never applies a superproject's attributes inside +a submodule. Measured in the composed candidate: `README.md` at the assembly +has `text: auto`, `eol: lf`; `park` and `tests/run.sh` in `code/` have +`text: unspecified`, `eol: unspecified` (`proof-check-attr.log`). + +## 3. The composed-versus-standalone contract + +* **Standalone** (the default, and the only mode today's layout has): a check + whose root is absent skips, naming the root — the courtesy the missing + submodule already gets. The no-submodule skip is today's, same condition and + reason. On today's layout nothing new skips. +* **Composed** (`OPENREPOTOOLS_COMPOSED=1`): `tests/conftest.py` refuses the + session (`pytest.UsageError`, exit 4, `ERROR: …`) when the assembly or spec + root is absent, the dependency lacks `scripts/repo_shape.py` or + `templates/workspace-root/README.md`, or the code or spec leg is not at the + commit the assembly records or has changes to tracked files — naming each. + `NEEDS_UPSTREAM` becomes a fixture that fails, and a document test that + reaches an absent root fails. A variable naming the wrong kind of tree, or a + mode other than `0`/`1`, is refused in either mode. + +## 4. Red at the base, green on the branch + +* **Red.** `tests/test_test_roots.py` (SHA256 `be4edff7…6cff`), copied + unchanged into a clone of `origin/main` `c4864ac`, `tests/run.sh -k roots`: + **18 failed**, every test of the module (3 passes are other modules' tests + whose names contain `roots`). The seven behavioural tests failed on what they + assert — the nested suite read this checkout's README, manual, gitlink and + dependency whatever the variables named, a composed triad with its spec leg + moved off its pin ran instead of exiting 4, and `tests/run.sh` passed a + relative root through relative — and the eleven resolver tests failed + because `conftest.resolve_roots` and `ROOTS` do not exist there. Log + `eb7d2bc4…525c`. +* **Green, today's layout.** `tests/run.sh -k 'repo_hygiene or upstream_pin or roots or composed'` + on `c139e88`: **188 passed, 0 skipped**. Log `b0289afa…cf4`. +* **CI on #190** (head `c139e88`): `tests` 1315 passed in 34m52s, + `tests-no-submodule`, `tests-windows` (173 passed, 1142 skipped), + `parse-macos`, `guard-launch-mode` and SonarCloud green; `tests-macos` + waits for `ready`. + +## 5. The two CI patches (T010) + +**Code leg, `.github/workflows/tests.yml`** (against run C's code leg +`73b04d6`, whose copy is byte-identical to `main`'s). The six jobs and their +trigger policy are kept exactly — `tests`, `tests-no-submodule`, +`tests-windows`, `parse-macos` and `guard-launch-mode` on every pull-request +push, `tests-macos` on the `ready` label, on `main`, nightly and on dispatch. +The leg is cloned at its own root, so its checkout and submodule contexts were +already right; the patch names them: workflow `env` +`OPENREPOTOOLS_CODE_ROOT: ${{ github.workspace }}` and +`OPENREPOTOOLS_COMPOSED: '0'`, `-rs` on `tests` so the leg's document skips +are printed, and comments saying that nothing in the leg is composed +acceptance and that the leg must carry its own `.gitattributes`. +SonarCloud Code Analysis is a GitHub App check, not a job in this file; the +code leg needs its own SonarCloud project binding, which is an owner's act. + +**Assembly, new `.github/workflows/composed.yml`** (against run C's split +`69b8924`). Its own file because the root `validate.yml` is a pinned shape +copy: `contracts/shape-pin.yaml` records it at +`fb8c0360863ad953af7825f3be01ed27f8cf1269e5cab19dc0d6c5423161b487`, which +the rehearsed file matches, so a job added there is drift that +`validate-pins.py` refuses. One Linux job, `exact-pins`: checkout with +`submodules: recursive` over https (no `file://` allowance anywhere in it), +a step that fails on any missing leg, pin or dependency file, then +`scripts/validate-manifest.py` and `scripts/validate-pins.py`, then +`bash code/tests/run.sh -rs` with `OPENREPOTOOLS_COMPOSED=1`, +`OPENREPOTOOLS_ASSEMBLY_ROOT=$GITHUB_WORKSPACE` and +`OPENREPOTOOLS_SPEC_ROOT=$GITHUB_WORKSPACE/spec`. The macOS, Windows and +no-submodule policies stay in the code leg's workflow. + +**Neither patch can run on GitHub until the legs exist** (Gate C). Both +`git apply --check` cleanly against run C, and both were proven locally as +below. + +## 6. Local proofs + +The composed candidate is a clone of run C's assembly in which PR #190's +commit was applied onto the code leg (`8ebf4ec`, on `73b04d6`), pushed to a +private copy of the code leg's bare remote, and pinned with the standard's own +`bump-leg.py --leg code --to 8ebf4ec… --local-remote-dir …` (exit 0; commit +`6ccdaf8`, `contracts/code-pin.yaml` digest recomputed as `577cdcf1…d850`); +the composed workflow is commit `386be81` on top. + +| Check (in the candidate, as the job runs it) | Exit | Log SHA256 | +| --- | ---: | --- | +| `python3 scripts/validate-manifest.py` | 0 | `0caaed6c…83a4` | +| `python3 scripts/validate-pins.py` (both legs at their gitlinks, both digests recompute, 11 shape copies match) | 0 | `fb085bee…cf4b` | +| `make validate` / `make pins` | 0 / 0 | `85c8fd9f…bbf0` / `440c5bf4…74e4` | +| the job's context step, all three legs present | 0 | `c4e4cdf2…ac22` | +| the job's context step, spec leg not checked out | **1** (`::error::the spec leg is not checked out at spec/`) | `fcf47871…444a` | +| P2: `code/tests/run.sh -k upstream_pin`, composed, spec leg not checked out | **4** — `ERROR: openRepoTools composed run (OPENREPOTOOLS_COMPOSED=1) refused … OPENREPOTOOLS_SPEC_ROOT=… is not a spec root` | `38889cab…1059` | +| P1: `code/tests/run.sh -k 'repo_hygiene or upstream_pin or roots or composed or openrepotools_command or lane_helpers_suite'`, composed, as the job runs it | 1: **285 passed, 2 failed, 0 skipped** | `f611c198…fe34` | +| P3: the code leg standalone with the patched workflow's context (`OPENREPOTOOLS_CODE_ROOT`=the leg, `COMPOSED=0`), same selector | 1: **268 passed, 2 failed, 17 skipped** | `0f5e8c21…6144` | +| P4: the code leg standalone without its dependency, `-k 'repo_hygiene or upstream_pin or roots'` | 1: **159 passed, 1 failed, 28 skipped** | `6cc26bb1…55d3` | + +The failures, recorded rather than deleted: + +* **`test_the_root_carries_the_line_ending_rule`, in P1, P3 and P4** — the + code leg has no `.gitattributes`. A finding for T006: the leg must carry + `* text=auto eol=lf` itself (section 2). Until it does, the leg's CI is red + on this one test, which is the point of keeping it. +* **Two assertions of `tests/test_lane_helpers.sh`, in P1 and P3** — "…with + clause (c)'s directory and window in the tail" and "the row's stamp states + clause (c)'s two facts for a person, in its TAIL". Both expect the lane's + directory in a register row that is cut at 240 characters, and under + `tests/run.sh` that directory sits beneath the run root + (`~/.local/state/openRepoTools/tmp/-/tmp/tmp.*/home/projects/…`), + long enough to fall past the cut. CI runs the same suite with a short + `TMPDIR` and passed it (1315 passed on #190). These sections are untouched + by PR #190; the length interaction belongs with the run-root work of + #162/#184 (inferred from the expected and actual rows, not bisected). + +Every skip in P3 and P4 names its root. P3's 17 are `README.md` ten times +(nine hygiene tests and the installer's check), `AGENTS.md` three times, +`CLAUDE.md` once and `docs/README-lanes.md` three times; inside the lane suite +its two manual sections printed `skip … no spec root in this run` as well. +P4's 28 are the hygiene module's 16 document skips plus today's 12 dependency +skips (`the pinned openRepoShape is not checked out; run git submodule update --init upstream/openRepoShape`). +P1 skipped nothing. + +Both workflow files parse and hold their structure (`check_workflows.py`: +the leg's six jobs, triggers, `ready` gate, per-job submodule contexts and +`env`; the composed job's one Linux job, recursive checkout, step order and +`env`). `actionlint` passed both on 2026-10-07, on the same content less one +comment sentence in `composed.yml`; it was not installed after the bench +restart, so it was not rerun. + +## 7. How the local runs were made + +The workstation-wide `pgrep` census in `tests/run.sh` did not reach zero for +some runs (the xFactory worktrees keep 3–20 `python3 -m pytest` alive), so a +run that had not started within 5 minutes was replaced, by the coordinator's +interim rule, with run.sh's own pytest line under `flock` on the same lock +file (M1 was; each log says which). That census is now a comment on +[#184](https://github.com/opensoft/openRepoTools/issues/184#issuecomment-6069746447). +On this profile `~/bin/python3` resolves to a virtual environment without +pytest, so every local run had `/usr/bin` first on `PATH`. CI is the record. + +## 8. What remains for T015 + +* Run both workflows on GitHub at the real exact pins once the legs exist, + including `tests-macos` and `tests-windows` in the real code leg. +* T006: the code leg's own `.gitattributes`, before the leg's `tests` can be + green. +* A SonarCloud binding for the code leg, and a leg credential for the + composed job if the legs are ever private (`project.yaml` says public). +* Re-measure against the frozen source chosen at Gate C; the run C commits + here are rehearsal identities, not the cutover's. + +## Log SHA256s + +The logs stay in the private prep area (`work-20261008/ort-test-roots/logs/`). + +| Log | SHA256 | +| --- | --- | +| M1 `m1-assembly-code.log` | `e1658c99b088bb2b571369431942a0170a4c78b14da9f5bd88c959ddc89eed0e` | +| M2 `m2-code-standalone.log` | `442588dd56997ec00f5a2773dfe3e6102bf7b2cfd137b755057d62f2991c8dd5` | +| M3 `m3-code-nosub.log` | `59027b3ceed4f52e1e6b2d0be926b0b7f3a2c7abfda2846b4fc9656b35208238` | +| red at base `red-at-base.log` | `eb7d2bc460f5b4d474a71ccfb2a19ecca0798e4fc778685e620311d37384525c` | +| green on branch `green-branch.log` | `b0289afab41217eb3c1e8ece140233b8a458492b37042727c668066a0f5a8cf4` | +| P1 `p1-composed.log` | `f611c1981520c4d479556d4b5ec9094845166a69772779dee7539113f473fe34` | +| P2 `p2-composed-refuses-without-spec.log` | `38889cab1e4a3c842bfddc9687e30b21df9c183b238df0676088dd1e8e981059` | +| P3 `p3-leg-standalone.log` | `0f5e8c2163fbe1457bb692eba3bdbc4b67e03020cf7ec1ba0ee0fc7e38806144` | +| P4 `p4-leg-no-submodule.log` | `6cc26bb153da368df80752f0b935ea660c965bb06b135c090f0835d0dbb055d3` | +| `proof-validate-manifest.log` | `0caaed6c40864ebce25366b7599b3f56149f8698d02d2ba7a42dafb53bc283a4` | +| `proof-validate-pins.log` | `fb085bee84b9f76fc814a78ca8e5ed17d1d9fd5c0030726633fb92fd7b8fcf4b` | +| `proof-make-validate.log` | `85c8fd9f834556cc7f8c42ac9625108b4cd33c90cd7db2bf6fbe49bbb39bbbf0` | +| `proof-make-pins.log` | `440c5bf4b7250418076bce4fb0be5a21c21def76c5fe9990808fa6b1cbc874e4` | +| `proof-context-step.log` | `c4e4cdf2ed3d39dee6775d9a01a27ab76869e76feefc561399aa686227ffac22` | +| `proof-context-step-nospec.log` | `fcf47871f23e005da6f06ace2953acdb81a2117b4bc2d0009d80986ca4e7444a` | +| `bump.log` | `aee4d464a7ef847c92d0acc00c28d539ae24e9ea54426a1747c0ffec8220fc80` | + +This record authorizes no conversion, repository creation, merge or +default-branch move. 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 11/42] 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. From 5772cadbcdbbdb52fa78760bb5cdc5cb764d9c72 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 00:46:11 +0000 Subject: [PATCH 12/42] Record T011's restore rehearsal of the source from the Gate A bundle: 90 checks pass, no captured ref moved except forward, and a dirty tree rebuilds only with the staged and unstaged diffs the October 7 capture does not take. First commit of specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md for opensoft/openRepoTools#186; the installed-payload rollback, the mutation table, the evidence index and the follow-up inventory follow on this branch. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../gate-ab-evidence-2026-10-09.md | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md diff --git a/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md b/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md new file mode 100644 index 00000000..fb8cabc9 --- /dev/null +++ b/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md @@ -0,0 +1,61 @@ +# Gate A/B evidence, the restore and rollback rehearsal, and the follow-up inventory — October 9, 2026 + +**Task:** T011 of [tasks.md](tasks.md), under [#186](https://github.com/opensoft/openRepoTools/issues/186). +No task is checked complete by this record. +**Writer:** lane openRepoTools-1, 2026-10-09. +**Evidence branch:** `004-migrate-to-triad-rollback` (from main `837928a`). +**Private receipt ID:** `rollback-20261009-ILvoVC` (brett-wip `migration/openRepoTools/`). + +This first commit records step 1 (the source restore). Steps 2 to 4 follow in the +next commits on this branch; until they are here, this document is incomplete. + +## 1. Restore rehearsal of the source + +From the Gate A backup `prep-20261007T100559Z` (host-local, read-only), following its +`RESTORE.md` commands with only the placeholder paths replaced by private scratch. +**90 checks passed, 0 failed.** + +- The manifest (414 entries) passes `sha256sum -c`. +- `RESTORE.md`'s `git bundle verify`, run from the backup directory as written, exits **1**: + `error: need a repository to verify a bundle`. Run inside the restored clone it exits 0. + A defect in `RESTORE.md`'s text, recorded here; the backup is read-only and was not edited. +- The `--mirror` restore and the working clone (`clone`, then `fetch --update-head-ok + --prune '+refs/*:refs/*'`) each list the same 33 refs with the same SHA as the live + `for-each-ref` at bundle time. `git fsck --full` prints nothing and exits 0 for both. +- The 2902 objects reachable from the 50 bundled tips are identical + (sorted-list SHA256 `338cb7bdd397076eeced1de0852eeb1510b4f97e02fb3f62de434531c7be327d`). +- Against the live repository **today**, two days on: of the 33 captured refs, 26 are + identical, 7 moved **forward only** (`main`, `origin/main`, `origin/HEAD` to `837928a`; + the three October 7 evidence branches and lane 3's `feat/triad-lane-tooling`), 0 were + deleted and **0 moved by anything but a fast-forward**. 18 refs were added since. +- All 17 captured worktree HEADs resolve in the restore, each tree ID equals the live one, + every index object is present, and the index a checkout of each HEAD makes equals the + captured `index.ls-files-s` byte for byte (17 of 17). +- One tree by `RESTORE.md`'s own procedure, `main` (the capture with workflow files): + `worktree add --detach`, the captured files copied back, the captured exclude file + restored. Its index (100 entries) equals the capture, all 70 copied files match + `files.sha256`, and its tracked status, untracked and ignored lists equal the capture's, + less the 4 cache entries the capture lists as not copied. +- Both dependency commits resolve from the dependency bundle, and each one's own + `tree_digest` gives the recorded value (`39d5c98` → `a44c0165…`, `7f84ca4` → `3be52767…`). + +### A worktree captured dirty + +The 17 Gate A captures were all clean, so the dirty case was rehearsed on a disposable +stand-in: a clone of the Gate A bundle given every kind of change (an unstaged edit, a +staged edit, a staged new file, a partly staged file, an unstaged and a staged deletion, +a mode change, an untracked note, ignored workflow configuration and an ignored cache). +Each capture was bundled and rebuilt into a fresh clone that held only the bundle's +objects, as a real restore would. + +| Capture | Index equal | Status equal | Every file equal (100) | +| --- | --- | --- | --- | +| A: the per-worktree block of the backup's own `capture.sh`, verbatim | **no** — 3 staged blobs are in no bundle | **no** | **no** | +| B: A plus `git diff --cached --binary`, `git diff --binary` and every untracked file | yes | yes | yes | + +**Finding for Gate A.** The October 7 capture takes a tree's index *listing*, its ignored +workflow configuration and nothing else of its working state. That was sufficient only +because all 17 trees were clean. The final capture at the owner-coordinated freeze must +take B's three extra artifacts for any tree that is not clean, or refuse to call that tree +captured. B's rebuild is `worktree add --detach `, `git apply --index staged.patch`, +`git apply unstaged.patch`, the untracked archive, then the workflow files. From 849592b20c3e9fc44504af585e6bb192a384a155 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 00:58:18 +0000 Subject: [PATCH 13/42] Record T011's rollback of the installed payload, the mutation table, the Gate A/B evidence index and the follow-up patch inventory: both rollbacks return every placed file, mode and hook to main's install, the copy-back leaves the receipt stale, and nothing real was written. Steps 2-4 of T011 in specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md for opensoft/openRepoTools#186: four disposable homes (main, the run C candidate from code/, PR #191 as a payload that differs, and rollback by the Gate A copies or by reinstalling the restored source), 18 classified write groups with empty real-repository, real-home and register columns, every evidence document with who verified it, the seven recorded patches re-applied onto their rehearsed bases with matching trees, and what Gate B still lacks. The private receipt ID is rollback-20261009-ILvoVC; its commit follows. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../gate-ab-evidence-2026-10-09.md | 409 ++++++++++++++++-- 1 file changed, 367 insertions(+), 42 deletions(-) diff --git a/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md b/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md index fb8cabc9..a30466ba 100644 --- a/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md +++ b/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md @@ -1,61 +1,386 @@ # Gate A/B evidence, the restore and rollback rehearsal, and the follow-up inventory — October 9, 2026 **Task:** T011 of [tasks.md](tasks.md), under [#186](https://github.com/opensoft/openRepoTools/issues/186). -No task is checked complete by this record. -**Writer:** lane openRepoTools-1, 2026-10-09. +No task is checked complete by this record, and it approves nothing. +**Writer:** lane openRepoTools-1, 2026-10-09T00:36Z–01:1xZ. **Evidence branch:** `004-migrate-to-triad-rollback` (from main `837928a`). -**Private receipt ID:** `rollback-20261009-ILvoVC` (brett-wip `migration/openRepoTools/`). +**Private receipt ID:** `rollback-20261009-ILvoVC` (brett-wip `migration/openRepoTools/`), holding every +script, log, snapshot and digest named below. Machine paths stay there. -This first commit records step 1 (the source restore). Steps 2 to 4 follow in the -next commits on this branch; until they are here, this document is incomplete. +T011 asks for three things. First, a rehearsal of restoring the local candidate and the +installed payload from the preserved artifacts. Second, proof that user work, jobs and claims +survive with no force push, no destructive reset and no workspace-pointer rewrite. Third, +publication of the Gate A/B evidence with the follow-up patch and commit inventory. +Sections 1–3 are the rehearsals, measured today. Sections 4–6 are the index and the +inventory. Section 7 is what Gate B still lacks. + +**Short answer.** +- **Source restore:** passes, 90 checks of 90. +- **Installed payload:** restores to the main install exactly by either path. +- **What the rehearsals wrote:** only private scratch. Nothing in a real repository, the + real home or the register. +- **Gate B is still open.** Section 7 lists what is missing. ## 1. Restore rehearsal of the source -From the Gate A backup `prep-20261007T100559Z` (host-local, read-only), following its -`RESTORE.md` commands with only the placeholder paths replaced by private scratch. -**90 checks passed, 0 failed.** +The source is the Gate A backup `prep-20261007T100559Z` (host-local, read-only). The +rehearsal followed its `RESTORE.md` commands, with only the placeholder paths replaced by +private scratch. **90 checks passed, 0 failed.** - The manifest (414 entries) passes `sha256sum -c`. -- `RESTORE.md`'s `git bundle verify`, run from the backup directory as written, exits **1**: - `error: need a repository to verify a bundle`. Run inside the restored clone it exits 0. - A defect in `RESTORE.md`'s text, recorded here; the backup is read-only and was not edited. -- The `--mirror` restore and the working clone (`clone`, then `fetch --update-head-ok - --prune '+refs/*:refs/*'`) each list the same 33 refs with the same SHA as the live - `for-each-ref` at bundle time. `git fsck --full` prints nothing and exits 0 for both. -- The 2902 objects reachable from the 50 bundled tips are identical - (sorted-list SHA256 `338cb7bdd397076eeced1de0852eeb1510b4f97e02fb3f62de434531c7be327d`). -- Against the live repository **today**, two days on: of the 33 captured refs, 26 are - identical, 7 moved **forward only** (`main`, `origin/main`, `origin/HEAD` to `837928a`; - the three October 7 evidence branches and lane 3's `feat/triad-lane-tooling`), 0 were - deleted and **0 moved by anything but a fast-forward**. 18 refs were added since. -- All 17 captured worktree HEADs resolve in the restore, each tree ID equals the live one, - every index object is present, and the index a checkout of each HEAD makes equals the - captured `index.ls-files-s` byte for byte (17 of 17). -- One tree by `RESTORE.md`'s own procedure, `main` (the capture with workflow files): - `worktree add --detach`, the captured files copied back, the captured exclude file - restored. Its index (100 entries) equals the capture, all 70 copied files match - `files.sha256`, and its tracked status, untracked and ignored lists equal the capture's, - less the 4 cache entries the capture lists as not copied. -- Both dependency commits resolve from the dependency bundle, and each one's own - `tree_digest` gives the recorded value (`39d5c98` → `a44c0165…`, `7f84ca4` → `3be52767…`). +- **`RESTORE.md`'s `git bundle verify`, run from the backup directory as written, exits 1:** + `error: need a repository to verify a bundle`. Run inside the restored clone, it exits 0. + This is a defect in `RESTORE.md`'s text. It is recorded here; the backup is read-only and + was not edited. +- The `--mirror` restore and the working clone each list the same 33 refs, with the same SHA + each, as the live `for-each-ref` at bundle time. The working clone is a `clone`, then + `fetch --update-head-ok --prune '+refs/*:refs/*'`. `git fsck --full` prints nothing and + exits 0 for both. +- The 2902 objects reachable from the 50 bundled tips are identical. The sorted-list SHA256 + is `338cb7bdd397076eeced1de0852eeb1510b4f97e02fb3f62de434531c7be327d`. +- **Against the live repository today, two days on**, the 33 captured refs are: + - 26 identical; + - 7 moved forward only: `main`, `origin/main` and `origin/HEAD` to `837928a`, the three + October 7 evidence branches, and lane 3's `feat/triad-lane-tooling`; + - 0 deleted; + - **0 moved by anything other than a fast-forward**. + + 18 refs were added since the capture. +- **All 17 captured trees:** + - each HEAD resolves in the restore; + - each tree ID equals the live one; + - every index object is present; + - the index a checkout of each HEAD makes equals the captured `index.ls-files-s` byte + for byte (17 of 17). +- **One tree by `RESTORE.md`'s own procedure**: `main`, the capture with workflow files. + - The steps: `worktree add --detach`, copy the captured files back, restore the captured + exclude file. + - Its index (100 entries) equals the capture. + - All 70 copied files match `files.sha256`. + - Its tracked status and its untracked and ignored lists equal the capture's, less the 4 + cache entries the capture lists as not copied. +- Both dependency commits resolve from the dependency bundle. Each one's own `tree_digest` + gives the recorded value: `39d5c98` → `a44c0165…`, `7f84ca4` → `3be52767…`. ### A worktree captured dirty The 17 Gate A captures were all clean, so the dirty case was rehearsed on a disposable -stand-in: a clone of the Gate A bundle given every kind of change (an unstaged edit, a -staged edit, a staged new file, a partly staged file, an unstaged and a staged deletion, -a mode change, an untracked note, ignored workflow configuration and an ignored cache). -Each capture was bundled and rebuilt into a fresh clone that held only the bundle's -objects, as a real restore would. +stand-in: a clone of the Gate A bundle given every kind of change: +- an unstaged edit and a staged edit; +- a staged new file and a partly staged file; +- an unstaged deletion and a staged deletion; +- a mode change; +- an untracked note; +- ignored workflow configuration and an ignored cache. + +Each capture was bundled and rebuilt into a fresh clone that held only the bundle's objects, +as a real restore would. | Capture | Index equal | Status equal | Every file equal (100) | | --- | --- | --- | --- | -| A: the per-worktree block of the backup's own `capture.sh`, verbatim | **no** — 3 staged blobs are in no bundle | **no** | **no** | +| A: the per-worktree block of the backup's own `capture.sh`, verbatim | **no**: 3 staged blobs are in no bundle | **no** | **no** | | B: A plus `git diff --cached --binary`, `git diff --binary` and every untracked file | yes | yes | yes | -**Finding for Gate A.** The October 7 capture takes a tree's index *listing*, its ignored -workflow configuration and nothing else of its working state. That was sufficient only -because all 17 trees were clean. The final capture at the owner-coordinated freeze must -take B's three extra artifacts for any tree that is not clean, or refuse to call that tree -captured. B's rebuild is `worktree add --detach `, `git apply --index staged.patch`, -`git apply unstaged.patch`, the untracked archive, then the workflow files. +**Finding for Gate A.** The October 7 capture takes three things from a tree: its index +*listing*, its ignored workflow configuration, and its HEAD. It takes none of its working +state. That was enough only because all 17 trees were clean. For any tree that is not clean, +the final capture at the owner-coordinated freeze must take B's three extra artifacts, or +must refuse to call that tree captured. B's rebuild is: +1. `worktree add --detach `; +2. `git apply --index staged.patch`; +3. `git apply unstaged.patch`; +4. unpack the untracked archive; +5. copy back the workflow files. + +## 2. Rollback rehearsal of the installed payload + +All of it ran in disposable homes under private scratch, never the real home. +- Every `--install` ran under `env -i` with: + - `HOME` and every `XDG_*` variable pointed into that home; + - `TMPDIR` in scratch; + - `PATH` = refusing `gh`/`curl`/`wget` shims, then `/usr/bin:/bin`; + - `GIT_ALLOW_PROTOCOL=file`. +- **The shims logged 0 calls.** +- Each home was seeded with things an install must leave alone: + - unrelated `settings.json` content: a model, an env, permissions, a status line, and + unrelated `SessionStart`, `UserPromptSubmit` and `Stop` hooks; + - a foreign command in `~/.local/bin`; + - a foreign skill under each of the two skill roots. + +| Stage | Source | Commit | +| --- | --- | --- | +| (a) today's main | a fresh clone of `origin/main` from GitHub, `./openRepoTools --install` | `63810dd` | +| (b) the rehearsed candidate | run C's assembly cloned recursively; installed from **`code/`**, the code leg's own `openRepoTools` | assembly `69b8924`, code `73b04d6` | +| (b″) a candidate whose payload differs | PR #191's head, a fresh clone | `ecd7aa4` | +| (c1) roll back by copy | `cp -a /installed/. "$HOME/"`, `RESTORE.md`'s command | the Gate A copies of the 2026-10-07T04:26:26Z install | +| (c2) roll back by reinstall | `./openRepoTools --install` from the source restored in section 1 | `c4864ac` | + +Notes on the sources: +- **(a):** main moved during the run. #189 landed as `63810dd` at 00:44Z. It touched + only `specs/` and `openspec/`, so its payload is byte-identical to `837928a`'s. +- **(b):** T007's assembly root entry point was **not** used. No + `004-migrate-to-triad-installer` branch is on origin, so (b) installed through + `code/openRepoTools`. +- **(b″):** it is not the T007 candidate. It is used only because a rollback is shown best + where the payload differs. + +**The four homes:** +- H1: (a), (b), (c1). +- H2: (a), (b), (c2). +- H3: (a), (b″), (c1). +- H4: (a), (b″), (c2). + +Every install exited 0, printed `17 of 17 placed` and wrote 29 receipt rows. The first in each +home merged the 2 hook entries; every later one printed `already installed … (unchanged)` for +both. + +| Compared with (a) | H1 (b)→(c1) | H2 (b)→(c2) | H3 (b″)→(c1) | H4 (b″)→(c2) | +| --- | --- | --- | --- | --- | +| (b) placed files (path, mode, sha256) | same | same | **1 differs** | **1 differs** | +| (c) placed files (path, mode, sha256), 29 | same | same | same | same | +| (c) receipt rows (name, destination, sha256) | same | same | **1 stale row** | same | +| (c) the two hook entries | same | same | same | same | +| (c) `settings.json`, canonical; its mode | same; same | same; same | same; same | same; same | +| unrelated settings after every stage = the seed | yes | yes | yes | yes | +| foreign command and skills after every stage | untouched | untouched | untouched | untouched | +| (c) the set of paths under the home | same | same | same | same | + +**Every difference, verbatim:** +- **(b″) vs (a), in H3 and H4.** `~/.local/bin/openRepoTools 755 + 47cfe74ea96e6ce9e56b47220a71e1de8c608af2f51f33cfdffa351410a4f373` became + `71e9ba2c9a06b755c79f3d01b52516ce0c1cfcc3a3c6a92b5fd200976b0787a4`, PR #191's installer. + Both rollbacks put `47cfe74e…` back. +- **(c1) in H3: the copy-back leaves the receipt stale.** The receipt row + `openRepoTools ~/.local/bin/openRepoTools 71e9ba2c…` still names the replaced file. The + file on disk is `47cfe74e…`. `RESTORE.md`'s copy-back restores the 29 files and not + `installed.tsv`. A later `--install` that retires a file asks the receipt whether it wrote + that file, and would get the wrong answer. **(c2) rewrites the receipt** and has no such + row. +- **In every home, (c) vs (a), the whole-home comparison differs in one file only:** + `installed.tsv`. That is its UTC stamp column, plus H3's stale row above. Examples: + - H2: `145f4de8…` vs `09a03339…`; + - H4: `74cfc104…` vs `9e8b2a59…`. +- **The first install changes `settings.json`'s mode from 644 to 600.** The installer prints + that it writes the file back at mode 600. This is by design and is the same in (a) and (c). + +**What this run cannot show.** The rehearsed candidate's payload is blob-identical to +main's: filter-repo keeps every blob. So (b)→(c) in H1/H2 rolls back no bytes, and only +checks the mechanism. Receipts, hooks, unrelated settings and foreign files hold through a +candidate install and a rollback. H3/H4 show a real byte rollback with PR #191's installer. +The real T014/T015 candidate, with #191 and the T007 entry point, must repeat (c2) at its +own exact commits. + +**Run 1, kept as evidence of all-or-none.** The first seed's unrelated `SessionStart` +command contained the word `session-start`. Every install refused it as a second writer of +that hook: 10 runs across main, the candidate, #191 and the restored source, exit 2, +`NOTHING was installed`. After each refusal, every file and `settings.json` was +byte-identical to the seed. The seed was then reworded and all four homes were rebuilt. + +**The rollback targets match what Eagle runs today:** +- the real home's 29 installed files equal (a)'s placed files (path, mode, sha256), read-only; +- the Gate A backup's 29 copies carry the same 29 digests. + +## 3. User work, jobs and claims survive + +**Every command the rehearsals ran that writes anything** — 18 groups in the receipt's +`mutations.tsv` — wrote only into private scratch: +- **temp dir only: 18;** +- **real repository: 0;** +- **real home: 0;** +- **register: 0.** + +The groups are: +- restore clones, the throwaway indexes, the `RESTORE.md` worktree and its copied files; +- the dirty stand-in and its two rebuilds; +- the install sources; +- the seeds; +- 12 installs and 10 refused ones; +- two copy-backs, snapshots and comparisons; +- the patch-application clones. + +**No script pushes, stashes, cleans, kills a process, deletes a branch, removes a worktree, +runs `checkout -f` or `update-ref`, or writes `workspace.yaml`** (`grep` over every script +in the receipt). The one `reset --hard` is in a scratch clone of the rehearsal that section 5 +patched. The live repository was only read, with `GIT_OPTIONAL_LOCKS=0`: `for-each-ref`, +`rev-parse`, `cat-file`, `merge-base`, `ls-tree`, `show`. + +**Real surfaces, before (00:42:08Z) and after (00:53:06Z):** + +| Surface | Result | +| --- | --- | +| The real home's 29 installed files, its receipt, its `~/.claude/settings.json` | unchanged (sha256 and mode) | +| `~/.agents/workspace.yaml` | exists; size, inode, mtime and mode unchanged; its contents were not read | +| The Gate A backup | manifest passes both times; whole-tree listing (size, mode, mtime) unchanged | +| The run C rehearsal | whole-tree listing (size, mode, mtime) unchanged | +| Lane 1's register row and objects (`lanes --all`, `lane-objects`, both with `LANES_NO_FETCH=1`) | the row unchanged but for its age column; objects gained one line, `LANDED opensoft/openRepoTools#189` (00:49:25Z, the coordinator's) | +| The live repository's 25 worktrees | the same 25 before and after; none locked, prunable or detached | +| The live repository's refs | this writer added `refs/heads/004-migrate-to-triad-rollback` and its remote-tracking ref (fast-forward pushes). Other writers' acts moved the rest: `main` to `63810dd`, the T006 and run C branches, `004-migrate-to-triad-evidence-2`, and the #190/#191 heads. | + +The lane's register clone and brett-wip's shared checkout both moved during the window. +Both moves were other actors' commits: the coordinator's LANDING/LANDED lines for #189, and +other lanes' handoffs. This writer ran no register write. + +**What a cutover, or a rollback after one, must preserve.** + +Lane 1's live register row, read-only: +- `openRepoTools-1 LIVE Eagle team-008`; +- window `openRepoTools-1:@20`; +- transcript `df82f553-7803-49c4-8ddf-3f9acdcb8ee0`; +- the shared checkout as its directory; +- holds `CLAIMED opensoft/openRepoTools#186`. + +In-place adoption keeps that directory, so neither the row nor the claim needs rewriting. +A rebinding is T018's, at a safe breakpoint, through `lane-start`/`lanes-edit.sh`. + +`~/.agents/workspace.yaml` exists. Only `resume --workspace` and `wip init` write it, and +neither is in any rollback path; this run left its metadata unchanged. + +**The rollback path in the plan's own words:** revert or pin-bump PRs, plus the preserved +installer, through (c2). It needs: +- no force push: every captured ref has only ever moved forward; +- no destructive reset; +- no pointer rewrite. + +**Acts outside the rehearsals.** These are publication and bookkeeping, listed so nothing is +hidden: +- four fetches in the live repository: three before the baseline, and one after the + rehearsals to re-read the evidence branches that had moved (remote-tracking refs only); +- `lane-worktrees add openRepoTools-1 ort-triad-rollback`: the worktree, its new branch, and + one record in lane 1's host-local tree inventory; +- the commits and fast-forward pushes of this branch; +- one commit to brett-wip `main` (`git add -- `), from a sparse clone in scratch, + never the shared checkout; +- one comment on #186; +- a stray empty file this writer made by mistake in the private `work-20261008` directory, + outside the rehearsal tree, and removed at once. + +## 4. Gate A/B evidence index + +Verification marked "T011" was done by this run: checksums and the git identities it names. + +| Branch, head | Document or receipt | What it proves | Verified by | +| --- | --- | --- | --- | +| main `63810dd` (#189) | `preservation-2026-10-07.md`, `inventory.json` | Gate A refresh (T001–T002): 17 trees, 33 refs, backup `prep-20261007T100559Z` | the writer's restore (97 PASS); lane 3's #189 review (LAND); **T011's restore today (90 PASS)** | +| main `63810dd` (#189) | `adopter-rehearsal-2026-10-07.md`, `adoption-plan.yaml` (run B's, source `e43b243`) | runs A and B at tool `7f84ca4`: 45/49/6 of 100, 0 dropped, bootstrap ok | lane 2's six-check verification (#186 comment 6036838832); #189 review | +| main `63810dd` (#189) | `consumer-reads-2026-10-07.md` | 33 outside consumers not covered, their timing | #189 review | +| main `63810dd` (#189) | `gate-b-checklist.md` | the 46-line Gate B walk of October 7 | #189 review | +| `004-migrate-to-triad-rehearsal-c` `1a26463` (#192, as `71b05cb`) | `adopter-rehearsal-2026-10-08.md`, run C's `adoption-plan.yaml` (source `837928a`) | run C: 65/49/6 of 120, 0 dropped, bootstrap ok; the arrangement T006–T011 used | **T011:** its receipt passes `sha256sum -c` (44); its three log digests are in it; the rehearsed commits resolve (`69b8924`, `593250d`, `73b04d6`, `7f84ca4`); its tree is unchanged by this run. #192 is not reviewed yet | +| `004-migrate-to-triad-root-guidance` `041a273` (#192) | `root-guidance-2026-10-08.md`, `patches/t006/` (5) | T006: 5 collisions, 32 repaired links, leg front doors, 35/35 provenance, bootstrap and validators | **T011:** the 5 patch digests match the document; every patch `git am`s onto its base; the trees equal the recorded ones (section 5). Lane 3's T006 review is not posted | +| `004-migrate-to-triad-ci` `cc7c7a1` (no PR) | `test-roots-and-ci-2026-10-08.md`, `patches/t010-*.patch` (2) | T009/T010: what the split breaks, the root rules, the composed contract, local proofs P1–P4 | **T011:** its 16 published log digests match the host-local logs (`sha256sum -c`, 16 OK); both patches apply (section 5). No review is recorded | +| PR #190 `e3a76b5` | the test-roots change | T009's explicit roots on today's layout | its own CI; lane 3's review is in progress | +| PR #191 `ecd7aa4` | the installer payload-source change | T007/T008's code side and the 15-row matrix as tests | its own CI; lane 3's review is in progress; lane 2's T008 verification is held | +| this branch | `gate-ab-evidence-2026-10-09.md` | T011 | — | +| brett-wip `f0ea2df7` | receipt `prep-20261007T100559Z` | Gate A's private record | **T011:** its manifest copy is the backup's (`2a46dff9…`), and the backup passes it | +| brett-wip `c311a78b` | receipt `adopter-fix-20261007-iM20tv` | runs A and B | **T011:** `sha256sum -c` 58 OK; the six published log digests are in it | +| brett-wip `3de0d37c` | receipt `adopter-fix-20261008-1HztL0` | run C | **T011:** `sha256sum -c` 44 OK | +| brett-wip (this run) | receipt `rollback-20261009-ILvoVC` | T011 | its own `SHA256SUMS` | +| host-local | backup `prep-20261007T100559Z`; rehearsal `work-20261008/rehearsal` | the preserved artifacts | **T011**, read-only, before and after | + +No private receipt exists for T006 or T009/T010. Their logs are host-local only, in the +private prep area. + +## 5. Follow-up patch and commit inventory + +Every recorded patch for the rehearsed arrangement was re-applied by this run with `git am`, +in a fresh clone out of run C, onto the commit it names. Trees are compared, not commit +hashes: the committer differs, so the hashes do. + +| # | Patch (branch, head) | sha256 | Leg | Applies to | Rehearsed result | Re-applied tree (T011) | At T014 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| 1 | `patches/t006/assembly-0001-…front-door…` (root-guidance `041a273`) | `cd69c2a9…c5d096` | assembly | split `69b8924` | `3361812` | — (the three stack, below) | **applies as-is** onto the real split, after the frozen source's root files are checked unchanged | +| 2 | `patches/t006/assembly-0002-Bump-spec-leg-to-39c18877e042…` | `574c0df1…3647f9` | assembly | `3361812` | `1024f18` | — | **regenerated**: `bump-leg.py` against the real spec commit | +| 3 | `patches/t006/assembly-0003-Bump-code-leg-to-6169e8e96164…` | `a7f8521b…ec14aa` | assembly | `1024f18` | `3e87e39` | 1–3: `6eb8145e…`, as recorded | **regenerated**: `bump-leg.py` against the real code commit | +| 4 | `patches/t006/code-0001-…own-front-door…` | `74b98ff9…2ae1ec1` | code | code `73b04d6` | `6169e8e` | `ca65c3b9…`, as recorded | **applies as-is** | +| 5 | `patches/t006/spec-0001-…own-front-door…` | `c11070f0…8210b3` | spec | spec `593250d` | `39c1887` | `10256135…`, as recorded | **applies as-is** | +| 6 | `patches/t010-code-leg-tests-workflow.patch` (ci `cc7c7a1`) | `7ee3c6d0…786c3b` | code | code `73b04d6` | `b95d667` | alone: exit 0; on #4: exit 0 (`007509d2…`) | **applies as-is**; its env names mean something once PR #190 is in the code | +| 7 | `patches/t010-assembly-exact-pin-job.patch` | `6ee09f00…74670b` | assembly | split `69b8924` (proof commit `386be81` sat on the rehearsal-only bump `6ccdaf8`) | `386be81` | alone: exit 0 (`c37aa4f0…`); on #1–3: exit 0 (`b351c8e9…`) | **applies as-is** (a new file); `bash code/tests/run.sh` with `OPENREPOTOOLS_COMPOSED=1` needs PR #190 in the code leg | +| — | T007's assembly root `openRepoTools` entry point | — | assembly | — | — | — | **absent**: no `004-migrate-to-triad-installer` on origin; PR #191's body says it will be recorded there | +| — | the rehearsal-only bump `6ccdaf8` (code leg to `8ebf4ec`, PR #190 on `73b04d6`) | not a patch file | assembly | — | — | — | **regenerated**, like #2–3 | + +**Counts:** +- **7 recorded patches:** T006 5, T010 2. +- **5 apply as-is:** #1, #4, #5, #6, #7. +- **2 regenerate at T014:** #2 and #3, plus the T010 proof's bump `6ccdaf8`, which has no + patch file. +- **T007's entry point: 0 recorded.** + +`git diff --check` exits 0 over the assembly stack (4) and the code stack (2). + +**They stack:** +- **assembly:** #1, #2, #3, then #7; +- **code:** #4, then #6, then PR #190's code diff (`apply --check` 0), then PR #191's on top + of that (`apply --check` 0, also with `--3way`); +- **spec:** #5 alone. + +This is a text-level check only. **No test ran on the stacked candidate.** + +## 6. Pull requests to main with layout-neutral changes + +| PR | Head | Files | State at 00:5xZ | +| --- | --- | --- | --- | +| #190 `feat/test-roots-for-the-triad` | `e3a76b5` | `tests/` only (8 files) | open; `guard-launch-mode`, `tests-windows`, `parse-macos` and SonarCloud green; `tests` and `tests-no-submodule` running; no `ready` label | +| #191 `feat/installer-payload-source-resolution` | `ecd7aa4` | `openRepoTools`, two test files | open; the same green set, `tests` and `tests-no-submodule` running. At `e406e0e` (00:3xZ), `guard-launch-mode` and `tests-windows` had failed; `ecd7aa4` is the push after that | + +Both are based on `c4864ac`. Both touch `tests/test_openrepotools_command.py`, and they +stack cleanly in the order #190 then #191. Neither touches a root front-door file, so T006's +`assembly-0001` is unaffected by either. + +Lane 3's `feat/triad-lane-tooling` (`c45a452`) is on origin with no PR. + +## 7. What Gate B still lacks + +Gate B is "complete local rehearsal evidence, restore rehearsal, reviewed follow-up patches, +resolved mapping and a passing compatibility test matrix" (plan.md). Against that: + +1. **Restore rehearsal.** Done for the source and for the installed payload, with two + findings: + - for any tree that is not clean, the October 7 capture procedure is not a restore + (section 1); + - `RESTORE.md`'s copy-back leaves `installed.tsv` stale. Rollback should be (c2): a + reinstall from the preserved source, or the copy-back followed by restoring the + receipt. + + `RESTORE.md`'s `git bundle verify` line also needs a repository to run in. +2. **T007's root entry point is not published.** Without it the inventory has no assembly + `openRepoTools`. PR #191, the code side, is open. +3. **No composed candidate carrying every follow-up has run the suite.** That candidate is + T006 + T010 + #190 + #191 + the entry point. It was only checked to apply (section 5). + The October 8 proof P1 (285 passed, 2 failed) ran on run C's code leg with #190 alone: + - one failure is the leg's missing `.gitattributes`, which T006's `code-0001` adds; + - the other is two lane-suite assertions that depend on run-root length. + + Lane 2's run C composed-suite discovery has not reported on #186. The hosted-runner part + (Windows, macOS) is blocked until real legs exist (checklist line 35). +4. **Reviews.** Lane 3's reviews of #190 and #191 are in progress. No review is recorded for + the T006 patch set (#192) or the T010 patches (checklist line 46). +5. **Mapping.** Main carries run B's plan, source `e43b243`, which survives only as + `refs/pull/188/head`. Run C's plan, source `837928a`, is in #192, unlanded. +6. **Gate A**, for orientation: + - the final capture at an owner-coordinated freeze, taking finding 1; + - installed-file receipts for py-bench and other profiles and workstations; + - owner dispositions. + +**Gate C is Brett Heap's explicit approval.** It names the source and standard revisions, +the leg repositories, their visibility and the final plan. Nothing here asks for it or +infers it. + +## 8. Log SHA256s + +The logs and snapshots are in the private receipt. + +| Log | SHA256 | +| --- | --- | +| `10-restore.log` (section 1, 90 PASS) | `bb3a5829d3a1e5ea6924331456b9f6b81b4a8380d878617344adf2807e70a848` | +| `11-dirty.log` (section 1, A 0/3, B 3/3) | `e6d1448fd18ebe2889b7c564307024ef8e2e49b48e2ef642838c8e0f89d1a9da` | +| `21-clones.log` (section 2 sources) | `d4b39b1cabeae6d2395f5db3610157689863af40bb2fc1354a2116ddaf855139` | +| `22-installs.log` (section 2, 12 installs, 2 copy-backs) | `4a7beffda610253de630b389f1be09c07df660df4d06a8745e10ed8d1703f2a6` | +| `23-compare.log` (section 2 comparisons) | `dca56f959f8075ac0054745965896dd43e4eb9b58cc07409b89d583e0f91e72c` | +| `24-patches.log` (section 5) | `f6a385eef8a32d683643aa7637e31cc88900af69888836c7fef7923cfac6de66` | +| `25-prs.log` (section 5, the two PRs) | `4ae121cd04df1bc87b796ec2c6969ab355273582f763375b13f9f7adb5787300` | +| `network-shim-calls.log` (empty) | `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855` | +| `mutations.tsv` (section 3) | `76fc8521b1738fde5471f99e1641c6d901ebf72a65bb677d612700caf7ed77b1` | +| `refs-vs-live-now.tsv` (section 1) | `d3b5de9d85fe9ae8fe8a7a965d9015cb177c1e67d77beb30639cd51a02644249` | +| `restore-trees.tsv` (section 1, 17 trees) | `ace098036ebcec7fe47dd56a0bb7f38b97b9b10bc4753c9dd2d652349a57db98` | + +This record authorizes no conversion, repository creation, merge, default-branch move or +lane rebinding. From fa44d75a7a4ed6cb255ecd74b75a7e27b01e9741 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 01:13:39 +0000 Subject: [PATCH 14/42] Name the T011 private receipt's brett-wip commit, 56f1e7c4, and say why the hygiene selector did not run on this document while the two all-files hygiene checks find nothing in it. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../gate-ab-evidence-2026-10-09.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md b/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md index a30466ba..e3b3f337 100644 --- a/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md +++ b/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md @@ -4,7 +4,8 @@ No task is checked complete by this record, and it approves nothing. **Writer:** lane openRepoTools-1, 2026-10-09T00:36Z–01:1xZ. **Evidence branch:** `004-migrate-to-triad-rollback` (from main `837928a`). -**Private receipt ID:** `rollback-20261009-ILvoVC` (brett-wip `migration/openRepoTools/`), holding every +**Private receipt ID:** `rollback-20261009-ILvoVC` (brett-wip `migration/openRepoTools/`, commit +`56f1e7c4`, 424 files under one `SHA256SUMS`), holding every script, log, snapshot and digest named below. Machine paths stay there. T011 asks for three things. First, a rehearsal of restoring the local candidate and the @@ -273,7 +274,7 @@ Verification marked "T011" was done by this run: checksums and the git identitie | brett-wip `f0ea2df7` | receipt `prep-20261007T100559Z` | Gate A's private record | **T011:** its manifest copy is the backup's (`2a46dff9…`), and the backup passes it | | brett-wip `c311a78b` | receipt `adopter-fix-20261007-iM20tv` | runs A and B | **T011:** `sha256sum -c` 58 OK; the six published log digests are in it | | brett-wip `3de0d37c` | receipt `adopter-fix-20261008-1HztL0` | run C | **T011:** `sha256sum -c` 44 OK | -| brett-wip (this run) | receipt `rollback-20261009-ILvoVC` | T011 | its own `SHA256SUMS` | +| brett-wip `56f1e7c4` | receipt `rollback-20261009-ILvoVC` | T011 | its own `SHA256SUMS` (423 entries) | | host-local | backup `prep-20261007T100559Z`; rehearsal `work-20261008/rehearsal` | the preserved artifacts | **T011**, read-only, before and after | No private receipt exists for T006 or T009/T010. Their logs are host-local only, in the @@ -382,5 +383,14 @@ The logs and snapshots are in the private receipt. | `refs-vs-live-now.tsv` (section 1) | `d3b5de9d85fe9ae8fe8a7a965d9015cb177c1e67d77beb30639cd51a02644249` | | `restore-trees.tsv` (section 1, 17 trees) | `ace098036ebcec7fe47dd56a0bb7f38b97b9b10bc4753c9dd2d652349a57db98` | +**This document's own check.** `tests/run.sh -k repo_hygiene` did not run: +- the wrapper waited 5 minutes (00:57–01:02Z) on 7 other repositories' pytest processes; +- its exact pytest line under `flock` on the workstation lock then queued 10 minutes behind + the T007/T008 writer's full suite and was stopped before pytest started. + +Two hygiene tests read every tracked file: the host-absolute-path test and the act-0 citation +test. Their own regexes, taken from `tests/test_repo_hygiene.py`, find nothing in this +file. CI is the suite of record. + This record authorizes no conversion, repository creation, merge, default-branch move or lane rebinding. From 51993a40cef56424e9291a62572c199a2e36a5c1 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 01:21:19 +0000 Subject: [PATCH 15/42] Record T007's assembly entry point as a patch and T007/T008's installer evidence for the triad migration. The root openRepoTools of the assembly cannot land on main before the split, because its path is the implementation's, so it is kept here as a format-patch of the commit made in a clone of run C's rehearsed split; git am on a fresh clone reproduces its tree. installer-2026-10-08.md records the arrangement relied on, the pin grammar, the one-liner contract and how it compares with T006's README sentence, the 15-row matrix with its tests and red-at-base results, the runs, lane 2's installer rows, and what remains for T009, T010, T014 and T015. Nothing here authorizes a real conversion. Part of #186. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../installer-2026-10-08.md | 187 +++++++++ .../patches/t007-assembly-entry-point.patch | 386 ++++++++++++++++++ 2 files changed, 573 insertions(+) create mode 100644 specs/004-migrate-to-triad/installer-2026-10-08.md create mode 100644 specs/004-migrate-to-triad/patches/t007-assembly-entry-point.patch diff --git a/specs/004-migrate-to-triad/installer-2026-10-08.md b/specs/004-migrate-to-triad/installer-2026-10-08.md new file mode 100644 index 00000000..03e6b60e --- /dev/null +++ b/specs/004-migrate-to-triad/installer-2026-10-08.md @@ -0,0 +1,187 @@ +# Installer payload source and the assembly entry point: October 8–9, 2026 + +**Task:** T007 and T008 of [tasks.md](tasks.md), under [#186](https://github.com/opensoft/openRepoTools/issues/186). Neither task is checked complete by this record. + +**Code:** [#191](https://github.com/opensoft/openRepoTools/pull/191), branch `feat/installer-payload-source-resolution` (head `2411f40`, based on `c4864ac`). + +**Assembly entry point:** [patches/t007-assembly-entry-point.patch](patches/t007-assembly-entry-point.patch). It cannot land on `main` before the split, because its path is the implementation's. + +**Writer:** lane openRepoTools-1. + +## The rehearsed arrangement relied on + +Run C (`/workspace/projects/.openrepotools-private-prep/work-20261008/rehearsal/README.txt`), cloned out of it read-only: + +| Repository | Commit | +| --- | --- | +| Source (`main` when cloned) | `837928a1029ac53b050a9bdcbc0ef4055b10719b` | +| Assembly split (`adopt/three-repo-shape`) | `69b89245029d0339afd3317df7c9b1b3bbaf7fc1` | +| Spec leg | `593250d600ca291f610671d993c2a6fc36cd690e` | +| Code leg | `73b04d637634f897ad24e22ee4c12bed287172d6` | +| Code's nested openRepoShape | `7f84ca42ca86a8902928345109d2bf6bad87bd91` | + +Clone commands: + +```sh +git -c protocol.file.allow=always clone -b adopt/three-repo-shape /source +git -C -c protocol.file.allow=always \ + -c url./mirror/openRepoShape.git.insteadOf=https://github.com/opensoft/openRepoShape.git \ + submodule update --init --recursive +``` + +The brief named run B (split `2726d3a`, code `6d52162`, spec `558a12b`). The 2026-10-08 bench restart wiped it from `/tmp`. Before run C existed, this writer rebuilt run B in its own scratch to see whether it reproduces. The rebuild used the same tool (`adopt-project.py` at `7f84ca4`), the same published plan (`004-migrate-to-triad-rehearsal` at `36c0324`), the same source (`e43b243`), local bare remotes, and refusing `gh`/`curl` shims (zero calls). + +- `execute` exited 0 with the same 45/49/6/0 accounting and 18 added paths. +- Spec reproduced as `558a12b` and code as `6d52162`, byte for byte. +- The split (`fea6ad8`) differed from `2726d3a` only in its commit time and in three files that embed local paths: `.gitmodules`, `shape/README.md` and `shape/AGENTS.md`. + +Run C, which the coordinator made the arrangement of record, superseded it. + +### The pin grammar validated + +This is `contracts/code-pin.yaml` as run C's adopter writes it. It is read with `read` and `case`; there is no YAML runtime. + +| Field | Required value | +| --- | --- | +| `schema_version` | `1` | +| `kind` | `pinned_contract_manifest` | +| `leg_role` | `code` | +| `source_repository` | an owner/name slug; never inferred by appending `-code` | +| `submodule_path` | one path segment | +| `commit` | 40 lowercase hex | +| `revision_kind` | `commit` | +| `digest_algorithm` | `sha256` | +| `digest_definition` | `sorted-ls-tree-r-v1` | +| `digests.tree_sha256` | 64 lowercase hex | +| `verify_pin`, `resync_runbook` | present | + +Each key that is read must appear once. The file must be LF-only, quotes must close, and backslashes are refused. + +## T007: the assembly entry point + +- Commit `8ed83b709a616a900601bf67d57c78f6d1525880` sits on `69b8924` in the writer's clone, on branch `t007-assembly-entry-point-v2`. + - Tree: `79db0a40c0148a4649af49d3fd91aecaf33b7ceb`. + - `openRepoTools`: blob `4c2525d9b01efd5d593073c9f1b3e1cfbdb90756`, mode 100755, sha256 `3b0d59f5cc2701edc9cdf088efeb34d476a4fefc456f7b1b5413f364ee32c5da`. + - The patch file's sha256 is `361c037d09ee55996a4cac6f52c2374fdb801bfa2abf781c409b40e9aa0a277d`. + - `git am` of the patch on a fresh clone of run C's split reproduces tree `79db0a4` exactly. + - It supersedes `a02a45c` (branch `t007-assembly-entry-point`, tree `a9fa46e`). The two differ only in `recorded_gitlink`, which now reads the gitlink with `read` instead of `awk` (see the runs below). +- **In a checkout,** it requires the pin (strict grammar), the gitlink (index first, then HEAD) and `code/openRepoTools`'s raw bytes (`hash-object --no-filters`) to name one commit. Then it `exec`s `code/openRepoTools` with every argument. That is the same process, so the exit status, signals and the implementation's own temporary-file cleanup are the implementation's. There are no temporaries of its own. +- **Anywhere else** (stdin, or a copy of the file on its own), it does four things: + - it resolves `OPENREPOTOOLS_REF` once, `gh` first and `curl` second; + - it reads `contracts/code-pin.yaml` at that commit; + - it fetches `openRepoTools` from the pin's `source_repository` at the pinned commit (the file must begin `#!`); + - it runs it from stdin, as the one-liners always ran it, with `OPENREPOTOOLS_REF` set to the resolved commit, so the implementation reads the same pin and re-checks the gitlink itself. +- **It has no installer mechanics.** It does no placement, receipt, merge or retirement. +- **It assumes nothing the legs may not carry.** That covers `.gitattributes`, README, AGENTS and LICENSE (T006 §10.2). +- **It is bash 3.2-clean.** Arguments are forwarded as `${1+"$@"}`, because a bare `"$@"` under `set -u` is unbound in 3.2. +- **Its pin reader is a deliberate copy** of the implementation's (10 definitions: three character constants, the six pin-reader functions and `recorded_gitlink`). `test_r15_composed_the_root_entry_points_pin_reader_is_the_implementations` fails when the two differ. + +**Rehearsal-only pin moves (not part of the patch).** + +- Openrepotools' PR #191 was applied to code `73b04d6` in a clone. It was pushed only to the writer's own bare copy of the code remote, never to run C. +- The standard's `scripts/bump-leg.py` (tool `7f84ca4`, with `--local-remote-dir` pointing at that copy) made the lockstep commits. Its validators printed `pins ok` and `manifest ok` and recomputed both digests: + - on `t007-assembly-entry-point`, `9321107` moved the code pin to `ecb4ef3` (PR at `e406e0e`), and `58e3ac5` moved it to `d12345b` (PR at `ecd7aa4`); + - on `t007-assembly-entry-point-v2`, `7bbd60b` moved it to `ea17f30` (PR at `2411f40`). +- Each bump printed bump-leg's `NEXT … push -u origin …` line. It was deliberately not followed. +- The clone's push URL is disabled. + +### The one-liner contract + +| Command line | What it resolves | +| --- | --- | +| `curl -fsSL https://raw.githubusercontent.com/opensoft/openRepoTools/main/openRepoTools \| bash -s -- --install` (README:228; openRepoShape's `--install` pointer, openRepoShape:280 at `7f84ca4`, byte for byte) | 1. The root entry point at `main`.
2. `main` resolved once to assembly commit *A*.
3. `contracts/code-pin.yaml` at *A*.
4. `openRepoTools` from `source_repository` at `commit`.
5. That implementation, with `OPENREPOTOOLS_REF=A`, resolves *A* (a commit, which cannot move), lists *A*'s tree, re-reads and re-checks the pin, checks the gitlink, confirms the commit exists in `source_repository`, fetches the 23 payload files from there, and places 29 files and 2 hook entries. | +| `gh api repos/opensoft/openRepoTools/contents/openRepoTools -H 'Accept: application/vnd.github.raw' \| bash -s -- --install` (README:236–237) | The same, through `gh`. | +| `./openRepoTools --install` in an assembly checkout | Checks that pin, gitlink and implementation bytes agree, then `exec`s `code/openRepoTools`. The implementation checks every payload file against the pinned commit and copies offline. | +| `openRepoTools --install`, installed in `~/.local/bin` | The implementation alone. Its default `REPO` is the assembly, which it resolves itself. It never needs the root file or a checkout again. | + +With run C's identities, *A* = `69b8924` and the pin names `opensoft/openRepoTools-code` at `73b04d6`. `73b04d6` predates T007, so the real cutover must pin a code commit that carries this change. That is T015's to verify at the assembled head. + +Transcript of row 3 in the writer's clone (assembly `7bbd60b`, code pin `ea17f30`), into a throwaway `HOME` with `gh`/`curl` shims that log and refuse: + +- exit 0, and **0 network calls**; +- first line: `openRepoTools: source: the code mounted at /code, opensoft/openRepoTools-code at ea17f302310fa75fdf61ad7199912a74971513bd as pins it; every payload file was checked against that commit and nothing fetched`; +- 29 `installed at` lines and 29 receipt rows; +- the installed `openRepoTools` is byte-identical to `code/openRepoTools`, the implementation and not the root file; +- `--version` prints `openRepoTools (opensoft/openRepoTools @ main)`; +- `./openRepoTools nope` exits 2. + +**Against T006's README sentence** (root-guidance-2026-10-08.md §10.9: "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 contract matches, with two precisions: + +1. Remotely, the implementation is fetched from the pin's `source_repository` at the pinned commit, which is the repository `code/` mounts. +2. In a one-liner run, `--version` prints the resolved commit rather than `main`, because the handover freezes `OPENREPOTOOLS_REF`. + +## T008: the compatibility matrix + +The tests are in `tests/test_install_payload_source.py` on #191. The fixtures build their own GitHub out of bare repositories, answered by one fake for `gh api`, `gh repo` and `curl` that runs `git` and logs every request. There is no real `gh` or `curl` on `PATH`, and every `$HOME` is temporary. + +| # | Row | Tests | Base `c4864ac` | After | Composed (run C clone) | +| --- | --- | --- | --- | --- | --- | +| 1 | Raw/gh one-liners | `test_r01_the_one_liner_against_a_single_repository_installs_as_before`; `test_r01_the_one_liner_against_an_adopted_assembly_installs_the_pinned_code[gh,curl]`; `test_r01_composed_the_documented_one_liners_resolve_through_the_root_entry_point[openRepoShape,gh]`; `test_r01_composed_the_root_entry_point_delegates_locally`; `test_r01_composed_a_signal_reaches_the_implementation_and_its_temporaries_go` | pass / 2 red / skip | pass | pass | +| 2 | Offline local single / adopted checkout | `test_r02_a_complete_single_repository_tree_installs_offline_byte_for_byte` (sentinel REPO/REF, zero requests); `test_r02_an_adopted_checkout_installs_its_pinned_code_offline` | pass / red | pass | pass | +| 3 | Explicit local code feature | `test_r03_an_explicit_code_feature_checkout_installs_its_own_bytes_and_names_them`; `test_r03_an_incomplete_code_feature_checkout_is_refused_not_completed_from_elsewhere` | 2 red | pass | pass | +| 4 | Installed reinstall / skill-only | `test_r04_an_installed_copy_reinstalls_from_the_adopted_source_without_any_checkout`; `test_r04_an_installed_copy_on_a_single_repository_keeps_the_sibling_rule` | red / pass | pass | pass | +| 5 | Override / adopted fork | `test_r05_a_single_repository_fork_named_by_the_override_installs_as_before`; `test_r05_an_adopted_fork_follows_its_own_pin_not_a_guessed_code_name` | pass / red | pass | pass | +| 6 | Branch, tag, commit, slash refs | `test_r06_every_ref_form_is_resolved_once_and_every_payload_names_one_commit` (4 forms × 2 layouts) | 8 red | pass | pass | +| 7 | Branch moves between downloads | `test_r07_a_branch_that_moves_between_downloads_never_mixes_versions[adopted,single]` | 2 red (single installed mixed versions) | pass | pass | +| 8 | Missing pin (single) / partial or wrong pin | `test_r08_a_single_repository_without_a_code_pin_is_permitted`; `…an_adoption_manifest_without_a_code_pin_is_refused`; `…a_malformed_code_pin_is_refused_never_read_as_a_single_repository` (19 malformations); `…a_pin_the_gitlink_disagrees_with_is_refused`; `…a_pin_naming_a_commit_its_repository_lacks_is_refused` | pass / 22 red (base exited 0 installing the assembly root's decoys) | pass | pass | +| 9 | API answer missing/malformed, auth, transport | `test_r09_an_api_failure_is_a_refusal_never_a_fallback_to_a_single_repository` (5 steps × 401/403/404/transport/garbage, both transports) | 25 red (decoys installed) | pass | pass | +| 10 | Changed gitlink / payload, incomplete local set | `test_r10_a_local_gitlink_that_disagrees_with_the_pin_is_refused_before_staging`; `…a_changed_local_payload_is_refused_before_staging`; `…an_incomplete_local_adopted_payload_is_refused_without_a_fetch` | 3 red | pass | pass | +| 11 | Missing command/skill/alias, failed merge | `test_r11_a_payload_file_missing_from_the_pinned_code_changes_nothing[park,skills/restart/SKILL.md,commands/swap.md]`; `test_r11_a_settings_merge_that_cannot_be_computed_changes_nothing` | 4 red | pass | pass | +| 12 | Modes, receipts, targets, foreign files, hooks | `test_r12_an_adopted_install_keeps_modes_receipts_targets_and_what_is_not_ours` | red | pass | pass | +| 13 | WIP init / template lookup | `test_r13_wip_init_takes_the_standard_from_codes_dependency_pin_not_the_assemblys` | red (seeded from the assembly's decoy pin commit) | pass | pass | +| 14 | Missing nested dependency | `test_r14_with_no_nested_standard_anywhere_the_dependency_pin_alone_selects`; `test_r14_the_documented_upstream_skip_is_unchanged`; `test_r14_composed_acceptance_requires_the_nested_dependency` | red (seeded from `main`) / pass / skip | pass | pass | +| 15 | bash 3.2, Windows/WSL | `test_r15_the_adopted_path_holds_under_macos_one_true_awk`; `test_r15_the_installer_uses_nothing_bash_3_2_lacks`; `test_r15_the_three_lists_stay_one_line_arrays_a_consumer_parses`; `test_r15_composed_the_root_entry_points_pin_reader_is_the_implementations`; `test_r15_composed_the_root_entry_point_parses_and_uses_nothing_bash_3_2_lacks` | red / pass / pass / skip / skip | pass | pass | + +The module is `WINDOWS_SKIP`, like every bash-driven test here. The composed tests skip in standalone code, and under `OPENREPOTOOLS_COMPOSED=1` (T009's convention) they fail instead. + +### Runs + +Every local run used `tests/run.sh`'s own invocation under its lock: the coordinator's deviation for a census that never reaches zero, with `PATH=/usr/bin:$PATH` because this profile's `~/bin/python3` is a venv without pytest. + +| Run | Code | Selection | Result | +| --- | --- | --- | --- | +| Red at base, 2026-10-08T21:04–21:10Z | `6337a52` (installer `c4864ac`) | the module | **75 failed, 8 passed, 6 skipped**. The 8 are the single-repository preservation tests, two static checks and the documented-skip check; every red was checked against its first failing assertion. | +| Green | `e406e0e` | module + `openrepotools_command`, `install_skill_and_hook`, `install_by_rename`, `wip_init` | **318 passed, 7 skipped** (the composed cases) | +| CI on `e406e0e` | | | `test_the_installer_reaches_only_this_repository` failed. Every fetching line must name `$REPO`; the helpers took the repository as an argument. Fixed in `ecd7aa4` (one current source, `${PAYLOAD_REPO:-$REPO}`), with no behaviour change. | +| Green with hygiene | `ecd7aa4` | the above + `repo_hygiene`, `upstream_pin` | **479 passed, 5 failed, 7 skipped**. All 5 are `test_wip_init_and_the_lane_helpers_read_one_pointer_file_the_same_way` cases whose failing assertion is the exit status of `lanes-edit.sh`, which is unchanged from base, as are its test and conftest. They passed in the green run above. This session inherits `LANES_WORKSPACE_ROOT` and other `LANES_*` binding variables, and with those unset 3 still fail on workstation state. CI's clean runner is the record. | +| Composed, strict | entry point `a02a45c` + code `ecb4ef3` | the module | **90 passed, 0 skipped** | +| Composed, strict, 2026-10-09T00:47–01:01Z | entry point `a02a45c` + code `d12345b` (= `ecd7aa4`) | module + install suites + `repo_hygiene` | **395 passed, 19 failed**. 17 `repo_hygiene` checks and `test_the_help_and_the_readme_say_the_mode_the_receipt_is_born_at` are T006 §10.6's front-door findings (T009's): they read root documents the code leg does not carry. The 19th was this change's. `test_without_jq_it_refuses_and_places_nothing` saw exit 2 but `line 734: awk: command not found` instead of "needs `jq`": the mounted-leg path read the gitlink with `awk`, which the test's minimal `PATH` lacks. Fixed in `2411f40` and in the entry point (`8ed83b7`) by reading it with `read`. | +| Green with the `read` fix, 2026-10-09T01:05–01:20Z | `2411f40` | as "Green with hygiene" | **479 passed, 5 failed, 7 skipped**: the same five `lanes-edit.sh` cases, nothing else. | +| Composed, strict | entry point `8ed83b7` + code `ea17f30` (= `2411f40`) | module + install suites + `repo_hygiene` | Queued behind the shared lock at the checkpoint (2026-10-09T01:2xZ). The expected result is the 18 front-door findings only; to be recorded on resume. | +| CI on `ecd7aa4` | | | SonarCloud, `guard-launch-mode`, `parse-macos` and `tests-windows` passed; `tests` and `tests-no-submodule` were still running when `2411f40` superseded it. | +| CI on `2411f40` | | | Queued at the checkpoint; to be recorded on resume. | + +## Lane 2's installer rows + +These are the 15 rows of "The installer's own assumptions" in `consumer-reads-2026-10-07.md`. + +| Row (`openRepoTools` at `c4864ac`) | Disposition | +| --- | --- | +| `:6-11`, README one-liners | Covered: the entry point patch and the composed one-liner tests. | +| `:70-72` `REPO`/`REF`/`SELF` | Covered: the meanings are unchanged; the adopted source is resolved through the pin. | +| `:258-275` `fetch_from_repo` | Covered: it reads the resolved source, still `gh` first. | +| `:282-285, 338, 962-990` `INSTALLABLES`/`collect_commands` | Covered: the names sit at the code root, and the three arrays stay one line (`test_r15_…one_line_arrays…`, the W4 constraint). | +| `:1290-1398` `collect_skills` from `~/.local/bin` | Covered: rows 4 and 11. | +| `:2502-2517` `wip_shape_ref` | Covered: rows 13 and 14. `main` stays the fallback only for a source that carries no pin. | +| `:2519-2523` `wip_fetch_template_file` | Unchanged. Row 13 shows the template comes from the standard the code pins. | +| `:1218-1222` `venv_note` names `docs/README-lanes.md` | **Not changed.** It is a comment this PR does not touch, and it names a spec-leg path after the split. Listed for T014's reconciliation (also T006 §10.9). | +| `:28-56, 393-396` counts vs design `:61-64` | Recorded: 17 commands, 23 payload files, 29 placed files, 31 artifacts on this layout, derived from the lists. The design's 15/27/29 are `daed209`'s. | +| Paths unmapped against `daed209` | Not this task's (plan regeneration, T003/T012). Run C's accounting covers all 120 entries. | +| `repos.tsv` leg aliases | Not this task's (lane 3; waits for real legs). | +| `:1320, 1352, 2211` hooks, `workspace.yaml` | Unchanged. | +| `lane-worktrees`/`lanes-edit.sh` lane roots | Not this task's (lane 3, I4). | +| `:291-292, 298-300, 322` consumer comments | Unchanged. | +| `.gitmodules`, `upstream/`, `contracts/openreposhape-pin.yaml` | Covered: the dependency unit is code's, and `wip init` reads code's pin. | + +workBenches-side: the sentinel REPO/REF partial row is covered (`test_r02_…byte_for_byte`), and so is W4 (one-line arrays). W1, W3 and W8 (re-vendoring from `openRepoTools-code`) are workBenches follow-ups that wait for real legs; nothing here changes workBenches. + +## For other tasks + +- **T009:** `test_the_installer_reaches_only_this_repository` holds because every fetching line names `$REPO`. Its docstring should say that the one other repository reached is the one `$REPO`'s own validated pin names. The composed hygiene results above are T006 §10.6's front-door findings. +- **T010:** a composed run whose code leg is checked out at a commit other than its pin will see `--install` from the mounted leg refuse (changed payload). Run install tests from the pinned state or from a standalone copy. +- **T014:** `venv_note`'s `docs/README-lanes.md` comment. +- **T015:** validate at the real assembled head, with a code pin that carries this change, against the real GitHub. That includes the one-liners and `bump-leg.py`'s lockstep, and the 3.2 parse of the root entry point (`parse-macos` covers the implementation only). + +## What this does not do + +It creates no repository, pushes nothing to run C, moves no default branch, rebinds no lane and authorizes no real conversion. Gate B remains open; Gate C is Brett Heap's word. diff --git a/specs/004-migrate-to-triad/patches/t007-assembly-entry-point.patch b/specs/004-migrate-to-triad/patches/t007-assembly-entry-point.patch new file mode 100644 index 00000000..1ea69a70 --- /dev/null +++ b/specs/004-migrate-to-triad/patches/t007-assembly-entry-point.patch @@ -0,0 +1,386 @@ +From 8ed83b709a616a900601bf67d57c78f6d1525880 Mon Sep 17 00:00:00 2001 +From: Brett Heap <1513478+brettheap@users.noreply.github.com> +Date: Fri, 9 Oct 2026 01:02:51 +0000 +Subject: [PATCH] Add the assembly's openRepoTools front door, which runs the + implementation the code pin names and nothing else. + +T007 of specs/004-migrate-to-triad/tasks.md (opensoft/openRepoTools#186). The +documented one-liners fetch openRepoTools from this repository's root, and +after the split the installer is the code leg's. This file keeps that line +working and adds no installer mechanics: in a checkout it requires the pin, +the gitlink and the implementation's bytes to name one commit and then execs +code/openRepoTools with every argument (same process, so exit status and +signals are the implementation's); anywhere else it resolves the ref once, +reads contracts/code-pin.yaml at that commit, fetches openRepoTools from the +pinned repository at the pinned commit, and runs it from stdin with +OPENREPOTOOLS_REF set to that commit, so the implementation reads the same pin. +The pin reader is a deliberate copy of the implementation's, guarded by the +code leg's tests/test_install_payload_source.py. + +Lane: openRepoTools-1 +Co-Authored-By: Claude Opus 5.5 +--- + openRepoTools | 351 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 351 insertions(+) + create mode 100755 openRepoTools + +diff --git a/openRepoTools b/openRepoTools +new file mode 100755 +index 0000000..4c2525d +--- /dev/null ++++ b/openRepoTools +@@ -0,0 +1,351 @@ ++#!/usr/bin/env bash ++# SPDX-License-Identifier: Apache-2.0 ++# ++# openRepoTools — THE ASSEMBLY'S FRONT DOOR, and nothing else. ++# ++# The installer is the CODE leg's `openRepoTools`. This file is the one the ++# documented one-liners still fetch from this repository's root: ++# ++# curl -fsSL https://raw.githubusercontent.com/opensoft/openRepoTools/main/openRepoTools | bash -s -- --install ++# gh api repos/opensoft/openRepoTools/contents/openRepoTools \ ++# -H 'Accept: application/vnd.github.raw' | bash -s -- --install ++# ++# and the one `./openRepoTools` runs in a checkout of this repository. It ++# SELECTS the implementation `contracts/code-pin.yaml` pins and hands it every ++# argument. It places nothing, merges nothing, writes no receipt and retires ++# nothing — every one of those is the implementation's, which is also what ++# `--install` puts on a PATH, so an installed `openRepoTools` never needs this ++# file or a checkout of this repository again (opensoft/openRepoTools#186, ++# T007; `specs/004-migrate-to-triad/installer-design.md`). ++# ++# * IN A CHECKOUT (this file beside `contracts/code-pin.yaml`): the pin, the ++# gitlink at its `submodule_path` and the implementation's bytes must name ++# ONE commit, and then this EXECs `/openRepoTools` with ++# every argument — the same process, so its exit status and every signal ++# are the implementation's own. That implementation then checks the rest ++# of the payload against the same pin before it copies a byte. ++# * ANYWHERE ELSE (stdin, or a copy of this file on its own): `$OPENREPOTOOLS_REF` ++# is resolved ONCE to a commit of `$OPENREPOTOOLS_REPO`, the code pin is ++# read AT that commit, the implementation is fetched from the pinned ++# repository AT the pinned commit, and it is run from stdin — the way the ++# one-liners always ran it — with `$OPENREPOTOOLS_REF` set to that commit, ++# so the implementation reads the same pin and no branch that moves in ++# between can give it another. It verifies the gitlink itself. ++# ++# NOTHING HERE ASSUMES A FILE THE LEGS MAY NOT CARRY: no `.gitattributes`, no ++# README, no AGENTS.md. Bytes are compared raw (`hash-object --no-filters`). ++# ++# bash 3.2 (macOS's /bin/bash): no `${x,,}`, `mapfile`, `declare -A` or ++# `local -n`, as everywhere in this project. ++ ++set -euo pipefail ++ ++REPO="${OPENREPOTOOLS_REPO:-opensoft/openRepoTools}" ++REF="${OPENREPOTOOLS_REF:-main}" ++SELF="${BASH_SOURCE[0]:-$0}" ++PIN_FILE="contracts/code-pin.yaml" ++ ++die() { printf '\nREFUSED: %s\n' "$1" >&2; exit "${2:-2}"; } ++ ++# --------------------------------------------------------------------------- ++# THE CODE PIN'S GRAMMAR — A DELIBERATE COPY of the implementation's own ++# reader (`pin_parse` and the five helpers above it in the code leg's ++# `openRepoTools`), byte for byte, because this file has to read the pin ++# BEFORE there is an implementation to ask. The code leg's ++# `tests/test_install_payload_source.py` fails when the two copies differ. ++# --------------------------------------------------------------------------- ++ ++ORT_ALNUM='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' ++ORT_TAB="$(printf '\t')" ++ORT_CR="$(printf '\r')" ++ ++is_lower_hex() { # ++ case "$2" in '' | *[!0123456789abcdef]*) return 1 ;; esac ++ [ "${#2}" -eq "$1" ] ++} ++ ++is_slug() { # — a repository as GitHub spells one, and nothing else ++ local owner="${1%%/*}" name="${1#*/}" ++ [ "$owner" != "$1" ] || return 1 ++ case "$owner" in '' | -* | *- | *[!${ORT_ALNUM}-]*) return 1 ;; esac ++ case "$name" in '' | . | .. | */* | *[!${ORT_ALNUM}._-]*) return 1 ;; esac ++ return 0 ++} ++ ++is_segment() { # — exactly one path segment ++ case "$1" in '' | . | .. | *[!${ORT_ALNUM}._-]*) return 1 ;; esac ++ return 0 ++} ++ ++PIN_SCALAR="" ++pin_scalar() { # — PIN_SCALAR, or 1 for a quote that does not close ++ local v="$1" q rest ++ while :; do case "$v" in ' '* | "$ORT_TAB"*) v="${v#?}" ;; *) break ;; esac; done ++ case "$v" in ++ '"'* | "'"*) ++ q="${v:0:1}" ++ rest="${v#?}" ++ case "$rest" in *"$q"*) ;; *) return 1 ;; esac ++ PIN_SCALAR="${rest%%"$q"*}" ++ rest="${rest#*"$q"}" ++ while :; do case "$rest" in ' '* | "$ORT_TAB"*) rest="${rest#?}" ;; *) break ;; esac; done ++ case "$rest" in '' | '#'*) ;; *) return 1 ;; esac ++ # No escapes in this grammar: a backslash is a value nobody wrote. ++ case "$PIN_SCALAR" in *\\*) return 1 ;; esac ++ ;; ++ '#'*) PIN_SCALAR="" ;; ++ *) ++ PIN_SCALAR="${v%% #*}" ++ while :; do ++ case "$PIN_SCALAR" in *' ' | *"$ORT_TAB") PIN_SCALAR="${PIN_SCALAR%?}" ;; *) break ;; esac ++ done ++ ;; ++ esac ++ return 0 ++} ++ ++pin_bad() { # — PIN_WHY, and 1 ++ if [ -z "$2" ]; then ++ PIN_WHY="\`$1\` is missing" ++ else ++ PIN_WHY="\`$1\` is '$2', $3" ++ fi ++ return 1 ++} ++ ++pin_parse() { # — the PIN_* fields and 0, or PIN_WHY and 1 ++ local line body key n=0 seen=" " block="" tree_seen=0 ++ PIN_SCHEMA="" PIN_KIND="" PIN_LEG_ROLE="" PIN_SOURCE_REPOSITORY="" ++ PIN_SUBMODULE_PATH="" PIN_COMMIT="" PIN_REVISION_KIND="" ++ PIN_DIGEST_ALGORITHM="" PIN_DIGEST_DEFINITION="" PIN_TREE_SHA256="" ++ PIN_VERIFY="" PIN_RUNBOOK="" PIN_WHY="" ++ [ -s "$1" ] || { PIN_WHY="it is empty"; return 1; } ++ while IFS= read -r line || [ -n "$line" ]; do ++ n=$((n + 1)) ++ case "$line" in ++ *"$ORT_CR"*) PIN_WHY="line $n ends in a carriage return, and the grammar is LF-only"; return 1 ;; ++ '' | '#'*) continue ;; ++ ' '* | "$ORT_TAB"*) ++ body="$line" ++ while :; do case "$body" in ' '* | "$ORT_TAB"*) body="${body#?}" ;; *) break ;; esac; done ++ case "$body" in '' | '#'*) continue ;; esac ++ # AN INDENTED LINE BELONGS TO THE KEY ABOVE IT, and the only one ++ # this reads into is `digests:`. ++ [ "$block" = digests ] || continue ++ key="${body%%:*}" ++ [ "$key" != "$body" ] || { PIN_WHY="line $n is not \`key: value\`"; return 1; } ++ pin_scalar "${body#*:}" || { PIN_WHY="line $n has a quoted value that does not close cleanly"; return 1; } ++ if [ "$key" = tree_sha256 ]; then ++ [ "$tree_seen" -eq 0 ] || { PIN_WHY="\`digests.tree_sha256\` appears twice"; return 1; } ++ tree_seen=1 ++ PIN_TREE_SHA256="$PIN_SCALAR" ++ fi ++ continue ;; ++ esac ++ block="" ++ key="${line%%:*}" ++ [ "$key" != "$line" ] || { PIN_WHY="line $n is not \`key: value\`"; return 1; } ++ case "$key" in ++ '' | *[!abcdefghijklmnopqrstuvwxyz0123456789_]*) ++ PIN_WHY="line $n's key '$key' is not a key this grammar has"; return 1 ;; ++ esac ++ case "$seen" in *" $key "*) PIN_WHY="\`$key\` appears twice"; return 1 ;; esac ++ seen="$seen$key " ++ pin_scalar "${line#*:}" || { PIN_WHY="line $n has a quoted value that does not close cleanly"; return 1; } ++ case "$key" in ++ schema_version) PIN_SCHEMA="$PIN_SCALAR" ;; ++ kind) PIN_KIND="$PIN_SCALAR" ;; ++ leg_role) PIN_LEG_ROLE="$PIN_SCALAR" ;; ++ source_repository) PIN_SOURCE_REPOSITORY="$PIN_SCALAR" ;; ++ submodule_path) PIN_SUBMODULE_PATH="$PIN_SCALAR" ;; ++ commit) PIN_COMMIT="$PIN_SCALAR" ;; ++ revision_kind) PIN_REVISION_KIND="$PIN_SCALAR" ;; ++ digest_algorithm) PIN_DIGEST_ALGORITHM="$PIN_SCALAR" ;; ++ digest_definition) PIN_DIGEST_DEFINITION="$PIN_SCALAR" ;; ++ verify_pin) PIN_VERIFY="$PIN_SCALAR" ;; ++ resync_runbook) PIN_RUNBOOK="$PIN_SCALAR" ;; ++ digests) ++ [ -z "$PIN_SCALAR" ] || { PIN_WHY="\`digests\` is '$PIN_SCALAR', not a mapping"; return 1; } ++ block=digests ;; ++ esac ++ done <"$1" ++ [ "$PIN_SCHEMA" = 1 ] || { pin_bad schema_version "$PIN_SCHEMA" "not 1"; return 1; } ++ [ "$PIN_KIND" = pinned_contract_manifest ] || ++ { pin_bad kind "$PIN_KIND" "not pinned_contract_manifest"; return 1; } ++ [ "$PIN_LEG_ROLE" = code ] || { pin_bad leg_role "$PIN_LEG_ROLE" "not code"; return 1; } ++ is_slug "$PIN_SOURCE_REPOSITORY" || ++ { pin_bad source_repository "$PIN_SOURCE_REPOSITORY" "not an owner/name repository"; return 1; } ++ is_segment "$PIN_SUBMODULE_PATH" || ++ { pin_bad submodule_path "$PIN_SUBMODULE_PATH" "not one path segment"; return 1; } ++ is_lower_hex 40 "$PIN_COMMIT" || ++ { pin_bad commit "$PIN_COMMIT" "not 40 lowercase hex — a branch, a tag or an abbreviated commit is a moving reference"; return 1; } ++ [ "$PIN_REVISION_KIND" = commit ] || { pin_bad revision_kind "$PIN_REVISION_KIND" "not commit"; return 1; } ++ [ "$PIN_DIGEST_ALGORITHM" = sha256 ] || ++ { pin_bad digest_algorithm "$PIN_DIGEST_ALGORITHM" "not sha256"; return 1; } ++ [ "$PIN_DIGEST_DEFINITION" = sorted-ls-tree-r-v1 ] || ++ { pin_bad digest_definition "$PIN_DIGEST_DEFINITION" "not sorted-ls-tree-r-v1"; return 1; } ++ is_lower_hex 64 "$PIN_TREE_SHA256" || ++ { pin_bad digests.tree_sha256 "$PIN_TREE_SHA256" "not 64 lowercase hex"; return 1; } ++ [ -n "$PIN_VERIFY" ] || { pin_bad verify_pin "" ""; return 1; } ++ [ -n "$PIN_RUNBOOK" ] || { pin_bad resync_runbook "" ""; return 1; } ++ return 0 ++} ++ ++# --------------------------------------------------------------------------- ++# IN A CHECKOUT: three facts, one commit, then the implementation itself. ++# --------------------------------------------------------------------------- ++ ++#: THE COMMIT A SUPERPROJECT RECORDS AT : the index first and HEAD ++#: second, the order openRepoShape's `recorded_gitlink` asks them in. ++recorded_gitlink() { # ++ # `read`, not `awk`: the offline path needs nothing a minimal PATH lacks. ++ local listed mode oid stage kind path ++ listed="$(git -C "$1" ls-files -s -- "$2" 2>/dev/null)" || listed="" ++ read -r mode oid stage path <<<"$listed" || : ++ if [ "$mode" != 160000 ] || [ "$stage" != 0 ] || [ "$path" != "$2" ]; then ++ listed="$(git -C "$1" ls-tree HEAD -- "$2" 2>/dev/null)" || listed="" ++ read -r mode kind oid path <<<"$listed" || : ++ [ "$mode" = 160000 ] && [ "$kind" = commit ] && [ "$path" = "$2" ] || return 1 ++ fi ++ is_lower_hex 40 "$oid" || return 1 ++ printf '%s\n' "$oid" ++} ++ ++delegate_local() { # ++ local root="$1" link code blob ++ shift ++ pin_parse "$root/$PIN_FILE" || die "$root/$PIN_FILE is not a code pin this can follow: $PIN_WHY. ++ A pin that is present and wrong is refused rather than read around; nothing ran." ++ link="$(recorded_gitlink "$root" "$PIN_SUBMODULE_PATH")" || ++ die "$root records no gitlink at $PIN_SUBMODULE_PATH, where $PIN_FILE mounts the code ++ leg, so which implementation it pins cannot be checked; nothing ran." ++ [ "$link" = "$PIN_COMMIT" ] || die "$root records $link at $PIN_SUBMODULE_PATH and $PIN_FILE says ++ $PIN_COMMIT. The gitlink and the pin move in ONE commit (the lockstep rule), so ++ this checkout does not say which implementation it means; nothing ran. Make ++ them agree — \`make validate\` names the drift — then re-run." ++ code="$root/$PIN_SUBMODULE_PATH" ++ [ -f "$code/openRepoTools" ] || die "the code leg is not checked out at $code, so there is no ++ implementation to run; nothing ran. Mount it, then re-run: ++ git -C \"$root\" submodule update --init --recursive (or: make bootstrap)" ++ git -C "$code" cat-file -e "$PIN_COMMIT^{commit}" 2>/dev/null || ++ die "the pinned commit $PIN_COMMIT is not in $code's object store, so the ++ implementation there cannot be checked against it; nothing ran. Run ++ \`make bootstrap\` at $root, then re-run." ++ blob="$(git -C "$code" rev-parse --verify --quiet "$PIN_COMMIT:openRepoTools" 2>/dev/null)" || ++ die "the pinned commit $PIN_COMMIT has no openRepoTools at its root; nothing ran." ++ [ "$(git -C "$code" hash-object --no-filters -- openRepoTools 2>/dev/null)" = "$blob" ] || ++ die "$code/openRepoTools is not the implementation $root pins ($PIN_SOURCE_REPOSITORY ++ at $PIN_COMMIT), so this front door does not run it; nothing ran. Put the code ++ leg back at its pin (git -C \"$code\" checkout $PIN_COMMIT, or make bootstrap), ++ or run a code checkout's own openRepoTools ON PURPOSE, which says what it installs." ++ exec "${BASH:-bash}" "$code/openRepoTools" ${1+"$@"} ++} ++ ++# --------------------------------------------------------------------------- ++# ANYWHERE ELSE: resolve once, read the pin there, run the pinned implementation. ++# --------------------------------------------------------------------------- ++ ++TMP="" ++ ++said() { # — its first line, as one clause ++ local first="" ++ IFS= read -r first <"$2" 2>/dev/null || : ++ case "$first" in ++ '') printf '%s failed and said nothing\n' "$1" ;; ++ "$1:"*) printf '%s\n' "$first" ;; ++ *) printf '%s: %s\n' "$1" "$first" ;; ++ esac ++} ++ ++#: `gh api` first and the raw URL second, the implementation's own order, into ++#: a file that is checked before it is used. WHY holds what both said. ++fetch_at() { # ++ WHY="" ++ if command -v gh >/dev/null 2>&1; then ++ if gh api "repos/$1/contents/$3?ref=$2" -H 'Accept: application/vnd.github.raw' \ ++ >"$4" 2>"$TMP/err" && [ -s "$4" ]; then ++ return 0 ++ fi ++ WHY="$(said gh "$TMP/err")" ++ else ++ WHY="gh: not installed" ++ fi ++ if command -v curl >/dev/null 2>&1; then ++ if curl -fsSL "https://raw.githubusercontent.com/$1/$2/$3" >"$4" 2>"$TMP/err" && ++ [ -s "$4" ]; then ++ return 0 ++ fi ++ WHY="$WHY; $(said curl "$TMP/err")" ++ else ++ WHY="$WHY; curl: not installed" ++ fi ++ return 1 ++} ++ ++delegate_remote() { # ++ local sha="" ref="" impl ++ is_slug "$REPO" || die "\$OPENREPOTOOLS_REPO is '$REPO', which is not an owner/name repository; nothing ran." ++ TMP="$(mktemp -d)" ++ trap 'rm -rf -- "$TMP"' EXIT ++ # `%` and `#` mean something else in a URL path; nothing else a ref may hold does. ++ ref="$(printf '%s' "$REF" | sed -e 's/%/%25/g' -e 's/#/%23/g')" ++ WHY="" ++ if command -v gh >/dev/null 2>&1; then ++ if gh api "repos/$REPO/commits/$ref" -H 'Accept: application/vnd.github.sha' \ ++ >"$TMP/sha" 2>"$TMP/err"; then ++ IFS= read -r sha <"$TMP/sha" || : ++ is_lower_hex 40 "$sha" || { sha=""; WHY="gh: an answer that is not a commit"; } ++ else ++ WHY="$(said gh "$TMP/err")" ++ fi ++ else ++ WHY="gh: not installed" ++ fi ++ if [ -z "$sha" ]; then ++ if command -v curl >/dev/null 2>&1 && ++ curl -fsSL -H 'Accept: application/vnd.github.sha' \ ++ "https://api.github.com/repos/$REPO/commits/$ref" >"$TMP/sha" 2>"$TMP/err"; then ++ IFS= read -r sha <"$TMP/sha" || : ++ is_lower_hex 40 "$sha" || { sha=""; WHY="$WHY; curl: an answer that is not a commit"; } ++ elif command -v curl >/dev/null 2>&1; then ++ WHY="$WHY; $(said curl "$TMP/err")" ++ else ++ WHY="$WHY; curl: not installed" ++ fi ++ fi ++ [ -n "$sha" ] || die "could not resolve $REPO at $REF to a commit, so no implementation was ++ selected and nothing ran. What answered: ++ $WHY" ++ fetch_at "$REPO" "$sha" "$PIN_FILE" "$TMP/pin" || die "could not read $PIN_FILE from $REPO at ++ $sha ($REF), so which implementation it pins is not known and nothing ran. ++ What answered: ++ $WHY ++ This is the assembly's front door. A single repository is installed by its own ++ root openRepoTools: gh api repos/$REPO/contents/openRepoTools?ref=$REF ..." ++ pin_parse "$TMP/pin" || die "$PIN_FILE in $REPO at $sha ($REF) is not a code pin this can ++ follow: $PIN_WHY. A pin that is present and wrong is refused rather than read ++ around; nothing ran." ++ fetch_at "$PIN_SOURCE_REPOSITORY" "$PIN_COMMIT" openRepoTools "$TMP/impl" || ++ die "could not fetch openRepoTools from $PIN_SOURCE_REPOSITORY at $PIN_COMMIT, the ++ implementation $REPO at $sha ($REF) pins, so nothing ran. What answered: ++ $WHY" ++ case "$(head -n 1 "$TMP/impl")" in ++ '#!'*) ;; ++ *) die "what came back for openRepoTools from $PIN_SOURCE_REPOSITORY at $PIN_COMMIT is not a ++ script, so nothing ran." ;; ++ esac ++ impl="$(cat "$TMP/impl")" ++ rm -rf -- "$TMP" ++ trap - EXIT ++ # THE REF THE IMPLEMENTATION RESOLVES IS THE COMMIT THIS ONE READ, so the ++ # pin it reads is this pin; it runs from stdin, as the one-liners run it. ++ OPENREPOTOOLS_REPO="$REPO" ++ OPENREPOTOOLS_REF="$sha" ++ export OPENREPOTOOLS_REPO OPENREPOTOOLS_REF ++ exec bash -s -- ${1+"$@"} <<<"$impl" ++} ++ ++if [ -f "$SELF" ] && [ -f "$(cd -- "$(dirname -- "$SELF")" && pwd)/$PIN_FILE" ]; then ++ delegate_local "$(cd -- "$(dirname -- "$SELF")" && pwd)" ${1+"$@"} ++fi ++delegate_remote ${1+"$@"} +-- +2.43.0 + From b21c5113fa7c74932277e8fedee2679eb36dbb9d Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 01:58:47 +0000 Subject: [PATCH 16/42] Record the composed triad candidate that carries every Gate B follow-up: its inputs, apply order, commits and trees, its validators, and its install compared with today's main. The suite runs on the candidate are queued behind the workstation lock and are recorded in a later commit. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../candidate-2026-10-09.md | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 specs/004-migrate-to-triad/candidate-2026-10-09.md diff --git a/specs/004-migrate-to-triad/candidate-2026-10-09.md b/specs/004-migrate-to-triad/candidate-2026-10-09.md new file mode 100644 index 00000000..d9a2a981 --- /dev/null +++ b/specs/004-migrate-to-triad/candidate-2026-10-09.md @@ -0,0 +1,179 @@ +# The composed candidate carrying every follow-up — October 9, 2026 + +**For:** Gate B of [tasks.md](tasks.md), under +[#186](https://github.com/opensoft/openRepoTools/issues/186). It answers item 3 of +section 7 of `gate-ab-evidence-2026-10-09.md` (branch `004-migrate-to-triad-rollback` +at `fa44d75`): "No composed candidate carrying every follow-up has run the suite." +No task is checked complete by this record, and it approves nothing. +**Writer:** lane openRepoTools-1, 2026-10-09 from 01:47Z. +**Evidence branch:** `004-migrate-to-triad-candidate`, from `main` `63810dd`. +**Private receipt ID:** `candidate-20261009-JR8YyA` (brett-wip `migration/openRepoTools/`). +It holds every script, log, patch and install snapshot named below. Machine paths stay +there. + +**State: in progress.** The candidate is built and its install is compared. The three +suite runs are queued behind the workstation lock; section 5 is filled in when they end. + +## 1. Inputs + +Every input was read without checking it out in any existing worktree. Patches were +taken with `git show :`, and the PR commits with `git format-patch` from +the shared checkout. + +| Input | Where | Identity | +| --- | --- | --- | +| Run C's rehearsed arrangement | private prep `work-20261008/rehearsal` (read-only) | source `837928a`, split `69b8924`, spec `593250d`, code `73b04d6`, nested openRepoShape `7f84ca4` | +| T006 patches (5) | `004-migrate-to-triad-root-guidance` `041a273` | each blob equals `465fa6d`'s; sha256 `cd69c2a9…`, `574c0df1…`, `a7f8521b…`, `74b98ff9…`, `c11070f0…`, as `gate-ab-evidence` records | +| T010 patches (2) | `004-migrate-to-triad-ci` `cc7c7a1` | code `tests.yml` `7ee3c6d0…`; assembly `composed.yml` `6ee09f00…` | +| T007 entry point | `004-migrate-to-triad-installer` `51993a4` | `t007-assembly-entry-point.patch` `361c037d…`, as `installer-2026-10-08.md` records | +| PR #190 | `feat/test-roots-for-the-triad` `dbc6837` (the head at 01:45Z) | 3 commits on `c4864ac`: `c139e88`, `e3a76b5`, `dbc6837` | +| PR #191 | `feat/installer-payload-source-resolution` `2411f40` (the head at 01:45Z) | 4 commits on `c4864ac`: `6337a52`, `e406e0e`, `ecd7aa4`, `2411f40` | +| The shape's `bump-leg.py` | a clone of the rehearsal's openRepoShape mirror, in scratch | `7f84ca4` | + +`assembly-0002` and `assembly-0003` were **not applied**. As `root-guidance-2026-10-08.md` +says, they pin the rehearsal's own leg hashes; the two pins were regenerated here with +`bump-leg.py` against this candidate's legs. + +## 2. How it was built + +All of it ran in private scratch (`work-20261008/ort-triad-candidate/`): + +- **Own copies first.** Bare copies, made with `--no-hardlinks`, of run C's two leg + remotes, of its assembly branch and of its openRepoShape mirror. Every write went to + these copies. In every clone the submodule URLs were set to them before any commit, and + a `url..insteadOf` rule sent `.gitmodules`' run C paths and the nested GitHub URL + to the copies. +- **No network.** `PATH` began with refusing `gh`, `curl` and `wget` shims that log every + call; `GIT_ALLOW_PROTOCOL=file`. The shim log is empty. +- **Run C unchanged.** A listing of the rehearsal directory (type, mode, size, mtime of + every entry) was taken before the work and compared after it (section 8). + +### Apply order and results + +Every patch was applied with `git am`. **Every one applied cleanly; no hunk needed a +resolution, and none was dropped.** + +| # | Leg | Patch | `git am` | Commit | Tree | +| --- | --- | --- | --- | --- | --- | +| 1–3 | code, on `73b04d6` | PR #190's three commits | exit 0 | `a70ecad`, `66a191c`, `8377c56` | `b528173e` after the third | +| 4–7 | code | PR #191's four commits | exit 0 | `36d5ccc`, `80b2119`, `edb902f`, `de49a91` | `66c636e9` after the fourth | +| 8 | code | T006 `code-0001` | exit 0 | `d1ec0b5` | `15014778` | +| 9 | code | T010 `t010-code-leg-tests-workflow.patch` | exit 0 | **`f0af7d9`** | **`a832c8a0`** | +| 10 | spec, on `593250d` | T006 `spec-0001` | exit 0 | **`9e1d006`** | **`10256135`**, the tree T006 and T011 recorded | +| 11 | assembly, on `69b8924` | T006 `assembly-0001` | exit 0 | `4f1e522` | `5060f3be` | +| 12 | assembly | T010 `t010-assembly-exact-pin-job.patch` (`composed.yml`) | exit 0 | `738857c` | `7905d570` | +| 13 | assembly | T007 `t007-assembly-entry-point.patch` | exit 0 | `f4133d6` | `8b0c860b` | +| 14 | assembly | `bump-leg.py --leg spec --to 9e1d006…` | exit 0 | `f0a4feb` | `94e9d9be` | +| 15 | assembly | `bump-leg.py --leg code --to f0af7d9…` | exit 0 | **`3bd95a7`** | **`6191142c`** | + +What was checked at each step: + +- **The PR commits are the PR heads.** Every path PR #190 touches has `dbc6837`'s blob + after step 3: 8 of 8. After step 7, `openRepoTools` (`04769c4`) and + `tests/test_install_payload_source.py` have `2411f40`'s blobs. +- **The one shared file.** Both PRs touch `tests/test_openrepotools_command.py`. On top + of #190, #191 applied the same two hunks as its own diff, three lines lower. The hunk + bodies are identical (26 lines). +- **T006's `.gitattributes` and #190's decision agree.** #190 reads `.gitattributes` from + the code root; `code-0001` puts the monorepo's blob `3560973` there. `git check-attr` + on `openRepoTools`, `park` and `tests/run.sh` in the leg gives `text: auto`, + `eol: lf`. +- **The entry point is T007's.** `openRepoTools` at the assembly root is blob `4c2525d`, + mode 100755, sha256 `3b0d59f5…`, as `installer-2026-10-08.md` records. +- **Clean text.** `git diff --check` exits 0 over the code range (`73b04d6..f0af7d9`), the + spec range and the assembly range (`69b8924..f4133d6`). + +The legs' candidate commits were pushed to `main` of their own bare copies, as +fast-forwards from run C's heads. The assembly was pushed as branch `candidate` to its +bare copy. `bump-leg.py` ran with `--local-remote-dir` set to the copies, with a dry run +first for each leg: exit 0 ×4. It added `Lane:` and `Co-Authored-By:` trailers. Its +`NEXT … push` line was not followed. + +### The candidate's identities + +| Repository | Commit | Tree | Pinned digest (`sorted-ls-tree-r-v1`) | +| --- | --- | --- | --- | +| assembly (`candidate`) | `3bd95a7a57d0c1eb4d2e339ab34007756938b6a5` | `6191142cb360a71132e2783a9cadf0ce36374ab2` | — | +| spec leg | `9e1d006e2c870615f159ad1ebab719bb7b38cc2a` | `10256135f87ae4d05166d2c5934a152c42ce19ae` | `3a4a84beb20b343390f5bae64cb166df4d0cfd693baff6309fc13c8060d1c866` (T006's spec digest) | +| code leg | `f0af7d9be42d0e5fbfa9a220b6ab6805d81e804d` | `a832c8a058d6dab7bf61f3b7ce469d7b911851ea` | `6550ae2e44d1f78368bdb82f2416dff4828943efb7cfd8a896d1693911e14d95` | +| code's nested openRepoShape | `7f84ca42ca86a8902928345109d2bf6bad87bd91` | — | unchanged from run C | + +The commit hashes are this run's alone, because `git am` stamps a new committer and date. +The trees are what reproduce. + +## 3. Bootstrap, validators and the composed job's own steps + +| Check (in the assembly) | Result | +| --- | --- | +| `python3 scripts/bootstrap.py` | exit 0; "spec: on main at 9e1d006e2c87 (branch tip == pin)", "code: on main at f0af7d9be42d (branch tip == pin)", `pins ok`, `manifest ok`, "bootstrap ok" | +| `make validate` (naming, manifest, pins) and `make pins` | exit 0 and 0. Both gitlinks equal their pins, both tree digests recompute, and all 11 copied shape files match `contracts/shape-pin.yaml` | +| `composed.yml`'s step "every context the composed run claims is here", run as written | exit 0 | +| `composed.yml`'s `validate-manifest.py`, then `validate-pins.py` | exit 0, exit 0 | +| A fresh recursive clone of the candidate from its bare copy, then `bootstrap.py` and `make validate` | exit 0 and 0. Spec `9e1d006`, code `f0af7d9`, nested `7f84ca4`. `status --ignored` is empty | + +`.gitmodules` still names run C's local remote paths, as the adopter wrote them in the +rehearsal. A recursive clone that does not redirect them fetches from run C, whose +remotes lack this candidate's leg commits. A real leg is an https remote and does not +have this problem. + +## 4. The install, compared with today's main + +Both installs used the same disposable home path, rebuilt from one seed, so that +absolute paths written into `settings.json` and the receipt compare byte for byte. +- **Environment:** `env -i`, with `HOME`, every `XDG_*` variable and `TMPDIR` in scratch. + `PATH` was the refusing shims, then `/usr/bin:/bin`. `GIT_ALLOW_PROTOCOL=file`. +- **The seed:** an unrelated `settings.json` (model, env, permissions, a status line, and + `SessionStart`, `UserPromptSubmit` and `Stop` hooks of its own), a foreign command in + `~/.local/bin`, and a foreign skill under each of the two skill roots. +- **The shims logged 0 calls.** + +| Stage | Source | Result | +| --- | --- | --- | +| A | today's main `63810dd` (a shallow clone of `origin/main`), `./openRepoTools --install` | exit 0; `17 of 17 placed`; 29 `installed at`; both hooks merged; 29 receipt rows | +| B | a freshly seeded home; the candidate assembly root `./openRepoTools --install` (the T007 entry point) | exit 0. First line: "source: the code mounted at `/code`, opensoft/openRepoTools-code at f0af7d9be42d… as `` pins it; every payload file was checked against that commit and nothing fetched". Then `17 of 17 placed`, 29 `installed at`, both hooks merged, 29 receipt rows | +| B2 | the candidate again, over B | exit 0; 31 `unchanged` (29 files and 2 hooks) | +| C | today's main again, over B: rollback by reinstall | exit 0; `openRepoTools: updated at …`, 30 `unchanged` | +| — | the candidate's `./openRepoTools --version`; `./openRepoTools nope` | "openRepoTools (opensoft/openRepoTools @ main)", exit 0; exit 2 | + +**Every difference between A and B**, by path, type, mode and sha256 over the whole home: + +| Path | A (main) | B (candidate) | +| --- | --- | --- | +| `~/.local/bin/openRepoTools` | 755 `47cfe74ea96e6ce9e56b47220a71e1de8c608af2f51f33cfdffa351410a4f373` | 755 `118cc2ccd376456e51f47d36f5fc9604a79fa0be285be9982d2304c0304c0f8f` | +| `~/.local/share/openRepoTools/installed.tsv` | 600, its `openRepoTools` row names `47cfe74e…` | 600, its row names `118cc2cc…`; the UTC column differs too | + +Nothing else differs: +- **The other 28 placed files** have the same path, mode and sha256. +- **`settings.json`** is byte-identical: the seed's model, env, permissions, status line + and three hooks of its own, plus the same two entries the installer merges, at mode 600. +- **The receipt's name, destination and sha256 columns** differ in the `openRepoTools` + row only. +- **The foreign command and both foreign skills** are untouched. + +The installed `openRepoTools` (`118cc2cc…`) is the bytes of `code/openRepoTools` +(PR #191's implementation, blob `04769c4`). It is not the root entry point (`3b0d59f5…`), +so the entry point handed over as T007 designed. + +**Rollback by reinstall (C) restores main.** C's whole-home listing equals A's except for +the receipt file's own digest. The receipt's name, destination and sha256 columns equal +A's; only the UTC stamps differ. This is the (c2) path of `gate-ab-evidence-2026-10-09.md` +section 2, repeated at this candidate's own commits, as its section 2 asked. + +## 5. The suite: composed, standalone, and standalone without the dependency + +Queued at 01:53:52Z. The results go here when the runs end. + +## 6. What this proves for Gate B, and what it does not + +To be written with section 5. + +## 7. Follow-ups + +To be written with section 5. + +## 8. Acts outside scratch, and log SHA256s + +To be written at the end of the run. + +This record authorizes no conversion, repository creation, merge, default-branch move or +lane rebinding. From b4ef6b51ae3f630800f5ebd207ee40a28642ae94 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 02:25:00 +0000 Subject: [PATCH 17/42] Bring the T009/T010 test-roots record up to PR #190's head dbc6837, with both reviews, the .gitattributes decision and the dated follow-ups. The record stopped at c139e88. Since then e3a76b5 refuses a leg whose git status cannot be read and keeps .gitattributes in the fixture legs (Copilot round 1), and dbc6837 adds T006's assembly-root caps and fails a missing lane manual by name. Readers of the migration plan need four things here that were not in it: the red and green runs for those commits, the CI counts at the head, the independent review's LAND, and Copilot round 2's pin-commit gap. That gap is reproduced here, and validate-pins.py is shown to close it in the assembly job. The .gitattributes decision is now stated outright. It agrees with T006's leg patch and disagrees with plan.md's placement table, which is a follow-up for the plan's owner. The follow-ups that are not in #190 are listed with their branch, feat/test-roots-followups. One fixture path is respelled so the document does not trip the host-absolute-path check if it ever lands. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../test-roots-and-ci-2026-10-08.md | 194 +++++++++++++++--- 1 file changed, 169 insertions(+), 25 deletions(-) diff --git a/specs/004-migrate-to-triad/test-roots-and-ci-2026-10-08.md b/specs/004-migrate-to-triad/test-roots-and-ci-2026-10-08.md index df58888a..8faca169 100644 --- a/specs/004-migrate-to-triad/test-roots-and-ci-2026-10-08.md +++ b/specs/004-migrate-to-triad/test-roots-and-ci-2026-10-08.md @@ -1,10 +1,15 @@ # Explicit test roots and the triad's CI — October 8, 2026 +**Updated:** October 9, 2026, for `e3a76b5`, `dbc6837`, both reviews and the +follow-ups. **Tasks:** T009 and T010 of [tasks.md](tasks.md), under [#186](https://github.com/opensoft/openRepoTools/issues/186). No task is checked complete by this record; Gate B stays open. **Code change:** [PR #190](https://github.com/opensoft/openRepoTools/pull/190) -(`feat/test-roots-for-the-triad`, on `main` `c4864ac`). +(`feat/test-roots-for-the-triad`, head `dbc6837`: `c139e88`, `e3a76b5` and +`dbc6837` on `main` `c4864ac`; `ready` applied 2026-10-09T02:11Z). +**Follow-ups (not in #190):** branch `feat/test-roots-followups`, `6e63083` +and `2064390` on `dbc6837` (section 9). **CI patches:** [patches/t010-code-leg-tests-workflow.patch](patches/t010-code-leg-tests-workflow.patch) and [patches/t010-assembly-exact-pin-job.patch](patches/t010-assembly-exact-pin-job.patch). **Rehearsed arrangement:** run C of 2026-10-08 (private prep @@ -79,11 +84,34 @@ it answers for all three roles, and every reader reads exactly the file it read before. The layout is decided by those trees and never by the documents, so a README deleted from today's tree is still a failure, not a skip. -`.gitattributes` stays the **code root's** in every layout, against the -placement table, because git never applies a superproject's attributes inside -a submodule. Measured in the composed candidate: `README.md` at the assembly -has `text: auto`, `eol: lf`; `park` and `tests/run.sh` in `code/` have -`text: unspecified`, `eol: unspecified` (`proof-check-attr.log`). +**Decision: `.gitattributes` stays the code root's** in every layout, +against the placement table, because git never applies a superproject's +attributes inside a submodule. Measured in the composed candidate +(`proof-check-attr-both.log`): +- `README.md` and `AGENTS.md` at the assembly have `text: auto`, `eol: lf`. +- `park` and `tests/run.sh` in `code/` have `text: unspecified`, + `eol: unspecified`. + +Reading the assembly's copy would pass while the leg's bash files went +unprotected. + +This agrees with T006: its leg patch `code-0001` gives the code leg its own +`.gitattributes`, and the assembly keeps its copy for its own files. + +It disagrees with the placement table in `plan.md` § Target placement on +`main`, which puts `.gitattributes` at the assembly only. Amending that table +is a follow-up for the plan's owner (section 9). A leg split exactly as the +table says is red on `test_the_root_carries_the_line_ending_rule`: see P1, P3 +and P4 below, and the independent review's C5 and C6. + +**Length caps** (`dbc6837`, T006): +- On today's layout, `AGENTS.md` stays at 316 and `README.md` at 486. +- When an assembly root is named, the caps are 340 and 510. The difference is + the 24 lines each that T006's `assembly-0001` adds. + +The dated entries cite `root-guidance-2026-10-08.md`, which is on #192's +branch: #192 should land with or before #190, or that citation points +nowhere until it does. ## 3. The composed-versus-standalone contract @@ -95,29 +123,78 @@ has `text: auto`, `eol: lf`; `park` and `tests/run.sh` in `code/` have session (`pytest.UsageError`, exit 4, `ERROR: …`) when the assembly or spec root is absent, the dependency lacks `scripts/repo_shape.py` or `templates/workspace-root/README.md`, or the code or spec leg is not at the - commit the assembly records or has changes to tracked files — naming each. + commit the assembly records, has changes to tracked files, or answers no + `git status` (`e3a76b5`, Copilot round 1). The refusal names each problem. `NEEDS_UPSTREAM` becomes a fixture that fails, and a document test that reaches an absent root fails. A variable naming the wrong kind of tree, or a mode other than `0`/`1`, is refused in either mode. +The conftest does not check the pin files' own `commit:` and `tree_sha256`. +That is the job of the standard's `scripts/validate-pins.py`, which the +assembly job runs in the step before the suite. Copilot's round-2 finding on +#190 is exactly this gap. + +I reproduced it in a copy of the composed candidate (`proof-stale-pin.log`). +I moved `contracts/code-pin.yaml`'s `commit:` to `73b04d6` and left the +gitlink and HEAD at `8ebf4ec`. On that tree: +- `conftest.ROOTS.refusal()` is `None`. +- `validate-pins.py` exits 1 with `pin-gitlink-mismatch` and + `pin-digest-mismatch`. + +In CI, the job stops before the suite runs. A composed run made by hand +without the validator would accept that leg. The follow-up is in section 9. + ## 4. Red at the base, green on the branch -* **Red.** `tests/test_test_roots.py` (SHA256 `be4edff7…6cff`), copied - unchanged into a clone of `origin/main` `c4864ac`, `tests/run.sh -k roots`: - **18 failed**, every test of the module (3 passes are other modules' tests - whose names contain `roots`). The seven behavioural tests failed on what they - assert — the nested suite read this checkout's README, manual, gitlink and - dependency whatever the variables named, a composed triad with its spec leg - moved off its pin ran instead of exiting 4, and `tests/run.sh` passed a - relative root through relative — and the eleven resolver tests failed - because `conftest.resolve_roots` and `ROOTS` do not exist there. Log - `eb7d2bc4…525c`. -* **Green, today's layout.** `tests/run.sh -k 'repo_hygiene or upstream_pin or roots or composed'` - on `c139e88`: **188 passed, 0 skipped**. Log `b0289afa…cf4`. -* **CI on #190** (head `c139e88`): `tests` 1315 passed in 34m52s, - `tests-no-submodule`, `tests-windows` (173 passed, 1142 skipped), - `parse-macos`, `guard-launch-mode` and SonarCloud green; `tests-macos` - waits for `ready`. +* **Red, round 1.** I copied `tests/test_test_roots.py` as of `c139e88` + (SHA256 `be4edff7…6cff`) unchanged into a clone of `origin/main` `c4864ac` + and ran `tests/run.sh -k roots`. Result: **18 failed**, every test of the + module. The 3 passes are tests in other modules whose names contain + `roots`. + - The seven behavioural tests failed on what they assert. The nested suite + read this checkout's README, manual, gitlink and dependency, whatever the + variables named. A composed triad with its spec leg moved off its pin ran + instead of exiting 4. `tests/run.sh` passed a relative root through + unchanged. + - The eleven resolver tests failed because `conftest.resolve_roots` and + `ROOTS` do not exist at the base. + + Log `eb7d2bc4…525c`. +* **Red, round 2.** I copied `dbc6837`'s module (SHA256 `aeb4b742…ea0c`) the + same way and ran `-k 'length_caps or missing_manual or status_cannot_be_read'`. + Result: **3 failed**, the three tests added since round 1: + - a leg whose `git status` cannot be read; + - the assembly-root caps ("passed on an assembly file one line over its + cap"); + - the lane suite's missing-manual failure. + + Log `bb87a06e…ad16`. The independent review copied the same module whole + into `c4864ac` and got **21 failed**: all 21 of the module's test cases. +* **Green, today's layout**, with `tests/run.sh -k 'repo_hygiene or upstream_pin or roots or composed'`: + + | Tree | Result | Log | + | --- | --- | --- | + | `c139e88` | **188 passed** | `b0289afa…cf4` | + | the working tree that became `e3a76b5` | **189 passed** | `d6f84d7f…65e3` (its header names the HEAD at its start, `c139e88`) | + | `dbc6837`, and its merge onto `main` `63810dd` | **191 passed, 0 skipped** | the independent review's run | + + My local run on `dbc6837` itself was queued for the lock at the + account-swap checkpoint and was stopped there. CI is the record. +* **CI on #190, head `dbc6837`** (run 37869489919, merge `5ad6205` onto + `63810dd`): + + | Job | `dbc6837` | `main` `63810dd` | + | --- | --- | --- | + | `tests` | 1318 passed | 1296 passed | + | `tests-no-submodule` | 1105 passed, 213 skipped | 1084 passed, 212 skipped | + | `tests-windows` | 176 passed, 1142 skipped | — | + | `guard-launch-mode` | 165 passed | — | + | `parse-macos`, SonarCloud | green | — | + + The added `tests-no-submodule` skip is the composed positive control, and + it skips with today's reason. `tests-macos` runs under `ready` (run + 37873415160). Earlier heads: `c139e88` had `tests` at 1315 passed, and + `e3a76b5` was green. ## 5. The two CI patches (T010) @@ -162,6 +239,10 @@ private copy of the code leg's bare remote, and pinned with the standard's own `6ccdaf8`, `contracts/code-pin.yaml` digest recomputed as `577cdcf1…d850`); the composed workflow is commit `386be81` on top. +These proofs ran on `c139e88`'s content (`8ebf4ec`). They were not repeated +for `e3a76b5` or `dbc6837`. The independent review's fake triad exercised +`dbc6837` both composed and standalone (its C1–C6). + | Check (in the candidate, as the job runs it) | Exit | Log SHA256 | | --- | ---: | --- | | `python3 scripts/validate-manifest.py` | 0 | `0caaed6c…83a4` | @@ -185,9 +266,10 @@ The failures, recorded rather than deleted: clause (c)'s two facts for a person, in its TAIL". Both expect the lane's directory in a register row that is cut at 240 characters, and under `tests/run.sh` that directory sits beneath the run root - (`~/.local/state/openRepoTools/tmp/-/tmp/tmp.*/home/projects/…`), + (`~/.local/state/openRepoTools/tmp/-/tmp/tmp.*/`, then the + fixture home's `projects/…`), long enough to fall past the cut. CI runs the same suite with a short - `TMPDIR` and passed it (1315 passed on #190). These sections are untouched + `TMPDIR` and passed it (1315 passed at `c139e88`, 1318 at `dbc6837`). These sections are untouched by PR #190; the length interaction belongs with the run-root work of #162/#184 (inferred from the expected and actual rows, not bisected). @@ -227,6 +309,62 @@ pytest, so every local run had `/usr/bin` first on `PATH`. CI is the record. composed job if the legs are ever private (`project.yaml` says public). * Re-measure against the frozen source chosen at Gate C; the run C commits here are rehearsal identities, not the cutover's. +* The follow-ups of section 9. Items 1, 4 and 5 should land before the real + split. + +## 9. Reviews and dated follow-ups + +* **Copilot round 1** (on `c139e88`) raised two findings. Both are fixed in + `e3a76b5`, and both threads are resolved: + - a `git status` that fails is a refusal; + - the fixture code legs keep `.gitattributes`. +* **Independent adversarial review** (lane 1's reviewer, read-only): **LAND** + on `dbc6837` + ([review](https://github.com/opensoft/openRepoTools/pull/190#pullrequestreview-5464938427)). + Its own fake triad agreed with this record: + - a split leg gives 17 = 16 named skips plus `.gitattributes`; + - every subprocess of a composed run stayed inside its fixture; + - the caps reproduced against T006's `assembly-0001`. +* **Copilot round 2** (on `dbc6837`) raised two findings: + - (High) the pin `commit:` gap of section 3; + - (Medium, "previously missed") `_carries` accepts a directory where a + marker file belongs. + + I answered on the thread. Nothing was pushed, because the macOS gate runs + on `dbc6837`. + +These follow-ups are dated 2026-10-09 and recorded on #186. None of them is +in #190. + +1. **Review finding 11.** Three code-only assertions came after the first + document read (`test_repo_hygiene.py:994`, `:1052` and `:1264` at + `dbc6837`), so a standalone code leg skipped them along with the + documents. + - `feat/test-roots-followups` `6e63083` asserts every mixed test's + command half first. + - `2064390` proves it with a nested run against a fixture leg whose + commands lost those lines. +2. **Review finding 13.** The lane suite's missing-manual failure is checked + only by its text. Its `bad` branch did run locally + (`proof-manual-block.log`): the block, extracted unchanged with stub + helpers, printed `FAIL the lane manual is where the run's roots put it` + for `/nonexistent/README-lanes.md` and skipped on an empty value. A + behaviour test that runs the block itself is still to do. +3. **Review finding 14.** The four variables are not in `AGENTS.md`. + `6e63083` adds seven lines to "Testing your changes" and moves the cap + from 316 to 323 (340 to 347 at an assembly root), in a dated entry. +4. **Copilot round 2.** + - The conftest should refuse a pin whose `commit:` differs from the + gitlink, before it compares HEAD, with its own red-at-base test. + - Root markers should be regular files. +5. **Placement table.** The plan's owner amends `plan.md`'s placement table + to say the code leg keeps its own `.gitattributes` (section 2). +6. **Review nits.** + - `Roots.owner_of` gives `LICENSE` and `.gitignore` to the code root. + This is latent. + - `tests/run.sh`'s `abs_root` turns a quoted `~/x` into `$PWD/~/x`. + - The `GIT_ENV` comment in `tests/test_test_roots.py` says "file + transport allowed". ## Log SHA256s @@ -239,6 +377,12 @@ The logs stay in the private prep area (`work-20261008/ort-test-roots/logs/`). | M3 `m3-code-nosub.log` | `59027b3ceed4f52e1e6b2d0be926b0b7f3a2c7abfda2846b4fc9656b35208238` | | red at base `red-at-base.log` | `eb7d2bc460f5b4d474a71ccfb2a19ecca0798e4fc778685e620311d37384525c` | | green on branch `green-branch.log` | `b0289afab41217eb3c1e8ece140233b8a458492b37042727c668066a0f5a8cf4` | +| red at base, round 2 `red-at-base-r2.log` | `bb87a06e0e09785aae078c0cea5a89be40bef0dc2c90328b3a2e6025a489ad16` | +| green, 189 `green-branch-r1.log` | `d6f84d7f05d13c1d42d702c7c5c7f846c39b876392e1ee9d94ab6658b25665e3` | +| green, 191 on the follow-ups tree `6e63083` `green-branch-r3.log` | `78deeb7e2a36a1f10322a15b5bad6def7d2cc5242e503211bd468bc46ce8fb01` | +| `proof-check-attr-both.log` | `fb0561553b4d3b38554507f48267985eb128b0ff9f39d3665171b90ce9bdc6c3` | +| `proof-stale-pin.log` | `6b2223d39d58372f73b42f77f7a0d314c431807783f8cc68f4b8e1d8835da68c` | +| `proof-manual-block.log` | `b8b8217c07ecc82bf95295200f5b2c1a1202667ef7ca66a64feec7206c8f21d9` | | P1 `p1-composed.log` | `f611c1981520c4d479556d4b5ec9094845166a69772779dee7539113f473fe34` | | P2 `p2-composed-refuses-without-spec.log` | `38889cab1e4a3c842bfddc9687e30b21df9c183b238df0676088dd1e8e981059` | | P3 `p3-leg-standalone.log` | `0f5e8c2163fbe1457bb692eba3bdbc4b67e03020cf7ec1ba0ee0fc7e38806144` | From d28e2cc8afcef0639712bc2938f921c904000a10 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 02:31:01 +0000 Subject: [PATCH 18/42] Correct the test-roots record: the standalone leg's lane suite aborted on an unguarded manual read, which this record had attributed to two stamp assertions alone. P3's log shows `ln_doc: unbound variable` at line 6595. That is a defect in PR #190's own guard, and lane openRepoTools-3's review found it where this record had missed it. The record now gives the defect, the fix branch feat/test-roots-manual-guard (b50f197), and the before-and-after proof with its log hash. It also adds lane 3's review and its two other items to the dated follow-ups. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../test-roots-and-ci-2026-10-08.md | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/specs/004-migrate-to-triad/test-roots-and-ci-2026-10-08.md b/specs/004-migrate-to-triad/test-roots-and-ci-2026-10-08.md index 8faca169..7badfa33 100644 --- a/specs/004-migrate-to-triad/test-roots-and-ci-2026-10-08.md +++ b/specs/004-migrate-to-triad/test-roots-and-ci-2026-10-08.md @@ -9,7 +9,8 @@ checked complete by this record; Gate B stays open. (`feat/test-roots-for-the-triad`, head `dbc6837`: `c139e88`, `e3a76b5` and `dbc6837` on `main` `c4864ac`; `ready` applied 2026-10-09T02:11Z). **Follow-ups (not in #190):** branch `feat/test-roots-followups`, `6e63083` -and `2064390` on `dbc6837` (section 9). +and `2064390` on `dbc6837`; branch `feat/test-roots-manual-guard`, `b50f197` +on `dbc6837` (sections 6 and 9). **CI patches:** [patches/t010-code-leg-tests-workflow.patch](patches/t010-code-leg-tests-workflow.patch) and [patches/t010-assembly-exact-pin-job.patch](patches/t010-assembly-exact-pin-job.patch). **Rehearsed arrangement:** run C of 2026-10-08 (private prep @@ -272,6 +273,22 @@ The failures, recorded rather than deleted: `TMPDIR` and passed it (1315 passed at `c139e88`, 1318 at `dbc6837`). These sections are untouched by PR #190; the length interaction belongs with the run-root work of #162/#184 (inferred from the expected and actual rows, not bisected). +* **The lane suite ABORTED in P3**, after those two FAILs, at + `test_lane_helpers.sh: line 6595: ln_doc: unbound variable`. This is a + defect in PR #190's own guard. `ln_doc` is bound only inside the + `if [ -n "$LANE_MANUAL" ]` section, the `--retire` section uses it 350 + lines later outside any guard, and the suite runs under `set -u`. + - This record first attributed P3's lane-suite failure to the stamp + assertions alone. Lane openRepoTools-3's review of #190 found the abort, + and it is in P3's own log. + - At `dbc6837` the lines are 6601–6602. + - The fix is `feat/test-roots-manual-guard` @ `b50f197`, one commit on + `dbc6837`. It guards the two lines with the same named skip, and adds a + text test that every use of the manual's text sits inside such a guard. + - With the section run alone under `set -u` and an empty manual + (`proof-retire-block.log`): `dbc6837` aborts, exit 1; `b50f197` gives 4 + passed and 1 skipped, exit 0. + - Today's layout always takes the guard. Every skip in P3 and P4 names its root. P3's 17 are `README.md` ten times (nine hygiene tests and the installer's check), `AGENTS.md` three times, @@ -309,8 +326,8 @@ pytest, so every local run had `/usr/bin` first on `PATH`. CI is the record. composed job if the legs are ever private (`project.yaml` says public). * Re-measure against the frozen source chosen at Gate C; the run C commits here are rehearsal identities, not the cutover's. -* The follow-ups of section 9. Items 1, 4 and 5 should land before the real - split. +* The follow-ups of section 9. Items 1, 4, 5 and 6 should land before the + real split. ## 9. Reviews and dated follow-ups @@ -325,6 +342,14 @@ pytest, so every local run had `/usr/bin` first on `PATH`. CI is the record. - a split leg gives 17 = 16 named skips plus `.gitattributes`; - every subprocess of a composed run stayed inside its fixture; - the caps reproduced against T006's `assembly-0001`. +* **Lane openRepoTools-3, reviewer of record:** **LAND** on `dbc6837` + (review 5464931249). It names three things to fix before the squash: + - (1) the `ln_doc` abort of section 6, with the fix ready; + - (2) the paired layout `/worktrees//code` finds no + assembly (measured on run C), so in a feature worktree every document + test would skip; + - (3) `OPENREPOTOOLS_CODE_ROOT` and `OPENREPOTOOLS_ASSEMBLY_ROOT` redirect + the suite silently, because the report header is hidden by `-q`. * **Copilot round 2** (on `dbc6837`) raised two findings: - (High) the pin `commit:` gap of section 3; - (Medium, "previously missed") `_carries` accepts a directory where a @@ -359,7 +384,12 @@ in #190. - Root markers should be regular files. 5. **Placement table.** The plan's owner amends `plan.md`'s placement table to say the code leg keeps its own `.gitattributes` (section 2). -6. **Review nits.** +6. **Lane 3's review.** + - (1) `feat/test-roots-manual-guard` `b50f197`, unless the coordinator + takes it into #190 before the squash. + - (2) Assembly discovery for the paired worktree layout, before Gate C. + - (3) Say which roots a run used where `-q` cannot hide it. +7. **Review nits.** - `Roots.owner_of` gives `LICENSE` and `.gitignore` to the code root. This is latent. - `tests/run.sh`'s `abs_root` turns a quoted `~/x` into `$PWD/~/x`. @@ -382,6 +412,7 @@ The logs stay in the private prep area (`work-20261008/ort-test-roots/logs/`). | green, 191 on the follow-ups tree `6e63083` `green-branch-r3.log` | `78deeb7e2a36a1f10322a15b5bad6def7d2cc5242e503211bd468bc46ce8fb01` | | `proof-check-attr-both.log` | `fb0561553b4d3b38554507f48267985eb128b0ff9f39d3665171b90ce9bdc6c3` | | `proof-stale-pin.log` | `6b2223d39d58372f73b42f77f7a0d314c431807783f8cc68f4b8e1d8835da68c` | +| `proof-retire-block.log` | `c593520e6659febe5a7952df174feb4c03a8939a593369d010e3f9d2aa4e0393` | | `proof-manual-block.log` | `b8b8217c07ecc82bf95295200f5b2c1a1202667ef7ca66a64feec7206c8f21d9` | | P1 `p1-composed.log` | `f611c1981520c4d479556d4b5ec9094845166a69772779dee7539113f473fe34` | | P2 `p2-composed-refuses-without-spec.log` | `38889cab1e4a3c842bfddc9687e30b21df9c183b238df0676088dd1e8e981059` | From 935f53f8b497b1409fe4523a14a84ba416f19d95 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 02:43:28 +0000 Subject: [PATCH 19/42] Record the candidate's state at the coordinator's checkpoint: no suite run has finished, the composed run was stopped at about 20% and must be rerun, and the CI numbers it will be compared with. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../candidate-2026-10-09.md | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/specs/004-migrate-to-triad/candidate-2026-10-09.md b/specs/004-migrate-to-triad/candidate-2026-10-09.md index d9a2a981..23e2d390 100644 --- a/specs/004-migrate-to-triad/candidate-2026-10-09.md +++ b/specs/004-migrate-to-triad/candidate-2026-10-09.md @@ -11,8 +11,8 @@ No task is checked complete by this record, and it approves nothing. It holds every script, log, patch and install snapshot named below. Machine paths stay there. -**State: in progress.** The candidate is built and its install is compared. The three -suite runs are queued behind the workstation lock; section 5 is filled in when they end. +**State: checkpoint (02:42Z).** The candidate is built and its install is compared. No +suite run has finished yet; section 5 says where each one stands. ## 1. Inputs @@ -161,7 +161,27 @@ section 2, repeated at this candidate's own commits, as its section 2 asked. ## 5. The suite: composed, standalone, and standalone without the dependency -Queued at 01:53:52Z. The results go here when the runs end. +### State at checkpoint (2026-10-09T02:42Z) + +**No suite run has finished. All three must be run, the composed one again from the +start.** + +- **Composed.** `OPENREPOTOOLS_COMPOSED=1`, with the assembly and spec roots named. It + took the workstation lock at 02:10:40Z, after `tests/run.sh` had waited 5 minutes on + the census. By 02:42:37Z it had reached about 20%: the lane helpers' shell suite was + running, and one non-pass result was printed near 15%. The coordinator then called a + checkpoint for an account swap. This writer stopped its own run (one process group, + TERM, nothing left), released the lock with it, and removed the run's own run root. + The partial log is kept in the receipt. It is not a result. +- **Standalone and no-submodule.** Not started. + +For comparison when they run, the CI numbers already read: + +| Where | `tests` | `tests-no-submodule` | +| --- | --- | --- | +| main `63810dd` (run 37866269342) | 1296 passed | 1084 passed, 212 skipped | +| PR #190 `dbc6837` (run 37869489919) | 1318 passed | 1105 passed, 213 skipped | +| PR #191 `2411f40` (run 37869321635) | 1379 passed, 7 skipped | 1165 passed, 221 skipped | ## 6. What this proves for Gate B, and what it does not From 63bff0896e019c55c552007e2627944e3b55406d Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 20:27:58 +0000 Subject: [PATCH 20/42] T018 disposable half: feature selection, OpenSpec and park/resume/status in a triad fixture (#186) Evidence of 2026-10-09 from the composed candidate (assembly 3bd95a7, spec 9e1d006, code f0af7d9, nested openRepoShape 7f84ca4), copied into fresh bare remotes and driven from three temporary homes under env -i, a disposable agent root and workspace record, file-only git and a private network namespace. Selection resolves the same root and feature directory from the root and both legs. OpenSpec answers only from the spec leg. park commits and pushes one WIP per leg and leaves the root and its gitlinks alone. resume refuses a root that the bootstrap itself dirtied, and silently drops a parked nested gitlink move. Every refusal is relayed verbatim; nothing was forced. No task is checked complete. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../t018-disposable-2026-10-09.md | 253 ++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 specs/004-migrate-to-triad/t018-disposable-2026-10-09.md diff --git a/specs/004-migrate-to-triad/t018-disposable-2026-10-09.md b/specs/004-migrate-to-triad/t018-disposable-2026-10-09.md new file mode 100644 index 00000000..9208f407 --- /dev/null +++ b/specs/004-migrate-to-triad/t018-disposable-2026-10-09.md @@ -0,0 +1,253 @@ +# T018, disposable half: feature selection, OpenSpec and the estate verbs in a triad fixture — October 9, 2026 + +**Task:** [T018](tasks.md)'s disposable workflow checks under +[#186](https://github.com/opensoft/openRepoTools/issues/186). The tasks.md +dependency section allows this as provisional rehearsal before Gate C. No task +is checked complete by it. Real leg repositories, the real cutover, lane +rebinding and WIP transitions in real estates come after Gate C. None of them +was attempted. +**Authority:** Brett Heap's words of 2026-10-07, "start the triad migration" +(#186), and of 2026-10-09, "usage reset, resume all. read handoff and resume +and fan out wide and do as much as possible in parallel". +**Fixture:** the composed candidate of 2026-10-09: assembly `3bd95a7` +(run C's split `69b8924` plus the T006, T007 and T010 follow-ups and two leg +bumps), spec leg `9e1d006`, code leg `f0af7d9`, and the code leg's nested +`upstream/openRepoShape` at `7f84ca4`. Every repository was copied fresh into +this run's own bare remotes. The candidate and run C were only read. +**Logs:** host-local, under the run directory (`/logs/00…93`), and not +committed. Below, `` is that directory and `~` is the temporary HOME of +the workstation named. + +## 1. The sandbox + +| Concern | How it was held | +| --- | --- | +| HOME | Three temporary homes, `/home1`, `home2` and `home3`, one per simulated workstation. Every command ran under `env -i` with only the variables below. | +| Agent root | `AGENT_PROTOCOL_ROOT=~/.agents`: a dereferenced copy (`cp -rL`) of the real agent root, with its own two-line `workspace.yaml` (`repository: t018-sandbox/t018-wip`, `path: ~/wip/t018-wip`). The real `workspace.yaml` was never written; its sha256 and mtime are unchanged. | +| Lane register | No `LANES_*` variable reached any command. No lane command ran in the fixture. | +| Git transport | `GIT_ALLOW_PROTOCOL=file`; `protocol.file.allow=always`. Each home's `~/.gitconfig` points `https://github.com/opensoft/`, `git@github.com:opensoft/` and run C's bare-remote prefix (the URLs the candidate's `.gitmodules` carries) at `/remotes/`, which holds this run's own bare copies plus a seeded disposable WIP repository. | +| Network | Every command ran inside `unshare --map-current-user --net`, a namespace in which a TCP connect fails with "Network is unreachable". Refusing `gh`/`curl`/`wget` shims logged two calls, both `gh api` from `status --fetch`, and both were refused. | +| Commands under test | Installed into each temporary HOME by the candidate assembly's own front door, `./openRepoTools --install`. That printed "every payload file was checked against that commit and nothing fetched", "17 of 17 placed" and "29 placed files recorded". The installed `park`, `resume`, `status` and `openRepoTools` are byte-identical to the code leg's blobs (`132ce6d…`, `92fd430…`, `bb5c255…`, `04769c4…`). | +| Speckit overlay | `setup-openspeckit` and its overlay templates were copied byte for byte from workBenches `ffd5516` (44 files, each equal to its `HEAD` blob) and run with `WORKBENCHES_ROOT` set to that copy. | +| No writes outside the run | Afterwards, the ref digests of the eight source repositories (the candidate's four bare copies, run C's two leg remotes, its source clone and its openRepoShape mirror) were unchanged, and no file under run C's remotes, its source's refs or the candidate's remotes was newer than the run's start. The real `~/.agents/workspace.yaml` and the real installed `park`, `resume`, `status`, `openRepoTools` and `setup-openspeckit` were unchanged too. | + +Tool versions: git 2.43.0, GNU make 4.3, bash 5.2.21, Python 3.12.3, `specify` +0.12.12.dev0, and OpenSpec 1.2.0 and 1.6.0. 1.2.0 is the `openspec` first on +the bench's `PATH`; 1.6.0 is `/usr/bin/openspec`. + +The estate on workstation 1 was a recursive clone of the assembly, then +`make bootstrap`. That exited 0: legs on `main` at their pins, "pins ok", and +`11 copied shape file(s) match their digests`. Then came the documented +bootstrap, `setup-openspeckit --shape on --integration codex --base-branch main +--no-global-agent-pointers`. It exited 0 with `git-config.yml` reading +`checkout_mode: worktree`, `base_branch: main` and `worktree_root: worktrees`. +It also left the tree dirty, which section 4 turns on: + +- at the root, `AGENTS.md` and `CLAUDE.md` each gained the 22-line managed + block, and `.specify/`, `.agents/`, `.claude/`, `.codex/` and 17 other agent + files or directories became untracked; +- in the spec leg, `openspec/README.md`, `openspec/changes/archive/.gitkeep` + and `openspec/specs/.gitkeep` became untracked, which the root reports as + ` M spec`. + +## 2. Speckit feature selection and branch naming + +| Command (workstation 1, from the assembly root unless named) | Exit | Result | +| --- | --- | --- | +| `.specify/extensions/git/scripts/bash/create-new-feature.sh --json --dry-run --short-name t018-probe "T018 disposable probe"` | 0 | `BRANCH_NAME 006-t018-probe`, `REPO_SHAPE three-leg`, `SPEC_WORKTREE_PATH …/worktrees/006-t018-probe/spec`, `CODE_WORKTREE_PATH …/worktrees/006-t018-probe/code`, `FEATURE_DIR …/worktrees/006-t018-probe/spec/specs/006-t018-probe` | +| The same, invoked as `../.specify/…` from `code/` and from `spec/` | 0, 0 | Identical JSON: `PROJECT_ROOT` is the assembly root, found from the script's own location, not from the current directory | +| A local branch `009-code-only-probe` in the code leg only, then the same dry run | 0 | `010-t018-probe`: the code leg's branches count | +| The real creation | 0 | Both legs got branch `006-t018-probe` from `origin/main`, as worktrees at `worktrees/006-t018-probe/{spec,code}`. The root stayed on `main` at `3bd95a7`, registered no worktree and showed only `!! worktrees/`. `.specify/feature.json` = `{"feature_directory":"worktrees/006-t018-probe/spec/specs/006-t018-probe"}` | +| `.specify/scripts/bash/check-prerequisites.sh --json --paths-only`, run from the root, `code/`, `spec/`, the spec worktree and the code worktree (each by its relative path to the root's script) | 0 ×5 | Identical: `REPO_ROOT` the assembly, `BRANCH 006-t018-probe`, `FEATURE_DIR` in the spec worktree | +| `.specify/` from inside a leg | — | None exists in either leg or either leg worktree. A cwd-relative `.specify/scripts/…` works only from the root | +| `SPECIFY_FEATURE_DIRECTORY=worktrees/006-t018-probe/spec/specs/004-migrate-to-triad` | 0 | Overrides `feature.json`: `BRANCH 004-migrate-to-triad`, `FEATURE_DIR` that directory | +| `SPECIFY_FEATURE=004-migrate-to-triad` | 0 | `BRANCH 004-migrate-to-triad`, but `FEATURE_DIR` stays 006's from `feature.json`. **The pair disagrees** | +| The `speckit-git-validate` skill's three-leg rule, applied by hand | — | The root's `main` is not validated. Spec and code are both on `006-t018-probe`, which matches `^[0-9]{3,}-.+`; the legs agree; the spec directory was found. The extension's own `speckit.git.validate.md` has no three-leg block; the installed skill does | +| Upstream of the new branch in each leg | — | `branch.006-t018-probe.merge=refs/heads/main`: it tracks `origin/main` | +| The code worktree's nested dependency | — | `-7f84ca4… upstream/openRepoShape`, an empty directory: no nested submodule is initialised | + +**How the number is chosen.** In a three-leg root the feature number is one more +than the highest of four sources: `specs/` in the spec leg's `origin/main`; +`specs/` in its working tree; and the `NNN-` branch names, local and +remote-tracking, of both legs. The root's own branches are not read. The branch +name is the same in both legs, and the root never gets one. On workstation 2, +the next feature came out as `007-nested-pin-probe`. + +## 3. OpenSpec from each root (workstation 1, feature 006 open) + +| Directory | `openspec list` 1.2.0 / 1.6.0 | `validate --all --strict` 1.2.0 / 1.6.0 | `validate migrate-to-triad --strict` 1.2.0 / 1.6.0 | +| --- | --- | --- | --- | +| assembly root | exit 1 "No OpenSpec changes directory found" / **exit 0 "No active changes found."** | **exit 0 "No items found to validate." / exit 0, the same** | exit 1 "Unknown item" / exit 1 | +| `code/` and the code worktree | as the root | as the root | as the root | +| `spec/` and the spec worktree | exit 0, five changes / exit 0, five changes | **exit 1, 3 passed and 2 failed** / exit 0, 5 passed | **exit 1** / exit 0, "is valid" | + +`openspec list --specs` printed "No specs found." everywhere: no canonical spec +exists yet under `openspec/specs/`. Under 1.6.0, `openspec context` from the +root or the code leg exits 1 with "No OpenSpec root found from the current +directory". From the spec leg or the spec worktree it names that directory as +the root. The 1.2.0 failures are +`ADDED "Installation retains the public interface and one revision" must contain SHALL or MUST` +in `migrate-to-triad`, and two of the same kind in +`interactive-lane-name-repair`. **They are not caused by the split:** 1.2.0 +gives the same 3 passed, 2 failed on today's single-repository `main` +`63810dd`, where 1.6.0 passes all five. + +## 4. The estate verbs `status`, `park` and `resume` + +Workstation 1 created and parked the feature. Workstation 2 started from +nothing and resumed it. Workstation 3 tested one resume flag. Feature 006 +carried four kinds of change: a new spec file and a staged edit in the spec +leg, and a tracked edit and an untracked file in the code leg. + +| Log | WS | Command | Exit | What it did | +| --- | --- | --- | --- | --- | +| 10 | 1 | `status openRepoTools`, before any clone | 2 | Refused: no estate (verbatim below) | +| 14 | 1 | `status openRepoTools`, cloned and bootstrapped | 0 | `0 finding(s) in 3 repositories` | +| 15 | 1 | `park openRepoTools --dry-run`, no overlay yet | 2 | `make park` refused (verbatim below) | +| 18 | 1 | `status`, after `setup-openspeckit` | 1 | Two findings: root `dirty: 23 path(s)`, spec leg `dirty: 3 path(s)` | +| 50 | 1 | `status`, feature open and edited in both legs | 1 | The same two findings. With no parked record, the open feature worktrees are not listed | +| 51, 52 | 1 | `park openRepoTools --dry-run --lane t018-sandbox`, then without `--dry-run` | 0, 0 | `PARKED: 006-t018-probe`, spec `4ea12d2` and code `139031a`, each "pushed origin/006-t018-probe". The record `workspaces/opensoft/openrepotools.yaml` was committed as `8f5cb0d` and pushed. Reported, not touched: "24 path(s) changed at the root … the root carries no feature branch at all" | +| 53 | — | Verification | — | One WIP commit per leg, each with a single parent at the leg's base, subject `wip: park 2026-10-09T13:47:46Z — lane t018-sandbox`. Spec: `M quickstart.md`, `A specs/006-t018-probe/spec.md`. Code: `M README.md`, `A t018-probe.txt`. The code WIP's `upstream/openRepoShape` gitlink was unchanged at `7f84ca4`. **The root was untouched**: still `3bd95a7`, gitlinks `9e1d006` and `f0af7d9`, assembly remote unmoved. Both worktrees were left clean at their WIP commits. The record lists both legs with `role`, `parked_commit`, `wip_depth: 1` and `pushed: true` | +| 54, 55 | 1 | `status`, then `status --fetch` | 1, 1 | Four findings. Two are the dirt above. The other two say, for each leg, "branch 006-t018-probe: 1 commit(s) unpushed to origin/main", although `origin/006-t018-probe` equals `HEAD` (log 62: the branch tracks `origin/main`). Under `--fetch`, both `gh api` checks were refused by the shim and reported on the row | +| 58, 59 | 1 | `park --dry-run` from inside the code worktree, and `status` from inside `spec/`, with no name | 0, 1 | Both walk up to the assembly root | +| 60, 61 | 1 | `park.sh --dry-run`, run directly from `code/` or from the code worktree | 1, 1 | `Error: not inside a Git repository or Speckit checkout.` | +| 56, 88 | 1 | `resume openRepoTools --dry-run`, then the real run | 1, 1 | **Root refused, because of the bootstrap's own outputs.** `make resume` never ran. The real run pulled the workspace record first | +| 57, 82 | 1 | `make resume ARGS=--dry-run`, then `make resume` at the root: the extension, as AGENTS-shape.md names it | 0, 0 | Same-workstation resume worked. "worktree already registered … left as it is", "un-committed 1 WIP commit" in each leg, and all four files' sha256 equal their values before the park | +| 71 | 2 | `resume openRepoTools` on a fresh home | 2 | It pulled the record, cloned the root with both legs and the nested dependency, and passed `make bootstrap`. Then `make resume` refused: overlay missing | +| 72–74 | 2 | `setup-openspeckit … --no-repo-agent-pointers`, plus the four per-clone excludes today's checkout carries (`/.agents/`, `/.claude/`, `/.codex/`, `/.specify/`), then `resume --dry-run` | 1 | **Still refused**: the three untracked spec-leg files make the root ` M spec` | +| 75, 76 | 2 | Then three exact-path excludes in the spec leg's common `info/exclude`, then `resume` | 0 | `RESUMED: 006-t018-probe`, "un-committed 1 WIP commit" in both legs. `FEATURE_JSON` and `STATE_FILE` were restored; the root stayed at `3bd95a7`, clean, with legs on `main` | +| 77 | — | Verification | — | All four sha256 values were OK. The staged edit came back unstaged, by design. The untracked file came back untracked. The branch now tracks `origin/006-t018-probe` | +| 78 | 2 | `status openRepoTools` | 0 | `0 finding(s)`, although the next `park` here refuses (log 79) | +| 79, 80 | 2 | A further edit, then `park --dry-run` and `park` | 2, 2 | Refused: behind by one parked WIP commit (verbatim below). No leg ref and no worktree moved. **But the real run still committed and pushed the record as `c1288a1`** ("0 feature(s)"), rewriting `parked_at` and `parked_by_lane` while keeping 006's old entries | +| 84–86 | 2 | New feature 007: the code worktree's nested dependency initialised, moved to `39d5c98`, plus a new file; then `park` | 2 (`make: *** … Error 3`) | 007 parked. Spec: `wip 0`, branch pushed at its base. Code: WIP `032c85b`, carrying the gitlink move `7f84ca4 → 39d5c98` while `contracts/openreposhape-pin.yaml` still says `7f84ca4`. 006 was refused again. Record `653f67d` | +| 89, 90 | 1 | `make resume` | 0 | 007 recreated with "un-committed 1 WIP commit". The worktree shows only `?? t018-nested.txt`. **The nested gitlink move is gone from both the index and the working tree**, because the nested submodule is not initialised there, and nothing warned. It survives only in `032c85b` on `origin/007-nested-pin-probe` | +| 92, 93 | 3 | `make resume "ARGS=--keep-staged --feature 007-nested-pin-probe"` | 0 | The move stays staged (`M upstream/openRepoShape` at `39d5c98`). `git submodule update --init upstream/openRepoShape` then checks out `39d5c98` | + +### Refusals, verbatim + +`~` stands for the workstation's temporary HOME. + +```text +REFUSED: no estate named 'openRepoTools' under your projects directory. + An estate is a FAMILY folder (openRepoTools/openRepoTools/family.yaml) or a standalone + project root (openRepoTools/project.yaml). Bring it back with `resume openRepoTools`, or + read one of these: + + (none) +``` + +```text +make park needs the Speckit worktree overlay; install it with: setup-openspeckit +make: *** [Makefile:55: park] Error 2 +``` + +```text +make resume needs the Speckit worktree overlay; install it with: setup-openspeckit +make: *** [Makefile:59: resume] Error 2 +``` + +```text + REFUSED root openRepoTools: uncommitted changes in ~/projects/openRepoTools. + Nothing was fast-forwarded and nothing was reset. + look: git -C ~/projects/openRepoTools status --short + then: commit or stash them yourself, and re-run `resume openRepoTools`. + [root openRepoTools] bootstrap skipped: refused above. Nothing here was fetched, moved or reset. +``` + +```text +Error: 006-t018-probe (spec leg) is behind origin by 1 parked WIP commit(s); that feature was NOT parked. +The parked commit 4ea12d231fdaa50dc2afe0377e5ced2c83575f6b is still the remote tip and this workspace recorded it, so retiring it +cannot destroy anyone else's work: + git -C worktrees/006-t018-probe/spec push --force-with-lease=006-t018-probe:4ea12d231fdaa50dc2afe0377e5ced2c83575f6b origin 006-t018-probe +park does not force-push on your behalf. Re-run `make park` afterwards, or pass +--retire-parked-wip to let park do exactly that one push. +``` + +```text +Error: not inside a Git repository or Speckit checkout. +``` + +No refusal was forced. The leased push and `--retire-parked-wip` are the +person's to choose, and neither was run. The excludes in rows 72–76 are a +measured posture, not a repair: they show which local state `resume` reads as +dirty. + +## 5. Follow-ups before cutover + +1. **A bootstrapped triad root is "dirty" to `resume`.** `setup-openspeckit` + writes the managed block into the tracked `AGENTS.md` and `CLAUDE.md` + (unless `--no-repo-agent-pointers` is passed), and leaves `.specify/` and + the agent directories untracked. It also writes three untracked files into + the spec leg, which the root reports as ` M spec`. Any of these makes + `resume ` refuse the root and skip `make resume`. Today's per-clone + excludes do not cover the spec-leg files. + - This is Brett Heap's posture decision (root-guidance §8). The options + measured here: land `openspec/README.md`, `openspec/changes/archive/.gitkeep` + and `openspec/specs/.gitkeep` in the spec leg before cutover, through a + spec PR and a root pin bump; run the bootstrap with + `--no-repo-agent-pointers`, or commit its block; and carry the four root + excludes. + - A leg's `info/exclude` lives in its common git dir, so it applies to + **every feature worktree** of that leg. A directory pattern there would + hide feature work from park's `git add -A`. Use exact paths only. + - Owners: Brett (posture); workBenches (what the bootstrap writes). +2. **The overlay does not travel with a clone.** On a fresh workstation, + `resume ` clones and bootstraps, then refuses until the person runs + `setup-openspeckit` and runs `resume` again. Put that order in the T018 + real-half and T019 runbook. Under a temporary HOME, the shim also needs + `WORKBENCHES_ROOT`. +3. **Feature worktrees never initialise the code leg's nested dependency.** + Neither `create-new-feature.sh` nor `resume.sh` runs a nested + `submodule update`. Two consequences: + - `upstream/openRepoShape` is empty in every code feature worktree, so the + command tests skip there and composed checks lack the dependency. + - A parked gitlink move is **silently dropped** by a default resume on + another workstation. The workaround measured here is + `git submodule update --init upstream/openRepoShape` in the code worktree, + and `resume -- --keep-staged` when the parked code WIP moves a + gitlink. + - Owner: workBenches (initialise nested submodules in leg worktrees, or warn + when an un-committed WIP carries a `160000` change). +4. **New feature branches track `origin/main`,** so `status` calls a pushed + park "unpushed to origin/main". After a resume elsewhere the branch tracks + `origin/`, so the same feature reads differently on two machines. + Owners: workBenches (`--no-track`, or `push -u` at park) or this repository + (`status` reading `origin/`). +5. **A park that refuses every feature still rewrites the record's header.** + `parked_at` and `parked_by_lane` change, with the subject "0 feature(s)". + A third machine would then misread whose park the record holds. Owner: + workBenches `park.sh`. +6. **Partial parks reach the person as exit 2.** `park.sh` exits 3, make prints + `Error 3`, and `park` says "`make park` exited 2". The Makefile header says + so, and the make line is the one to read. This repository's `park` could + relay the code. Optional. +7. **Run OpenSpec only from the spec leg or a spec worktree.** + - From the root or the code leg, 1.6.0 reports "No active changes found." + and `validate --all --strict` passes vacuously with exit 0. Any CI or + agent validation step must change into the spec leg, or use a store that + resolves it. + - The bench's `PATH`-first 1.2.0 fails two of five changes that 1.6.0 + passes. This predates the split, but the bench should resolve 1.6.0. + Owner: workBenches. +8. **Select a feature with `.specify/feature.json` or `SPECIFY_FEATURE_DIRECTORY`, + never `SPECIFY_FEATURE` alone.** The latter yields a `BRANCH` and a + `FEATURE_DIR` that disagree. +9. **`speckit.git.validate.md` has no three-leg rule;** the installed skill + does. Owner: workBenches, in the command doc. +10. **`status` does not report open feature worktrees,** either before any + record exists or after a resume that `park` will next refuse (rows 50 + and 78). Consider a line for each. Owner: this repository. +11. **Wording:** `resume --dry-run` reports the estate "absent … the workspace + was not cloned" when the workspace clone exists but has not been pulled. + +## 6. What this did not do + +- It touched no real leg repository, real estate or lane register, and did no + lane rebinding or real WIP transition. Those are T018's post-Gate-C half. +- It ran no `--retire-parked-wip`, no force-push and no reset. +- It wrote nothing to the candidate's or run C's repositories. +- It ran no suite in the fixture. Only this branch's own hygiene check ran. +- It did not exercise macOS, or the `gh`-backed fork and shape-currency checks. +- Every workstation reports the same `parked_on: docker-desktop`, because all + three homes are on one host. Distinguishing workstations by host was not + tested. From 096de93fd4577e04c68e25156c89a97a9d71939f Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 20:32:43 +0000 Subject: [PATCH 21/42] =?UTF-8?q?Dated=20corrections=20to=20#192's=20run?= =?UTF-8?q?=20C=20and=20T006=20evidence:=20#193=20items=208=E2=80=9313?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part of #186. Text only, in three Markdown records under specs/004-migrate-to-triad/. No patch file, plan or YAML is touched. Every figure was measured again on 2026-10-09. - adopter-rehearsal-2026-10-08.md - Item 8 (review 5464893559 finding 5): one dated block says where the records are now, what run C superseded, and that run B's plan stays at 63810dd. It records the 2026-10-08 restart that removed runs A and B's repositories, and the openspec 1.2.0 observation. openspec 1.13.1 now exits 0 on 837928a and on 71b05cb. - Item 10 (finding 17): `check` reads the clone's local main, not HEAD. This plan gives plan-stale, exit 1, against any newer main. Reproduced both ways. - Item 12 (finding 6): the "no ensurepip" cause is withdrawn. The refusal is real, and its cause is not established. - root-guidance-2026-10-08.md - Item 9 (findings 8 and 9): 18 front-door test functions (17 + 1), not 17. The command test stays red after code-0001. The "23 in 9" total disagrees with its own per-file list (26 in 11), and which is right is not established. Also :6, and :195's consumer-reads is on main. - Item 11 (finding 16): no receipt is cited. The record now says what the patches reproduce from public inputs, and which rows exist only in the document. - adopter-rehearsal-2026-10-07.md - Item 8: one dated line. The false "On this branch" sentence is in this file. Lane: openRepoTools-2 Co-Authored-By: Claude Opus 5.5 --- .../adopter-rehearsal-2026-10-07.md | 5 ++ .../adopter-rehearsal-2026-10-08.md | 57 ++++++++++++++++++- .../root-guidance-2026-10-08.md | 40 +++++++++++-- 3 files changed, 95 insertions(+), 7 deletions(-) diff --git a/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-07.md b/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-07.md index e6e20afe..f447de83 100644 --- a/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-07.md +++ b/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-07.md @@ -182,6 +182,11 @@ blob, `8043147a`. The source clone was unchanged except for the one new ref [quickstart.md](quickstart.md) and [preparation.md](preparation.md) still describe the October 4 version; the plan's owner decides when to refresh them. +- *Dated 2026-10-09 (#193 item 8):* since #192, the mapping link above opens + run C's plan ([adopter-rehearsal-2026-10-08.md](adopter-rehearsal-2026-10-08.md)), + and run B's stays in history at `63810dd`. #188 landed as `837928a`. The + bench restart of 2026-10-08 cleared both runs' disposable repositories, so + this file's receipt holds their only record. - Each run used a fresh, empty `--local-remote-dir` and `--work-dir`. Without the first, `execute` calls `gh repo create`. The plan's `execution_authorized: false` is informational and the tool does not read it. 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 index 26f0a537..acbe2098 100644 --- a/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-08.md +++ b/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-08.md @@ -21,10 +21,37 @@ 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. +**Dated 2026-10-09, after #192 landed (#193 items 8 and 13):** + +- **Where the records are.** #192 brought this file and run C's plan to + main. The 2026-10-07 record has been on main since #189 (`63810dd`), not + on a branch as the paragraph above says. +- **What run C superseded.** No record: runs A and B stand as recorded. Its + plan did replace run B's as the + [mapping](../../openspec/changes/migrate-to-triad/adoption-plan.yaml), in + merge `1a26463`. On main that link opens run C's plan from both records. + So "On this branch" in the mapping section below holds for main, and the + 2026-10-07 record's "On this branch, the mapping is the regenerated run B + plan" held only on its own branch. Run B's plan stays in history at + `63810dd` (blob `f1067e3`). Neither plan is the cutover plan. +- **Runs A and B are gone from the bench.** The bench restart of + 2026-10-08, at about 09:45Z, cleared `/tmp` (#186 comment 6068861167). + Both runs were built in a session scratch directory there, as the + 2026-10-07 receipt's README records, so their disposable repositories are + gone and that receipt's logs are their only record. That is why run C is + kept host-local. +- **An observation left out.** The run C comment on #186 (6068938765) + noted that `openspec validate migrate-to-triad --strict` with `openspec` + 1.2.0 fails on pristine `837928a` too (`ADDED "Installation retains the + public interface and one revision" must contain SHALL or MUST`), so run C + does not cause it. The bench's `openspec` is now 1.13.1. On 2026-10-09 + the same command exited 0 on `837928a` and on `71b05cb`, #192's head, + whose tree its squash `1abee1d` carries unchanged. + ## 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 +- **filter-repo:** `git-filter-repo` 2.47.0 from a private venv. + `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 @@ -32,6 +59,18 @@ the private receipt. 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`. + - *Corrected 2026-10-09 (#193 item 12):* this bullet first gave the cause + as "the rebuilt bench has no `ensurepip`", and the bench contradicts it. + Its dpkg log has `python3-venv` and `python3.12-venv` installed on + 2026-09-23 at 00:18:06Z, before the 2026-10-08 boot. + `/usr/bin/python3 -m ensurepip --version` prints `pip 24.0`, and + `/usr/bin/python3 -m venv` succeeds (lane 2, #186 comments 6072333212 + and 6072724043; measured again on 2026-10-09). The refusal itself is + real: the receipt's setup log records it. Its cause is not established. + The `python3` the writer ran was the first on its `PATH`: the venv's + `pyvenv.cfg`, in run C's host-local arrangement, records a user-local + `python3` based on `/usr/bin/python3.12`. That entry is no longer on + the bench, so the refusal cannot be retried as it ran. - **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 @@ -65,6 +104,20 @@ published copy. It differs from the executed plan only by its header, `local_path: .`, and the two closing status fields (`execution_authorized: false`). +*Added 2026-10-09 (#193 item 10):* `check` validates against the source +clone's local `main`, not its `HEAD`. In openRepoShape `7f84ca4`'s +`adopt-project.py`, `_default_branch` (:288) takes the branch name from +`origin/HEAD`, and `git rev-parse` of that name (:256-257) is the commit +compared with the plan's source commit (:1026-1032). This plan therefore +passes `check` only in a clone whose local `main` is `837928a`. Against any +newer `main` it exits 1 with `FINDING plan-stale: the plan was written +against 837928a1029a but main is now at …`, whatever `HEAD` is checked out. +That includes main once this plan is on it: a plan cannot name the commit +that carries it. Measured on 2026-10-09 with this plan and a fresh clone, +local `main` at `63810dd` gave that finding and exit 1, also with `HEAD` +detached at `837928a`. Local `main` moved to `837928a` gave `plan ok` and +exit 0, also with `HEAD` detached at `63810dd`. + ## Run C — main `837928a` `execute --local-remote-dir … --work-dir … --yes` exited **0** and printed 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 index ed610850..3c832521 100644 --- a/specs/004-migrate-to-triad/root-guidance-2026-10-08.md +++ b/specs/004-migrate-to-triad/root-guidance-2026-10-08.md @@ -3,7 +3,7 @@ **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. +**State**: rehearsal evidence only. No patch here is applied to today's layout, which is untouched. (Corrected 2026-10-09: this line first said "Nothing here lands on `main`", but #192 brought this document and the patch files to main as evidence.) 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 @@ -28,6 +28,34 @@ 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. +**No receipt (added 2026-10-09, #193 item 11).** T006's evidence cites no +private receipt. None is named here, in the five patches, in #192's +description or in T006's comment on #186 (6071824778). brett-wip's +`migration/openRepoTools/`, which holds the migration's receipts, has none +for T006 either (read 2026-10-09). Run C has one, +`adopter-fix-20261008-1HztL0`. + +What the committed patches let anyone reproduce from public inputs alone +(main `837928a` and openRepoShape `7f84ca4`), checked on 2026-10-09: +- the patch SHA256s below; +- the leg trees: `code-0001` and `spec-0001`, applied with `git am` to the + paths the plan gives each leg at `837928a` (trees `3915e78` and + `ad6cdcb`), give exactly `ca65c3b9…` and `10256135…`; +- `assembly-0001`, applied with `git am` to `837928a`'s six root files, + gives section 2's 340-line `AGENTS.md` and 510-line `README.md`. + +The exact commit IDs, the assembly tree `6eb8145e…`, bootstrap and +`make validate` need run C's host-local arrangement. These rows exist only +in this document: +- section 4's doctor and `shape_advisory` rows; +- section 7's `setup-openspeckit`, paired-selection, `check-prerequisites`, + worktree-override and store-precedence rows, and its `openspec` 1.6.0 + exits; +- section 9's protocol hashes. + +T006's #186 comment repeats some of those exit codes from the same work, +which is not separate evidence. + The patches are in [patches/t006/](patches/t006/), as `git format-patch` output: @@ -178,6 +206,7 @@ doctor's row for it is a `note`, which the shape's own agent rules say is - 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. + - *Noted 2026-10-09 (#193 item 9):* that list adds up to 26 mentions in 11 files, not 23 in 9. Section 10 and T006's comment on #186 (6071824778) repeat 23. The counting rule is not recorded, and three plausible recounts here reproduced neither the list nor the total, so which figure is right is not established. - 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`; @@ -192,7 +221,7 @@ doctor's row for it is a `note`, which the shape's own agent rules say is - `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. +- **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. That file has been on main since #189 (`63810dd`), in a later revision than `76dcb93`'s; what is cited here is as read at `76dcb93` (noted 2026-10-09). - **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); @@ -394,8 +423,9 @@ Lane 2 is reading that side. 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). + - 18 test functions read `README.md`, `AGENTS.md`, `CLAUDE.md`, `.gitattributes` or `docs/README-lanes.md` at the **code leg's** root: 17 in `test_repo_hygiene.py`, 1 in `test_openrepotools_command.py` (`test_the_help_and_the_readme_say_the_mode_the_receipt_is_born_at`). *Corrected 2026-10-09 (#193 item 9): this line first said 17, as 16 + 1.* + - Measured on the code leg alone with `-k repo_hygiene`, which selects only `test_repo_hygiene.py`: 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). + - The command test is outside that subset. Called directly on 2026-10-09, it fails before `code-0001` (`FileNotFoundError` on `README.md`) and still fails after it (`AssertionError`: the leg's README has no install-receipt paragraph). - **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. @@ -408,7 +438,7 @@ Lane 2 is reading that side. - `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. +8. **T017.** Feature 004's own working files in the spec leg carry the pre-split path mentions listed in section 5: 23 by its stated total, 26 in 11 files by its per-file list, which is not settled (see there). 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 From 4f773b83a001c7caeb57b19110387000e4506cde Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 20:37:28 +0000 Subject: [PATCH 22/42] Record v3 of T007's entry-point patch, which checks the gitlink before it runs any pinned implementation, and the network needs T014 should document. Lane 3's review of the v2 patch (T3) required the remote path to confirm the gitlink at the pin's submodule_path before it fetches or runs the implementation the pin selects. v3 (6acc147 on 69b8924) does that, and git am on a fresh run C clone reproduces its tree 87782af. The runs table rows for the round-2 head and CI are still to be added. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../installer-2026-10-08.md | 27 ++++-- .../patches/t007-assembly-entry-point.patch | 91 +++++++++++++++---- 2 files changed, 90 insertions(+), 28 deletions(-) diff --git a/specs/004-migrate-to-triad/installer-2026-10-08.md b/specs/004-migrate-to-triad/installer-2026-10-08.md index 03e6b60e..9ccebd2d 100644 --- a/specs/004-migrate-to-triad/installer-2026-10-08.md +++ b/specs/004-migrate-to-triad/installer-2026-10-08.md @@ -59,18 +59,22 @@ Each key that is read must appear once. The file must be LF-only, quotes must cl ## T007: the assembly entry point -- Commit `8ed83b709a616a900601bf67d57c78f6d1525880` sits on `69b8924` in the writer's clone, on branch `t007-assembly-entry-point-v2`. - - Tree: `79db0a40c0148a4649af49d3fd91aecaf33b7ceb`. - - `openRepoTools`: blob `4c2525d9b01efd5d593073c9f1b3e1cfbdb90756`, mode 100755, sha256 `3b0d59f5cc2701edc9cdf088efeb34d476a4fefc456f7b1b5413f364ee32c5da`. - - The patch file's sha256 is `361c037d09ee55996a4cac6f52c2374fdb801bfa2abf781c409b40e9aa0a277d`. - - `git am` of the patch on a fresh clone of run C's split reproduces tree `79db0a4` exactly. - - It supersedes `a02a45c` (branch `t007-assembly-entry-point`, tree `a9fa46e`). The two differ only in `recorded_gitlink`, which now reads the gitlink with `read` instead of `awk` (see the runs below). +- Commit `6acc147f751e5a2ca57b4814395769b7b75b339e` sits on `69b8924` in the writer's clone, on branch `t007-assembly-entry-point-v3`. + - Tree: `87782af2ff3075284682f02fa41763d5df7112ff`. + - `openRepoTools`: blob `ff432c6dc189ce272e778a89e375076094737b48`, mode 100755, sha256 `8248224d3bb425f69a944a41cdfe0a68f0b449d13978f67c76b6bc5a0a73a773`. + - The patch file's sha256 is `67d8e7ce8b0f7008d2151a44c0eabaf74cda7cd2697c6e09ee7da0c341c671b5`. + - `git am` of the patch on a fresh clone of run C's split reproduces tree `87782af` exactly. + - History: + - v1, `a02a45c` (tree `a9fa46e`): the first version. + - v2, `8ed83b7` (tree `79db0a4`): `recorded_gitlink` reads the gitlink with `read` instead of `awk` (see the runs below). + - v3, this commit: on the remote path, the gitlink is now checked against the pin BEFORE any implementation is fetched or run. This answers lane 3's review of the v2 patch (#191 review 5471121210, T3), which found a stale or rolled-back pin running whichever installer it selected, with a misleading refusal. - **In a checkout,** it requires the pin (strict grammar), the gitlink (index first, then HEAD) and `code/openRepoTools`'s raw bytes (`hash-object --no-filters`) to name one commit. Then it `exec`s `code/openRepoTools` with every argument. That is the same process, so the exit status, signals and the implementation's own temporary-file cleanup are the implementation's. There are no temporaries of its own. - **Anywhere else** (stdin, or a copy of the file on its own), it does four things: - it resolves `OPENREPOTOOLS_REF` once, `gh` first and `curl` second; - it reads `contracts/code-pin.yaml` at that commit; + - it reads the gitlink at the pin's `submodule_path` in that commit (one level of the tree, by `gh api --jq`, or by `curl` and `jq`). It refuses, naming both commits and with "nothing ran", unless the gitlink is the pinned commit; - it fetches `openRepoTools` from the pin's `source_repository` at the pinned commit (the file must begin `#!`); - - it runs it from stdin, as the one-liners always ran it, with `OPENREPOTOOLS_REF` set to the resolved commit, so the implementation reads the same pin and re-checks the gitlink itself. + - it runs it from stdin, as the one-liners always ran it, with `OPENREPOTOOLS_REF` set to the resolved commit, so the implementation reads the same pin and checks it again. - **It has no installer mechanics.** It does no placement, receipt, merge or retirement. - **It assumes nothing the legs may not carry.** That covers `.gitattributes`, README, AGENTS and LICENSE (T006 §10.2). - **It is bash 3.2-clean.** Arguments are forwarded as `${1+"$@"}`, because a bare `"$@"` under `set -u` is unbound in 3.2. @@ -81,7 +85,8 @@ Each key that is read must appear once. The file must be LF-only, quotes must cl - Openrepotools' PR #191 was applied to code `73b04d6` in a clone. It was pushed only to the writer's own bare copy of the code remote, never to run C. - The standard's `scripts/bump-leg.py` (tool `7f84ca4`, with `--local-remote-dir` pointing at that copy) made the lockstep commits. Its validators printed `pins ok` and `manifest ok` and recomputed both digests: - on `t007-assembly-entry-point`, `9321107` moved the code pin to `ecb4ef3` (PR at `e406e0e`), and `58e3ac5` moved it to `d12345b` (PR at `ecd7aa4`); - - on `t007-assembly-entry-point-v2`, `7bbd60b` moved it to `ea17f30` (PR at `2411f40`). + - on `t007-assembly-entry-point-v2`, `7bbd60b` moved it to `ea17f30` (PR at `2411f40`), then `e5a901e` and `316aed6` to the round-1 heads; + - on `t007-assembly-entry-point-v3`, `4c6a0b7` moved it to `fa460f2`. That commit is the whole code leg as the PR head `6568f08` has it, merged with main `f87fd5a`. It includes #190's test roots, and every one of the leg's paths is the PR head's blob. - Each bump printed bump-leg's `NEXT … push -u origin …` line. It was deliberately not followed. - The clone's push URL is disabled. @@ -89,7 +94,7 @@ Each key that is read must appear once. The file must be LF-only, quotes must cl | Command line | What it resolves | | --- | --- | -| `curl -fsSL https://raw.githubusercontent.com/opensoft/openRepoTools/main/openRepoTools \| bash -s -- --install` (README:228; openRepoShape's `--install` pointer, openRepoShape:280 at `7f84ca4`, byte for byte) | 1. The root entry point at `main`.
2. `main` resolved once to assembly commit *A*.
3. `contracts/code-pin.yaml` at *A*.
4. `openRepoTools` from `source_repository` at `commit`.
5. That implementation, with `OPENREPOTOOLS_REF=A`, resolves *A* (a commit, which cannot move), lists *A*'s tree, re-reads and re-checks the pin, checks the gitlink, confirms the commit exists in `source_repository`, fetches the 23 payload files from there, and places 29 files and 2 hook entries. | +| `curl -fsSL https://raw.githubusercontent.com/opensoft/openRepoTools/main/openRepoTools \| bash -s -- --install` (README:228; openRepoShape's `--install` pointer, openRepoShape:280 at `7f84ca4`, byte for byte) | 1. The root entry point at `main`.
2. `main` resolved once to assembly commit *A*.
3. `contracts/code-pin.yaml` at *A*.
4. The gitlink at `submodule_path` in *A* must be `commit`, or nothing runs.
5. `openRepoTools` from `source_repository` at `commit`.
6. That implementation, with `OPENREPOTOOLS_REF=A`, resolves *A* (a commit, which cannot move), lists *A*'s tree, re-reads and re-checks the pin, checks the gitlink, confirms the commit exists in `source_repository`, fetches the 23 payload files from there, and places 29 files and 2 hook entries. | | `gh api repos/opensoft/openRepoTools/contents/openRepoTools -H 'Accept: application/vnd.github.raw' \| bash -s -- --install` (README:236–237) | The same, through `gh`. | | `./openRepoTools --install` in an assembly checkout | Checks that pin, gitlink and implementation bytes agree, then `exec`s `code/openRepoTools`. The implementation checks every payload file against the pinned commit and copies offline. | | `openRepoTools --install`, installed in `~/.local/bin` | The implementation alone. Its default `REPO` is the assembly, which it resolves itself. It never needs the root file or a checkout again. | @@ -179,7 +184,9 @@ workBenches-side: the sentinel REPO/REF partial row is covered (`test_r02_…byt - **T009:** `test_the_installer_reaches_only_this_repository` holds because every fetching line names `$REPO`. Its docstring should say that the one other repository reached is the one `$REPO`'s own validated pin names. The composed hygiene results above are T006 §10.6's front-door findings. - **T010:** a composed run whose code leg is checked out at a commit other than its pin will see `--install` from the mounted leg refuse (changed payload). Run install tests from the pinned state or from a standalone copy. -- **T014:** `venv_note`'s `docs/README-lanes.md` comment. +- **T014:** + - `venv_note`'s `docs/README-lanes.md` comment. + - The README's one-liners need a sentence on network needs after the cutover, next to the one-liner (lane 3's T4 on the v2 patch). Today's single repository needs raw.githubusercontent.com only, or `gh`. After the cutover, the curl one-liner needs api.github.com and `jq`. By reading v3 and the PR head, that is 6 api.github.com requests per install: the root makes 2 (`commits`, `git/trees`), and the implementation makes 4 (`commits`, two `git/trees`, the pinned commit). Lane 3 measured 5 on v2, which had no root tree read. So a gh-less machine that is rate-limited (60 an hour per address, unauthenticated) cannot install. - **T015:** validate at the real assembled head, with a code pin that carries this change, against the real GitHub. That includes the one-liners and `bump-leg.py`'s lockstep, and the 3.2 parse of the root entry point (`parse-macos` covers the implementation only). ## What this does not do diff --git a/specs/004-migrate-to-triad/patches/t007-assembly-entry-point.patch b/specs/004-migrate-to-triad/patches/t007-assembly-entry-point.patch index 1ea69a70..407741b3 100644 --- a/specs/004-migrate-to-triad/patches/t007-assembly-entry-point.patch +++ b/specs/004-migrate-to-triad/patches/t007-assembly-entry-point.patch @@ -1,35 +1,43 @@ -From 8ed83b709a616a900601bf67d57c78f6d1525880 Mon Sep 17 00:00:00 2001 +From 6acc147f751e5a2ca57b4814395769b7b75b339e Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> -Date: Fri, 9 Oct 2026 01:02:51 +0000 +Date: Fri, 9 Oct 2026 20:33:03 +0000 Subject: [PATCH] Add the assembly's openRepoTools front door, which runs the implementation the code pin names and nothing else. T007 of specs/004-migrate-to-triad/tasks.md (opensoft/openRepoTools#186). The documented one-liners fetch openRepoTools from this repository's root, and after the split the installer is the code leg's. This file keeps that line -working and adds no installer mechanics: in a checkout it requires the pin, -the gitlink and the implementation's bytes to name one commit and then execs -code/openRepoTools with every argument (same process, so exit status and -signals are the implementation's); anywhere else it resolves the ref once, -reads contracts/code-pin.yaml at that commit, fetches openRepoTools from the -pinned repository at the pinned commit, and runs it from stdin with -OPENREPOTOOLS_REF set to that commit, so the implementation reads the same pin. +working and adds no installer mechanics. + +In a checkout it requires the pin, the gitlink and the implementation's +bytes to name one commit, and then execs code/openRepoTools with every +argument. That is the same process, so the exit status and signals are the +implementation's. + +Anywhere else it resolves the ref once and reads contracts/code-pin.yaml at +that commit. It checks that the gitlink at the pin's submodule_path in that +commit is the pinned commit, so the lockstep rule is enforced here and not +left to whichever code a stale pin selects. Only then does it fetch +openRepoTools from the pinned repository at the pinned commit, and run it +from stdin with OPENREPOTOOLS_REF set to that commit, so the implementation +reads the same pin. + The pin reader is a deliberate copy of the implementation's, guarded by the code leg's tests/test_install_payload_source.py. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- - openRepoTools | 351 ++++++++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 351 insertions(+) + openRepoTools | 398 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 398 insertions(+) create mode 100755 openRepoTools diff --git a/openRepoTools b/openRepoTools new file mode 100755 -index 0000000..4c2525d +index 0000000..ff432c6 --- /dev/null +++ b/openRepoTools -@@ -0,0 +1,351 @@ +@@ -0,0 +1,398 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: Apache-2.0 +# @@ -58,11 +66,16 @@ index 0000000..4c2525d +# of the payload against the same pin before it copies a byte. +# * ANYWHERE ELSE (stdin, or a copy of this file on its own): `$OPENREPOTOOLS_REF` +# is resolved ONCE to a commit of `$OPENREPOTOOLS_REPO`, the code pin is -+# read AT that commit, the implementation is fetched from the pinned -+# repository AT the pinned commit, and it is run from stdin — the way the -+# one-liners always ran it — with `$OPENREPOTOOLS_REF` set to that commit, -+# so the implementation reads the same pin and no branch that moves in -+# between can give it another. It verifies the gitlink itself. ++# read AT that commit, and the gitlink at its `submodule_path` in that ++# commit must name the pinned commit too — the lockstep rule, checked HERE, ++# before any implementation is fetched or run, so a stale or rolled-back pin ++# is named as what it is rather than left to whichever code it selects. ++# Then the implementation is fetched from the pinned repository AT the ++# pinned commit and run from stdin — the way the one-liners always ran it — ++# with `$OPENREPOTOOLS_REF` set to that commit, so the implementation reads ++# the same pin and no branch that moves in between can give it another. ++# Reading the gitlink is one level of the assembly's tree: `gh api --jq`, ++# or `curl` with `jq` (an adopted assembly's one-liner needs api.github.com). +# +# NOTHING HERE ASSUMES A FILE THE LEGS MAY NOT CARRY: no `.gitattributes`, no +# README, no AGENTS.md. Bytes are compared raw (`hash-object --no-filters`). @@ -314,6 +327,39 @@ index 0000000..4c2525d + return 1 +} + ++#: THE GITLINK `$REPO` RECORDS AT IN , from one level of its tree: ++#: `gh --jq` first, `curl` + `jq` second. 0 with GITLINK — empty when the tree ++#: holds no gitlink there, which is an answer — or 1 with WHY when neither ++#: transport answered. is a pin's `submodule_path`, which `pin_parse` ++#: has already held to one segment of letters, digits, `.`, `_` and `-`. ++gitlink_at() { # ++ local filter=".tree[] | select(.type == \"commit\" and .path == \"$2\") | .sha" ++ GITLINK="" WHY="" ++ if command -v gh >/dev/null 2>&1; then ++ if gh api "repos/$REPO/git/trees/$1" --jq "$filter" >"$TMP/link" 2>"$TMP/err"; then ++ IFS= read -r GITLINK <"$TMP/link" || : ++ return 0 ++ fi ++ WHY="$(said gh "$TMP/err")" ++ else ++ WHY="gh: not installed" ++ fi ++ if ! command -v curl >/dev/null 2>&1; then ++ WHY="$WHY; curl: not installed" ++ elif ! command -v jq >/dev/null 2>&1; then ++ WHY="$WHY; curl: not asked, because reading api.github.com's JSON needs \`jq\` and there is none" ++ elif ! curl -fsSL "https://api.github.com/repos/$REPO/git/trees/$1" >"$TMP/tree.json" 2>"$TMP/err"; then ++ WHY="$WHY; $(said curl "$TMP/err")" ++ elif jq -er '.tree | arrays | length >= 0' "$TMP/tree.json" >/dev/null 2>&1 && ++ jq -r "$filter" "$TMP/tree.json" >"$TMP/link" 2>/dev/null; then ++ IFS= read -r GITLINK <"$TMP/link" || : ++ return 0 ++ else ++ WHY="$WHY; curl: an answer that is not a tree listing" ++ fi ++ return 1 ++} ++ +delegate_remote() { # + local sha="" ref="" impl + is_slug "$REPO" || die "\$OPENREPOTOOLS_REPO is '$REPO', which is not an owner/name repository; nothing ran." @@ -357,6 +403,15 @@ index 0000000..4c2525d + pin_parse "$TMP/pin" || die "$PIN_FILE in $REPO at $sha ($REF) is not a code pin this can + follow: $PIN_WHY. A pin that is present and wrong is refused rather than read + around; nothing ran." ++ # THE LOCKSTEP RULE, BEFORE ANYTHING IS FETCHED TO RUN: the gitlink at the ++ # pinned path in this same commit names the pinned commit, or nothing runs. ++ gitlink_at "$sha" "$PIN_SUBMODULE_PATH" || die "could not list $REPO at $sha ($REF), so the gitlink its $PIN_FILE ++ must agree with cannot be read, and nothing ran. What answered: ++ $WHY" ++ [ "$GITLINK" = "$PIN_COMMIT" ] || die "$REPO at $sha ($REF) records ${GITLINK:-no gitlink} at $PIN_SUBMODULE_PATH and ++ its $PIN_FILE says $PIN_COMMIT. The gitlink and the pin move in ONE commit ++ (the lockstep rule), so this assembly does not say which code it means, and ++ nothing ran." + fetch_at "$PIN_SOURCE_REPOSITORY" "$PIN_COMMIT" openRepoTools "$TMP/impl" || + die "could not fetch openRepoTools from $PIN_SOURCE_REPOSITORY at $PIN_COMMIT, the + implementation $REPO at $sha ($REF) pins, so nothing ran. What answered: From c1283b50f906cd8c6444ef4e8f5edac5a461b92f Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 20:38:20 +0000 Subject: [PATCH 23/42] Refresh the T011 record's index, inventory and Gate B list to origin at f87fd5a, and correct what lane 3's review and lane 2's receipt check found Answers lane 3's review of fa44d75 (#186 comment 6082139424) and folds in lane 2's verification of receipt rollback-20261009-ILvoVC (#186 comment 6082370012). Only specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md changes. Every refreshed passage says Refreshed or Corrected and keeps the 01:1xZ text it qualifies. - F11: no stage changed a hook, so the hook rollback is vacuous. The Gate A backup holds no settings.json, and RESTORE.md's copy-back restores only installed/. 849592b's subject overclaims hooks. - F15 and F18: sections 4 to 7 re-read origin at 20:29Z-20:37Z, with main at f87fd5a after #192 and #190 landed. They now record T007's entry point on the installer branch at 51993a4, the candidate at 935f53f, every review since 01:13Z, and an inventory that maps each named follow-up to a patch, a PR, an owner or a deferral. - F4-F9, F12-F14, F17, F19, F20, F22 and lane 2's corrections: - the restored bundle's identity, and the 90 checks by category against #189's 97; - the two near-tautological check families, and main's 70 files plus one symlink; - "live" meaning the shared checkout at 00:44:08Z; - 10 installs, not 12, and no placed file added or retired; - 076062af and the final snapshot; - T010 trees with no earlier record; - GIT_OPTIONAL_LOCKS, the copied capture block, and nested repositories rather than caches. Part of #186. Lane: openRepoTools-2 Co-Authored-By: Claude Opus 5.5 --- .../gate-ab-evidence-2026-10-09.md | 320 +++++++++++++++++- 1 file changed, 303 insertions(+), 17 deletions(-) diff --git a/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md b/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md index e3b3f337..5d7717bc 100644 --- a/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md +++ b/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md @@ -3,10 +3,24 @@ **Task:** T011 of [tasks.md](tasks.md), under [#186](https://github.com/opensoft/openRepoTools/issues/186). No task is checked complete by this record, and it approves nothing. **Writer:** lane openRepoTools-1, 2026-10-09T00:36Z–01:1xZ. +**Refreshed:** lane openRepoTools-2, 2026-10-09T18:51Z–20:4xZ, on branch +`004-migrate-to-triad-rollback-refresh` (from `fa44d75`). It answers lane 3's review of +`fa44d75` (#186 comment 6082139424, whose findings are cited here as F1–F22) and folds in +lane 2's verification of the receipt (#186 comment 6082370012). Each refreshed passage +starts with "Refreshed" or "Corrected"; the 01:1xZ text it qualifies stays as written +unless a finding showed it wrong. Origin was read at 2026-10-09T20:29:34Z–20:30:29Z, with +main at `f87fd5a`, and again before the push (section 8). **Evidence branch:** `004-migrate-to-triad-rollback` (from main `837928a`). -**Private receipt ID:** `rollback-20261009-ILvoVC` (brett-wip `migration/openRepoTools/`, commit -`56f1e7c4`, 424 files under one `SHA256SUMS`), holding every -script, log, snapshot and digest named below. Machine paths stay there. +**Private receipt ID:** `rollback-20261009-ILvoVC` (brett-wip `migration/openRepoTools/`), +holding every script, log, snapshot and digest named below. Machine paths stay there. It +has two commits (F17): +- `56f1e7c4` (00:59:32Z): 424 files, 423 of them listed in one `SHA256SUMS`; +- `076062af` (01:14:30Z): 18 more files, the final real-surface snapshot (`state/final/`, + 01:13:46Z; section 3) and the two hygiene logs (section 8), with `README.md` and + `SHA256SUMS` updated: 442 files, 441 listed. + +Refreshed: brett-wip `ea6c09d1` holds the receipt exactly as `076062af` left it, and +`sha256sum -c SHA256SUMS` gives 441 OK. T011 asks for three things. First, a rehearsal of restoring the local candidate and the installed payload from the preserved artifacts. Second, proof that user work, jobs and claims @@ -17,7 +31,13 @@ inventory. Section 7 is what Gate B still lacks. **Short answer.** - **Source restore:** passes, 90 checks of 90. -- **Installed payload:** restores to the main install exactly by either path. +- **Installed payload:** the 29 placed files restore to the main install exactly by either + path; only the reinstall (c2) also restores the receipt. Refreshed (F13): the target is + main's install, not the home before any install, because the first install rewrites + `settings.json` at mode 600 and both rollbacks keep that. +- **Hooks and settings: not rehearsed** (F11). No stage changed a hook entry or + `settings.json`, so section 2's hook rows test nothing about hooks, and the Gate A + backup holds no `settings.json` for `RESTORE.md`'s copy-back to restore. - **What the rehearsals wrote:** only private scratch. Nothing in a real repository, the real home or the register. - **Gate B is still open.** Section 7 lists what is missing. @@ -28,7 +48,29 @@ The source is the Gate A backup `prep-20261007T100559Z` (host-local, read-only). rehearsal followed its `RESTORE.md` commands, with only the placeholder paths replaced by private scratch. **90 checks passed, 0 failed.** -- The manifest (414 entries) passes `sha256sum -c`. +Refreshed (F5): how the 90 add up, from receipt `logs/10-restore.log`. +- 22 single checks: the manifest 1; `RESTORE.md`'s mirror clone, working clone, fetch and + `set-url` 4; `bundle verify` inside the clone 1; refs 3 (each restore against the + capture, and the capture against the shared checkout today); `fsck` 2; the object set 1; + `RESTORE.md`'s worktree procedure on `main` 6; the dependency bundle 4. +- 68 tree checks: 4 for each of the 17 captured trees. + +The 97 of #189 (`preservation-2026-10-07.md:89`; receipt `prep-20261007T100559Z`, +`verify.log`) were 12 single checks plus 5 for each tree (85). +- This run did not repeat 18 of them: the status-and-lists comparison for the 16 trees + other than `main` (October 7 made it for all 17, `preservation-2026-10-07.md:95–98`; + this run made it for `main` only, below), the bundle's `HEAD` against the main + checkout's, and the bundle's 16 per-worktree `HEAD` refs. +- It added 11: the manifest, the mirror clone, `set-url`, `bundle verify`, the capture + against the shared checkout today, the second `fsck`, and five steps of the worktree + procedure on `main`. +- 97 − 18 + 11 = 90. + +- The manifest (414 entries) passes `sha256sum -c`. Refreshed (F4): it lists the two + bundles restored here, source `60db3acd…` and dependency `aa5a6eca…`, which are the + digests #189 published (`preservation-2026-10-07.md:49–50, 54`), and its own digest is + #189's `2a46dff9…` (`:59`). So the bundle restored here is the one #189 recorded. Both + bundle files still hash to those values (re-hashed 2026-10-09T20:35:05Z). - **`RESTORE.md`'s `git bundle verify`, run from the backup directory as written, exits 1:** `error: need a repository to verify a bundle`. Run inside the restored clone, it exits 0. This is a defect in `RESTORE.md`'s text. It is recorded here; the backup is read-only and @@ -39,7 +81,10 @@ private scratch. **90 checks passed, 0 failed.** exits 0 for both. - The 2902 objects reachable from the 50 bundled tips are identical. The sorted-list SHA256 is `338cb7bdd397076eeced1de0852eeb1510b4f97e02fb3f62de434531c7be327d`. -- **Against the live repository today, two days on**, the 33 captured refs are: +- **Against the live repository today, two days on**, the 33 captured refs are as below. + Corrected: "live" here is the shared checkout's own `for-each-ref`, read at 00:44:08Z + (`10-restore.sh:58`, `logs/10-restore.log:1`), not GitHub. #189 had merged on GitHub as + `63810dd` at 00:44:03Z, and that checkout's `main` still read `837928a`. - 26 identical; - 7 moved forward only: `main`, `origin/main` and `origin/HEAD` to `837928a`, the three October 7 evidence branches, and lane 3's `feat/triad-lane-tooling`; @@ -47,19 +92,40 @@ private scratch. **90 checks passed, 0 failed.** - **0 moved by anything other than a fast-forward**. 18 refs were added since the capture. + + Refreshed 2026-10-09T20:29:34Z, against GitHub (`git ls-remote`; a local branch and its + remote-tracking ref are read as the GitHub branch of that name, and `pr/N` as + `refs/pull/N/head`): 15 identical, 9 moved forward only, 9 with no GitHub ref of that + name, and **0 moved by anything other than a fast-forward**. Each of those 9 commits is + still contained in a GitHub ref; for example `e43b243` is in `refs/pull/188/head`. - **All 17 captured trees:** - each HEAD resolves in the restore; - each tree ID equals the live one; - every index object is present; - the index a checkout of each HEAD makes equals the captured `index.ls-files-s` byte for byte (17 of 17). + + Refreshed (F6): two of these four prove little on their own. + - "Tree ID equals the live one" compares the tree of the same commit ID, read in the + restore and in the shared checkout (`10-restore.sh:94–95`). A commit ID fixes its + tree ID, and `fsck` already covers the objects. + - All 17 captures were clean (`preservation-2026-10-07.md:69–85`, Dirty 0), so each + captured index is its HEAD's tree. The byte-equal index proves that the objects are + present, not that index state survives a restore. The dirty-tree stand-in below is + the evidence on index state. - **One tree by `RESTORE.md`'s own procedure**: `main`, the capture with workflow files. - The steps: `worktree add --detach`, copy the captured files back, restore the captured exclude file. - Its index (100 entries) equals the capture. - - All 70 copied files match `files.sha256`. + - All 70 copied files match `files.sha256`. Refreshed (F7): these are the 70 regular + files. With one symlink they are the 71 workflow entries #189 counts for `main` + (`preservation-2026-10-07.md:61`; receipt `prep-20261007T100559Z`, + `file-lists/main.classified.tsv`: 70 `workflow-copied-verified`, 1 + `workflow-copied-symlink-verified`), and `files.sha256` lists regular files only. 71 + ignored files are restored (`logs/10-restore.log:160`). - Its tracked status and its untracked and ignored lists equal the capture's, less the 4 - cache entries the capture lists as not copied. + cache entries the capture lists as not copied. Corrected: those 4 are nested + repositories (`nested-repository-not-copied` in the same file), not caches. - Both dependency commits resolve from the dependency bundle. Each one's own `tree_digest` gives the recorded value: `39d5c98` → `a44c0165…`, `7f84ca4` → `3be52767…`. @@ -79,9 +145,15 @@ as a real restore would. | Capture | Index equal | Status equal | Every file equal (100) | | --- | --- | --- | --- | -| A: the per-worktree block of the backup's own `capture.sh`, verbatim | **no**: 3 staged blobs are in no bundle | **no** | **no** | +| A: the per-worktree block of the backup's own `capture.sh`, verbatim (corrected below: a copy) | **no**: 3 staged blobs are in no bundle | **no** | **no** | | B: A plus `git diff --cached --binary`, `git diff --binary` and every untracked file | yes | yes | yes | +Corrected (lane 2's verification, #186 comment 6082370012): the receipt's copy of that +block (`11-dirty.sh:42–66`) is not verbatim. Against the backup's `capture.sh:68–92` it +drops the info-exclude copy (`:75–77`) and the `skipped-secret-like.tsv` append (`:82`). +Lane 2 ran the backup's own block, verbatim, on the same stand-in: 0 of 3 again, with the +same 3 staged blobs in no bundle. + **Finding for Gate A.** The October 7 capture takes three things from a tree: its index *listing*, its ignored workflow configuration, and its HEAD. It takes none of its working state. That was enough only because all 17 trees were clean. For any tree that is not clean, @@ -121,7 +193,8 @@ Notes on the sources: only `specs/` and `openspec/`, so its payload is byte-identical to `837928a`'s. - **(b):** T007's assembly root entry point was **not** used. No `004-migrate-to-triad-installer` branch is on origin, so (b) installed through - `code/openRepoTools`. + `code/openRepoTools`. Refreshed (F15): that branch has been on origin since 01:21:15Z, + at `51993a4`, 7 min 40 s after this branch's last push (section 4). - **(b″):** it is not the T007 candidate. It is used only because a rollback is shown best where the payload differs. @@ -135,12 +208,34 @@ Every install exited 0, printed `17 of 17 placed` and wrote 29 receipt rows. The home merged the 2 hook entries; every later one printed `already installed … (unchanged)` for both. +**Refreshed (F11): so no stage changed a hook, and the hook rollback is vacuous.** +- In all four homes the two hook entries and the canonical `settings.json` are identical at + (a), at (b) or (b″), and at (c) (receipt `state/homes/H1`–`H4`, `hooks.ours` and + `settings.canon`). Every candidate here writes the same two entries main writes. +- So the row "(c) the two hook entries: same" below shows that nothing moved, not that a + moved hook comes back. Commit `849592b`'s subject, "both rollbacks return every placed + file, mode and hook to main's install", holds for hooks only in that sense. +- (c1) cannot roll back a hook or a setting at all. The Gate A backup's `installed/` holds + the 29 placed files and no `settings.json`; no file in the backup is a `settings.json`; + and `RESTORE.md:45` copies back `installed/` only. The backup does hold the 29-row + `installed.tsv` of 2026-10-07T04:26:26Z at its top level, which `RESTORE.md` does not + copy back (F14; section 7, item 6). +- No candidate measured to date changes a hook or `settings.json`: + - (b) and (b″) here; + - PR #191 at `13cc752`: `settings.json` and both hook entries are byte-identical to + main's install (lane 3's delta review 5471121210, finding 2); + - the composed candidate at `935f53f`: `settings.json` is byte-identical (lane 3's + review, #186 comment 6082068576, finding 4). + + A candidate that does change one would need this rollback rehearsed, by (c2), with a + payload whose hook entry differs. + | Compared with (a) | H1 (b)→(c1) | H2 (b)→(c2) | H3 (b″)→(c1) | H4 (b″)→(c2) | | --- | --- | --- | --- | --- | | (b) placed files (path, mode, sha256) | same | same | **1 differs** | **1 differs** | | (c) placed files (path, mode, sha256), 29 | same | same | same | same | | (c) receipt rows (name, destination, sha256) | same | same | **1 stale row** | same | -| (c) the two hook entries | same | same | same | same | +| (c) the two hook entries (never changed; see above) | same | same | same | same | | (c) `settings.json`, canonical; its mode | same; same | same; same | same; same | same; same | | unrelated settings after every stage = the seed | yes | yes | yes | yes | | foreign command and skills after every stage | untouched | untouched | untouched | untouched | @@ -171,6 +266,14 @@ candidate install and a rollback. H3/H4 show a real byte rollback with PR #191's The real T014/T015 candidate, with #191 and the T007 entry point, must repeat (c2) at its own exact commits. +Refreshed (F13): two more things it cannot show. +- **No stage adds or retires a placed file.** All 12 snapshots after the seed, in the four + homes, place the same 29 paths (receipt `state/homes/*/*/placed.tsv`). So two hazards are + argued here, not run: (c1) leaving behind a file a candidate added, and the stale receipt + misleading a later `--install` that retires a file (above). +- **The rollback target is main's install, not the home before it.** The first install + rewrites `settings.json` at mode 600, and neither rollback returns it to the seed's 644. + **Run 1, kept as evidence of all-or-none.** The first seed's unrelated `SessionStart` command contained the word `session-start`. Every install refused it as a second writer of that hook: 10 runs across main, the candidate, #191 and the restored source, exit 2, @@ -195,7 +298,9 @@ The groups are: - the dirty stand-in and its two rebuilds; - the install sources; - the seeds; -- 12 installs and 10 refused ones; +- 10 installs and 10 refused ones. Corrected (F12): this said 12. The receipt's + `README.md:35` and `mutations.tsv:15` say 12, but `logs/22-installs.log` has 10 + `--install` runs and the 2 copy-backs of the next line, 12 `exit=0` lines in all; - two copy-backs, snapshots and comparisons; - the patch-application clones. @@ -205,17 +310,36 @@ in the receipt). The one `reset --hard` is in a scratch clone of the rehearsal t patched. The live repository was only read, with `GIT_OPTIONAL_LOCKS=0`: `for-each-ref`, `rev-parse`, `cat-file`, `merge-base`, `ls-tree`, `show`. +Corrected (lane 2's verification, F22): the rehearsal scripts only read the live +repository, but not all of them with `GIT_OPTIONAL_LOCKS=0`. `10-restore.sh` (`:7`) and +`snapshot-real.sh` (`:5`) set it; `snapshot-real.sh:15` also runs `worktree list`, which +the list above omits. `24-patches.sh` runs `ls-tree` and `show` there (`:14–15`) without +it (`:7`). The four fetches under "Acts outside the rehearsals" did write there: +remote-tracking refs and their objects. + **Real surfaces, before (00:42:08Z) and after (00:53:06Z):** +Refreshed (F17): a third snapshot, final (01:13:46Z, `state/final/`, added by `076062af`), +came after this branch's three pushes (the last at 01:13:35Z) and the first brett-wip +commit. It came before the second brett-wip commit (01:14:30Z), which adds it, and before +the comment on #186 (01:14:55Z). The rows below hold at final too, except where a +correction says otherwise. + | Surface | Result | | --- | --- | | The real home's 29 installed files, its receipt, its `~/.claude/settings.json` | unchanged (sha256 and mode) | | `~/.agents/workspace.yaml` | exists; size, inode, mtime and mode unchanged; its contents were not read | | The Gate A backup | manifest passes both times; whole-tree listing (size, mode, mtime) unchanged | | The run C rehearsal | whole-tree listing (size, mode, mtime) unchanged | -| Lane 1's register row and objects (`lanes --all`, `lane-objects`, both with `LANES_NO_FETCH=1`) | the row unchanged but for its age column; objects gained one line, `LANDED opensoft/openRepoTools#189` (00:49:25Z, the coordinator's) | -| The live repository's 25 worktrees | the same 25 before and after; none locked, prunable or detached | -| The live repository's refs | this writer added `refs/heads/004-migrate-to-triad-rollback` and its remote-tracking ref (fast-forward pushes). Other writers' acts moved the rest: `main` to `63810dd`, the T006 and run C branches, `004-migrate-to-triad-evidence-2`, and the #190/#191 heads. | +| Lane 1's register row and objects (`lanes --all`, `lane-objects`, both with `LANES_NO_FETCH=1`) | the row unchanged but for its age column; objects gained one line, `LANDED opensoft/openRepoTools#189` (00:49:25Z, the coordinator's). Corrected: they also gained two lines from another lane's log that the reader reports as unreadable (its count went from 3 to 5) | +| The live repository's 25 worktrees | the same 25 before and after; none locked, prunable or detached. Corrected: the same 25 paths at final, but four HEADs moved forward: the shared checkout's `main` `837928a` → `63810dd`, #191's branch `e406e0e` → `2411f40`, #190's `c139e88` → `e3a76b5`, and this writer's `837928a` → `fa44d75` | +| The live repository's refs | this writer added `refs/heads/004-migrate-to-triad-rollback` and its remote-tracking ref (fast-forward pushes). Other writers' acts moved the rest: `main` to `63810dd`, the T006 and run C branches, `004-migrate-to-triad-evidence-2`, and the #190/#191 heads. Corrected: the branch was already in the baseline at `837928a` (`state/before/live-refs.sorted:27`), made by this writer's `lane-worktrees add`; its commits moved it, and only its remote-tracking ref was new. The remote-tracking `origin/004-migrate-to-triad-evidence` (`3f7c48f`) was pruned before 00:53:06Z; #189's branch is no longer on GitHub. A remote-tracking ref here moves on a push from any worktree of this repository or on a fetch, this writer's four included, so which act moved each one is not recorded (F22) | + +Corrected (lane 2's verification): the receipt's `README.md:65–67` says these final +surfaces "all equal `state/before/`". Byte for byte, three do not, as the rows above now +say: the register row (its age column), the register objects, and the worktree list (four +HEADs). The real home's files, receipt and `settings.json`, `workspace.yaml`'s metadata, +the backup and the rehearsal do. The lane's register clone and brett-wip's shared checkout both moved during the window. Both moves were other actors' commits: the coordinator's LANDING/LANDED lines for #189, and @@ -242,6 +366,14 @@ installer, through (c2). It needs: - no destructive reset; - no pointer rewrite. +Refreshed (F8, F19): this is an argument, not a rehearsal. +- "Every captured ref has only ever moved forward" describes two days of other writers' + work. It shows that nothing has needed a force push so far. +- No revert or pin-bump PR was rehearsed, and neither was a restore of the local + candidate (run C's triad) from preserved artifacts. Section 7 lists both. +- Jobs: no before/after list of running jobs was taken, although the plan preserves + "uncertain jobs" (plan.md:245). The only evidence is that no script kills a process. + **Acts outside the rehearsals.** These are publication and bookkeeping, listed so nothing is hidden: - four fetches in the live repository: three before the baseline, and one after the @@ -275,11 +407,35 @@ Verification marked "T011" was done by this run: checksums and the git identitie | brett-wip `c311a78b` | receipt `adopter-fix-20261007-iM20tv` | runs A and B | **T011:** `sha256sum -c` 58 OK; the six published log digests are in it | | brett-wip `3de0d37c` | receipt `adopter-fix-20261008-1HztL0` | run C | **T011:** `sha256sum -c` 44 OK | | brett-wip `56f1e7c4` | receipt `rollback-20261009-ILvoVC` | T011 | its own `SHA256SUMS` (423 entries) | +| brett-wip `076062af` (refreshed, F17) | the same receipt, plus the final snapshot and two hygiene logs | T011 | its own `SHA256SUMS` (441 entries); lane 2's verification (#186 comment 6082370012) | | host-local | backup `prep-20261007T100559Z`; rehearsal `work-20261008/rehearsal` | the preserved artifacts | **T011**, read-only, before and after | No private receipt exists for T006 or T009/T010. Their logs are host-local only, in the private prep area. +**Refreshed 2026-10-09T20:30Z (F15).** Origin moved after this index was written. Main is +`f87fd5a` (`git ls-remote`, 20:29:34Z): lane 3 landed #192 as `1abee1d` (20:24:59Z) and +then #190 as `f87fd5a` (20:26:03Z), on Brett Heap's word (#186 comment 6088703549). Each +squash equals its reviewed head: `1abee1d`'s tree is `71b05cb`'s, and `f87fd5a`'s diff is +`c4864ac..b50f197`'s. Every other move below is a fast-forward (`git merge-base +--is-ancestor`, each step). + +| Branch or PR, head now | Since 01:13Z | Reviews and verification now | +| --- | --- | --- | +| #192, landed as `1abee1d` (head `71b05cb`; `004-migrate-to-triad-evidence-2` deleted) | run C's records, its plan and T006's `root-guidance-2026-10-08.md` and five patches are on main | lane 3: **LAND** on `71b05cb` (review 5464893559), with an exact rebuild of all five T006 patch commits; lane 2: run C's receipt verified, all six checks pass (#186 comment 6072724043) | +| `004-migrate-to-triad-root-guidance` `041a273`, `004-migrate-to-triad-rehearsal-c` `1a26463` | unchanged; their content landed with #192 | as #192 | +| #190, landed as `f87fd5a` (head `b50f197`; `feat/test-roots-for-the-triad` deleted) | `e3a76b5` → `dbc6837` (01:22:43Z) → `b50f197` (13:59:10Z, the manual guard, #193 item 1) | **LAND** on `dbc6837` from lane 3 (5464931249) and lane 1's independent review (5464938427); **LAND** on the `b50f197` delta (5471080687); CI green on `b50f197`, `tests-macos` included | +| `004-migrate-to-triad-ci` `d28e2cc` | `cc7c7a1` → `b4ef6b5` → `d28e2cc` (02:30:57Z); only `test-roots-and-ci-2026-10-08.md` changed, and both T010 patch blobs are identical at both heads | no review verdict posted; lane 3's reviewer of the T010 patches has resumed (#186 comment 6088703549) | +| #191 `13cc752` | `ecd7aa4` → `2411f40` (01:20:38Z) → `13cc752` (02:42:51Z) | **DO NOT LAND** on `2411f40` from lane 3 (5464963807) and lane 1's independent review (5465121368); **LAND** on `13cc752` from lane 3's delta review (5471121210), on condition that the PR body is corrected before the squash and `tests-macos` runs on `ready`. CI on `13cc752`: every job passed except `tests-macos`, skipped without `ready`. Lane 1's next head, `fdc5f18`, is announced and not on origin (#186 comment 6088714587, item 3) | +| `004-migrate-to-triad-installer` `51993a4` (new, 01:21:15Z; no PR) | T007's entry point, `patches/t007-assembly-entry-point.patch` (sha256 `361c037d…a277d`), and `installer-2026-10-08.md` | lane 3: **CHANGES REQUIRED** on the patch: confirm the gitlink before running the pinned implementation on the remote path (review 5471121210, T3). Lane 1 puts that change on this branch (#186 comment 6088714587, item 3) | +| `004-migrate-to-triad-candidate` `935f53f` (new, 01:58:43Z; no PR) | `candidate-2026-10-09.md`: the composed candidate, built from #190 `dbc6837` and #191 `2411f40` (`:29–30`); receipt `candidate-20261009-JR8YyA` | lane 3: **REPRODUCES**, fit for the composed run (#186 comment 6082068576). Its section 5 records that no suite run had finished. Lane 1's r2 runs finished at 18:20:47Z (lane 2's reading, #186 comment 6087267593); no record of them is on origin. The receipt is not on brett-wip `ea6c09d1` | +| `004-migrate-to-triad-t018` `63bff08` (new, 20:28:03Z; no PR) | lane 1's T018 exercise (#186 comment 6088703549) | — | +| `feat/triad-lane-tooling` `30f9dc0` | `c45a452` → `2aa8a59` → `96da9af` → `30f9dc0` (14:18:02Z) | no PR | +| `feat/test-roots-followups` `01ccc86` | #190's follow-ups (#193 items 2–7); `7144eef` → `01ccc86` (20:31:02Z), a merge of main `f87fd5a` (re-read 20:36:57Z) | lane 3's review has resumed (#186 comment 6088703549) | +| `004-migrate-to-triad-reads` `a7a8e00` | `a6830a1` → `a7a8e00` (13:50:08Z): lane 2's `gate-b-status-2026-10-09.md` | no PR; lane 1's own walk goes into its next evidence PR (#186 comment 6088714587, item 1) | +| brett-wip `99eed35` | Gate A capture tooling v2, which answers this record's three findings (#186 comment 6073036070) | lane 2's final verification: `capture.sh:170` rewrites its source's index, and `restore.sh:132` can aim at `/` (#186 comment 6087261307). Lane 1 accepts both; no capture runs on a real checkout until a fixed version is re-verified (#186 comment 6088714587, item 4) | +| issue #193 (13:42:32Z; updated 20:26:33Z) | the follow-ups deferred at the landing of #190 and #192: item 1 resolved by #190, item 15 added | — | + ## 5. Follow-up patch and commit inventory Every recorded patch for the rehearsed arrangement was re-applied by this run with `git am`, @@ -315,6 +471,54 @@ hashes: the committer differs, so the hashes do. This is a text-level check only. **No test ran on the stacked candidate.** +Corrected (F20; lane 2's verification, #186 comment 6082370012): +- "As recorded" holds for the T006 trees (#1–#3 as a stack, #4, #5). Nothing recorded a + tree for the T010 patches before this run. `test-roots-and-ci-2026-10-08.md` names commit + `386be81` and no tree, and #6's commit `b95d667` appears only in the patch's own `From` + line. So #6's and #7's trees above are this run's first record of them, not a match. +- #7 was re-applied on the split `69b8924`, not on its rehearsed base `6ccdaf8`. +- Commit `849592b`'s body says "the seven recorded patches re-applied onto their rehearsed + bases with matching trees". The two points above are the exceptions. +- The first attempt to fetch the two PR heads ran under `GIT_ALLOW_PROTOCOL=file` and was + refused (`fatal: transport 'ssh' not allowed`, exit 128; receipt + `logs/24-patches.log:21–32`). `25-prs.sh` redid it, and the stack result above is the + redo's (`logs/25-prs.log`). + +**Refreshed 2026-10-09T20:30Z: the follow-up inventory (F15, F18).** +- T007's entry point has been recorded since 01:21:15Z, so "T007's entry point: 0 + recorded" above no longer holds. +- The stack check above used #190 `e3a76b5` and #191 `ecd7aa4`. Two later builds + supersede it. One is the composed candidate at `dbc6837` and `2411f40` + (`candidate-2026-10-09.md` section 2), which lane 3 rebuilt exactly (#186 comment + 6082068576). The other is lane 1's r2 at `b50f197` and `13cc752`, which lane 2 rebuilt + with every `git am --3way` step exiting 0 and the same trees (#186 comment 6087232548, + item 3). + +Gate B asks for "reviewed follow-up patches" (plan.md:140–141). Each named follow-up below +maps to a patch, a branch or PR, an owner, or "deferred". Line numbers in run C's plan and +in `root-guidance-2026-10-08.md` are on main since #192 (`1abee1d`). + +| Follow-up (where named) | Patch, branch or PR | Owner | State at 20:30Z | +| --- | --- | --- | --- | +| T006's five patches (the table above) | `patches/t006/`, on main since #192 | lane 1 (T006) | reviewed: LAND (5464893559). #2, #3 and the bump `6ccdaf8` regenerate at T014 | +| T010's two patches (the table above) | `004-migrate-to-triad-ci` `d28e2cc` | lane 1 (T010) | no review verdict posted; lane 3's reviewer has resumed (#186 comment 6088703549). Both are authored by the fixture identity, to carry a real author at T014 (#186 comment 6082068576, finding 9) | +| T007's assembly entry point | `004-migrate-to-triad-installer` `51993a4` | lane 1 (T007) | CHANGES REQUIRED (5471121210, T3) | +| T007/T008's code side | #191 `13cc752` | lane 1 (T007) | LAND with conditions (5471121210); next head `fdc5f18` announced. Open there: the parent-directory walk (delta 4, which `fdc5f18` answers per lane 1) and the hygiene test passing by spelling (delta 5) | +| #190's manual guard (#193 item 1) | in #190 since `b50f197` | lane 1 (T009) | resolved: landed with #190 | +| #190's other follow-ups (#193 items 2–7) | `feat/test-roots-followups` `01ccc86` (main merged in at 20:31:02Z) | lane 1 (T009) | no PR yet; one against main is now possible (#186 comment 6088703549). Its `AGENTS.md` hunk does not apply to the triad's assembly `AGENTS.md` (#186 comment 6082068576, finding 8) | +| The named-root refusal wording (#193 item 15) | — | lane 1 (T009) | open (#186 comment 6088714587, item 6) | +| T006 × #190, `test_agents_md_names_the_pin_rules` (#193 item 14) | — | lane 1 decides | open; it fails in r2's composed run (#186 comment 6087267593) | +| r2's other failures: `test_r14_the_documented_upstream_skip_is_unchanged` (`KeyError: 'reason'`), the lane suite's row-stamp cases, `test_run_wrapper`'s timeouts | — | lane 1 | being classified by lane 1 (#186 comment 6088714587, item 5) | +| #192's text findings (#193 items 8–13) | lane 2's `004-migrate-to-triad-evidence-2-followups` (#186 comment 6082466833, item 3) | lane 2, for lane 1 | on origin since 20:32:51Z at `096de93`, with main `f87fd5a` merged in; no PR (re-read 20:36:57Z) | +| Gate A capture tooling v2 | brett-wip `99eed35` | lane 1 | to be fixed and re-verified before any real capture (#186 comments 6087261307, 6088714587) | +| Run C's `follow_ups`: 29 entries (`adoption-plan.yaml:392–421`) | the next five rows | | | +| … 13 entries: a shipped command or test names `docs/`, `openspec/` or `specs/` (`:393–405`) | lane 3's `feat/triad-lane-tooling` `30f9dc0`, no PR; #190 (landed) for the suite's roots | `root-guidance-2026-10-08.md:188–194` routes the files it names: `lane-start`, `lanes-edit.sh`, `test_lane_helpers.sh` and the OpenSpec keys in `lanes-edit.sh` and `lane-handoff` to lane 3; `openRepoTools` to T007; `test_repo_hygiene.py`, `test_lane_start_claude_current.py` and `tests/run.sh` to T009/T010; `claude-current` to its text owner | that routing names neither `test_install_skill_and_hook.py` nor `test_park_resume_commands.py`, nor the plan's unnamed ninth `docs/` file: no owner is recorded for them | +| … 5 `shape/` collisions (`:406–410`) | T006's `assembly-0001` (`root-guidance-2026-10-08.md:70–76`) | lane 1 (T006) | merged in the patch; deleting the unpinned `shape/AGENTS.md`, `shape/CLAUDE.md` and `shape/README.md` is deferred to T014's reviewer (`:76`, `:394`) | +| … open the split as a pull request (`:411`) | — | T013 (tasks.md:96–98) | deferred | +| … the entry point (`:413`) and the test roots (`:414`) | the T007 rows above; #190 and the T010 row above | lane 1 | as above | +| … 8 more plan-level items (`:412`, `:415–421`) | `:412` was rehearsed in runs B and C; `:415`, the workflow bootstrap, in T006 | by tasks.md: `:412` and `:415` T013; `:416` T016–T018; `:417` T012; `:418` and `:420` T014; `:419` T016; `:421` T015 | deferred | +| The 33 outside consumers not covered (`consumer-reads-2026-10-07.md:303`) | — | the owners listed there | deferred | + ## 6. Pull requests to main with layout-neutral changes | PR | Head | Files | State at 00:5xZ | @@ -328,6 +532,22 @@ stack cleanly in the order #190 then #191. Neither touches a root front-door fil Lane 3's `feat/triad-lane-tooling` (`c45a452`) is on origin with no PR. +**Refreshed 2026-10-09T20:30Z (F15)** (`gh pr view`, 20:29:45Z; check runs, 20:30:23Z): + +| PR | Head | State | +| --- | --- | --- | +| #190 | `b50f197` | **landed** as `f87fd5a` at 20:26:03Z, after #192. CI on `b50f197` was green, `tests-macos` included. | +| #191 | `13cc752` | open; no `ready` label. On `13cc752` every CI job passed except `tests-macos`, which was skipped. LAND from lane 3's delta review, with conditions (section 4). Next head announced, not pushed. | +| #192 | `71b05cb` | **landed** as `1abee1d` at 20:24:59Z. CI on `71b05cb` was green, `tests-macos` included. | + +#191 is still based on `c4864ac`. It still stacks on #190 in that order: +- lane 2 applied `dbc6837` then `13cc752` cleanly onto the patched code leg (#186 comment + 6082370012, check (v)); +- lane 2's rebuild of lane 1's r2 applied `b50f197` then `13cc752`, every `git am --3way` + step exit 0 (#186 comment 6087232548, item 3). + +Lane 3's `feat/triad-lane-tooling` is at `30f9dc0`, still with no PR. + ## 7. What Gate B still lacks Gate B is "complete local rehearsal evidence, restore rehearsal, reviewed follow-up patches, @@ -342,8 +562,21 @@ resolved mapping and a passing compatibility test matrix" (plan.md). Against tha receipt. `RESTORE.md`'s `git bundle verify` line also needs a repository to run in. + + Refreshed 2026-10-09T20:30Z (F11, F13): the hook and settings rollback is not rehearsed, + and no stage added or retired a placed file (section 2). Gate A's capture tooling v2 + (brett-wip `99eed35`, #186 comment 6073036070) answers the two findings and the + `bundle verify` line. Lane 2's final verification found that its `capture.sh` rewrites + its source's index (#186 comment 6087261307), and no capture runs on a real checkout + until a fixed version is re-verified (#186 comment 6088714587, item 4). 2. **T007's root entry point is not published.** Without it the inventory has no assembly `openRepoTools`. PR #191, the code side, is open. + + Refreshed 2026-10-09T20:30Z (F15): it has been recorded since 01:21:15Z, on + `004-migrate-to-triad-installer` @ `51993a4`. Lane 3 requires one change before it goes + into the root-split PR: confirm the gitlink before running the pinned implementation on + the remote path (review 5471121210, T3). #191 has LAND on `13cc752` with conditions, + and a next head is announced (section 4). 3. **No composed candidate carrying every follow-up has run the suite.** That candidate is T006 + T010 + #190 + #191 + the entry point. It was only checked to apply (section 5). The October 8 proof P1 (285 passed, 2 failed) ran on run C's code leg with #190 alone: @@ -352,15 +585,57 @@ resolved mapping and a passing compatibility test matrix" (plan.md). Against tha Lane 2's run C composed-suite discovery has not reported on #186. The hosted-runner part (Windows, macOS) is blocked until real legs exist (checklist line 35). + + Refreshed 2026-10-09T20:30Z (F15): + - Lane 2's discovery reported at 01:21:53Z (#186 comment 6072319462, with its addendum + 6072333212). + - The composed candidate is recorded on `004-migrate-to-triad-candidate` @ `935f53f`, + and lane 3 rebuilt it exactly (#186 comment 6082068576). + - Lane 1's r2 runs (code with #190 at `b50f197` and #191 at `13cc752`) finished at + 18:20:47Z. In lane 2's reading of the logs (#186 comment 6087267593): composed, 5 + failed and 1411 passed; standalone, 3 failed; without the dependency, 6 failed. + Lane 1 is classifying them (#186 comment 6088714587, item 5). + - **No suite run of the composed candidate passes yet, and none is recorded on origin.** 4. **Reviews.** Lane 3's reviews of #190 and #191 are in progress. No review is recorded for the T006 patch set (#192) or the T010 patches (checklist line 46). + + Refreshed 2026-10-09T20:30Z (F15): + - #190: LAND twice and on its delta, and landed (section 4). + - The T006 patch set: lane 3's #192 review rebuilt all five patch commits exactly (LAND, + 5464893559), and #192 landed. + - #191: LAND on `13cc752` with conditions (5471121210). + - The T007 patch: CHANGES REQUIRED (5471121210). + - The T010 patches: no verdict yet; lane 3's reviewer has resumed (#186 comment + 6088703549). 5. **Mapping.** Main carries run B's plan, source `e43b243`, which survives only as `refs/pull/188/head`. Run C's plan, source `837928a`, is in #192, unlanded. + + Corrected (F9): `e43b243` survives on origin only as `refs/pull/188/head` (`git + ls-remote`, 20:29:34Z). It is also in the Gate A bundle, as the captured branch + `chore/openreposhape-pin-7f84ca4` (`inventory.json` `local_branches`), and that branch + was still in the shared checkout at 01:13:46Z (receipt `state/final/live-refs.sorted`). + Refreshed 2026-10-09T20:30Z: #192 landed, so main now carries run C's plan, source + `837928a`, in place of run B's. 6. **Gate A**, for orientation: - the final capture at an owner-coordinated freeze, taking finding 1; - installed-file receipts for py-bench and other profiles and workstations; - owner dispositions. + Refreshed (F14): T002 asks for "installed receipts/config" (tasks.md:28–29; plan.md:79). + The Gate A backup holds the receipt (`installed.tsv`, 29 rows) but no `settings.json`, + and `RESTORE.md`'s copy-back restores neither (section 2). So the backup cannot restore + the hooks and settings an install writes. +7. **T011's own acceptance (refreshed, F19).** This record checks no task complete. + - Rehearsed: the source restore from the Gate A bundle, and the rollback of the + installed payload's 29 files and receipt. + - Not rehearsed: + - a restore of the local candidate (run C's triad) from preserved artifacts; + - the post-merge rollback the plan names, "reviewed revert/pin-bump PRs and the + preserved installer" (plan.md:243); + - a hook or settings rollback (section 2); + - a before/after list of running jobs (plan.md:245). + - Published: the evidence index and the follow-up inventory, refreshed above. + **Gate C is Brett Heap's explicit approval.** It names the source and standard revisions, the leg repositories, their visibility and the final plan. Nothing here asks for it or infers it. @@ -374,7 +649,7 @@ The logs and snapshots are in the private receipt. | `10-restore.log` (section 1, 90 PASS) | `bb3a5829d3a1e5ea6924331456b9f6b81b4a8380d878617344adf2807e70a848` | | `11-dirty.log` (section 1, A 0/3, B 3/3) | `e6d1448fd18ebe2889b7c564307024ef8e2e49b48e2ef642838c8e0f89d1a9da` | | `21-clones.log` (section 2 sources) | `d4b39b1cabeae6d2395f5db3610157689863af40bb2fc1354a2116ddaf855139` | -| `22-installs.log` (section 2, 12 installs, 2 copy-backs) | `4a7beffda610253de630b389f1be09c07df660df4d06a8745e10ed8d1703f2a6` | +| `22-installs.log` (section 2, 10 installs and 2 copy-backs; corrected from "12 installs") | `4a7beffda610253de630b389f1be09c07df660df4d06a8745e10ed8d1703f2a6` | | `23-compare.log` (section 2 comparisons) | `dca56f959f8075ac0054745965896dd43e4eb9b58cc07409b89d583e0f91e72c` | | `24-patches.log` (section 5) | `f6a385eef8a32d683643aa7637e31cc88900af69888836c7fef7923cfac6de66` | | `25-prs.log` (section 5, the two PRs) | `4ae121cd04df1bc87b796ec2c6969ab355273582f763375b13f9f7adb5787300` | @@ -384,7 +659,8 @@ The logs and snapshots are in the private receipt. | `restore-trees.tsv` (section 1, 17 trees) | `ace098036ebcec7fe47dd56a0bb7f38b97b9b10bc4753c9dd2d652349a57db98` | **This document's own check.** `tests/run.sh -k repo_hygiene` did not run: -- the wrapper waited 5 minutes (00:57–01:02Z) on 7 other repositories' pytest processes; +- the wrapper waited 5 minutes (00:57–01:02Z) on 7 other repositories' pytest processes + (corrected: the log says "7 foreign pytest processes", receipt `logs/31-hygiene-flock.log:1`); - its exact pytest line under `flock` on the workstation lock then queued 10 minutes behind the T007/T008 writer's full suite and was stopped before pytest started. @@ -392,5 +668,15 @@ Two hygiene tests read every tracked file: the host-absolute-path test and the a test. Their own regexes, taken from `tests/test_repo_hygiene.py`, find nothing in this file. CI is the suite of record. +Refreshed (2026-10-09T20:4xZ): on this refreshed file, `git diff --check` exits 0. The same two +regexes, read from `tests/test_repo_hygiene.py` at `fa44d75` (`HOST_ABSOLUTE_PATH` and +`ACT0_CITATION`), find nothing, and neither does a stricter scan for any host-absolute path +prefix. No pytest ran: another lane's openRepoTools suite was live on the workstation. CI +is still the suite of record, and this branch has no PR. Origin was re-read before the +push, at 20:36:57Z (`git ls-remote`). Main `f87fd5a`, #191 `13cc752` and +`004-migrate-to-triad-rollback` `fa44d75` were unchanged since 20:29:34Z. +`feat/test-roots-followups` and `004-migrate-to-triad-evidence-2-followups` had moved, +and sections 4 and 5 say so. + This record authorizes no conversion, repository creation, merge, default-branch move or lane rebinding. From 612ea944fa9b9592f3688dbec42057a1d71a1986 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 22:03:55 +0000 Subject: [PATCH 24/42] Correct three rows of the refreshed T011 record from lane 3's review of c1283b5: the candidate branch's creation sha, the two #190 follow-up branches' creation and first move, and the owner of run C's ninth docs/ file From lane 3's review (#186 comment 6089982753), N1 to N3, each checked at its source: - N1: 004-migrate-to-triad-candidate was created at b21c511 at 01:58:43Z, and b21c511 -> 935f53f was pushed at 02:43:24Z (GitHub activity API). - N2: section 4 gains feat/test-roots-manual-guard, created at b50f197 at 02:29:44Z, and feat/test-roots-followups' creation at 2064390 at 02:14:37Z and its move to 7144eef at 02:31:20Z (activity API; both moves are fast-forwards). - N3: the plan's unnamed ninth docs/ file is tests/test_repo_hygiene.py. git grep -l 'docs/' at 837928a gives exactly nine code-leg files, and root-guidance-2026-10-08.md:191 routes that one to T009/T010. Part of #186. Lane: openRepoTools-2 Co-Authored-By: Claude Opus 5.5 --- specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md b/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md index 5d7717bc..3bc8f030 100644 --- a/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md +++ b/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md @@ -428,10 +428,11 @@ squash equals its reviewed head: `1abee1d`'s tree is `71b05cb`'s, and `f87fd5a`' | `004-migrate-to-triad-ci` `d28e2cc` | `cc7c7a1` → `b4ef6b5` → `d28e2cc` (02:30:57Z); only `test-roots-and-ci-2026-10-08.md` changed, and both T010 patch blobs are identical at both heads | no review verdict posted; lane 3's reviewer of the T010 patches has resumed (#186 comment 6088703549) | | #191 `13cc752` | `ecd7aa4` → `2411f40` (01:20:38Z) → `13cc752` (02:42:51Z) | **DO NOT LAND** on `2411f40` from lane 3 (5464963807) and lane 1's independent review (5465121368); **LAND** on `13cc752` from lane 3's delta review (5471121210), on condition that the PR body is corrected before the squash and `tests-macos` runs on `ready`. CI on `13cc752`: every job passed except `tests-macos`, skipped without `ready`. Lane 1's next head, `fdc5f18`, is announced and not on origin (#186 comment 6088714587, item 3) | | `004-migrate-to-triad-installer` `51993a4` (new, 01:21:15Z; no PR) | T007's entry point, `patches/t007-assembly-entry-point.patch` (sha256 `361c037d…a277d`), and `installer-2026-10-08.md` | lane 3: **CHANGES REQUIRED** on the patch: confirm the gitlink before running the pinned implementation on the remote path (review 5471121210, T3). Lane 1 puts that change on this branch (#186 comment 6088714587, item 3) | -| `004-migrate-to-triad-candidate` `935f53f` (new, 01:58:43Z; no PR) | `candidate-2026-10-09.md`: the composed candidate, built from #190 `dbc6837` and #191 `2411f40` (`:29–30`); receipt `candidate-20261009-JR8YyA` | lane 3: **REPRODUCES**, fit for the composed run (#186 comment 6082068576). Its section 5 records that no suite run had finished. Lane 1's r2 runs finished at 18:20:47Z (lane 2's reading, #186 comment 6087267593); no record of them is on origin. The receipt is not on brett-wip `ea6c09d1` | +| `004-migrate-to-triad-candidate` `935f53f` (new: created at `b21c511` at 01:58:43Z, then `b21c511` → `935f53f` at 02:43:24Z; no PR) | `candidate-2026-10-09.md`: the composed candidate, built from #190 `dbc6837` and #191 `2411f40` (`:29–30`); receipt `candidate-20261009-JR8YyA` | lane 3: **REPRODUCES**, fit for the composed run (#186 comment 6082068576). Its section 5 records that no suite run had finished. Lane 1's r2 runs finished at 18:20:47Z (lane 2's reading, #186 comment 6087267593); no record of them is on origin. The receipt is not on brett-wip `ea6c09d1` | | `004-migrate-to-triad-t018` `63bff08` (new, 20:28:03Z; no PR) | lane 1's T018 exercise (#186 comment 6088703549) | — | | `feat/triad-lane-tooling` `30f9dc0` | `c45a452` → `2aa8a59` → `96da9af` → `30f9dc0` (14:18:02Z) | no PR | -| `feat/test-roots-followups` `01ccc86` | #190's follow-ups (#193 items 2–7); `7144eef` → `01ccc86` (20:31:02Z), a merge of main `f87fd5a` (re-read 20:36:57Z) | lane 3's review has resumed (#186 comment 6088703549) | +| `feat/test-roots-manual-guard` `b50f197` (new: created at 02:29:44Z; no PR of its own) | the manual-guard fix, one commit on `dbc6837`, which #190 has carried since 13:59:10Z | landed with #190 (row above); the branch is still on origin | +| `feat/test-roots-followups` `01ccc86` (new: created at `2064390` at 02:14:37Z) | #190's follow-ups (#193 items 2–7); `2064390` → `7144eef` (02:31:20Z), then `7144eef` → `01ccc86` (20:31:02Z), a merge of main `f87fd5a` (re-read 20:36:57Z) | lane 3's review has resumed (#186 comment 6088703549) | | `004-migrate-to-triad-reads` `a7a8e00` | `a6830a1` → `a7a8e00` (13:50:08Z): lane 2's `gate-b-status-2026-10-09.md` | no PR; lane 1's own walk goes into its next evidence PR (#186 comment 6088714587, item 1) | | brett-wip `99eed35` | Gate A capture tooling v2, which answers this record's three findings (#186 comment 6073036070) | lane 2's final verification: `capture.sh:170` rewrites its source's index, and `restore.sh:132` can aim at `/` (#186 comment 6087261307). Lane 1 accepts both; no capture runs on a real checkout until a fixed version is re-verified (#186 comment 6088714587, item 4) | | issue #193 (13:42:32Z; updated 20:26:33Z) | the follow-ups deferred at the landing of #190 and #192: item 1 resolved by #190, item 15 added | — | @@ -512,7 +513,7 @@ in `root-guidance-2026-10-08.md` are on main since #192 (`1abee1d`). | #192's text findings (#193 items 8–13) | lane 2's `004-migrate-to-triad-evidence-2-followups` (#186 comment 6082466833, item 3) | lane 2, for lane 1 | on origin since 20:32:51Z at `096de93`, with main `f87fd5a` merged in; no PR (re-read 20:36:57Z) | | Gate A capture tooling v2 | brett-wip `99eed35` | lane 1 | to be fixed and re-verified before any real capture (#186 comments 6087261307, 6088714587) | | Run C's `follow_ups`: 29 entries (`adoption-plan.yaml:392–421`) | the next five rows | | | -| … 13 entries: a shipped command or test names `docs/`, `openspec/` or `specs/` (`:393–405`) | lane 3's `feat/triad-lane-tooling` `30f9dc0`, no PR; #190 (landed) for the suite's roots | `root-guidance-2026-10-08.md:188–194` routes the files it names: `lane-start`, `lanes-edit.sh`, `test_lane_helpers.sh` and the OpenSpec keys in `lanes-edit.sh` and `lane-handoff` to lane 3; `openRepoTools` to T007; `test_repo_hygiene.py`, `test_lane_start_claude_current.py` and `tests/run.sh` to T009/T010; `claude-current` to its text owner | that routing names neither `test_install_skill_and_hook.py` nor `test_park_resume_commands.py`, nor the plan's unnamed ninth `docs/` file: no owner is recorded for them | +| … 13 entries: a shipped command or test names `docs/`, `openspec/` or `specs/` (`:393–405`) | lane 3's `feat/triad-lane-tooling` `30f9dc0`, no PR; #190 (landed) for the suite's roots | `root-guidance-2026-10-08.md:188–194` routes the files it names: `lane-start`, `lanes-edit.sh`, `test_lane_helpers.sh` and the OpenSpec keys in `lanes-edit.sh` and `lane-handoff` to lane 3; `openRepoTools` to T007; `test_repo_hygiene.py`, `test_lane_start_claude_current.py` and `tests/run.sh` to T009/T010; `claude-current` to its text owner | that routing names neither `test_install_skill_and_hook.py` (`:398`) nor `test_park_resume_commands.py` (`:405`): no owner is recorded for those two. Corrected: the plan's unnamed ninth `docs/` file is `tests/test_repo_hygiene.py` (`git grep -l 'docs/'` at `837928a`, outside `docs/`, `specs/`, `openspec/` and the root front doors, gives exactly nine files), which `root-guidance-2026-10-08.md:191` routes to T009/T010 | | … 5 `shape/` collisions (`:406–410`) | T006's `assembly-0001` (`root-guidance-2026-10-08.md:70–76`) | lane 1 (T006) | merged in the patch; deleting the unpinned `shape/AGENTS.md`, `shape/CLAUDE.md` and `shape/README.md` is deferred to T014's reviewer (`:76`, `:394`) | | … open the split as a pull request (`:411`) | — | T013 (tasks.md:96–98) | deferred | | … the entry point (`:413`) and the test roots (`:414`) | the T007 rows above; #190 and the T010 row above | lane 1 | as above | From 2e7e55e6c80f494d3e590b18f49902886896632a Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 22:06:08 +0000 Subject: [PATCH 25/42] Answer lane 3's low findings N4 to N9 on the refreshed T011 record: collision rows, the dropped-check arithmetic, the copy-back's limits, deleted refs, the refresh's real time, and the verbatim run's public citation From lane 3's review of c1283b5 (#186 comment 6089982753), each checked at its source: - N4: root-guidance-2026-10-08.md:69-76 holds the collision and shape/ rows (AGENTS.md is :69; CLAUDE.md, :70, is unchanged), so "resolved in the patch", not "merged". - N5: October 7's verify.log:13 is one check of the 16 per-worktree HEAD refs, and main's status check moved to the singles (10-restore.log:160): 12 - 2 + 1 + 11 = 22 and 85 - 17 = 68. - N6: the copy-back route restores the 29 files and the receipt only, with no settings.json and no hook entry. - N7: a captured ref moved forward or was deleted with its commit still on GitHub; re-checked against ls-remote at 22:05:16Z (15/9/9/0). - N8: c1283b5 was committed at 20:38:20Z, so the hygiene line and the header's refresh window say 20:3xZ. - N9: the public comment marks as inferred only its conclusion; the verbatim run's log is host-local and unpublished, so the record now says no public citation exists for the run itself. Part of #186. Lane: openRepoTools-2 Co-Authored-By: Claude Opus 5.5 --- .../gate-ab-evidence-2026-10-09.md | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md b/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md index 3bc8f030..020e1571 100644 --- a/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md +++ b/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md @@ -3,13 +3,14 @@ **Task:** T011 of [tasks.md](tasks.md), under [#186](https://github.com/opensoft/openRepoTools/issues/186). No task is checked complete by this record, and it approves nothing. **Writer:** lane openRepoTools-1, 2026-10-09T00:36Z–01:1xZ. -**Refreshed:** lane openRepoTools-2, 2026-10-09T18:51Z–20:4xZ, on branch +**Refreshed:** lane openRepoTools-2, 2026-10-09T18:51Z–20:3xZ (commit `c1283b5`), on branch `004-migrate-to-triad-rollback-refresh` (from `fa44d75`). It answers lane 3's review of `fa44d75` (#186 comment 6082139424, whose findings are cited here as F1–F22) and folds in lane 2's verification of the receipt (#186 comment 6082370012). Each refreshed passage starts with "Refreshed" or "Corrected"; the 01:1xZ text it qualifies stays as written unless a finding showed it wrong. Origin was read at 2026-10-09T20:29:34Z–20:30:29Z, with -main at `f87fd5a`, and again before the push (section 8). +main at `f87fd5a`, and again before the push (section 8). Lane 3's review of `c1283b5` +(#186 comment 6089982753, N1–N9) is answered by two later commits on this branch. **Evidence branch:** `004-migrate-to-triad-rollback` (from main `837928a`). **Private receipt ID:** `rollback-20261009-ILvoVC` (brett-wip `migration/openRepoTools/`), holding every script, log, snapshot and digest named below. Machine paths stay there. It @@ -60,11 +61,14 @@ The 97 of #189 (`preservation-2026-10-07.md:89`; receipt `prep-20261007T100559Z` - This run did not repeat 18 of them: the status-and-lists comparison for the 16 trees other than `main` (October 7 made it for all 17, `preservation-2026-10-07.md:95–98`; this run made it for `main` only, below), the bundle's `HEAD` against the main - checkout's, and the bundle's 16 per-worktree `HEAD` refs. + checkout's (`verify.log:12`), and one check that the bundle carries the 16 per-worktree + `HEAD` refs (`verify.log:13`: one check, not 16). - It added 11: the manifest, the mirror clone, `set-url`, `bundle verify`, the capture against the shared checkout today, the second `fsck`, and five steps of the worktree procedure on `main`. -- 97 − 18 + 11 = 90. +- `main`'s status-and-lists comparison moved from the tree checks to the single ones + (`logs/10-restore.log:160`). So the single checks are 12 − 2 + 1 + 11 = 22, the tree + checks are 85 − 17 = 68, and the total is 97 − 18 + 11 = 90. - The manifest (414 entries) passes `sha256sum -c`. Refreshed (F4): it lists the two bundles restored here, source `60db3acd…` and dependency `aa5a6eca…`, which are the @@ -152,7 +156,10 @@ Corrected (lane 2's verification, #186 comment 6082370012): the receipt's copy o block (`11-dirty.sh:42–66`) is not verbatim. Against the backup's `capture.sh:68–92` it drops the info-exclude copy (`:75–77`) and the `skipped-secret-like.tsv` append (`:82`). Lane 2 ran the backup's own block, verbatim, on the same stand-in: 0 of 3 again, with the -same 3 staged blobs in no bundle. +same 3 staged blobs in no bundle. Corrected: the public comment states that result and +marks as inferred only its conclusion, that the omissions change nothing. The run's own +log is host-local, in lane 2's private prep area, and is not published, so no public +citation for the run itself exists. **Finding for Gate A.** The October 7 capture takes three things from a tree: its index *listing*, its ignored workflow configuration, and its HEAD. It takes none of its working @@ -362,7 +369,8 @@ neither is in any rollback path; this run left its metadata unchanged. **The rollback path in the plan's own words:** revert or pin-bump PRs, plus the preserved installer, through (c2). It needs: -- no force push: every captured ref has only ever moved forward; +- no force push: every captured ref has only ever moved forward, or was deleted with its + commit still on GitHub (refreshed: section 1; re-checked 2026-10-09T22:05:16Z); - no destructive reset; - no pointer rewrite. @@ -514,7 +522,7 @@ in `root-guidance-2026-10-08.md` are on main since #192 (`1abee1d`). | Gate A capture tooling v2 | brett-wip `99eed35` | lane 1 | to be fixed and re-verified before any real capture (#186 comments 6087261307, 6088714587) | | Run C's `follow_ups`: 29 entries (`adoption-plan.yaml:392–421`) | the next five rows | | | | … 13 entries: a shipped command or test names `docs/`, `openspec/` or `specs/` (`:393–405`) | lane 3's `feat/triad-lane-tooling` `30f9dc0`, no PR; #190 (landed) for the suite's roots | `root-guidance-2026-10-08.md:188–194` routes the files it names: `lane-start`, `lanes-edit.sh`, `test_lane_helpers.sh` and the OpenSpec keys in `lanes-edit.sh` and `lane-handoff` to lane 3; `openRepoTools` to T007; `test_repo_hygiene.py`, `test_lane_start_claude_current.py` and `tests/run.sh` to T009/T010; `claude-current` to its text owner | that routing names neither `test_install_skill_and_hook.py` (`:398`) nor `test_park_resume_commands.py` (`:405`): no owner is recorded for those two. Corrected: the plan's unnamed ninth `docs/` file is `tests/test_repo_hygiene.py` (`git grep -l 'docs/'` at `837928a`, outside `docs/`, `specs/`, `openspec/` and the root front doors, gives exactly nine files), which `root-guidance-2026-10-08.md:191` routes to T009/T010 | -| … 5 `shape/` collisions (`:406–410`) | T006's `assembly-0001` (`root-guidance-2026-10-08.md:70–76`) | lane 1 (T006) | merged in the patch; deleting the unpinned `shape/AGENTS.md`, `shape/CLAUDE.md` and `shape/README.md` is deferred to T014's reviewer (`:76`, `:394`) | +| … 5 `shape/` collisions (`:406–410`) | T006's `assembly-0001` (`root-guidance-2026-10-08.md:69–76`) | lane 1 (T006) | resolved in the patch; deleting the unpinned `shape/AGENTS.md`, `shape/CLAUDE.md` and `shape/README.md` is deferred to T014's reviewer (`:76`, `:394`) | | … open the split as a pull request (`:411`) | — | T013 (tasks.md:96–98) | deferred | | … the entry point (`:413`) and the test roots (`:414`) | the T007 rows above; #190 and the T010 row above | lane 1 | as above | | … 8 more plan-level items (`:412`, `:415–421`) | `:412` was rehearsed in runs B and C; `:415`, the workflow bootstrap, in T006 | by tasks.md: `:412` and `:415` T013; `:416` T016–T018; `:417` T012; `:418` and `:420` T014; `:419` T016; `:421` T015 | deferred | @@ -560,7 +568,8 @@ resolved mapping and a passing compatibility test matrix" (plan.md). Against tha (section 1); - `RESTORE.md`'s copy-back leaves `installed.tsv` stale. Rollback should be (c2): a reinstall from the preserved source, or the copy-back followed by restoring the - receipt. + receipt. Refreshed: the latter for the 29 files and the receipt only; it restores no + `settings.json` and no hook entry (section 2). `RESTORE.md`'s `git bundle verify` line also needs a repository to run in. @@ -669,7 +678,7 @@ Two hygiene tests read every tracked file: the host-absolute-path test and the a test. Their own regexes, taken from `tests/test_repo_hygiene.py`, find nothing in this file. CI is the suite of record. -Refreshed (2026-10-09T20:4xZ): on this refreshed file, `git diff --check` exits 0. The same two +Refreshed (2026-10-09T20:3xZ): on this refreshed file, `git diff --check` exits 0. The same two regexes, read from `tests/test_repo_hygiene.py` at `fa44d75` (`HOST_ABSOLUTE_PATH` and `ACT0_CITATION`), find nothing, and neither does a stricter scan for any host-absolute path prefix. No pytest ran: another lane's openRepoTools suite was live on the workstation. CI From bfc0814ef8e40cf895101c490afb7c3997a155c1 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Fri, 9 Oct 2026 22:08:43 +0000 Subject: [PATCH 26/42] Gate B status walk of 2026-10-09: the 46 checklist lines (MET 30, PARTLY 15, OPEN 0, AFTER-GATE-C 1), T001-T019, the Gate C open questions with proposed wording, and a line-by-line reconciliation with lane 2's walk specs/004-migrate-to-triad/gate-b-status-2026-10-09.md walks gate-b-checklist.md against origin as fetched at 22:07:06Z and #186's comments through 22:06:49Z. It approves nothing and changes no task status. - Section 1: each line's status, evidence, what remains and owner, and which lines moved since lane 1's unpublished first pass. - Section 2: lane 2's walk, the same path on 004-migrate-to-triad-reads @ a7a8e00 (a cross-check that goes into no PR), line by line. It differs in substance on 22, 2, 12, 18, 19 and 21. It also answers lane 2's review of the first pass (6088829392) item by item. - Section 3: T001-T019 against the evidence. - Section 4: questions a-e for Brett, each with text to approve: - (a) openspec 1.2.0 fails migrate-to-triad and 1.6.0 passes; - (b) "account move"; - (c) .gitattributes in plan.md against tests/conftest.py on main; - (d) hosted CI before the legs exist; - (e) e1-e10, including the six-commit openRepoShape drift and the post-cutover one-liner's API and jq dependency. - Section 5: the blockers in dependency order. - Section 6: the commands. Private receipts are named by id only, and no host path appears. Part of #186 Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../gate-b-status-2026-10-09.md | 589 ++++++++++++++++++ 1 file changed, 589 insertions(+) create mode 100644 specs/004-migrate-to-triad/gate-b-status-2026-10-09.md diff --git a/specs/004-migrate-to-triad/gate-b-status-2026-10-09.md b/specs/004-migrate-to-triad/gate-b-status-2026-10-09.md new file mode 100644 index 00000000..1d2c1b8f --- /dev/null +++ b/specs/004-migrate-to-triad/gate-b-status-2026-10-09.md @@ -0,0 +1,589 @@ +# Gate B status walk and the Gate C open questions — October 9, 2026 + +**Checked:** 2026-10-09T20:30Z–20:55Z, refreshed 21:56Z–22:15Z (UTC). The git state is a +`fetch --all --prune` at 22:07:06Z. The GitHub reads were made between 20:32Z and 20:41Z and again +between 21:58Z and 22:10Z. #186's comments were read through 22:06:49Z. Anything later is not in +this walk. +**By:** lane openRepoTools-1's reviewer, under [#186](https://github.com/opensoft/openRepoTools/issues/186). +It ran read-only everywhere except this file and its own scratch. A first, unpublished pass was +made at 13:27Z–13:50Z; section 1 says which statuses moved since. +**Walks:** the 46 lines of [gate-b-checklist.md](gate-b-checklist.md) (main, from #189). +**Cross-check:** lane openRepoTools-2's independent walk, which has the same path as this file, +`specs/004-migrate-to-triad/gate-b-status-2026-10-09.md`, on `004-migrate-to-triad-reads` @ +`a7a8e00` (reads to 13:48:37Z). Section 2 reconciles the two line by line, and also answers lane +2's review of this walk's first pass (#186 6088829392). Lane 2's copy stays on its branch as a +cross-check and goes into no PR. + +This walk records facts. It approves nothing and changes no status in [tasks.md](tasks.md), where +all 19 tasks stay open. Gate C stays Brett Heap's explicit approval naming the actual revisions +(plan.md:160–163). Machine paths are left out (plan.md:38–39). Private receipts are named by id +only, and their contents are not quoted. Logs held only in lane 1's private scratch are cited +through the #186 comment that reported them. + +## Refs read + +Paths are under `specs/004-migrate-to-triad/` unless they start with `openspec/`. + +| Short | Ref | Head read | +| --- | --- | --- | +| M | `main` | `f87fd5a` (#190), on `1abee1d` (#192), on `63810dd` (#189) | +| CI | `004-migrate-to-triad-ci` | `d28e2cc` | +| IN | `004-migrate-to-triad-installer` | `51993a4` | +| RB | `004-migrate-to-triad-rollback` | `fa44d75` | +| RR | `004-migrate-to-triad-rollback-refresh` (lane 2's slice A) | `2e7e55e` (`c1283b5`, `612ea94`, `2e7e55e` on RB) | +| EF | `004-migrate-to-triad-evidence-2-followups` (lane 2's slice B) | `096de93` | +| CA | `004-migrate-to-triad-candidate` | `935f53f` (the r1 build; r2 is not published) | +| T18 | `004-migrate-to-triad-t018` | `63bff08` | +| L2 | `004-migrate-to-triad-reads` | `a7a8e00` | +| #191 | `feat/installer-payload-source-resolution` | `13cc752` | +| FU | `feat/test-roots-followups` (no PR) | `01ccc86` | + +Main's planning files (plan, spec, tasks, the capability spec and the change's tasks) are +unchanged since `63810dd`; `git diff --stat 63810dd f87fd5a` over them is empty. Every `plan.md`, +`spec.md` and `tasks.md` line cited below holds on `f87fd5a`. + +**Live facts at the check:** +- `gh api repos/opensoft/openRepoTools-spec` and `-code`: HTTP 404 (20:32Z, 21:58Z and 22:08Z). The `gh` + account is `brettheap`, an active admin of `opensoft`. +- openRepoShape `main` is `22c9fdd`, ahead 6 and behind 0 of the pinned and rehearsed `7f84ca4`: + `b18cca9`, `21f5c33`, `f25d805`, `bc09bc5`, `4ce9cb0`, `22c9fdd` (question e1). +- `git ls-remote origin 'refs/heads/adopt/*'`: nothing. +- #191 is the only open PR, at `13cc752`. Lane 3's delta review 5471121210 is LAND, with + conditions. Lane 1's next head was announced (#186 6088714587) and was not on origin at + 22:07Z. Lane 2's T008 matrix at `13cc752` is final (#186 6088812080). +- Lane 2's slices are on origin, both meant for the evidence-3 PR. A is RR: lane 3 reviewed + `c1283b5` as LAND within evidence-3 (#186 6089982753), and `612ea94` and `2e7e55e` take in its + notes N1–N9 (6090048356). B is EF `096de93` (6088799627); lane 3's review of it is in progress. +- Lane 3's review of FU and the T010 patches is posted (#186 6089801221, 21:47Z). +- The r2 composed candidate's three suites finished at 18:20:47Z. Lane 2's reading of the logs is + #186 6087267593; this walk checked the summary lines against the logs. Lane 1's + classification of them is in progress. +- In progress, so not counted as evidence: the Gate A capture-tool fix (#186 6087232548, + 6087261307; nothing new in brett-wip since `99eed35`, per 6089772323 and 6089982753); lane 2's + verification of T18 (6089772323). +- Not counted, because its head is not on origin: lane 2's pre-push T008 matrix on lane 1's + unpushed #191 head and v3 entry-point patch (#186 6090038382). It reports the ancestor-walk + cases, (c) and (e) fixed, v3 refusing a gitlink that disagrees with the pin, and nine items + still open. It is a preview of the next head's check. + +## Status legend + +- **MET**: the evidence named in the checklist's "Evidence that would satisfy it" column exists + on a pushed ref and, where the line asks for a review, the review is recorded. +- **PARTLY**: evidence exists but is incomplete, unreviewed, measured on a head that has moved, + or carries an open defect or an open decision. +- **OPEN**: no finished evidence. +- **AFTER-GATE-C**: cannot be met before real legs exist. + +Publishing the T006–T011 records on `main` is not itself a checklist line. It is listed among the +blockers in section 5. + +## 1. The 46 checklist lines, at 2026-10-09T22:1xZ + +| # | Line (short) | Status | Evidence | What remains | Owner | +| --- | --- | --- | --- | --- | --- | +| 1 | Leg names and visibility confirmed (T003, FR-012) | MET | #186 6035767825 ("legs and visibility confirmed"); M:`openspec/changes/migrate-to-triad/adoption-plan.yaml` (legs; `visibility: public`) | Recheck at the Gate C request. 404 is not proof the names are free (preparation.md:53–56) | Brett (given); lane 1 (recheck) | +| 2 | `git-filter-repo` via the approved prerequisite path | PARTLY | M:adopter-rehearsal-2026-10-08.md#Setup; M:adopter-rehearsal-2026-10-07.md#Setup; #186 6072724043 (i) | No record names who approves the private-venv path for the real `execute` (plan.md:105–106, :160–162): question e4. Neither receipt carries the October 4 receipt's "existing bench/system installations were unchanged" (preparation.md:62–63), which the evidence column asks for (lane 2, 6088829392 item 10). The "no ensurepip" cause is withdrawn on EF; the refusal's cause is not established (#193 item 12) | Brett; lane 1 | +| 3 | Plan regenerated for the rehearsal source, overrides kept | MET | M:`openspec/changes/migrate-to-triad/adoption-plan.yaml` (source `837928a`, 22 resolutions); M:adopter-rehearsal-2026-10-08.md#Mapping (T003); #192 review 5464893559; #186 6072724043 (iv) | The final plan is regenerated at the frozen source before the request (question e2). `check` reads the clone's local `main`, so the committed plan is `plan-stale` against any later `main` (#193 item 10; EF:adopter-rehearsal-2026-10-08.md:107–119) | lane 1 | +| 4 | Standard `check` passes, every path, no unapproved drop | MET | M:adopter-rehearsal-2026-10-08.md#Run C (`plan ok`; 120 = 65/49/6/0); #186 6072724043 (ii) | Rerun at the frozen source | lane 1 | +| 5 | Amendment folders to code; full proposals and specs to spec | MET | M plan: `docs/`, `ideation/`, `openspec/`, `specs/` to spec; 0 `features/` paths on `main` | Re-observe at the frozen source | lane 1 | +| 6 | No amendment paths recorded where none exist | MET | M:inventory.json `refreshed.amendment_path_observations` (every head empty) | Re-observe at the Gate A final capture | lane 1 | +| 7 | Clean isolated source clone | MET | M:adopter-rehearsal-2026-10-08.md#Setup; receipt `adopter-fix-20261008-1HztL0`, verified in #186 6072724043 | — | — | +| 8 | Rehearsal into temporary local bare remotes | MET | M run C (`execute` 0, file transport only, 0 shim calls) | — | — | +| 9 | Identity, history and path/blob accounting | MET | M:adopter-rehearsal-2026-10-08.md#Run C; #186 6072724043 (iii); #192 review finding 4 | — | — | +| 10 | Recoverability; source unchanged | MET | M run C; M:adopter-rehearsal-2026-10-07.md#Run A; RB:gate-ab-evidence-2026-10-09.md#1; no `adopt/` ref on origin | — | — | +| 11 | Standard's refusal recorded | MET | M:adopter-blocker.md | — | — | +| 12 | Upstream resolution with regressions | MET | M:adopter-rehearsal.md#Regression evidence (draft `91d5685`: each case adopter-blocker.md:61–65 names); openRepoShape #162's CI at its head `6fa4e07`: `tests`, `tests-windows` and `tests-macos` SUCCESS (2026-10-07T00:57Z–01:06Z); its merge `7f84ca4`: `tests` (`python3 -m pytest tests -q`, `tests/test_adopt_submodules.py` included) SUCCESS at 01:13:26Z | The regression "does not qualify every mixed-registration plan" (adopter-rehearsal.md:30–32); this repository's plan is not one (line 14). openRepoShape `main` has moved 6 commits past `7f84ca4`: question e1. Lane 2 holds PARTLY (section 2) | Brett; openRepoShape owner | +| 13 | Tested consumer pin bump on `main` | MET | M@`837928a` (#188); #186 6068861167 | — | — | +| 14 | Nested submodule; original `.gitmodules` | MET | M runs B and C (code keeps blob `8043147a`; the assembly gets its own) | — | — | +| 15 | Pin integrity, coupled | MET | M run C (`3be52767…` MATCH); #186 6072724043 (v) | — | — | +| 16 | Recursive bootstrap | MET | M run C (`bootstrap ok`, three validators) | — | — | +| 17 | Root collisions and leg guidance as reviewed patches | MET | M:root-guidance-2026-10-08.md#1, #4; M:patches/t006/*; #192 review 5464893559 finding 7 (exact rebuild) | #193 items 8–13, the text fixes: on EF `096de93`, under lane 3's review, for evidence-3. The `shape/*.md` deletion is T014's reviewer's | lane 1 (EF by lane 2) | +| 18 | Shape-aware root bootstrap after the legs exist | PARTLY | M:root-guidance#7; T18:t018-disposable-2026-10-09.md#1 (a second run: `setup-openspeckit --shape on` exit 0, `worktree_root: worktrees`) | Neither run is reviewed, and neither has a receipt: EF now says so plainly (#193 item 11; EF:root-guidance-2026-10-08.md:31–57). Lane 2 is verifying T18 (6089772323). T18 finds the bootstrap leaves the root "dirty" to `resume` (T18 §5 item 1): question e7 | lane 1; Brett | +| 19 | First-line guidance, relative paths, actual `.specify`, paired selection, no secrets | PARTLY | M:root-guidance#2, #3 (both verified by #192 review finding 7), #7, #8; T18 §2 (paired selection from the root, either leg and either worktree; `check-prerequisites.sh` agrees ×5) | `.specify` was read from today's checkout, not from Gate A's captured configuration (lane 2, L2 line 19). The `.specify` posture is undecided (question e7). `SPECIFY_FEATURE` alone gives a branch and directory that disagree (T18 §5 item 8). T18 is unreviewed | lane 1; Brett | +| 20 | Provenance correspondence; link repair without guessed hashes | MET | M:root-guidance#6 (35/35; 47 citations resolve in the assembly), #3; #192 review finding 10 | EF marks "23 mentions in 9 files" as not established (#193 item 9). Feature 004's own pre-split paths are T017's | lane 1 | +| 21 | Explicit roots, local-root precedence, supported store selection | PARTLY | M:root-guidance#7 (store precedence; strict validate in the spec leg); T18 §3 (OpenSpec from each root with 1.2.0 and 1.6.0) | Store precedence rests on the document alone (#193 item 11). T18 shows the root and the code leg pass `validate --all --strict` vacuously, so validation must run in the spec leg (T18 §5 item 7). `--store` needs openspec 1.6.0 or later (question a) | lane 1; workBenches owner | +| 22 | Protocol/command distribution per target workstation through workBenches | PARTLY (no owner) | M:root-guidance#9: Eagle's two installed protocol files carry "Triad Feature Amendments" (sha256 `f9dd6464…`, `bbbc2b2c…`) | Not via workBenches delivery; command and skill digests absent; no other workstation; no target list; no writer holds it (lane 2 read only vendoring, consumer-reads-2026-10-07.md:85–105). Section 2 records lane 2's NOBODY | Brett (names the workstations); workBenches owner; a holder to be named by lane 1 | +| 23 | Installer design | MET | M:installer-design.md | — | — | +| 24 | Installer inventory refreshed | MET | M:consumer-reads-2026-10-07.md#The installer's own assumptions; IN:installer-2026-10-08.md#Lane 2's installer rows (17/23/29/31) | installer-design.md:61–64 still carries `daed209`'s counts | lane 1 | +| 25 | Entry point and payload-source resolution as reviewed patches | PARTLY | IN:patches/t007-assembly-entry-point.patch; #191 @ `13cc752`: LAND with conditions (review 5471121210; delta 8 is the stale PR body, plus `tests-macos` under `ready`) | The next #191 head needs its own check; it was not on origin at 22:07Z. The entry-point patch is CHANGES REQUIRED (5471121210 T3: on the remote path, confirm the gitlink before running the pinned implementation). Not landed | lane 1; lane 3 | +| 26 | One-liners resolve one code pin; no developer checkout | PARTLY | 5471121210 T1–T2 (a disposable assembly at `13cc752`: one code commit, the installed tree byte-identical to a checkout install, local delegation with no fallback); #186 6088812080 (final matrix at `13cc752`) | Rerun at the next head with the T3 fix. On the single layout a ref that moves mid-install mixes versions, as on `main` (6088812080 item 7); the PR body should scope "resolve once" to the adopted layout (5471121210 delta 8). Real one-liners are T015's | lane 2; lane 1 | +| 27 | 15-row compatibility matrix measured | PARTLY | #186 6073209094 and 6073234714 (15/15 at `2411f40`); #186 6088812080 (final at `13cc752`, 337 row logs; F1, F2, (b) and (d) fixed) | Rows 18–22 bear on the next head (6088812080 items 1–6): the layout search walks every ancestor, and a HOME holding `contracts/code-pin.yaml` refuses to reinstall; a missing ref or repository is read as a single repository and never named; a 502 on a probe refuses where `main` installs; the 404 path; garbage at 200 is reported as a moved ref; (c) and (e) remain. Rerun at the next head | lane 2; lane 1 | +| 28 | Names, counts, modes, hooks, receipts, all-or-none unchanged | PARTLY | 5471121210 delta 2 (offline install byte-identical at `13cc752`); #186 6088812080 (210 of 210 refusals placed nothing; 82 of 83 installs 29/29 byte-exact, the exception row 7's deliberate mixed case; 40 of 40 receipts consistent; both hook entries present); #186 6087267593 (the r2 install: 29 files byte-exact) | Rerun at the next head. An adopted `wip init` that refuses still leaves an empty server repository behind (#186 6087232548, its F2 row) | lane 2; lane 1 | +| 29 | Explicit roots in fixtures and hygiene checks | MET | M@`f87fd5a` (#190 at `b50f197`): LAND 5464931249, LAND 5464938427, delta LAND 5471080687; CI green including `tests-macos` | FU @ `01ccc86` (#193 items 2, 6): lane 3 finds it fit for a PR onto `main` after one edit, dropping or re-premising the 340 → 347 assembly cap (6089801221 finding 7). #193 items 3–7 and 15 stay open; item 5, the paired layout, is wanted before Gate C | lane 1; lane 3 | +| 30 | `tests/run.sh -k upstream_pin` inside the rehearsed code leg | MET | #186 6072319462 (all 14 `test_upstream_pin.py` pass in code leg `73b04d6`); CI:test-roots-and-ci-2026-10-08.md#1 | The runs used `tests/run.sh`'s own pytest line under the same lock, not its census (#184) | lane 1 | +| 31 | Composed checks refuse instead of skipping | MET | M@`f87fd5a` (#190); #186 6087267593 (five refusal cases exit 4 on an r2-equivalent); 6089801221 finding 15 | #193 item 4: conftest never reads a pin's `commit:`, closed in CI only by `composed.yml`'s step order (6089801221 finding 13). Item 15 (wording) is confirmed there (finding 14). About forty other conditional skips can still pass a composed run; none fires on ubuntu-latest today (finding 13 (ii)) | lane 1 | +| 32 | Code-leg CI and assembly exact-pin job patches | PARTLY | CI:patches/t010-*.patch; CI:test-roots-and-ci#5–#6; lane 3's review 6089801221 (c): fit for evidence-3, both patches apply to run C and the code-leg patch to `f87fd5a` | `composed.yml` runs the suite through `run.sh`, whose long run root makes #180's two row-stamp assertions fail, so `exact-pins` would be red until #180 is fixed or the job uses a short `TMPDIR` (finding 13 (i), inferred). Both patches need a real author at T014 (finding 13 (iv)). `actionlint` last ran 2026-10-07. SonarCloud and required checks: question e9 | lane 1; lane 3; Brett | +| 33 | Platform policies, Bash 3.2 and serialized locking kept | MET | CI:test-roots-and-ci#5; lane 3's review 6089801221 finding 12: all six jobs keep their names and order, `tests-macos` keeps its `ready` gate verbatim, the triggers, per-job `submodules:`, `parse-macos`'s bash 3.2 file list and the Windows env are unchanged, WSL stays a documentation policy, and locking is as today | The hosted run is line 35. The assembly has no `tests-macos` check, which T015's landing tooling must allow for (finding 12) | lane 1 | +| 34 | Exact composed state passes required checks and the matrix (local) | PARTLY | #186 6087267593: r2 = code `4dbcf6c` (#190 `b50f197`, #191 `13cc752`, T006, T010), spec `9e1d006`, assembly `5d92962`. Composed 5 failed / 1411 passed; standalone 3 failed / 1389 passed / 24 skipped; no dependency 6 failed / 1168 passed / 242 skipped | It does not pass yet. `test_agents_md_names_the_pin_rules` (T006 × #190, #193 item 14) and `test_r14_the_documented_upstream_skip_is_unchanged` (`KeyError: 'reason'`, first seen in r1, unanalysed) need fixes. The lane-helper shell FAILs (row stamps, #180 per 6089801221 finding 13), the `test_run_wrapper` timeouts and the observer timeouts (load, inferred) need classifying. r2 rests on `13cc752`, so the composed record is redone at #191's final head and the other final heads, then published | lane 1 | +| 35 | Hosted-runner part of T010 | AFTER-GATE-C | CI:test-roots-and-ci#5 ("Neither patch can run on GitHub until the legs exist"), #8 | Question d | Brett | +| 36 | Restore/rollback of the candidate and installed payload | PARTLY | RB:gate-ab-evidence-2026-10-09.md#1–#2; lane 2's independent check (#186 6082370012: restore 91 PASS, four homes reproduce); M:candidate record r1 §4 on CA; RR (the record now says the hook rollback is vacuous and the backup holds no `settings.json`; lane 3 LAND at `c1283b5`, 6089982753) | No hook-changing rollback, no restore of the local candidate from preserved artifacts and no post-merge revert/pin-bump rollback is rehearsed (lane 3 6082139424 findings 11 and 19; 6082370012 (iv)). The capture tooling v2 cannot stand as evidence until A1 is fixed (6087261307). RR is not on `main` | lane 1 | +| 37 | Work, jobs and claims survive; no force push, reset or pointer rewrite | PARTLY | RB:gate-ab-evidence#3; #186 6082370012 (verified; 0 non-fast-forward against GitHub); RR §3 | Jobs have no before/after record, only "no script … kills a process" (6082139424 finding 19). No force push is argued for a real rollback, not shown | lane 1 | +| 38 | Publish the Gate A evidence | MET | M:preservation-2026-10-07.md, M:inventory.json (#189); receipt `prep-20261007T100559Z` | Gate A itself stays open (section 5) | lane 1; Brett | +| 39 | Publish the Gate B rehearsal evidence (a receipt in adopter-rehearsal form) | MET | M:adopter-rehearsal-2026-10-07.md (runs A and B, #189) and M:adopter-rehearsal-2026-10-08.md (run C, #192); receipts `adopter-fix-20261007-iM20tv`, `adopter-fix-20261008-1HztL0`, verified in #186 6036838832 and 6072724043 | The other Gate B records are not on `main` (section 5, item 4) | lane 1 | +| 40 | Follow-up patch/commit inventory | PARTLY | RB:gate-ab-evidence#5 (7 patches); RR §5 (maps each follow-up to a patch, PR, owner or "deferred", including run C's 29 `follow_ups`; lane 3: finding 18 RESOLVED at `c1283b5` except N3, which `612ea94` takes in, 6089982753 and 6090048356) | Lane 3's finding 18 (6082139424) lists what the inventory lacked. Since 01:13Z: T007's entry point; #190's follow-ups (`b50f197`, the `ln_doc` abort fix, FU); #191's heads `2411f40` and `13cc752`; capture tooling v2. Known at 01:13Z: run C's 29 named `follow_ups`; the routing in root-guidance-2026-10-08.md:186–194; lane 3's `feat/triad-lane-tooling`; the 33 outside consumers not covered; the `shape/` deletion left to T014. The stack check used superseded heads. RR answers all of it, and states that T010's two patches have no recorded resulting tree (finding 20). It is not on `main`, and the heads it lists are not final: T007's entry point (T3), #191's next head and FU's edit, so it is refreshed once more at the final heads | lane 1 | +| 41 | Repeated into fresh remotes after the upstream fix | MET | M runs B and C at `7f84ca4` on openRepoShape `main` | A repin needs a new run (question e1) | — | +| 42 | Never the live consumer as `--source` | MET | M run C setup; no `adopt/` ref on origin | — | — | +| 43 | No in-place submodule edit; digests not adjusted | MET | #186 6035767825 and 6036838832 (v); M:root-guidance#1 (11/11 shape digests) | — | — | +| 44 | No real repository created for a test | MET | Both legs 404 at 20:32Z and 21:58Z; RB:gate-ab-evidence#3; T18 §1 (network namespace, 0 successful network calls) | Recheck at Gate C | lane 1 | +| 45 | Reviewed refreshed inventory (T001) with lines 2–6 | MET | M:inventory.json (stamp 2026-10-07T10:09:03Z; #189 LAND) | Refresh at the Gate A final capture | lane 1 | +| 46 | A review record for each T006–T010 patch | PARTLY | T006: 5464893559 LAND (landed). T009: #190 LAND ×2 and delta LAND (landed). T007/T008 code side: #191 `13cc752` LAND with conditions (5471121210). T010: lane 3 6089801221 (c), fit for evidence-3 | The T007 entry-point patch is CHANGES REQUIRED (5471121210 T3). #191's next head: unchecked. T010's `composed.yml` carries finding 13 (line 32) | lane 3; lane 1 | + +**Counts (46):** MET 30 · PARTLY 15 · OPEN 0 · AFTER-GATE-C 1. +- **PARTLY:** 2, 18, 19, 21, 22, 25, 26, 27, 28, 32, 34, 36, 37, 40, 46. +- **AFTER-GATE-C:** 35. + +**Moved since lane 1's first pass (13:27Z–13:50Z, unpublished; then MET 29 · PARTLY 15 · OPEN 1 · +AFTER-GATE-C 1):** +- **29: PARTLY → MET.** #190 landed at `b50f197`. +- **33: PARTLY → MET.** Lane 3's review of the T010 patches (21:47Z) confirms every job, gate and + policy the line names. +- **34: OPEN → PARTLY.** The r2 suites finished, with failures. +- **36 and 37: MET → PARTLY.** Lane 3's T011 review (13:43Z) shows the hook rollback, the + candidate restore, the post-merge rollback and jobs are not rehearsed. +- **39: PARTLY → MET.** Run C landed with #192. This walk now reads line 39 by the checklist's own + evidence column; the other records' publication moves to section 5. + +## 2. Reconciliation with lane 2's walk at `a7a8e00` + +Lane 2's walk is `004-migrate-to-triad-reads` @ `a7a8e00`: +`specs/004-migrate-to-triad/gate-b-status-2026-10-09.md`, the same path as this file. Lane 2 +keeps it on that branch, unrevised, as a cross-check; it goes into no PR (lane 2, #186 +6088829392). Lane 2 read to 13:48:37Z; this walk reads to 22:10Z. + +Lane 2's legend is the checklist's own: EXISTS, IN PROGRESS, NOBODY, BLOCKED, DECIDED. It +describes who holds a line. This walk's legend describes the evidence. The two map as follows: +- DECIDED ≈ MET; +- BLOCKED ≈ AFTER-GATE-C; +- IN PROGRESS ≈ PARTLY; +- EXISTS is MET when nothing remains, and PARTLY when a review, a receipt or a decision does. + +**Lines that differ in substance, with this walk's verdict:** + +| # | Lane 2 | This walk | Verdict | +| --- | --- | --- | --- | +| 22 | NOBODY | PARTLY (no owner) | **Both are right about different things, and they agree on the gap.** Lane 2 records that no writer holds it. This walk records that a start exists: Eagle's installed protocol-file digests (root-guidance §9). But that start is not "through owning workBenches delivery" and has no command or skill digests, so even Eagle is partial. Kept as PARTLY, flagged as having **no owner**; no lane-2 writer holds it either (6088829392 item 6). It cannot finish until Brett names the target workstations (question e8) and the workBenches owner delivers. Lane 1 should name a holder. | +| 2 | EXISTS | PARTLY | Lane 2 itself notes that no source names who approved the private-venv path, and later adds that neither receipt says bench and system installs were left unchanged (6088829392 item 10). plan.md:105–106 asks for "the approved prerequisite path", and Gate C asks that "prerequisites … are current". Kept as PARTLY until Brett answers question e4. | +| 12 | EXISTS, candidate only; PARTLY in 6088829392 item 2 | MET | Lane 2 found no regression record at the merged head and asks this walk to cite #162's CI at `6fa4e07` or say there is none. This walk cites it: at #162's head `6fa4e07`, `tests`, `tests-windows` and `tests-macos` succeeded, and at its merge `7f84ca4` the `tests` job, `python3 -m pytest tests -q` with `tests/test_adopt_submodules.py` included, succeeded at 2026-10-07T01:13:26Z (Windows and macOS are skipped on that push). Each case adopter-blocker.md:61–65 names is covered by adopter-rehearsal.md:23–36, and those tests pass at the merged head. The stated limit, "does not qualify every mixed-registration plan", does not reach this repository, whose one registration moves whole to code (line 14). MET. | +| 18 | EXISTS (document only) | PARTLY | A row that exists only in the writer's document, with no receipt and no independent rerun, is evidence not yet checked (#193 item 11). T18 adds a second run by lane 1 itself, also unreviewed, and a new finding: the bootstrapped root is "dirty" to `resume`. PARTLY. | +| 19 | EXISTS | PARTLY | Lane 2's own note that `.specify` was read from today's checkout and not from Gate A's capture is a gap against the checklist's evidence column. The posture decision is open (e7), and T18 adds the `SPECIFY_FEATURE` disagreement. PARTLY. | +| 21 | EXISTS (document only) | PARTLY | As for 18. T18 also shows that validation run from the root passes vacuously. PARTLY. | + +**Lines that agree in substance:** +- **Same status, different legend:** 1, 4–11, 13–16, 23, 24, 30, 35, 38, 41–45 (MET or + AFTER-GATE-C here; DECIDED, EXISTS or BLOCKED there). +- **Unlanded at lane 2's cutoff, landed since:** 3, 17, 20, 29, 31 (lane 2's "EXISTS + (unlanded)" are MET now). +- **Line 33:** EXISTS there, MET here since lane 3's review of the T010 patches (6089801221 + finding 12). This walk's earlier PARTLY waited only on that review. +- **IN PROGRESS there, PARTLY here:** 25–28, 32, 34, 36, 37, 40, 46. + - The newer facts are 5471121210 (#191 LAND with conditions; T007 patch CHANGES REQUIRED), + 6088812080 (the final matrix at `13cc752`), 6087267593 (r2's three suites), 6089801221 + (lane 3's T010 review), 6089982753 (lane 3's LAND on RR), and RR and EF (lane 2's slices). + - Lane 2 also holds 36 and 37 open on its T011 verification. That verification has since + passed (6082370012), and lines 36 and 37 stay PARTLY on lane 3's review findings instead. + - Lane 2's `a7a8e00` still names holders for lines 32, 36, 37 and 40 that have since finished + (6088829392). +- **Line 39:** EXISTS there, MET here. Both read the run C receipt, which is now on `main`. + +**Lane 2's "Facts noticed" — this walk's verdicts:** +- **Item 16 (a host-absolute path in IN:installer-2026-10-08.md:13): agree.** It must be removed + before IN goes into the evidence-3 PR; plan.md:38–39. +- **Items 1 and 8 (the candidate receipt is absent from brett-wip; the record cites #191 at + `2411f40`): agree.** Both are owed with the r2 publication (line 34). +- **Item 13 (two openspec results): agree.** It is question a. T18 §3 now measures 1.2.0 and + 1.6.0 side by side. +- **Item 14 (two reviews of lane 1's PRs are headed "lane openRepoTools-1"): agree; a note, not a + status change.** The reviews of record for #190 and #191 are lane 3's; lane 1's are same-lane + second opinions. +- **Items 2–7, 9–12, 15 and 17: agree.** They are overtaken by the landings, or are text fixes + carried by #193. + +**Lane 2's review of this walk's first pass (#186 6088829392), item by item:** +1. Lines 36 and 37 to PARTLY: **adopted.** +2. Line 12 to PARTLY: **not adopted.** The merged-head CI record it asks for is cited (the table + above); MET stands. +3. Line 34 to PARTLY with the r2 numbers: **adopted**, with the note that r2 rests on `13cc752`. +4. Lines 25 and 46, 5471121210's CHANGES REQUIRED (T3) and #191's LAND with conditions: + **adopted.** +5. Question e1 is stale: **adopted.** `main` is `22c9fdd`, and its `scripts/repo_shape.py` change + is the only one of the six commits that touches a mounted file (question e1). +6. Line 22, drop "lane 2" from the owner: **adopted.** +7. Re-cites: **adopted.** Run C's records are cited on `main`; "Land #192" and "fold `b50f197`" + are gone; line 39 is MET. +8. Question a, record `command -v` and `--version` for each CLI: **adopted** (question a). +9. Questions e6, c and b: **adopted** (section 4). +10. Lines 40 and 2: **adopted** (section 1). + +Lane 2's corrections to its own records stand as it gives them: r14's `KeyError 'reason'` was +first seen in lane 1's r1, and #193 item 15 was found on lane 2's own r2-equivalent build, not in +lane 1's r2 logs (#193 now says so). + +## 3. T001–T019 against the evidence + +Every task stays open on `main`. + +| Task | What the evidence already covers | What remains before Gate C | After Gate C | +| --- | --- | --- | --- | +| T001 inventory | M:inventory.json, M:preservation-2026-10-07.md (17 trees, 33 refs, the estate-status refusal relayed, amendment observations) | Owner dispositions for every retained object, approved by Brett (question e5); a refreshed inventory at the final capture | — | +| T002 preservation | Gate A bundle; restore 97 PASS (October 7), 90 PASS (RB), 91 PASS (lane 2's independent run) | Capture tooling v3: lane 2 found that v2 rewrites the source's index (A1) and can write under `/` (A2) (#186 6087232548, 6087261307). No fix was posted by 21:44Z, and no capture runs on a real checkout until lane 2 re-verifies one. Then the owner-coordinated freeze, the final capture, and installed-file receipts for py-bench and the other workstations | — | +| T003 mapping | Names and visibility confirmed; filter-repo; run C's plan (`plan ok`, 0 drops) on `main` | Approve the prerequisite path (e4); regenerate and `check` at the frozen source; the final mapping goes into the approval (e2) | — | +| T004 rehearsal | Run C on `main` | A rerun at the frozen source if question e3 requires one | The real `execute` is T013 | +| T005 nested pin | Refusal, upstream fix and repin; the nested pin and bootstrap in runs B and C; upstream CI at `6fa4e07` and `7f84ca4` | Question e1 (stay on `7f84ca4`, or repin and rehearse again) | — | +| T006 root/leg guidance | Collisions, 32 repairs, leg front doors, provenance 35/35 (on `main`); bootstrap, paired selection and OpenSpec roots measured twice (root-guidance §7; T18 §§1–3) | #193 items 8–13 (EF, unreviewed); lane 2's verification of T18, then a review; a receipt, or the sentence EF adds saying there is none; questions a, c, e7, e8 | T014 applies `assembly-0001`, `code-0001` and `spec-0001`, regenerates the two bumps with `bump-leg.py`, and settles `shape/*.md`. T18's follow-ups go to workBenches and lane 3 | +| T007 entry point | The entry-point patch (IN); #191's code side, LAND with conditions at `13cc752`; T1–T2 of 5471121210 | #191's next head: proof, lane 3's check, the PR body, `tests-macos`, landing, against the matrix findings of 6088812080. The entry-point patch's T3 change, then its re-review | T014: the entry point ships in the root-split PR itself (lane 3, #186 6072087071); T015: the real one-liners | +| T008 matrix | 15 rows at `2411f40`; the final matrix at `13cc752` (337 row logs) | A rerun at #191's next head (one command, lane 2) | T015 against the real GitHub; workBenches re-vendors (W1/W3/W8) once the legs exist | +| T009 test roots | #190 landed with `b50f197`; its CI green, macOS included | FU @ `01ccc86`: one edit (6089801221 finding 7), then a PR and landing; #193 items 3–7 and 15; item 5 (paired layout) before Gate C; item 14 settled by lane 1 | Verify at the real head (T015) | +| T010 CI | Two workflow patches; local proofs P1–P4; lane 3's review, fit for evidence-3 | `composed.yml` against #180 (finding 13 (i)); a composed run of the final candidate that passes (line 34); question d | Hosted runs at the real legs (T015); a real author on both patches (T014); SonarCloud binding and required checks (e9) | +| T011 restore/rollback | Source restore and four rollback homes, independently verified; RR refreshes the record, LAND by lane 3 within evidence-3 | A hook-changing rollback, the candidate restore and a post-merge pin-bump rollback, or a dated statement of what cannot be rehearsed; jobs before/after | Rollback checks at the real head (T015) | +| T012 freeze and approval | #97 and #121 landed and are in the baseline | Freeze; regenerate and `check`; recheck the names; present the plan, follow-ups and dispositions; Brett's approval | Refuse source drift before `execute` | +| T013–T015 | Prepared patches re-apply (RB §5; the candidate's §2) | — | All of them; T015 carries the hosted CI that question d moves there | +| T016–T019 | T18 is the disposable half of T018 (unreviewed, on its branch; lane 2 is verifying it) | — | All of them. T016–T018 are prepared in the candidate once the legs exist; the real half of T018 and T019 follow cutover | + +## 4. Gate C open questions + +Each question ends in text for Brett Heap to approve, correct or reject. None of it is approved +by being written here. + +### (a) `openspec validate migrate-to-triad --strict` fails on `main` with the CLI first on `PATH` + +**Run** in a scratch single-branch clone of `main` at `f87fd5a` (22:00Z; the first run, at +`63810dd`, gave the same results). The change's files are identical at both heads. On lane 1's +workstation, `command -v openspec` is `~/.npm-global/bin/openspec`, and `--version` prints +`1.2.0`. The system openspec, second on `PATH`, prints `1.6.0`. + +```text +$ ~/.npm-global/bin/openspec validate migrate-to-triad --strict # 1.2.0, first on PATH +Change 'migrate-to-triad' has issues +✗ [ERROR] repository-triad-migration/spec.md: ADDED "Installation retains the public interface and one revision" must contain SHALL or MUST +exit=1 +$ validate migrate-to-triad --strict # 1.6.0 +Change 'migrate-to-triad' is valid +exit=0 +``` + +On lane 2's host the system openspec is 1.13.1, and it passes `migrate-to-triad` and 5 of 5 +changes on `main` (6088829392 item 8). EF records the bench's openspec as 1.13.1 too. + +**Cause.** 1.2.0's `extractRequirementText` (`dist/core/validation/validator.js:369-390` in its +package) takes only the first non-blank body line of a requirement. Line 45 of +`openspec/changes/migrate-to-triad/specs/repository-triad-migration/spec.md` reads `Existing +one-line installation, command names, payload destinations/modes,`, and the requirement's SHALL +is on line 46. 1.6.0 reads the whole body. Every other requirement in the file puts SHALL on its +first body line. The same 1.2.0 rule fails `interactive-lane-name-repair` (two requirements), +which is not this migration's change. T18 §3 and §5 item 7 add that from the root or the code +leg, `validate --all --strict` finds no change and passes vacuously, so validation belongs in the +spec leg. verification.md:28, :66, :96, :126 and :144 record strict validation as passed without +naming a CLI version. + +**Proposed minimal change.** Tested in scratch copies: 1.2.0 and 1.6.0 both exit 0. No +requirement is added, removed or renamed, and no scenario changes. + +```diff + ### 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 ++The migration SHALL keep the existing one-line installation, command names, ++payload destinations/modes, skills/hooks/receipts and supported fork/ref/local ++invocation 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. +``` + +A no-wording alternative also passes both CLIs: join the first sentence onto one line. + +**Brett decides one of:** +1. "Approve replacing lines 45–48 of + `openspec/changes/migrate-to-triad/specs/repository-triad-migration/spec.md` with: *The + migration SHALL keep the existing one-line installation, command names, payload + destinations/modes, skills/hooks/receipts and supported fork/ref/local invocation usable. An + adopted assembly entry point SHALL resolve one immutable code revision for the complete + installation. Missing/inconsistent pin evidence* (the rest unchanged). It changes no + requirement's meaning." +2. "Leave the text. The validator of record is openspec 1.6.0 or later, run in the spec leg, and + the 1.2.0 copy in `~/.npm-global/bin` is removed or upgraded on every workstation that + validates." + +Optional in the same edit: line 64 ends `…its manifest/pins; product` and line 65 begins `Full +proposals`, so the sentence reads "pins; product Full proposals". The stray word does not affect +validation. + +### (b) "account move" + +**Every use.** `git grep -n -i -E 'account[ -]?move'` over `main` and every evidence branch, and +#186's body and comments, find one line: + +```text +specs/004-migrate-to-triad/adopter-rehearsal.md:103:conversion, merge, account move or lane rebinding. +``` + +It came in with `ff6f6a7` (2026-10-06, "docs: record successful triad adopter candidate +rehearsal"), squashed into `main` by #187 as `44983ce`. + +**plan.md, spec.md and tasks.md never say it**, nor do the change's proposal, design, tasks and +capability spec. Their "account" words are all about accounting for content: "account for", +"accounting", "accounted" and "misaccounts" (for example plan.md:111–112, :152, :169, :209; +spec.md:14, :40, :95, :111; tasks.md:47, :66, :89, :97, :135, :163; the capability spec's :13). +Nothing in the plan defines an account move, and no task performs one. + +**What it needs.** The phrase sits in a list of acts the October 6 receipt does not authorize. +Gate C's approval should say whether any account act belongs to the migration: +- the legs would be created in the `opensoft` organization (adoption-plan.yaml `org: opensoft`), + and `gh` here is `brettheap`, an active admin of `opensoft`; +- in-place adoption keeps `opensoft/openRepoTools` where it is; +- no repository is transferred between accounts or organizations. + +The lanes' Claude "account swaps" in #186's comments are operational and unrelated. +adopter-rehearsal.md is a dated receipt and is best left as written. + +**Text for Brett to approve or correct:** "No account move is part of this migration: +`opensoft/openRepoTools` stays in place, `opensoft/openRepoTools-spec` and +`opensoft/openRepoTools-code` are created in the `opensoft` organization by `brettheap`, and no +repository is transferred between accounts or organizations." + +### (c) `.gitattributes`: plan.md against the code on `main` + +**The plan** (M:plan.md:55–62, § Target placement): + +```text +57 | 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. | +59 | Code `openRepoTools-code` | All executable tools, `repos.tsv`, `commands/`, `skills/`, `tests/`, implementation CI | Shipped implementation and its installer payload. | +``` + +**The code on `main`** since #190: `f87fd5a:tests/conftest.py:62–68` says everything outside the +front-door documents and spec trees is the code leg's, "and that includes `.gitattributes`, +DELIBERATELY: the placement table puts it at the assembly, but git reads a submodule's attributes +from the submodule's own tree". So `main` now disagrees with plan.md:57 (6088829392 item 9). + +Related, and unchanged by this question: `openspec/changes/migrate-to-triad/design.md:48` ("Root owns front-door guidance, license, +project metadata and workflow bootstrap") and adoption-plan.yaml's `.gitattributes` → `root`, +which is correct for extraction. The original file and its history stay at the root. + +**Why** (#190's PR body, "Decision: `.gitattributes` stays in the code leg"; +CI:test-roots-and-ci-2026-10-08.md#2): +- Git never applies a superproject's attributes inside a submodule. Measured in the composed + candidate: `code/park` and `code/tests/run.sh` are `text: unspecified`. +- A leg split exactly by the table is red on `test_the_root_carries_the_line_ending_rule`. +- T006's `code-0001` adds the code leg's own copy, and `spec-0001` gives the spec leg one. The + candidate confirms `text: auto`, `eol: lf` in the leg. + +**Proposed amendment** (the plan is Brett's own). Row 57 is unchanged. Row 59 becomes: + +```text +| Code `openRepoTools-code` | All executable tools, `repos.tsv`, `commands/`, `skills/`, `tests/`, implementation CI; its own `.gitattributes`, added after extraction | Shipped implementation and its installer payload. Git never applies the assembly's `.gitattributes` inside a submodule, so the leg carries the LF rule its bash files and byte-for-byte installer checks need. | +``` + +Add after the table: + +```text +Extraction gives each original front-door file one owner, the assembly. T006's leg patches then +add new files to the legs: each leg's own `AGENTS.md`, `CLAUDE.md`, `README.md`, `LICENSE` and +`.gitattributes`, and the code leg's `.gitignore`. `spec-0001` also re-points three test paths in +the spec leg's `docs/README-claude-current.md`. These are reviewed changes after byte accounting, +not moved paths, so the adoption mapping does not change. +``` + +**Text for Brett:** "Approve amending plan.md § Target placement: the code leg carries its own +`.gitattributes` (and each leg its own short front door), added after extraction; the assembly +keeps the originals." The minimal form, exactly what #190 asks, is the row 59 change alone. + +### (d) Hosted CI before the real legs exist + +**The conflict.** +- T010 is in "Phase 3 — Installation and checks (Gate B completion)" and asks to "Verify the exact + rehearsed composed state passes required checks and the compatibility matrix" (tasks.md:77–81). +- "T011 closes Gate B" (tasks.md:164). +- The rehearsed commits live only in host-local bare remotes, which GitHub-hosted runners cannot + check out (checklist line 35). +- "No real repository is created for a test" (plan.md:253). #186's rules forbid a real leg + repository or a PR to one before Gate C. +- CI:test-roots-and-ci#5: "Neither patch can run on GitHub until the legs exist." +- T015 already validates "the real exact assembled head using … required platform/CI" + (tasks.md:107–108) before the root adoption merge. + +**What exists today.** The code these jobs would test already runs on hosted runners, on today's +layout: +- #190: `tests`, `tests-no-submodule`, `tests-windows`, `parse-macos`, `tests-macos`, + `guard-launch-mode` and SonarCloud, all green; +- #191: the same less `tests-macos`, which waits on `ready`. + +What only the legs can show: +- the code leg standalone with its document checks skipping by name; +- the Windows LF check on the leg's own `.gitattributes`; +- the assembly's `composed.yml` with an https recursive checkout, which lane 3 expects to be red + until #180 is fixed (6089801221 finding 13 (i)). + +**Options for Brett:** +1. **(Recommended by this walk.)** "Gate B does not wait for hosted runners on rehearsed + commits. The hosted runs of the code leg's `tests.yml` jobs (including `tests-windows`, + `parse-macos` and `tests-macos`) and of the assembly's `composed.yml` move to T015, before the + root adoption merge. Before Gate C, the same jobs green on the code-side PRs against today's + layout and the local composed and standalone runs of the exact candidate stand in for them. No + repository is created to host them." Matching addition to tasks.md T010: "Hosted-runner checks + that need the real leg repositories run at T015 against the real exact assembled head." +2. "Approve a never-merged draft PR on `opensoft/openRepoTools` whose tree is the candidate code + leg, to run its `tests.yml` on hosted runners before Gate C." It creates no repository, but it + is a PR against the real assembly repository whose tree deletes most of `main`, and it cannot + run `composed.yml`. +3. "Approve temporary repositories for hosted CI of the rehearsed commits, + deleted after T015." This contradicts plan.md:253, which would need amending in the same word. + +Two facts T015 must carry whichever option is chosen: +- The assembly has no `tests-macos` check, so landing tooling that reads `tests-macos` by name + has nothing to read on an assembly PR (6089801221 finding 12). +- After cutover the documented curl one-liner needs api.github.com, 5 API calls per install, and + `jq` on the curl path. A gh-less, rate-limited machine cannot install (5471121210 T4; question + e10). + +### (e) Other decisions only Brett can make + +In dependency order. Each is a sentence Brett approves, corrects or rejects. + +1. **The standard revision.** openRepoShape `main` is `22c9fdd`, six commits past the pinned and + rehearsed `7f84ca4`: #170 (`b18cca9`: `execute` refuses every finding `check` reports), #182 + (`21f5c33`: shape files past a source `.gitignore` are force-staged and verified), #187 + (`f25d805`: canonical leg mount paths), #185 (`bc09bc5`: scaffold force-staging), #186 + (`4ce9cb0`: shape-doctor's submodule note) and #193 (`22c9fdd`: `load_yaml` refuses a + non-UTF-8 byte). Four of them change `adopt-project.py` itself. Only `22c9fdd` touches one of + the eight files this repository mounts (`scripts/repo_shape.py`; the eight are listed in + `837928a`'s message). A repin to `4ce9cb0` or earlier moves no mounted file. Gate C "names the + actual source/standard revisions" (plan.md:161), and the rules allow only a commit on + openRepoShape `main`. + - Either: "Execute with openRepoShape `7f84ca42ca86a8902928345109d2bf6bad87bd91`, the pinned + and rehearsed revision." + - Or: "Repin to first, through a pin PR with a recomputed digest, + and rehearse again (run D) before Gate C." +2. **The frozen source, the landing hold and where the final plan lives.** A committed plan + counts `openspec/`, which contains the plan, so no commit that carries the plan can be its + source (#192 finding 17). `check` reads the clone's local `main` (#193 item 10). + - Text: "Freeze `main` at <40-hex> once the pre-Gate-C PRs land. Hold every landing on `main` + from then until the root-split PR merges or is abandoned. The final plan for that commit is + presented in the Gate C request and kept in the private receipt, not committed to `main` + before `execute`." + - The freeze also starts Gate A's owner-coordinated final capture (plan.md:77, :154–155). +3. **Whether the rehearsal is repeated at the frozen source.** T012: "Verify current … + rehearsal evidence; refuse any source drift before execution". Gate C: "Gate A/B evidence … + current". + - Either: "A source that differs from `837928a` only under paths the plan already assigns + needs a regenerated plan and `check`, not a new rehearsal." + - Or: "Rehearse again (run D) at the frozen source before Gate C." +4. **The prerequisite path.** "`git-filter-repo` 2.47.0, installed into a private venv from the + wheel with SHA256 `2cd04929b9024e83e65db571cbe36aec65ead0cb5f9ec5abe42158654af5ad83` + (`--no-index`), is the approved prerequisite path for the real `execute`; bench and system + installs stay unchanged, and the execution receipt says so." (plan.md:105–106; checklist + line 2) +5. **Work dispositions.** T012 asks for approval of "work dispositions", and the change's + tasks.md 1.2 for "active-work dispositions". The October 7 inventory's dispositions are + proposals held in a private receipt (M:preservation-2026-10-07.md#Inventory (T001)). Text: + "Approve the dispositions in receipt as listed." +6. **#191's behaviour on today's layout.** The review rule makes any change to today's + single-repository behaviour before Gate C a Gate C item (#191 review 5464963807 finding 3). + Measured differences at `13cc752`: + - two extra probe requests per install (26 raw requests against `main`'s 24 with no gh; 25 gh + contents against 23), each answered 404 (5471121210 delta 3); + - an offline installed-copy reinstall refuses in new words that name the layout question, + where `main` names the skills (delta 3); + - the ancestor walk, unreachable in today's estate, adds an output line under an unrelated + LF pin and exits 2 under a CRLF one (delta 4); + - lane 2's matrix adds a 502 on a probe refusing where `main` installs, a missing ref read as + a single repository after 34 requests with `main`'s words, and a HOME holding + `contracts/code-pin.yaml` refusing a reinstall (6088812080 items 1–3). + + Re-check every one at #191's next head. Lane 2's pre-push matrix on that head (6090038382, + not counted here) already reports the ancestor-walk and HOME-pin cases fixed, and the missing + ref, the 502, the garbage body and the offline wording still different from `main`, plus a + residual walk match where an unrelated pin's own `submodule_path` lines up. Text, for whatever + remains: "Accept on today's layout as the price of telling the layouts + apart, disclosed in #191's body." +7. **The `.specify` posture at the assembly root.** "Keep `.specify/`, the managed blocks and the + generated agent files local and ignored per clone, as today (`.git/info/exclude`), with only + `/worktrees/` in the tracked `.gitignore`." The alternatives are to ignore them in the tracked + `.gitignore` or to commit `.specify/` (root-guidance §8). T18 §5 item 1 adds that a + bootstrapped root is "dirty" to `resume`, and measures the options: land the spec leg's three + OpenSpec scaffold files before cutover, run the bootstrap with `--no-repo-agent-pointers` or + commit its block, and carry four exact-path root excludes. +8. **Target workstations** for T006's distribution check and SC-006: "The target workstations + are ; each needs the workBenches delivery receipt before cutover." Only Eagle has + evidence (root-guidance §9). +9. **Owner acts on the new repositories** (after Gate C, but part of the plan approved there): + "Bind SonarCloud to `opensoft/openRepoTools-code`; protect each leg's `main` with its + `tests.yml` jobs required; require `composed / exact-pins` on the assembly's `main`." + CI:test-roots-and-ci#5 and #8 name the SonarCloud binding as an owner's act. +10. **The post-cutover one-liner's dependencies** (5471121210 T4). Either: "Accept that after + cutover the curl one-liner needs api.github.com (5 calls per install) and `jq`; document it + beside the one-liner." Or: "Require the one-liner to install without the API, and make that + a T007 change before Gate C." + +**A Gate C approval sentence that would cover the above** (a draft for the request, with blanks): +"Gate C for #186: I approve the in-place adoption of `opensoft/openRepoTools` as the assembly, +executed once with openRepoShape `adopt-project.py` at , from an isolated +single-branch clone of `main` at , with the plan regenerated for that source +(sha256 <…>); creating `opensoft/openRepoTools-spec` and `opensoft/openRepoTools-code`, both +public; `git-filter-repo` 2.47.0 from the private venv (wheel `2cd04929…ad83`); the follow-ups + applied after source verification through leg and root +PRs; the work dispositions in ; and a landing hold on `main` from until the +root-split PR merges or is abandoned. It does not approve lane rebinding, closing old PRs or +retiring worktrees." + +## 5. Blockers to requesting Gate C, in dependency order + +1. **#191 to a landable head.** Push the next head; prove its red/green claims; lane 2's matrix + rerun and lane 3's check at that head, against 6088812080's items 1–7 and the open items of + lane 2's pre-push run (6090038382); correct the PR body + (5471121210 delta 8); `tests-macos` under `ready`; land. Then the T007 entry-point patch's T3 + change and its re-review. Or Brett's ruling on any kept difference (question e6). +2. **The test-root follow-ups and the T010 patches.** FU's one edit (6089801221 finding 7), its + PR and landing; `composed.yml` against #180 (finding 13 (i)); #193 item 14 settled by lane 1, + and item 5 (paired-layout discovery) before Gate C. +3. **The composed candidate at the final heads (r3).** Every suite passing, or each failure + classified with its cause: r14's `KeyError 'reason'`, `test_agents_md_names_the_pin_rules`, + the row stamps (#180), the load-sensitive timeouts. Then the install and rollback recheck, + and publication with its receipt (lines 34, 36, 40). +4. **The evidence-3 PR.** Fold CI (`d28e2cc`'s three new files), IN (with the host-absolute path + at installer-2026-10-08.md:13 removed), RR at `2e7e55e` (the branch tip's file: a cherry-pick of + `c1283b5` alone conflicts, 6089982753), EF `096de93`, the candidate record, T18 + and this walk onto `main`; do not reuse `849592b`'s subject or body (6088884829); review; + land. +5. **Gate A.** Capture tooling v3 (A1, A2) and lane 2's re-verification; the owner-coordinated + freeze; the final capture; owner dispositions (question e5); installed-file receipts for + py-bench and the other workstations. +6. **Brett's decisions:** questions a–e, e10 included. +7. **The request.** Regenerate the plan at the frozen source and `check` it, rehearsing again if + question e3 says so; recheck the leg names; present the plan, follow-ups, dispositions and the + approval sentence above. + +## 6. How this walk was read + +Read-only except this file and scratch. `` is lane 1's checkout of this repository and +`` is lane 1's private scratch. + +```sh +git -C fetch --all --prune # 20:30:48Z, 21:58:55Z, 22:07:06Z +git -C rev-parse --short origin/ +git -C show : # every document cited +git -C diff --stat 63810dd f87fd5a -- # empty +git -C ls-remote origin 'refs/heads/adopt/*' # empty +gh pr list --repo opensoft/openRepoTools --state open # #191 at 13cc752 only +gh issue view 186 --repo opensoft/openRepoTools --json body,comments # through 22:06:49Z +gh issue view 193 --repo opensoft/openRepoTools --json body +gh pr view {190,191,192} --repo opensoft/openRepoTools --json body,reviews,comments +gh api repos/opensoft/openRepoTools-{spec,code} # 404, 404 +gh api user; gh api user/memberships/orgs/opensoft # brettheap; admin, active +gh api repos/opensoft/openRepoShape/compare/7f84ca4...main # ahead 6, behind 0 +gh api repos/opensoft/openRepoShape/commits/ # files changed +gh api repos/opensoft/openRepoShape/pulls/162 # head 6fa4e07, merge 7f84ca4 +gh api repos/opensoft/openRepoShape/commits/{6fa4e07,7f84ca4}/check-runs +git clone -q --no-local --single-branch --branch main /main-clone +command -v openspec; which -a openspec; --version # 1.2.0 first, 1.6.0 second +(cd /main-clone && ~/.npm-global/bin/openspec validate migrate-to-triad --strict) # exit 1 +(cd /main-clone && validate migrate-to-triad --strict) # exit 0 +# the proposed text and the one-line join, each in a scratch copy: both CLIs exit 0 +git grep -n -i -E 'account[ -]?move' -- . +git grep -n -i account origin/main -- specs/004-migrate-to-triad openspec/changes/migrate-to-triad +``` From 3cca1e711f9d73cd191079b56847681173485a0a Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sat, 10 Oct 2026 02:44:29 +0000 Subject: [PATCH 27/42] Record the runs of #191's review rounds, the strict composed result at 6568f08, and CI on each head, in the installer evidence. The runs table had two rows still waiting at the first checkpoint. It now records what each review round changed and every proof, with which ran under the lock and which ran unlocked under the coordinator's rule for scoped proofs. It also records the strict composed run of root v3 with the head's whole code leg, where the only failure is the code leg's missing .gitattributes (T006), and CI per job on 6568f08. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../installer-2026-10-08.md | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/specs/004-migrate-to-triad/installer-2026-10-08.md b/specs/004-migrate-to-triad/installer-2026-10-08.md index 9ccebd2d..cb1a71a0 100644 --- a/specs/004-migrate-to-triad/installer-2026-10-08.md +++ b/specs/004-migrate-to-triad/installer-2026-10-08.md @@ -2,7 +2,7 @@ **Task:** T007 and T008 of [tasks.md](tasks.md), under [#186](https://github.com/opensoft/openRepoTools/issues/186). Neither task is checked complete by this record. -**Code:** [#191](https://github.com/opensoft/openRepoTools/pull/191), branch `feat/installer-payload-source-resolution` (head `2411f40`, based on `c4864ac`). +**Code:** [#191](https://github.com/opensoft/openRepoTools/pull/191), branch `feat/installer-payload-source-resolution` (head `6568f08`, based on `c4864ac` with main `f87fd5a` merged in as `45979a4`). **Assembly entry point:** [patches/t007-assembly-entry-point.patch](patches/t007-assembly-entry-point.patch). It cannot land on `main` before the split, because its path is the implementation's. @@ -141,7 +141,7 @@ The module is `WINDOWS_SKIP`, like every bash-driven test here. The composed tes ### Runs -Every local run used `tests/run.sh`'s own invocation under its lock: the coordinator's deviation for a census that never reaches zero, with `PATH=/usr/bin:$PATH` because this profile's `~/bin/python3` is a venv without pytest. +Locked runs used `tests/run.sh`'s own invocation under its lock. That is the coordinator's deviation for a census that never reaches zero. They ran with `PATH=/usr/bin:$PATH`, because this profile's `~/bin/python3` is a venv without pytest. Runs marked "unlocked" are scoped proofs of about two minutes, run under the coordinator's rule of 2026-10-09, with HOME, TMPDIR, `--basetemp` and `PYTHONPYCACHEPREFIX` all in the writer's scratch. | Run | Code | Selection | Result | | --- | --- | --- | --- | @@ -152,9 +152,19 @@ Every local run used `tests/run.sh`'s own invocation under its lock: the coordin | Composed, strict | entry point `a02a45c` + code `ecb4ef3` | the module | **90 passed, 0 skipped** | | Composed, strict, 2026-10-09T00:47–01:01Z | entry point `a02a45c` + code `d12345b` (= `ecd7aa4`) | module + install suites + `repo_hygiene` | **395 passed, 19 failed**. 17 `repo_hygiene` checks and `test_the_help_and_the_readme_say_the_mode_the_receipt_is_born_at` are T006 §10.6's front-door findings (T009's): they read root documents the code leg does not carry. The 19th was this change's. `test_without_jq_it_refuses_and_places_nothing` saw exit 2 but `line 734: awk: command not found` instead of "needs `jq`": the mounted-leg path read the gitlink with `awk`, which the test's minimal `PATH` lacks. Fixed in `2411f40` and in the entry point (`8ed83b7`) by reading it with `read`. | | Green with the `read` fix, 2026-10-09T01:05–01:20Z | `2411f40` | as "Green with hygiene" | **479 passed, 5 failed, 7 skipped**: the same five `lanes-edit.sh` cases, nothing else. | -| Composed, strict | entry point `8ed83b7` + code `ea17f30` (= `2411f40`) | module + install suites + `repo_hygiene` | Queued behind the shared lock at the checkpoint (2026-10-09T01:2xZ). The expected result is the 18 front-door findings only; to be recorded on resume. | -| CI on `ecd7aa4` | | | SonarCloud, `guard-launch-mode`, `parse-macos` and `tests-windows` passed; `tests` and `tests-no-submodule` were still running when `2411f40` superseded it. | -| CI on `2411f40` | | | Queued at the checkpoint; to be recorded on resume. | +| Composed, strict | entry point `8ed83b7` + code `ea17f30` (= `2411f40`) | module + install suites + `repo_hygiene` | Not completed. The 2026-10-09T01:3xZ account swap stopped it, and `6568f08`'s run below superseded it. | +| CI on `ecd7aa4` | | | All jobs green (run 37866464029). | +| CI on `2411f40` | | | All jobs green (run 37869321635). `tests-macos` skipped (no `ready`). | +| Review of #191 at `2411f40` | | | DO NOT LAND (review 5465121368). F1: on today's layout, the curl path needed api.github.com and `jq`. F2: an installed `wip init` refused at step 7 where base fell back to `main`. | +| F1/F2 fix | `13cc752` | module | 90 passed, 7 skipped. CI on `13cc752`: green. Lane 3's delta review: LAND (5471121210), with the stale body (delta 8) and the `ready` macOS run as conditions. | +| Round 2 | `fdc5f18`, `6fa3b43`, `6568f08` | | Dropped the `project.yaml` probe; bounded the assembly search (delta 4); the adopted pin refusal now says what answered (delta 6); r14 reads conftest's text (the `KeyError 'reason'` under #190's composed mode); a composed test for the root's gitlink check; merged main `f87fd5a`. | +| Module, under the lock | `fdc5f18` content | `-k payload_source` | **96 passed, 7 skipped** | +| New and changed tests × four installers, unlocked | `c4864ac` / `2411f40` / `13cc752` / `fdc5f18` | the 20 new and changed tests | **19 pass + 1 fail** (the adopted step-7 words, which are new behaviour) / **20 fail** / **16 pass + 4 fail** (the `project.yaml` request, the parent walk ×2, the partial refusal) / **20 pass** | +| Delta 6 and r14, unlocked | `6fa3b43` | what-answered, documented skip, malformed pins, gitlink pins | **23 passed** | +| Composed root gitlink test, strict, unlocked | root v2 `8ed83b7` / v3 `6acc147`, with code `fa460f2` | the module's composed tests | v2: the new test fails ×2. v3: **9 passed**. | +| Broader suites, under the lock | `6568f08` | `payload_source`, `openrepotools_command`, `wip_init`, `install_skill_and_hook`, `install_by_rename`, `repo_hygiene`, `upstream_pin`, `test_roots` | **516 passed, 9 skipped, 5 failed**. The 5 are the `lanes-edit.sh who` exit-8 cases on this workstation's lane state. That file and its test are outside the diff, and CI runs them green. | +| Composed, strict, under the lock, 2026-10-09T22:08–23:18Z | assembly `4c6a0b7` = root v3 `6acc147` + code `fa460f2` (this head's whole code leg) | module + install suites + `repo_hygiene` + `openrepotools_command` | **429 passed, 1 failed**. The failure is `test_the_root_carries_the_line_ending_rule`, which needs `.gitattributes` in the code leg; run C's legs carry none (T006 §10.2). After #190's roots, the 17 earlier front-door failures read the assembly root and pass. | +| CI on `6568f08` | | | Run 37997688693: `tests` 1416 passed, 9 skipped. `tests-no-submodule`: 1196 passed, 229 skipped. `tests-windows`: 177 passed. `guard-launch-mode`: 165 passed. `parse-macos`, SonarCloud and Sourcery: pass. `tests-macos` waits on `ready`. | ## Lane 2's installer rows From 16e6ea425a13a059e8ed99dc2b38f2e370415653 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sat, 10 Oct 2026 23:47:44 +0000 Subject: [PATCH 28/42] Record the r2 candidate's three suite runs and classify every failure: two candidate defects with owners (the r14 KeyError fixed in #191, #193 item 14) and four environment classes, each shown the same on main or in isolation. r2 is code 4dbcf6c, spec 9e1d006 and assembly 5d92962, built from #190 at b50f197 and #191 at 13cc752. Composed: 1411 passed, 5 failed, 0 skipped. Standalone: 1389 passed, 3 failed, 24 skipped. No submodule: 1168 passed, 6 failed, 242 skipped. The install from the assembly root differs from main's only in the implementation it hands over to. The composed rerun at #191's final head 6568f08 with T007's v3 entry point is still owed. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../candidate-2026-10-09.md | 355 ++++++++++++++---- 1 file changed, 282 insertions(+), 73 deletions(-) diff --git a/specs/004-migrate-to-triad/candidate-2026-10-09.md b/specs/004-migrate-to-triad/candidate-2026-10-09.md index 23e2d390..c2c61779 100644 --- a/specs/004-migrate-to-triad/candidate-2026-10-09.md +++ b/specs/004-migrate-to-triad/candidate-2026-10-09.md @@ -5,14 +5,34 @@ section 7 of `gate-ab-evidence-2026-10-09.md` (branch `004-migrate-to-triad-rollback` at `fa44d75`): "No composed candidate carrying every follow-up has run the suite." No task is checked complete by this record, and it approves nothing. -**Writer:** lane openRepoTools-1, 2026-10-09 from 01:47Z. +**Writer:** lane openRepoTools-1, 2026-10-09: 01:47Z–02:42Z (r1), 13:29Z–18:20Z (r2), and +the classification from 21:56Z to 2026-10-10T02:4xZ. **Evidence branch:** `004-migrate-to-triad-candidate`, from `main` `63810dd`. **Private receipt ID:** `candidate-20261009-JR8YyA` (brett-wip `migration/openRepoTools/`). It holds every script, log, patch and install snapshot named below. Machine paths stay there. -**State: checkpoint (02:42Z).** The candidate is built and its install is compared. No -suite run has finished yet; section 5 says where each one stands. +**Two builds.** **r1** was built at 01:5xZ from PR #190 `dbc6837` and PR #191 `2411f40`. +Its composed run was stopped at about 20% by the 02:42Z checkpoint for an account swap. +**r2** is the candidate of record. It was rebuilt at 13:3xZ from the moved heads, #190 +`b50f197` and #191 `13cc752`, on the coordinator's order. Everything below is r2's unless +it says r1. + +**State: final for r2 (2026-10-10T02:4xZ).** All three suite runs finished: + +| Run | Passed | Failed | Skipped | +| --- | --- | --- | --- | +| composed | 1411 | 5 | 0 | +| standalone | 1389 | 3 | 24 | +| no-submodule | 1168 | 6 | 242 | + +- **Two failures are candidate defects with owners.** F1 is fixed in #191's next head. + F2 is #193 item 14. +- **The rest are environment.** Each is shown the same on main, or in isolation + (section 5). +- **Two inputs have moved since r2:** #191 to `6568f08` (with F1's fix), and T007's + entry-point patch to v3 (`004-migrate-to-triad-installer` `4f773b8`). Neither is in + this record (section 6). ## 1. Inputs @@ -24,16 +44,19 @@ the shared checkout. | --- | --- | --- | | Run C's rehearsed arrangement | private prep `work-20261008/rehearsal` (read-only) | source `837928a`, split `69b8924`, spec `593250d`, code `73b04d6`, nested openRepoShape `7f84ca4` | | T006 patches (5) | `004-migrate-to-triad-root-guidance` `041a273` | each blob equals `465fa6d`'s; sha256 `cd69c2a9…`, `574c0df1…`, `a7f8521b…`, `74b98ff9…`, `c11070f0…`, as `gate-ab-evidence` records | -| T010 patches (2) | `004-migrate-to-triad-ci` `cc7c7a1` | code `tests.yml` `7ee3c6d0…`; assembly `composed.yml` `6ee09f00…` | +| T010 patches (2) | `004-migrate-to-triad-ci` `cc7c7a1` (r1); the branch is at `d28e2cc` for r2, with both patch blobs unchanged | code `tests.yml` `7ee3c6d0…`; assembly `composed.yml` `6ee09f00…` | | T007 entry point | `004-migrate-to-triad-installer` `51993a4` | `t007-assembly-entry-point.patch` `361c037d…`, as `installer-2026-10-08.md` records | -| PR #190 | `feat/test-roots-for-the-triad` `dbc6837` (the head at 01:45Z) | 3 commits on `c4864ac`: `c139e88`, `e3a76b5`, `dbc6837` | -| PR #191 | `feat/installer-payload-source-resolution` `2411f40` (the head at 01:45Z) | 4 commits on `c4864ac`: `6337a52`, `e406e0e`, `ecd7aa4`, `2411f40` | +| PR #190 | r2: `b50f197` (on origin as `feat/test-roots-manual-guard`, a fast-forward of the PR head). r1: `dbc6837` | r2: 4 commits on `c4864ac`: `c139e88`, `e3a76b5`, `dbc6837`, `b50f197` | +| PR #191 | r2: `13cc752`, Linux CI green. r1: `2411f40` | r2: 5 commits on `c4864ac`: `6337a52`, `e406e0e`, `ecd7aa4`, `2411f40`, `13cc752` | | The shape's `bump-leg.py` | a clone of the rehearsal's openRepoShape mirror, in scratch | `7f84ca4` | `assembly-0002` and `assembly-0003` were **not applied**. As `root-guidance-2026-10-08.md` says, they pin the rehearsal's own leg hashes; the two pins were regenerated here with `bump-leg.py` against this candidate's legs. +#191 will move again in T007's fix cycle. This record is at `13cc752`; a later head +needs a delta rerun. + ## 2. How it was built All of it ran in private scratch (`work-20261008/ort-triad-candidate/`): @@ -42,61 +65,63 @@ All of it ran in private scratch (`work-20261008/ort-triad-candidate/`): remotes, of its assembly branch and of its openRepoShape mirror. Every write went to these copies. In every clone the submodule URLs were set to them before any commit, and a `url..insteadOf` rule sent `.gitmodules`' run C paths and the nested GitHub URL - to the copies. + to the copies. r2 has its own set of copies, so r1's commits stay where they were. - **No network.** `PATH` began with refusing `gh`, `curl` and `wget` shims that log every call; `GIT_ALLOW_PROTOCOL=file`. The shim log is empty. - **Run C unchanged.** A listing of the rehearsal directory (type, mode, size, mtime of every entry) was taken before the work and compared after it (section 8). -### Apply order and results +### Apply order and results (r2) -Every patch was applied with `git am`. **Every one applied cleanly; no hunk needed a -resolution, and none was dropped.** +Every patch was applied with `git am`. **Every one exited 0; no hunk needed a resolution, +and none was dropped.** | # | Leg | Patch | `git am` | Commit | Tree | | --- | --- | --- | --- | --- | --- | -| 1–3 | code, on `73b04d6` | PR #190's three commits | exit 0 | `a70ecad`, `66a191c`, `8377c56` | `b528173e` after the third | -| 4–7 | code | PR #191's four commits | exit 0 | `36d5ccc`, `80b2119`, `edb902f`, `de49a91` | `66c636e9` after the fourth | -| 8 | code | T006 `code-0001` | exit 0 | `d1ec0b5` | `15014778` | -| 9 | code | T010 `t010-code-leg-tests-workflow.patch` | exit 0 | **`f0af7d9`** | **`a832c8a0`** | -| 10 | spec, on `593250d` | T006 `spec-0001` | exit 0 | **`9e1d006`** | **`10256135`**, the tree T006 and T011 recorded | -| 11 | assembly, on `69b8924` | T006 `assembly-0001` | exit 0 | `4f1e522` | `5060f3be` | -| 12 | assembly | T010 `t010-assembly-exact-pin-job.patch` (`composed.yml`) | exit 0 | `738857c` | `7905d570` | -| 13 | assembly | T007 `t007-assembly-entry-point.patch` | exit 0 | `f4133d6` | `8b0c860b` | -| 14 | assembly | `bump-leg.py --leg spec --to 9e1d006…` | exit 0 | `f0a4feb` | `94e9d9be` | -| 15 | assembly | `bump-leg.py --leg code --to f0af7d9…` | exit 0 | **`3bd95a7`** | **`6191142c`** | +| 1–4 | code, on `73b04d6` | PR #190's four commits | exit 0 | `b112e01`, `4ee37cf`, `e80350e`, `6e294fe` | `08d5b4ca` after the fourth | +| 5–9 | code | PR #191's five commits | exit 0 | `eab478f`, `85e6ffb`, `5758859`, `69b4785`, `b52df0a` | `cceeb37d` after the fifth | +| 10 | code | T006 `code-0001` | exit 0 | `085ec81` | `c3437ded` | +| 11 | code | T010 `t010-code-leg-tests-workflow.patch` | exit 0 | **`4dbcf6c`** | **`c0bedc53`** | +| 12 | spec, on `593250d` | T006 `spec-0001` (r1's commit; no spec input moved) | exit 0 | **`9e1d006`** | **`10256135`**, the tree T006 and T011 recorded | +| 13 | assembly, on `69b8924` | T006 `assembly-0001` | exit 0 | `4f1e522` | `5060f3be` | +| 14 | assembly | T010 `t010-assembly-exact-pin-job.patch` (`composed.yml`) | exit 0 | `738857c` | `7905d570` | +| 15 | assembly | T007 `t007-assembly-entry-point.patch` | exit 0 | `f4133d6` | `8b0c860b` | +| 16 | assembly | `bump-leg.py --leg spec --to 9e1d006…` | exit 0 | `f0a4feb` | `94e9d9be` | +| 17 | assembly | `bump-leg.py --leg code --to 4dbcf6c…` | exit 0 | **`5d92962`** | **`d263907a`** | + +Steps 12–16 are r1's commits, reused: none of their inputs moved. Step 17 is new. What was checked at each step: -- **The PR commits are the PR heads.** Every path PR #190 touches has `dbc6837`'s blob - after step 3: 8 of 8. After step 7, `openRepoTools` (`04769c4`) and - `tests/test_install_payload_source.py` have `2411f40`'s blobs. -- **The one shared file.** Both PRs touch `tests/test_openrepotools_command.py`. On top - of #190, #191 applied the same two hunks as its own diff, three lines lower. The hunk - bodies are identical (26 lines). +- **The PR commits are the PR heads.** Every path #190 touches has `b50f197`'s blob after + the stack, and every path #191 touches has `13cc752`'s blob. The exception is the one + file both touch. +- **The one shared file.** Both PRs touch `tests/test_openrepotools_command.py`. On top of + #190, #191 applied the same hunks as its own diff, at an offset. The hunk bodies are + identical: 14 lines at `13cc752`, and 26 at r1's `2411f40`. - **T006's `.gitattributes` and #190's decision agree.** #190 reads `.gitattributes` from - the code root; `code-0001` puts the monorepo's blob `3560973` there. `git check-attr` - on `openRepoTools`, `park` and `tests/run.sh` in the leg gives `text: auto`, - `eol: lf`. + the code root, and `code-0001` puts the monorepo's blob `3560973` there. + `git check-attr eol` on `openRepoTools`, `park` and `tests/run.sh` in the leg gives + `lf`. - **The entry point is T007's.** `openRepoTools` at the assembly root is blob `4c2525d`, mode 100755, sha256 `3b0d59f5…`, as `installer-2026-10-08.md` records. -- **Clean text.** `git diff --check` exits 0 over the code range (`73b04d6..f0af7d9`), the - spec range and the assembly range (`69b8924..f4133d6`). +- **Clean text.** `git diff --check` exits 0 over the code range (`73b04d6..4dbcf6c`), the + spec range and the assembly range. -The legs' candidate commits were pushed to `main` of their own bare copies, as -fast-forwards from run C's heads. The assembly was pushed as branch `candidate` to its -bare copy. `bump-leg.py` ran with `--local-remote-dir` set to the copies, with a dry run -first for each leg: exit 0 ×4. It added `Lane:` and `Co-Authored-By:` trailers. Its -`NEXT … push` line was not followed. +The legs' commits were pushed to `main` of their own bare copies, as fast-forwards from +run C's heads. The assembly was pushed as branch `candidate-r2`. `bump-leg.py` ran with +`--local-remote-dir` set to the copies, a dry run first each time, all exit 0. It added +`Lane:` and `Co-Authored-By:` trailers. Its `NEXT … push` line was not followed. ### The candidate's identities | Repository | Commit | Tree | Pinned digest (`sorted-ls-tree-r-v1`) | | --- | --- | --- | --- | -| assembly (`candidate`) | `3bd95a7a57d0c1eb4d2e339ab34007756938b6a5` | `6191142cb360a71132e2783a9cadf0ce36374ab2` | — | +| assembly (`candidate-r2`) | `5d9296216fbf8255440c315344f29ac2aa1c19f8` | `d263907a4ab2b675c9b5272a4c60ffb2ea65ce12` | — | | spec leg | `9e1d006e2c870615f159ad1ebab719bb7b38cc2a` | `10256135f87ae4d05166d2c5934a152c42ce19ae` | `3a4a84beb20b343390f5bae64cb166df4d0cfd693baff6309fc13c8060d1c866` (T006's spec digest) | -| code leg | `f0af7d9be42d0e5fbfa9a220b6ab6805d81e804d` | `a832c8a058d6dab7bf61f3b7ce469d7b911851ea` | `6550ae2e44d1f78368bdb82f2416dff4828943efb7cfd8a896d1693911e14d95` | +| code leg | `4dbcf6ce40fe897a8caf770abc5cf55535b5f0b6` | `c0bedc535b4c69afc32e6235140a874c05821c3a` | `7a5dc958db2ea74213f604b883bc2bfd374960015313bd4de24494965ccc32a6` | | code's nested openRepoShape | `7f84ca42ca86a8902928345109d2bf6bad87bd91` | — | unchanged from run C | +| r1, superseded | assembly `3bd95a7`, code `f0af7d9` (tree `a832c8a0`, digest `6550ae2e…`), spec `9e1d006` | | | The commit hashes are this run's alone, because `git am` stamps a new committer and date. The trees are what reproduce. @@ -105,11 +130,12 @@ The trees are what reproduce. | Check (in the assembly) | Result | | --- | --- | -| `python3 scripts/bootstrap.py` | exit 0; "spec: on main at 9e1d006e2c87 (branch tip == pin)", "code: on main at f0af7d9be42d (branch tip == pin)", `pins ok`, `manifest ok`, "bootstrap ok" | +| `python3 scripts/bootstrap.py` | exit 0; "spec: on main at 9e1d006e2c87 (branch tip == pin)", "code: on main at 4dbcf6ce40fe (branch tip == pin)", `pins ok`, `manifest ok`, "bootstrap ok" | | `make validate` (naming, manifest, pins) and `make pins` | exit 0 and 0. Both gitlinks equal their pins, both tree digests recompute, and all 11 copied shape files match `contracts/shape-pin.yaml` | | `composed.yml`'s step "every context the composed run claims is here", run as written | exit 0 | | `composed.yml`'s `validate-manifest.py`, then `validate-pins.py` | exit 0, exit 0 | -| A fresh recursive clone of the candidate from its bare copy, then `bootstrap.py` and `make validate` | exit 0 and 0. Spec `9e1d006`, code `f0af7d9`, nested `7f84ca4`. `status --ignored` is empty | +| A fresh recursive clone of the candidate from its bare copy, then `bootstrap.py` and `make validate` | exit 0 and 0. Spec `9e1d006`, code `4dbcf6c`, nested `7f84ca4`. `status --ignored` is empty | +| The assembly's `AGENTS.md` and `README.md` | 340 and 510 lines, exactly #190's assembly caps | `.gitmodules` still names run C's local remote paths, as the adopter wrote them in the rehearsal. A recursive clone that does not redirect them fetches from run C, whose @@ -125,22 +151,22 @@ absolute paths written into `settings.json` and the receipt compare byte for byt - **The seed:** an unrelated `settings.json` (model, env, permissions, a status line, and `SessionStart`, `UserPromptSubmit` and `Stop` hooks of its own), a foreign command in `~/.local/bin`, and a foreign skill under each of the two skill roots. -- **The shims logged 0 calls.** +- **The shims logged 0 calls**, in r1 and in r2. -| Stage | Source | Result | +| Stage | Source | Result (r2, 13:34Z) | | --- | --- | --- | | A | today's main `63810dd` (a shallow clone of `origin/main`), `./openRepoTools --install` | exit 0; `17 of 17 placed`; 29 `installed at`; both hooks merged; 29 receipt rows | -| B | a freshly seeded home; the candidate assembly root `./openRepoTools --install` (the T007 entry point) | exit 0. First line: "source: the code mounted at `/code`, opensoft/openRepoTools-code at f0af7d9be42d… as `` pins it; every payload file was checked against that commit and nothing fetched". Then `17 of 17 placed`, 29 `installed at`, both hooks merged, 29 receipt rows | +| B | a freshly seeded home; the candidate assembly root `./openRepoTools --install` (the T007 entry point) | exit 0. First line: "source: the code mounted at `/code`, opensoft/openRepoTools-code at 4dbcf6ce40fe… as `` pins it; every payload file was checked against that commit and nothing fetched". Then `17 of 17 placed`, 29 `installed at`, both hooks merged, 29 receipt rows | | B2 | the candidate again, over B | exit 0; 31 `unchanged` (29 files and 2 hooks) | | C | today's main again, over B: rollback by reinstall | exit 0; `openRepoTools: updated at …`, 30 `unchanged` | | — | the candidate's `./openRepoTools --version`; `./openRepoTools nope` | "openRepoTools (opensoft/openRepoTools @ main)", exit 0; exit 2 | **Every difference between A and B**, by path, type, mode and sha256 over the whole home: -| Path | A (main) | B (candidate) | +| Path | A (main) | B (candidate r2) | | --- | --- | --- | -| `~/.local/bin/openRepoTools` | 755 `47cfe74ea96e6ce9e56b47220a71e1de8c608af2f51f33cfdffa351410a4f373` | 755 `118cc2ccd376456e51f47d36f5fc9604a79fa0be285be9982d2304c0304c0f8f` | -| `~/.local/share/openRepoTools/installed.tsv` | 600, its `openRepoTools` row names `47cfe74e…` | 600, its row names `118cc2cc…`; the UTC column differs too | +| `~/.local/bin/openRepoTools` | 755 `47cfe74ea96e6ce9e56b47220a71e1de8c608af2f51f33cfdffa351410a4f373` | 755 `a6eda1f4b7b72a8e832f93185dc4604de66dab5a677472f57452b3ee09342588` | +| `~/.local/share/openRepoTools/installed.tsv` | 600, its `openRepoTools` row names `47cfe74e…` | 600, its row names `a6eda1f4…`; the UTC column differs too | Nothing else differs: - **The other 28 placed files** have the same path, mode and sha256. @@ -150,9 +176,11 @@ Nothing else differs: row only. - **The foreign command and both foreign skills** are untouched. -The installed `openRepoTools` (`118cc2cc…`) is the bytes of `code/openRepoTools` -(PR #191's implementation, blob `04769c4`). It is not the root entry point (`3b0d59f5…`), -so the entry point handed over as T007 designed. +The installed `openRepoTools` (`a6eda1f4…`) is the bytes of `code/openRepoTools`, PR #191's +implementation at `13cc752`. It is not the root entry point (`3b0d59f5…`), so the entry +point handed over as T007 designed. r1 gave the same result with `2411f40`'s +implementation, `118cc2cc…`; between r1's and r2's candidate installs, that file and its +receipt row are the only differences. **Rollback by reinstall (C) restores main.** C's whole-home listing equals A's except for the receipt file's own digest. The receipt's name, destination and sha256 columns equal @@ -161,39 +189,220 @@ section 2, repeated at this candidate's own commits, as its section 2 asked. ## 5. The suite: composed, standalone, and standalone without the dependency -### State at checkpoint (2026-10-09T02:42Z) - -**No suite run has finished. All three must be run, the composed one again from the -start.** - -- **Composed.** `OPENREPOTOOLS_COMPOSED=1`, with the assembly and spec roots named. It - took the workstation lock at 02:10:40Z, after `tests/run.sh` had waited 5 minutes on - the census. By 02:42:37Z it had reached about 20%: the lane helpers' shell suite was - running, and one non-pass result was printed near 15%. The coordinator then called a - checkpoint for an account swap. This writer stopped its own run (one process group, - TERM, nothing left), released the lock with it, and removed the run's own run root. - The partial log is kept in the receipt. It is not a result. -- **Standalone and no-submodule.** Not started. - -For comparison when they run, the CI numbers already read: - -| Where | `tests` | `tests-no-submodule` | -| --- | --- | --- | -| main `63810dd` (run 37866269342) | 1296 passed | 1084 passed, 212 skipped | -| PR #190 `dbc6837` (run 37869489919) | 1318 passed | 1105 passed, 213 skipped | -| PR #191 `2411f40` (run 37869321635) | 1379 passed, 7 skipped | 1165 passed, 221 skipped | +**How the runs were made.** The three runs ran one after another, 13:33Z–18:20Z, from one +driver script. Each took the workstation lock on its own and released it before the next. +- **Environment:** `env -i` with only `HOME`, `PATH=/usr/bin:/bin`, `LANG=C.UTF-8`, the + user names and the CI jobs' four `GIT_AUTHOR_*`/`GIT_COMMITTER_*` variables. So no + `LANES_*`, `TMUX`, `CLAUDE_*` or workstation binding variable reached the suite. +- **Composed.** `tests/run.sh -rfEs` waited 5 minutes on the workstation census. As + briefed, this writer then stopped its own waiter and ran `run.sh`'s exact pytest line + under `flock` on the same lock, with `run.sh`'s run root. +- **Standalone and no-submodule.** `tests/run.sh -rfEs` itself started pytest in each. +- **The load average at each run's start** was 131.7 (composed), 6.9 (standalone) and + 8.6 (no-submodule). + +| Run | Where, and the variables | Result | Time | +| --- | --- | --- | --- | +| **Composed** | `/code`; `OPENREPOTOOLS_COMPOSED=1`, `OPENREPOTOOLS_ASSEMBLY_ROOT=`, `OPENREPOTOOLS_SPEC_ROOT=/spec` | **1411 passed, 5 failed, 0 skipped** (1416) | 1:48:00 | +| **Standalone** | a clone of the code leg alone, its dependency initialized; no `OPENREPOTOOLS_*` variable | **1389 passed, 3 failed, 24 skipped** (1416) | 1:00:08 | +| **No submodule** | a clone of the code leg alone, dependency not initialized ("the submodule really is not materialized", the job's own check); `tests.yml`'s workflow env, `OPENREPOTOOLS_CODE_ROOT=` and `OPENREPOTOOLS_COMPOSED=0` | **1168 passed, 6 failed, 242 skipped** (1416) | 1:31:30 | + +**The skips are the designed ones.** The composed run skipped nothing, so no check went +green by skipping. The standalone run's 24 skips are: +- 10 for `README.md`, 3 for `AGENTS.md` and 1 for `CLAUDE.md`, each naming the assembly + root it needs; +- 3 for `docs/README-lanes.md`, naming the spec root; +- 7 composed-acceptance tests of #191, which skip in standalone code. + +The no-submodule run has the same 24, plus 218 that name +`git submodule update --init upstream/openRepoShape`. Inside the lane helpers' shell suite +in both leg runs, the three manual sections printed `skip … no spec root in this run`. +There is no `unbound variable` anywhere: `b50f197`'s guard holds, and the abort that +`dbc6837` had in a code leg does not occur. + +**Against CI.** All three runs collected 1416 tests: main's 1296 plus #190's and #191's +additions. On CI the PR heads read: +- main `63810dd`: `tests` 1296 passed; `tests-no-submodule` 1084 passed, 212 skipped; +- #190 `dbc6837`: 1318 passed; 1105 passed, 213 skipped; +- #191 `2411f40`: 1379 passed, 7 skipped; 1165 passed, 221 skipped. + +None of those jobs is composed. + +### Every failure, classified + +| # | Test | Runs | First assertion line | Class | Owner | +| --- | --- | --- | --- | --- | --- | +| F1 | `test_install_payload_source.py::test_r14_the_documented_upstream_skip_is_unchanged` | composed only | `KeyError: 'reason'` at `conftest.NEEDS_UPSTREAM.kwargs["reason"]` (line 1697) | **Candidate defect, new, from combining #190 and #191.** Under #190's conftest a composed run makes `NEEDS_UPSTREAM` `pytest.mark.usefixtures("required_dependency")`, a fixture that fails, not a `skipif`, so it has no `reason`. #191's test assumed a `skipif`. It passes standalone (both leg runs) | **#191.** Fixed by the T007 writer in `6fa3b43`, inside #191's head `6568f08`: the test now reads the command from conftest's own text, which names it in both modes. Not re-run here | +| F2 | `test_repo_hygiene.py::test_agents_md_names_the_pin_rules` | composed only | `assert 'Never edit anything under \`upstream/openRepoShape\` in place' in "Read AGENTS-shape.md first …"` | **Candidate defect, known: #193 item 14**, predicted by lane 3. T006's `assembly-0001` rewrites the two `AGENTS.md` lines the test checks, and in the composed run the test reads the assembly's `AGENTS.md` | the decision is pending with lane 1's coordinator | +| F3 | `test_lane_helpers_suite.py::test_the_lane_helper_suite_passes`: shell `FAIL …with clause (c)'s directory and window in the tail` and `FAIL the row's stamp states clause (c)'s two facts for a person, in its TAIL` | all three | `expected to contain [; dir /projects/repoA; window ]`, the sandbox HOME being `/tmp/tmp.*/home`; the row ends `…; dir /projects/repoA; ... \|` | **Environment, pre-existing: #180.** The register row's stamp is cut, and under `run.sh` the directory sits under the long run root `~/.local/state/openRepoTools/tmp/-/tmp`, so the expected text falls past the cut. **On main `63810dd` the same three FAIL.** The shell suite ran directly, at a run root of the same 71 characters as the standalone run's, under one `flock`: `4595 passed, 3 failed, 0 skipped`, F3's two and F4. CI's runner has a short `TMPDIR` and passes them. | #180 | +| F4 | the same shell suite: `FAIL …having found the checkout by its real directory name, not the typed one` | standalone and no-submodule only | `expected to contain [dir /projects/repoCase;]`; the row ends `— lane-start on Eagle: window renamed, no launch; dir ... \|` | **Environment, pre-existing: #180, the same cut.** The run root here was one character longer than the composed run's (`` had 7 digits, against 6), and this third row crosses the cut at that length | #180 | +| F5 | `test_run_wrapper.py::test_a_killed_run_removes_its_run_root_and_stops_the_suite[flock-2-130]` and `[mkdir-lock-2-130]` | all three | `subprocess.TimeoutExpired: … tests/run.sh … timed out after 30 seconds` | **Environment, of this writer's harness, and not load.** The driver was launched as an asynchronous command of a non-interactive shell, so every run inherited SIGINT *ignored*, and bash cannot trap a signal ignored at entry. Only the SIGINT cases failed; the SIGTERM cases passed. Measured isolated at load 42–61: with SIGINT at its default, 4 passed in 1.9 s; launched the way the runs were, the same 2 failed with the same timeout. Main `63810dd` behaves identically (4 passed, then 2 failed) | none for the candidate. The test could start its wrapper with both signals at default, as `test_openrepotools_command.py::test_a_run_killed_mid_placement_leaves_no_temporary` already does (`preexec_fn=default_signals`, whose docstring names this very trap) (follow-up 4) | +| F6 | `test_supervised_restart_regressions.py::test_restart_observer_confirms_late_uuid_prepared_by_real_child[live]`, `::test_respawn_restores_configured_roots_in_new_pane`, `::test_completed_observer_cleanup_never_signals_saved_pid[signal]` | no-submodule only | `subprocess.TimeoutExpired: … lane-handoff --supervise …` / `Failed: observer never confirmed readiness` | **Environment: timing under load.** These files and `lane-handoff` are byte-identical to main's. All three passed in the composed and standalone runs of the same code. Rerun isolated at load 48–54: all three passed, once with SIGINT at default and once ignored. But a sibling, `test_completed_observer_cleanup_never_signals_saved_pid[exit]`, failed once in that rerun. So these tests are timing-sensitive on this bench, independent of the candidate | none for the candidate; the flake is pre-existing (follow-up 5) | + +**Lane 2's reading** (#186 comment 6087267593) **checked against the logs.** Its counts are +right for all three runs, and so is its S2 finding (no abort on r2). One refinement and +one correction: +- **The lane-suite FAILs** are all #180 row-stamp cases, as lane 2 says. They are 2 in the + composed run and 3 in each leg run; the third is F4. +- **The `test_run_wrapper` pair** is not a load timeout. It is the inherited ignored SIGINT + of F5, and it fails the same way on main. + +**One other test sends SIGINT:** `test_a_run_killed_mid_placement_leaves_no_temporary`. +It resets both signals in its child for exactly this reason, and it failed in none of the +three runs. + +### r1's stopped composed run + +It reached result 288 of 1408 before the 02:42Z checkpoint stopped it. Its one failure, at +result 211, was F1. That test alone, rerun in r1's tree, failed the same way. So it was +not the `$ln_doc` abort, which only a code leg without a spec root can reach; the composed +run has one. ## 6. What this proves for Gate B, and what it does not -To be written with section 5. +**What it proves:** +- **Every recorded follow-up composes.** T006's three portable patches, T010's two, T007's + entry point, #190 at `b50f197` and #191 at `13cc752` stack onto run C with no conflict + and no resolution. That is the stack `gate-ab-evidence` section 5 only checked as + text. +- **The result is a valid triad.** It bootstraps and validates: legs at their pins, both + digests recomputed, all 11 copied shape files intact, and `composed.yml`'s own + context and validator steps green. +- **The whole canonical suite has run on it.** + - Composed at the exact pins, nothing skipped: **1411 of 1416 pass**. + - Of the 5 failures, 2 are candidate defects with owners. F1's fix is in #191's head + `6568f08`. F2 is #193 item 14, with a decision pending. + - The other 3 are this bench's environment. F3 is #180, the same on main (section 5). + F5 is this writer's harness, the same on main. +- **The standalone code leg behaves as T009/T010 designed.** Its document checks skip, + naming the root they need. Its dependency checks skip, naming the command. Nothing + aborts. It has no failure of its own: its 3 and 6 failures are F3–F6, all environment. +- **The install from the assembly root hands over to the code leg.** It places the same + 29 files as today's main, every one byte-identical except the implementation itself, + and merges the same hooks into a byte-identical `settings.json`. A reinstall from main + restores main. + +**What it does not prove:** +- **Hosted CI.** Not the code leg's `tests`, `tests-no-submodule`, `tests-windows`, + `parse-macos` or `tests-macos` jobs, and not `composed.yml` checking out over https. + They need the real legs, which only exist after Gate C (checklist line 35). +- **The real legs and remotes.** This used local bare copies: https `.gitmodules`, + `bump-leg.py` against the real legs, and the raw and `gh` one-liners against GitHub + are T015's. +- **The final heads.** #191 is now `6568f08`, which carries F1's fix (`6fa3b43`) and + changes the implementation `openRepoTools` (159 changed lines since `13cc752`). + T007's entry-point patch is now v3 (`4f773b8`). #190 landed as `f87fd5a`, whose + `tests/` equal `b50f197`'s, so that leg input is unchanged. + - Neither moved input ran here: not the suite, and not the install comparison. + - The T007 writer did run part of it (`installer-2026-10-08.md` at `3cca1e7`). + - Its strict composed run of the install suites, `repo_hygiene` and + `openrepotools_command` at `6568f08` with root v3: 429 passed, 1 failed. The one + is `.gitattributes`: that code leg did not carry T006's `code-0001`. + - CI on `6568f08`: `tests` 1416 passed, 9 skipped. + - Neither is this candidate, and neither is the full composed suite. + - The record owes a composed rerun at `6568f08` with T007 v3. The rebuild takes about 5 + minutes: the code leg plus a regenerated code bump, and the assembly with the v3 + patch in place of v1. + - The full composed run took 1:48 at load 130 here. CI's `tests` takes 45–48 minutes. + Add the wait for the lock. + - A scoped composed rerun of the install modules (`test_install_payload_source.py`, + `test_openrepotools_command.py`, `test_install_*`), plus the install comparison, is + about 15–20 minutes. +- **A clean composed green.** Gate B's "passing compatibility test matrix" is not met + until the composed rerun at `6568f08` shows F1 gone and F2 is decided. F3 and F5 do not occur on a CI + runner (main's own `tests` is 1296 of 1296 there). +- **The bash 3.2 parse of the root entry point.** `parse-macos` parses the + implementation, not the entry point. ## 7. Follow-ups -To be written with section 5. +1. **#191 carries F1's fix** (`6fa3b43`, in its head `6568f08`: the r14 test reads + conftest's text). The composed rerun at `6568f08` with T007 v3 is owed (section 6). +2. **F2, #193 item 14:** decide whether `test_agents_md_names_the_pin_rules` or T006's + `assembly-0001` gives, before the composed run is the record. +3. **#180:** under `tests/run.sh`'s run root the row-stamp cases fail locally. There are 2 + at a 70-character run root and 3 at 71, the third being F4. CI is unaffected. Today + any local full run is red on them, main included. +4. **`test_run_wrapper.py`:** start the wrapper with SIGINT and SIGTERM at their defaults, + as `test_openrepotools_command.py` does. Otherwise a suite launched from a background + shell reports two false failures (F5), on main too. +5. **`test_supervised_restart_regressions.py`'s observer tests are timing-sensitive** on a + loaded bench (F6). They are pre-existing and unchanged by the candidate. +6. **Launch any rerun of this candidate with SIGINT at default (F5).** +7. **The rehearsed split's `.gitmodules` names run C's local remote paths.** A real + adoption writes https URLs, so nothing is owed unless the rehearsal is reused for + another candidate. ## 8. Acts outside scratch, and log SHA256s -To be written at the end of the run. +**Every write went to private scratch** (`work-20261008/ort-triad-candidate/`), with these +exceptions, listed so nothing is hidden: +- **The evidence worktree.** `lane-worktrees add openRepoTools-1 ort-triad-candidate` + made it (exit 0): the worktree, its new branch and one record in lane 1's host-local + tree inventory. +- **This branch.** Its commits, and fast-forward pushes of `004-migrate-to-triad-candidate`. +- **In the shared checkout, reads only.** Fetches (remote-tracking refs only), + `git show`/`format-patch` reads, and one shallow `file://` clone of `main` from its + object store. +- **`gh`, reads only.** PR views, run lists, CI job logs, and issues #180, #184 and #193. +- **`tests/run.sh`'s own conventions.** + - Run roots under `~/.local/state/openRepoTools/tmp/-/`, each removed when + its run ended. The r1 composed run's root was removed by hand after this writer + stopped that run at the 02:42Z checkpoint. + - Bytecode under `~/.cache/openRepoTools/pycache`. +- **The workstation lock.** This writer held `/tmp/openrepotools-pytest.lock`: + - r1 composed, 02:10:40–02:42:37Z (stopped); + - r2 composed, 13:59:33–15:47:49Z; + - through `tests/run.sh`, the r2 standalone and no-submodule runs, 15:4xZ–18:20Z; + - main's lane suite at the same run-root length, about 22:07Z to 23:09:57Z. + + It was released between runs. The isolated reruns of F5 and F6 (2 seconds to 2 + minutes 16 seconds each) ran without it, under the coordinator's rule for runs of about + two minutes. The r1 test collection and F1's single-test reproduction took it with + `flock -w 600`. +- **A stray file moved at once.** A temporary file was written into the private + `work-20261008/` directory by mistake, outside the rehearsal tree, and moved into scratch + within a minute. +- **The receipt and the comment.** One commit to brett-wip `main` (`git add -- `), from a single-branch clone in scratch, never the shared checkout. One comment + on #186. + +**Real surfaces, before (2026-10-09T01:59Z) and after (2026-10-10T02:44Z):** +- the real home's 29 installed files (path, mode, sha256), its receipt and its + `settings.json`: **unchanged**; +- `~/.agents/workspace.yaml` (size, inode, mtime, mode; its contents not read): + **unchanged**; +- run C's rehearsal directory, as a whole-tree listing (type, mode, size, mtime of 1830 + entries): **byte-identical** before (2026-10-09T01:47Z) and after (2026-10-10T02:44Z). + +**No network:** the refusing shims logged no call in any build, install or suite step. +Nothing was pushed to run C. No real repository was created, and the register was not +written. + +| Log (in the receipt's `logs/`) | SHA256 | +| --- | --- | +| `41-r2-code-leg.log` | `053af3830c118885238f1f50dfc2ff50bf29c2f4ac9db9cc6d539f407633d12b` | +| `42-r2-assembly.log` | `7d8f086587e63e4a78c93cc2db5d1f4996aab4a983a28e0ab389d4265c7ad272` | +| `43-r2-clones-and-steps.log` | `295db90bcfe12c626919d0a49710d8e98e080851997deab68853d80bf03bd59b` | +| `run-51-r2-composed.log` | `d57492a6be710493c51b052eef14119c248d0d5de29f8d58f2dc8a99e7b1c5c6` | +| `run-52-r2-standalone.log` | `18562f7cf544179060917128660fef4d535b95e0ac94240cd101d862fd5171da` | +| `run-53-r2-nosub.log` | `1b9cc2668593e7c44075528acef9cbfc4540a12fe2a5f2a055819131f6e7d366` | +| `44-r2-install-check.log` | `ba9e6df4f0aff84e5e362c3de35bd236d45073e5b3b1aabfbe309f9d27ae4c3d` | +| `45-r2-install-compare.log` | `eb0de425bfee2214d39490498e5e0a2ed5fc8e331258cc48c498b8033cbe20d9` | +| `60-r14-classification.log` | `920cf5db6cdbf47b03294c022d5e15087fed61e30edabde2958ab39e236af088` | +| `61-rerun-run-wrapper.log` | `5ca28af487ca34b6957b7601672005a067bcc598358bfa209ce185d0616cefb4` | +| `62-rerun-supervised-restart.log` | `e6409f8fdd7d4a96ed8c158d85ad00bde77a6e214fec43bedb89f5ff8211c927` | +| `63-run-wrapper-on-main.log` | `f5a3724535d12ff887fc44613cf011dc2adc17d6a7416f6cebed068b32b4b29f` | +| `64-main-lane-suite-at-length.log` | `d07652d526e131d271347138c7c204efb3c15c86445e6b7f8c37a36de73dcdb9` | +| `killed/run-21-composed.r1-stopped-at-checkpoint.log` | `60c90271e3fb6acaa78ebbfd56b3483598b03cd67e5cfb19711ae0036e1daf7a` | +| `killed/r1-composed-F211-repro.log` | `3df58782bf4a317516cb5f098c2b8460e63e119e8fb5a98efb7159dd6f3cd750` | +| `10-code-leg.log` | `70f1af98b15b7f52fff2c47de6a68930f156138cfcfceacaf813382fac84a378` | +| `12-assembly.log` | `256decaff94d57c4fc14ddf0a6e03dbfa39c485e3056ccec66c65a68100d119d` | +| `13-bump.log` | `5523b0e9df43ff2f11b7207e901e1bf69e084dc461752a36b17104b2b458dfed` | +| `32-install-compare.log` | `c4f48b9f4649fb775874fa1bb962773a269778dc28eb92e6c4e28420d7cc4108` | +| `rehearsal-listing-before.txt` | `1dbf1ae38a9f98fac7b9f1ded1f0b6b2d699cc8ecb7b4f914c2cd5171f412038` | +| `rehearsal-listing-after.txt` | `1dbf1ae38a9f98fac7b9f1ded1f0b6b2d699cc8ecb7b4f914c2cd5171f412038` | +| `network-shim-calls.log` | `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855` | This record authorizes no conversion, repository creation, merge, default-branch move or lane rebinding. From 40f26f7edad8467d75059ced8eea841bded89f9e Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sat, 10 Oct 2026 23:55:46 +0000 Subject: [PATCH 29/42] Lane 3's follow-ups on 096de93: name 837928a and #190 in the T006 test note, narrow "only record", add the census row, cite 1abee1d MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part of #186. These answer the should-fix findings 1–3 and nit 4 of lane 3's review of #193 items 8–13 (#186 comment 6092925484). Text only, in the same three records. Every claim was checked again on 2026-10-10. - root-guidance-2026-10-08.md, section 10 item 6: the 18 tests are now described as at 837928a. One dated bullet says what #190 (f87fd5a) changed. Through ROOTS.path_for, 17 of them read the assembly or spec root, and they skip in a standalone code leg. The line-ending test reads the code root's own .gitattributes. This was measured by direct calls on main's code paths. - Runs A and B (both rehearsal records): "only record" becomes "their logs survive only in that receipt". Run C's legs keep 558a12b and 48d98cd as their heads' parents. The splits, 6d52162 and the remotes are gone. Checked in --no-local clones of run C's legs. - root-guidance-2026-10-08.md: section 6's 85-token census joins the list of rows that exist only in the document. - adopter-rehearsal-2026-10-08.md: the plan was replaced on main in #192's squash 1abee1d. 1a26463 is kept and named as the branch merge. Lane: openRepoTools-2 Co-Authored-By: Claude Opus 5.5 --- .../adopter-rehearsal-2026-10-07.md | 3 ++- .../adopter-rehearsal-2026-10-08.md | 12 +++++++++--- .../004-migrate-to-triad/root-guidance-2026-10-08.md | 9 ++++++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-07.md b/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-07.md index f447de83..218d5fa0 100644 --- a/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-07.md +++ b/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-07.md @@ -186,7 +186,8 @@ blob, `8043147a`. The source clone was unchanged except for the one new ref run C's plan ([adopter-rehearsal-2026-10-08.md](adopter-rehearsal-2026-10-08.md)), and run B's stays in history at `63810dd`. #188 landed as `837928a`. The bench restart of 2026-10-08 cleared both runs' disposable repositories, so - this file's receipt holds their only record. + their logs survive only in this file's receipt. Run C's legs still carry + `558a12b` and `48d98cd`, as the parents of their heads. - Each run used a fresh, empty `--local-remote-dir` and `--work-dir`. Without the first, `execute` calls `gh repo create`. The plan's `execution_authorized: false` is informational and the tool does not read it. 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 index acbe2098..863f9ad0 100644 --- a/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-08.md +++ b/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-08.md @@ -28,8 +28,10 @@ the private receipt. on a branch as the paragraph above says. - **What run C superseded.** No record: runs A and B stand as recorded. Its plan did replace run B's as the - [mapping](../../openspec/changes/migrate-to-triad/adoption-plan.yaml), in - merge `1a26463`. On main that link opens run C's plan from both records. + [mapping](../../openspec/changes/migrate-to-triad/adoption-plan.yaml), on + main in #192's squash `1abee1d`. (On #192's branch it was merge `1a26463`, + which main does not contain.) On main that link opens run C's plan from + both records. So "On this branch" in the mapping section below holds for main, and the 2026-10-07 record's "On this branch, the mapping is the regenerated run B plan" held only on its own branch. Run B's plan stays in history at @@ -38,7 +40,11 @@ the private receipt. 2026-10-08, at about 09:45Z, cleared `/tmp` (#186 comment 6068861167). Both runs were built in a session scratch directory there, as the 2026-10-07 receipt's README records, so their disposable repositories are - gone and that receipt's logs are their only record. That is why run C is + gone and their logs survive only in that receipt. Two of their commits + live on in run C's legs: `558a12b`, the spec head of runs A and B, is the + parent of run C's spec head, and `48d98cd`, run A's code head, is the + parent of run C's code head. The splits `9e5f934` and `2726d3a`, run B's + code head `6d52162` and the local remotes are gone. That is why run C is kept host-local. - **An observation left out.** The run C comment on #186 (6068938765) noted that `openspec validate migrate-to-triad --strict` with `openspec` 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 index 3c832521..cbceb661 100644 --- a/specs/004-migrate-to-triad/root-guidance-2026-10-08.md +++ b/specs/004-migrate-to-triad/root-guidance-2026-10-08.md @@ -51,6 +51,8 @@ in this document: - section 7's `setup-openspeckit`, paired-selection, `check-prerequisites`, worktree-override and store-precedence rows, and its `openspec` 1.6.0 exits; +- section 6's 85-token census of cited hashes, whose tokens were resolved + against the original checkout's branches as well as `main`; - section 9's protocol hashes. T006's #186 comment repeats some of those exit codes from the same work, @@ -423,9 +425,10 @@ Lane 2 is reading that side. 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.** - - 18 test functions read `README.md`, `AGENTS.md`, `CLAUDE.md`, `.gitattributes` or `docs/README-lanes.md` at the **code leg's** root: 17 in `test_repo_hygiene.py`, 1 in `test_openrepotools_command.py` (`test_the_help_and_the_readme_say_the_mode_the_receipt_is_born_at`). *Corrected 2026-10-09 (#193 item 9): this line first said 17, as 16 + 1.* - - Measured on the code leg alone with `-k repo_hygiene`, which selects only `test_repo_hygiene.py`: 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). - - The command test is outside that subset. Called directly on 2026-10-09, it fails before `code-0001` (`FileNotFoundError` on `README.md`) and still fails after it (`AssertionError`: the leg's README has no install-receipt paragraph). + - At `837928a`, 18 test functions read `README.md`, `AGENTS.md`, `CLAUDE.md`, `.gitattributes` or `docs/README-lanes.md` at the **code leg's** root: 17 in `test_repo_hygiene.py`, 1 in `test_openrepotools_command.py` (`test_the_help_and_the_readme_say_the_mode_the_receipt_is_born_at`). *Corrected 2026-10-09 (#193 item 9): this line first said 17, as 16 + 1.* + - Measured on run C's code leg (`837928a`'s code paths) alone with `-k repo_hygiene`, which selects only `test_repo_hygiene.py`: 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). + - The command test is outside that subset. Called directly on `837928a`'s code paths on 2026-10-09, it fails before `code-0001` (`FileNotFoundError` on `README.md`) and still fails after it (`AssertionError`: the leg's README has no install-receipt paragraph). + - *Since #190 (`f87fd5a`; noted 2026-10-10):* 17 of the 18 read their file from the root that owns it, through `ROOTS.path_for` in `tests/conftest.py`. `README.md`, `AGENTS.md` and `CLAUDE.md` belong to the assembly root and `docs/README-lanes.md` to the spec root, so in a standalone code leg these 17 skip, and a composed run reads them from those roots. The 18th, `test_the_root_carries_the_line_ending_rule`, deliberately reads the code root's own `.gitattributes`. Called directly on main's code paths (`f87fd5a`), the 17 skip, including the command test. The 18th raises `FileNotFoundError` before `code-0001` and passes after it. - **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. From 67a978ccd187545cc9ad98b02246ed10e34e91e7 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sun, 11 Oct 2026 00:18:54 +0000 Subject: [PATCH 30/42] Record #191's two Copilot rounds and the final head 8246983 in the installer evidence. Round 1's two fixes and the one finding kept, with the reason; round 2's body correction and the test that shows the behaviour; and a strict composed run of the module's composed and local rows with root v3 and the final head's code leg. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- specs/004-migrate-to-triad/installer-2026-10-08.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/specs/004-migrate-to-triad/installer-2026-10-08.md b/specs/004-migrate-to-triad/installer-2026-10-08.md index cb1a71a0..700d3398 100644 --- a/specs/004-migrate-to-triad/installer-2026-10-08.md +++ b/specs/004-migrate-to-triad/installer-2026-10-08.md @@ -2,7 +2,7 @@ **Task:** T007 and T008 of [tasks.md](tasks.md), under [#186](https://github.com/opensoft/openRepoTools/issues/186). Neither task is checked complete by this record. -**Code:** [#191](https://github.com/opensoft/openRepoTools/pull/191), branch `feat/installer-payload-source-resolution` (head `6568f08`, based on `c4864ac` with main `f87fd5a` merged in as `45979a4`). +**Code:** [#191](https://github.com/opensoft/openRepoTools/pull/191), branch `feat/installer-payload-source-resolution` (head `8246983`, based on `c4864ac` with main `f87fd5a` merged in as `45979a4`). **Assembly entry point:** [patches/t007-assembly-entry-point.patch](patches/t007-assembly-entry-point.patch). It cannot land on `main` before the split, because its path is the implementation's. @@ -165,6 +165,9 @@ Locked runs used `tests/run.sh`'s own invocation under its lock. That is the coo | Broader suites, under the lock | `6568f08` | `payload_source`, `openrepotools_command`, `wip_init`, `install_skill_and_hook`, `install_by_rename`, `repo_hygiene`, `upstream_pin`, `test_roots` | **516 passed, 9 skipped, 5 failed**. The 5 are the `lanes-edit.sh who` exit-8 cases on this workstation's lane state. That file and its test are outside the diff, and CI runs them green. | | Composed, strict, under the lock, 2026-10-09T22:08–23:18Z | assembly `4c6a0b7` = root v3 `6acc147` + code `fa460f2` (this head's whole code leg) | module + install suites + `repo_hygiene` + `openrepotools_command` | **429 passed, 1 failed**. The failure is `test_the_root_carries_the_line_ending_rule`, which needs `.gitattributes` in the code leg; run C's legs carry none (T006 §10.2). After #190's roots, the 17 earlier front-door failures read the assembly root and pass. | | CI on `6568f08` | | | Run 37997688693: `tests` 1416 passed, 9 skipped. `tests-no-submodule`: 1196 passed, 229 skipped. `tests-windows`: 177 passed. `guard-launch-mode`: 165 passed. `parse-macos`, SonarCloud and Sourcery: pass. `tests-macos` waits on `ready`. | +| Copilot round 1, on `6568f08` | `cddd1f7` | | Three findings. Fixed: a copy-time race on the mounted-leg path (the checked snapshot is what is installed), and a commented `submodule_path` that `pin_mounts` missed (its new test fails at `6568f08` and passes at `cddd1f7`, scoped and unlocked). Kept: the index-then-HEAD gitlink fallback, which is openRepoShape's `recorded_gitlink` contract. Local rows r02/r03/r10/r15, scoped and unlocked: **13 passed, 2 skipped**. | +| Copilot round 2, on `cddd1f7` | `8246983` | | One finding: the PR body overclaimed a refusal for a ref that moves between the probe and the resolution. The body is corrected, and a test (moved right after the probe: adopted → installed whole at the second commit; single → refused) passes, together with row 7, scoped and unlocked: **3 passed**. The cap is reached. | +| Composed, strict, unlocked | assembly `4b8b05a` = root v3 + code `3fc1e95` (= `8246983`) | the module's composed tests + r02, r03, r07, r10 | **22 passed** | ## Lane 2's installer rows From 1578f991c0cc10f434affd6af26451423b642d60 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sun, 11 Oct 2026 16:24:56 +0000 Subject: [PATCH 31/42] Date the "4 now pass only because they read the leg's own files" bullet: since #190 three of the four skip in a standalone leg (lane 3's 5481348731, item 8 leftover) Lane 3's review 5481348731 on #196, item 8, left one [low]: root-guidance's bullet "4 now pass only because they read the leg's own files" sat right under the bullet that says, since #190, the 17 document-reading tests skip in a standalone leg. The two contradicted each other back to back. The four are the ones that passed after code-0001 at 837928a. At main (f87fd5a) three of them (test_agents_md_is_short_enough_to_be_read, test_readme_is_short_enough_to_be_read, test_claude_md_points_at_agents_md) read through ROOTS.path_for and skip, with or without code-0001's own AGENTS.md, CLAUDE.md and README.md. The fourth, test_the_root_carries_the_line_ending_rule, reads the code root's own .gitattributes: FileNotFoundError before code-0001, a pass after it. Measured on 2026-10-11 in a scratch standalone leg built from f87fd5a (spec trees and front-door files removed): before code-0001, 3 skipped and 1 failed; after it, 3 skipped and 1 passed. The bullet now says what held at 837928a and carries one dated correction. Nothing else in the file moves. Part of #186. Co-Authored-By: Claude Sonnet 5.5 Lane: openRepoTools-2 Co-Authored-By: Claude Opus 5.5 --- specs/004-migrate-to-triad/root-guidance-2026-10-08.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index cbceb661..1b449655 100644 --- a/specs/004-migrate-to-triad/root-guidance-2026-10-08.md +++ b/specs/004-migrate-to-triad/root-guidance-2026-10-08.md @@ -429,7 +429,7 @@ Lane 2 is reading that side. - Measured on run C's code leg (`837928a`'s code paths) alone with `-k repo_hygiene`, which selects only `test_repo_hygiene.py`: 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). - The command test is outside that subset. Called directly on `837928a`'s code paths on 2026-10-09, it fails before `code-0001` (`FileNotFoundError` on `README.md`) and still fails after it (`AssertionError`: the leg's README has no install-receipt paragraph). - *Since #190 (`f87fd5a`; noted 2026-10-10):* 17 of the 18 read their file from the root that owns it, through `ROOTS.path_for` in `tests/conftest.py`. `README.md`, `AGENTS.md` and `CLAUDE.md` belong to the assembly root and `docs/README-lanes.md` to the spec root, so in a standalone code leg these 17 skip, and a composed run reads them from those roots. The 18th, `test_the_root_carries_the_line_ending_rule`, deliberately reads the code root's own `.gitattributes`. Called directly on main's code paths (`f87fd5a`), the 17 skip, including the command test. The 18th raises `FileNotFoundError` before `code-0001` and passes after it. - - **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. + - **At `837928a`, 4 passed 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 was about the wrong file. *Corrected 2026-10-11 (lane 3's review of #196, item 8): since #190 (`f87fd5a`), only the last of the four passes in a standalone leg, and it reads the right file. The first three read through `ROOTS.path_for`, so they skip, with or without the leg's own `AGENTS.md`, `CLAUDE.md` and `README.md` from `code-0001`. Run directly on main's code paths (`f87fd5a`, spec trees and front-door files removed): before `code-0001`, 3 skip and 1 fails (`FileNotFoundError` on `.gitattributes`); after it, 3 skip and 1 passes.* - 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. From 22bdb2eb0b6fb6691932983a04e4dfe7e897a7ea Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sun, 11 Oct 2026 16:35:23 +0000 Subject: [PATCH 32/42] Date the T010 record against #192's and #190's landings, correct its summary of lane 3's #190 review, and give lane 3's review of the T010 patches a disposition per finding. Lane 3's review of #196 (5481348731) raised three points. - Finding 4. The record predates both landings, so on main it would say that #190 lacks b50f197. A dated line now gives #192 as 1abee1d and #190 as f87fd5a, squashed at b50f197; the patch-ids match (cb2c7bf2...). - Finding 5. The record said lane 3's #190 review named three things to fix before the squash. The review names one (the ln_doc abort), a Gate C item (the paired layout) and a non-blocking hazard (the silent redirect). - Finding 6. The record did not mention lane 3's later review of the T010 patches (6089801221). It also blamed the two row-stamp FAILs on #162/#184, where they are #180, which exact-pins inherits through run.sh. Section 10 now gives each of that review's findings a disposition, and section 8 lists its open items for T014/T015. The two patches are byte-identical. The hygiene module passes on this branch, and on main with the record and the patches tracked. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../test-roots-and-ci-2026-10-08.md | 90 ++++++++++++++++--- 1 file changed, 77 insertions(+), 13 deletions(-) diff --git a/specs/004-migrate-to-triad/test-roots-and-ci-2026-10-08.md b/specs/004-migrate-to-triad/test-roots-and-ci-2026-10-08.md index 7badfa33..cbc89608 100644 --- a/specs/004-migrate-to-triad/test-roots-and-ci-2026-10-08.md +++ b/specs/004-migrate-to-triad/test-roots-and-ci-2026-10-08.md @@ -2,6 +2,12 @@ **Updated:** October 9, 2026, for `e3a76b5`, `dbc6837`, both reviews and the follow-ups. +**Landed (added October 11, 2026):** #192 landed as `1abee1d` +(2026-10-09T20:24:58Z), then #190 as `f87fd5a` (20:26:03Z), squashed at +`b50f197`. `git diff 1abee1d f87fd5a` and `git diff c4864ac b50f197` have the +same patch-id (`cb2c7bf2…`). So `b50f197`, which is listed below as a +follow-up outside #190, is in #190 as it landed. #192 landed before #190, as +section 2 asks. The rest of section 9's follow-ups are #195 and #193. **Tasks:** T009 and T010 of [tasks.md](tasks.md), under [#186](https://github.com/opensoft/openRepoTools/issues/186). No task is checked complete by this record; Gate B stays open. @@ -271,8 +277,14 @@ The failures, recorded rather than deleted: fixture home's `projects/…`), long enough to fall past the cut. CI runs the same suite with a short `TMPDIR` and passed it (1315 passed at `c139e88`, 1318 at `dbc6837`). These sections are untouched - by PR #190; the length interaction belongs with the run-root work of - #162/#184 (inferred from the expected and actual rows, not bisected). + by PR #190. + - The length interaction is **#180**. That is inferred from the expected and + actual rows, not bisected. + - This record first sent it to the run-root work of #162/#184. It was + corrected on 2026-10-11, after lane 3's 6089801221, finding 13 (i). + - `composed.yml` runs the suite through `code/tests/run.sh` (assembly patch, + line 125), so `exact-pins` inherits #180. It would be red on these two + assertions until #180 is fixed (section 8). * **The lane suite ABORTED in P3**, after those two FAILs, at `test_lane_helpers.sh: line 6595: ln_doc: unbound variable`. This is a defect in PR #190's own guard. `ln_doc` is bound only inside the @@ -327,7 +339,22 @@ pytest, so every local run had `/usr/bin` first on `PATH`. CI is the record. * Re-measure against the frozen source chosen at Gate C; the run C commits here are rehearsal identities, not the cutover's. * The follow-ups of section 9. Items 1, 4, 5 and 6 should land before the - real split. + real split. Their state on 2026-10-11: + - item 6 (1), `b50f197`, is in #190; + - items 1–4, 6 (2) and (3), and 7 are #195; + - item 5 is the plan owner's. +* From lane 3's 6089801221 (section 10). The patches here stay byte-identical + as rehearsed; each item below changes them at T014/T015, not in this + record. + - **#180 before `exact-pins` is relied on.** Fix #180, or give the job + `run.sh`'s pytest line with a short `TMPDIR` (finding 13 (i)). + - **Make "never skips" a guarantee.** About forty other conditional skips + would still pass with exit 0, so fail `exact-pins` on any skip, for + example on the junit skipped count (finding 13 (ii)). + - **The landing tooling reads `tests-macos` by name, and the assembly has + no such check** (finding 12). + - **Regenerate both patches with a real author.** They carry the fixture + identity `openRepoTools CI ` (finding 13 (iv)). ## 9. Reviews and dated follow-ups @@ -343,13 +370,19 @@ pytest, so every local run had `/usr/bin` first on `PATH`. CI is the record. - every subprocess of a composed run stayed inside its fixture; - the caps reproduced against T006's `assembly-0001`. * **Lane openRepoTools-3, reviewer of record:** **LAND** on `dbc6837` - (review 5464931249). It names three things to fix before the squash: - - (1) the `ln_doc` abort of section 6, with the fix ready; - - (2) the paired layout `/worktrees//code` finds no - assembly (measured on run C), so in a feature worktree every document - test would skip; - - (3) `OPENREPOTOOLS_CODE_ROOT` and `OPENREPOTOOLS_ASSEMBLY_ROOT` redirect - the suite silently, because the report header is hidden by `-q`. + (review 5464931249). It names one defect that is best fixed before the + squash, and two later items: + - (1) the `ln_doc` abort of section 6 (its finding 10), with the fix ready; + - (2) a **Gate C item** (its finding 11): the paired layout + `/worktrees//code` finds no assembly (measured on run + C), so in a feature worktree every document test would skip; + - (3) a **non-blocking hazard**: `OPENREPOTOOLS_CODE_ROOT` and + `OPENREPOTOOLS_ASSEMBLY_ROOT` redirect the suite silently, because the + report header is hidden by `-q`. + + Corrected 2026-10-11 (lane 3's 5481348731, finding 5). This record first + said all three were to be fixed before the squash, following lane 3's + summary on #186 (6072817992). The review itself dates them as above. * **Copilot round 2** (on `dbc6837`) raised two findings: - (High) the pin `commit:` gap of section 3; - (Medium, "previously missed") `_carries` accepts a directory where a @@ -358,8 +391,8 @@ pytest, so every local run had `/usr/bin` first on `PATH`. CI is the record. I answered on the thread. Nothing was pushed, because the macOS gate runs on `dbc6837`. -These follow-ups are dated 2026-10-09 and recorded on #186. None of them is -in #190. +These follow-ups are dated 2026-10-09 and recorded on #186. None of them was +in #190 at `dbc6837`. Item 6 (1) landed in it (see **Landed** at the top). 1. **Review finding 11.** Three code-only assertions came after the first document read (`test_repo_hygiene.py:994`, `:1052` and `:1264` at @@ -386,7 +419,8 @@ in #190. to say the code leg keeps its own `.gitattributes` (section 2). 6. **Lane 3's review.** - (1) `feat/test-roots-manual-guard` `b50f197`, unless the coordinator - takes it into #190 before the squash. + takes it into #190 before the squash. He did: it was fast-forwarded at + 2026-10-09T13:59Z and landed in `f87fd5a`. - (2) Assembly discovery for the paired worktree layout, before Gate C. - (3) Say which roots a run used where `-q` cannot hide it. 7. **Review nits.** @@ -396,6 +430,36 @@ in #190. - The `GIT_ENV` comment in `tests/test_test_roots.py` says "file transport allowed". +## 10. Lane 3's review of the T010 patches (6089801221), dated 2026-10-11 + +Lane 3's review of #190's follow-ups and of these patches came after this +record (2026-10-09T21:47Z, on `d28e2cc`). Its verdict on this branch was +"fit for evidence-3", with one thing to settle before the composed job is +relied on (finding 13 (i)). Each finding's disposition is below. "#195" +means that PR as it stands on 2026-10-11: open at `e378871`, with lane 3's +LAND on `fb77bed` (5481360555), and not landed. + +| # | Finding | Disposition | +| --- | --- | --- | +| 1–3 | `b50f197` covers every manual read, changes no outcome on today's layout, and does what it says | Confirmed. It landed in #190 (see **Landed** at the top). | +| 4 | The text test finds the guard by exact line match: a false red, never a false green | Fixed in #195 (`43ee05b`): the test now follows the `if`/`fi` nesting. | +| 5, 6 | How the follow-ups sit on main; no outcome changes | Confirmed. The follow-ups are #195. | +| 7 (i) | The AGENTS.md paragraph describes rather than gives a rule | Open. #195 does not change it, and it is not yet filed. | +| 7 (ii) | The 340 → 347 assembly cap rested on a false premise | Fixed in #195 (`c7b27bc`): the paragraph moved below `assembly-0001`'s hunks, so the patch applies and gives 347. | +| 7 (iii) | The paragraph names only `docs/` for the spec leg | Open: #193 item 20. | +| 8 | The nested proof covers 3 of 5 reordered tests, and asserts only the outcome | Open. The missing two are #193 item 20. That the proof checks only the outcome is not yet filed. | +| 9 | The `--help` half of the receipt test; the refusal wording; items 3–7 and 15 | The `--help` half is #193 item 20. The rest is #195. | +| 10 | Run C, standalone and composed, with the follow-ups' tests | Recorded. The `.gitattributes` failure is T006's `code-0001` (section 2). | +| 11 | The patches apply; the branch as pushed conflicts with main | Confirmed. Evidence-3 (#196) carries this branch's three files onto main. | +| 12 | `tests.yml` keeps everything the gate reads; the assembly has no `tests-macos` check | Confirmed. The missing check is in section 8. | +| 13 | Copilot round 2's High is closed in CI only, by step order | Closed in the suite too by #195 (`e79bd82`): a pin whose `commit:` differs from the gitlink is refused. | +| 13 (i) | #180 lands in `exact-pins` through `run.sh` | Open. Section 6 is corrected and section 8 lists it. The patch is unchanged here. | +| 13 (ii) | "Fails, never skips" holds only for roots and the dependency | Open. Section 8: fail the job on any skip. | +| 13 (iii), 14 | The refusal misnames a named root | Fixed in #195 (`0c89b9e`, #193 item 15). | +| 13 (iv) | The patches carry the fixture author | Open. Section 8: regenerate at T014. | +| 15 | The composed refusal contract holds | Confirmed. | +| 16 | Eight local shell FAILs attributed to the environment | Recorded. CI is the record. | + ## Log SHA256s The logs stay in the private prep area (`work-20261008/ort-test-roots/logs/`). From 3fd81c69bb59da0ca3c5e97397ae2d2f141d7289 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sun, 11 Oct 2026 16:38:37 +0000 Subject: [PATCH 33/42] Gate B walk: lines 20, 29 and 31 are PARTLY by the walk's own legend, and the walk agrees with the candidate record at 16e6ea4 and slice B at 40f26f7, which #196 carries beside it (MET 27, PARTLY 18, OPEN 0, AFTER-GATE-C 1) This answers lane 3's review 5481348731 of #196, findings 12 and 14. Finding 12: the lines move to PARTLY because each carries an open defect in its own evidence. - Line 29: #193 items 2-7 and 15. It now also cites lane 3's verdict on b50f197 (6089801221 (a)). - Line 31: #193 items 4 and 15, both fixed on #195, which is open. - Line 20: the record's "23 in 9" count, which slice B leaves not established. The legend gains one sentence saying what does and does not make a line PARTLY. Finding 14: the walk now agrees with the two records. - The candidate record publishes r2 and classifies F1-F6. Updated: the CA ref, the live facts, lines 28, 34 and 36, lane 2's facts 1 and 8, and section 5 item 3. - Lane 3 reviewed slice B as LAND (6092925484). Updated: the EF ref, the live facts, lines 3, 17 and 18, and the T006 row. - Question (a) names the host for openspec 1.13.1. One dated entry in the header says what changed and why. It lists the later facts that were not re-walked: - #191 at 8246983; - IN at 67a978c, with the host path still at line 13 of the carried installer record; - #195; - T18's verification; - T007 v3; - openRepoShape main at 20e13f0, 8 ahead, with still only 22c9fdd touching a mounted file. Part of #186 Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../gate-b-status-2026-10-09.md | 99 +++++++++++++------ 1 file changed, 69 insertions(+), 30 deletions(-) diff --git a/specs/004-migrate-to-triad/gate-b-status-2026-10-09.md b/specs/004-migrate-to-triad/gate-b-status-2026-10-09.md index 1d2c1b8f..4a632fb0 100644 --- a/specs/004-migrate-to-triad/gate-b-status-2026-10-09.md +++ b/specs/004-migrate-to-triad/gate-b-status-2026-10-09.md @@ -4,6 +4,28 @@ `fetch --all --prune` at 22:07:06Z. The GitHub reads were made between 20:32Z and 20:41Z and again between 21:58Z and 22:10Z. #186's comments were read through 22:06:49Z. Anything later is not in this walk. +**Updated 2026-10-11T16:3xZ, after lane 3's review of #196 (5481348731 findings 12 and 14):** +lines 20, 29 and 31 move from MET to PARTLY, because each carries an open defect, which this +walk's own legend makes PARTLY (the legend now also says what does not count); and the walk now +agrees with two records #196 carries beside it: the candidate record at `16e6ea4`, which +publishes r2 and classifies every failure, and lane 2's slice B at `40f26f7`, which lane 3 +reviewed as LAND. Nothing else was re-walked. These later facts are not read into the lines: +- #191 is at `8246983`, with lane 3's LAND (5481368124), `ready` since 16:31:43Z (6111192078). +- IN is at `67a978c`. #196 carries `3cca1e7`, whose line 13 still names a host path, which + section 2 and section 5 item 4 say must go first (5481348731 finding 13). +- The test-root follow-ups are #195, open at `e378871`. Lane 3's LAND (5481360555) covers + `fb77bed`; `e378871` adds #193 item 14, decided as "the test gives". +- Lane 2's verification of T18 is posted (6103482530): the record holds, with five minor + contradictions and findings a–g the record does not report. +- T007's v3 entry-point patch makes the T3 change, and #196 carries it. Its re-review is not + recorded here. +- openRepoShape `main` is `20e13f0`, 8 ahead of `7f84ca4`. `e4decfb` (#194) makes a plan that + sends `.gitmodules` and a submodule it registers to different legs a `check` finding; this + plan sends both to code (5481348731 finding 14), so line 12 stays MET. `20e13f0` refuses a + leg mount path inside a path the root holds. Both change `adopt-project.py`, so six of the + eight commits do. Still only `22c9fdd` touches a mounted file. Question e1 names the head + current at the request. + **By:** lane openRepoTools-1's reviewer, under [#186](https://github.com/opensoft/openRepoTools/issues/186). It ran read-only everywhere except this file and its own scratch. A first, unpublished pass was made at 13:27Z–13:50Z; section 1 says which statuses moved since. @@ -31,8 +53,8 @@ Paths are under `specs/004-migrate-to-triad/` unless they start with `openspec/` | IN | `004-migrate-to-triad-installer` | `51993a4` | | RB | `004-migrate-to-triad-rollback` | `fa44d75` | | RR | `004-migrate-to-triad-rollback-refresh` (lane 2's slice A) | `2e7e55e` (`c1283b5`, `612ea94`, `2e7e55e` on RB) | -| EF | `004-migrate-to-triad-evidence-2-followups` (lane 2's slice B) | `096de93` | -| CA | `004-migrate-to-triad-candidate` | `935f53f` (the r1 build; r2 is not published) | +| EF | `004-migrate-to-triad-evidence-2-followups` (lane 2's slice B) | `40f26f7`, as #196 carries it: `096de93` plus lane 3's findings 1–3 and one nit (the branch has since moved to `1578f99`) | +| CA | `004-migrate-to-triad-candidate` | `16e6ea4`, r2 published and classified, as #196 carries it (on 2026-10-09 the branch was `935f53f`, the r1 build) | | T18 | `004-migrate-to-triad-t018` | `63bff08` | | L2 | `004-migrate-to-triad-reads` | `a7a8e00` | | #191 | `feat/installer-payload-source-resolution` | `13cc752` | @@ -53,11 +75,12 @@ unchanged since `63810dd`; `git diff --stat 63810dd f87fd5a` over them is empty. 22:07Z. Lane 2's T008 matrix at `13cc752` is final (#186 6088812080). - Lane 2's slices are on origin, both meant for the evidence-3 PR. A is RR: lane 3 reviewed `c1283b5` as LAND within evidence-3 (#186 6089982753), and `612ea94` and `2e7e55e` take in its - notes N1–N9 (6090048356). B is EF `096de93` (6088799627); lane 3's review of it is in progress. + notes N1–N9 (6090048356). B is EF: lane 3 reviewed `096de93` as LAND within evidence-3 + (6092925484), and `40f26f7` takes in its findings 1–3 and one nit (6103529172). - Lane 3's review of FU and the T010 patches is posted (#186 6089801221, 21:47Z). - The r2 composed candidate's three suites finished at 18:20:47Z. Lane 2's reading of the logs is - #186 6087267593; this walk checked the summary lines against the logs. Lane 1's - classification of them is in progress. + #186 6087267593; this walk checked the summary lines against the logs. CA at `16e6ea4` + publishes them and classifies every failure, F1–F6 (CA §5). - In progress, so not counted as evidence: the Gate A capture-tool fix (#186 6087232548, 6087261307; nothing new in brett-wip since `99eed35`, per 6089772323 and 6089982753); lane 2's verification of T18 (6089772323). @@ -75,6 +98,10 @@ unchanged since `63810dd`; `git diff --stat 63810dd f87fd5a` over them is empty. - **OPEN**: no finished evidence. - **AFTER-GATE-C**: cannot be met before real legs exist. +An open #193 item that is a defect in the line's own evidence makes the line PARTLY. A later +step listed under "What remains", such as a rerun at the frozen source or a recheck at the +request, does not (added 2026-10-11). + Publishing the T006–T011 records on `main` is not itself a checklist line. It is listed among the blockers in section 5. @@ -84,7 +111,7 @@ blockers in section 5. | --- | --- | --- | --- | --- | --- | | 1 | Leg names and visibility confirmed (T003, FR-012) | MET | #186 6035767825 ("legs and visibility confirmed"); M:`openspec/changes/migrate-to-triad/adoption-plan.yaml` (legs; `visibility: public`) | Recheck at the Gate C request. 404 is not proof the names are free (preparation.md:53–56) | Brett (given); lane 1 (recheck) | | 2 | `git-filter-repo` via the approved prerequisite path | PARTLY | M:adopter-rehearsal-2026-10-08.md#Setup; M:adopter-rehearsal-2026-10-07.md#Setup; #186 6072724043 (i) | No record names who approves the private-venv path for the real `execute` (plan.md:105–106, :160–162): question e4. Neither receipt carries the October 4 receipt's "existing bench/system installations were unchanged" (preparation.md:62–63), which the evidence column asks for (lane 2, 6088829392 item 10). The "no ensurepip" cause is withdrawn on EF; the refusal's cause is not established (#193 item 12) | Brett; lane 1 | -| 3 | Plan regenerated for the rehearsal source, overrides kept | MET | M:`openspec/changes/migrate-to-triad/adoption-plan.yaml` (source `837928a`, 22 resolutions); M:adopter-rehearsal-2026-10-08.md#Mapping (T003); #192 review 5464893559; #186 6072724043 (iv) | The final plan is regenerated at the frozen source before the request (question e2). `check` reads the clone's local `main`, so the committed plan is `plan-stale` against any later `main` (#193 item 10; EF:adopter-rehearsal-2026-10-08.md:107–119) | lane 1 | +| 3 | Plan regenerated for the rehearsal source, overrides kept | MET | M:`openspec/changes/migrate-to-triad/adoption-plan.yaml` (source `837928a`, 22 resolutions); M:adopter-rehearsal-2026-10-08.md#Mapping (T003); #192 review 5464893559; #186 6072724043 (iv) | The final plan is regenerated at the frozen source before the request (question e2). `check` reads the clone's local `main`, so the committed plan is `plan-stale` against any later `main` (#193 item 10; EF:adopter-rehearsal-2026-10-08.md:113–125) | lane 1 | | 4 | Standard `check` passes, every path, no unapproved drop | MET | M:adopter-rehearsal-2026-10-08.md#Run C (`plan ok`; 120 = 65/49/6/0); #186 6072724043 (ii) | Rerun at the frozen source | lane 1 | | 5 | Amendment folders to code; full proposals and specs to spec | MET | M plan: `docs/`, `ideation/`, `openspec/`, `specs/` to spec; 0 `features/` paths on `main` | Re-observe at the frozen source | lane 1 | | 6 | No amendment paths recorded where none exist | MET | M:inventory.json `refreshed.amendment_path_observations` (every head empty) | Re-observe at the Gate A final capture | lane 1 | @@ -98,10 +125,10 @@ blockers in section 5. | 14 | Nested submodule; original `.gitmodules` | MET | M runs B and C (code keeps blob `8043147a`; the assembly gets its own) | — | — | | 15 | Pin integrity, coupled | MET | M run C (`3be52767…` MATCH); #186 6072724043 (v) | — | — | | 16 | Recursive bootstrap | MET | M run C (`bootstrap ok`, three validators) | — | — | -| 17 | Root collisions and leg guidance as reviewed patches | MET | M:root-guidance-2026-10-08.md#1, #4; M:patches/t006/*; #192 review 5464893559 finding 7 (exact rebuild) | #193 items 8–13, the text fixes: on EF `096de93`, under lane 3's review, for evidence-3. The `shape/*.md` deletion is T014's reviewer's | lane 1 (EF by lane 2) | -| 18 | Shape-aware root bootstrap after the legs exist | PARTLY | M:root-guidance#7; T18:t018-disposable-2026-10-09.md#1 (a second run: `setup-openspeckit --shape on` exit 0, `worktree_root: worktrees`) | Neither run is reviewed, and neither has a receipt: EF now says so plainly (#193 item 11; EF:root-guidance-2026-10-08.md:31–57). Lane 2 is verifying T18 (6089772323). T18 finds the bootstrap leaves the root "dirty" to `resume` (T18 §5 item 1): question e7 | lane 1; Brett | +| 17 | Root collisions and leg guidance as reviewed patches | MET | M:root-guidance-2026-10-08.md#1, #4; M:patches/t006/*; #192 review 5464893559 finding 7 (exact rebuild) | #193 items 8–13, the text fixes: on EF, LAND by lane 3 (6092925484), carried by #196 at `40f26f7`; item 9's count stays open (line 20). The `shape/*.md` deletion is T014's reviewer's | lane 1 (EF by lane 2) | +| 18 | Shape-aware root bootstrap after the legs exist | PARTLY | M:root-guidance#7; T18:t018-disposable-2026-10-09.md#1 (a second run: `setup-openspeckit --shape on` exit 0, `worktree_root: worktrees`) | Neither run is reviewed, and neither has a receipt: EF now says so plainly (#193 item 11; EF:root-guidance-2026-10-08.md:31–56). Lane 2's verification of T18 has since been posted (6103482530; the dated note). T18 finds the bootstrap leaves the root "dirty" to `resume` (T18 §5 item 1): question e7 | lane 1; Brett | | 19 | First-line guidance, relative paths, actual `.specify`, paired selection, no secrets | PARTLY | M:root-guidance#2, #3 (both verified by #192 review finding 7), #7, #8; T18 §2 (paired selection from the root, either leg and either worktree; `check-prerequisites.sh` agrees ×5) | `.specify` was read from today's checkout, not from Gate A's captured configuration (lane 2, L2 line 19). The `.specify` posture is undecided (question e7). `SPECIFY_FEATURE` alone gives a branch and directory that disagree (T18 §5 item 8). T18 is unreviewed | lane 1; Brett | -| 20 | Provenance correspondence; link repair without guessed hashes | MET | M:root-guidance#6 (35/35; 47 citations resolve in the assembly), #3; #192 review finding 10 | EF marks "23 mentions in 9 files" as not established (#193 item 9). Feature 004's own pre-split paths are T017's | lane 1 | +| 20 | Provenance correspondence; link repair without guessed hashes | PARTLY | M:root-guidance#6 (35/35; 47 citations resolve in the assembly), #3; #192 review finding 10 | The record's own count is open: "23 mentions in 9 files" sums to 26 in 11, and no recount settles it. EF says so and leaves it "not established" (EF:root-guidance-2026-10-08.md:210–211), and lane 3 rates that part of #193 item 9 PARTLY (6092925484). T017 recounts, with feature 004's own pre-split paths | lane 1 | | 21 | Explicit roots, local-root precedence, supported store selection | PARTLY | M:root-guidance#7 (store precedence; strict validate in the spec leg); T18 §3 (OpenSpec from each root with 1.2.0 and 1.6.0) | Store precedence rests on the document alone (#193 item 11). T18 shows the root and the code leg pass `validate --all --strict` vacuously, so validation must run in the spec leg (T18 §5 item 7). `--store` needs openspec 1.6.0 or later (question a) | lane 1; workBenches owner | | 22 | Protocol/command distribution per target workstation through workBenches | PARTLY (no owner) | M:root-guidance#9: Eagle's two installed protocol files carry "Triad Feature Amendments" (sha256 `f9dd6464…`, `bbbc2b2c…`) | Not via workBenches delivery; command and skill digests absent; no other workstation; no target list; no writer holds it (lane 2 read only vendoring, consumer-reads-2026-10-07.md:85–105). Section 2 records lane 2's NOBODY | Brett (names the workstations); workBenches owner; a holder to be named by lane 1 | | 23 | Installer design | MET | M:installer-design.md | — | — | @@ -109,15 +136,15 @@ blockers in section 5. | 25 | Entry point and payload-source resolution as reviewed patches | PARTLY | IN:patches/t007-assembly-entry-point.patch; #191 @ `13cc752`: LAND with conditions (review 5471121210; delta 8 is the stale PR body, plus `tests-macos` under `ready`) | The next #191 head needs its own check; it was not on origin at 22:07Z. The entry-point patch is CHANGES REQUIRED (5471121210 T3: on the remote path, confirm the gitlink before running the pinned implementation). Not landed | lane 1; lane 3 | | 26 | One-liners resolve one code pin; no developer checkout | PARTLY | 5471121210 T1–T2 (a disposable assembly at `13cc752`: one code commit, the installed tree byte-identical to a checkout install, local delegation with no fallback); #186 6088812080 (final matrix at `13cc752`) | Rerun at the next head with the T3 fix. On the single layout a ref that moves mid-install mixes versions, as on `main` (6088812080 item 7); the PR body should scope "resolve once" to the adopted layout (5471121210 delta 8). Real one-liners are T015's | lane 2; lane 1 | | 27 | 15-row compatibility matrix measured | PARTLY | #186 6073209094 and 6073234714 (15/15 at `2411f40`); #186 6088812080 (final at `13cc752`, 337 row logs; F1, F2, (b) and (d) fixed) | Rows 18–22 bear on the next head (6088812080 items 1–6): the layout search walks every ancestor, and a HOME holding `contracts/code-pin.yaml` refuses to reinstall; a missing ref or repository is read as a single repository and never named; a 502 on a probe refuses where `main` installs; the 404 path; garbage at 200 is reported as a moved ref; (c) and (e) remain. Rerun at the next head | lane 2; lane 1 | -| 28 | Names, counts, modes, hooks, receipts, all-or-none unchanged | PARTLY | 5471121210 delta 2 (offline install byte-identical at `13cc752`); #186 6088812080 (210 of 210 refusals placed nothing; 82 of 83 installs 29/29 byte-exact, the exception row 7's deliberate mixed case; 40 of 40 receipts consistent; both hook entries present); #186 6087267593 (the r2 install: 29 files byte-exact) | Rerun at the next head. An adopted `wip init` that refuses still leaves an empty server repository behind (#186 6087232548, its F2 row) | lane 2; lane 1 | -| 29 | Explicit roots in fixtures and hygiene checks | MET | M@`f87fd5a` (#190 at `b50f197`): LAND 5464931249, LAND 5464938427, delta LAND 5471080687; CI green including `tests-macos` | FU @ `01ccc86` (#193 items 2, 6): lane 3 finds it fit for a PR onto `main` after one edit, dropping or re-premising the 340 → 347 assembly cap (6089801221 finding 7). #193 items 3–7 and 15 stay open; item 5, the paired layout, is wanted before Gate C | lane 1; lane 3 | +| 28 | Names, counts, modes, hooks, receipts, all-or-none unchanged | PARTLY | 5471121210 delta 2 (offline install byte-identical at `13cc752`); #186 6088812080 (210 of 210 refusals placed nothing; 82 of 83 installs 29/29 byte-exact, the exception row 7's deliberate mixed case; 40 of 40 receipts consistent; both hook entries present); #186 6087267593 and CA §4 (the r2 install from the assembly root: the same 29 files as main's, byte-identical except the implementation, and a byte-identical `settings.json`) | Rerun at the next head. An adopted `wip init` that refuses still leaves an empty server repository behind (#186 6087232548, its F2 row) | lane 2; lane 1 | +| 29 | Explicit roots in fixtures and hygiene checks | PARTLY | M@`f87fd5a` (#190 at `b50f197`): lane 3's LAND 5464931249 and 5464938427, and its verdict on `b50f197`, 6089801221 (a) (lane 1's own delta review is 5471080687); CI green including `tests-macos` | #193 items 2–7 and 15 are open defects of these fixtures on `main`. Item 5, the paired layout, is a Gate C item lane 3 wants settled before Gate C (section 5, item 2). The command test still reads the README before its `--help` half (6089801221 finding 9). The follow-ups were FU @ `01ccc86` at the read; see the dated note for #195 | lane 1; lane 3 | | 30 | `tests/run.sh -k upstream_pin` inside the rehearsed code leg | MET | #186 6072319462 (all 14 `test_upstream_pin.py` pass in code leg `73b04d6`); CI:test-roots-and-ci-2026-10-08.md#1 | The runs used `tests/run.sh`'s own pytest line under the same lock, not its census (#184) | lane 1 | -| 31 | Composed checks refuse instead of skipping | MET | M@`f87fd5a` (#190); #186 6087267593 (five refusal cases exit 4 on an r2-equivalent); 6089801221 finding 15 | #193 item 4: conftest never reads a pin's `commit:`, closed in CI only by `composed.yml`'s step order (6089801221 finding 13). Item 15 (wording) is confirmed there (finding 14). About forty other conditional skips can still pass a composed run; none fires on ubuntu-latest today (finding 13 (ii)) | lane 1 | +| 31 | Composed checks refuse instead of skipping | PARTLY | M@`f87fd5a` (#190); #186 6087267593 (five refusal cases exit 4 on an r2-equivalent); 6089801221 finding 15 | The checklist's evidence column is met, but this refusal carries two open defects. #193 item 15: it misnames a root that was named (6089801221 finding 14). Item 4: conftest never reads a pin's `commit:`, closed in CI only by `composed.yml`'s step order (finding 13). Lane 2 confirms both fixed on #195 (6103529172), which is open. About forty other conditional skips can still pass a composed run; none fires on ubuntu-latest today (finding 13 (ii)) | lane 1 | | 32 | Code-leg CI and assembly exact-pin job patches | PARTLY | CI:patches/t010-*.patch; CI:test-roots-and-ci#5–#6; lane 3's review 6089801221 (c): fit for evidence-3, both patches apply to run C and the code-leg patch to `f87fd5a` | `composed.yml` runs the suite through `run.sh`, whose long run root makes #180's two row-stamp assertions fail, so `exact-pins` would be red until #180 is fixed or the job uses a short `TMPDIR` (finding 13 (i), inferred). Both patches need a real author at T014 (finding 13 (iv)). `actionlint` last ran 2026-10-07. SonarCloud and required checks: question e9 | lane 1; lane 3; Brett | | 33 | Platform policies, Bash 3.2 and serialized locking kept | MET | CI:test-roots-and-ci#5; lane 3's review 6089801221 finding 12: all six jobs keep their names and order, `tests-macos` keeps its `ready` gate verbatim, the triggers, per-job `submodules:`, `parse-macos`'s bash 3.2 file list and the Windows env are unchanged, WSL stays a documentation policy, and locking is as today | The hosted run is line 35. The assembly has no `tests-macos` check, which T015's landing tooling must allow for (finding 12) | lane 1 | -| 34 | Exact composed state passes required checks and the matrix (local) | PARTLY | #186 6087267593: r2 = code `4dbcf6c` (#190 `b50f197`, #191 `13cc752`, T006, T010), spec `9e1d006`, assembly `5d92962`. Composed 5 failed / 1411 passed; standalone 3 failed / 1389 passed / 24 skipped; no dependency 6 failed / 1168 passed / 242 skipped | It does not pass yet. `test_agents_md_names_the_pin_rules` (T006 × #190, #193 item 14) and `test_r14_the_documented_upstream_skip_is_unchanged` (`KeyError: 'reason'`, first seen in r1, unanalysed) need fixes. The lane-helper shell FAILs (row stamps, #180 per 6089801221 finding 13), the `test_run_wrapper` timeouts and the observer timeouts (load, inferred) need classifying. r2 rests on `13cc752`, so the composed record is redone at #191's final head and the other final heads, then published | lane 1 | +| 34 | Exact composed state passes required checks and the matrix (local) | PARTLY | CA `16e6ea4` §§2, 5: r2 = code `4dbcf6c` (#190 `b50f197`, #191 `13cc752`, T006, T010), spec `9e1d006`, assembly `5d92962`; composed 1411 passed / 5 failed / 0 skipped; standalone 1389 / 3 / 24; no submodule 1168 / 6 / 242; every failure classified; private receipt `candidate-20261009-JR8YyA`. Lane 2's counts agree (6087267593) | It does not pass yet (CA §6). Two failures are candidate defects: F1, r14's `KeyError: 'reason'`, first seen in r1, fixed in #191's `6fa3b43` and not re-run; F2, `test_agents_md_names_the_pin_rules`, #193 item 14, pending in CA and since implemented on #195 as "the test gives". F3 and F4 are #180's row stamps; F5 is the harness's inherited ignored SIGINT, not load; both fail the same on main. F6 is timing under load, pre-existing. Owed: a composed rerun at #191's final head with T007's v3, F1 gone and F2 decided | lane 1 | | 35 | Hosted-runner part of T010 | AFTER-GATE-C | CI:test-roots-and-ci#5 ("Neither patch can run on GitHub until the legs exist"), #8 | Question d | Brett | -| 36 | Restore/rollback of the candidate and installed payload | PARTLY | RB:gate-ab-evidence-2026-10-09.md#1–#2; lane 2's independent check (#186 6082370012: restore 91 PASS, four homes reproduce); M:candidate record r1 §4 on CA; RR (the record now says the hook rollback is vacuous and the backup holds no `settings.json`; lane 3 LAND at `c1283b5`, 6089982753) | No hook-changing rollback, no restore of the local candidate from preserved artifacts and no post-merge revert/pin-bump rollback is rehearsed (lane 3 6082139424 findings 11 and 19; 6082370012 (iv)). The capture tooling v2 cannot stand as evidence until A1 is fixed (6087261307). RR is not on `main` | lane 1 | +| 36 | Restore/rollback of the candidate and installed payload | PARTLY | RB:gate-ab-evidence-2026-10-09.md#1–#2; lane 2's independent check (#186 6082370012: restore 91 PASS, four homes reproduce); CA §4 (r2's install; a reinstall from main restores main); RR (the record now says the hook rollback is vacuous and the backup holds no `settings.json`; lane 3 LAND at `c1283b5`, 6089982753) | No hook-changing rollback, no restore of the local candidate from preserved artifacts and no post-merge revert/pin-bump rollback is rehearsed (lane 3 6082139424 findings 11 and 19; 6082370012 (iv)). The capture tooling v2 cannot stand as evidence until A1 is fixed (6087261307). RR is not on `main` | lane 1 | | 37 | Work, jobs and claims survive; no force push, reset or pointer rewrite | PARTLY | RB:gate-ab-evidence#3; #186 6082370012 (verified; 0 non-fast-forward against GitHub); RR §3 | Jobs have no before/after record, only "no script … kills a process" (6082139424 finding 19). No force push is argued for a real rollback, not shown | lane 1 | | 38 | Publish the Gate A evidence | MET | M:preservation-2026-10-07.md, M:inventory.json (#189); receipt `prep-20261007T100559Z` | Gate A itself stays open (section 5) | lane 1; Brett | | 39 | Publish the Gate B rehearsal evidence (a receipt in adopter-rehearsal form) | MET | M:adopter-rehearsal-2026-10-07.md (runs A and B, #189) and M:adopter-rehearsal-2026-10-08.md (run C, #192); receipts `adopter-fix-20261007-iM20tv`, `adopter-fix-20261008-1HztL0`, verified in #186 6036838832 and 6072724043 | The other Gate B records are not on `main` (section 5, item 4) | lane 1 | @@ -129,8 +156,8 @@ blockers in section 5. | 45 | Reviewed refreshed inventory (T001) with lines 2–6 | MET | M:inventory.json (stamp 2026-10-07T10:09:03Z; #189 LAND) | Refresh at the Gate A final capture | lane 1 | | 46 | A review record for each T006–T010 patch | PARTLY | T006: 5464893559 LAND (landed). T009: #190 LAND ×2 and delta LAND (landed). T007/T008 code side: #191 `13cc752` LAND with conditions (5471121210). T010: lane 3 6089801221 (c), fit for evidence-3 | The T007 entry-point patch is CHANGES REQUIRED (5471121210 T3). #191's next head: unchecked. T010's `composed.yml` carries finding 13 (line 32) | lane 3; lane 1 | -**Counts (46):** MET 30 · PARTLY 15 · OPEN 0 · AFTER-GATE-C 1. -- **PARTLY:** 2, 18, 19, 21, 22, 25, 26, 27, 28, 32, 34, 36, 37, 40, 46. +**Counts (46):** MET 27 · PARTLY 18 · OPEN 0 · AFTER-GATE-C 1 (on 2026-10-09: 30 · 15 · 0 · 1). +- **PARTLY:** 2, 18, 19, 20, 21, 22, 25, 26, 27, 28, 29, 31, 32, 34, 36, 37, 40, 46. - **AFTER-GATE-C:** 35. **Moved since lane 1's first pass (13:27Z–13:50Z, unpublished; then MET 29 · PARTLY 15 · OPEN 1 · @@ -144,6 +171,11 @@ AFTER-GATE-C 1):** - **39: PARTLY → MET.** Run C landed with #192. This walk now reads line 39 by the checklist's own evidence column; the other records' publication moves to section 5. +**Moved on 2026-10-11** (lane 3's 5481348731 finding 12, by this walk's own legend): +- **29: MET → PARTLY.** #193 items 2–7 and 15 are open defects of the fixtures it names. +- **31: MET → PARTLY.** #193 items 4 and 15 are open defects of the refusal it names. +- **20: MET → PARTLY.** The record's "23 in 9" count is open. + ## 2. Reconciliation with lane 2's walk at `a7a8e00` Lane 2's walk is `004-migrate-to-triad-reads` @ `a7a8e00`: @@ -168,12 +200,13 @@ describes who holds a line. This walk's legend describes the evidence. The two m | 18 | EXISTS (document only) | PARTLY | A row that exists only in the writer's document, with no receipt and no independent rerun, is evidence not yet checked (#193 item 11). T18 adds a second run by lane 1 itself, also unreviewed, and a new finding: the bootstrapped root is "dirty" to `resume`. PARTLY. | | 19 | EXISTS | PARTLY | Lane 2's own note that `.specify` was read from today's checkout and not from Gate A's capture is a gap against the checklist's evidence column. The posture decision is open (e7), and T18 adds the `SPECIFY_FEATURE` disagreement. PARTLY. | | 21 | EXISTS (document only) | PARTLY | As for 18. T18 also shows that validation run from the root passes vacuously. PARTLY. | +| 20, 29, 31 | EXISTS (unlanded) | PARTLY (2026-10-11) | All three landed (#192, #190), and each still carries an open defect in its own evidence: 20 the "23 in 9" count, 29 #193 items 2–7 and 15, 31 items 4 and 15. Lane 3's 5481348731 finding 12. PARTLY. | **Lines that agree in substance:** - **Same status, different legend:** 1, 4–11, 13–16, 23, 24, 30, 35, 38, 41–45 (MET or AFTER-GATE-C here; DECIDED, EXISTS or BLOCKED there). -- **Unlanded at lane 2's cutoff, landed since:** 3, 17, 20, 29, 31 (lane 2's "EXISTS - (unlanded)" are MET now). +- **Unlanded at lane 2's cutoff, landed since:** 3 and 17 (lane 2's "EXISTS (unlanded)" + are MET now). 20, 29 and 31 also landed, and are in the table above since 2026-10-11. - **Line 33:** EXISTS there, MET here since lane 3's review of the T010 patches (6089801221 finding 12). This walk's earlier PARTLY waited only on that review. - **IN PROGRESS there, PARTLY here:** 25–28, 32, 34, 36, 37, 40, 46. @@ -190,7 +223,8 @@ describes who holds a line. This walk's legend describes the evidence. The two m - **Item 16 (a host-absolute path in IN:installer-2026-10-08.md:13): agree.** It must be removed before IN goes into the evidence-3 PR; plan.md:38–39. - **Items 1 and 8 (the candidate receipt is absent from brett-wip; the record cites #191 at - `2411f40`): agree.** Both are owed with the r2 publication (line 34). + `2411f40`): agree, and both are answered.** CA at `16e6ea4` names the receipt + `candidate-20261009-JR8YyA` and records r2 against #191 at `13cc752` (line 34). - **Item 13 (two openspec results): agree.** It is question a. T18 §3 now measures 1.2.0 and 1.6.0 side by side. - **Item 14 (two reviews of lane 1's PRs are headed "lane openRepoTools-1"): agree; a note, not a @@ -230,10 +264,10 @@ Every task stays open on `main`. | T003 mapping | Names and visibility confirmed; filter-repo; run C's plan (`plan ok`, 0 drops) on `main` | Approve the prerequisite path (e4); regenerate and `check` at the frozen source; the final mapping goes into the approval (e2) | — | | T004 rehearsal | Run C on `main` | A rerun at the frozen source if question e3 requires one | The real `execute` is T013 | | T005 nested pin | Refusal, upstream fix and repin; the nested pin and bootstrap in runs B and C; upstream CI at `6fa4e07` and `7f84ca4` | Question e1 (stay on `7f84ca4`, or repin and rehearse again) | — | -| T006 root/leg guidance | Collisions, 32 repairs, leg front doors, provenance 35/35 (on `main`); bootstrap, paired selection and OpenSpec roots measured twice (root-guidance §7; T18 §§1–3) | #193 items 8–13 (EF, unreviewed); lane 2's verification of T18, then a review; a receipt, or the sentence EF adds saying there is none; questions a, c, e7, e8 | T014 applies `assembly-0001`, `code-0001` and `spec-0001`, regenerates the two bumps with `bump-leg.py`, and settles `shape/*.md`. T18's follow-ups go to workBenches and lane 3 | +| T006 root/leg guidance | Collisions, 32 repairs, leg front doors, provenance 35/35 (on `main`); bootstrap, paired selection and OpenSpec roots measured twice (root-guidance §7; T18 §§1–3) | #193 items 8–13 (EF: LAND, carried by #196 at `40f26f7`; item 9's count open); lane 2's verification of T18 (posted, 6103482530), then a review; a receipt, or the sentence EF adds saying there is none; questions a, c, e7, e8 | T014 applies `assembly-0001`, `code-0001` and `spec-0001`, regenerates the two bumps with `bump-leg.py`, and settles `shape/*.md`. T18's follow-ups go to workBenches and lane 3 | | T007 entry point | The entry-point patch (IN); #191's code side, LAND with conditions at `13cc752`; T1–T2 of 5471121210 | #191's next head: proof, lane 3's check, the PR body, `tests-macos`, landing, against the matrix findings of 6088812080. The entry-point patch's T3 change, then its re-review | T014: the entry point ships in the root-split PR itself (lane 3, #186 6072087071); T015: the real one-liners | | T008 matrix | 15 rows at `2411f40`; the final matrix at `13cc752` (337 row logs) | A rerun at #191's next head (one command, lane 2) | T015 against the real GitHub; workBenches re-vendors (W1/W3/W8) once the legs exist | -| T009 test roots | #190 landed with `b50f197`; its CI green, macOS included | FU @ `01ccc86`: one edit (6089801221 finding 7), then a PR and landing; #193 items 3–7 and 15; item 5 (paired layout) before Gate C; item 14 settled by lane 1 | Verify at the real head (T015) | +| T009 test roots | #190 landed with `b50f197`; its CI green, macOS included | Land the follow-ups (FU @ `01ccc86` at the read; #195 since): #193 items 2–7, 14 and 15, with item 5 (paired layout) before Gate C | Verify at the real head (T015) | | T010 CI | Two workflow patches; local proofs P1–P4; lane 3's review, fit for evidence-3 | `composed.yml` against #180 (finding 13 (i)); a composed run of the final candidate that passes (line 34); question d | Hosted runs at the real legs (T015); a real author on both patches (T014); SonarCloud binding and required checks (e9) | | T011 restore/rollback | Source restore and four rollback homes, independently verified; RR refreshes the record, LAND by lane 3 within evidence-3 | A hook-changing rollback, the candidate restore and a post-merge pin-bump rollback, or a dated statement of what cannot be rehearsed; jobs before/after | Rollback checks at the real head (T015) | | T012 freeze and approval | #97 and #121 landed and are in the baseline | Freeze; regenerate and `check`; recheck the names; present the plan, follow-ups and dispositions; Brett's approval | Refuse source drift before `execute` | @@ -263,7 +297,9 @@ exit=0 ``` On lane 2's host the system openspec is 1.13.1, and it passes `migrate-to-triad` and 5 of 5 -changes on `main` (6088829392 item 8). EF records the bench's openspec as 1.13.1 too. +changes on `main` (6088829392 item 8). EF records 1.13.1 on the bench it measured +(EF:adopter-rehearsal-2026-10-08.md:53). On lane 3's host on 2026-10-11 the order was 1.2.0, +then 1.6.0, as here (5481348731 finding 9). Which CLI runs depends on the container. **Cause.** 1.2.0's `extractRequirementText` (`dist/core/validation/validator.js:369-390` in its package) takes only the first non-blank body line of a requirement. Line 45 of @@ -538,14 +574,14 @@ retiring worktrees." lane 2's pre-push run (6090038382); correct the PR body (5471121210 delta 8); `tests-macos` under `ready`; land. Then the T007 entry-point patch's T3 change and its re-review. Or Brett's ruling on any kept difference (question e6). -2. **The test-root follow-ups and the T010 patches.** FU's one edit (6089801221 finding 7), its - PR and landing; `composed.yml` against #180 (finding 13 (i)); #193 item 14 settled by lane 1, - and item 5 (paired-layout discovery) before Gate C. -3. **The composed candidate at the final heads (r3).** Every suite passing, or each failure - classified with its cause: r14's `KeyError 'reason'`, `test_agents_md_names_the_pin_rules`, - the row stamps (#180), the load-sensitive timeouts. Then the install and rollback recheck, - and publication with its receipt (lines 34, 36, 40). -4. **The evidence-3 PR.** Fold CI (`d28e2cc`'s three new files), IN (with the host-absolute path +2. **The test-root follow-ups and the T010 patches.** The follow-ups, now #195 (#193 items + 2–7, 14 and 15, item 5's paired-layout discovery among them), reviewed at their final head + and landed before Gate C; `composed.yml` against #180 (finding 13 (i)). +3. **The composed candidate at the final heads.** r2 is published with its receipt, and every + failure is classified (CA `16e6ea4`). Owed: a composed rerun at #191's final head with + T007's v3, with F1 gone and F2 (#193 item 14) decided, then the install and rollback recheck + at those heads (lines 34, 36, 40). +4. **The evidence-3 PR, #196.** Fold CI (`d28e2cc`'s three new files), IN (with the host-absolute path at installer-2026-10-08.md:13 removed), RR at `2e7e55e` (the branch tip's file: a cherry-pick of `c1283b5` alone conflicts, 6089982753), EF `096de93`, the candidate record, T18 and this walk onto `main`; do not reuse `849592b`'s subject or body (6088884829); review; @@ -579,6 +615,9 @@ gh api repos/opensoft/openRepoShape/compare/7f84ca4...main # ahead 6 gh api repos/opensoft/openRepoShape/commits/ # files changed gh api repos/opensoft/openRepoShape/pulls/162 # head 6fa4e07, merge 7f84ca4 gh api repos/opensoft/openRepoShape/commits/{6fa4e07,7f84ca4}/check-runs +gh api repos/opensoft/openRepoTools/pulls/196/reviews # 5481348731 (2026-10-11) +git -C show 16e6ea4:specs/004-migrate-to-triad/candidate-2026-10-09.md # CA +git -C show 40f26f7: # EF git clone -q --no-local --single-branch --branch main /main-clone command -v openspec; which -a openspec; --version # 1.2.0 first, 1.6.0 second (cd /main-clone && ~/.npm-global/bin/openspec validate migrate-to-triad --strict) # exit 1 From 17dd54f7b807ea4af661f7f637d84828d462d139 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sun, 11 Oct 2026 16:54:54 +0000 Subject: [PATCH 34/42] Point the three "no review verdict posted" rows of the T011 record at lane 3's verdict, 6089801221 (lane 3's 5481348731, item 7) Lane 3's review 5481348731 on #196, item 7, left one [low]: gab:436, :443 and :513 still say "no review verdict posted; lane 3's reviewer ... has resumed" for the T010 patches and the follow-ups. Lane 3's verdict, #186 comment 6089801221, was posted at 21:47Z on 2026-10-09, after this record's origin read of 20:29-20:30Z. Each of the three rows now carries a dated correction that points at it and says what it concluded for that row. The original text and the read time stay as written. - The `004-migrate-to-triad-ci` row and the T010 inventory row: `d28e2cc` is fit for evidence-3, both patches apply, and #180 reaches the composed job through `run.sh` (its finding 13). - The `feat/test-roots-followups` row: `01ccc86` is fit for a PR onto main after one edit, the 340 to 347 assembly cap (its finding 7). Part of #186. Co-Authored-By: Claude Sonnet 5.5 Lane: openRepoTools-2 Co-Authored-By: Claude Opus 5.5 --- specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md b/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md index 020e1571..a7b3ed48 100644 --- a/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md +++ b/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md @@ -433,14 +433,14 @@ squash equals its reviewed head: `1abee1d`'s tree is `71b05cb`'s, and `f87fd5a`' | #192, landed as `1abee1d` (head `71b05cb`; `004-migrate-to-triad-evidence-2` deleted) | run C's records, its plan and T006's `root-guidance-2026-10-08.md` and five patches are on main | lane 3: **LAND** on `71b05cb` (review 5464893559), with an exact rebuild of all five T006 patch commits; lane 2: run C's receipt verified, all six checks pass (#186 comment 6072724043) | | `004-migrate-to-triad-root-guidance` `041a273`, `004-migrate-to-triad-rehearsal-c` `1a26463` | unchanged; their content landed with #192 | as #192 | | #190, landed as `f87fd5a` (head `b50f197`; `feat/test-roots-for-the-triad` deleted) | `e3a76b5` → `dbc6837` (01:22:43Z) → `b50f197` (13:59:10Z, the manual guard, #193 item 1) | **LAND** on `dbc6837` from lane 3 (5464931249) and lane 1's independent review (5464938427); **LAND** on the `b50f197` delta (5471080687); CI green on `b50f197`, `tests-macos` included | -| `004-migrate-to-triad-ci` `d28e2cc` | `cc7c7a1` → `b4ef6b5` → `d28e2cc` (02:30:57Z); only `test-roots-and-ci-2026-10-08.md` changed, and both T010 patch blobs are identical at both heads | no review verdict posted; lane 3's reviewer of the T010 patches has resumed (#186 comment 6088703549) | +| `004-migrate-to-triad-ci` `d28e2cc` | `cc7c7a1` → `b4ef6b5` → `d28e2cc` (02:30:57Z); only `test-roots-and-ci-2026-10-08.md` changed, and both T010 patch blobs are identical at both heads | no review verdict posted; lane 3's reviewer of the T010 patches has resumed (#186 comment 6088703549). *Corrected 2026-10-11 (lane 3's 5481348731 on #196, item 7): the verdict came after this record's origin read of 20:29–20:30Z. It is #186 comment 6089801221 (2026-10-09T21:47Z), on `d28e2cc` (unchanged): fit for evidence-3, carried as its three new files on main. One thing to settle first: `composed.yml` runs the suite through `run.sh`, which brings #180 into the one job that must be green (its finding 13).* | | #191 `13cc752` | `ecd7aa4` → `2411f40` (01:20:38Z) → `13cc752` (02:42:51Z) | **DO NOT LAND** on `2411f40` from lane 3 (5464963807) and lane 1's independent review (5465121368); **LAND** on `13cc752` from lane 3's delta review (5471121210), on condition that the PR body is corrected before the squash and `tests-macos` runs on `ready`. CI on `13cc752`: every job passed except `tests-macos`, skipped without `ready`. Lane 1's next head, `fdc5f18`, is announced and not on origin (#186 comment 6088714587, item 3) | | `004-migrate-to-triad-installer` `51993a4` (new, 01:21:15Z; no PR) | T007's entry point, `patches/t007-assembly-entry-point.patch` (sha256 `361c037d…a277d`), and `installer-2026-10-08.md` | lane 3: **CHANGES REQUIRED** on the patch: confirm the gitlink before running the pinned implementation on the remote path (review 5471121210, T3). Lane 1 puts that change on this branch (#186 comment 6088714587, item 3) | | `004-migrate-to-triad-candidate` `935f53f` (new: created at `b21c511` at 01:58:43Z, then `b21c511` → `935f53f` at 02:43:24Z; no PR) | `candidate-2026-10-09.md`: the composed candidate, built from #190 `dbc6837` and #191 `2411f40` (`:29–30`); receipt `candidate-20261009-JR8YyA` | lane 3: **REPRODUCES**, fit for the composed run (#186 comment 6082068576). Its section 5 records that no suite run had finished. Lane 1's r2 runs finished at 18:20:47Z (lane 2's reading, #186 comment 6087267593); no record of them is on origin. The receipt is not on brett-wip `ea6c09d1` | | `004-migrate-to-triad-t018` `63bff08` (new, 20:28:03Z; no PR) | lane 1's T018 exercise (#186 comment 6088703549) | — | | `feat/triad-lane-tooling` `30f9dc0` | `c45a452` → `2aa8a59` → `96da9af` → `30f9dc0` (14:18:02Z) | no PR | | `feat/test-roots-manual-guard` `b50f197` (new: created at 02:29:44Z; no PR of its own) | the manual-guard fix, one commit on `dbc6837`, which #190 has carried since 13:59:10Z | landed with #190 (row above); the branch is still on origin | -| `feat/test-roots-followups` `01ccc86` (new: created at `2064390` at 02:14:37Z) | #190's follow-ups (#193 items 2–7); `2064390` → `7144eef` (02:31:20Z), then `7144eef` → `01ccc86` (20:31:02Z), a merge of main `f87fd5a` (re-read 20:36:57Z) | lane 3's review has resumed (#186 comment 6088703549) | +| `feat/test-roots-followups` `01ccc86` (new: created at `2064390` at 02:14:37Z) | #190's follow-ups (#193 items 2–7); `2064390` → `7144eef` (02:31:20Z), then `7144eef` → `01ccc86` (20:31:02Z), a merge of main `f87fd5a` (re-read 20:36:57Z) | lane 3's review has resumed (#186 comment 6088703549). *Corrected 2026-10-11 (lane 3's 5481348731 on #196, item 7): the verdict came after this record's origin read (20:36:57Z for this head). It is #186 comment 6089801221 (2026-10-09T21:47Z), on `01ccc86`: fit for a PR onto main after one edit, which is to drop or re-premise the 340 → 347 assembly cap (its finding 7). It answers the code-half part of #193 item 2.* | | `004-migrate-to-triad-reads` `a7a8e00` | `a6830a1` → `a7a8e00` (13:50:08Z): lane 2's `gate-b-status-2026-10-09.md` | no PR; lane 1's own walk goes into its next evidence PR (#186 comment 6088714587, item 1) | | brett-wip `99eed35` | Gate A capture tooling v2, which answers this record's three findings (#186 comment 6073036070) | lane 2's final verification: `capture.sh:170` rewrites its source's index, and `restore.sh:132` can aim at `/` (#186 comment 6087261307). Lane 1 accepts both; no capture runs on a real checkout until a fixed version is re-verified (#186 comment 6088714587, item 4) | | issue #193 (13:42:32Z; updated 20:26:33Z) | the follow-ups deferred at the landing of #190 and #192: item 1 resolved by #190, item 15 added | — | @@ -510,7 +510,7 @@ in `root-guidance-2026-10-08.md` are on main since #192 (`1abee1d`). | Follow-up (where named) | Patch, branch or PR | Owner | State at 20:30Z | | --- | --- | --- | --- | | T006's five patches (the table above) | `patches/t006/`, on main since #192 | lane 1 (T006) | reviewed: LAND (5464893559). #2, #3 and the bump `6ccdaf8` regenerate at T014 | -| T010's two patches (the table above) | `004-migrate-to-triad-ci` `d28e2cc` | lane 1 (T010) | no review verdict posted; lane 3's reviewer has resumed (#186 comment 6088703549). Both are authored by the fixture identity, to carry a real author at T014 (#186 comment 6082068576, finding 9) | +| T010's two patches (the table above) | `004-migrate-to-triad-ci` `d28e2cc` | lane 1 (T010) | no review verdict posted; lane 3's reviewer has resumed (#186 comment 6088703549). Both are authored by the fixture identity, to carry a real author at T014 (#186 comment 6082068576, finding 9). *Corrected 2026-10-11 (lane 3's 5481348731 on #196, item 7): the verdict came after this record's origin read. It is #186 comment 6089801221 (2026-10-09T21:47Z), on `d28e2cc`: both patches apply (to run C, and the code-leg patch also to `f87fd5a`), and the code leg's `tests.yml` keeps everything the gate reads. It is fit for evidence-3, with #180 to settle first, because `composed.yml` runs the suite through `run.sh` (its finding 13). It repeats the author point: give both patches a real author at T014.* | | T007's assembly entry point | `004-migrate-to-triad-installer` `51993a4` | lane 1 (T007) | CHANGES REQUIRED (5471121210, T3) | | T007/T008's code side | #191 `13cc752` | lane 1 (T007) | LAND with conditions (5471121210); next head `fdc5f18` announced. Open there: the parent-directory walk (delta 4, which `fdc5f18` answers per lane 1) and the hygiene test passing by spelling (delta 5) | | #190's manual guard (#193 item 1) | in #190 since `b50f197` | lane 1 (T009) | resolved: landed with #190 | From 2c040d685cfbe8b445f044b716d4b5d6fcb45840 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sun, 11 Oct 2026 16:55:21 +0000 Subject: [PATCH 35/42] Name the host behind "the bench's openspec is now 1.13.1" in run C's record, and give lane 3's 1.2.0 and 1.6.0 reading (lane 3's 5481348731, item 9) Lane 3's review 5481348731 on #196, item 9, left one [low] on adopter-rehearsal-2026-10-08.md:53: "The bench's `openspec` is now 1.13.1." That depends on the container. On lane 3's host at 00:1xZ on 2026-10-11, `which -a openspec` lists a user-level npm install first, at 1.2.0, and the system install second, at 1.6.0. `validate migrate-to-triad --strict` then exits 1 under 1.2.0 (the SHALL or MUST error) and 0 under 1.6.0. The sentence now names the bench, lane 2's, and the dates of the two readings (6088829392 item 8 on 2026-10-09; 6103482530 on 2026-10-10). A dated correction records the first wording, says the version depends on the container, and gives lane 3's reading with its exit codes. It points at lane 1's walk, section 4 (a), which records the same split. Locations are described, not written as paths. gate-b-status-2026-10-09.md:266 repeats the claim and is lane 1's file, so it is left for its owner. Part of #186. Co-Authored-By: Claude Sonnet 5.5 Lane: openRepoTools-2 Co-Authored-By: Claude Opus 5.5 --- .../adopter-rehearsal-2026-10-08.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) 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 index 863f9ad0..2bf0d970 100644 --- a/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-08.md +++ b/specs/004-migrate-to-triad/adopter-rehearsal-2026-10-08.md @@ -50,9 +50,19 @@ the private receipt. noted that `openspec validate migrate-to-triad --strict` with `openspec` 1.2.0 fails on pristine `837928a` too (`ADDED "Installation retains the public interface and one revision" must contain SHALL or MUST`), so run C - does not cause it. The bench's `openspec` is now 1.13.1. On 2026-10-09 - the same command exited 0 on `837928a` and on `71b05cb`, #192's head, - whose tree its squash `1abee1d` carries unchanged. + does not cause it. Lane 2's bench had `openspec` 1.13.1 (read on + 2026-10-09, #186 comment 6088829392 item 8, and again on 2026-10-10, + 6103482530). On 2026-10-09 the same command exited 0 on `837928a` and on + `71b05cb`, #192's head, whose tree its squash `1abee1d` carries unchanged. + *Corrected 2026-10-11 (lane 3's 5481348731 on #196, item 9): this note first + said "The bench's `openspec` is now 1.13.1", as if one version held on every + host. The version depends on the container. On lane 3's host on 2026-10-11 at + 00:1xZ, `which -a openspec` lists a user-level npm install first, at 1.2.0, + and the system install second, at 1.6.0. The validate command above exits 1 + under 1.2.0 (the SHALL or MUST error) and 0 under 1.6.0. So the result follows + which `openspec` comes first on the reader's `PATH`. Lane 1's walk records + the same split + ([gate-b-status-2026-10-09.md](gate-b-status-2026-10-09.md), section 4 (a)).* ## Setup From 83bcec725641a9086d032bfadb00ac9bc59d6c67 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sun, 11 Oct 2026 16:55:34 +0000 Subject: [PATCH 36/42] Correct root-guidance:432 after lane 2's check of 1578f99: the fourth test reads the same file at 837928a and since #190, and the note records the line's first wording (lane 3's 5481348731, item 8) Lane 2's checker verified 1578f99. Every measurement in it holds, but its wording on line 432 needed a fix. - test_the_root_carries_the_line_ending_rule reads the code root's own .gitattributes the same way at 837928a and at f87fd5a. #190 changed only its docstring. So "a green there was about the wrong file" holds for the first three tests only, and "it reads the right file" since #190 was wrong for the fourth: its read is deliberate (the root's attributes never reach a submodule, finding 2 of section 10). - The line did not record what it first said. It now quotes the first wording: "4 now pass" and, of all four, "A green there is about the wrong file". - "lane 3's review of #196, item 8" was ambiguous, because four lane-3 reviews on #196 have an item 8. The note now cites lane 3's 5481348731 by id. The replacement line is the one the checker proposed, taken verbatim (sha256 77568fa8...64d). Nothing else in the file moves. Part of #186. Co-Authored-By: Claude Sonnet 5.5 Lane: openRepoTools-2 Co-Authored-By: Claude Opus 5.5 --- specs/004-migrate-to-triad/root-guidance-2026-10-08.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 1b449655..c70514e1 100644 --- a/specs/004-migrate-to-triad/root-guidance-2026-10-08.md +++ b/specs/004-migrate-to-triad/root-guidance-2026-10-08.md @@ -429,7 +429,7 @@ Lane 2 is reading that side. - Measured on run C's code leg (`837928a`'s code paths) alone with `-k repo_hygiene`, which selects only `test_repo_hygiene.py`: 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). - The command test is outside that subset. Called directly on `837928a`'s code paths on 2026-10-09, it fails before `code-0001` (`FileNotFoundError` on `README.md`) and still fails after it (`AssertionError`: the leg's README has no install-receipt paragraph). - *Since #190 (`f87fd5a`; noted 2026-10-10):* 17 of the 18 read their file from the root that owns it, through `ROOTS.path_for` in `tests/conftest.py`. `README.md`, `AGENTS.md` and `CLAUDE.md` belong to the assembly root and `docs/README-lanes.md` to the spec root, so in a standalone code leg these 17 skip, and a composed run reads them from those roots. The 18th, `test_the_root_carries_the_line_ending_rule`, deliberately reads the code root's own `.gitattributes`. Called directly on main's code paths (`f87fd5a`), the 17 skip, including the command test. The 18th raises `FileNotFoundError` before `code-0001` and passes after it. - - **At `837928a`, 4 passed 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 was about the wrong file. *Corrected 2026-10-11 (lane 3's review of #196, item 8): since #190 (`f87fd5a`), only the last of the four passes in a standalone leg, and it reads the right file. The first three read through `ROOTS.path_for`, so they skip, with or without the leg's own `AGENTS.md`, `CLAUDE.md` and `README.md` from `code-0001`. Run directly on main's code paths (`f87fd5a`, spec trees and front-door files removed): before `code-0001`, 3 skip and 1 fails (`FileNotFoundError` on `.gitattributes`); after it, 3 skip and 1 passes.* + - **At `837928a`, 4 passed 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`. For the first three, a green there was about the wrong file. *Corrected 2026-10-11 (lane 3's 5481348731 on #196, item 8): this line first said "4 now pass" and, of all four, "A green there is about the wrong file". Since #190 (`f87fd5a`), the first three read through `ROOTS.path_for`, so in a standalone leg they skip, with or without the leg's own `AGENTS.md`, `CLAUDE.md` and `README.md` from `code-0001`. The fourth still reads the code root's own `.gitattributes`, as it did at `837928a`. #190 changed only its docstring, which makes that read deliberate: the root's attributes never reach a submodule (finding 2). So its green is about the right file. Run directly on main's code paths (`f87fd5a`, spec trees and front-door files removed): before `code-0001`, 3 skip and 1 fails (`FileNotFoundError` on `.gitattributes`); after it, 3 skip and 1 passes.* - 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. From e81100c8cbb65064aed58ca9a1b6580cbc029829 Mon Sep 17 00:00:00 2001 From: Brett Heap <1513478+brettheap@users.noreply.github.com> Date: Sun, 11 Oct 2026 17:00:40 +0000 Subject: [PATCH 37/42] Record r3 of the Gate B candidate: #191 at its final head 8246983 with T007's v3 entry point, the install modules green in composed mode, F1 gone, and the install still differing from main only in the implementation it hands over to. Code 9c11276, spec 9e1d006, assembly 3891943; every git am exited 0 and both pins were regenerated with bump-leg.py. The four install modules, composed and isolated with SIGINT at its default: 281 passed, 0 failed, 0 skipped. The intermediate r3a at 6568f08 (278 passed) is recorded beside it. The full composed suite is redone at the frozen source for Gate C. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../candidate-2026-10-09.md | 101 +++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/specs/004-migrate-to-triad/candidate-2026-10-09.md b/specs/004-migrate-to-triad/candidate-2026-10-09.md index c2c61779..c8628cb2 100644 --- a/specs/004-migrate-to-triad/candidate-2026-10-09.md +++ b/specs/004-migrate-to-triad/candidate-2026-10-09.md @@ -34,6 +34,11 @@ it says r1. entry-point patch to v3 (`004-migrate-to-triad-installer` `4f773b8`). Neither is in this record (section 6). +**r3 (2026-10-11), section 9:** #191 at its final head `8246983`, with T007 v3 (patch +sha256 `67d8e7ce…`). Code `9c11276`, spec `9e1d006`, assembly `3891943`. The install +modules, composed: 281 passed, 0 failed, 0 skipped. F1 is gone. The install differs from +main `f87fd5a` only in the implementation it hands over to. + ## 1. Inputs Every input was read without checking it out in any existing worktree. Patches were @@ -300,7 +305,9 @@ run has one. is `.gitattributes`: that code leg did not carry T006's `code-0001`. - CI on `6568f08`: `tests` 1416 passed, 9 skipped. - Neither is this candidate, and neither is the full composed suite. - - The record owes a composed rerun at `6568f08` with T007 v3. The rebuild takes about 5 + - The record owes a composed rerun at `6568f08` with T007 v3. *(2026-10-11: done in + scoped form at the final head `8246983`, in section 9. The full composed suite is + redone at the frozen source for Gate C.)* The rebuild takes about 5 minutes: the code leg plus a regenerated code bump, and the assembly with the v3 patch in place of v1. - The full composed run took 1:48 at load 130 here. CI's `tests` takes 45–48 minutes. @@ -404,5 +411,97 @@ written. | `rehearsal-listing-after.txt` | `1dbf1ae38a9f98fac7b9f1ded1f0b6b2d699cc8ecb7b4f914c2cd5171f412038` | | `network-shim-calls.log` | `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855` | +## 9. r3: the install path at #191's final head — October 10–11, 2026 + +**Why a scoped rerun.** After r2, #191 moved on: +- `6568f08` carried F1's fix; +- `cddd1f7` installs exactly the bytes that were checked from a mounted code leg; +- `8246983` is lane 3's LAND. + +T007's entry point also moved, to v3. On the coordinator's order this is the scoped +rerun section 6 offered: the install modules composed, plus the install comparison. The +full composed suite is redone once, at the frozen source, for Gate C. + +**Inputs.** Every input not listed here is r2's: + +| Input | Identity | +| --- | --- | +| PR #191 | `8246983`. Its 10 non-merge commits since `f87fd5a`, by `git format-patch f87fd5a..8246983`. The merge of main, `45979a4`, has no conflict hunks (`git show --cc` is empty) | +| PR #190 | as landed, `f87fd5a`. Every path under `tests/` has `b50f197`'s blob, so r2's #190 stack (`6e294fe`) is the base | +| T007 entry point v3 | `specs/004-migrate-to-triad/patches/t007-assembly-entry-point.patch` at `004-migrate-to-triad-installer` `67a978c`: blob `407741b`, unchanged since `4f773b8`; **sha256 `67d8e7ce8b0f7008d2151a44c0eabaf74cda7cd2697c6e09ee7da0c341c671b5`**, as `installer-2026-10-08.md` records. It writes `openRepoTools` as blob `ff432c6`, mode 100755, sha256 `8248224d…` | + +**Build. Every `git am` exited 0, with no resolution:** + +| Leg | Steps | Commit | Tree | +| --- | --- | --- | --- | +| code | `6e294fe` + #191's 10 commits (`e06fd90` … `830f673`) | `830f673` | `b0d15e4a` | +| code | + T006 `code-0001` (`6e4761b`) + T010 `tests.yml` | **`9c112769d4311675b4eee89852cf7e51384bfb4b`** | **`e53dfa62`**; pinned digest `268cd8ca6dcb5cd35a1c7060ee112399d060db41bba9d4b0ed4a08bcdb10dd0b` | +| spec | unchanged | **`9e1d006`** | `10256135` | +| assembly | `69b8924` + `assembly-0001` (`4f1e522`) + `composed.yml` (`738857c`) + T007 v3 (`325131e`) + `bump-leg.py` spec (`23439bb`) and code | **`3891943274f74b6c87683153e35ee9a961b5b1eb`** | **`fd997c3d`** | + +What was checked: +- **The code leg is #191's final head.** All 51 code-leg paths that `8246983` carries + have its blob. +- **The assembly is valid.** `bootstrap.py` reports both legs "branch tip == pin"; + `make validate` exits 0; `composed.yml`'s context step exits 0. +- **It reproduces.** A fresh recursive clone bootstraps and validates the same way. + +**The install modules, composed** (`test_install_payload_source.py`, +`test_openrepotools_command.py`, `test_install_skill_and_hook.py`, +`test_install_by_rename.py`): +- **How:** isolated, with `HOME`, `TMPDIR`, `--basetemp` and `PYTHONPYCACHEPREFIX` in + scratch. `OPENREPOTOOLS_COMPOSED=1`, with the assembly and spec roots named. +- **SIGINT at its default:** reset before `flock`, and printed as + `default_int_handler` by the process that then became pytest, so F5 cannot recur. +- **The lock:** one `flock` on the workstation lock, 2026-10-11T16:33:32Z–16:59:15Z, + at load about 150. + +| Run | Result | +| --- | --- | +| **r3, `8246983` + T007 v3** | **281 passed, 0 failed, 0 skipped** in 25:39 | +| r3a, `6568f08` + T007 v3 (2026-10-10T23:52Z, superseded within the hour by `cddd1f7`) | 278 passed, 0 failed, 0 skipped in 10:59; code `663d775`, assembly `e195ba9` | + +**F1 is gone.** `test_r14_the_documented_upstream_skip_is_unchanged` passes in both +module runs. Run alone in r3's composed tree it is `PASSED` (0.53 s). At `8246983` the +test reads the command from conftest's own text, so composed mode no longer depends on +`NEEDS_UPSTREAM` being a `skipif`. + +**The install, compared with main `f87fd5a`.** Same method as section 4 (one disposable +home path, the same seed, `env -i`, refusing shims: **0 calls**): + +| Stage | Result | +| --- | --- | +| A: main `f87fd5a`'s `./openRepoTools --install` | exit 0; `17 of 17 placed`; 29 `installed at`; 29 receipt rows | +| B: the r3 assembly root's `./openRepoTools --install` (T007 v3) | exit 0. First line: "source: the code mounted at `/code`, opensoft/openRepoTools-code at 9c112769d431… as `` pins it; every payload file was checked against that commit and nothing fetched". Then `17 of 17 placed`, 29 rows | +| B2: the candidate again | exit 0; 31 `unchanged` | +| C: main again, over B | exit 0; 1 `updated`, 30 `unchanged` | + +Between A and B the only differences are these two: +- `~/.local/bin/openRepoTools`: `47cfe74e…` becomes `697fdbd4977454f18b80f33d3501c8d2b7dee44335f1afafbd1e1595d76f2aed`, + which is the bytes of `8246983:openRepoTools`, not the entry point (`8248224d…`); +- that file's receipt row, and the receipt's timestamps. + +`settings.json` is byte-identical, and the foreign command and skills are untouched. C +restores A, except the receipt's timestamps. r3a gave the same result with +`6568f08`'s implementation, `b2ff0f1c…`. + +**What r3 changes, and what it does not.** +- **F1 is closed** at #191's final head. +- **The install path is green** in composed mode with T007 v3. +- **F2–F6 stand as classified in section 5.** None is in these modules. +- **This is not the full composed suite.** That run is redone once at the frozen source, + for Gate C. + +| r3 log (in the receipt's `logs/`) | SHA256 | +| --- | --- | +| `90-r3f-build.log` | `1654219464e453fdea2cc652b07decea9e6baf374055660b0ea485b9863b252a` | +| `92-r3f-install-modules-composed.log` | `3eb6c875cdd223aaaf174ea5a8cb4fd09f1d2238323406326fd4664641e572b3` | +| `93-r3f-install-check.log` | `364bd77941704c420008fa5f9ad13fc90da43e6cf6c3009acdc5e23149eb5095` | +| `94-r3f-install-compare.log` | `09369829865ad226f4c2e8a4e71852c738ccb5981a5082dc8891caebd40acee0` | +| `95-r3f-r14-composed.log` | `e66b0843a2ffa3fb74f386615ae0e6d40ad3a4f42bdbf6f3d127856e78c7f567` | +| `80-r3-build.log` (r3a) | `1663134c45fbc3c3f8588a72d461d2e20205d653eca28861b0323e50d6325754` | +| `82-r3-install-modules-composed.log` (r3a) | `bc11cf7936a75b9ba0851beca7b311dae7fd9d614103296ff36207b8cf156ddf` | +| `84-r3-install-compare.log` (r3a) | `d3d15457017dc0fe84a0a5338a005c177ea0b5c118233a12ffb595c853d42674` | + This record authorizes no conversion, repository creation, merge, default-branch move or lane rebinding. From 118a2e20efaf76e88f7004460f5b9f49dd701053 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Tue, 13 Oct 2026 18:30:58 +0000 Subject: [PATCH 38/42] Close W22's findings 2-4 on 83bcec7 in the T011 record: date the section 7 leftover, restore "before the composed job is relied on", name the branch at :443 (lane 3's 5481348731, item 7) W22's adversarial check of 83bcec7 (lane 2) found three leftovers of lane 3's 5481348731 item 7, in specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md. Finding 2 [low]: section 7 item 4 ("Refreshed 2026-10-09T20:30Z") still said "The T010 patches: no verdict yet; lane 3's reviewer has resumed (#186 comment 6088703549)" with no pointer, while :436 and :513 already point at 6089801221. It now carries the same dated correction, 2026-10-11 like its siblings: the verdict, #186 comment 6089801221 (2026-10-09T21:47Z), came after the record's origin read; fit for evidence-3, with #180 to settle before the composed job is relied on (its finding 13). The original words stay as written. Finding 3 [nit]: :436 and :513 said "One thing to settle first" and "with #180 to settle first". 6089801221 (c) says "One thing must be settled before the composed job is relied on". Both rows now carry that condition, so it attaches to relying on the composed job and not to "fit for evidence-3". Finding 4 [nit]: :443 said "It answers the code-half part of #193 item 2" right after a sentence that begins "It is #186 comment 6089801221", so "It" read as the comment. It now says "The branch `01ccc86` answers ...". One file, +7/-4. Part of #186. Lane: openRepoTools-2 Co-Authored-By: Claude Opus 5.5 --- .../gate-ab-evidence-2026-10-09.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md b/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md index a7b3ed48..f6c2809e 100644 --- a/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md +++ b/specs/004-migrate-to-triad/gate-ab-evidence-2026-10-09.md @@ -433,14 +433,14 @@ squash equals its reviewed head: `1abee1d`'s tree is `71b05cb`'s, and `f87fd5a`' | #192, landed as `1abee1d` (head `71b05cb`; `004-migrate-to-triad-evidence-2` deleted) | run C's records, its plan and T006's `root-guidance-2026-10-08.md` and five patches are on main | lane 3: **LAND** on `71b05cb` (review 5464893559), with an exact rebuild of all five T006 patch commits; lane 2: run C's receipt verified, all six checks pass (#186 comment 6072724043) | | `004-migrate-to-triad-root-guidance` `041a273`, `004-migrate-to-triad-rehearsal-c` `1a26463` | unchanged; their content landed with #192 | as #192 | | #190, landed as `f87fd5a` (head `b50f197`; `feat/test-roots-for-the-triad` deleted) | `e3a76b5` → `dbc6837` (01:22:43Z) → `b50f197` (13:59:10Z, the manual guard, #193 item 1) | **LAND** on `dbc6837` from lane 3 (5464931249) and lane 1's independent review (5464938427); **LAND** on the `b50f197` delta (5471080687); CI green on `b50f197`, `tests-macos` included | -| `004-migrate-to-triad-ci` `d28e2cc` | `cc7c7a1` → `b4ef6b5` → `d28e2cc` (02:30:57Z); only `test-roots-and-ci-2026-10-08.md` changed, and both T010 patch blobs are identical at both heads | no review verdict posted; lane 3's reviewer of the T010 patches has resumed (#186 comment 6088703549). *Corrected 2026-10-11 (lane 3's 5481348731 on #196, item 7): the verdict came after this record's origin read of 20:29–20:30Z. It is #186 comment 6089801221 (2026-10-09T21:47Z), on `d28e2cc` (unchanged): fit for evidence-3, carried as its three new files on main. One thing to settle first: `composed.yml` runs the suite through `run.sh`, which brings #180 into the one job that must be green (its finding 13).* | +| `004-migrate-to-triad-ci` `d28e2cc` | `cc7c7a1` → `b4ef6b5` → `d28e2cc` (02:30:57Z); only `test-roots-and-ci-2026-10-08.md` changed, and both T010 patch blobs are identical at both heads | no review verdict posted; lane 3's reviewer of the T010 patches has resumed (#186 comment 6088703549). *Corrected 2026-10-11 (lane 3's 5481348731 on #196, item 7): the verdict came after this record's origin read of 20:29–20:30Z. It is #186 comment 6089801221 (2026-10-09T21:47Z), on `d28e2cc` (unchanged): fit for evidence-3, carried as its three new files on main. One thing must be settled before the composed job is relied on: `composed.yml` runs the suite through `run.sh`, which brings #180 into the one job that must be green (its finding 13).* | | #191 `13cc752` | `ecd7aa4` → `2411f40` (01:20:38Z) → `13cc752` (02:42:51Z) | **DO NOT LAND** on `2411f40` from lane 3 (5464963807) and lane 1's independent review (5465121368); **LAND** on `13cc752` from lane 3's delta review (5471121210), on condition that the PR body is corrected before the squash and `tests-macos` runs on `ready`. CI on `13cc752`: every job passed except `tests-macos`, skipped without `ready`. Lane 1's next head, `fdc5f18`, is announced and not on origin (#186 comment 6088714587, item 3) | | `004-migrate-to-triad-installer` `51993a4` (new, 01:21:15Z; no PR) | T007's entry point, `patches/t007-assembly-entry-point.patch` (sha256 `361c037d…a277d`), and `installer-2026-10-08.md` | lane 3: **CHANGES REQUIRED** on the patch: confirm the gitlink before running the pinned implementation on the remote path (review 5471121210, T3). Lane 1 puts that change on this branch (#186 comment 6088714587, item 3) | | `004-migrate-to-triad-candidate` `935f53f` (new: created at `b21c511` at 01:58:43Z, then `b21c511` → `935f53f` at 02:43:24Z; no PR) | `candidate-2026-10-09.md`: the composed candidate, built from #190 `dbc6837` and #191 `2411f40` (`:29–30`); receipt `candidate-20261009-JR8YyA` | lane 3: **REPRODUCES**, fit for the composed run (#186 comment 6082068576). Its section 5 records that no suite run had finished. Lane 1's r2 runs finished at 18:20:47Z (lane 2's reading, #186 comment 6087267593); no record of them is on origin. The receipt is not on brett-wip `ea6c09d1` | | `004-migrate-to-triad-t018` `63bff08` (new, 20:28:03Z; no PR) | lane 1's T018 exercise (#186 comment 6088703549) | — | | `feat/triad-lane-tooling` `30f9dc0` | `c45a452` → `2aa8a59` → `96da9af` → `30f9dc0` (14:18:02Z) | no PR | | `feat/test-roots-manual-guard` `b50f197` (new: created at 02:29:44Z; no PR of its own) | the manual-guard fix, one commit on `dbc6837`, which #190 has carried since 13:59:10Z | landed with #190 (row above); the branch is still on origin | -| `feat/test-roots-followups` `01ccc86` (new: created at `2064390` at 02:14:37Z) | #190's follow-ups (#193 items 2–7); `2064390` → `7144eef` (02:31:20Z), then `7144eef` → `01ccc86` (20:31:02Z), a merge of main `f87fd5a` (re-read 20:36:57Z) | lane 3's review has resumed (#186 comment 6088703549). *Corrected 2026-10-11 (lane 3's 5481348731 on #196, item 7): the verdict came after this record's origin read (20:36:57Z for this head). It is #186 comment 6089801221 (2026-10-09T21:47Z), on `01ccc86`: fit for a PR onto main after one edit, which is to drop or re-premise the 340 → 347 assembly cap (its finding 7). It answers the code-half part of #193 item 2.* | +| `feat/test-roots-followups` `01ccc86` (new: created at `2064390` at 02:14:37Z) | #190's follow-ups (#193 items 2–7); `2064390` → `7144eef` (02:31:20Z), then `7144eef` → `01ccc86` (20:31:02Z), a merge of main `f87fd5a` (re-read 20:36:57Z) | lane 3's review has resumed (#186 comment 6088703549). *Corrected 2026-10-11 (lane 3's 5481348731 on #196, item 7): the verdict came after this record's origin read (20:36:57Z for this head). It is #186 comment 6089801221 (2026-10-09T21:47Z), on `01ccc86`: fit for a PR onto main after one edit, which is to drop or re-premise the 340 → 347 assembly cap (its finding 7). The branch `01ccc86` answers the code-half part of #193 item 2.* | | `004-migrate-to-triad-reads` `a7a8e00` | `a6830a1` → `a7a8e00` (13:50:08Z): lane 2's `gate-b-status-2026-10-09.md` | no PR; lane 1's own walk goes into its next evidence PR (#186 comment 6088714587, item 1) | | brett-wip `99eed35` | Gate A capture tooling v2, which answers this record's three findings (#186 comment 6073036070) | lane 2's final verification: `capture.sh:170` rewrites its source's index, and `restore.sh:132` can aim at `/` (#186 comment 6087261307). Lane 1 accepts both; no capture runs on a real checkout until a fixed version is re-verified (#186 comment 6088714587, item 4) | | issue #193 (13:42:32Z; updated 20:26:33Z) | the follow-ups deferred at the landing of #190 and #192: item 1 resolved by #190, item 15 added | — | @@ -510,7 +510,7 @@ in `root-guidance-2026-10-08.md` are on main since #192 (`1abee1d`). | Follow-up (where named) | Patch, branch or PR | Owner | State at 20:30Z | | --- | --- | --- | --- | | T006's five patches (the table above) | `patches/t006/`, on main since #192 | lane 1 (T006) | reviewed: LAND (5464893559). #2, #3 and the bump `6ccdaf8` regenerate at T014 | -| T010's two patches (the table above) | `004-migrate-to-triad-ci` `d28e2cc` | lane 1 (T010) | no review verdict posted; lane 3's reviewer has resumed (#186 comment 6088703549). Both are authored by the fixture identity, to carry a real author at T014 (#186 comment 6082068576, finding 9). *Corrected 2026-10-11 (lane 3's 5481348731 on #196, item 7): the verdict came after this record's origin read. It is #186 comment 6089801221 (2026-10-09T21:47Z), on `d28e2cc`: both patches apply (to run C, and the code-leg patch also to `f87fd5a`), and the code leg's `tests.yml` keeps everything the gate reads. It is fit for evidence-3, with #180 to settle first, because `composed.yml` runs the suite through `run.sh` (its finding 13). It repeats the author point: give both patches a real author at T014.* | +| T010's two patches (the table above) | `004-migrate-to-triad-ci` `d28e2cc` | lane 1 (T010) | no review verdict posted; lane 3's reviewer has resumed (#186 comment 6088703549). Both are authored by the fixture identity, to carry a real author at T014 (#186 comment 6082068576, finding 9). *Corrected 2026-10-11 (lane 3's 5481348731 on #196, item 7): the verdict came after this record's origin read. It is #186 comment 6089801221 (2026-10-09T21:47Z), on `d28e2cc`: both patches apply (to run C, and the code-leg patch also to `f87fd5a`), and the code leg's `tests.yml` keeps everything the gate reads. It is fit for evidence-3, with #180 to settle before the composed job is relied on, because `composed.yml` runs the suite through `run.sh` (its finding 13). It repeats the author point: give both patches a real author at T014.* | | T007's assembly entry point | `004-migrate-to-triad-installer` `51993a4` | lane 1 (T007) | CHANGES REQUIRED (5471121210, T3) | | T007/T008's code side | #191 `13cc752` | lane 1 (T007) | LAND with conditions (5471121210); next head `fdc5f18` announced. Open there: the parent-directory walk (delta 4, which `fdc5f18` answers per lane 1) and the hygiene test passing by spelling (delta 5) | | #190's manual guard (#193 item 1) | in #190 since `b50f197` | lane 1 (T009) | resolved: landed with #190 | @@ -616,7 +616,10 @@ resolved mapping and a passing compatibility test matrix" (plan.md). Against tha - #191: LAND on `13cc752` with conditions (5471121210). - The T007 patch: CHANGES REQUIRED (5471121210). - The T010 patches: no verdict yet; lane 3's reviewer has resumed (#186 comment - 6088703549). + 6088703549). *Corrected 2026-10-11 (lane 3's 5481348731 on #196, item 7): the + verdict came after this record's origin read. It is #186 comment 6089801221 + (2026-10-09T21:47Z): fit for evidence-3, with #180 to settle before the composed + job is relied on (its finding 13).* 5. **Mapping.** Main carries run B's plan, source `e43b243`, which survives only as `refs/pull/188/head`. Run C's plan, source `837928a`, is in #192, unlanded. From 7668c51fc97598152dff4111901fdd4ac6c7d8db Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Tue, 13 Oct 2026 18:35:29 +0000 Subject: [PATCH 39/42] Correct the installer record for lane 3's DO NOT LAND on #196's installer files: no host path, a T008 table of the tests at the final head, and a disposition for every open item. Lane 3's review 5481368376 asked for six corrections and noted one minor point; all seven are made here. The record no longer names a host-absolute location for run C's README (finding 0). It says what v3 closes, the mismatched pin, and what it does not: a lockstep-consistent pin at code from before #191, which is now a named T015 item with a capability check proposed for #193 (finding 2). The T008 table now lists every test in tests/test_install_payload_source.py at 8246983, which is main a74c7fc (52 tests, 109 cases). Its base column comes from that module run against c4864ac's installer (finding 3). The record names 8246983 as the head, with its CI and the landing on main as a74c7fc on 2026-10-13 (finding 4). The wip_shape_ref disposition gives the F2 fallbacks (finding 5). Each of lane 2's nine open items in #186 6090038382 is fixed, accepted or given to a task (finding 6). The minor point: "five things" and both reviews of 2411f40 (finding 7). The patch is unchanged. Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../installer-2026-10-08.md | 82 +++++++++++++------ 1 file changed, 56 insertions(+), 26 deletions(-) diff --git a/specs/004-migrate-to-triad/installer-2026-10-08.md b/specs/004-migrate-to-triad/installer-2026-10-08.md index 700d3398..d8d40117 100644 --- a/specs/004-migrate-to-triad/installer-2026-10-08.md +++ b/specs/004-migrate-to-triad/installer-2026-10-08.md @@ -4,13 +4,15 @@ **Code:** [#191](https://github.com/opensoft/openRepoTools/pull/191), branch `feat/installer-payload-source-resolution` (head `8246983`, based on `c4864ac` with main `f87fd5a` merged in as `45979a4`). +**Landed:** #191 landed on main as `a74c7fc` on 2026-10-13, the squash of `8246983`. + **Assembly entry point:** [patches/t007-assembly-entry-point.patch](patches/t007-assembly-entry-point.patch). It cannot land on `main` before the split, because its path is the implementation's. **Writer:** lane openRepoTools-1. ## The rehearsed arrangement relied on -Run C (`/workspace/projects/.openrepotools-private-prep/work-20261008/rehearsal/README.txt`), cloned out of it read-only: +Run C (its `rehearsal/README.txt` in the private prep area's `work-20261008/`), cloned out of it read-only: | Repository | Commit | | --- | --- | @@ -67,9 +69,10 @@ Each key that is read must appear once. The file must be LF-only, quotes must cl - History: - v1, `a02a45c` (tree `a9fa46e`): the first version. - v2, `8ed83b7` (tree `79db0a4`): `recorded_gitlink` reads the gitlink with `read` instead of `awk` (see the runs below). - - v3, this commit: on the remote path, the gitlink is now checked against the pin BEFORE any implementation is fetched or run. This answers lane 3's review of the v2 patch (#191 review 5471121210, T3), which found a stale or rolled-back pin running whichever installer it selected, with a misleading refusal. + - v3, this commit: on the remote path, the gitlink is checked against the pin BEFORE any implementation is fetched or run. That is the change T3 of lane 3's review of v2 required (#191 review 5471121210): a pin its gitlink disagrees with now refuses, naming both commits, with no request to the code repository (measured by lane 3 in 5481368376, finding 1). + - **What v3 does not close** is T3's second case, a pin and gitlink that agree on code from before #191 (a rollback, or run C's own `73b04d6`). It passes the check, and the root then runs that older installer, which reads the assembly root as a single repository. On a real root that is a refusal in that installer's words, with nothing placed and the cause unnamed. With decoy files at the root (synthetic) it installs them (lane 2's item 7; lane 3's 5481368376, finding 2). This is recorded as a T015 item below, and a capability check is proposed for #193. - **In a checkout,** it requires the pin (strict grammar), the gitlink (index first, then HEAD) and `code/openRepoTools`'s raw bytes (`hash-object --no-filters`) to name one commit. Then it `exec`s `code/openRepoTools` with every argument. That is the same process, so the exit status, signals and the implementation's own temporary-file cleanup are the implementation's. There are no temporaries of its own. -- **Anywhere else** (stdin, or a copy of the file on its own), it does four things: +- **Anywhere else** (stdin, or a copy of the file on its own), it does five things: - it resolves `OPENREPOTOOLS_REF` once, `gh` first and `curl` second; - it reads `contracts/code-pin.yaml` at that commit; - it reads the gitlink at the pin's `submodule_path` in that commit (one level of the tree, by `gh api --jq`, or by `curl` and `jq`). It refuses, naming both commits and with "nothing ran", unless the gitlink is the pinned commit; @@ -86,7 +89,7 @@ Each key that is read must appear once. The file must be LF-only, quotes must cl - The standard's `scripts/bump-leg.py` (tool `7f84ca4`, with `--local-remote-dir` pointing at that copy) made the lockstep commits. Its validators printed `pins ok` and `manifest ok` and recomputed both digests: - on `t007-assembly-entry-point`, `9321107` moved the code pin to `ecb4ef3` (PR at `e406e0e`), and `58e3ac5` moved it to `d12345b` (PR at `ecd7aa4`); - on `t007-assembly-entry-point-v2`, `7bbd60b` moved it to `ea17f30` (PR at `2411f40`), then `e5a901e` and `316aed6` to the round-1 heads; - - on `t007-assembly-entry-point-v3`, `4c6a0b7` moved it to `fa460f2`. That commit is the whole code leg as the PR head `6568f08` has it, merged with main `f87fd5a`. It includes #190's test roots, and every one of the leg's paths is the PR head's blob. + - on `t007-assembly-entry-point-v3`, `4c6a0b7` moved it to `fa460f2`. That commit is the whole code leg as the PR head `6568f08` has it, merged with main `f87fd5a`. It includes #190's test roots, and every one of the leg's paths is the PR head's blob. `4b8b05a` then moved the pin to `3fc1e95`, the code leg at the final head `8246983`. - Each bump printed bump-leg's `NEXT … push -u origin …` line. It was deliberately not followed. - The clone's push URL is disabled. @@ -117,25 +120,29 @@ Transcript of row 3 in the writer's clone (assembly `7bbd60b`, code pin `ea17f30 ## T008: the compatibility matrix -The tests are in `tests/test_install_payload_source.py` on #191. The fixtures build their own GitHub out of bare repositories, answered by one fake for `gh api`, `gh repo` and `curl` that runs `git` and logs every request. There is no real `gh` or `curl` on `PATH`, and every `$HOME` is temporary. - -| # | Row | Tests | Base `c4864ac` | After | Composed (run C clone) | -| --- | --- | --- | --- | --- | --- | -| 1 | Raw/gh one-liners | `test_r01_the_one_liner_against_a_single_repository_installs_as_before`; `test_r01_the_one_liner_against_an_adopted_assembly_installs_the_pinned_code[gh,curl]`; `test_r01_composed_the_documented_one_liners_resolve_through_the_root_entry_point[openRepoShape,gh]`; `test_r01_composed_the_root_entry_point_delegates_locally`; `test_r01_composed_a_signal_reaches_the_implementation_and_its_temporaries_go` | pass / 2 red / skip | pass | pass | -| 2 | Offline local single / adopted checkout | `test_r02_a_complete_single_repository_tree_installs_offline_byte_for_byte` (sentinel REPO/REF, zero requests); `test_r02_an_adopted_checkout_installs_its_pinned_code_offline` | pass / red | pass | pass | -| 3 | Explicit local code feature | `test_r03_an_explicit_code_feature_checkout_installs_its_own_bytes_and_names_them`; `test_r03_an_incomplete_code_feature_checkout_is_refused_not_completed_from_elsewhere` | 2 red | pass | pass | -| 4 | Installed reinstall / skill-only | `test_r04_an_installed_copy_reinstalls_from_the_adopted_source_without_any_checkout`; `test_r04_an_installed_copy_on_a_single_repository_keeps_the_sibling_rule` | red / pass | pass | pass | -| 5 | Override / adopted fork | `test_r05_a_single_repository_fork_named_by_the_override_installs_as_before`; `test_r05_an_adopted_fork_follows_its_own_pin_not_a_guessed_code_name` | pass / red | pass | pass | -| 6 | Branch, tag, commit, slash refs | `test_r06_every_ref_form_is_resolved_once_and_every_payload_names_one_commit` (4 forms × 2 layouts) | 8 red | pass | pass | -| 7 | Branch moves between downloads | `test_r07_a_branch_that_moves_between_downloads_never_mixes_versions[adopted,single]` | 2 red (single installed mixed versions) | pass | pass | -| 8 | Missing pin (single) / partial or wrong pin | `test_r08_a_single_repository_without_a_code_pin_is_permitted`; `…an_adoption_manifest_without_a_code_pin_is_refused`; `…a_malformed_code_pin_is_refused_never_read_as_a_single_repository` (19 malformations); `…a_pin_the_gitlink_disagrees_with_is_refused`; `…a_pin_naming_a_commit_its_repository_lacks_is_refused` | pass / 22 red (base exited 0 installing the assembly root's decoys) | pass | pass | -| 9 | API answer missing/malformed, auth, transport | `test_r09_an_api_failure_is_a_refusal_never_a_fallback_to_a_single_repository` (5 steps × 401/403/404/transport/garbage, both transports) | 25 red (decoys installed) | pass | pass | -| 10 | Changed gitlink / payload, incomplete local set | `test_r10_a_local_gitlink_that_disagrees_with_the_pin_is_refused_before_staging`; `…a_changed_local_payload_is_refused_before_staging`; `…an_incomplete_local_adopted_payload_is_refused_without_a_fetch` | 3 red | pass | pass | -| 11 | Missing command/skill/alias, failed merge | `test_r11_a_payload_file_missing_from_the_pinned_code_changes_nothing[park,skills/restart/SKILL.md,commands/swap.md]`; `test_r11_a_settings_merge_that_cannot_be_computed_changes_nothing` | 4 red | pass | pass | -| 12 | Modes, receipts, targets, foreign files, hooks | `test_r12_an_adopted_install_keeps_modes_receipts_targets_and_what_is_not_ours` | red | pass | pass | -| 13 | WIP init / template lookup | `test_r13_wip_init_takes_the_standard_from_codes_dependency_pin_not_the_assemblys` | red (seeded from the assembly's decoy pin commit) | pass | pass | -| 14 | Missing nested dependency | `test_r14_with_no_nested_standard_anywhere_the_dependency_pin_alone_selects`; `test_r14_the_documented_upstream_skip_is_unchanged`; `test_r14_composed_acceptance_requires_the_nested_dependency` | red (seeded from `main`) / pass / skip | pass | pass | -| 15 | bash 3.2, Windows/WSL | `test_r15_the_adopted_path_holds_under_macos_one_true_awk`; `test_r15_the_installer_uses_nothing_bash_3_2_lacks`; `test_r15_the_three_lists_stay_one_line_arrays_a_consumer_parses`; `test_r15_composed_the_root_entry_points_pin_reader_is_the_implementations`; `test_r15_composed_the_root_entry_point_parses_and_uses_nothing_bash_3_2_lacks` | red / pass / pass / skip / skip | pass | pass | +The tests are `tests/test_install_payload_source.py` at #191's final head `8246983`, which is main `a74c7fc`: 52 tests, 109 cases. Every `def test_` line in that file is in the table below, and no name outside it is. The fixtures build their own GitHub out of bare repositories, answered by one fake for `gh api`, `gh repo` and `curl` that runs `git` and logs every request. There is no real `gh` or `curl` on `PATH`, and every `$HOME` is temporary. + +`tests/test_openrepotools_command.py` gains no test. Its fake `gh` answers the one layout question (`contracts/code-pin.yaml`) with GitHub's 404, and 4 of its 63 tests run the single-repository fetch path through that fake: `test_install_places_all_nine_or_none`, `test_a_withheld_command_file_places_nothing_either`, `test_install_from_stdin_fetches_itself_into_a_live_workdir` and `test_a_fetching_install_follows_the_ref_it_was_given`. No assertion of theirs changed. + +**Base column:** `8246983`'s module was run against `c4864ac`'s installer, under the lock, on 2026-10-11: **27 passed, 73 failed, 9 skipped** (the composed cases). Every pass is a case that must behave exactly as base does (today's single repository, or a tree that is not this assembly's), or a static check. **The `8246983` column:** CI run 38097860219 (`tests`: 1419 passed, 9 skipped, the 9 being the composed cases). The composed tests passed in the strict composed runs recorded below. + +| # | Row | Tests at `8246983` (cases) | Base `c4864ac`, per test | `8246983` | +| --- | --- | --- | --- | --- | +| 1 | Raw/gh one-liners | `test_r01_the_one_liner_against_a_single_repository_installs_as_before`; `test_r01_the_one_liner_against_an_adopted_assembly_installs_the_pinned_code` (2); `test_r01_a_single_repository_by_curl_never_needs_api_github_com` (2); `test_r01_a_single_repository_without_jq_fetches_then_names_jq_as_it_always_did`; `test_r01_a_single_repository_by_gh_asks_for_files_and_nothing_else`; `test_r01_composed_the_documented_one_liners_resolve_through_the_root_entry_point` (2); `test_r01_composed_the_root_entry_point_delegates_locally`; `test_r01_composed_a_signal_reaches_the_implementation_and_its_temporaries_go` | pass; 2 red; 2 pass; pass; pass; skip (composed); skip (composed); skip (composed) | pass; composed tests pass composed | +| 2 | Offline local single / adopted checkout | `test_r02_a_complete_single_repository_tree_installs_offline_byte_for_byte`; `test_r02_a_complete_tree_inside_another_projects_assembly_installs_as_before` (2); `test_r02_an_adopted_checkout_installs_its_pinned_code_offline`; `test_r02_an_adopted_checkout_whose_pin_comments_its_path_is_still_its_assembly` | pass; 2 pass; red; red | pass | +| 3 | Explicit local code feature | `test_r03_an_explicit_code_feature_checkout_installs_its_own_bytes_and_names_them`; `test_r03_an_incomplete_code_feature_checkout_is_refused_not_completed_from_elsewhere` | red; red | pass | +| 4 | Installed reinstall / skill-only | `test_r04_an_installed_copy_reinstalls_from_the_adopted_source_without_any_checkout`; `test_r04_an_installed_copy_on_a_single_repository_keeps_the_sibling_rule` | red; pass | pass | +| 5 | Override / adopted fork | `test_r05_a_single_repository_fork_named_by_the_override_installs_as_before`; `test_r05_an_adopted_fork_follows_its_own_pin_not_a_guessed_code_name` | pass; red | pass | +| 6 | Branch, tag, commit, slash refs | `test_r06_every_ref_form_is_resolved_once_and_every_payload_names_one_commit` (4); `test_r06_a_single_repository_reads_every_ref_form_as_it_always_did` (4) | 4 red; 4 pass | pass | +| 7 | Branch moves between downloads | `test_r07_a_branch_that_moves_between_downloads_never_mixes_versions`; `test_r07_a_branch_that_moves_right_after_the_layout_question_is_read_at_one_commit` (2) | red; 2 red | pass | +| 8 | Missing pin (single) / partial or wrong pin | `test_r08_a_single_repository_without_a_code_pin_is_permitted`; `test_r08_an_assembly_root_without_its_code_pin_refuses_as_a_single_repository_would`; `test_r08_composed_the_root_entry_point_refuses_a_pin_its_gitlink_disagrees_with` (2); `test_r08_a_malformed_code_pin_is_refused_never_read_as_a_single_repository` (19); `test_r08_a_pin_the_gitlink_disagrees_with_is_refused`; `test_r08_a_pin_naming_a_commit_its_repository_lacks_is_refused` | pass; pass; skip (composed); 19 red; red; red | pass; composed tests pass composed | +| 9 | API answer missing/malformed, auth, transport | `test_r09_an_api_failure_is_a_refusal_never_a_fallback_to_a_single_repository` (24); `test_r09_a_pin_the_listing_shows_but_that_will_not_come_back_is_refused_saying_what_answered` | 24 red; red | pass | +| 10 | Changed gitlink / payload, incomplete local set | `test_r10_a_local_gitlink_that_disagrees_with_the_pin_is_refused_before_staging`; `test_r10_a_changed_local_payload_is_refused_before_staging`; `test_r10_an_incomplete_local_adopted_payload_is_refused_without_a_fetch` | red; red; red | pass | +| 11 | Missing command/skill/alias, failed merge | `test_r11_a_payload_file_missing_from_the_pinned_code_changes_nothing` (3); `test_r11_a_single_repository_missing_a_file_refuses_in_the_words_it_always_did` (2); `test_r11_a_single_repository_at_a_ref_it_lacks_refuses_in_the_words_it_always_did` (2); `test_r11_a_settings_merge_that_cannot_be_computed_changes_nothing` | 3 red; 2 pass; 2 pass; red | pass | +| 12 | Modes, receipts, targets, foreign files, hooks | `test_r12_an_adopted_install_keeps_modes_receipts_targets_and_what_is_not_ours` | red | pass | +| 13 | WIP init / template lookup | `test_r13_wip_init_takes_the_standard_from_codes_dependency_pin_not_the_assemblys`; `test_r13_an_installed_wip_init_whose_source_does_not_answer_seeds_from_main_as_before`; `test_r13_an_installed_wip_init_at_a_ref_the_source_lacks_seeds_from_main_as_before`; `test_r13_an_installed_wip_init_never_needs_a_single_repositorys_commit_lookup`; `test_r13_an_installed_wip_init_on_a_single_repository_reads_its_pin_as_before`; `test_r13_an_adopted_source_wip_init_cannot_follow_refuses_in_step_7s_words` | red; pass; pass; pass; pass; red | pass | +| 14 | Missing nested dependency | `test_r14_with_no_nested_standard_anywhere_the_dependency_pin_alone_selects`; `test_r14_the_documented_upstream_skip_is_unchanged`; `test_r14_composed_acceptance_requires_the_nested_dependency` | red; pass; skip (composed) | pass; composed tests pass composed | +| 15 | bash 3.2, Windows/WSL | `test_r15_the_installer_uses_nothing_bash_3_2_lacks`; `test_r15_the_three_lists_stay_one_line_arrays_a_consumer_parses`; `test_r15_the_adopted_path_holds_under_macos_one_true_awk`; `test_r15_composed_the_root_entry_points_pin_reader_is_the_implementations`; `test_r15_composed_the_root_entry_point_parses_and_uses_nothing_bash_3_2_lacks` | pass; pass; red; skip (composed); skip (composed) | pass; composed tests pass composed | The module is `WINDOWS_SKIP`, like every bash-driven test here. The composed tests skip in standalone code, and under `OPENREPOTOOLS_COMPOSED=1` (T009's convention) they fail instead. @@ -155,7 +162,7 @@ Locked runs used `tests/run.sh`'s own invocation under its lock. That is the coo | Composed, strict | entry point `8ed83b7` + code `ea17f30` (= `2411f40`) | module + install suites + `repo_hygiene` | Not completed. The 2026-10-09T01:3xZ account swap stopped it, and `6568f08`'s run below superseded it. | | CI on `ecd7aa4` | | | All jobs green (run 37866464029). | | CI on `2411f40` | | | All jobs green (run 37869321635). `tests-macos` skipped (no `ready`). | -| Review of #191 at `2411f40` | | | DO NOT LAND (review 5465121368). F1: on today's layout, the curl path needed api.github.com and `jq`. F2: an installed `wip init` refused at step 7 where base fell back to `main`. | +| Review of #191 at `2411f40` | | | DO NOT LAND (lane 1's reviewer, 5465121368; lane 3's 5464963807 reached the same verdict on F1). F1: on today's layout, the curl path needed api.github.com and `jq`. F2: an installed `wip init` refused at step 7 where base fell back to `main`. | | F1/F2 fix | `13cc752` | module | 90 passed, 7 skipped. CI on `13cc752`: green. Lane 3's delta review: LAND (5471121210), with the stale body (delta 8) and the `ready` macOS run as conditions. | | Round 2 | `fdc5f18`, `6fa3b43`, `6568f08` | | Dropped the `project.yaml` probe; bounded the assembly search (delta 4); the adopted pin refusal now says what answered (delta 6); r14 reads conftest's text (the `KeyError 'reason'` under #190's composed mode); a composed test for the root's gitlink check; merged main `f87fd5a`. | | Module, under the lock | `fdc5f18` content | `-k payload_source` | **96 passed, 7 skipped** | @@ -168,6 +175,9 @@ Locked runs used `tests/run.sh`'s own invocation under its lock. That is the coo | Copilot round 1, on `6568f08` | `cddd1f7` | | Three findings. Fixed: a copy-time race on the mounted-leg path (the checked snapshot is what is installed), and a commented `submodule_path` that `pin_mounts` missed (its new test fails at `6568f08` and passes at `cddd1f7`, scoped and unlocked). Kept: the index-then-HEAD gitlink fallback, which is openRepoShape's `recorded_gitlink` contract. Local rows r02/r03/r10/r15, scoped and unlocked: **13 passed, 2 skipped**. | | Copilot round 2, on `cddd1f7` | `8246983` | | One finding: the PR body overclaimed a refusal for a ref that moves between the probe and the resolution. The body is corrected, and a test (moved right after the probe: adopted → installed whole at the second commit; single → refused) passes, together with row 7, scoped and unlocked: **3 passed**. The cap is reached. | | Composed, strict, unlocked | assembly `4b8b05a` = root v3 + code `3fc1e95` (= `8246983`) | the module's composed tests + r02, r03, r07, r10 | **22 passed** | +| CI on `8246983` | | | Run 38097860219: `tests` 1419 passed, 9 skipped. `tests-no-submodule`: 1199 passed, 229 skipped. `tests-windows`: 177 passed. `guard-launch-mode`: 165 passed. `parse-macos`, SonarCloud and Sourcery: pass. `tests-macos` ran on the `ready` label (run 38155833862). Lane 3's delta check: LAND (5481368124). | +| Base column, under the lock, 2026-10-11 | module at `8246983`, installer `c4864ac` | `-k payload_source` | **27 passed, 73 failed, 9 skipped** (the composed cases). This is the T008 table's base column. | +| Landed | `a74c7fc` | | #191 landed on main as `a74c7fc` on 2026-10-13, the squash of `8246983`. | ## Lane 2's installer rows @@ -180,7 +190,7 @@ These are the 15 rows of "The installer's own assumptions" in `consumer-reads-20 | `:258-275` `fetch_from_repo` | Covered: it reads the resolved source, still `gh` first. | | `:282-285, 338, 962-990` `INSTALLABLES`/`collect_commands` | Covered: the names sit at the code root, and the three arrays stay one line (`test_r15_…one_line_arrays…`, the W4 constraint). | | `:1290-1398` `collect_skills` from `~/.local/bin` | Covered: rows 4 and 11. | -| `:2502-2517` `wip_shape_ref` | Covered: rows 13 and 14. `main` stays the fallback only for a source that carries no pin. | +| `:2502-2517` `wip_shape_ref` | Covered: rows 13 and 14. For a single repository the pin is read at `$REF` as before. For an adopted assembly it is the pinned code's pin. `main` is the fallback, as on main, for a source that carries no pin, a source that does not answer, and a ref the source lacks (#191's F2 fix: `test_r13_an_installed_wip_init_whose_source_does_not_answer_…`, `…_at_a_ref_the_source_lacks_…`, `…_never_needs_a_single_repositorys_commit_lookup`). An adopted source that cannot be followed refuses at step 7, in step 7's words (`test_r13_an_adopted_source_wip_init_cannot_follow_refuses_in_step_7s_words`). | | `:2519-2523` `wip_fetch_template_file` | Unchanged. Row 13 shows the template comes from the standard the code pins. | | `:1218-1222` `venv_note` names `docs/README-lanes.md` | **Not changed.** It is a comment this PR does not touch, and it names a spec-leg path after the split. Listed for T014's reconciliation (also T006 §10.9). | | `:28-56, 393-396` counts vs design `:61-64` | Recorded: 17 commands, 23 payload files, 29 placed files, 31 artifacts on this layout, derived from the lists. The design's 15/27/29 are `daed209`'s. | @@ -193,6 +203,22 @@ These are the 15 rows of "The installer's own assumptions" in `consumer-reads-20 workBenches-side: the sentinel REPO/REF partial row is covered (`test_r02_…byte_for_byte`), and so is W4 (one-line arrays). W1, W3 and W8 (re-vendoring from `openRepoTools-code`) are workBenches follow-ups that wait for real legs; nothing here changes workBenches. +## Lane 2's open items on #191 (#186 comment 6090038382) + +Lane 2's matrix ran on `6568f08` with v3. Each item it found there holds at the final head `8246983` too: `cddd1f7` touched only the mounted-leg snapshot and `pin_mounts`' grammar, and `8246983` added a test only. Each item is fixed, accepted (with the reason), or given to another task: + +| Item | Disposition | +| --- | --- | +| 1. The walk residue: an unrelated pin whose `submodule_path` names this directory (`outer/product` refuses; `outer/worktrees/001-x/product` installs with the "NOT the code … pins" line; an installed copy in `$H/bin` under a `$HOME` pin with `submodule_path: bin` refuses to reinstall). | **Accepted residual, proposed for #193.** `fdc5f18` limited the walk to the two layouts the design names, and the pin must name this directory. A foreign pin that names exactly this directory as its leg cannot be told apart from this tree's own with what the walk reads. The proposal is to also require the checkout's own identity to match the pin (its `origin` is the pin's `source_repository`, or its HEAD is the pinned commit). Unreachable today: no ancestor of a checkout or of `~/.local/bin` carries `contracts/` (lane 3, #191 review 5471121210, delta 4). | +| 2. A missing ref or repository is read as a single repository and refused in main's words, without naming the ref. | **Accepted, by the byte-for-byte rule.** These are main's words for the same request (`test_r11_a_single_repository_at_a_ref_it_lacks_refuses_in_the_words_it_always_did[gh, curl]`). | +| 3. A 502 on `contracts/code-pin.yaml` refuses where main installs. | **Accepted and disclosed.** It is the one extra request on today's layout. An unanswered layout question is a refusal, never a reading of the older layout (the design's "no silent legacy fallback"). #191's body says so. | +| 4. Garbage or an empty body at 200 on `code-pin.yaml` gives "the ref moved between the two reads". | **Open, wording only; proposed for #193.** The refusal is right, because nothing is placed, but its words assume a move. The proposed words name both readings: the ref moved, or the first answer was not GitHub's. | +| 5. The offline reinstall from an installed copy refuses in different words from main's. | **Accepted and disclosed** in #191's body as the one new refusal on today's layout (lane 3's delta 3). Base refused there too. | +| 6. A missing pin on the adopted path is read as a single repository, so decoys at the root (synthetic) install. | **Accepted, by structure.** Asking `project.yaml` on the 404 would add a second request to today's layout. An adopter writes the pin and the manifest in one commit, and `make validate` refuses one without the other. A real assembly root carries no payload, so it refuses in main's words (`test_r08_an_assembly_root_without_its_code_pin_refuses_as_a_single_repository_would`). For T015: confirm that the real assembled root carries no payload file. | +| 7. v3 with pin = gitlink at code before #191. | **T015 item** (see T007 above). The cutover must pin a code commit that carries #191, and T015 checks it at the assembled head. Proposed for #193: the root refuses an implementation that lacks the adopted resolver (a declared marker, or `payload_resolve_remote()`), naming the pin as older than T007. | +| 8. The adopted one-liner needs `jq` at the entry point on the curl path. | **T014** (the network-needs sentence below). | +| 9. Row 7, single: a branch that moves mid-install still mixes versions, as on main. | **Accepted, by the byte-for-byte rule.** Freezing the ref needs api.github.com, which today's layout never needed. Row 7 holds the adopted layout only. | + ## For other tasks - **T009:** `test_the_installer_reaches_only_this_repository` holds because every fetching line names `$REPO`. Its docstring should say that the one other repository reached is the one `$REPO`'s own validated pin names. The composed hygiene results above are T006 §10.6's front-door findings. @@ -200,7 +226,11 @@ workBenches-side: the sentinel REPO/REF partial row is covered (`test_r02_…byt - **T014:** - `venv_note`'s `docs/README-lanes.md` comment. - The README's one-liners need a sentence on network needs after the cutover, next to the one-liner (lane 3's T4 on the v2 patch). Today's single repository needs raw.githubusercontent.com only, or `gh`. After the cutover, the curl one-liner needs api.github.com and `jq`. By reading v3 and the PR head, that is 6 api.github.com requests per install: the root makes 2 (`commits`, `git/trees`), and the implementation makes 4 (`commits`, two `git/trees`, the pinned commit). Lane 3 measured 5 on v2, which had no root tree read. So a gh-less machine that is rate-limited (60 an hour per address, unauthenticated) cannot install. -- **T015:** validate at the real assembled head, with a code pin that carries this change, against the real GitHub. That includes the one-liners and `bump-leg.py`'s lockstep, and the 3.2 parse of the root entry point (`parse-macos` covers the implementation only). +- **T015:** + - Validate at the real assembled head, with a code pin that carries this change, against the real GitHub. That includes the one-liners, `bump-leg.py`'s lockstep, and the 3.2 parse of the root entry point (`parse-macos` covers the implementation only). + - **Named item, the pre-T007 pin:** confirm that the pinned code carries #191 before the cutover. v3 does not refuse a lockstep-consistent pin at older code (lane 2's item 7). + - **Named item, a root with no payload:** confirm that the assembled root carries no payload file (lane 2's item 6). +- **#193 (follow-ups, proposed):** a capability check in the root entry point (item 7); clearer words for a 200-garbage probe (item 4); an identity match for the walk (item 1). ## What this does not do From 7428e7889a96025320d05bc855df405c3267e71d Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Tue, 13 Oct 2026 18:43:21 +0000 Subject: [PATCH 40/42] Correct the installer record against lane 2's check at 8246983: the walk residue's reason, the 200-garbage refusal, row 7's cases, CI on cddd1f7 and the measured holds. - Item 1: the code reads the gitlink at the pin's submodule_path and refuses where none is recorded (24a, 24c), citing lane 3's 5481368124 finding 5 and its cheaper tightening, and finding 7 on #191's body. - Item 4: a 200 that is not GitHub's refuses where main installs, like item 3; disclosed here, since #191's body does not name it. - Item 2: the adopted missing-ref and missing-repository rows (19c-f, 19h). - Item 7: the patch's header and commit message overstate; it stays. - Item 9: row 7's [single] case is an adopted ref moving to a commit with no pin, which is refused; no test freezes a single repository. - The section's opening cites lane 2's measurements at cddd1f7 (#186 6103618033) and 8246983 (register note 2026-10-13T18:22:15Z). - The runs table gains CI on cddd1f7 (run 38096352239). Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../installer-2026-10-08.md | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/specs/004-migrate-to-triad/installer-2026-10-08.md b/specs/004-migrate-to-triad/installer-2026-10-08.md index d8d40117..ea63cf29 100644 --- a/specs/004-migrate-to-triad/installer-2026-10-08.md +++ b/specs/004-migrate-to-triad/installer-2026-10-08.md @@ -173,6 +173,7 @@ Locked runs used `tests/run.sh`'s own invocation under its lock. That is the coo | Composed, strict, under the lock, 2026-10-09T22:08–23:18Z | assembly `4c6a0b7` = root v3 `6acc147` + code `fa460f2` (this head's whole code leg) | module + install suites + `repo_hygiene` + `openrepotools_command` | **429 passed, 1 failed**. The failure is `test_the_root_carries_the_line_ending_rule`, which needs `.gitattributes` in the code leg; run C's legs carry none (T006 §10.2). After #190's roots, the 17 earlier front-door failures read the assembly root and pass. | | CI on `6568f08` | | | Run 37997688693: `tests` 1416 passed, 9 skipped. `tests-no-submodule`: 1196 passed, 229 skipped. `tests-windows`: 177 passed. `guard-launch-mode`: 165 passed. `parse-macos`, SonarCloud and Sourcery: pass. `tests-macos` waits on `ready`. | | Copilot round 1, on `6568f08` | `cddd1f7` | | Three findings. Fixed: a copy-time race on the mounted-leg path (the checked snapshot is what is installed), and a commented `submodule_path` that `pin_mounts` missed (its new test fails at `6568f08` and passes at `cddd1f7`, scoped and unlocked). Kept: the index-then-HEAD gitlink fallback, which is openRepoShape's `recorded_gitlink` contract. Local rows r02/r03/r10/r15, scoped and unlocked: **13 passed, 2 skipped**. | +| CI on `cddd1f7` | | | Run 38096352239: `tests` 1417 passed, 9 skipped. `tests-no-submodule`: 1197 passed, 229 skipped. `tests-windows`, `guard-launch-mode` and `parse-macos`: pass. `tests-macos` skipped (no `ready`). | | Copilot round 2, on `cddd1f7` | `8246983` | | One finding: the PR body overclaimed a refusal for a ref that moves between the probe and the resolution. The body is corrected, and a test (moved right after the probe: adopted → installed whole at the second commit; single → refused) passes, together with row 7, scoped and unlocked: **3 passed**. The cap is reached. | | Composed, strict, unlocked | assembly `4b8b05a` = root v3 + code `3fc1e95` (= `8246983`) | the module's composed tests + r02, r03, r07, r10 | **22 passed** | | CI on `8246983` | | | Run 38097860219: `tests` 1419 passed, 9 skipped. `tests-no-submodule`: 1199 passed, 229 skipped. `tests-windows`: 177 passed. `guard-launch-mode`: 165 passed. `parse-macos`, SonarCloud and Sourcery: pass. `tests-macos` ran on the `ready` label (run 38155833862). Lane 3's delta check: LAND (5481368124). | @@ -205,19 +206,24 @@ workBenches-side: the sentinel REPO/REF partial row is covered (`test_r02_…byt ## Lane 2's open items on #191 (#186 comment 6090038382) -Lane 2's matrix ran on `6568f08` with v3. Each item it found there holds at the final head `8246983` too: `cddd1f7` touched only the mounted-leg snapshot and `pin_mounts`' grammar, and `8246983` added a test only. Each item is fixed, accepted (with the reason), or given to another task: +Lane 2's matrix ran on `6568f08` with v3, and lane 2 re-ran it on each later head: + +- at `cddd1f7`, of 341 rows only the new row 25 changed, and it is fixed (#186 comment 6103618033); +- at `8246983`, 0 of 341 verdicts differ from `cddd1f7` (lane 2's register note of 2026-10-13T18:22:15Z, `lanes/log/openRepoTools-2.md` in brett-wip). The installer is the same blob at both heads, `7aa697c`, and at `a74c7fc`. + +So each item below holds at the final head as measured. Each is fixed, accepted (with the reason), or given to another task: | Item | Disposition | | --- | --- | -| 1. The walk residue: an unrelated pin whose `submodule_path` names this directory (`outer/product` refuses; `outer/worktrees/001-x/product` installs with the "NOT the code … pins" line; an installed copy in `$H/bin` under a `$HOME` pin with `submodule_path: bin` refuses to reinstall). | **Accepted residual, proposed for #193.** `fdc5f18` limited the walk to the two layouts the design names, and the pin must name this directory. A foreign pin that names exactly this directory as its leg cannot be told apart from this tree's own with what the walk reads. The proposal is to also require the checkout's own identity to match the pin (its `origin` is the pin's `source_repository`, or its HEAD is the pinned commit). Unreachable today: no ancestor of a checkout or of `~/.local/bin` carries `contracts/` (lane 3, #191 review 5471121210, delta 4). | -| 2. A missing ref or repository is read as a single repository and refused in main's words, without naming the ref. | **Accepted, by the byte-for-byte rule.** These are main's words for the same request (`test_r11_a_single_repository_at_a_ref_it_lacks_refuses_in_the_words_it_always_did[gh, curl]`). | +| 1. The walk residue: an unrelated pin whose `submodule_path` names this directory (lane 2's rows 24a–c, re-measured by lane 3 at `cddd1f7` in #191 review 5481368124, finding 5). Main installs in all three. | **Accepted residual (low), proposed for #193.** `fdc5f18` limited the walk to the two layouts the design names, and the pin must name this directory, but the walk takes that name match as identity. The code then reads the gitlink at that name, and where none is recorded it refuses instead of taking the plain-tree path main takes (`openRepoTools:894-897` at `8246983`). 24a, a complete tree at `outer/product` under `outer/contracts/code-pin.yaml` with `submodule_path: product`, exits 2 with "…/outer records no gitlink at product". 24c, an installed copy in `$H/bin` under a `$H` pin with `submodule_path: bin`, refuses to reinstall with "… records no gitlink at bin" after 0 requests. 24b, at `outer/worktrees/001-x/product`, installs with one extra line, "NOT the code … pins". Lane 3's cheaper tightening uses that same gitlink read: take the parent as the mount only when it records a gitlink at that name, and otherwise fall through to the plain-tree path (5481368124, finding 5). That would close 24a and 24c. #193 item 21 proposes the stricter identity match (the checkout's `origin` is the pin's `source_repository`, or its HEAD is the pinned commit). Unreachable today: no ancestor of a checkout or of `~/.local/bin` carries `contracts/` (lane 3, #191 review 5471121210, delta 4, and 5481368124, finding 5). #191's body sentence "A pin anywhere else above the file is another project's and is not read" is true except for this name match (5481368124, finding 7). | +| 2. A missing ref or repository is read as a single repository and refused in main's words, without naming the ref. | **Accepted, by the byte-for-byte rule.** These are main's words for the same request (`test_r11_a_single_repository_at_a_ref_it_lacks_refuses_in_the_words_it_always_did[gh, curl]`). That test is single-layout only. In lane 2's rows, an adopted assembly at a missing ref (19c, 19d, 19h) and a missing repository (19e, 19f) end the same way, in main's words, with one request more than main (35 against 34; 18 against 17 with curl only). | | 3. A 502 on `contracts/code-pin.yaml` refuses where main installs. | **Accepted and disclosed.** It is the one extra request on today's layout. An unanswered layout question is a refusal, never a reading of the older layout (the design's "no silent legacy fallback"). #191's body says so. | -| 4. Garbage or an empty body at 200 on `code-pin.yaml` gives "the ref moved between the two reads". | **Open, wording only; proposed for #193.** The refusal is right, because nothing is placed, but its words assume a move. The proposed words name both readings: the ref moved, or the first answer was not GitHub's. | -| 5. The offline reinstall from an installed copy refuses in different words from main's. | **Accepted and disclosed** in #191's body as the one new refusal on today's layout (lane 3's delta 3). Base refused there too. | +| 4. Garbage or an empty body at 200 on `code-pin.yaml` refuses where main installs, saying "the ref moved between the two reads" (lane 2's rows: 8 of 8 exit 2, with nothing placed and 0 payload requests). | **A refusal where main installs, like item 3: accepted, and disclosed here; clearer words proposed for #193 (item 20).** On today's layout, `probe_path` takes any `gh` success or curl 200 as a pin (`openRepoTools:656-658`, `:668` at `8246983`). The listing at the resolved commit then shows no pin, and the run refuses at `:761-764`. Main never asks for that file, so it installs. As for item 3, an answer that is not GitHub's 404 is never read as the older layout. #191's body does not disclose this case. It says that "anything but a 404 (offline, 5xx, 401/403, a portal)" refuses "naming what answered", and that the one new refusal on this layout is a layout question with no answer at all. A 200 that is not GitHub's, a portal's page among them, is an answer: it refuses, names nothing that answered, and blames a moved ref. The proposed words name both readings (the ref moved, or the first answer was not GitHub's). They keep "the ref moved", which `test_r07_a_branch_that_moves_right_after_the_layout_question_is_read_at_one_commit[single]` asserts. | +| 5. The offline reinstall from an installed copy refuses in different words from main's. | **Accepted and disclosed** in #191's body, under its one new refusal on today's layout, a layout question with no answer at all (lane 3's delta 3). Base refused there too. | | 6. A missing pin on the adopted path is read as a single repository, so decoys at the root (synthetic) install. | **Accepted, by structure.** Asking `project.yaml` on the 404 would add a second request to today's layout. An adopter writes the pin and the manifest in one commit, and `make validate` refuses one without the other. A real assembly root carries no payload, so it refuses in main's words (`test_r08_an_assembly_root_without_its_code_pin_refuses_as_a_single_repository_would`). For T015: confirm that the real assembled root carries no payload file. | -| 7. v3 with pin = gitlink at code before #191. | **T015 item** (see T007 above). The cutover must pin a code commit that carries #191, and T015 checks it at the assembled head. Proposed for #193: the root refuses an implementation that lacks the adopted resolver (a declared marker, or `payload_resolve_remote()`), naming the pin as older than T007. | +| 7. v3 with pin = gitlink at code before #191. | **T015 item** (see T007 above). The cutover must pin a code commit that carries #191, and T015 checks it at the assembled head. Proposed for #193: the root refuses an implementation that lacks the adopted resolver (a declared marker, or `payload_resolve_remote()`), naming the pin as older than T007. The patch's header comment ("a stale or rolled-back pin is named as what it is") and its commit message ("not left to whichever code a stale pin selects") overstate: a rolled-back pin that agrees with its gitlink is not named. The patch stays as it is, because this residual is a named T015 item (lane 3, 5481368376). | | 8. The adopted one-liner needs `jq` at the entry point on the curl path. | **T014** (the network-needs sentence below). | -| 9. Row 7, single: a branch that moves mid-install still mixes versions, as on main. | **Accepted, by the byte-for-byte rule.** Freezing the ref needs api.github.com, which today's layout never needed. Row 7 holds the adopted layout only. | +| 9. Row 7, single: a branch that moves mid-install still mixes versions, as on main (lane 2's row 7 single: a failure, with 5 + 24 files placed). | **Accepted, by the byte-for-byte rule.** Freezing the ref needs api.github.com, which today's layout never needed. Row 7's tests are about an adopted ref. `test_r07_a_branch_that_moves_between_downloads_never_mixes_versions` is adopted only. In `test_r07_a_branch_that_moves_right_after_the_layout_question_is_read_at_one_commit`, the `[adopted]` case moves to another adopted commit and installs it whole. The `[single]` case moves an adopted ref to a commit with no pin, which is refused as "the ref moved". Neither is a single repository moving, and no test freezes one. | ## For other tasks @@ -230,7 +236,7 @@ Lane 2's matrix ran on `6568f08` with v3. Each item it found there holds at the - Validate at the real assembled head, with a code pin that carries this change, against the real GitHub. That includes the one-liners, `bump-leg.py`'s lockstep, and the 3.2 parse of the root entry point (`parse-macos` covers the implementation only). - **Named item, the pre-T007 pin:** confirm that the pinned code carries #191 before the cutover. v3 does not refuse a lockstep-consistent pin at older code (lane 2's item 7). - **Named item, a root with no payload:** confirm that the assembled root carries no payload file (lane 2's item 6). -- **#193 (follow-ups, proposed):** a capability check in the root entry point (item 7); clearer words for a 200-garbage probe (item 4); an identity match for the walk (item 1). +- **#193 (follow-ups, proposed):** a capability check in the root entry point (item 7); a 200-garbage probe, which refuses where main installs, and clearer words for it (item 4); an identity match for the walk (item 1). ## What this does not do From 8c03f7604b9804d15505abcd2b95cea76b89e133 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Tue, 13 Oct 2026 18:47:46 +0000 Subject: [PATCH 41/42] Add dated 2026-10-13 lines to the candidate record: #191 landed as a74c7fc with r3's installer blob, what r3 covered and what is still owed, where the answered gap is recorded, and F2's decision with #195's status. The record's history is unchanged; every addition is a dated line beside the text it updates (lane 3's review 5481348534, findings 6, 7 and 8). Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../candidate-2026-10-09.md | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/specs/004-migrate-to-triad/candidate-2026-10-09.md b/specs/004-migrate-to-triad/candidate-2026-10-09.md index c8628cb2..57112207 100644 --- a/specs/004-migrate-to-triad/candidate-2026-10-09.md +++ b/specs/004-migrate-to-triad/candidate-2026-10-09.md @@ -5,6 +5,14 @@ section 7 of `gate-ab-evidence-2026-10-09.md` (branch `004-migrate-to-triad-rollback` at `fa44d75`): "No composed candidate carrying every follow-up has run the suite." No task is checked complete by this record, and it approves nothing. +*2026-10-13, where that gap is recorded:* +- **The quoted sentence** is `gate-ab-evidence-2026-10-09.md:347` at `fa44d75`. + `git ls-remote` at 18:46Z shows `004-migrate-to-triad-rollback` still at `fa44d75`. +- **In #196** (`004-migrate-to-triad-evidence-3` @ `7c08f7e`) the same sentence is + `:590`. That file's dated refresh at `:602–608` (2026-10-09T20:30Z) says no suite run of + the candidate "is recorded on origin". +- **That was true then.** This record's suite results reached origin later: r2's in + `16e6ea4` (2026-10-10T23:47Z) and r3's in `e81100c` (2026-10-11T17:00Z). **Writer:** lane openRepoTools-1, 2026-10-09: 01:47Z–02:42Z (r1), 13:29Z–18:20Z (r2), and the classification from 21:56Z to 2026-10-10T02:4xZ. **Evidence branch:** `004-migrate-to-triad-candidate`, from `main` `63810dd`. @@ -39,6 +47,10 @@ sha256 `67d8e7ce…`). Code `9c11276`, spec `9e1d006`, assembly `3891943`. The i modules, composed: 281 passed, 0 failed, 0 skipped. F1 is gone. The install differs from main `f87fd5a` only in the implementation it hands over to. +*2026-10-13:* #191 landed on `main` as `a74c7fc`, a squash of `8246983` with parent +`f87fd5a`. Its tree, `d77e80b3`, is `8246983`'s, and `openRepoTools` is blob `7aa697c` at +`cddd1f7`, `8246983` and `a74c7fc` alike. So r3 measured the code now on `main`. + ## 1. Inputs Every input was read without checking it out in any existing worktree. Patches were @@ -244,6 +256,18 @@ None of those jobs is composed. | F5 | `test_run_wrapper.py::test_a_killed_run_removes_its_run_root_and_stops_the_suite[flock-2-130]` and `[mkdir-lock-2-130]` | all three | `subprocess.TimeoutExpired: … tests/run.sh … timed out after 30 seconds` | **Environment, of this writer's harness, and not load.** The driver was launched as an asynchronous command of a non-interactive shell, so every run inherited SIGINT *ignored*, and bash cannot trap a signal ignored at entry. Only the SIGINT cases failed; the SIGTERM cases passed. Measured isolated at load 42–61: with SIGINT at its default, 4 passed in 1.9 s; launched the way the runs were, the same 2 failed with the same timeout. Main `63810dd` behaves identically (4 passed, then 2 failed) | none for the candidate. The test could start its wrapper with both signals at default, as `test_openrepotools_command.py::test_a_run_killed_mid_placement_leaves_no_temporary` already does (`preexec_fn=default_signals`, whose docstring names this very trap) (follow-up 4) | | F6 | `test_supervised_restart_regressions.py::test_restart_observer_confirms_late_uuid_prepared_by_real_child[live]`, `::test_respawn_restores_configured_roots_in_new_pane`, `::test_completed_observer_cleanup_never_signals_saved_pid[signal]` | no-submodule only | `subprocess.TimeoutExpired: … lane-handoff --supervise …` / `Failed: observer never confirmed readiness` | **Environment: timing under load.** These files and `lane-handoff` are byte-identical to main's. All three passed in the composed and standalone runs of the same code. Rerun isolated at load 48–54: all three passed, once with SIGINT at default and once ignored. But a sibling, `test_completed_observer_cleanup_never_signals_saved_pid[exit]`, failed once in that rerun. So these tests are timing-sensitive on this bench, independent of the candidate | none for the candidate; the flake is pre-existing (follow-up 5) | +*2026-10-13, F2's decision.* +- **Decided on 2026-10-11: the test gives.** #193 item 14 was settled by lane 1's + decision (#186 comment 6103665910, 00:16Z), as #195's writer recommended in 6103460962. + T006's `assembly-0001` is unchanged. +- **Implemented in #195 at `e378871`.** At an assembly root, + `test_agents_md_names_the_pin_rules` now expects `code/upstream/openRepoShape` and + `git -C code submodule update --init upstream/openRepoShape`, taking the prefix from + the code pin. +- **#195's status at 18:46Z:** open at `e378871`. Its CI on the merge with `a74c7fc` is + still running (run 38447402302), and lane 1 lands it when green. +- **Until #195 lands, F2 stands** against `main`'s test. + **Lane 2's reading** (#186 comment 6087267593) **checked against the logs.** Its counts are right for all three runs, and so is its S2 finding (no abort on r2). One refinement and one correction: @@ -325,8 +349,22 @@ run has one. 1. **#191 carries F1's fix** (`6fa3b43`, in its head `6568f08`: the r14 test reads conftest's text). The composed rerun at `6568f08` with T007 v3 is owed (section 6). + *2026-10-13, reconciled with section 9.* + - **What was asked.** Lane 3's review 5481348534, finding 6, asked for the composed + suite and the install comparison at #191's final head. + - **What r3 covered,** at `8246983` (`a74c7fc` on `main`): the install comparison in + full, and the composed suite for the four install modules only (281 passed). + - **Still owed:** the full composed suite on this candidate at the final head. + - **Why it is not run now.** openRepoShape v1.0.0 holds any new rehearsal at + `7f84ca4` (Brett Heap's word, #186 comment 6152410188). The coordinator's order of + 2026-10-11 runs it once, at the frozen source, for Gate C. + - **Lane 2's run is not this record's.** Lane 2's full composed run on its own r3 + (`f87fd5a` + #191 `8246983` + #195 `e378871`) gave 1454 passed and 1 failed: the + lane suite's 5400 s timeout (#186 comment 6152491855). 2. **F2, #193 item 14:** decide whether `test_agents_md_names_the_pin_rules` or T006's `assembly-0001` gives, before the composed run is the record. + *2026-10-13:* decided on 2026-10-11, the test gives. It is implemented in #195 + `e378871`; see the dated note under section 5's table. 3. **#180:** under `tests/run.sh`'s run root the row-stamp cases fail locally. There are 2 at a 70-character run root and 3 at 71, the third being F4. CI is unaffected. Today any local full run is red on them, main included. From ee2e3476f0d33b9b2b5725f30cd741f70cf5efa6 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Tue, 13 Oct 2026 18:47:50 +0000 Subject: [PATCH 42/42] Gate B walk, dated 2026-10-13: #191 landed as a74c7fc, what #196's final refold carries, and the hold for openRepoShape v1.0.0 These are dated lines only; no earlier text is rewritten. No status changes: MET 27, PARTLY 18, OPEN 0, AFTER-GATE-C 1. - #191 landed on main as a74c7fc, the squash of 8246983. Recorded at the header's #191 line and at question d's #191 CI line. - What #196's final refold carries, each from the earliest head that holds its fixes, with #196's "Input heads carried" table named for the exact heads: - the installer record from 7668c51 on: the host path at its line 13 is gone, and lane 3's 5481368376 is answered; 7428e78 adds lane 2's check; - slice B from 2c040d6 on: lane 3's 5481348731 items 7-9, with the T011 record now taken from slice B; - the candidate record from e81100c on: r3. Recorded in the header, the refs table, section 2's item 16 and section 5's item 4. - The hold for openRepoShape v1.0.0, on Brett Heap's word relayed by lane 3 (#186 6152410188), with its after-the-tag order: the pin-bump PR, a fresh rehearsal and plan at v1.0.0, then the cutover. #196 is not held. Part of #186 Lane: openRepoTools-1 Co-Authored-By: Claude Opus 5.5 --- .../gate-b-status-2026-10-09.md | 47 ++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/specs/004-migrate-to-triad/gate-b-status-2026-10-09.md b/specs/004-migrate-to-triad/gate-b-status-2026-10-09.md index 4a632fb0..778278fc 100644 --- a/specs/004-migrate-to-triad/gate-b-status-2026-10-09.md +++ b/specs/004-migrate-to-triad/gate-b-status-2026-10-09.md @@ -11,8 +11,11 @@ agrees with two records #196 carries beside it: the candidate record at `16e6ea4 publishes r2 and classifies every failure, and lane 2's slice B at `40f26f7`, which lane 3 reviewed as LAND. Nothing else was re-walked. These later facts are not read into the lines: - #191 is at `8246983`, with lane 3's LAND (5481368124), `ready` since 16:31:43Z (6111192078). + *2026-10-13:* it landed on `main` as `a74c7fc`, the squash of `8246983`. - IN is at `67a978c`. #196 carries `3cca1e7`, whose line 13 still names a host path, which - section 2 and section 5 item 4 say must go first (5481348731 finding 13). + section 2 and section 5 item 4 say must go first (5481348731 finding 13). *2026-10-13:* the + host path is gone from `7668c51` on, and #196's final refold carries the installer record + from there (the 2026-10-13 entry below). - The test-root follow-ups are #195, open at `e378871`. Lane 3's LAND (5481360555) covers `fb77bed`; `e378871` adds #193 item 14, decided as "the test gives". - Lane 2's verification of T18 is posted (6103482530): the record holds, with five minor @@ -26,6 +29,31 @@ reviewed as LAND. Nothing else was re-walked. These later facts are not read int eight commits do. Still only `22c9fdd` touches a mounted file. Question e1 names the head current at the request. +**Updated 2026-10-13T18:4xZ, before #196's final refold.** No line's status changes; the counts +stay MET 27 · PARTLY 18 · OPEN 0 · AFTER-GATE-C 1. +- #191 landed on `main` as `a74c7fc` on 2026-10-13, the squash of `8246983`. #195 is still + open at `e378871`. +- #196's final refold carries each record from the earliest head that holds its fixes. The exact + heads are in #196's "Input heads carried" table, which moves with each fold: + - the installer record from `7668c51` on: no host path at its line 13, and lane 3's 5481368376 + answered; from `7428e78` on, also lane 2's check at `8246983`; + - slice B from `2c040d6` on, for lane 3's 5481348731 items 7–9: item 8's leftover at + `1578f99`, item 7 at `17dd54f`, item 9 (the host behind openspec 1.13.1) at `2c040d6`. The + T011 record, `gate-ab-evidence-2026-10-09.md`, is now taken from slice B, which carries RR + `2e7e55e`'s file with those corrections; + - the candidate record from `e81100c` on: r3, #191 at its final head `8246983` with T007's v3, + the four install modules green in composed mode, and F1 gone. The full composed suite is + redone at the frozen source for Gate C. + +**Hold, 2026-10-13, on Brett Heap's word relayed by lane 3 (#186 6152410188, 18:29:47Z):** the +cutover (Gate C), any new adoption rehearsal or adoption plan at `7f84ca4`, and any bump of +`upstream/openRepoShape` wait until openRepoShape v1.0.0 is tagged. After the tag, in order: the +pin-bump PR, a fresh rehearsal and adoption plan at v1.0.0, then the cutover. #196 is not held, +because it is evidence of what held at `7f84ca4`, which is what the statuses here record. The +same comment reports that five of the eight mounted files have changed upstream since +`7f84ca4`. For that later `main`, this supersedes the 2026-10-11 count of one above. Read +question e1 with this hold. + **By:** lane openRepoTools-1's reviewer, under [#186](https://github.com/opensoft/openRepoTools/issues/186). It ran read-only everywhere except this file and its own scratch. A first, unpublished pass was made at 13:27Z–13:50Z; section 1 says which statuses moved since. @@ -52,9 +80,9 @@ Paths are under `specs/004-migrate-to-triad/` unless they start with `openspec/` | CI | `004-migrate-to-triad-ci` | `d28e2cc` | | IN | `004-migrate-to-triad-installer` | `51993a4` | | RB | `004-migrate-to-triad-rollback` | `fa44d75` | -| RR | `004-migrate-to-triad-rollback-refresh` (lane 2's slice A) | `2e7e55e` (`c1283b5`, `612ea94`, `2e7e55e` on RB) | -| EF | `004-migrate-to-triad-evidence-2-followups` (lane 2's slice B) | `40f26f7`, as #196 carries it: `096de93` plus lane 3's findings 1–3 and one nit (the branch has since moved to `1578f99`) | -| CA | `004-migrate-to-triad-candidate` | `16e6ea4`, r2 published and classified, as #196 carries it (on 2026-10-09 the branch was `935f53f`, the r1 build) | +| RR | `004-migrate-to-triad-rollback-refresh` (lane 2's slice A) | `2e7e55e` (`c1283b5`, `612ea94`, `2e7e55e` on RB). *2026-10-13:* #196's final refold takes this file from slice B | +| EF | `004-migrate-to-triad-evidence-2-followups` (lane 2's slice B) | `40f26f7`, as #196 carries it: `096de93` plus lane 3's findings 1–3 and one nit (the branch has since moved to `1578f99`). *2026-10-13:* the final refold carries it from `2c040d6` on | +| CA | `004-migrate-to-triad-candidate` | `16e6ea4`, r2 published and classified, as #196 carries it (on 2026-10-09 the branch was `935f53f`, the r1 build). *2026-10-13:* the final refold carries r3, from `e81100c` on | | T18 | `004-migrate-to-triad-t018` | `63bff08` | | L2 | `004-migrate-to-triad-reads` | `a7a8e00` | | #191 | `feat/installer-payload-source-resolution` | `13cc752` | @@ -221,7 +249,8 @@ describes who holds a line. This walk's legend describes the evidence. The two m **Lane 2's "Facts noticed" — this walk's verdicts:** - **Item 16 (a host-absolute path in IN:installer-2026-10-08.md:13): agree.** It must be removed - before IN goes into the evidence-3 PR; plan.md:38–39. + before IN goes into the evidence-3 PR; plan.md:38–39. *2026-10-13:* it is removed from + `7668c51` on, and #196's final refold carries the record from there. - **Items 1 and 8 (the candidate receipt is absent from brett-wip; the record cites #191 at `2411f40`): agree, and both are answered.** CA at `16e6ea4` names the receipt `candidate-20261009-JR8YyA` and records r2 against #191 at `13cc752` (line 34). @@ -445,7 +474,8 @@ keeps the originals." The minimal form, exactly what #190 asks, is the row 59 ch layout: - #190: `tests`, `tests-no-submodule`, `tests-windows`, `parse-macos`, `tests-macos`, `guard-launch-mode` and SonarCloud, all green; -- #191: the same less `tests-macos`, which waits on `ready`. +- #191: the same less `tests-macos`, which waits on `ready`. *2026-10-13:* #191 landed as + `a74c7fc`. What only the legs can show: - the code leg standalone with its document checks skipping by name; @@ -585,7 +615,10 @@ retiring worktrees." at installer-2026-10-08.md:13 removed), RR at `2e7e55e` (the branch tip's file: a cherry-pick of `c1283b5` alone conflicts, 6089982753), EF `096de93`, the candidate record, T18 and this walk onto `main`; do not reuse `849592b`'s subject or body (6088884829); review; - land. + land. *2026-10-13:* the final refold carries the installer record from `7668c51` on (host + path removed), slice B from `2c040d6` on (with the T011 record now taken from it), and the + candidate record from `e81100c` on (r3). The exact heads are in #196's "Input heads carried" + table. 5. **Gate A.** Capture tooling v3 (A1, A2) and lane 2's re-verification; the owner-coordinated freeze; the final capture; owner dispositions (question e5); installed-file receipts for py-bench and the other workstations.