Repository navigation
OpenSpec: prefer-triad-in-project-new (task 5.6 of openxFactory prefer-triad-project-shape) - #5
Conversation
Scaffold the change and draft proposal.md only: an interactive `project new` offers the Triad first, explicit flags keep today's behaviour, and `status`/`doctor` give the advisory once for a non-exempt single repository, never as a gate, check or review input. Alignment review, council and design follow in the same pull request. Refs #3 Lane: openRepoProject-1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThis proposal introduces a Triad-first interactive path for project creation and a terminal-only advisory for non-exempt single repositories, while preserving existing non-interactive behavior and keeping the preference elective and non-gating. Review should focus on the four documented open questions—especially whether inspection advisories are authorized, whether the flagged stderr line violates byte-for-byte compatibility, how exemptions can be determined offline, and behavior when Triad prerequisites are unavailable—before ratification. Sequence diagram for the Triad-first project creation flowsequenceDiagram
actor User
participant Project as project new
participant OpenRepoShape as openRepoShape
participant Bench as Bench generator
User->>Project: new()
alt Terminal and no shape-selection flag
Project-->>User: Ask Triad or single, Triad preselected
alt User selects Triad
Project->>OpenRepoShape: --shape
OpenRepoShape-->>User: Typed confirmation
User->>OpenRepoShape: Confirm creation
else User selects single
Project->>Bench: Create single repository
Bench-->>User: Type yes confirmation
User->>Bench: Confirm creation
end
else Explicit flag or non-terminal
Project->>Project: Preserve existing behavior
end
Flow diagram for terminal-only single-repository advisoriesflowchart TD
Start[project status or project doctor] --> Terminal{Human terminal and CI unset or false?}
Terminal -->|No| Existing[Existing report unchanged]
Terminal -->|Yes| Kind{Inspected root reports kind single?}
Kind -->|No| Existing
Kind -->|Yes| Exempt{Declared exemption applies?}
Exempt -->|Yes| Existing
Exempt -->|No| Record{Valid single-repository.yaml?}
Record -->|Yes| Existing
Record -->|No| Advisory[Print one advisory in human report]
Advisory --> Existing
File-Level Changes
Assessment against linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
…r-triad-in-project-new Lane: openRepoProject-1 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Lane: openRepoProject-1 Proposal drafted at |
Apply the lead's rulings R1 to R14 on both alignment reports. - Scope is creation-only: the status/doctor advisory and its exemption reader leave What Changes, Impact and Rules; Out of Scope records why, the detection and record-rule divergences, and the pre-existing doctor --validate --strict exit. - D2's stderr advisory is owed after a flag-chosen or non-interactive single-repository creation, before any --workflow step; the rule that contradicted it is restated. - One helper decides a person at the terminal (stdin and stdout ttys, CI false-y); the choosing flags, the question's position, its answers, the Triad prerequisites, the name constraint and the -wip exemption are stated. - Modified Capabilities now names project-command (archive PR #4 merged as 67efa80); the standalone capability is dropped. - Why adds the routing through workBenches lane project-command's claim; Impact adds the pty test helper, pinned fixtures and the one Speckit handoff. Lane: openRepoProject-1 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Lane: openRepoProject-1 Alignment review: resolutionBoth alignment reports (Stack Architect, QA Lead) were ruled on by the lead (R1 to R15) and applied to Stack Architect (5 MISMATCH, 15 DRIFT)
QA Lead (8 MISMATCH, 21 GAP)
Open questions, as ruled
Carried to clarifications: whether the creation question is owed for a workspace (
Reviewer reports, verbatimThe Stack Architect's report is below. The QA Lead's report is in the next comment (#5 (comment)), because both together exceed a comment's size limit. Stack Architect alignment report (verbatim, sha256 7896ef88...)Alignment review: Stack Architect, prefer-triad-in-project-newLane openRepoProject-1, phase 3 of /opsx:propose. Read-only review. Material read:
1. Pattern alignment1.1 Finding: The proposal introduces a second, undeclared definition of "a person at the terminal".
1.2 Finding: The reader would leak into
1.3 Finding: The list of flags that "have already chosen" is open-ended, and several existing flags are not classified.
1.4 Finding: Pseudo-terminal tests are a new test pattern, and the existing harness passes the CI runner's
2. Interface accuracyVerified as described:
2.1 Finding:
2.2 Finding: "Modified Capabilities" is stale. PR #4 has merged, and
2.3 Finding: Pressing Enter does not always reach openRepoShape's confirmation.
3. Governing-text consistencyVerified as accurate:
3.1 Finding: The proposal contradicts itself. It says the advisory is never printed without a person at the terminal, but it also adds D2's stderr line to invocations whose stdin is not a terminal.
3.2 Finding: The proposal conflates two record rules. "Checking
3.3 Finding: The proposal lists six silent classes and then says no other class is exempted. The ratified list has five, and the sixth is never justified.
3.4 Finding: The proposal addresses only half of task 5.6's routing. It covers this repository's governance and says nothing about workBenches lane
3.5 Finding: The creation advisory is specified as "one line", but the ratified advisory must carry the posture and both exits. Both realized surfaces needed two lines for that.
4. Integration surface (exemption detection; feeds OQ-3)Family holder: clean. Non-Git directory: detectable from 4.1 Finding: Keying the advisory on the reported kind
4.2 Finding: The leg-clone exemption reaches only standalone clones, and the three readers detect a leg differently.
4.3 Finding: The
4.4 Finding: The proposal does not say what the reader answers when the record exists but PyYAML is unavailable. Under workBenches that is a live path.
4.5 Finding: Showing record problems only in the human report conflicts on its face with the canonical "Inspect and diagnose" requirement, and the proposal does not reconcile them.
4.6 Finding: A shape-keyed exit status already exists.
5. OpenSpec conventionsClean:
5.1 Finding: The proposal names no Speckit handoff.
5.2 Finding: The archived reason for separate capabilities no longer holds now that the baseline is archived.
Open questions, answered from the governing textOQ-1 (is the inspection advisory admitted?): decided by the text, and the answer is no without an amendment.
The forms are named surfaces, and Dropping it also removes 1.2, 4.1-4.6 and most of OQ-3. A freshly created single repository is never exempt, so creation needs no reader. OQ-2 (does "byte for byte" admit D2's stderr line?): decided by the text, and the answer is yes.
Recommended reading: keep stdout, prompts, refusals, created files, OQ-3 (offline exemption detection): partly decided.
The text is silent on the mechanism: a local copy, an import, or delegation. The proposal's two exclusions hold:
One nuance on the second reason: openRepoShape's importer test scans only "nothing else this repository ships" ( Recommended reading: a local, never-raising reader that mirrors openRepoShape's
openRepoShape owns the mechanics, so OQ-4 (Triad chosen without
Recommended reading:
Summary
|
|
Lane: openRepoProject-1 Alignment review: QA Lead report, verbatimAttached to the resolution comment above (#5 (comment)); split off because both reports together exceed a comment's size limit. QA Lead alignment report (verbatim, sha256 ef00ef0a...)Alignment review: QA Lead,
|
| Dimension | MISMATCH | GAP |
|---|---|---|
| 1. Requirement accuracy | 2 | 0 |
| 2. Value and wording consistency | 4 | 5 |
| 3. Spec coverage and conflicts | 2 | 4 |
| 4. Acceptance criteria and testability | 0 | 9 |
| 5. Scope and gaps | 0 | 3 |
| Total | 8 | 21 |
Reflow the terminal-helper paragraph and say plainly why project asks for the visibility: openRepoShape would otherwise default it to private. Lane: openRepoProject-1 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Apply the lead's eleven VALID verdicts (V1 to V11) to proposal.md and record the six NOTED ones (N1 to N6) in a new clarifications.md. - Known obstacles: the assembly-root name form, openRepoShape on PATH and the parent directory are read before the question, named beside the Triad, and refused with exit 2 on a Triad answer before any prompt or network; openRepoShape's own refusals keep its status. "CamelCase" is gone; Preview is reworded for both answers. - --workflow: project keeps its advisory; setup-openspeckit's follows as a second ratified form, and the double offer is recorded with its cases and a workBenches follow-up. - The organisation and visibility prompts get the question's rules and a restating line; the --workflow refusal is hoisted only when the question is asked. - The prompt-stream wording and the pty helper are corrected; the advisory write is guarded and ASCII-only; a bench creation counts as a single repository whether or not Git is initialised. - Declining, agents at a pty, the -wip reading at creation, the workBenches pin-advance handoff issue, the y/yes/n/no answers and four small corrections. Lane: openRepoProject-1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Lane: openRepoProject-1 Council review: resolutionThe three council reports (Product Advocate, Systems Architect, Adversary Engineer) on Counts: 13 concerns raised (Product Advocate 4 plus a lower-impact scenario, Systems Architect 4, Adversary Engineer 5 plus smaller issues). 11 VALID applied, 6 NOTED to VALID (applied to
|
| Verdict | Raised by | Ruling, in brief | Landed in |
|---|---|---|---|
| V1 Known obstacles checked before the question and named beside the Triad | PA1 HIGH, SA4 LOW, AE1 HIGH | assembly-root form ^[A-Za-z][A-Za-z0-9]*$ pinned (contracts/repository-naming.yaml:345 @ openRepoShape 1a9fc537) and stated in plain words; "CamelCase" dropped everywhere; the name, openRepoShape on PATH and the parent directory are read before the question and named beside the Triad; a Triad answer with an obstacle exits 2 before the organisation and visibility prompts and before any network; openRepoShape's validator stays authoritative and its other refusals keep its own status; a terminal --dry-run with an obstacle exits 2; no --into named for a parent the person did not type; Preview reworded for both answers |
What Changes: first bullet, "Where the question sits", "Known obstacles are checked before the question and named beside the Triad", "Names"; Capabilities (Preview); Impact, tests/test_project.py; Questions Resolved, OQ-4 |
V2 --workflow and the second offer |
PA3c/d MEDIUM, SA2 MEDIUM, AE2 HIGH | project keeps its own advisory in every case D2 covers; it neither suppresses nor coordinates with setup-openspeckit's bootstrap advisory, a second ratified form; the double offer is recorded with its three cases (answer 2 then n exits 130; --yes --workflow prints two advisories; a -wip creation is still advised by setup-openspeckit); current behaviour cited at workBenches d86ba59, merged advisory at 9fbe609c; real suppression is workBenches' follow-up |
What Changes, "--workflow"; Out of Scope (suppression item); Impact, "Handoffs" and tests/test_project.py (echoing fake setup-openspeckit) |
V3 Delivery to onp is a tracked handoff |
PA2 HIGH, AE smaller (task 5.6 tick) | on merge this lane opens exactly one opensoft/workBenches issue to lane project-command carrying the merge sha and the merged project file's sha256 (plus the V2 follow-up); its link goes with the merge sha on #3, saying onp and new-project.sh offer nothing until the pin moves; opening an issue edits nothing the claim holds |
Why (routing paragraph); Impact, "Handoffs" |
| V4 The organisation and visibility prompts | PA4 MEDIUM, SA3 MEDIUM, AE4 MEDIUM | the question's rules: full words, case-insensitive, listed private, public, internal, no numbers, no default; ask once more then exit 2; end-of-file exits 2; interrupt 130; every refusal says nothing was created; organisation checked before the visibility prompt; one stdout line restates name, organisation and visibility and names a public answer's consequence; the --workflow refusal (today project:242-244) is hoisted only when the question will be asked; new() computes "will the question be asked" once |
What Changes, "The organisation and visibility prompts" and "Where the question sits"; Impact, project (design constraint) and tests/test_project.py |
| V5 Prompt stream and the pty harness | SA1 HIGH | the question's lines go to stdout; its prompt goes through ask(), which at a terminal CPython writes to stderr (bpo-1927); tests never pin a prompt's stream; pty helper respecified (os.openpty() for stdin and stdout, stderr on a pipe, start_new_session=True, select loop, EIO or empty read as end, answer after prompt, repeated SIGINT, \r\n normalised, timeout, no pty.fork); in-process tests patch input and isatty |
What Changes, "One helper decides"; Impact, project (design constraint) and tests/test_project.py |
| V6 Guarded stderr write | AE3 MEDIUM | closed stderr, EPIPE, ENOSPC swallowed; never written to stdout; exit status never changes; ASCII-only text; tests for 2>&- and 2>/dev/full |
What Changes, "The advisory cannot fail the run"; Impact, tests/test_project.py |
| V7 "Created a single repository" defined | AE5 MEDIUM | a successful bench creation counts (directory plus .project.json) whether or not the generator initialised Git; a creation-time rule that does not contradict openRepoShape's inspection-time not-a-repository silence |
What Changes, "What counts as creating a single repository"; Out of Scope (exemptions paragraph, "At inspection") |
| V8 Declining | PA3a, PA3b | (a) the single line names single-repository.yaml in one clause, text only; (b) no setting stops the question (packet spec.md:99-100, design A3) |
(a) What Changes, first bullet; (b) Rules, "No setting stops the question" |
| V9 Agents at a pty | PA lower-impact scenario, AE4 last point | AGENTS.md and README tell an agent to offer the Triad in conversation, then run project new with a choosing flag, never answering the question for a person; residual risk to clarifications |
Impact, README.md and AGENTS.md; What Changes, "README, AGENTS.md and tests"; clarifications.md (N2) |
| V10 Workspace exemption at creation | AE smaller | only the -wip name form is read at creation; the workspace-configuration form is not, since a path that does not yet exist is no declared fact and creation reads no YAML |
What Changes, "Silent for a workspace name" |
| V11 Small corrections | AE smaller (a, b, c), PA (d), AE2 (e) | (a) AGENT_PROTOCOL_ROOT dropped from the test plan, CI kept; (b) the impossible "--yes the person typed" after a single answer reworded; (c) the fake openRepoShape is asserted to be what PATH resolves and GH_TOKEN, GITHUB_TOKEN, OPENREPOSHAPE_* are dropped; (d) one sentence of value to the person in Why; (e) y/yes take the Triad, n/no the single repository |
(a), (c) Impact, tests/test_project.py; (b) Rules, "No repository is created that the person has not confirmed"; (d) Why; (e) What Changes, "The answers" |
NOTED (recorded in clarifications.md)
| Verdict | Raised by | clarifications.md section |
|---|---|---|
N1 The interactive double question under --workflow |
PA3c, AE2 | "The interactive double question under --workflow" |
| N2 Agents at a pty look like people | PA lower-impact scenario, AE4 | "Agents at a pseudo-terminal look like people" |
| N3 Ctrl-C during openRepoShape | AE smaller | "Ctrl-C while openRepoShape runs" |
N4 Is the creation question owed for a -wip name? |
alignment review (carried) | "Is the creation question owed for a -wip name?" |
| N5 Prompt stream differs between harnesses | SA1 | "The prompt's stream differs between harnesses" |
| N6 Exit codes on the Triad path | SA4, AE1 | "Exit codes on the Triad path" |
DISMISSED (recorded here only)
| Verdict | Raised by | Reason |
|---|---|---|
D1 Omit project's advisory when --workflow will run |
SA2 (alternative; AE2's option (a) is the same) | loses D2 whenever setup-openspeckit refuses before its advisory step, which it can: its main() runs ensure_safe_specify_destination, preflight_source_roots and resolve_project_shape first (workBenches d86ba59, setup-openspeckit:4059-4078); V2 is the chosen reading |
| D2 Measurability | PA ("Where the proposal is solid") | the advisory records nothing by ratified design ("it records nothing, and no later act reads whether it was given", packet spec.md:107-108); the tests are the evidence |
| D3 Windows and locale | PA ("Where the proposal is solid") | no regression: where no terminal is detected the flag-and-advisory path runs, and the answers are ASCII digits and words |
Two points of precision in applying the verdicts
- openRepoShape's passed-through status. Its name check exits 1 (
die(..., 1),setup-project.py:1532-1535), but itsdie()defaults to 2 (setup-project.py:224-225): 18 of its 22die()calls use that default, and four pass 1 (the name check, two--familynaming checks, and a declined typed confirmation,:1622). The proposal therefore says openRepoShape's refusals pass through "with openRepoShape's own exit status (1 for its name check; most of its other refusals exit 2)", and promises no particular status for them;clarifications.md(N6) says the same. /dev/fullis Linux-only. CI runs ubuntu and macOS (.github/workflows/tests.yml), so the test plan keeps2>&-and2>/dev/fulland has the macOS job cover the same guard with a stderr pipe whose read end is closed (EPIPE, which V6 names).
The council reports, verbatim
Product Advocate council report (verbatim, sha256 004b82ff...)
Council: Product Advocate, prefer-triad-in-project-new
Lane openRepoProject-1, phase 4 of /opsx:propose. Read-only review of proposal.md at worktree HEAD b8d566b (PR #5, issue #3, openxFactory task 5.6). This review works within OQ-1 (the change is creation-only).
Sources: the openxFactory packet at e6380965, openRepoShape at 1a9fc537, workBenches at 9fbe609c and origin/main d86ba59, project at b8d566b, and issue #3.
Concerns, ranked by impact
1. HIGH: for many ordinary names, the pre-selected answer always ends in a refusal, and the refusal comes late. The proposal's description of the name rule is also wrong, and it can be read two ways.
Concern: For any name with a hyphen, an underscore or a leading digit, pressing Enter leads the person through four steps before openRepoShape refuses: the organisation prompt, the visibility prompt, openRepoShape's self-bootstrap clone, and its preflight. The proposal's "CamelCase" wording would also turn away valid names.
Evidence:
-
Proposal, "Names": "The question states that constraint beside the Triad.
projectadds no second validator: a Triad answer for a name such asmy-appreaches openRepoShape, and the refusal is openRepoShape's own." -
project:206accepts[A-Za-z0-9][A-Za-z0-9_-]*. -
openRepoShape's actual rule is the naming-policy form
project-leg/assembly,^[A-Za-z][A-Za-z0-9]*$(contracts/repository-naming.yaml:345,1a9fc537). It is not "CamelCase". -
I ran openRepoShape's own validator at
1a9fc537, assetup-project.pystep (4) runs it:Name Result atlaspasses (exit 0) Atlaspasses (exit 0) MyApppasses (exit 0) my-appfails (exit 1) my_appfails (exit 1) 2fafails (exit 1) Atlas-Installfails (exit 1) -
Order in
setup-project.pymain(lines 1836-1852):self_bootstrap(the launcher "clones the standard into a temporary directory first"), thenpreflight,resolve_org,resolve_project_and_elector, and only thencheck_names.check_namesdies with exit 1. -
"States that constraint" has two readings:
- The question always prints a fixed sentence.
- The question names the given name when it does not fit. That needs a reading the proposal says it does not add.
Impact:
- Two implementers will build different questions.
- A person who types
project new my-appand presses Enter does not get the safe default they expected:- They answer two more prompts and may wait for a network clone and preflight offers.
- Then they get exit 1 and must start again.
- Nothing is created, so this is safe, but the first run fails.
- "CamelCase" wording makes a person with
atlasthink they must rename or choose single, whenatlasis valid.
Suggestion: In "Names", replace "The question states that constraint beside the Triad." with:
Beside the Triad, the question states openRepoShape's assembly-root name rule in plain words: a letter first, then letters and digits only, with no hyphen, underscore, dot or space (
project-leg/assembly,^[A-Za-z][A-Za-z0-9]*$,contracts/repository-naming.yamlat1a9fc537). It does not say "CamelCase". When the given name does not match that pattern, the Triad line names it, for example "my-appcannot be a Triad name; a Triad needs a name such asMyApp". This reading only selects a sentence. The Triad stays first and the default,projectrefuses nothing, and the verdict stays openRepoShape's (setup-project.pystep (4), exit 1, passed through).
Add a test: a hyphenated name sees the name-specific line, and the fake openRepoShape's exit 1 is passed through.
If the lead reads "no second validator" as ruling out even a message-only reading, the fixed sentence must still state the accurate rule. The README must then say that a Triad answer for such a name is refused only after the organisation prompt, the visibility prompt and openRepoShape's preflight.
2. HIGH: onp will not offer the Triad until the workBenches pin is advanced, nobody is tracking that advance, and task 5.6 will be ticked anyway.
Concern: The estate's documented creation command does not change until a workBenches pin advance happens. No tracked item asks for it, and the lane that owns it has no register row. Task 5.6, and then the parent's archive, will close on this repository's merge sha.
Evidence:
- Proposal, Why: "they carry this change's behaviour only once workBenches advances that pin on its owner's act."
- Proposal, Impact: "Handoffs this change does not perform: workBenches advances its pinned
projectartifact on its owner's act (laneproject-command); the realization's merge sha is posted on issue project new offers the Triad first (openxFactory prefer-triad-project-shape, task 5.6) #3, and openxFactory's archiving actor checks task 5.6's box citing it". - Issue project new offers the Triad first (openxFactory prefer-triad-project-shape, task 5.6) #3 body: "Lane
project-commandhas no row and no log in the register." - workBenches
specs/003-project-command/tasks.mdhas one pin task,[x] T001 Pin and install the upstream executable, already done. There is no open workBenches issue aboutopenrepoproject-pin. config/openrepoproject-pin.jsonis still ata0407904.- workBenches docs present
onpas the way people create projects:docs/spec-driven-development.md:211: "Useonpto create new projects from benches".docs/setup-script-ui.md:154: "onp (opensoft new project) - Quick project creation".
- Packet
tasks.md6.1 archives on "merged plus green realization evidence".
Impact:
- The doctrine records 5.6 as realized and the parent change archives.
- Every person who creates projects with
onpornew-project.shstill runsa0407904: no question, no advisory. - The behaviour reaches users on an unnamed act by an inactive owner, which in practice may be never.
Suggestion: Add to Impact, under handoffs:
On merge, this lane opens one issue on opensoft/workBenches addressed to lane
project-command. It asks forconfig/openrepoproject-pin.jsonto move to the merge sha, and states that sha and the mergedprojectfile's sha256 so the advance is mechanical. This lane posts the issue's link with the merge sha on #3. The comment says thatonpandnew-project.shruna0407904and offer nothing until that pin moves, so the 5.6 tick reads as "realized inproject new; reachesonpwith workBenches#N".
Opening an issue edits nothing the claim holds. In README "Legacy onp…", add "from the pinned release that includes it".
3. MEDIUM: a person who declines meets the preference again in the same run, is never told how to stop meeting it, and the proposal does not say that "stop asking" is not available.
Concern:
- The interactive path never names
single-repository.yaml. --workflowrepeats the advisory within one run.- Nothing tells the person who always wants a single repository that no "stop asking" setting exists, or why.
Evidence:
- Proposal: the question states "its posture beside it: preferred, not required; elective; confers nothing", with no exits named. Also: "A creation that went through the question prints none, because the question was the offer."
- Only the flag path names the record: "
single-repository.yamlas the way to stop meeting it". --workflow"choose[s] nothing", so it still asks.project:265-268then runssetup-openspeckit --repo <dest>on the new single repository in the same terminal.- setup-openspeckit's
give_triad_advisory(9fbe609c:2344ff) prints its two lines on that repository. At a terminal it then asksTRIAD_QUESTION: "Press Enter to continue (s = show the steps; n or Ctrl-C stops…)". None of the five exemptions covers a repository created seconds earlier, and the packet closes the list ("the list above is the whole list",spec.md:204). - On the flag path with
--workflow, the person getsproject's twowarning:lines and then setup-openspeckit's near-identical pair. - The requirement is titled "A person starting work outside a Triad is advised once" (
spec.md:90). - On "stop asking":
- The packet requires "the creating tool or agent SHALL offer the Triad first" (
spec.md:99-100). - Design A3 rejects "Per-person suppression in the person's workspace manifest" (
design.md:372-381). - So the ratified text forbids a "stop asking" setting, and the proposal never says so.
- The packet requires "the creating tool or agent SHALL offer the Triad first" (
Impact:
- The person who declined most deliberately, by answering
2, gets the least information. They then meet the advisory in every later agent session and bootstrap in that repository, without having been told of the one ratified way to stop. project new foo --workflowasks about shape twice within seconds.- With no statement that "stop asking" is forbidden, a later issue or implementer will likely add a
PROJECT_NO_TRIAD-style switch that the doctrine rejects.
Suggestion: Add a bullet "Declining" to What Changes:
(a) The single-repository line of the question names
single-repository.yamlas the way the new project can later stop meeting the preference. This is text only:projectwrites nothing.(b) No setting stops the question. The ratified text requires the offer at every interactive creation (packet
spec.md:99-100) and rejects per-person suppression (design A3). A person who always wants a single repository answers2, or chooses by flag (--bench,--type); either skips the question and gets the stderr advisory instead.(c) With
--workflow, setup-openspeckit gives its own advisory on the new repository: at a terminal, its Enter-to-continue question; otherwise twowarning:lines. The README says so and a test pins the order.
For a flag-chosen creation with --workflow, the proposal names its choice between two options:
projectkeeps its own lines. The person reads the advisory twice in one run, against "advised once".projectleaves the advisory to setup-openspeckit, which gives it at the start of its run. The design must confirm that setup-openspeckit cannot refuse on a fresh repository before advising.--workflow's missing-setup-openspeckitcase already refuses before creation.
Whichever it picks, the double encounter on the interactive path comes from the doctrine itself. Carry it to the follow-up and to workBenches as a note, not into this change.
4. MEDIUM: the prompts that pressing Enter leads to are not defined, and the only way back from an accidental Enter is an interrupt.
Concern: The question's answers are fully specified, but the organisation and visibility prompts that follow a Triad answer are not. This is the path every reflexive Enter takes.
Evidence:
- Proposal: "
projectasks for the organisation, checked by today's organisation check (project:223), and then for the visibility, one ofpublic,privateorinternal, with no default." - Today's check refuses at once with "Invalid GitHub organization name." (
project:223-224). An empty answer would therefore get that message. ask()'s end-of-file falls through to "Cancelled." with exit 130 (project:1001-1003). The question itself refuses end-of-file with exit 2. One flow would have two end-of-file outcomes.- The proposal leaves undefined:
- whether visibility is typed or numbered;
- case handling (
Private); - re-ask versus refuse on bad input;
- how a person who meant "single" gets back.
Impact:
- Two implementers will differ in four places: empty input, invalid input, end-of-file, and visibility input form.
- The common mistake (Enter pressed by habit, a single repository wanted) has no stated exit. The person sees an organisation prompt, may type an empty answer, and gets a misleading "Invalid GitHub organization name." with exit 2.
Suggestion: Append to "The Triad answer and its prerequisites":
The organisation and visibility prompts follow the question's answer rules:
- Surrounding whitespace is stripped.
- Visibility is typed as
public,privateorinternal, compared case-insensitively, with no default.- An empty or unrecognised answer is asked again once, then refused with exit 2.
- End-of-file refuses with exit 2.
- An interrupt exits 130 with
Cancelled..- Every refusal ends with "nothing was created".
The organisation prompt's first line says that Ctrl-C cancels with nothing created, and that a single repository is answer
2on a re-run.
Add tests for an empty organisation, an invalid visibility, and end-of-file at each prompt.
Where the proposal is solid
- Problem clarity: the Why is complete for the doctrine but says nothing about what the person at
project newgains. Add one sentence: "Creation is the one moment the Triad costs nothing extra. Converting later isadopt-project.py, a deliberate migration, and today the Triad is reached only by knowing to type--shape --org --visibility." - Scope: right-sized. One question, one stderr pair, no new flag. The MVP is clear, and what the inspection follow-up needs is recorded under Out of Scope.
- The no-unconfirmed-repository guarantee: holds. An organisation and visibility with no defaults, then openRepoShape's "Type yes to continue", mean that repeated Enter can never create three repositories.
- Measurability: not measurable in use, by design (the advisory "records nothing"). The pinned test cases are the evidence, which is acceptable.
- openRepoShape on PATH: explicit. The question says it is missing, and a Triad answer refuses at once.
- Speckit handoff: explicit (one
specs/002-<slug>/). -wipexemption: well defined (pattern and commit pinned). The open question is correctly carried to clarifications.- Non-English locales: the answers are Enter,
1and2, so they work in any locale. Optionally, keep the new text ASCII, as setup-openspeckit does (9fbe609c:108-110, "ASCII only, so no locale can turn the advisory into an encoding failure"). The question goes to strict-encoded stdout. - Windows terminals: no regression. Where
isatty()is false (Git Bash/mintty), the run takes the flag path with the advisory, and today'sask()already refuses there. The pty tests are POSIX-only, as stated.
Missing scenario, lower impact
- Agents and scripts driving
project newthrough a pty (for example, a tmux lane):- Gap: the helper cannot tell such an agent from a person. The agent meets a new prompt between
Project name:andType yes, and Enter takes the Triad. - Fix: add to Impact a line for
AGENTS.md(this repository has one; it is not in Impact) and for the README: "An agent offers the Triad in conversation (shared protocol,openspec-speckit-workflow.md:52-54), then runsproject newwith a choosing flag. It never answers the question for a person, as openRepoShape's AGENTS.md already says of its typed confirmation (design D2)."
- Gap: the helper cannot tell such an agent from a person. The agent meets a new prompt between
Side effects of this review
git fetch -q originin/workspace/projects/workBenchesupdated its remote-tracking refs only. No working-tree or branch change.- openRepoShape
1a9fc537'sscripts/andcontracts/were extracted intoscratchpad/pa-shape-1a9/to run its naming validator. - Nothing else was written.
Systems Architect council report (verbatim, sha256 3e1529af...)
Lane: openRepoProject-1
Council, Systems Architect: prefer-triad-in-project-new @ b8d566b
Verdict: this can be built in the single project file with no new dependency and no change to ask(). There are four concerns. One is HIGH: the test harness, the riskiest piece. One MEDIUM is a behaviour gap the proposal does not name (--workflow). The rest are wording and design constraints.
Concerns, ranked by impact
1. HIGH: the question's prompt does not reach stdout at a terminal, and the planned pty harness assumes it does
Concern: At a real terminal, input(prompt) writes its prompt to stderr. Tests built as the proposal describes them (stderr captured apart, pinned fixtures, the question "shown on stdout") will fail or flake on the four-job ubuntu/macOS × 3.10/3.12 matrix. That holds unless the harness is designed around this and around the platform differences between ptys.
Evidence:
- What the proposal says.
- "The helper decides whether the question, the interactive form of the offer, is shown on stdout."
- Impact: "a new
pty-based helper ... (stdin and stdout on a pseudo-terminal, stderr captured apart)". - "expected prompts, refusals and advisory lines pinned as fixtures".
- Probe of
project:149(return input(prompt + " ").strip()). Setup: Python 3.12.3 on Linux, with and without-I; stdin and stdout onos.openpty(), stderr on a pipe.print()output came back on the pty.- Both
input()prompts came back on the stderr pipe:b'QUESTION-PROMPT:SECOND:'. - Why: CPython's no-readline path writes the prompt to stderr (bpo-1927), and a script never loads readline, so this applies under
-Itoo.
- Probe of the in-process case. The same
input()withsys.stdoutswapped byredirect_stdout, as an in-processmodule.main()test does, wrote the prompt to the captured stdout. So the same prompt lands on different streams in the two harnesses. - Alignment premise. The alignment review assumed the opposite: alignment-qa-lead.md:91 says "
input()writes its prompt to stdout". - Prior art: setup-openspeckit's
run_on_terminal(devBenches/devcontainer.test/test-openspeckit-bootstrap.sh:3794-3814@9fbe609c).- It puts stdin, stdout and stderr all on the pty and uses
start_new_session=True. - It writes each answer only after the question appears.
- It sends SIGINT again every second, because "a signal landing between the prompt and the blocking read is absorbed without interrupting that read".
- It runs only in the Linux devcontainer. No pty-driven test in the estate runs on macOS.
- It puts stdin, stdout and stderr all on the pty and uses
- pty differences the harness has to absorb:
- ONLCR turns
\ninto\r\n, and typed answers are echoed into the master stream. - When the child exits, Linux ends a master read with
EIO. BSD-family ptys, macOS included, can return an empty read instead (see bpo-26228, CPython'spty.spawnhang on BSD). - Writing
^Cto the master signals nothing, because aPopenchild has no controlling terminal.
- ONLCR turns
Impact: HIGH. The CI matrix gates every merge.
- Tests written to the proposal's words assert two things that are false at a terminal: that the question is on stdout, and that stderr holds only the advisory or
REFUSED:. Those tests fail on their first run. - An interrupt test written naively flakes on the slower macOS runners. It gets the signal's own status (
-2) or a hang instead of 130. - A fix chosen under that pressure, such as writing the question with
sys.stdout.write, quietly makes the question behave unlike everyask()prompt.
Suggestion:
- Replace the harness sentence in Impact/tests with: "a
ptyhelper besiderun_cli:os.openpty()for stdin and stdout, and a separate pipe for stderr;- a
Popenchild started withstart_new_session=True; - a
selectloop that drains both the master and the stderr pipe until the child exits, treating bothEIOand an empty read as end of output; - each answer written only after its prompt has appeared on stderr;
- SIGINT sent with
send_signaland repeated until the child exits; \r\nnormalised and echoed input ignored;- every run bounded by a timeout."
- Correct "shown on stdout" to: "the question's lines go to stdout; its prompt, like every prompt
ask()shows at a terminal, goes to stderr". - Add this design constraint. The question's prompt goes through
ask(), so it behaves exactly like today's prompts. Pty fixtures pin prompts on stderr. In-process tests patchinputas well asisatty, and pin no prompt stream.
2. MEDIUM: --workflow runs setup-openspeckit, which gives its own Triad advisory and question on the repository just created
Concern: project's own silence rules hold, but the --workflow follow-up advises again by setup-openspeckit's rules. In such a run, "the question was the offer" and "silent for a workspace name" fail for the run as a whole.
Evidence:
- How
projectruns the follow-up.project:242-245,265-269runsetup-openspeckit --repo <destination>, which inheritsproject's stdin, stdout and stderr. - What setup-openspeckit does on every run (
9fbe609c:3966-3968). It callsgive_triad_advisory()once, before its first write.- On a single repository with a person at the terminal, it prints
TRIAD_ADVISORY_LINESon stdout and asks "Press Enter to continue (s = show the steps; Ctrl-C stops ...)" (:2357-2363). - Otherwise it writes two
warning:lines to stderr (:2364-2370).
- On a single repository with a person at the terminal, it prints
- Its workspace exemption reads the
originname orworkspace.yaml(:2200-2205). A freshly generatedbrett-wiphas noorigin, so it is advised unlessworkspace.yamlnames it. - What the proposal says.
- "A creation that went through the question prints none, because the question was the offer."
- A
-wipcreation "gets neither the question nor the stderr advisory". - The only
--workflowtest fakes a failing setup-openspeckit ("the stderr advisory after a--workflowfailure").
Impact: MEDIUM, and only for runs with --workflow:
- In
onp myapp --workflowat a terminal, the person answers2, and seconds later setup-openspeckit advises again and asks a second question. - A flag-chosen or CI
--workflowrun prints fourwarning:lines back to back:project's two, then setup-openspeckit's two, worded differently. The proposal itself records that wording divergence. - A
-wipcreation with--workflowis still advised. - Because the follow-up is faked in tests, none of this is seen before release.
Suggestion: Name the interaction in What Changes and decide it explicitly.
- Smallest consistent option. Add: "When
--workflowruns, setup-openspeckit's bootstrap advisory follows. It is a ratified form of its own.projectneither suppresses it nor coordinates with it, and the second offer is recorded as known." - Alternative.
projectomits its own stderr advisory when--workflowwill run. This loses the advisory whenever setup-openspeckit refuses beforegive_triad_advisory, so say so if it is chosen. - Real suppression needs a signal from
projectto setup-openspeckit, which is a workBenches change. Record it in Out of Scope as a follow-up for that owner. - Test. Add one test with an echoing fake
setup-openspeckitthat pins whichever behaviour is chosen.
3. MEDIUM: the placement bullet misstates today's refusal order, and handing the answers over "as if given by flag" drops a check
Concern: Three placement details need restating, so that an implementer cannot satisfy one bullet by breaking another.
Evidence:
- The
--workflowcheck is not where the proposal says it is.- The proposal says the question comes "after every pre-flight refusal today makes before a path is chosen: ... and
--workflowwithoutsetup-openspeckiton PATH". It also says "Invocations that skip the question keep today's order of refusals". - In the code, the
--workflowcheck runs after the path is chosen:project:242-244follows the shape refusals (:219-228) andchoose()(:239). choose()can itself prompt "Select a generator number" before that refusal.- So the check must move ahead of the question only when the question will be asked, and stay where it is otherwise.
- The proposal says the question comes "after every pre-flight refusal today makes before a path is chosen: ... and
- A typed visibility is not checked.
- argparse checks the
--visibilityflag againstchoices(project:939), but the shape branch only checks that a value is present (project:221). - A typed
Privateorprivput intoargs.visibilitypassesprojectand reaches openRepoShape. openRepoShape refuses it (setup-project.py:576-578), but only after its launcher has fetchedsetup.shfrom GitHub. - The proposal names the three values but not what an invalid or empty answer does.
- argparse checks the
- Triad prerequisites that need no answer are checked late.
- "Shape creation requires an existing --into parent directory." (
project:225-226) is checked after the Triad answer. - If it fires after the organisation and visibility prompts, the person typed both for nothing.
- The message names a
--intothey never typed: the default parent is~/projects(project:210).
- "Shape creation requires an existing --into parent directory." (
- End-of-input at the new prompts is unspecified. Through
ask(), end-of-input at the organisation or visibility prompt becomesCancelled.and 130 (project:1001-1003). At the question it is a refusal with exit 2.
Impact: MEDIUM. All of it can be built:
- The question goes between
project:217and:218. - A Triad answer sets
args.shapeand falls into:218-235. - The destination at
:213-215is unchanged, because--familyis a choosing flag and so is absent.
As written, though, two bullets contradict each other on the --workflow check, and an unchecked typed value can reach openRepoShape, which runs over the network.
Suggestion:
- Placement bullet. Reword it to: "...an invalid name,
parenttogether with--into, an existing destination, and, for an invocation that will be asked the question,--workflowwithoutsetup-openspeckiton PATH. Today that check comes after the path's own refusals (project:242-244), and it keeps that place when the question is skipped." - Triad answer. Add: "A Triad answer first checks what needs no answer: that
openRepoShapeis on PATH and the parent directory exists. It then asks for the organisation and the visibility. A typed visibility is checked against the flag's three values. Empty or unrecognised input is asked again once and then refused with exit 2, as at the question. End-of-input at either prompt exits [2 or 130, stated by design]." - Implementation. Compute "will the question be asked" once in
new(). Use that single predicate for the moved--workflowcheck, for the question and for the advisory.
4. LOW: the default answer sends every hyphen, underscore or digit-led name through three answers and a network run of openRepoShape to an exit-1 refusal
Concern: "project adds no second validator" makes the name refusal arrive late and remotely. Yet the proposal already pins one pattern from the same naming contract.
Evidence:
- The two name rules.
project:206acceptsmy-app,my_appand2048game.- openRepoShape's assembly-root form is
^[A-Za-z][A-Za-z0-9]*$(contracts/repository-naming.yaml:345@1a9fc537). - Every form in that file except
workspacestarts with a letter, and none admits_. - A hyphenated root produces leg names that no form admits.
- Where openRepoShape refuses, and with what status.
check_namesis step (4) (setup-project.py:1852).- It runs after
preflight(:1848), which runsgh auth statusand may offergh auth login. - It refuses with
die(..., 1)(:1532-1535): exit 1, not 2.
- The network fetch before it. Before any of that, the launcher fetches
setup.shfrom GitHub at${OPENREPOSHAPE_REF:-main}(openRepoShape:31,549). It skips the fetch only if a checkout sits beside it orOPENREPOSHAPE_SETUP_SHis set. - The existing pin. The proposal already pins
^[a-z0-9]+(?:-[a-z0-9]+)*-wip$from the same file at the same sha.
Impact: LOW. Nothing is created. But pressing Enter for my-app costs three answers (the question, organisation, visibility), a GitHub fetch and possibly a gh login offer. Only then comes a refusal that project could predict offline. The spec must not promise exit 2 for this refusal.
Suggestion: Take one of two routes.
-
Keep the decision, and record that this refusal exits with openRepoShape's status, 1.
-
Or pin the root form beside the
-wipform (same file, same sha), and use it only to:- state, beside the Triad, that openRepoShape will refuse this particular name;
- refuse a Triad answer before the organisation prompt, with exit 2 and openRepoShape's rule quoted.
This only rejects names openRepoShape is certain to refuse. It is not a second schema for what is accepted.
Dimensions that are solid
- Architecture fit.
- No new flag.
ask(),confirm()andchoose()are unchanged. - The new helper sits beside
ask()(project:146), and both the question and the advisory use it. - Exit codes keep today's rules:
Refusedgives 2 and an interrupt gives 130 (project:995-1003). - End-of-input at the question is caught around a single
ask()call, soask()itself does not change.
- No new flag.
- Advisory placement.
- The advisory belongs to the bench path only, between the
.project.jsonwrite (project:259-264) and the follow-up loop (:265). - The Triad path prints none, so
projectnever needs to know when openRepoShape has finished. What "single repository" would mean on the shape path never comes up.
- The advisory belongs to the bench path only, between the
- Delivery to
onp.onp→new-project.sh(exec ... project new "$@", which adds no flags) → launcher →setup-project-command.py --exec-owned.- That last step
exec()s the pinned artifact in the same process underpython3 -I(setup-project-command.py:457-480), so the terminal reachesprojectunchanged. - The question therefore reaches
onpusers once the pin advances. The proposal says the pin advance is workBenches' act.
- Tech stack.
projectneeds onlyisatty,os.environandre.- Because the
-wiprule is a literal, the creation path reads no YAML and needs no PyYAML. - Tests need
os.openpty,selectandsignal, which are stdlib on Linux and macOS for 3.10 and 3.12. - Avoid
pty.forkandos.forkpty: 3.12 warns about forking a threaded process.termiosis not needed.
-wippinning. The literal matchescontracts/repository-naming.yaml:412and setup-openspeckit:103. Cite the source sha in a comment and pin the literal in a test. Drift risk is low, because the contract states that adding this form changed no existing name's classification.- Performance.
- The question and the advisory read only the arguments,
CIand PATH (twoshutil.whichcalls). They touch neither git nor the network. - The only network access is openRepoShape's run on a Triad answer. That is today's
--shapepath; the change makes it the default answer.
- The question and the advisory read only the arguments,
Probes ran on Python 3.12.3 only. 3.10 is not installed here; the prompt-to-stderr path is long-standing CPython behaviour (bpo-1927).
Adversary Engineer council report (verbatim, sha256 abd43761...)
Council: Adversary Engineer, prefer-triad-in-project-new
Target: openspec/changes/prefer-triad-in-project-new/proposal.md at worktree HEAD b8d566b (PR #5).
Sources read: project, tests/test_project.py, the canonical specs, and the README at that HEAD. openRepoShape at 1a9fc537 (the openRepoShape launcher and setup-project.py). setup-openspeckit at workBenches 9fbe609c and d86ba59. The workBenches forwarders, launcher and pin on origin/main. The packet at openxFactory e6380965.
Verified by running Python 3.12.3 -I: the stderr edge cases in concern 3, and the interrupt behaviour in the last list.
Ranked by impact. All five stay inside the settled scope.
1. The pre-selected Triad answer runs into refusals that project could foresee locally, and the name refusal only arrives after network and gh steps — HIGH
Concern. Enter takes the Triad. For names, parents and machines that project already knows will fail, that default ends in a refusal, and only after two more prompts.
Evidence.
- Name.
project:206accepts[A-Za-z0-9][A-Za-z0-9_-]*.- The assembly-root form in openRepoShape
contracts/repository-naming.yaml@1a9fc537is^[A-Za-z][A-Za-z0-9]*$. - The proposal copies the
-wipregex from that same file ("Silent for a workspace name"). It also says "projectadds no second validator" ("Names"). These two positions contradict each other.
- Where openRepoShape's name refusal actually happens.
- The launcher fetches
setup.shat${OPENREPOSHAPE_REF:-main}(openRepoShape:31), not at1a9fc537. It then self-bootstraps a clone. setup-project.py_mainthen runs, in order:preflight: isghinstalled,gh auth status, and interactive offers to installghor log in (:1110-1140,:982-1007).resolve_org.resolve_project_and_elector, which callsgh api user. A Triad answer never carries--elected-by, so this call always happens.check_names→die(..., 1), which exits 1, not 2 (:1525-1534).
- So for
my-app, the default path is: Enter, type an org, type a visibility, a network fetch and a clone, possibly agh auth login, and then exit 1. - Offline, the person gets "could not fetch setup.sh" and exit 2. The name is never mentioned.
- The launcher fetches
- Parent directory.
- The default parent is
projects_dirs()[0](project:210). - The bench path creates it (
project:252). - The shape path refuses "Shape creation requires an existing --into parent directory." (
project:225-226). That message names a flag the person never typed.
- The default parent is
- openRepoShape missing. By the proposal's own rule, Enter means an immediate exit 2.
- Dry run.
- Today,
project new App --dry-runat a terminal exits 0 and prints the generator plan. The bench dry run never needs the parent (test_dry_run_creates_no_parent). - With Enter it now exits 2 when openRepoShape or the parent is missing. When it succeeds, it prints no generator.
- Canonical "Delegate project creation" → Scenario "Preview" ("prints the selected generator and destination without writes") is false for the Triad answer.
- The Capabilities paragraph does not say that the MODIFIED block restates Preview.
- Today,
Impact. HIGH.
onp my-app,project new my_toolandproject new 2fa-proxyare all accepted byproject. For each one, the pre-selected answer is a guaranteed refusal after two prompts and network round-trips.- The feature ships a default whose most likely outcome for any non-CamelCase name is a wall.
Suggestion. Add a "Triad prerequisites" requirement to the spec delta:
- Before asking,
projectchecks the obstacles it can see locally:openRepoShapeis on PATH;- the resolved parent exists;
- the name matches the assembly-root form, copied from
contracts/repository-naming.yaml@1a9fc537with a citation, exactly as the-wippattern is.
- The question names the specific obstacle beside the Triad. The Triad stays first and the default (D2).
- A Triad answer with an obstacle refuses at once, with exit 2:
- before the organisation and visibility prompts;
- before any network access;
- naming the next command:
project new <CamelName> …,--into <existing dir>, or answer2.
- Restate "Preview" in the MODIFIED block for both answers.
- Add scenarios:
- a terminal dry run with Enter and no
openRepoShape→ exit 2, nothing written; - a hyphenated name answered Triad → local refusal, no fake
openRepoShapeinvoked.
- a terminal dry run with Enter and no
- If the local name check is rejected anyway, the hyphenated-name test must pin openRepoShape's exit 1.
2. --workflow re-offers the Triad through setup-openspeckit, which breaks "advised once", and n means opposite things in the two consecutive questions — HIGH
Concern. With --workflow, project runs setup-openspeckit on the new repository. setup-openspeckit is itself a ratified advisory surface. It advises again, or asks again, right after project's own question or advisory.
Evidence.
- How
projectruns the workflow.project:242-245and:265-269runsetup-openspeckit --repo <destination>throughexecute()on the inherited terminal. - What setup-openspeckit does.
- Its
main()callsgive_triad_advisorybefore the first write (9fbe609c:3968). - It treats the fresh bench directory as a non-exempt single repository.
- Its workspace test reads only
origin(is_workspace_repository,:2200-2205), and a fresh bench directory has no origin. Soproject new brett-wip --workflow:projectstays silent by name, then setup-openspeckit advises.
- Its
- What its question means now. At a terminal it asks: "Press Enter to continue (s = show the steps; n or Ctrl-C stops before anything is written)". Since workBenches
d86ba59(#140, D004),n/nostop the run by SIGINT. - What the proposal says.
- "A creation that went through the question prints none, because the question was the offer."
- The non-interactive advisory is printed "before any
--workflowstep".
- The ratified rule. The requirement's title is "A person starting work outside a Triad is advised once".
Impact. HIGH. This hits a documented invocation: README project new MyApp ~/projects --bench flutterBench --type flutter --workflow.
- Terminal, single answer.
- The person answers
2and typesyes. The repository is created. - setup-openspeckit asks about the Triad again.
- Having just declined, the person types
n. - setup-openspeckit kills itself with SIGINT, and
executereturns 130. projectprints "Project created at …; workflow setup failed." and exits 130.
- The person answers
--yes --workflow. Two advisories are printed back to back, with the divergent wording the proposal itself records.- Grammar mismatch. At
project's question,nis "unrecognised", asked again, then exit 2. At the very next Triad question in the same command,nmeans stop.
Suggestion. Add an "Interaction with --workflow" section, and decide it explicitly:
- Option (a). When
--workflowwill run setup-openspeckit on a single repository,projectgives no stderr advisory of its own. This is a static rule about the delegate, not a record of having advised, so the packet's "no later act reads whether it was given" is kept. The cost: if the workflow fails before setup-openspeckit's advisory, the person gets no advisory. - Option (b). Accept the double offer and document it.
Either way:
- Define what
y,yes,nandnodo atproject's question. Either align with the bootstrap or refuse them with a message that names1/2. Do not leave them as "unrecognised". - Add tests for:
--workflowplus the single answer under the pty helper, with a fakesetup-openspeckitthat asks;--yes --workflow, asserting exactly one advisory block fromproject, or none under option (a).
- Cite
d86ba59, not only9fbe609c, as setup-openspeckit's current behaviour.
3. As specified, the stderr advisory can change stdout bytes and the exit status, which breaks two of the proposal's own Rules — MEDIUM
Concern. The proposal requires outcomes ("changes no stdout byte", "changes no exit status"). It specifies only a mechanism: "flushes stdout and writes the advisory to stderr". Inside project's error handling, that mechanism can be fatal.
Evidence.
- The handler.
project:995-1000,except (Refused, OSError)→print("REFUSED…", file=sys.stderr), exit 2. A failed advisory write is anOSError, so this handler catches it. - Stderr closed (
2>&-), verified.sys.stderr is None, andprint("warning: …", file=sys.stderr)writes to stdout, with exit 0. - Stderr unwritable (
2>/dev/full), verified. The write raisesOSError. The handler's ownREFUSEDprint raises again, and the process exits 120. - Both sibling realizations hardened exactly this:
- setup-openspeckit
emit_advisory_line(9fbe609c:2284-2292): "no encoding or closed pipe can turn into a failure". It swallows AttributeError, LookupError, OSError and ValueError.TRIAD_ADVISORY_LINESis ASCII-only "so no locale can turn the advisory into an encoding failure that would change the exit status" (:108-110). - openRepoShape
1a9fc537's review round F1–F9 "made the advisory unable to crash a run or change an exit status".
- setup-openspeckit
Impact. MEDIUM. Consider project new App --type t --yes --workflow with stderr closed or full, for example under a daemon, cron or CI wrapper:
- the repository exists;
projectexits 2 or 120;--workflowis silently skipped;Created:/Next:is never printed.
With 2>&-, the advisory lands in the stdout that a calling script parses.
Suggestion. Add a requirement, "The advisory cannot fail the run":
- write through one guarded helper;
- skip the write when
sys.stderrisNone; - swallow
OSError,ValueError,UnicodeErrorandAttributeErroron both the stdout flush and the stderr write; - use ASCII-only text.
Add scenarios and tests for:
- stderr closed:
preexec_fn=lambda: os.close(2); - stderr unwritable, using a pipe whose read end is closed. This works on macOS too, which has no
/dev/full.
Each asserts: exit 0, the workflow runs, and stdout is unchanged.
4. The organisation and visibility prompts have no grammar, ordering, re-ask or EOF rule, so public can be one keystroke away — MEDIUM
Concern. The question's answers are fully specified. The two prompts that actually decide where three repositories go, and who can read them, are not.
Evidence.
- What the proposal says. It asks for "the visibility, one of
public,privateorinternal, with no default".- That lists
publicfirst, in the argparse order (project:939). - openRepoShape's order is
VISIBILITY_CHOICES = ("private", "public", "internal")(setup-project.py:80).
- That lists
- The question is numbered. Its answers are numbered "as
choose()numbers". Nothing stops the design from numbering visibility the same way. - No re-ask or EOF rule is given for either prompt.
- Through
ask(), end-of-file becomesCancelled., exit 130 (project:1001-1003). At the question it is exit 2. - An invalid organisation refuses at once (
project:223). The question gets one re-ask.
- Through
- The final gate does not repeat visibility. openRepoShape says only "This will create THREE repositories in ''. Type yes to continue:" (
setup-project.py:1599-1622). - pty drivers. The Rule says "The question is asked only of a person at the terminal".
- The settled helper cannot tell tmux
send-keysorexpectfrom a person, and this estate runs its lanes in tmux. - The packet's D2 rests the guard against three repositories on openRepoShape's AGENTS.md, not on any mechanism in
project.
- The settled helper cannot tell tmux
Impact. MEDIUM. If the design numbers visibility, a person who just pressed 1 for the Triad can press 1 again. That makes three repositories public, and the final yes does not restate the choice.
Suggestion. Specify the visibility prompt:
- the full words only, case-insensitive and stripped;
- listed
private, public, internal; - no numbers and no default;
- unrecognised input → asked once more → exit 2.
Specify both prompts:
- end-of-file at the organisation or visibility prompt → exit 2, nothing created, the same as the question;
- a
publicanswer is echoed with its consequence before delegating.
Test every branch. Reword the Rule to "asked only where the terminal helper is true". Record the residual risk explicitly: a pty-driven agent cannot be told from a person, and the "no unconfirmed repository" guarantee then rests on openRepoShape's typed confirmation and the agent instructions (packet D2).
5. "Created a single repository" is undefined: bench generators create non-repositories, and the proposal itself says those get no advisory — MEDIUM
Concern. The advisory's trigger is a directory plus .project.json. That is a class the proposal, and openRepoShape, say is not a repository.
Evidence.
- The trigger. "Once the repository exists (the generator succeeded, the destination is a directory,
.project.jsonis written)". - The proposal's own Out of Scope. "A directory that is not a Git repository is not a repository at all (openRepoShape's
not-a-repository)". - openRepoShape.
1a9fc537's advisory is "silent for … a non-repository". - The packet. D2 says it "prints the recommendation when it creates a single repository".
- The generators.
- cppBench
new-cpp-project.shruns nogitat all. - flutterBench
new-flutter-project.sh:248-249runsrm -rf .gitafter copying specKit. - The test fixture generator is
mkdir -p(tests/test_project.py:31).
- cppBench
Impact. MEDIUM.
- On the bench path,
projectadvises exactly where openRepoShape is silent. - The advisory points at
adopt-project.py, which plans from a commit, and at a rootsingle-repository.yamlin something that has no repository. - The new tests would pin that contradiction in place.
Suggestion. Define the trigger in the spec delta. Choose one:
- (a) A successful bench creation is "the creation of a single-repository project" whether or not the generator ran
git init. Write down why the inspection-timenot-a-repositoryrule does not apply at creation. - (b) Give the advisory only when
<destination>/.gitexists, a declared fact of the new tree read locally with no network. Otherwise stay silent.
Add a fixture for each case.
Smaller issues (one line each)
- Interrupt during the Triad delegate (verified).
- On Ctrl-C,
subprocess.runkills its child 0.25 s later (run()'sexcept: process.kill()). - That child is the openRepoShape launcher, so the launcher's
trap 'rm -rf -- "$WORKDIR"' EXIT(openRepoShape:114) never fires. setup-project.pyandghkeep running afterprojecthas printedCancelled.and exited 130. Repositories may exist by then.- Scope "An interrupt exits 130 … as today" to the question, and record or fix this case, for example by ignoring SIGINT in the parent while the shape delegate runs.
- On Ctrl-C,
- Narrowed exemption. The packet's
-wipexemption also applies "because the person's own workspace configuration names it" (packetspec.md:186). "The one ratified exemption readable … at creation is the-wipform" therefore narrows it. Record that as deliberate, or carry it to clarifications along with the-wipquestion. - Leftover test setting. Impact says the change "reads nothing beyond the arguments, PATH and
CI". The tests still setAGENT_PROTOCOL_ROOT, which is left over from the dropped inspection reader. Drop it, or name what reads it. - Rules wording. "The single answer ends at … the
--yesthe person typed" cannot happen, because--yesskips the question. Reword it. - Task 5.6 tick. The tick is booked on the merge sha, but
onpandnew-project.sh, the entrypoints task 5.6 names, keep running pinneda0407904until workBenches moves the pin. File the pin-advance request on workBenches and cite it in the 5.6 evidence. - The fake
openRepoShapein tests.- It is safe with a sealed PATH in the subprocess tests.
- The risk is in-process
module.main(). Withexecuteunpatched, it resolves the real launcher from the developer's PATH, and the pty-typed answers go to it. - Assert that
shutil.which("openRepoShape")is the fake, and scrubGH_TOKEN,GITHUB_TOKENandOPENREPOSHAPE_*fromself.env.
Write the change's delta on the project-command capability. - MODIFIED "Delegate project creation": the whole requirement restated, adding the Triad-first offer, typed organization and visibility as explicit, the creation advisory, and that nothing is created without the person's confirmation; "Preview" now holds for both answers. - ADDED six requirements: who is asked the creation question and where it sits; the question's entries and answers; the known Triad obstacles, named beside the Triad and refused with exit 2 on a Triad answer; the organization and visibility prompts and the restating line; the creation advisory and its guarded write; and the order under --workflow. - 47 scenarios, behaviour only; openRepoShape's refusals promise no status beyond passing through. Lane: openRepoProject-1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Decide how project new builds the spec delta, answering the council's noted constraints N1 to N6. - One terminal rule and one offer value computed once in new(); the --workflow refusal is hoisted only for an asked invocation. - Both answers re-enter today's shape and bench branches; the question and the Triad prompts share one retry and end-of-input rule through ask(). - Known obstacles are three local reads; the assembly-root and -wip forms are pinned from openRepoShape 1a9fc537. - The advisory is written with os.write on stderr's descriptor: probes show a swallowed buffered write exits 120 on /dev/full and print(file=sys.stderr) lands on stdout when stderr is closed. - Under --workflow, project's offer comes first and setup-openspeckit follows unchanged; Ctrl-C while openRepoShape runs keeps today's behaviour, documented, with the wait-for-child alternative recorded. - Fixed ASCII text, exit statuses, the pty helper and the fakes. Lane: openRepoProject-1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tasks.md is the governance and handoff record only: the governance boxes (ratification by Brett Heap's word left open), the one Speckit feature still to be created by /speckit.specify, and the landing and archive steps. Implementation tasks belong to that feature alone. Lane: openRepoProject-1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Lane: openRepoProject-1 Phase 6: the spec delta, design and governance tasks (
|
| Operation | Requirement | Scenarios |
|---|---|---|
| MODIFIED | Delegate project creation ("Preview" reworded so that it holds for both answers; "Existing project" kept) | 4 |
| ADDED | Creation question is asked only of a person choosing at the terminal | 9 |
| ADDED | Creation question offers the Triad first | 8 |
| ADDED | Known Triad obstacles are named before the question and refused on a Triad answer | 7 |
| ADDED | A Triad answer asks for the organization and the visibility | 8 |
| ADDED | Creation advisory follows a single repository created without the question | 7 |
| ADDED | Creation offer precedes the workflow follow-up | 4 |
The scenarios describe behaviour only. Where the proposal left the exact text to design, the spec states the required elements. An openRepoShape refusal is promised only to pass through with its own status (N6).
Design decisions: design.md
- D1: One terminal rule decides the question: stdin and stdout are both terminals, and
CIis false-y.new()computesofferonce, and the hoisted check, the question and the advisory all read that one value. - D2: The order inside
new(). The--workflowrefusal is hoisted only for an invocation that will be asked; every other invocation keeps today's refusal order. - D3: The known obstacles are three local reads, with no network. The assembly-root form is pinned from openRepoShape
1a9fc537. - D4: The question's lines go to stdout and its prompt goes through
ask(). The question and the two Triad prompts share one rule: a retry once, then exit 2; end of input exits 2; an interrupt printsCancelled.and exits 130. - D5: The organization uses today's check, and an answer that fails it counts as unrecognised. The visibility is a full word, with no number and no default. The restating line is printed before the plan, in a dry run too.
- D6: The single-repository answer re-enters today's bench path, and no advisory follows it.
- D7: The advisory is written with
os.writeon stderr's descriptor, and is never written whensys.stderrisNone. Probes showed that a swallowed buffered write exits 120 on/dev/full, and thatprint(file=sys.stderr)lands on stdout when stderr is closed. - D8 (N1): Under
--workflow,project's offer comes first and setup-openspeckit follows unchanged, with no suppression and no coordination. The double offer is recorded. - D9 (N3): Ctrl-C while openRepoShape runs keeps today's behaviour, and the README documents it. The alternative, waiting for the child, is described and not taken: it would change
--shapeandupdate --component shapebeyond this change's scope. - D10 (N4): A
-wipname gets neither the question nor the advisory. Only the name form is read, and the regex is pinned from1a9fc537. - D11 (N2): Agents at a pseudo-terminal are not detected.
AGENTS.mdand the README tell an agent to offer the Triad in conversation and then runproject newwith a choosing flag. - D12 (N6): A table of exit statuses.
project's own refusals exit 2; openRepoShape, the generator and the follow-up pass their own statuses through. - D13: Every new string is fixed here as ASCII text, for the tests to pin as fixtures.
- D14: The tests: the pty helper as the proposal specifies it, in-process tests that patch
inputandisatty, a fakeopenRepoShapeand a fakesetup-openspeckiton a controlledPATH, a scrubbed environment, and the three guarded-write cases.
Tasks: tasks.md
tasks.md holds governance and the handoff only. Governance boxes 1.1 to 1.5 are ticked; ratification by Brett Heap's word (1.6) is left open. The Speckit handoff names specs/002-<slug>/, which /speckit.specify creates after ratification, and /opsx:apply is not run until that section names exactly one feature.
Open questions
None changes what gets built. Two are left to the lead:
- Whether to file D9's alternative, waiting for the delegate on
new --shapeandupdate --component shape, as an issue on this repository now. - Whether the workBenches handoff issue should also note that setup-openspeckit's advisory writer exits 120 when stderr is a full device, and that its advisory omits "run by a person deciding for that project".
Checks
openspec validate prefer-triad-in-project-new --strictandopenspec validate --all --strictpass (6 of 6).openspec statusshows proposal, specs, design and tasks all done.python3 -m unittest discover -s testsruns 43 tests OK, as on main.
brettheap
left a comment
There was a problem hiding this comment.
Lane: openRepoProject-1
VERDICT: READY at 11d3e9c
Independent re-verification of PR #5, prefer-triad-in-project-new
Verifier: Sonnet (lane openRepoProject-1's re-verifier; wrote none of the change). Read-only throughout: no edit, commit, push or register line. Worktree orp-triad was at 11d3e9c456b58a1e5f5bb33cffa4573deb0813ce, branch change/prefer-triad-in-project-new, clean (git status --short printed nothing); origin/change/prefer-triad-in-project-new is the same sha.
Checklist
| # | Item | Result | Evidence |
|---|---|---|---|
| 1 | Delta vs canonical | PASS | grep -n '^### Requirement: Delegate project creation$' hits canonical spec.md:9 and delta spec.md:3, byte-identical (cat -A). The delta restates the whole requirement text and keeps both canonical scenarios: "Preview" (reworded for both answers, spec.md:21-24) and "Existing project" (verbatim, :26-28). The only MODIFIED header is that one; the other four canonical requirements are untouched (no REMOVED or RENAMED block); the six ADDED headers collide with none. Archive simulated in a throwaway copy (openspec archive prefer-triad-in-project-new --yes): + 6 added, ~ 1 modified, - 0, then openspec validate --all --strict on the result: 5 passed, 0 failed. Canonical specs on this branch equal origin/main (git diff --quiet origin/main HEAD -- openspec/specs). |
| 2 | Delta vs proposal | PASS | Every What Changes bullet maps (details below the table). All 13 options of new (project:931-972; the two positionals aside): 9 choosing (--shape --org --visibility --family --elected-by --bench --type --description --yes) and 4 non-choosing (--dry-run, --workflow, --into/positional parent, --workbenches), identical lists in proposal :87-91, spec :50-56, design :91-96. No contradiction found. |
| 3 | Spec hygiene | PASS | 7 requirements, each body has SHALL; 47 #### Scenario: headers (none at another depth), 47 of 47 carry WHEN and THEN (script); no function, library, line-number or implementation-step text in the delta (grep of ask, isatty, os., sys., fullmatch, subprocess, .py:, project:NNN: nothing but ordinary words). openspec validate ... --json: "issues": []. |
| 4 | Design vs clarifications | FAIL (non-blocking) | N1 to N6 are the six clarifications.md headings in file order; design :9-14 maps N1 D8, N2 D11, N3 D9, N4 D10, N5 D4+D14, N6 D12, and each decision exists and answers its note. All Impact constraints appear: prompt through ask() (D4), one offer value (D1), hoist only when asked (D2 step 3), guarded ASCII os.write skipped when sys.stderr is None (D7 steps 2 and 4), the full pty helper (D14 bullet 1: os.openpty, start_new_session, select, EIO or empty read, answer after prompt, repeated send_signal SIGINT, \r\n normalised, echo ignored, timeout, no pty.fork), in-process input and isatty patches with no stream pinned (D14 bullet 2), scrubbed env without GH_TOKEN, GITHUB_TOKEN, OPENREPOSHAPE_* (D14 bullet 3). One gap: the on-PATH assertion is stated for the fake openRepoShape only (see note 1). |
| 5 | D13 strings | PASS | Script over design.md:431-484: all ASCII; advisory is exactly 2 lines (:477-478), both start warning:; together they contain preferred, not required, elective, confers nothing, adopt-project.py, a person deciding for this project, single-repository.yaml, Nothing here changes. Question: entry 1 is Triad (default) and precedes entry 2; entry 1 carries the posture; single-repository.yaml is on entry 2 only; prompt Create it as a Triad? [Y/n or 1/2]: reads naturally with y/yes = Triad and n/no = single. |
| 6 | Cross-document identity | PASS | -wip regex ^[a-z0-9]+(?:-[a-z0-9]+)*-wip$: identical in proposal :217, spec :52, :90, design :382, clarifications :102. Root-name regex ^[A-Za-z][A-Za-z0-9]*$: identical in proposal :123, spec :175, design :157. git show 1a9fc537:contracts/repository-naming.yaml: 345: pattern: "^[A-Za-z][A-Za-z0-9]*$" (project-leg role assembly) and 412: pattern: "^[a-z0-9]+(?:-[a-z0-9]+)*-wip$" (family workspace). CI false values: workBenches 9fbe609c CI_FALSE_VALUES: Final = ("", "0", "false", "no") (line 127) equals proposal :74, spec :48 and :85-87, design :79-80. Choosing-flag list: identical (above). Exit codes 2 and 130 are consistent across proposal, spec and design D12; the spec makes no promise about openRepoShape's own refusals (spec.md:278-280: "exits with that same status"; :242-244). |
| 7 | tasks.md | PASS | Governance and handoff only; - [ ] / - [x] numbered format; openspec instructions apply --change ... --json parses 13 total, 5 complete; 1.1 to 1.5 ticked, 1.6 open (tasks.md:11-16). ## Speckit Handoff (:31-43) names exactly one future feature specs/002-<slug>/, created by /speckit.specify, and says /opsx:apply is not run until it names exactly one feature. grep -rn project-triad-preference: one hit, proposal.md:322, the "dropped" note. .openspec.yaml is schema: spec-driven and created: 2026-10-07. |
| 8 | Tooling | PASS | openspec validate prefer-triad-in-project-new --strict: Change ... is valid, exit 0. openspec validate --all --strict: 6 passed, 0 failed. openspec status --change ... --json: proposal, specs, design, tasks all done, isComplete: true. python3 -m unittest discover -s tests: Ran 43 tests in 7.178s OK. git diff --stat origin/main...HEAD -- . ':!openspec/changes/prefer-triad-in-project-new': empty (the whole diff is the six change files, 1664 insertions). The design's five stream probes were re-run on Python 3.12.3 and all reproduce (rows 1 to 4: 2>&- puts ADVISORY on stdout; buffered write on /dev/full swallowed but exit 120; os.write swallowed and exit 0; closed-read pipe BrokenPipeError swallowed, exit 0; row 5: pty probe puts PROMPT? on the stderr pipe, echo yes\r\n on the pty). |
| 9 | PR #5 | PASS | gh pr view 5: body line 1 Lane: openRepoProject-1; line 61 Refs #3. The realization PR closes the issue; this PR is governance. (no Closes #3); closingIssuesReferences: []; isDraft: true; headRefOid: 11d3e9c4.... Body lists phases 1 to 6 and the six files, and states 47 scenarios and six ADDED requirements (both confirmed by script). gh pr checks 5 and statusCheckRollup: 9 of 9 SUCCESS (SonarCloud, and ubuntu and macos on Python 3.10 and 3.12, each run on push and on pull_request). |
Item 2 mapping, bullet by bullet (proposal What Changes to delta)
- Offers the Triad first, one question, Triad listed first and default with posture, obstacle named beside it, single accepted without a reason and naming
single-repository.yamlin one clause, question creates nothing and is not a confirmation: "Delegate project creation":10-19, "Creation question offers the Triad first":112-135, scenarios "The question's entries", "An answer is not a confirmation". - One terminal rule (stdin and stdout terminals,
CI), lines to stdout, prompt behaves as existing prompts, existing prompts unchanged: "Creation question is asked only of...":43-59; "offers the Triad first":120-122. - Nine choosing and four non-choosing flags, dry run asks and prints each plan:
:50-56, scenarios "A choosing flag skips the question", "A dry run asks too". - Where the question sits (after name and the three pre-flight refusals;
--workflowrefusal hoisted only for an asked invocation; other invocations keep today's order)::61-68, scenarios:99-110. - Answers Enter/1/2/y/yes/n/no, case-insensitive, whitespace ignored, ask once more then exit 2, EOF exit 2, interrupt
Cancelled.and 130::124-130, scenarios:142-170. - Three known obstacles read before the question, named beside the Triad, Triad answer refused with exit 2 before any prompt or network, dry run too, single answer unaffected, openRepoShape authoritative: "Known Triad obstacles..."
:173-221. - Organization then visibility, full words in order
private,public,internal, no default, retry once then exit 2, EOF exit 2, interrupt 130, restating line with thepublicconsequence, openRepoShape run without--yes, its refusals passed through with its own status::224-280. - Single-repository advisory only when the question was not asked, after the destination and
.project.jsonexist and before any--workflowstep, two ASCIIwarning:lines on stderr, guarded write, never stdout, never exit status, neverchecks, never--json, silent for-wip, dry run, Triad, generator failure::283-335. -wipname silent::52-53, scenario "A workspace name":89-92.--workfloworder, no suppression and no coordination, failure after the advisory: "Creation offer precedes the workflow follow-up":338-364.
Blocking findings
None.
Non-blocking notes
- Item 4, the on-PATH assertion covers only
openRepoShape.design.md:509-514(D14, "The environment") andproposal.md:375-379(Impact) say each Triad test assertsshutil.which("openRepoShape", path=env["PATH"])is the fake and each missing test assertsNone. The fakesetup-openspeckit(design.md:522-526) has no such sentence. Because PATH is built from the directories ofbash,gitandsys.executable, asetup-openspeckitinstalled in one of those directories would silently defeat the--workflowrefusal test. Fix: add afterdesign.md:514"The--workflowtests assertshutil.which("setup-openspeckit", path=env["PATH"])is the fake, and the refusal test asserts it isNone", and the same clause inproposal.md:394-395. Adds an assertion only; no behaviour changes. - The ratified "convert nothing" rule is in the proposal but not in the delta.
proposal.md:275-278(Rules This Change Keeps) saysprojectnever runsadopt-project.py, never converts an existing repository and never writes a manifest or record on the advisory's account (packetspec.md:103-107: "SHALL convert nothing"). The delta carries only "SHALL NOT writesingle-repository.yaml" (spec.md:120-121) and "SHALL record nothing about having given it" (spec.md:303-304). It contradicts nothing and is not a What Changes bullet, so it is not a mapping failure. Suggested sentence atspec.md:297: "The command SHALL NOT runadopt-project.py, convert an existing repository, or write a manifest or record on the advisory's account." - Capitalisation of the posture phrase.
design.md:440hasPreferred, not required(capital P) in the question's entry 1; the scenario atspec.md:139quotespreferred, not required. The advisory (design.md:477) is lower-case and exact. Tests pin the D13 fixtures, so nothing breaks; either lower-case the clause in D13 or let the scenario say "in any letter case". - Parenthetical in the advisory requirement.
spec.md:283-284says "(chosen by flag, or not at a terminal)"; theCIcase, whichspec.md:311-313covers by scenario, is "at a terminal" yet not asked. The governing clause ("without the creation question having been asked") is correct, so this is wording only. Suggested: "(chosen by flag, or the terminal rule does not hold)". - Restating line wording.
proposal.md:158says "Before delegating to openRepoShape";spec.md:237and design D5 say "before the plan is shown", so it also prints in a dry run (D5 gives the reason). Consistent, and the spec is the stricter; noted so the implementer follows the spec. - Ruling count.
tasks.md:11says alignment rulings "R1 to R14"; the resolution comment 6028469515 header says "(R1 to R15)" but its tables cite only R1 to R14 and R15 is defined nowhere. The tasks.md text matches the evidence; the comment header is off by one. No repository change needed. - Why section length.
openspec archiveprints "Why section should not exceed 1000 characters" (the section is 2293 characters,proposal.md:20-54). A warning only;validate --strictpasses and the archive proceeds. The archive simulation also shows INFO-level "Requirement text is very long (>500 characters)" on seven requirements; informational. - PR body. After the lane's text, the body carries a bot-appended "Summary by Sourcery" block. Harmless, and it contains no closing keyword.
Every external citation spot-checked was accurate: openRepoShape 1a9fc537 (openRepoShape:53, :114, :549; setup-project.py:224-225, :428, :1532-1535, :1622, :1848-1852; shape_advisory.py:169, :207-215), workBenches 9fbe609c and d86ba59 (setup-openspeckit lines 103, 127/131, 2204-2209, 2273, 2288, 2320-2356, 4059-4078, warning: at 2370), the packet at e6380965 (spec.md:58-59, :90, :99-105, :178, :186; design.md:85-87, :372-380), and every project:NNN citation.
Verdict
READY at 11d3e9c. Zero blocking findings; items 1, 2, 3, 5, 6, 7, 8, 9 PASS; item 4 has one non-blocking gap (note 1). The PR can be marked ready for Brett Heap's word; task 1.6 (ratification) stays open.
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 2 days and 20 hours by commenting |
From review 5436721813 (READY, zero blocking findings): 1. design.md D14 and proposal.md test plan: the --workflow tests assert the fake setup-openspeckit is what PATH resolves, and the refusal test without it asserts None. 2. spec.md creation-advisory requirement: SHALL NOT run adopt-project.py, convert an existing repository, or write a manifest or record on the advisory's account. 3. design.md D13: lower-case "preferred, not required" in entry 1 so it matches the spec scenario. 4. spec.md creation-advisory requirement: "(chosen by flag, or the terminal rule does not hold)" now covers the CI case. Nothing else changed. Lane: openRepoProject-1 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Lane: openRepoProject-1 The four non-blocking notes from review 5436721813 are folded at the new head
Nothing else changed. |
|
Apply the second review round to the four-document brainstorm packet and
re-align the overview, synthesis and handoff with the feature documents:
- Probe directory rule: worktree-specific probes run in the worktree's own
index (`git -C <worktree path>`), repository-wide probes at the canonical
root; without it the hidden-local-state probe read the main index.
- Caps follow the removal floor: a removal child is spawned only with at
least `removal_floor_seconds` (5 s) of the work deadline left, so the
batch target cap is 16, not 24 (128 worktree rows unchanged); the
overview's listing phase is an estimate counted against the deadline,
with a 5 s bound per root, and the break-even and single-repository
figures move with it.
- Submodules: any gitlink, or an admin `modules` directory, excludes a
worktree as `contains-submodule` from the same bounded `ls-files` probe.
- One path-escaping form in both documents (`\xHH` bytes, `\uXXXX` code
points, lowercase hex); the packet carries no raw bidirectional control
character.
- Merge target: strip `refs/remotes/origin/` from the `origin/HEAD` symref;
manifest revalidation compares against the plan-time value.
- Rewritten scenarios whose expectations the documents' own rules made
impossible ("Repository replaced mid-batch", "Gated merged worktrees");
explicit error-row fields; `{"error", "code"}` exit-2 JSON; `doctor`'s Git
refusal; the overview's fifth gate also withholds `--all-safe` above the
batch row cap.
- Handoff resolution record updated with the round-2 result and the cap
decision; baseline notes record origin/main at ca4c615 (PR #5, OpenSpec
only) and the closure of PR #2 (`cleanup`, retained on origin) with the
follow-ups its decision record names.
Design only: no code, tests, OpenSpec or Speckit artifacts. Packet
validator passes; review rounds 1 and 2 resolved for every high and medium
finding; targeted re-verification recorded in the PR.
Lane: openRepoProject-3
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…fications.md
Lane: openRepoProject-1
The proposal's status paragraph now states the ratification ("ratify 5", PR #5) and the landing sha instead of the pre-ratification draft status. clarifications.md gains the lane line the other files of the change carry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-new) (#8) * Specify feature 002-triad-first-project-new from the ratified change Lane: openRepoProject-1 Creates the single Speckit feature that realizes OpenSpec change prefer-triad-in-project-new (ratified by Brett Heap, "ratify 5", 2026-10-07; PR #5 landed as ca4c615), records the ratification and the Speckit handoff in the change's tasks.md, and adds the spec quality checklist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Record the ratification in proposal.md and add the lane line to clarifications.md Lane: openRepoProject-1 The proposal's status paragraph now states the ratification ("ratify 5", PR #5) and the landing sha instead of the pre-ratification draft status. clarifications.md gains the lane line the other files of the change carry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Plan feature 002-triad-first-project-new Lane: openRepoProject-1 The plan sequences the ratified design (D1 to D14) into nine slices, with research, the data model, the project new command-line contract and a quickstart validation guide. It cites the design rather than restating it and contradicts none of it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add tasks for feature 002-triad-first-project-new Lane: openRepoProject-1 Seventy-three dependency-ordered tasks in eight phases, tests before implementation in each user-story phase, with every one of the 47 delta scenarios named by at least one test task and a traceability record to be kept in traceability.md. Landing, the workBenches issue and the issue comments stay with the lane lead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Phases 1 and 2: traceability record, test harness and the offer decision Lane: openRepoProject-1 Adds the D13 fixtures, the D14 environment, fakes, pty and in-process helpers, the terminal-rule test, person_at_terminal(), the pinned name forms and the offer value computed once in new(), which nothing reads yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Phase 3 (US1): ask the creation question at the terminal Lane: openRepoProject-1 A person at a terminal without a choosing flag is asked Triad or single repository once the name is known; the single-repository answer, retries, refusals and interrupts work end to end, with T012 to T022 as pty tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Phase 4 (US2): create the Triad from the answer Lane: openRepoProject-1 A Triad answer asks for the organization and then the visibility, restates them, and re-enters the unchanged shape branch, so openRepoShape runs without --yes and keeps its own confirmation; T025 to T033 cover it at the pty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Apply the analyze findings to the feature artifacts and record phases 1 to 4 Lane: openRepoProject-1 Applies the 13 findings of the /speckit-analyze pass (C1, C2, G1 and ten LOW) to tasks.md, contracts/project-new.md, data-model.md and plan.md, and ticks T001 to T034, verified by the lead at 4f23450 (65 tests OK on all four CI jobs). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Phase 5 (US3): name the known Triad obstacles and refuse them on a Triad answer Lane: openRepoProject-1 The obstacle reader checks the name form, openRepoShape on PATH and the parent directory before the question, names each under the Triad entry, and refuses a Triad answer that meets one (T035 to T041). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Phase 6 (US4): keep scripted creation unasked and advise it on standard error Lane: openRepoProject-1 A single repository created without the question gets the two advisory lines through a guarded descriptor write that cannot reach stdout or change the exit status (T042 to T053). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Phase 7 (US5): workspace names stay unasked and the offer precedes the workflow follow-up Lane: openRepoProject-1 An asked invocation now meets the --workflow prerequisite refusal before the question, and the tests pin the -wip silence and the advisory's place ahead of setup-openspeckit (T054 to T061). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Phase 8 (Polish): document the creation question, the advisory and the agent rule Lane: openRepoProject-1 README "Create a project" and its Configuration table, and an AGENTS.md section, describe the question, what skips it, the Triad prompts, the stderr advisory, the --workflow order, Ctrl-C and agents (T062 to T064). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Record phases 5 to 8 as verified and fix the T043 seam Lane: openRepoProject-1 Ticks T035 to T073, verified by the lead at 4e20f61 (94 tests OK on all four CI jobs; traceability 47 of 47; fixtures 25 of 25), and removes a stray punctuation seam left by the analyze edit to T043. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Apply the re-verification findings F1 to F3, F6 and F7 Lane: openRepoProject-1 F1 pins the typed organization's case on its way to openRepoShape; F2 states that CI is compared case-insensitively and stripped; F3 corrects a comment; F6 and F7 are governance bookkeeping (handoff wording, box 2.2, spec status). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>



Lane: openRepoProject-1
What this is
The OpenSpec change
prefer-triad-in-project-new: phases 1 to 6 of this repository's/opsx:proposeflow are complete (scaffold,proposal.md, alignment review, council, verdicts, then the spec delta,design.mdand the governancetasks.md). Nothing here is ratified, and no code byte moves.Awaiting Brett Heap's word; the single Speckit feature is created after ratification with
/speckit.specify.openspec/changes/prefer-triad-in-project-new/.openspec.yamlproposal.mdclarifications.mdspecs/project-command/spec.mddesign.mdtasks.mdproject-command. "Delegate project creation" is MODIFIED, and six requirements are ADDED (47 scenarios in all). No new capability.11d3e9c:openspec validate prefer-triad-in-project-new --strictandopenspec validate --all --strictpass;openspec statusshows every artifact done;python3 -m unittest discover -s testsruns 43 tests OK, as on main.Governing pointers
openRepoProject-1(claim comment).prefer-triad-project-shape,openspec/changes/prefer-triad-project-shape/tasks.mdon opensoft/openxFactory maine63809650d39586134c4ec4fdb6e9effc87a7860. It was ratified by Brett Heap on 2026-10-06 ("ratify 1249 as recommended") and merged on "merge 1249".project-command's claim. This change edits nothing that claim holds (scripts/new-project.sh,scripts/onp,config/openrepoproject-pin.json);onpcarries the new behaviour once workBenches advances its pin on its owner's act. The realization's merge sha goes on project new offers the Triad first (openxFactory prefer-triad-project-shape, task 5.6) #3 for codeXfactory-5 (comment).e081c5ab);9fbe609c);single-repository.yamlschema, Prefer the Triad: the work-start advisory, the optional single-repository.yaml, and the posture text (prefer-triad-project-shape 5.2) openRepoShape#164 (1a9fc537), under Realize prefer-triad-project-shape: the Triad is preferred, the staying-single record, and the work-start advisory openRepoShape#163, now closed as completed.What the proposal says, in brief
project newoffers the Triad first to a person at the terminal who has not chosen by flag.CIis false-y.Type yesare both kept; no repository is created that the person has not confirmed.warning:advisory on stderr once the repository exists (ratified design D2).<user>-wipname gets neither the question nor the advisory.Revision after alignment review
project status/project doctoradvisory sketched in project new offers the Triad first (openxFactory prefer-triad-project-shape, task 5.6) #3 is outside the ratified forms of the advisory; it would need the doctrine owner's amendment or Brett Heap's word. Its detection findings are recorded in the proposal's Out of Scope section..project.jsonand exit status stay as today.-wipname form is read.openRepoShapestated and refused; the CamelCase name constraint stated, refusal stays openRepoShape's.Council review
proposal.mdat92e4d2c; 6 NOTED recorded in the newclarifications.md; 3 DISMISSED.openRepoShapeon PATH, the parent directory) are read before the question, named beside the Triad, and refused with exit 2 on a Triad answer before any prompt or network; the second offer that--workflowbrings from setup-openspeckit is recorded, with a workBenches follow-up; the organisation and visibility prompts are specified; the advisory write is guarded; and the workBenches pin advance becomes a tracked handoff issue.Spec delta and design
specs/project-command/spec.md): the MODIFIED "Delegate project creation" restates the whole requirement, with "Preview" reworded so that it holds for both answers. The ADDED requirements cover who is asked the creation question and where it sits; its entries and answers; the known Triad obstacles; the organization and visibility prompts; the creation advisory; and the order under--workflow.design.md): decisions D1 to D14 answer the council's noted constraints N1 to N6 and fix every new string as ASCII text for the tests to pin. No open question changes what gets built.tasks.md): governance and handoff only. Ratification is left unticked, and the one Speckit feature (specs/002-<slug>/) is still to be created.Refs #3. The realization PR closes the issue; this PR is governance.
🤖 Generated with Claude Code
Summary by Sourcery
Establish the OpenSpec proposal and governance artifacts for preferring Triad-shaped projects during eligible interactive
project newflows without implementing the behavior yet.Enhancements:
Chores: