Research & Decisions
Evidence base: research/post-spec-squad-findings.md (3-lens profile-loaded squad, convergent). This file records the resulting decisions.
D1 — Reject the projection cache (brief's original target)
- Decision: Do NOT add a step-projection cache.
- Rationale:
project_action_sequencecumulative time ≈ 0ms across all three profiles; the seam is already@functools.cached (mission_step_repository.py:456,mission_type_repository.py:62). A cache there moves cold-start ~0ms. - Alternatives considered: persistent on-disk projection cache — rejected; reaches at most the ~70ms
_inject_projected_fieldsload slice (<10%), not worth the NFR-002 staleness surface unless a cold-FS runner profile flips it (edge case, deferred).
D2 — Durable fix Lever A: trim the next-path import graph
- Decision: Defer eager pydantic doctrine/charter/events/status model imports on the read-only
kind:"query"path via function-level imports; anchornext_cmd.py:21-48; secondary lazy command registration__init__.py:161. - Rationale: 84% of cold-start is per-process import; ~0.42s builds 336 pydantic models the query path doesn't all use. This dominates the CI-fixture number that red-blocks PRs.
- Alternatives considered: rewriting the model foundation (out of scope, C-004); only lazy command registration (~43ms, insufficient alone).
- Risk: a deferred import needed on the query path must resolve identically when first used (behavior-preserving) — guarded by NFR-004 byte-identical test.
D3 — Durable fix Lever B: content-hash cache the charter freshness verdict
- Decision: Persist
CharterFreshnessto a content-keyed sidecar; onnext, compute the cheap key and skipcompute_freshness's ruamel parse on a hit. - Key composition (C-005):
compute_bundle_content_hash(repo_root)(existing, per-file mtime-agnostic BOM/CRLF-normalized sha256) folded with the sha256 of the synthesized-DRG graph file read by_compute_synthesized_drg(computer.py:803) — the latter is NOT inBUNDLE_CONTENT_HASH_FILES, so a bundle-only key would serve a stalesynthesized_drgsub-state. - Rationale: charter preflight (~0.5s ruamel of a 1588-line
charter.yaml) dominates real-projectnextlatency. Content hashing is the only safe invalidation family (mtime unreliable across git checkouts — DIR-010/011). - Fail-closed: any key miss, hash error, or unreadable sidecar → recompute (never serve a possibly-stale verdict).
- Cache location: a repo-local cache dir (e.g. under
.kittify/-adjacent runtime cache, gitignored) keyed by the composite hash — finalized in data-model.md; must be per-repo (freshness isrepo_root-global, no per-mission fan-out). - Alternatives considered: mtime key (rejected — DIR-010/011 footgun); doctrine-version-stamp key (rejected — misses local uncommitted edits); caching in-process only (rejected — fresh process per
next). - Risk (elevated): serving a stale "fresh" governance verdict is worse than a stale projection → mandatory pre-merge adversarial review pass (WP-B + pre-merge squad).
D4 — Perf benchmark shape
- Decision: subprocess-based
@pytest.mark.performancebenchmark —benchmark.pedantic(lambda: subprocess.run([sys.executable,"-m","specify_cli","next",…]), rounds=N, warmup_rounds=1, iterations=1). - Rationale: cold-start = fresh process; an in-process
benchmark.pedanticmeasures a warm interpreter (structurally different, much faster). Pinned rounds avoid pytest-benchmark auto-calibration ballooning CI time on a ~1s/round subprocess. Exemplar:tests/review/test_verdict_save_performance.py. - Placement:
tests/specify_cli/next/→ matchesperformance.yml:99next-domain leg (paths: tests/next tests/runtime tests/specify_cli/next), no workflow edit. - Baseline: seed post-fix via
--benchmark-save(workflow_dispatchupdate_baseline,performance.yml:67);tests/performance/baselines/Linux-CPython-3.11-64bit/has only0001_seed.jsontoday.
D5 — CI-gate retirement
- Decision: Remove the discrete "NFR-003 latency regression gate" step (
ci-quality.yml:4076); KEEP the structural smoke step (:4031, C-001). Deletescripts/check_nfr_003_latency.py; drop the absoluteci_target_median_secondsceiling fromnfr-003-baseline.json(leave a historical note pointing toperformance.yml). - Rationale: the step is a single-shot wall-clock ceiling on the blocking path — the anti-pattern ADR
2026-08-22-1eliminates; independently valuable (US2). Localnextalready at baseline (0.752s); the CI red is runner variance. - Alternatives considered: keep-and-recalibrate (rejected — the ratchet this mission ends; #3783 already did the last recalibration).
Supply-chain
No dependency add/upgrade/remove (pytest-benchmark already pinned). Section N/A; no adversarial supply-chain pass required.
Contested findings disposition (adversarial-evidence-contract)
All three squad lenses converged; no contested finding was dropped. The one divergence (whether an on-disk step-load cache is worth it on a cold FS) is recorded as deferred_with_rationale (D1 alternative) — resolved by the runner profile that WP-C's benchmark produces, not blocking this plan.