singularity-forge/docs/dev/drafts/sf-ace-patterns.md

190 lines
12 KiB
Markdown
Raw Permalink Normal View History

# SF → ACE Coder Pattern Reference
**Status:** Draft — no active consumer. Parked here for when ACE Coder maintainers pull on convergence; SF's own priorities take precedence until then.
**Purpose:** Document six proven SF patterns that ACE Coder can adopt to stay conceptually compatible without collapsing the Forge/ACE split. Each pattern includes its SF implementation, the value it protects, and a concrete adoption path for ACE.
**Consumer:** ACE Coder maintainers and SF architects evaluating convergence points.
**Cross-References:**
- `docs/adr/ADR-019-workspace-vm-convergence.md` — Phase 6 mandate for one-way pattern transfer from SF to ACE.
- `docs/adr/ADR-020-internal-wire-architecture.md` — gRPC wire format as the integration seam for converged execution.
---
## 1. Preferences
### What It Is
A layered, mergeable preference system that combines global user settings, project-level overrides, profile defaults, and mode defaults into a single effective configuration. SF preferences are stored as pure YAML (`preferences.yaml`) — no frontmatter markers — and support worktree-aware resolution so that linked worktrees read from the main worktree's project prefs.
### SF Implementation
- **Source:** `src/resources/extensions/sf/preferences.js`
- **Key behaviors:**
- Global (`~/.sf/preferences.yaml`) + project (`.sf/preferences.yaml`) merge with project winning.
- Token profiles (`budget`, `speed`, `quality`) inject low-priority defaults for models and phase skips.
- Mode defaults (`solo` vs `team`) set context thresholds, timeouts, and parallel agent limits.
- Worktree-aware `projectPrefsRoot()` resolves the main worktree when running inside a git worktree.
- **Validation:** `preferences-validation.js` enforces schema and emits warnings for deprecated fields.
### ACE Adoption Path
1. Adopt a two-layer (global + workspace) YAML preference file with explicit merge semantics.
2. Introduce mode profiles (`solo`/`team`) that set safe defaults for context limits, timeouts, and parallelism.
3. Add validation at load time so bad prefs fail fast with a clear warning rather than silent misbehavior.
4. Keep preferences as pure data; avoid embedding logic (e.g., callback hooks) inside the preference file itself.
### Cross-References
- `docs/SPEC_FIRST_TDD.md` — Iron Law on preference-driven run control.
- `docs/adr/0000-purpose-to-software-compiler.md` — Run-control policy derived from confidence, risk, and reversibility.
---
## 2. PDD (Purpose-Driven Development)
### What It Is
Before any non-trivial implementation, SF requires eight structured fields — the PDD — that answer *why* the behavior exists, *who* uses it, and *what breaks* if it is wrong. PDD is the gate that blocks implementation until purpose is clear.
### SF Implementation
- **Spec:** `docs/SPEC_FIRST_TDD.md` and `docs/adr/0000-purpose-to-software-compiler.md`
- **Key fields:**
1. **Purpose** — why this behavior exists.
2. **Consumer** — who calls it in production.
3. **Contract** — observable behavior boundary.
4. **Failure Boundary** — failures that must be contained or surfaced.
5. **Evidence** — proof gathered so far.
6. **Non-Goals** — what is intentionally not solved here.
7. **Invariants** — rules that remain true across iterations.
8. **Assumptions** — uncertain facts and how to falsify them.
- **Enforcement:** Doctor checks reject malformed planning artifacts. Exported symbols must carry a JSDoc `Purpose:` line.
### ACE Adoption Path
1. Add a PDD gate to the planning phase (before coding starts) that requires at least Purpose, Consumer, Contract, and Evidence.
2. Require every exported function to open with a JSDoc block containing `Purpose:` and `Consumer:`.
3. Treat tests as the spec — write the failing test for the *correct* behavior before fixing a bug.
4. Use the PDD fields as the schema for autonomous checkpoints so the solver can verify intent was preserved.
### Cross-References
- `docs/adr/0000-purpose-to-software-compiler.md` — Canonical PDD/TDD gate definition.
- `docs/adr/0077-spec-runtime-evidence-schema-separation.md` — Separating spec (immutable intent) from runtime (mutable state) and evidence (audit trail).
---
## 3. UOK Gates
### What It Is
A structured, extensible post-unit verification pipeline. Every gate implements a uniform execution contract, emits durable audit events, and respects a per-failure-class retry matrix. Gates run in parallel where possible and are protected by circuit breakers.
### SF Implementation
- **Source:** `src/resources/extensions/sf/uok/gate-runner.js`
- **Key behaviors:**
- `UokGateRunner` registers gates by `id` and validates them with `contracts.js`.
- `GateResult` is a sealed union: `outcome` (`pass` | `fail` | `retry` | `manual-attention`), `failureClass`, `rationale`, optional `findings` and `recommendation`.
- Retry matrix: `timeout` gets 2 retries; `execution`/`artifact`/`verification`/`git` get 1; `policy`/`input`/`manual-attention` get 0.
- Circuit breaker per gate: `closed``open` after failure threshold, cooldown with exponential backoff, then `half-open`.
- Durable audit: every gate run is persisted to SQLite (`gate_runs`) and emitted as a trace event.
- **Implemented gates:** SecurityGate, CostGuardGate, OutcomeLearningGate, MultiPackageGate, ChaosMonkey (opt-in).
### ACE Adoption Path
1. Replace ad-hoc post-task checks with a gate registry that accepts new gates as single files + a registration line.
2. Standardize gate output to `outcome` + `failureClass` + `rationale` so downstream automation can act on it.
3. Add a retry matrix per failure class rather than hard-coding retries per check.
4. Introduce circuit breakers on expensive or flaky gates so one bad gate does not stall the entire pipeline.
### Cross-References
- `docs/adr/0075-uok-gate-architecture.md` — Full gate contract, retry matrix, and circuit-breaker design.
- `docs/adr/0076-uok-memory-integration.md` — Gate failures enriched with historical memory context.
---
## 4. Notifications
### What It Is
A durable, classified notification store that survives context resets and session restarts. Notifications carry `noticeKind` metadata (`system_notice`, `tool_notice`, `blocking_notice`, `user_visible`) so renderers can group and style them without parsing message text. Deduplication uses `dedupe_key` with a time window, and noisy status patterns are suppressed.
### SF Implementation
- **Source:** `src/resources/extensions/sf/notification-store.js` and `notification-overlay.js`
- **Key behaviors:**
- Persisted to `.sf/notifications.jsonl` with a schema version and a max-entry rotation cap (500).
- `NOTICE_KIND` classification separates automated workflow notices from user-visible messages.
- Deduplication: rows with the same `dedupe_key` within `DURABLE_DEDUP_WINDOW_MS` merge into one row with `repeatCount`.
- Actionable kinds (`action_required`, `approval_request`, `blocker`, `error`, `terminal`) never merge.
- Overlay: scrollable history panel with severity filtering, toggled via `Ctrl+Alt+N`.
### ACE Adoption Path
1. Add a `noticeKind` field to all notification events so the UI can distinguish system chatter from user-facing messages.
2. Persist notifications to disk (JSONL or SQLite) so they survive restarts and can be reviewed later.
3. Implement deduplication with `dedupe_key` rather than string matching to avoid spam.
4. Never render automated notices as if the user typed them.
### Cross-References
- `docs/product-specs/notification-source-hygiene.md` — Source classification and trust preservation.
- `docs/adr/0002-sf-schedule-pull-based.md` — Why SF avoids background notification daemons.
---
## 5. Skills-as-Contracts
### What It Is
SF skills are markdown files with YAML frontmatter that act as invocation contracts. The frontmatter declares permission profiles, side-effect awareness, and invocation policy so the loader can enforce safety without loading the full skill body into model context.
### SF Implementation
- **Source:** `src/resources/extensions/sf/skills/frontmatter.js`
- **Key behaviors:**
- Required fields: `name`, `description`.
- SF-specific optional fields: `user-invocable`, `model-invocable`, `side-effects`, `permission-profile` (`restricted` | `normal` | `trusted` | `unrestricted`), `triggers`, `max-activations`, `locked`.
- `buildSkillRecord` normalizes frontmatter into a typed record with safe defaults (e.g., `permissionProfile` defaults to `normal`).
- `validateSkillFrontmatter` rejects invalid permission profiles.
- **Skill loading:** `preferences-skills.js` resolves skill references against the effective preference policy (`always_use_skills`, `prefer_skills`, `avoid_skills`, `skill_rules`).
### ACE Adoption Path
1. Require every skill/tool to declare its `permission-profile` and `side-effects` in frontmatter.
2. Gate skill invocation on the profile: `restricted` skills require explicit user approval; `unrestricted` skills run automatically.
3. Use the skill record as the contract: if the frontmatter says `side-effects: none`, the skill must not write to disk or mutate state.
4. Add a skill preference layer so users can `always_use`, `prefer`, or `avoid` specific skills without editing prompts.
### Cross-References
- `docs/adr/0000-purpose-to-software-compiler.md` — Skills are contracts, not prompts.
- `docs/SPEC_FIRST_TDD.md` — JSDoc purpose convention (same contract discipline applied to code).
---
## 6. Idempotency
### What It Is
Every durable write path in SF is designed to be safe to call multiple times. DB operations use upserts, runtime record writes are versioned, and crash recovery assumes the last operation may have partially succeeded.
### SF Implementation
- **Source:** `src/resources/extensions/sf/uok/unit-runtime.js` (runtime records), `src/resources/extensions/sf/db-writer.js` (DB upserts)
- **Key behaviors:**
- `writeUnitRuntimeRecord` merges with previous state on disk rather than blindly overwriting; `recoveryAttempts`/`retryCount` are coalesced safely.
- `reconcileDurableCompleteUnitRuntimeRecords` and `reconcileStaleCompleteSliceRecords` clear runtime records only when durable artifacts (DB + SUMMARY.md) prove completion, preventing double-recovery.
- DB writer uses `upsertRequirement`, `upsertDecision`, `upsertTaskPlanning` — safe to retry.
- `clearRunawayRecoveredRuntimeRecords` is idempotent: clearing an already-cleared record is a no-op.
- **Tests:** `tests/crash-recovery.test.ts` validates "prevents double-recovery (idempotent)"; `tests/uok-scheduler.test.mjs` validates token cancellation idempotency.
### ACE Adoption Path
1. Make all state-transition writes upserts or conditional updates rather than blind INSERT/DELETE.
2. Version runtime records so partial writes can be detected and resumed safely.
3. In crash recovery, verify durable artifacts (files, DB rows) before assuming the previous attempt failed.
4. Add adversarial tests that call the same write path twice and assert the second call is a no-op or produces the same result.
### Cross-References
- `docs/adr/0077-spec-runtime-evidence-schema-separation.md` — Spec immutability + runtime mutability separation.
- `docs/adr/0079-autonomous-solver-executor-separation.md` — Solver pass is a separate, retry-safe observer.
---
### Summary Table
| Pattern | SF Source | ACE Adoption Priority | Key ADR |
|---|---|---|---|
| Preferences | `preferences.js` | High | ADR-0000 |
| PDD | `SPEC_FIRST_TDD.md` | Critical | ADR-0000, ADR-0077 |
| UOK Gates | `uok/gate-runner.js` | Medium | ADR-0075, ADR-0076 |
| Notifications | `notification-store.js` | Medium | Notification Hygiene Spec |
| Skills-as-Contracts | `skills/frontmatter.js` | High | ADR-0000 |
| Idempotency | `uok/unit-runtime.js`, `db-writer.js` | Critical | ADR-0077, ADR-0079 |
---
*This document is a convergence artifact. It does not mandate ACE adoption; it documents proven patterns so both systems can share vocabulary and reduce integration friction.*