From 3793922fac02df3da698525a408f7a1ee02cafc5 Mon Sep 17 00:00:00 2001 From: SSFSKIM Date: Sat, 18 Jul 2026 05:27:13 +0900 Subject: [PATCH] =?UTF-8?q?refactor(board-schema):=20the=20Ticket=20Gate?= =?UTF-8?q?=20is=20board=20schema=20=E2=80=94=20one=20copy=20in=20issue-tr?= =?UTF-8?q?acker,=20every=20consumer=20routes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Completes the single-sourcing arc: ready-for-agent's bar (WELL-DEFINED + WELL-SCOPED) was consumed by five parties — the implement worker, spike graduation, decompose child triage, feedback triage, and human/sprint registrars — yet its definition lived inside one role's protocol, with three skills cross-pointing into 'the IMPLEMENT gate'. Same inversion the park discriminant had; same fix. - NEW skills/issue-tracker/references/ticket-gate.md — the canonical bar: fork-ownership taxonomy (mechanical/architecture/taste), scope sizing (1-2 ExecPlans, big-but-ATOMIC, land-independently), answer-source rule incl. the human-as-async-source clause (moved intact from 385bb81), and the registrar honesty contract (verdicts are recommendations — the gate re-runs at dispatch). - implementing-tickets SKILL.md: the Gate section keeps the worker flow (gate-before-code, grill posture, orient-work rule, Check-2 outcome execution) and routes the check definitions to {{BOARD_SCRIPTS}}/../references/ticket-gate.md — no new placeholder, the path derives from an existing binding. - operation-manual, spike graduation, decompose step 3, issue-tracker's ready-for-agent row, triaging-feedback overview + authoring bar: all cross-references retargeted to the schema file; vendored copies removed. The Codex-sandbox triage worker protocol keeps its vendored Korean condensation (no Skill/file access in that harness — documented exception). - tests: RED-first — gate-file canon asserts (taxonomy, sizing, recommendation-not-trust, human-as-source), protocol routing + anti-re-vendoring asserts, cross-file pointer asserts. --- skills/implementing-tickets/SKILL.md | 35 ++++++----------- .../references/implement-decompose.md | 3 +- .../references/operation-manual.md | 27 ++++--------- .../references/spike-worker-protocol.md | 3 +- skills/issue-tracker/SKILL.md | 2 +- .../issue-tracker/references/ticket-gate.md | 39 +++++++++++++++++++ skills/triaging-feedback/SKILL.md | 5 ++- .../test-protocol-content.sh | 29 +++++++++++--- 8 files changed, 90 insertions(+), 53 deletions(-) create mode 100644 skills/issue-tracker/references/ticket-gate.md diff --git a/skills/implementing-tickets/SKILL.md b/skills/implementing-tickets/SKILL.md index 402427818b..6d12d512cc 100644 --- a/skills/implementing-tickets/SKILL.md +++ b/skills/implementing-tickets/SKILL.md @@ -27,37 +27,26 @@ Toolkit: THE GATE comes before everything. Do not write code until the ticket passes. Interrogate the brief the way a doperpowers:brainstorming grill -interrogates a human — but every answer must come from the ticket body, the -codebase, or repo docs. The human is a source too — asynchronously: a -human-grade fork parks, and the relayed answers become ticket content -before work resumes. Trivial lookups (docs, grep, an API's actual shape) +interrogates a human. Trivial lookups (docs, grep, an API's actual shape) are orient work: do them, never park for them. -Check 1 — WELL-DEFINED. Classify every fork the implementation will hit: -- Mechanical/technical with one obvious best answer (internal naming, - idiomatic choice, repo precedent) → YOUR call. Parking these is a - protocol violation, not caution. -- Non-trivial architecture (subsystem boundary, data model, API shape) → - must be answered by ticket + codebase; unanswered → gate-fail. -- Product design or taste, major OR minor (user-facing behavior, wording, - interaction/visual choices — anywhere a reasonable human could prefer - differently on non-technical grounds) → must be answered by the ticket; - unanswered → gate-fail. Even minor taste is never your call. - -Check 2 — WELL-SCOPED. The work must fit this ticket as one purpose-unit -(roughly 1–2 ExecPlans — big-but-ATOMIC work that cannot land halfway -still counts as ONE unit; that is what plan-mode execution exists for. -Decompose only work whose children could land on main independently). -Too big? One question decides: can the remainder -be written down as self-contained child pre-specs right now? -- Yes → DECOMPOSE: open the decomposition procedure — read this file and +The two checks — WELL-DEFINED (who owns each fork the implementation +will hit) and WELL-SCOPED (fits this ticket as one purpose-unit) — are +board schema, one copy next to the board scripts. Open the gate file and +run both checks against the ticket: + {{BOARD_SCRIPTS}}/../references/ticket-gate.md +Check-2 outcomes are yours to execute: +- Too big, and the remainder CAN be written down as self-contained child + pre-specs right now → DECOMPOSE: open the decomposition procedure — read + this file and follow it before registering a single child: {{DECOMPOSE_DOC}} It carries the register command with typed edges, the pre-spec bar for child bodies, honest gate-triage, the Roadmap escape hatch for contingent phases, and the parent update. You write NO code; end your turn when the children stand. -- No — the slices need one continuously steered human context → +- Too big and NOT decomposable — the slices need one continuously steered + human context → {{BOARD_SCRIPTS}}/board-transition.sh {{ISSUE_NUMBER}} interactive-preferred "" and end your turn. diff --git a/skills/implementing-tickets/references/implement-decompose.md b/skills/implementing-tickets/references/implement-decompose.md index f4cd44b74e..ce20d3630f 100644 --- a/skills/implementing-tickets/references/implement-decompose.md +++ b/skills/implementing-tickets/references/implement-decompose.md @@ -20,7 +20,8 @@ only the PROCEDURE and grants no authority beyond your prompt's. 3. Gate-triage each child HONESTLY per the doperpowers:issue-tracker ticket contract and park discriminant — `ready-for-agent` only if YOU - believe it passes the Ticket Gate. + believe it passes the Ticket Gate + (`/../references/ticket-gate.md`). 4. Register only children you can spec self-contained NOW. Contingent later phases live as a `## Roadmap` section in the parent body — the diff --git a/skills/implementing-tickets/references/operation-manual.md b/skills/implementing-tickets/references/operation-manual.md index c71d5acacb..8ddf1600d6 100644 --- a/skills/implementing-tickets/references/operation-manual.md +++ b/skills/implementing-tickets/references/operation-manual.md @@ -24,30 +24,17 @@ audit trail, not requests. Full design + rationale: | `references/spike-worker-protocol.md` | the Spike Worker Protocol — bound as `PROTOCOL_FILE` when the ticket's category is `spike` (the exploration lane below) | | `references/implement-decompose.md` | runtime-opened decomposition procedure — the protocol carries only a pointer (`{{DECOMPOSE_DOC}}` = absolute path); the worker opens it when Check-2 says decompose. Conditional-large protocol blocks live this way: procedure in a plugin file, instance facts in the prompt | | `references/engine-blocks/execution.md` | the EXECUTION text — one block for both model routes, since every worker is a Claude-harness session (the engine label picks the model route: `codex` = clodex gateway/GPT, `claude` = plain Claude); keeps interactive-session skills (writing-plans, subagent-driven-development) out of daemon workers; rides the bootstrap's EXECUTION_BLOCK binding (implement lane only — spikes are exploration, not TDD) | -| The Ticket Gate | the pre-code pass/park verdict (below) | +| The Ticket Gate | the pre-code pass/park verdict (below; check definitions in issue-tracker's `references/ticket-gate.md`) | | board schema + dispatch ritual | owned by doperpowers:issue-tracker (states, scripts, the mechanical ritual, the wake ritual) | | `scripts/` | empty this phase — the auto-attach trigger (`implement-dispatch.sh` + workflow template) lands here next phase | ## The Ticket Gate -Runs during ORIENT, before any source file opens — brainstorming's grill in -absentia: every answer must come from the ticket body, the codebase, or -repo docs. Trivial lookups are orient work, never a park. - -**Check 1 — well-defined.** Every fork the implementation will hit: - -| fork class | who answers | -|---|---| -| mechanical/technical, one obvious best answer | the worker — parking these is a protocol violation, not caution | -| non-trivial architecture (subsystem boundary, data model, API shape) | ticket + codebase; unanswered → gate-fail | -| product design or taste, **major or minor** | the ticket; unanswered → gate-fail — even minor taste is never the worker's call | - -**Check 2 — well-scoped.** Fits ~1–2 ExecPlans — big-but-atomic work that -cannot land halfway still counts as ONE unit (that is what ExecPlan mode -exists for); decompose only work whose children could land on main -independently. Too big forks on ONE question: *can the remainder be -written as self-contained child pre-specs right now?* Yes → decompose. -No → `interactive-preferred`. +Runs during ORIENT, before any source file opens — brainstorming's grill +in absentia. The two checks' definitions (WELL-DEFINED fork ownership, +WELL-SCOPED sizing) are board schema, owned by doperpowers:issue-tracker +in `references/ticket-gate.md` — one copy for workers and registrars +alike; the worker protocol routes there at gate time. **The verdict is the worker's first board write.** Dispatch writes nothing; `in-progress` + a `[gate]` comment = pass, a park state = fail. @@ -130,7 +117,7 @@ What changes and what doesn't: re-parks), or graduates. - **Graduation** — production work the findings clearly justify is registered `--spawned-by ` with honest gate-triage against the - IMPLEMENT gate; murkier outcomes stay a Recommendation line for the + Ticket Gate; murkier outcomes stay a Recommendation line for the human. - The engine label picks a model route only (web reach is harness-level — every worker has it); choose per the work's model fit, mechanism diff --git a/skills/implementing-tickets/references/spike-worker-protocol.md b/skills/implementing-tickets/references/spike-worker-protocol.md index 1116695c36..c49593d51a 100644 --- a/skills/implementing-tickets/references/spike-worker-protocol.md +++ b/skills/implementing-tickets/references/spike-worker-protocol.md @@ -74,7 +74,8 @@ Graduation: when the findings clearly warrant production work you can spec self-contained NOW, register it ({{BOARD_SCRIPTS}}/board-register.sh "" <bug|enhancement> <P0..P3> --spawned-by {{ISSUE_NUMBER}} --body-file <spec>), gate-triaged honestly (ready-for-agent only if it -would pass the IMPLEMENT gate; an open taste fork → born needs-human). +would pass the Ticket Gate — {{BOARD_SCRIPTS}}/../references/ticket-gate.md; +an open taste fork → born needs-human). Per the doperpowers:issue-tracker ticket contract, author its body at register time — the pre-spec sections filled from your findings; a skeleton "to fill in later" is not a graduation. Anything diff --git a/skills/issue-tracker/SKILL.md b/skills/issue-tracker/SKILL.md index 0c0042d6e1..eb7297b2e5 100644 --- a/skills/issue-tracker/SKILL.md +++ b/skills/issue-tracker/SKILL.md @@ -56,7 +56,7 @@ the review loop (doperpowers:reviewing-prs) a PR passes through | state | GitHub encoding | meaning | note | |---|---|---|---| -| `ready-for-agent` | open + `status:ready-for-agent` | pre-spec complete (the bar: it would pass the implement gate — well-defined + well-scoped, doperpowers:implementing-tickets; the next worker re-runs that gate, so this claim is a recommendation, never inherited trust); dispatchable once blockers are done | — | +| `ready-for-agent` | open + `status:ready-for-agent` | pre-spec complete (the bar: the Ticket Gate — `references/ticket-gate.md`, well-defined + well-scoped; the next worker re-runs it, so a registrar's claim is a recommendation, never inherited trust); dispatchable once blockers are done | — | | `in-progress` | open + `status:in-progress` | a worker passed the gate and is driving it (an epic stays here while children run) | optional | | `needs-human` | open + `status:needs-human` | parked for the human **as themselves**: a decision only they can make, or a real-world input only they possess (credentials, auth, production data) | **required** | | `needs-info` | open + `status:needs-info` | rare: the spec is unambiguous but lacks depth for a sophisticated result, or core decisions need substantial research first | **required** | diff --git a/skills/issue-tracker/references/ticket-gate.md b/skills/issue-tracker/references/ticket-gate.md new file mode 100644 index 0000000000..196bb44b94 --- /dev/null +++ b/skills/issue-tracker/references/ticket-gate.md @@ -0,0 +1,39 @@ +# The Ticket Gate — what `ready-for-agent` means + +The bar a ticket must pass before implement work begins — board schema, +one copy, owned by doperpowers:issue-tracker. Consumed everywhere a +readiness judgment is made: the implement worker re-runs both checks at +every dispatch (a registrar's verdict is a +recommendation, never inherited trust), and every registrar — follow-ups, +decompose children, spike graduations, feedback triage, sprint +materialization — triages honestly against it: `ready-for-agent` only if +the ticket would pass. + +Every answer must come from the ticket body, the codebase, or repo docs. +The human is a source too — asynchronously: a human-grade fork parks the +ticket, and the relayed answers become ticket content before work +resumes. + +## Check 1 — WELL-DEFINED + +Classify every fork the implementation will hit: + +- Mechanical/technical with one obvious best answer (internal naming, + idiomatic choice, repo precedent) → the worker's call. Parking these is + a protocol violation, not caution. +- Non-trivial architecture (subsystem boundary, data model, API shape) → + must be answered by ticket + codebase; unanswered → gate-fail. +- Product design or taste, major OR minor (user-facing behavior, wording, + interaction/visual choices — anywhere a reasonable human could prefer + differently on non-technical grounds) → must be answered by the ticket; + unanswered → gate-fail. Even minor taste is never the worker's call. + +## Check 2 — WELL-SCOPED + +The work must fit the ticket as one purpose-unit: roughly 1–2 ExecPlans — +big-but-ATOMIC work that cannot land halfway still counts as ONE unit +(that is what plan-mode execution exists for). Decompose only work whose +children could land on main independently. Too big? One question decides: +can the remainder be written down as self-contained child pre-specs right +now? Yes → decompose. No → the work needs one continuously steered human +context: `interactive-preferred`. diff --git a/skills/triaging-feedback/SKILL.md b/skills/triaging-feedback/SKILL.md index 67f7ca14ff..a1eb7314e5 100644 --- a/skills/triaging-feedback/SKILL.md +++ b/skills/triaging-feedback/SKILL.md @@ -15,7 +15,8 @@ launchd-cron'd) claims pending rows, drives a read-only Codex-SDK worker through the Triage Worker Protocol (`references/triage-worker-protocol.md`) in a disposable detached worktree, and registers the ticket the worker authored — born `ready-for-agent` when the diagnosis is grounded and the -ticket honestly passes the implement-side gate definitions, else parked +ticket honestly passes the Ticket Gate (the board schema's +`references/ticket-gate.md` in doperpowers:issue-tracker), else parked (`needs-human`/`needs-info`) with an explicit note saying what is unclear. **The worker is a translator, not a fixer.** It writes no code and opens no @@ -147,7 +148,7 @@ network. It cannot register a ticket or write to the `feedback` table — it can only emit a diagnosis and a structured verdict (the fenced JSON block) that the dispatcher parses. But *within* that verdict the worker has full editorial voice: it authors the ticket's title and body to the board's -standard (the implement-side well-defined + well-scoped gate is the +standard (the board's well-defined + well-scoped Ticket Gate is the authoring bar) and recommends the birth state. The dispatcher (`dispatch.ts`, running with real credentials) is the only diff --git a/tests/implementing-tickets/test-protocol-content.sh b/tests/implementing-tickets/test-protocol-content.sh index 886f44082c..30956430d0 100755 --- a/tests/implementing-tickets/test-protocol-content.sh +++ b/tests/implementing-tickets/test-protocol-content.sh @@ -32,9 +32,10 @@ echo "protocol content:" [ -f "$PROTO" ] || { echo "missing $PROTO"; exit 1; } proto="$(cat "$PROTO")" assert_contains "$proto" "THE GATE comes before everything" "gate precedes everything" -assert_contains "$proto" "WELL-DEFINED" "check 1 present" -assert_contains "$proto" "WELL-SCOPED" "check 2 present" -assert_contains "$proto" "Even minor taste is never your call" "minor-taste rule present" +assert_contains "$proto" "WELL-DEFINED" "check 1 named" +assert_contains "$proto" "WELL-SCOPED" "check 2 named" +assert_contains "$proto" "/../references/ticket-gate.md" "gate definitions route to the schema file (BOARD_SCRIPTS-relative)" +assert_not_contains "$proto" "internal naming" "fork taxonomy not re-vendored in the protocol" assert_contains "$proto" "VERDICT IS YOUR FIRST BOARD WRITE" "verdict-first-write present" assert_contains "$proto" "WHO UNPARKS IT" "park discriminant present" assert_contains "$proto" "{{DECOMPOSE_DOC}}" "decompose procedure pointer present (runtime-opened)" @@ -55,8 +56,7 @@ assert_contains "$proto" "## Validation Evidence" "validation-evidence section m assert_contains "$proto" "## Confusions" "confusions section (conditional) mandated" assert_contains "$proto" "ORIENTATION SUMMARY" "park orientation summary mandated" assert_contains "$proto" "no live progress mirror" "no-mirror doctrine stated in the protocol" -assert_contains "$proto" "big-but-ATOMIC" "atomic-counts-as-one-unit scoping clause present" -assert_contains "$proto" "land on main independently" "landability decompose criterion present" +assert_not_contains "$proto" "land on main independently" "landability criterion lives in the gate file, not the protocol" assert_contains "$proto" "single home" "park discriminant routes to issue-tracker (single-source)" assert_not_contains "$proto" "Knowledge work anyone could do" "needs-info definition not re-vendored in the protocol" assert_contains "$proto" "doperpowers:reviewing-prs" "handoff to the review loop named" @@ -179,6 +179,25 @@ assert_contains "$tracker" "worker-bootstrap.md" "ritual: renders the bootstrap, assert_not_contains "$tracker" "embedded verbatim" "ritual: verbatim-embed spawn retired" assert_not_contains "$tracker" "implement-worker-protocol.md" "ritual: no reference to the retired protocol file" +echo "ticket gate (schema file, single copy):" +GATE="$REPO_ROOT/skills/issue-tracker/references/ticket-gate.md" +[ -f "$GATE" ] || { echo "missing $GATE"; exit 1; } +gate="$(cat "$GATE")" +assert_contains "$gate" "WELL-DEFINED" "gate: check 1 canonical here" +assert_contains "$gate" "WELL-SCOPED" "gate: check 2 canonical here" +assert_contains "$gate" "protocol violation, not caution" "gate: mechanical forks are the worker's (parking them is the violation)" +assert_contains "$gate" "never the worker's call" "gate: minor-taste rule canonical here" +assert_contains "$gate" "big-but-ATOMIC" "gate: atomic-counts-as-one-unit sizing canonical here" +assert_contains "$gate" "land on main independently" "gate: landability decompose criterion canonical here" +assert_contains "$gate" "recommendation, never inherited trust" "gate: registrar verdicts are recommendations (gate re-runs)" +assert_contains "$gate" "The human is a source too" "gate: human-as-async-answer-source clause carried over" +assert_not_contains "$gate" "{{" "gate: placeholder-free (opened at runtime, never rendered)" +assert_contains "$tracker" "ticket-gate.md" "tracker: ready-for-agent row names its bar (the gate file)" +assert_contains "$manual" "ticket-gate.md" "manual: gate section routes to the schema file" +assert_not_contains "$manual" "one obvious best answer" "manual: fork table not re-vendored" +assert_contains "$spike" "ticket-gate.md" "spike: graduation bar routes to the gate file" +assert_contains "$decomp" "ticket-gate.md" "decompose doc: child triage bar routes to the gate file" + echo "board schema single-source (issue-tracker owns the discriminant):" assert_contains "$tracker" "Park discriminant — who unparks it?" "tracker: canonical discriminant lives here" assert_contains "$tracker" "recommended answer" "tracker: needs-human note contract (question list with recommendations)"