docs(planning): commit accumulated workflow artifacts
This commit is contained in:
139
.planning/tmp/plan-pre-ai-integration.md
Normal file
139
.planning/tmp/plan-pre-ai-integration.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# API Coverage Decision Checkpoint
|
||||
|
||||
> Full API Coverage by Default — Opt Out, Never Opt In. Fires when a phase
|
||||
> integrates an external API / SDK / service. Most non-API phases will not fire
|
||||
> it — that is the point.
|
||||
|
||||
## Why this exists
|
||||
|
||||
"We integrated the API" too often silently means "we integrated whatever the
|
||||
first use case exercised." Every un-built capability is then an invisible hole,
|
||||
discovered later by a user who reasonably expected it to work. The phase sealed
|
||||
green because its tasks completed; nobody decided the gaps were acceptable,
|
||||
because nobody enumerated them. This checkpoint makes the surface **visible and
|
||||
decided** before the phase can seal.
|
||||
|
||||
## Detect whether this phase integrates an external API
|
||||
|
||||
The detector is a deterministic scan over the phase scope. It strips fenced
|
||||
code blocks first, so a trigger term inside a code snippet does not fire. It
|
||||
returns a typed result: `{ detected, signals[], terms }`. Run it on the phase
|
||||
scope (the concatenation of this phase's ROADMAP section + the PLAN body):
|
||||
|
||||
```bash
|
||||
SCOPE="$(cat "${PHASE_DIR}"/*-PLAN.md 2>/dev/null) $(gsd_run query roadmap.get-phase "${PHASE}" 2>/dev/null || true)"
|
||||
API_COVERAGE_JSON=$(printf '%s' "$SCOPE" | node gsd-core/bin/lib/api-coverage.cjs --json 2>/dev/null) || true
|
||||
[ -n "$API_COVERAGE_JSON" ] || API_COVERAGE_JSON='{"skipped":true,"reason":"probe_unavailable"}'
|
||||
```
|
||||
|
||||
The `|| true` neutralizes the assignment's status without discarding the
|
||||
detector's own payload: the detector exits **1** for a real "no integration"
|
||||
verdict, so treating any non-zero exit as failure would throw away a correct
|
||||
answer. Emptiness — not exit status — is what proves the probe never ran, and
|
||||
the second line is the only place the fragment manufactures a payload of its
|
||||
own — one that records the *absence* of a verdict rather than asserting one.
|
||||
|
||||
The detector's exit code and `--json` payload now distinguish a real negative
|
||||
from an unexamined input (ADR-3889 Phase 3, #3907): empty/whitespace-only
|
||||
`$SCOPE` or a stdin read failure emit `{"skipped":true,"reason":"no_input"|
|
||||
"stdin_error"}` — no `detected` key at all. **Check for `skipped` before
|
||||
reading `detected`**: a `skipped` payload is not a confirmed "no API
|
||||
integration" verdict, it means the detector never examined real input. Do not
|
||||
treat it as `detected:false`. Read `API_COVERAGE_JSON.detected` only when
|
||||
`skipped` is absent — act on it only, do **not** pattern-match the prose
|
||||
yourself.
|
||||
|
||||
**If `skipped` is `true`:** the detector could not establish a scope (empty
|
||||
`$SCOPE`) or failed to run (stdin read error). Skip the checkpoint for this
|
||||
run rather than asserting a verdict about input that was never examined; do
|
||||
not raise it with the user.
|
||||
|
||||
**If `detected` is `false`:** this phase does not integrate an external API. Skip
|
||||
the checkpoint entirely and continue planning. Do not raise it with the user.
|
||||
|
||||
**If `detected` is `true`:** an external-API integration is in scope. You MUST
|
||||
produce a **coverage matrix** before the plan is finalized.
|
||||
|
||||
**If `detected` is `true` but the phase genuinely integrates no external API**
|
||||
(the detector is deterministic, not infallible — confirm by re-reading the phase
|
||||
scope, not by preference): do NOT fabricate a matrix row for a capability that
|
||||
does not exist. Write a reasoned declaration to `${PHASE_DIR}/COVERAGE.md`
|
||||
instead:
|
||||
|
||||
```markdown
|
||||
No external API integration: <one-line reason — what the phase touches instead>.
|
||||
```
|
||||
|
||||
The reason is required, exactly like an `OPT-OUT` reason. The seal-time gate
|
||||
accepts this declaration in place of a matrix.
|
||||
|
||||
## Produce the coverage matrix
|
||||
|
||||
Enumerate the external API's full **capability surface** — the verb/endpoint/method
|
||||
list (e.g. for a music service: `search`, `play`, `pause`, `skip`, `set_volume`,
|
||||
`get_playlist`, `create_playlist`, `add_to_playlist`, …). For each capability
|
||||
record a decision, starting from **full coverage** as the default:
|
||||
|
||||
| capability | decision | reason |
|
||||
|---|---|---|
|
||||
| `<capability-id>` | `INTEGRATE` \| `OPT-OUT` | `<one-line reason if OPT-OUT>` |
|
||||
|
||||
Rules:
|
||||
|
||||
- **`INTEGRATE` is the default.** Every capability starts as INTEGRATE; the
|
||||
matrix is the *subtraction record*.
|
||||
- **Every `OPT-OUT` MUST carry a one-line reason** (`not needed`, `not needed
|
||||
yet`, `explicitly out of scope`, …). An opt-out without a reason is an
|
||||
un-decided hole — the exact failure mode this gate exists to close.
|
||||
- **A second integration against the same need** (e.g. a second platform for the
|
||||
same capability) starts from the **same full-coverage baseline** as the first.
|
||||
Do not carry over the first integration's opt-outs silently — re-decide each
|
||||
capability for the new surface, so a first-class/fallback asymmetry cannot
|
||||
accumulate.
|
||||
|
||||
Write the matrix to `${PHASE_DIR}/COVERAGE.md` (canonical markdown-table form):
|
||||
|
||||
```markdown
|
||||
# API Coverage — <service>
|
||||
|
||||
> Full coverage by default. Opt-outs are explicit, reasoned decisions.
|
||||
|
||||
| capability | decision | reason |
|
||||
|---|---|---|
|
||||
| search | INTEGRATE | |
|
||||
| playlists | INTEGRATE | |
|
||||
| skip | OPT-OUT | not needed yet — tracked for follow-up phase |
|
||||
```
|
||||
|
||||
A fenced ` ```coverage ` JSON block is also accepted for machine-generated
|
||||
matrices; the markdown table is preferred (human-editable, diff-friendly).
|
||||
|
||||
## The seal-time gate
|
||||
|
||||
This checkpoint is enforced. At `verify:pre` the `api-coverage.verify-pre` gate
|
||||
runs `check api-coverage.verify-pre <phase-dir>`:
|
||||
|
||||
- If `COVERAGE.md` exists, it is validated — every row needs a valid decision and
|
||||
every `OPT-OUT` a reason. A malformed/partial matrix **blocks the seal**. A
|
||||
reasoned `No external API integration: …` declaration (and no rows) passes.
|
||||
- If `COVERAGE.md` is absent, the detector runs again over the phase scope. If a
|
||||
strong external-API-integration signal is found, the seal is **blocked** until a
|
||||
matrix is produced. If no signal is found, the phase is treated as a non-API
|
||||
phase and the seal proceeds.
|
||||
|
||||
So: an API-integrating phase cannot seal without a decided matrix. Produce it at
|
||||
plan time; do not leave it for seal time.
|
||||
|
||||
## Tuning the vocabulary (optional)
|
||||
|
||||
The trigger vocabulary is a curated, additive-only set in
|
||||
`gsd-core/bin/lib/api-coverage.cjs` (`DEFAULT_API_COVERAGE_TERMS`). To widen it
|
||||
for a project, override at the call site:
|
||||
|
||||
```bash
|
||||
printf '%s' "$SCOPE" | node gsd-core/bin/lib/api-coverage.cjs --json \
|
||||
--verbs integrate,wrap,connect,embed --nouns api,sdk,rest,grpc,webhook,plugin
|
||||
```
|
||||
|
||||
The whole checkpoint is toggleable via `workflow.api_coverage_gate` in
|
||||
`.planning/config.json`.
|
||||
Reference in New Issue
Block a user