Round: 001-cli-bootstrap-and-config · Repo: gosharplite/tellme
Artifact under review: specs/plans/001-cli-bootstrap-and-config/tasks.md
Subject: architect · Griller: griller · Orchestrator: butler
Question cap: 8 (used 8/8) · Issue: #5
Verdict: proceed with changes
Understanding. specs/plans/001-cli-bootstrap-and-config/tasks.md is the /axb-tasks output and the execution control plane for round 001 — 62 tasks (- [ ] T001…T062), split as Phase 1 Setup (T001–T003), Phase 2 Foundational (T004–T008), Phase 3 Test Alignment (T009–T053 = the 45 DSL stepdefs, T054 = the phase review gate), and four Feature phases 4A–4D (T055–T062, one [BDD-GREEN] -> [BDD-REFACTOR] pair each). Its inputs are the round's executable CLI contract — 4 interface feature files, 17 Rules, and 45 DSL rows = 8 root + 9 configuration + 12 workspace + 14 diagnostics + 2 usage (specs/truth/features/cli/dsl.md plus the four {module}/dsl.md). The repo has no go.mod/Go code/Makefile, so this is the starting project — which is precisely why Setup is present (Rule: Setup only when the round introduces new technology; here Go 1.26 module + pflag + yaml.v3 + godog, named verbatim in T001).
Phase 2/3 mechanics. All 45 Phase-3 stepdefs are [BDD-RED] — correct, because there are no pre-existing stepdefs to ALIGN/REMOVE (no Go code before this round). The /axb-dsl-refine MODIFY rows in truth-delta.md (the grill-#4 fixes: restored W1/W2/W5 arrange, pinned --json key schema, error-code distinctness) are in-round truth edits made before any stepdef existed, so they have nothing to align to; the W2/D2 row is a NOOP (session 8, upstream entailment criterion → fold) and is correctly not given its own task, while its sentences still appear as RED because the workspace feature uses them and no stepdef exists. The Parallel Hint reads "T009–T053 各派一個獨立 subagent;T054 等全部回來再啟動 subagent 來 review" — matching the SOP's one-DSL-one-[P]-task, review-last, review-not-in-batch rule.
Feature phases. 4A–4D each realize one ADD truth feature and carry only [BDD-GREEN]/[BDD-REFACTOR] + a Test Scope (Rule 2 forbids RED/ALIGN/REMOVE in Feature phases — honored here). The order 4A config → 4B workspace → 4C diagnostics → 4D usage is dependency-safe: 4A delivers -c parsing + the config resolver (Decision 3's 6-step algorithm, TELL_ME_HOME-first), 4B the output/<mode>/ lifecycle, 4C the -d/-d --json reporting that consumes both, 4D the unrecognized-flag usage path. The load-bearing gates are T006 (exit taxonomy: Success(0), UsageError, ConfigError, EnvironmentError, DiagnosticUnresolvedError — distinct/deterministic per FR-014) and T008 (SC-004 witness: go list -deps cmd/tellme build-graph guard). Parity with tell-me-go holds on the mechanics I can verify: its Makefile build is go build -ldflags="-X 'main.version=$(VERSION)'" -o tell-me-go ./cmd/tell-me-go, its E2E lives in tests/e2e/, and verify-no-test-sleep is a real ADR-036 gate.
My execution plan (via /axb-implement). One-Shot: Setup and Foundational are strictly serial single-task rounds (each stops at its own 只做/不做); Phase 3 is the single parallel batch (T009–T053, each subagent reads its sentence's dsl.md row and touches only the test layer), followed by the T054 review loop that must show zero undefined steps and only assertion-or-product failures before Phase 4 unlocks; Feature phases stay serial and delegate /axb-bdd one requested step at a time with the phase's Test Scope. I will not commit without explicit approval.
Where I believe the plan is weakest — the seams I expect to pressure-test. (a) The Phase-3 stepdef landing/registration convention is under-pinned. Foundational T004 creates tests/e2e/suite_test.go (suite entry + scenarioContext) but no task pins where the 45 stepdefs land or how godog's single InitializeScenario registration is extended — the reference templates/tasks.example.md has exactly a "pin the landing file" Foundational task (T006 in the example), and only the Phase-3 Shared Must Read mentions tests/e2e/steps/. Under the conflict rule (Phase-3 same-file writers merge-on-write), an unpinned landing leaves "the file the later writer merges into" undefined. (b) The VERSION=0.0.0-harness sentinel build wiring is unassigned. Decision 6 and the diagnostics DSL row mandate the harness build with the sentinel so a dropped -X fails instead of false-greening against the dev default; T005 builds the binary but names no -ldflags, and its Read doesn't cite the sentinel row. (c) A minor hygiene item: the Core Inputs header still lists specs/truth/contracts/**, specs/truth/data/**, and ui/** — all NOOP/skipped/deleted this round (template-mandated, but stale-looking). I treat (a) and (b) as the first things to nail inside Foundational rather than latent Feature-phase surprises, and I am ready to be shown I have mis-scoped them or missed others.
Q1: Where exactly do the 45 Phase‑3 stepdefs land, and by what mechanism are they registered into godog's single suite — and is that convention actually tenable for a 45‑subagent parallel batch? (Verified: T004 names no step-file landing; T009–T053 name no write target; only hint is tests/e2e/steps/ unsuffixed; the consumer template pins the landing and per-task Read; /axb-implement Rule 1 dispatches all 45 [P] as one batch while Rule 3 assumes a named read-modify-write merge target.)
A1: Conceded — the landing and registration convention are genuinely unbound; the subject flagged the absence but never chose one. The recommended answer has the right shape (module-per-file, aggregator, Foundational pin, per-task Reads) but as literally written does not compile: (1) five files in one package cannot each expose RegisterSteps (name collision); (2) tests/e2e/ (package e2e) and tests/e2e/steps/ (package steps) are two packages, so an unexported scenarioContext in suite_test.go is invisible to handlers in steps. Binding convention: landing = tests/e2e/steps/, one file per DSL authority with distinctly-named registrars; register.go exposes RegisterAll(ctx); per-scenario state moves into the steps package (scenario_context.go) created by a Before hook and threaded via context.Context; suite_test.go keeps only the TestSuite wiring. New Foundational pin task (renumber tail); T004 drops its in-suite scenarioContext; every T009–T053 Read gains its landing file. Tenable and better than the template's own one-file funnel, though not lock-free (diagnostics.go takes up to 14 writers).
Q2: The Parallel Hint mandates exactly the concurrent dispatch that triggers the same-file race — so why is the race-surviving convention the fallback rather than the default, and what actually serializes the diagnostics.go writers? (Verified: /axb-implement Phase 4 step 5 + ParallelHint…判準 Rule 1 mandate one-batch dispatch; Rule 3's read-latest-then-merge presupposes a serialized write order nothing imposes; Feature-phase serialization was deliberately not extended to Phase 3.)
A2: Conceded on both counts — the "fallback" label was wrong; the Hint mandates concurrency and nothing serializes writes, so the 5-file split hits a guaranteed 14-way lost-update window on every run. The race-surviving convention is the default: one step file per Phase-3 task (45 files), each appending its registrar in init() into a package-level var registrars []func(*godog.ScenarioContext); RegisterAll iterates it → zero shared edits, no merge needed. Verified: init() runs single-goroutine (race-free); build order is deterministic; step-registration order is irrelevant. Decisive argument is dominance under the Hint's ambiguity: file-per-task is correct whether the batch runs concurrently or serialized; the 5-file split only if serialized. The griller's module-by-module alternative is rejected (contradicts "一次派出該批"). Residual: Rule 3's unsound merge assumption is itself an aixbdd-tmg host-rule gap (R1/R2 pattern).
Q3: How does the E2E harness obtain the five exit-code values, and how is FR-014's cross-class distinctness actually asserted — without the black-box strategy (Decision 5) and the "values = implementation" ruling (grill #4 Q6) becoming inconsistent? (Verified: no DSL row pins a 碼值 literal; only T006 defines the constants; no task says how the test learns them; no task asserts pairwise distinctness.)
A3: Conceded — the artifact does not choose. Bind: import github.com/gosharplite/tellme/internal/cli into tests/e2e as the single source of the five constants, plus one host-harness TestExitCodesAreDistinct (four non-zero codes pairwise distinct + distinct-from-zero). Discipline: the import supplies only the expected value; every step's observed value still comes from the black-box subprocess. That is a gray-box oracle — record it in research.md Decision 4/5. Bind the assertion into T006 (export the five constants). Not hard-code (reverses Q6, drifts); not "non-zero only" (cannot witness distinctness); not pin-values-as-contract (reverses Q6). Importing is correctly-placed: the test runs at T006 with no binary.
Q4: The boot resolver contract lives only in research.md Decision 3, yet no task reads it and no feature asserts its order. Where does Decision 3 enter the execution control plane — contract or implementation? (Verified: Decision 3 binds the 6-step order + divergence rule; no phase Shared Must Read cites research.md; only T006→D1&7 and T008→D5 read it; outcomes are order-insensitive except an uncovered overlap.)
A4: Conceded; verdict: implementation guidance, not contract. The fix is a Read binding, not a new Example: add research.md -> Decision 3 to 4A+4B (+4C) Shared Must Read, quote the two load-bearing clauses into their Boundary ("TELL_ME_HOME is resolved before the config path even when -c is supplied"; "${REQ_MODE:-butler} is a filename seed only; filename/mode divergence is legal and non-fatal"). Disclosed correction: the outcomes are not strictly unobservable but unasserted — a failing -c config and an unusable home yields environment-vs-configuration error depending on order, and -d reason precedence is the resolver order; record that as an explicit residual.
Q5: {workspace_path} is declared "relative to the runtime home" and composed as {home}/{workspace_path}, but every feature supplies a value that already contains the home name ("ait-tmg/output/butler"). Which is canonical — and where does the fix land? (Verified: root dsl.md defines {home} as a name the temp dir "stands for"; workspace/dsl.md + diagnostics/dsl.md compose {home}/{workspace_path}; features pass "ait-tmg/output/butler".)
A5: Conceded — a real, non-cosmetic mismatch the topology audit cannot see. Blast-radius correction: 8 rows, not 26 (the 7 {workspace_path} rows in workspace/dsl.md + the 1 in diagnostics/dsl.md). Binds reading (b) — operator-facing, {home}-rooted paths — grounded not on "acceptance would need changing" (false; acceptance is carried, not executed) but on the suite's name-substitution spine: {home} is defined as a name the temp dir stands for, and the report rows already force name→actual mapping. Fix routes to /axb-dsl-refine (features + dsl.md are truth): re-word "relative to the runtime home" → "expressed with {home} as its root; the {home} name stands for the actual TELL_ME_HOME," drop the literal {home}/{workspace_path} composition, state the single substitution rule once, and mark {config_path} as a distinct home-relative input token. tasks.md must not encode a normalizer until settled; 4B/4C Green block behind it.
Q6: The [BDD-RED] completion criterion is registration and T054 checks only registration/undefined/failure-mode — against T007's skeleton, does the gate bind each stepdef to its row's StepDef 實作語意, or does it pass a registered-but-semantically-wrong stepdef?
A6: Conceded — the gate proves the test layer is wired, not that it encodes truth: a placeholder Given registers, and a wrong-channel Then still "fails on assertion." Premise correction: not "the success-path Thens pass" — only the Givens/Whens and the single exit-code Then pass trivially; the content-asserting success Thens fail on assertion, which only sharpens the gap (the failure clause cannot distinguish a correct from a wrong assertion, and cannot catch a no-op Then). Bind body-conformance in three homes: T054 (4th criterion: body implements the row's channels — Given/When: 怎麼做+權威狀態落地+回寫; Then: 必查's 呈現結果/權威狀態/再讀確認), the Phase-3 Boundary (per-task self-verification, One-Shot Rule 5), and the Markers [BDD-RED] completion clause (the semantic root). Mechanism is an LLM review of prose channels, not a mechanical lint.
Q7: The sentinel is asserted by T042 and must be injected by the harness build, but no task cites it, no task names the -ldflags build, and make build defaults VERSION ?= dev — the value Decision 6 exists to exclude. Where is the sentinel bound, and what keeps the harness off the dev path?
A7: Conceded. Bind in T005 (the single E2E-build owner): its 只做 names the exact go build -ldflags "-X main.version=<sentinel>" -o <tmp>/tellme ./cmd/tellme; its Read gains the techstack.md "Version assertion" row and the diagnostics/dsl.md tellme prints the build version row and research.md -> Decision 6; the sentinel is defined once as a harness constant consumed by both the build and T042's assertion. Refinements: (a) the helper/sentinel/scenarioContext cannot live in package e2e (would cycle steps → e2e → steps) — they belong in a leaf package; (b) bar "any build path that can omit the sentinel" rather than "make is forbidden" (make build VERSION=0.0.0-harness is fine; the footgun is the silent ?= dev). Systemic finding: Q4 and Q7 are the same orphaned-artifact class.
Q8: Decision 7 adopts verify-no-test-sleep parity and the techstack "Task runner" row declares a verify target — but T002 builds neither, and no task creates them. Which task owes the adopted gate, and does the orphan-coverage sweep run before delivery? (Verified: Decision 7 adopt list (a); techstack "Task runner" declares verify; T002 builds only build/fmt/vet/staticcheck/tidy/test; tell-me-go has a real verify-no-test-sleep: wired into check/check-full.)
A8: Conceded — a third instance of the Q4/Q7 class. Bind T002 as owner: add a verify-no-test-sleep target (mirroring tell-me-go's grep) and a verify aggregator running the adopted verify-class gates (no-sleep + build-graph guard). Reconcile Decision 5(2)'s "Makefile gate" wording vs T008's Go test (network_guard_test.go) — relax to "a verify-class gate (Makefile target or host-harness test)" and have verify invoke it. Run the orphan-coverage sweep now, pre-delivery — enumerate every specs/truth/** row and every research.md Decision, assert each is reached by a Read or delivered by a task, record it in tasks.md; route the durable fix upstream (the axb-tasks Phase-5 self-check) on the R1/R2 pattern. tasks.md is not yet delivered — ordering: (1) /axb-dsl-refine Q5 fix; (2) revise tasks.md for Q1–Q4/Q6–Q8 + re-derive the 8 Q5 rows; (3) orphan sweep as pre-delivery gate; (4) then deliver.
proceed with changes. The subject's structural diagnosis held — the 62-task shape, the 45-row coverage (8+9+12+14+2), the NOOP handling of W2/D2, the one-batch/review-last Parallel Hint, and every parity claim checked (VERSION ?= dev, the -ldflags build, tests/e2e/, a real verify-no-test-sleep) were verified true — but its readiness diagnosis did not: the artifact is not executable as-is, because Phase 3 halves responsibility for stepdefs with no pinned landing/registration (Q1/Q2), the exit-code oracle, resolver, sentinel and determinism gates are bound by no Read (Q3/Q4/Q7/Q8 — three distinct orphans in one class), the [BDD-RED]/T054 gate proves only wiring, not truth-encoding (Q6), and, decisively, workspace/dsl.md + diagnostics/dsl.md carry a truth defect (Q5). Apply the bound edit set, route the Q5 truth fix to /axb-dsl-refine and block 4B/4C behind it, and treat tasks.md as not delivered until the orphan-coverage sweep passes as a pre-delivery gate.
I stand by the structural diagnosis and concede the readiness one. Everything the griller verified true remains true (62-task shape; 8+9+12+14+2 coverage; the W2/D2 NOOP; the one-batch/review-last Hint; the tell-me-go parity claims). What I got wrong was the readiness verdict: I flagged the missing stepdef pin and the unassigned sentinel as "seams" but asserted the artifact was otherwise executable. It is not — it is not runnable against a correct product, and I own that.
Revised, in order of weight: (1) the Q5 truth defect — bind reading (b), corrected blast radius = 8 rows, fix routes to /axb-dsl-refine, 4B/4C Green block behind it; (2) Phase-3 registration/landing is unbound — one step file per task with init() self-registration as the default; (3) the orphan class (D3, D6 + "Version assertion", D7 + "Task runner") — bind D3 into 4A/4B(/4C), D6 + the sentinel into T005, verify-no-test-sleep + verify into T002; (4) the [BDD-RED]/T054 criterion proves only wiring — add body-conformance to T054, Boundary, Markers; (5) the exit-code oracle is internal/cli imported by the host harness + TestExitCodesAreDistinct, landed with T006.
Two in-round corrections on record: the "26 stepdefs" figure was an overcount (real number 8), and "success-path Thens pass" against the T007 skeleton is imprecise (only the Givens/Whens and the single exit-code Then pass trivially). Neither weakens the finding; both sharpen it.
Open questions for the user: (1) ratify the Q5 reading (b) consciously; (2) the leaf harness package vs the steps package for scenario_context/run-helper/sentinel (cycle risk); (3) the build-graph guard's form (Decision 5 "Makefile gate" vs T008 Go test) and whether verify invokes it as a Make step or go test -run; (4) the exit-code test's home (host-harness tests/e2e vs a zero-import internal/cli unit test); (5) whether the orphan-coverage sweep and the unsound same-file merge rule route upstream to aixbdd-tmg (R1/R2 pattern). Bottom line: tasks.md is not delivered until the truth fix lands, the Q1–Q4/Q6–Q8 edits are applied, and the orphan-coverage sweep passes.