T-1HA7-fast-pre-pr-quality-subset
Status: closed/superseded · Impact: medium · Complexity: small
AUTO-DEFINED: this spec was best-effort machine-authored by /sdlc:task-auto-define on 2026-07-19 because the task is autonomy: autonomous/pr. Review the Goal, Approach, Today, Files-to-touch, and Acceptance-criteria carefully before trusting it.
The full quality gate does not fit inside the 10-minute inline command timeout, so every gate run and every baseline capture has to be backgrounded and polled by hand — friction rediscovered independently by two separate task executions. Close that gap either by defining a fast pre-PR subset that fits the inline budget, or by documenting the background-and-poll recipe so no operator has to work it out again.
Brief from the originating task:
The full quality gate takes longer than the 10-minute inline command timeout, so every run — and both baseline captures — has to be backgrounded and polled by hand. Recorded independently in the post-mortems of T-1YSW-amend-d0001-solutions-tier and T-J5DI-remove-clear-duplicate-augmented-packages. Resolve it one of two ways: define a fast pre-PR subset of quality verbs that reliably completes inside the inline timeout and have /sdlc:task-work Step 7 run that subset, deferring the full gate to CI; or document capture and gate as background-only steps with an explicit poll recipe so no operator has to rediscover it. Touchpoints: apps/sdlc/skills/task-work/SKILL.md (Step 7) and the quality service under apps/sdlc/lib/services/quality/.
| Location | Role today |
|---|---|
sdlc.yaml | Declares a single quality_checks: roster (12 verbs, including two full bun test sweeps, tsc --noEmit, rumdl check, and two docs generate runs). That one roster is what both the baseline capture and the gate execute. |
apps/sdlc/lib/config/load.ts#SdlcConfigSchema | The .strict() sdlc.yaml schema. quality_checks: is the only gate-verb roster key it knows, so any second roster key is rejected as an unrecognized key today. |
apps/sdlc/lib/config/sdlc_yaml.ts#SCHEMA_OWNED_KEYS | The set of keys loadVerbList schema-validates before returning verbs; keys outside it fall through to a looser inline type check. |
apps/sdlc/lib/services/quality/ops/run.ts | sdlc quality run. Already carries a --key flag defaulting to quality_checks, plus --diff-against-baseline / --baseline-dir, and records a per-verb duration_ms in each result. |
apps/sdlc/lib/services/quality/ops/baseline/capture.ts | sdlc quality baseline capture. Also carries --key; runs the whole roster once to write <baseline-dir>/<sha>.json. |
apps/sdlc/skills/task-work/SKILL.md | Step 3a captures the baseline and Step 7 gates the PR, both over the full roster and both invoked inline. Step 9 sub-step 6 re-runs the same gate after a rebase. None of the three fits the 10-minute inline command timeout, so an operator has to background and poll each one by hand. |
apps/sdlc/conventions/sdlc-yaml.md | Canonical per-key prose for sdlc.yaml, including the quality_checks: section and the baseline-gated-mode subsection. |
.github/workflows/release.yml | Runs the full roster (quality run --config sdlc.yaml --log) on the release path, where no inline-command budget applies. |
apps/sdlc/lib/services/quality/tests/quality_ops.test.ts | Covers the run op’s text projection, the detect op, and the baseline capture/diff/prune roundtrip. |
apps/sdlc/lib/config/tests/sdlc_yaml.test.ts | Covers loadVerbList for schema-owned keys (quality_checks, worktree_init) and for non-schema-owned keys. |
Proposed
Section titled “Proposed”Split the roster in two rather than making the single roster faster.
sdlc.yaml gains an optional quality_checks_fast: key — a subset of
quality_checks: chosen to complete well inside the 10-minute inline
command timeout. sdlc quality run and sdlc quality baseline capture
each gain a --fast flag that selects that key, falling back to
quality_checks: with a visible warning when the project has not
declared a fast roster. /sdlc:task-work passes --fast at its three
inline call sites (Step 3a capture, Step 7 gate, Step 9 post-rebase
re-gate), so the loop an operator runs on every task fits inline. The
full roster keeps running on the release path and stays available on
demand as the same commands minus --fast.
This closes the friction the two post-mortems recorded — no hand-rolled background-and-poll for the routine path — while keeping the full roster a first-class, unchanged surface for the cases that want it.
Approach
Section titled “Approach”- Measure the current roster. Run
bun apps/sdlc/cli/sdlc.ts quality run --config sdlc.yaml --output jsononce (backgrounded, since it is the slow thing being measured) and record each result’sduration_ms. This produces the per-verb wall times the subset selection in step 6 is based on. - Add the schema key. In
apps/sdlc/lib/config/load.ts, addquality_checks_fasttoSdlcConfigSchemanext toquality_checks: the same array-of-trimmed-non-empty-strings shape,.default([]), with a.describe()stating that it is a subset ofquality_checks:chosen to finish inside the 10-minute inline command timeout and that an empty or absent list means “no fast roster — fall back toquality_checks:”. - Make it schema-owned. Add
"quality_checks_fast"toSCHEMA_OWNED_KEYSinapps/sdlc/lib/config/sdlc_yaml.tssoloadVerbListvalidates it against the schema instead of the looser legacy inline check. - Add
--fastto the run op. Inapps/sdlc/lib/services/quality/ops/run.ts, addfast: z.boolean().default(false)to the input schema and a matchingfastentry tocli.flags. In the handler, before resolving the verb list: raiseOpError("INVALID_INPUT", ...)when--fastis combined with an explicit--key(mirroring the existing--configvs. positional-verbs mutual-exclusion check); otherwise, when--fastis set, callloadVerbList(cfgPath, "quality_checks_fast")first and, if it returns an empty list, emitwarning: no quality_checks_fast configured in <path>; falling back to quality_checksonctx.io.stderrand re-read with thequality_checkskey. - Add
--fastto the capture op. Apply the same input field, CLI flag, mutual-exclusion check, and fallback-with-warning resolution inapps/sdlc/lib/services/quality/ops/baseline/capture.ts, so the baseline covers exactly the roster the gate will diff against. Factor the shared key-resolution into one exported helper inapps/sdlc/lib/services/quality/fast-roster.tsand call it from both ops rather than duplicating the branch. - Populate the repo’s fast roster. In the repo-root
sdlc.yaml, add aquality_checks_fast:list holding the subset of the existingquality_checks:verbs whose summedduration_msfrom step 1 is under 5 minutes — half the inline budget, leaving headroom for a cold worktree. Prefer the cheap structural checks (bunx tsc --noEmit, theapps/sdlc/scripts/check_command_seam.shverb,gate corpus-invocation,gate markdown-fixtures,entities audit) and push the long sweeps (bun test apps/sdlc,docs generate site) to the full roster only. Add a YAML comment naming the omitted verbs and where they still run. - Wire
/sdlc:task-work. Inapps/sdlc/skills/task-work/SKILL.md, add--fastto the Step 3a capture invocation, the Step 7 gate invocation, and the Step 9 sub-step 6 post-rebase re-gate, and state in Step 7 that the fast roster is the PR gate, that the full roster runs on the release workflow, and that an on-demand full run is the same command without--fastand should be backgrounded because it exceeds the inline timeout. - Document the key. In
apps/sdlc/conventions/sdlc-yaml.md, addquality_checks_fast:to the “Shape at a glance” YAML block and add a### quality_checks_fast:subsection under thequality_checks:section covering the subset intent, the fallback rule, the--fastflag on both verbs, and the 10-minute inline-timeout rationale. - Test. Extend
apps/sdlc/lib/services/quality/tests/quality_ops.test.tswith cases for--fastselecting the fast roster, the fallback-plus-warning when the fast key is absent or empty, and the--fast+--keyrejection; extendapps/sdlc/lib/config/tests/sdlc_yaml.test.tswith aloadVerbList(..., "quality_checks_fast")case proving the key is schema-owned (a malformed entry surfaces the schema-violation error shape, not the legacy inline one).
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
apps/sdlc/lib/config/load.ts | modify | Add quality_checks_fast to SdlcConfigSchema with its describe text. |
apps/sdlc/lib/config/sdlc_yaml.ts | modify | Add "quality_checks_fast" to SCHEMA_OWNED_KEYS. |
apps/sdlc/lib/services/quality/fast-roster.ts | new | Shared --fast key-resolution helper (fast key, empty-list fallback to quality_checks, warning text). |
apps/sdlc/lib/services/quality/ops/run.ts | modify | Add the fast input field and --fast CLI flag; resolve the verb list through the shared helper; reject --fast with an explicit --key. |
apps/sdlc/lib/services/quality/ops/baseline/capture.ts | modify | Same fast input field, --fast flag, and helper-based key resolution so the baseline matches the gated roster. |
sdlc.yaml | modify | Declare quality_checks_fast: with the measured under-5-minute subset plus a comment naming the deferred verbs. |
apps/sdlc/skills/task-work/SKILL.md | modify | Pass --fast in Step 3a, Step 7, and Step 9 sub-step 6; note the release-path full run and the on-demand background recipe. |
apps/sdlc/conventions/sdlc-yaml.md | modify | Document quality_checks_fast: in “Shape at a glance” and as its own subsection. |
apps/sdlc/lib/services/quality/tests/quality_ops.test.ts | modify | Cover --fast selection, the empty/absent fallback warning, and the --fast + --key rejection. |
apps/sdlc/lib/config/tests/sdlc_yaml.test.ts | modify | Cover loadVerbList over the new schema-owned key. |
Acceptance criteria
Section titled “Acceptance criteria”-
AC-1:
bun apps/sdlc/cli/sdlc.ts config get-quality-checks --config sdlc.yamlstill exits 0 against the repo-rootsdlc.yamlafter the new key is added, proving the strict schema acceptsquality_checks_fast:. -
AC-2: The verbs listed under
quality_checks_fast:in the repo-rootsdlc.yamlare a subset of the verbs listed underquality_checks:in the same file — checked by comparing the two lists emitted bybun apps/sdlc/cli/sdlc.ts quality run --config sdlc.yaml --fast --output jsonand the same command without--fast. -
AC-3:
bun apps/sdlc/cli/sdlc.ts quality run --config sdlc.yaml --fast --linecompletes in under 10 minutes of wall time when run inline from a freshly initialised task worktree, and reportsOK n/n. -
AC-4:
bun apps/sdlc/cli/sdlc.ts quality run --config <path-to-a-config-without-the-fast-key> --fast --lineemits a
no quality_checks_fast configuredwarning on stderr and runs thequality_checks:verbs instead of failing. -
AC-5:
bun apps/sdlc/cli/sdlc.ts quality run --config sdlc.yaml --fast --key quality_checksexits non-zero with anINVALID_INPUTmessage naming the--fast/--keyconflict. -
AC-6:
bun test apps/sdlc/lib/services/quality apps/sdlc/lib/configpasses, including new cases for--fastselection, the fallback warning, and the--fast+--keyrejection. -
AC-7:
grep -c -- '--fast' apps/sdlc/skills/task-work/SKILL.mdreports at least 3, and the Step 3a capture, Step 7 gate, and Step 9 sub-step 6 re-gate invocations each carry the flag. -
AC-8:
apps/sdlc/conventions/sdlc-yaml.mdcontains a### quality_checks_fast:heading and lists the key in its “Shape at a glance” block. -
AC-9:
.github/workflows/release.ymlstill invokesquality runwithout--fast, so the release path continues to run the full roster.
Out of scope
Section titled “Out of scope”- Making any individual quality verb faster (parallelising
bun test, incrementalisingtsc, scopingrumdl). This task splits the roster; it does not optimise the verbs in it. - Running verbs concurrently inside
sdlc quality run— the op’s sequential, order-honouring execution contract is unchanged. - Adding a per-PR GitHub Actions workflow that runs the full roster. The full roster’s CI home stays the release workflow.
- Teaching
/sdlc:find-quality-checksto populatequality_checks_fast:interactively; the fast roster is authored by hand for now. - Changing the baseline file format, the
pre-existing:/new-drift:diff semantics, or the 5-file baseline pruning policy.
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”Spawned by /sdlc:spawn-task-pr on 2026-07-20 UTC from T-1YSW-amend-d0001-solutions-tier in https://github.com/sksizer/dev.