spec + ADR annotations + dormant-code cleanup

- .sf/REQUIREMENTS.md: today's R-entries (R066..R090) covering parallel-rescue
  targets — bus deliver verify, drift detection gate, PDD typed contracts,
  lane split, Wiggums detector family, repo supervisor design.
- ADR-014/019/020: SF-first banners (operator direction: get SF working before
  ACE/wire-architecture changes land downstream).
- docs/records + drafts: 2026-05-07 strategy + cli-agent survey index refresh;
  SF/ACE pattern draft annotations.
- roadmap-mutations.js removed (dormant — never imported; reachable shape
  verified against handler-relative + dynamic import audit).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mikael Hugo 2026-05-17 13:45:00 +02:00
parent d2ff4e84ba
commit e93d17a3b4
10 changed files with 68 additions and 136 deletions

View file

@ -942,3 +942,47 @@ ADR-0000 declares SF a **purpose-to-software compiler**. R036R040 codify that
- Supporting slices: M039/S07, M048/S05
- Validation: unmapped
- Notes: Operator's codex tool stays available unchanged — R075 is the SF-internal capability that runs without operator. Model-router exclusion rule: skip worker model + same vendor family. Self-feedback shape: `kind='adversarial-finding'`, severity from review verdict, occurredIn anchors review target. R066 firewall consumes high-severity findings as a quarantine signal. Budget governance: per-cycle token cap (e.g. 5% of unit token budget) + per-week cap (e.g. 100 reviews) configurable via preferences. System-lane task (R057) so reviews run concurrent with unit execution rather than serial. Sibling to R053 (repeated self-feedback kind detector) — when same finding kind recurs N times, escalates to operator notification.
### R087 — Typed PDD Contracts via engine-types.js
- Class: capability
- Status: active
- Description: Per ADR-0000, the 8 PDD fields (Purpose, Consumer, Contract, FailureBoundary, Evidence, NonGoals, Invariants, Assumptions) and the 7-dimension RunControlPolicy (confidence, risk, reversibility, blastRadius, cost, legal, customerImpact) become TypeScript interfaces in engine-types.js (currently empty stub: export {};). Per ADR-0075, GateResult shape is also exported from engine-types.js. All engine/policy/validator/planner code imports these typed contracts as the canonical leaf-node — turns ADR-0000 prose into compile-time contracts. plan-milestone/plan-slice/plan-task tool handlers validate input against PDDFields; UokGateRunner consumes GateResult; routing policy reads RunControlPolicy.
- Why it matters: ADR-0000 specifies the 8 PDD fields + run-control policy but they exist only in prose. Codex doctrine review 2026-05-17: "PDD eight-field gate not enforced by plan-milestone or plan-slice." R036-R040 (M030) cover data-level anchoring; R087 adds the typed schema fix at the code level. Pairs with R086 (dormant code triage) since engine-types.js IS one of the dormant files M058 wires in.
- Source: codex-doctrine-review-2026-05-17 + supervisor-deep-audit-2026-05-17
- Primary owning slice: M058/S05
- Supporting slices: M030/S01
- Validation: unmapped
- Notes: engine-types.js header explicitly says "All engine/policy interfaces depend on these types." File is purpose-built for this content; stub never filled. M030 R036 purposeAnchor data-level enforcement gets stronger when backed by typed PDDFields. Compile-time at import boundary; runtime at tool input boundary.
### R088 — Pre-Removal Test-Import Safety Gate
- Class: capability
- Status: active
- Description: UokGate per ADR-0075 (type=policy, id=removal-safety) that fires BEFORE any file removal proposed by the autonomous loop or triggered by R086 dormant-code triage. Greps tests/* for static + dynamic-import references; scans file content for external-integration env-var signatures (TWILIO_, ELEVENLABS_, STRIPE_, DISCORD_, SENDGRID_, etc.). Returns outcome=manual-attention with cited test paths or env-var matches when found.
- Why it matters: Operator-supervisor 2026-05-17 removed web/app/api/voice/ that turned out to be Twilio IVR on-call infrastructure with src/tests/integration/web-voice-ivr-contract.test.ts as paired contract test. R086 dormant-code-triage catches after removal; R088 prevents BEFORE. Without R088, autonomous removal repeats the same operator mistake at scale.
- Source: operator-supervisor-mistake-recovered-2026-05-17
- Primary owning slice: M058/S12
- Supporting slices: M048/S05
- Validation: unmapped
- Notes: Sibling to R082 (drift detector UokGate). M048/S05 (smoke-fixture + last-green ledger) catches destructive autonomous changes in general; R088 specifically guards removal-safety.
### R089 — Migrate Voice IVR On-Call Infra to Central Cloud Ops
- Class: capability
- Status: active
- Description: Migrate web/app/api/voice/ + voice/prompt/ routes + src/tests/integration/web-voice-ivr-contract.test.ts (Twilio IVR + ElevenLabs voice synthesis + on-call paging) out of SF to central cloud-ops repo/service. SF is per-project autonomous compiler (ADR-0000); operator-paging infra is org-level. Migrate first, verify cloud-ops endpoint, then remove from SF (gated by R088 pre-removal safety gate).
- Why it matters: Current placement in SF is historical drift. Voice infrastructure does not belong in a per-project compiler codebase. Migration to the right home + removal from SF is the correct fix.
- Source: operator-direction-2026-05-17
- Primary owning slice: M058/S13
- Supporting slices: M058/S14
- Validation: unmapped
- Notes: Files: web/app/api/voice/route.ts, web/app/api/voice/prompt/route.ts, src/tests/integration/web-voice-ivr-contract.test.ts, related env-var docs (TWILIO_, ELEVENLABS_). Removal from SF gated by R089/S13 (migration done) AND R088 (pre-removal safety gate green).
### R090 — Planning-Execute Lane Split
- Class: capability
- Status: active
- Description: Two concurrent lanes in the autonomous dispatcher: PLANNING (discuss/plan/research/reassess for ANY milestone, writes only to .sf/+DB, file-conflict-free) and EXECUTE (execute-task + complete-*, exclusive worktree, N=1). Planning queue stays ahead so execute never waits on plan gating. Subset of R046 (full multi-unit lanes via M033); much smaller blast radius, ships much earlier.
- Why it matters: Today execute waits ~10-20 min for next plan-slice to finish before starting next execute. Splitting lanes ~2x throughput on planning-heavy slices. Safer than R046 full parallel; only planning runs concurrent with execute, never two executes.
- Source: operator-direction-2026-05-17
- Primary owning slice: M059/S01
- Supporting slices: M059/S02, M059/S03
- Validation: unmapped
- Notes: Planning writes bounded to .sf/{sf.db, runtime/, milestones/}; never source. Permission-profile enforces. R082 drift detector watches the boundary. M033/R046 eventually generalizes; R090 is the safe near-term cut.

View file

@ -276,4 +276,4 @@ If a task cannot be described this way, it is underspecified.
- GitHub Spec Kit — spec-first authoring patterns.
- Ousterhout, *A Philosophy of Software Design* — deep modules, contract pattern.
- Trail of Bits — anti-rationalisation rules.
- ACE — original Iron Law / Purpose Gate framing this doc adapts.
- Iron Law / Purpose Gate framing — concept adapted from external prior art (originally an ACE concept; preserved here as SF doctrine independent of ACE runtime).

View file

@ -1,7 +1,7 @@
# ADR-014: Singularity Knowledge + Agent Platform stack
**Date**: 2026-04-29
**Status:** proposed (deferred; Phase 4 cancelled per ADR-019). No active implementation work; future federation work uses Singularity Memory primitive per ADR-012.
**Status:** proposed (deferred; Phase 4 cancelled per ADR-019). No active implementation work; future federation work uses Singularity Memory primitive per ADR-012; SF-first amendment 2026-05-17 — see R084
**Revised**: 2026-05-02 — Phase 4 cancelled, see [ADR-019](./ADR-019-workspace-vm-convergence.md)
## Context
@ -104,6 +104,8 @@ Phase 4 was originally planned as a "central persistent-agent runtime" built on
**What replaced it:** Persistent agents now live as **Firecracker VM snapshots managed by ACE**'s orchestration layer. A "persistent agent" is a named VM snapshot: restore it, and the agent wakes with its full memory and context intact. singularity-memory's scope is now strictly the knowledge layer (Phases 03). See ADR-019 § "ADR-014 Phase 4 is reassigned" for the authoritative statement.
> **2026-05-17 amendment — SF-first.** ACE is not currently operational; SF runs standalone. The Phase 4 reassignment to ACE's Firecracker VMs is suspended. Persistent-agent capability (code-reviewer, memory-curator, security-auditor, build-watch) re-homes in SF per R084 (Persistent Agent Runtime in SF). When ACE becomes operational, the original reassignment can be revisited.
### Historical: Original Phase 4 Plan
> *The content below is the original Phase 4 design, preserved as a historical record. It is **not** the current plan.*

View file

@ -1,6 +1,8 @@
# ADR-019: Workspace VM Convergence Architecture
**Status:** Proposed
> **2026-05-17 reframe — SF-first.** ACE exists as a peer project but is not currently operational. SF must work standalone. The convergence described here is forward-looking; SF's near-term roadmap does NOT depend on ACE being available. Phase 4 persistent-agent reassignment to ACE (originally written here) is suspended — persistent-agent capability is re-homed in SF per R084 (Persistent Agent Runtime in SF). When ACE becomes operational, this ADR's convergence path can be reactivated.
**Status:** Proposed — convergence forward-looking; SF-first per 2026-05-17 banner
**Date:** 2026-05-01
**Revised:** 2026-05-02 — wire-format scope superseded by ADR-020
**Deciders:** Mikael Hugo

View file

@ -9,6 +9,8 @@
## Context
> **2026-05-17 note — SF-first.** ACE is not currently operational. The wire-format table rows that touch ACE (ACE host → ACE tools, ACE host → singularity-memory, SF → ACE worker, ACE worker VM → host, Claude Code → ACE) describe the forward-looking architecture and are not active. SF's current internal wire is @singularity-forge/rpc-client (stdio JSON-RPC) for SF↔SF child processes; first-party SF→singularity-memory remains gRPC when wired. ACE rows reactivate when ACE becomes operational.
The first-party services that make up the singularity stack — SF
(`singularity-forge`), ACE (`ace-coder`), `singularity-memory`, and the
future `singularity-*` services — need a wire format for talking to each
@ -89,15 +91,15 @@ other services — it does not expose a gRPC API of its own.
This is the canonical reference for which wire goes where.
| Caller | Callee | Wire | Why |
|--------|--------|------|-----|
| ACE host → ACE tools | in-process Python imports | function call | type-safe, zero overhead |
| ACE host → singularity-memory | typed Python client (gen from Go API) | HTTP/gRPC | typed, fast, refactorable |
| SF → singularity-memory | typed TS client (gen from Go API) | HTTP/gRPC | same, in TS |
| SF → ACE worker | existing JSON-RPC stdio (`rpc-client`) | stdio JSON-RPC | already in production, language-agnostic |
| ACE worker VM → host | direct gRPC over tailnet | gRPC | typed, low-latency |
| Claude Code / Cursor → singularity-memory | MCP façade | MCP | external tool, no shared types |
| Claude Code → ACE | MCP façade (temporary) | MCP | external coder helping build, until self-hosting |
| Caller | Callee | Wire | Why | Status |
|--------|--------|------|-----|--------|
| ACE host → ACE tools | in-process Python imports | function call | type-safe, zero overhead | Future (ACE not operational) |
| ACE host → singularity-memory | typed Python client (gen from Go API) | HTTP/gRPC | typed, fast, refactorable | Future (ACE not operational) |
| SF → singularity-memory | typed TS client (gen from Go API) | HTTP/gRPC | same, in TS | Active |
| SF → ACE worker | existing JSON-RPC stdio (`rpc-client`) | stdio JSON-RPC | already in production, language-agnostic | Future (ACE not operational) |
| ACE worker VM → host | direct gRPC over tailnet | gRPC | typed, low-latency | Future (ACE not operational) |
| Claude Code / Cursor → singularity-memory | MCP façade | MCP | external tool, no shared types | Active |
| Claude Code → ACE | MCP façade (temporary) | MCP | external coder helping build, until self-hosting | Future (ACE not operational) |
---

View file

@ -1,6 +1,6 @@
# 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.
**Status:** Draft — no active consumer. Parked here for when ACE Coder maintainers pull on convergence; SF's own priorities take precedence until then. SF-first reaffirmed 2026-05-17 (ACE not currently operational).
**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.

View file

@ -1,5 +1,7 @@
# SF + ACE Full-Stack Reference Survey — 2026-05-07
> **Historical record — annotated 2026-05-17.** This survey assumed ACE was operational at the time of writing. Per 2026-05-17 operator decision, SF runs standalone; ACE is not currently operational. Survey content is preserved as historical context; do not treat its ACE references as current architecture.
This record compares local coding-agent, orchestration, retrieval, model, and
platform-engineering references under `/home/mhugo/code/` plus selected indexed
public references against the intended SF+ACE full-stack flow. It is planning

View file

@ -1,5 +1,7 @@
# Strategy Alignment — 2026-05-07
> **Historical record — annotated 2026-05-17.** This survey assumed ACE was operational at the time of writing. Per 2026-05-17 operator decision, SF runs standalone; ACE is not currently operational. Survey content is preserved as historical context; do not treat its ACE references as current architecture.
Aligned the top-level SF docs and roadmap framing around the current architecture and end goal.
## Canonical direction

View file

@ -7,5 +7,5 @@ Repo-memory audits, decision ledgers, context-gardening notes, and records-keepe
| Date | Note | Summary |
|------|------|---------|
| 2026-05-01 | [repo-vcs and notifications](./2026-05-01-repo-vcs-and-notifications.md) | repo-vcs skill landed; notification specs drafted; JSDoc annotations added; placeholder docs filled |
| 2026-05-07 | [SF + ACE full-stack reference survey](./2026-05-07-cli-agent-code-survey.md) | repo-wise map of coding agents, spec systems, orchestration systems, retrieval tools, model references, and platform/golden-path systems; priority pulls are execution permissions, typed machine events, DB-first state, generated human exports, trust gating, orchestration, cumulative diffs, eval pipelines, and MCP-client-only lifecycle hardening |
| 2026-05-07 | [SF + ACE full-stack reference survey](./2026-05-07-cli-agent-code-survey.md) | repo-wise map of coding agents, spec systems, orchestration systems, retrieval tools, model references, and platform/golden-path systems; priority pulls are execution permissions, typed machine events, DB-first state, generated human exports, trust gating, orchestration, cumulative diffs, eval pipelines, and MCP-client-only lifecycle hardening (historical; ACE not operational, SF-first per 2026-05-17) |
| 2026-05-07 | [strategy alignment](./2026-05-07-strategy-alignment.md) | aligned top-level docs and roadmap framing around Forge as product, UOK as kernel, and external CLIs as sharpening inputs |

View file

@ -1,122 +0,0 @@
/**
* Roadmap Mutations shared utilities for modifying roadmap checkbox state.
*
* Extracts the duplicated "flip slice checkbox" pattern that existed in
* doctor.ts, mechanical-completion.ts, and auto-recovery.ts.
*/
import { readFileSync } from "node:fs";
import { atomicWriteSync } from "./atomic-write.js";
import { clearParseCache } from "./files.js";
import { resolveMilestoneFile } from "./paths.js";
/**
* Mark a slice as done ([x]) in the milestone roadmap.
* Idempotent no-op if already checked or if the slice isn't found.
*
* @returns true if the roadmap was modified, false if no change was needed
*/
export function markSliceDoneInRoadmap(basePath, mid, sid) {
const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
if (!roadmapFile) return false;
let content;
try {
content = readFileSync(roadmapFile, "utf-8");
} catch {
return false;
}
// Try checkbox format first: "- [ ] **S01: Title**"
let updated = content.replace(
new RegExp(`^(\\s*-\\s+)\\[ \\]\\s+\\*\\*${sid}:`, "m"),
`$1[x] **${sid}:`,
);
// If checkbox format didn't match, try prose format: "## S01: Title" -> "## S01: \u2713 Title"
if (updated === content) {
updated = content.replace(
new RegExp(
`^(#{1,4}\\s+(?:\\*{0,2})(?:Slice\\s+)?${sid}\\*{0,2}[:\\s.\\u2014\\u2013-]+\\s*)(.+)`,
"m",
),
(match, prefix, title) => {
// Already marked done — no-op
if (
/^[\u2713\u2705]/.test(title) ||
/[\u2705]\s*$/.test(title) ||
/\(Complete\)\s*$/i.test(title)
)
return match;
return `${prefix}\u2713 ${title}`;
},
);
}
if (updated === content) return false;
atomicWriteSync(roadmapFile, updated);
clearParseCache();
return true;
}
/**
* Mark a slice as not done ([ ]) in the milestone roadmap.
* Idempotent no-op if already unchecked or if the slice isn't found.
*
* @returns true if the roadmap was modified, false if no change was needed
*/
export function markSliceUndoneInRoadmap(basePath, mid, sid) {
const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
if (!roadmapFile) return false;
let content;
try {
content = readFileSync(roadmapFile, "utf-8");
} catch {
return false;
}
const updated = content.replace(
new RegExp(`^(\\s*-\\s+)\\[x\\]\\s+\\*\\*${sid}:`, "m"),
`$1[ ] **${sid}:`,
);
if (updated === content) return false;
atomicWriteSync(roadmapFile, updated);
clearParseCache();
return true;
}
/**
* Mark a task as done ([x]) in the slice plan.
* Idempotent no-op if already checked or if the task isn't found.
*
* @returns true if the plan was modified, false if no change was needed
*/
export function markTaskDoneInPlan(_basePath, planPath, tid) {
let content;
try {
content = readFileSync(planPath, "utf-8");
} catch {
return false;
}
const updated = content.replace(
new RegExp(`^(\\s*-\\s+)\\[ \\]\\s+\\*\\*${tid}:`, "m"),
`$1[x] **${tid}:`,
);
if (updated === content) return false;
atomicWriteSync(planPath, updated);
clearParseCache();
return true;
}
/**
* Mark a task as not done ([ ]) in the slice plan.
* Idempotent no-op if already unchecked or if the task isn't found.
*
* @returns true if the plan was modified, false if no change was needed
*/
export function markTaskUndoneInPlan(_basePath, planPath, tid) {
let content;
try {
content = readFileSync(planPath, "utf-8");
} catch {
return false;
}
const updated = content.replace(
new RegExp(`^(\\s*-\\s+)\\[x\\]\\s+\\*\\*${tid}:`, "mi"),
`$1[ ] **${tid}:`,
);
if (updated === content) return false;
atomicWriteSync(planPath, updated);
clearParseCache();
return true;
}