Skip to content

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/.

LocationRole today
sdlc.yamlDeclares 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#SdlcConfigSchemaThe .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_KEYSThe 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.tssdlc 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.tssdlc quality baseline capture. Also carries --key; runs the whole roster once to write <baseline-dir>/<sha>.json.
apps/sdlc/skills/task-work/SKILL.mdStep 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.mdCanonical per-key prose for sdlc.yaml, including the quality_checks: section and the baseline-gated-mode subsection.
.github/workflows/release.ymlRuns 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.tsCovers the run op’s text projection, the detect op, and the baseline capture/diff/prune roundtrip.
apps/sdlc/lib/config/tests/sdlc_yaml.test.tsCovers loadVerbList for schema-owned keys (quality_checks, worktree_init) and for non-schema-owned keys.

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.

  1. Measure the current roster. Run bun apps/sdlc/cli/sdlc.ts quality run --config sdlc.yaml --output json once (backgrounded, since it is the slow thing being measured) and record each result’s duration_ms. This produces the per-verb wall times the subset selection in step 6 is based on.
  2. Add the schema key. In apps/sdlc/lib/config/load.ts, add quality_checks_fast to SdlcConfigSchema next to quality_checks: the same array-of-trimmed-non-empty-strings shape, .default([]), with a .describe() stating that it is a subset of quality_checks: chosen to finish inside the 10-minute inline command timeout and that an empty or absent list means “no fast roster — fall back to quality_checks:”.
  3. Make it schema-owned. Add "quality_checks_fast" to SCHEMA_OWNED_KEYS in apps/sdlc/lib/config/sdlc_yaml.ts so loadVerbList validates it against the schema instead of the looser legacy inline check.
  4. Add --fast to the run op. In apps/sdlc/lib/services/quality/ops/run.ts, add fast: z.boolean().default(false) to the input schema and a matching fast entry to cli.flags. In the handler, before resolving the verb list: raise OpError("INVALID_INPUT", ...) when --fast is combined with an explicit --key (mirroring the existing --config vs. positional-verbs mutual-exclusion check); otherwise, when --fast is set, call loadVerbList(cfgPath, "quality_checks_fast") first and, if it returns an empty list, emit warning: no quality_checks_fast configured in <path>; falling back to quality_checks on ctx.io.stderr and re-read with the quality_checks key.
  5. Add --fast to the capture op. Apply the same input field, CLI flag, mutual-exclusion check, and fallback-with-warning resolution in apps/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 in apps/sdlc/lib/services/quality/fast-roster.ts and call it from both ops rather than duplicating the branch.
  6. Populate the repo’s fast roster. In the repo-root sdlc.yaml, add a quality_checks_fast: list holding the subset of the existing quality_checks: verbs whose summed duration_ms from step 1 is under 5 minutes — half the inline budget, leaving headroom for a cold worktree. Prefer the cheap structural checks (bunx tsc --noEmit, the apps/sdlc/scripts/check_command_seam.sh verb, 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.
  7. Wire /sdlc:task-work. In apps/sdlc/skills/task-work/SKILL.md, add --fast to 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 --fast and should be backgrounded because it exceeds the inline timeout.
  8. Document the key. In apps/sdlc/conventions/sdlc-yaml.md, add quality_checks_fast: to the “Shape at a glance” YAML block and add a ### quality_checks_fast: subsection under the quality_checks: section covering the subset intent, the fallback rule, the --fast flag on both verbs, and the 10-minute inline-timeout rationale.
  9. Test. Extend apps/sdlc/lib/services/quality/tests/quality_ops.test.ts with cases for --fast selecting the fast roster, the fallback-plus-warning when the fast key is absent or empty, and the --fast + --key rejection; extend apps/sdlc/lib/config/tests/sdlc_yaml.test.ts with a loadVerbList(..., "quality_checks_fast") case proving the key is schema-owned (a malformed entry surfaces the schema-violation error shape, not the legacy inline one).
LocationKindChange
apps/sdlc/lib/config/load.tsmodifyAdd quality_checks_fast to SdlcConfigSchema with its describe text.
apps/sdlc/lib/config/sdlc_yaml.tsmodifyAdd "quality_checks_fast" to SCHEMA_OWNED_KEYS.
apps/sdlc/lib/services/quality/fast-roster.tsnewShared --fast key-resolution helper (fast key, empty-list fallback to quality_checks, warning text).
apps/sdlc/lib/services/quality/ops/run.tsmodifyAdd 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.tsmodifySame fast input field, --fast flag, and helper-based key resolution so the baseline matches the gated roster.
sdlc.yamlmodifyDeclare quality_checks_fast: with the measured under-5-minute subset plus a comment naming the deferred verbs.
apps/sdlc/skills/task-work/SKILL.mdmodifyPass --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.mdmodifyDocument quality_checks_fast: in “Shape at a glance” and as its own subsection.
apps/sdlc/lib/services/quality/tests/quality_ops.test.tsmodifyCover --fast selection, the empty/absent fallback warning, and the --fast + --key rejection.
apps/sdlc/lib/config/tests/sdlc_yaml.test.tsmodifyCover loadVerbList over the new schema-owned key.
  • AC-1: bun apps/sdlc/cli/sdlc.ts config get-quality-checks --config sdlc.yaml still exits 0 against the repo-root sdlc.yaml after the new key is added, proving the strict schema accepts quality_checks_fast:.

  • AC-2: The verbs listed under quality_checks_fast: in the repo-root sdlc.yaml are a subset of the verbs listed under quality_checks: in the same file — checked by comparing the two lists emitted by bun apps/sdlc/cli/sdlc.ts quality run --config sdlc.yaml --fast --output json and the same command without --fast.

  • AC-3: bun apps/sdlc/cli/sdlc.ts quality run --config sdlc.yaml --fast --line completes in under 10 minutes of wall time when run inline from a freshly initialised task worktree, and reports OK n/n.

  • AC-4:

    bun apps/sdlc/cli/sdlc.ts quality run --config <path-to-a-config-without-the-fast-key> --fast --line

    emits a no quality_checks_fast configured warning on stderr and runs the quality_checks: verbs instead of failing.

  • AC-5: bun apps/sdlc/cli/sdlc.ts quality run --config sdlc.yaml --fast --key quality_checks exits non-zero with an INVALID_INPUT message naming the --fast / --key conflict.

  • AC-6: bun test apps/sdlc/lib/services/quality apps/sdlc/lib/config passes, including new cases for --fast selection, the fallback warning, and the --fast + --key rejection.

  • AC-7: grep -c -- '--fast' apps/sdlc/skills/task-work/SKILL.md reports 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.md contains a ### quality_checks_fast: heading and lists the key in its “Shape at a glance” block.

  • AC-9: .github/workflows/release.yml still invokes quality run without --fast, so the release path continues to run the full roster.

  • Making any individual quality verb faster (parallelising bun test, incrementalising tsc, scoping rumdl). 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-checks to populate quality_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.
  • none

Spawned by /sdlc:spawn-task-pr on 2026-07-20 UTC from T-1YSW-amend-d0001-solutions-tier in https://github.com/sksizer/dev.


← Back to Tasks