diff --git a/.sf/REQUIREMENTS.md b/.sf/REQUIREMENTS.md index 9929fe2c3..a46086fb7 100644 --- a/.sf/REQUIREMENTS.md +++ b/.sf/REQUIREMENTS.md @@ -507,15 +507,24 @@ The next group enforces ADR-0000's contract: **purpose is the driver**, not work | R053 | failure-visibility | active | M038/S03 | none | unmapped | | R054 | failure-visibility | active | M038/S04 | none | unmapped | | R055 | differentiator | active | M038/S05 | none | unmapped | -| R056 | core-capability | active | M038 | M038/S01, M038/S02, M038/S03, M038/S04, M038/S05 | unmapped | +| R056 | core-capability | active | M038 | M038/S01-S05 | unmapped | +| R057 | differentiator | active | M039/S01 | M039/S02, M039/S03, M039/S04, M039/S05 | unmapped | +| R058 | core-capability | active | M040/S01 | M040/S02, M040/S03, M040/S04 | unmapped | +| R059 | core-capability | active | M041 | M041/S01-S06 | unmapped | +| R060 | quality-attribute | active | M042/S01 | M042/S02, M042/S03, M042/S04 | unmapped | +| R061 | core-capability | active | M043/S02 | M043/S01, M043/S03, M043/S04 | unmapped | +| R062 | quality-attribute | active | M044/S01 | M044/S02, M044/S03, M044/S04 | unmapped | +| R063 | differentiator | active | M045/S01 | M045/S02, M045/S03 | unmapped | +| R064 | quality-attribute | active | M046/S01 | M046/S02 | unmapped | +| R065 | quality-attribute | active | M047/S01 | M047/S02 | unmapped | ## Coverage Summary -- Active requirements: 56 -- Mapped to slices: **54** +- Active requirements: 65 +- Mapped to slices: **63** - Validated: 0 - Unmapped active requirements: **2** (R049 — multi-provider parallel routing; R050 — auto-benchmark uncovered models) -- Owning milestones: M003 (R001-R006), M005 (R007-R010), M010 (R013-R015, R020), M011 (R011-R012), M012 (R016), M013 (R017), M014 (R018), M015 (R019), M016-M030 (R021-R040), M031 (R041-R044), M032 (R045), M033 (R046), M034 (R047), M035 (R048), [pending] M036-M037 (R049-R050), **M038 (R051-R056 — Wiggums Detector family)** +- Owning milestones: M003 (R001-R006), M005 (R007-R010), M010 (R013-R015, R020), M011 (R011-R012), M012 (R016), M013 (R017), M014 (R018), M015 (R019), M016-M030 (R021-R040), M031 (R041-R044), M032 (R045), M033 (R046), M034 (R047), M035 (R048), [pending] M036-M037 (R049-R050), M038 (R051-R056 Wiggums Detector), M039 (R057 System Lane), M040 (R058 DB-via-Bus), M041 (R059 Typed Vocabulary), M042 (R060 Backup+Restore), M043 (R061 Schema Migration), M044 (R062 Secrets Lifecycle), M045 (R063 Rate Budget), M046 (R064 Cache Policy), M047 (R065 Deprecation) ## Purpose Anchor @@ -766,3 +775,70 @@ ADR-0000 declares SF a **purpose-to-software compiler**. R036–R040 codify that - Migration is mechanical: rename ADR files, add ID frontmatter to DECISIONS.md entries, backfill DB tables with id columns. - Doctor check: every typed reference resolves (cross-link integrity). - Lint: SF-authored docs/code must use typed IDs for cross-references (e.g. "per A0000" not "per ADR-0000" or "per the purpose ADR"). + + +### R060 — DB Backup + Restore +- Class: quality-attribute +- Status: active +- Description: Scheduled .sf/sf.db atomic snapshots with documented restore procedure + CI drill that proves snapshots are restorable. Default cadence: daily + on every milestone-complete. Snapshots exclude transient state (locks, in-flight runtime). `sf restore ` command swaps in a snapshot atomically. Point-in-time recovery via WAL replay against most recent snapshot. +- Why it matters: Today a WAL corruption or accidental DELETE wipes the entire roadmap. 2-4 week unattended runs accumulate state continuously — no backup = single point of failure. Restore drill in CI is essential because untested backups don't work. +- Source: spec +- Primary owning slice: M042/S01 +- Supporting slices: M042/S02, M042/S03, M042/S04 +- Validation: unmapped +- Notes: Backup inclusion list pinned (db + planning .md files); excludes locks/runtime. PITR via WAL replay needs the WAL to be preserved alongside snapshots. + +### R061 — Schema Migration Runner +- Class: core-capability +- Status: active +- Description: Versioned, idempotent, transactional, downgradable DB schema migrations via migrations/NNNN-name.{up,down}.sql files. Runner verifies signatures, applies in BEGIN/COMMIT, rolls back on any failure. Downgrade path supported for last 3 versions. Doctor flags unrun migrations; sf headless auto-applies on startup if behind. +- Why it matters: Schema evolves ad-hoc today (schema_version table exists but no formal runner). A half-applied migration during a 2-4 week unattended run = silent corruption. Transactional + downgradable runner makes schema changes safe. +- Source: spec +- Primary owning slice: M043/S02 +- Supporting slices: M043/S01, M043/S03, M043/S04 +- Validation: unmapped +- Notes: Signature (SHA-256) prevents tampered migrations from being silently applied. Downgrade window of 3 versions is conservative; longer windows accumulate complexity. + +### R062 — Secrets Lifecycle (rotation + audit + leak detection) +- Class: quality-attribute +- Status: active +- Description: Storage already solved (auth.json + vault + env, 3-layer resolver). R062 narrows to lifecycle: per-provider rotation cadence with reminder, per-LLM-call audit log {lane, unit, provider, key-fingerprint, ts}, runtime leak detection (scan SF-authored artifacts for secret patterns), and lane-scoped credentials (R057 system lane can use different / cheaper / read-only key set than unit lane). +- Why it matters: Auth storage exists but operations don't. Without rotation reminders, keys live forever and risk accumulates. Without per-call audit, attributing a breach to a specific lane/unit is impossible. Without leak detection, a misbehaving LLM could write a key into a SUMMARY.md and it'd silently leak. Lane-scoped credentials make system lane cheaper + less powerful, matching its read-mostly workload. +- Source: spec (operator clarified 2026-05-17 — auth storage exists in auth.json, R062 narrows to lifecycle) +- Primary owning slice: M044/S01 +- Supporting slices: M044/S02, M044/S03, M044/S04 +- Validation: unmapped +- Notes: Audit log at .sf/audit-llm.jsonl. Leak detection runs as post-unit hook + system-lane periodic scan. Per-lane credentials configured via preferences. + +### R063 — Cross-Provider Rate Budget +- Class: differentiator +- Status: active +- Description: Central token-bucket per provider as a MessageBus actor, shared across all lanes (R046 unit lanes + R057 system lane). Lanes request capacity before dispatching LLM calls. 429 responses feed the bucket → exponential backoff until provider window resets. `sf status --quota` shows live state. +- Why it matters: Bursty parallel lanes without central rate limiting will exhaust provider quotas simultaneously — wasting LLM spend on 429-retries and degrading throughput. Bucket actor enforces shared-state coordination across lanes. Combined with R049 (per-lane model routing), bucket exhaustion on one provider triggers routing to another. +- Source: spec +- Primary owning slice: M045/S01 +- Supporting slices: M045/S02, M045/S03 +- Validation: unmapped +- Notes: Bucket capacity reads from existing provider-quota.json. Sits naturally on the MessageBus alongside the db-writer actor (R058). + +### R064 — Prompt Cache Policy +- Class: quality-attribute +- Status: active +- Description: Cross-session prompt cache reuse for stable prefixes (system prompts, ARTIFACT_KEYS blocks, etc.). Cache hit rate per unit type recorded in unit_metrics. TTL + eviction documented + enforced. Cost-savings from cache hits reported in sf cost. +- Why it matters: Today prompt cache fires per-call without cross-session reuse — every sf headless cold-start re-pays the cache cost. Stable-prefix hashing across sessions could cut LLM spend on common prompt prefixes by 50%+. Cost optimization, not blocking. +- Source: spec +- Primary owning slice: M046/S01 +- Supporting slices: M046/S02 +- Validation: unmapped +- Notes: Existing prompt-ordering.js handles the cache_control:ephemeral hints; R064 adds the cross-session reuse strategy. + +### R065 — Deprecation Lifecycle +- Class: quality-attribute +- Status: active +- Description: R/M/S/A/D/G/K/P entries can have status='deprecated' + sunset_date + reason. Doctor flags entries past sunset that haven't been removed. Cross-link integrity check warns when an active entry depends on a deprecated one. +- Why it matters: Today cancelled/skipped is binary; no graceful sunset path. Without formal deprecation, replaced patterns (e.g. legacy v1 builders after v2 migration) linger forever. Sunset dates force cleanup discipline. +- Source: spec +- Primary owning slice: M047/S01 +- Supporting slices: M047/S02 +- Validation: unmapped +- Notes: Pairs with R059 typed vocabulary — sunset is a property of any typed entity.