feat(ai-integration): API-coverage verify:pre gate (#1562)
Full API Coverage by Default — Opt Out, Never Opt In. A phase that integrates an external API/SDK/service can no longer seal without a decided coverage matrix. - src/api-coverage.cts: deterministic detector (compound verb+noun signal + <Service> API/SDK surface; stopword-guarded; strips fenced code) + matrix parse/validate/render with field-length caps. - check api-coverage.verify-pre: blocking seal-time gate; phase arg resolved as a token under .planning/phases/ only (traversal-neutralized); validates COVERAGE.md or blocks iff a strong integration signal is detected and no matrix exists; fail-closed when phases tree exists but phase unresolvable. - capabilities/ai-integration: workflow.api_coverage_gate config key (default true), plan:pre contribution, blocking verify:pre gate. Data-driven. - gsd-core/workflows/verify-work.md: generic verify:pre gate dispatch. - Tests: detector FP/FN + matrix validation + fast-check bijection; gate e2e. Code+security review findings fixed (stopword FP, scope containment, pipe/cap rejection, prompt-injection message hygiene). - Regenerated registry/matrix/loop-host-contract/goldens/baseline + docs. Closes #1562
This commit is contained in:
@@ -58,6 +58,44 @@ MVP_MODE=$(gsd_run query phase.mvp-mode "${phase_number}" ${GSD_WS} --pick activ
|
||||
```
|
||||
</step>
|
||||
|
||||
<step name="verify_pre_hooks">
|
||||
**Verify:pre gate dispatch.** Before verification begins, dispatch every active
|
||||
gate hook registered at the `verify:pre` loop extension point. Each gate is
|
||||
data-driven — resolved from the capability registry, not hardcoded here.
|
||||
|
||||
```bash
|
||||
VERIFY_PRE_HOOKS_JSON=$(gsd_run loop render-hooks verify:pre --raw)
|
||||
PHASE_DIR=$(printf '%s' "$INIT" | jq -r '.phase_dir // empty')
|
||||
```
|
||||
|
||||
Resolve active gate hooks from `VERIFY_PRE_HOOKS_JSON` where `kind == "gate"`.
|
||||
For each active gate hook, run its declared check (a `check.query` gate runs
|
||||
`gsd_run check ${hook.check.query} "${PHASE_DIR}" --raw`; a `predicate` gate
|
||||
runs `gsd_run check predicate --predicate '<hook.check.predicate as JSON>' --phase-dir "${PHASE_DIR}" --raw`):
|
||||
|
||||
```bash
|
||||
GATE_RESULT=$(gsd_run check "${hook_check_query}" "${PHASE_DIR}" --raw)
|
||||
GATE_BLOCK=$(printf '%s' "$GATE_RESULT" | jq -r '.block // false' 2>/dev/null || echo "false")
|
||||
```
|
||||
|
||||
**Two-step gate contract (same as execute:wave:post / execute:post):**
|
||||
|
||||
- **Step 1 — command failure:** if the `gsd_run check ...` invocation itself
|
||||
fails (non-zero exit, no JSON), route by the gate's `onError`. An `onError:
|
||||
halt` gate HALTs; an `onError: skip` gate logs a warning and continues.
|
||||
- **Step 2 — block evaluation:** parse `GATE_RESULT.block`. For a **blocking
|
||||
gate** (`hook.blocking == true`) with `block == true`: HALT — do not begin UAT,
|
||||
present the gate's `message`, and tell the user what artifact resolves it. For
|
||||
a **non-blocking gate** with a non-empty `message`: print
|
||||
`⚠ {hook.capId} advisory: {GATE_RESULT.message}` and continue. For any gate
|
||||
with `block == false`: continue silently.
|
||||
|
||||
Example — the `ai-integration` capability's `api-coverage.verify-pre` gate
|
||||
(when `workflow.api_coverage_gate` is on) blocks here if the phase integrates an
|
||||
external API without a decided COVERAGE.md matrix. Present its `message` and
|
||||
point the user at producing COVERAGE.md before re-running verification.
|
||||
</step>
|
||||
|
||||
<step name="check_active_session">
|
||||
**First: Check for active UAT sessions**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user