Commit graph

4513 commits

Author SHA1 Message Date
Mikael Hugo
16db710468 sf snapshot: uncommitted changes after 49m inactivity 2026-05-12 16:45:04 +02:00
Mikael Hugo
0426aafad2 fix(headless): drop /sf prefix so typed commands route through extension dispatch
headless.ts was sending `/sf {subcommand} {args}` to the RPC session, but
commands are registered without the sf namespace (e.g. 'todo', 'autonomous').
_tryExecuteExtensionCommand parsed commandName='sf', found no match, and the
LLM handled the request instead of the typed backend.

Fix: send `/{subcommand} {args}` directly — matches what registerSFCommands
registers and what the TUI already uses.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 15:55:46 +02:00
Mikael Hugo
2bb9cdbeef feat(scaffold): ADR-022 scaffold profiles (all phases)
Add profile-aware scaffold system so SF does not lay down irrelevant
templates in infra/ops/docs repos.

## What ships

Phase 1 — data model
- scaffold-versioning.js: add 'disabled' to VALID_STATES; readScaffoldManifest
  returns profile field; recordScaffoldApply preserves manifest.profile (fixes
  roundtrip bug where profile was stripped on every write).
- scaffold-constants.js: PROFILES (app/library/infra/docs/minimal as Set<string>)
  and PROFILE_NAMES exports.

Phase 2 — profile-aware drift detection
- scaffold-drift.js: disabled bucket in emptyCounts, resolveActiveProfileSet
  integration, profile param on detectScaffoldDrift/migrateLegacyScaffold.
- doc-checker.js: filter to active profile, skip disabled-state files.

Phase 3 — auto-detection on first run
- scaffold-profiles.js: detectRepoProfile() heuristics (nix→infra,
  terraform→infra, react→app, node-no-ui→library, docs-only→docs, else→app).
- agentic-docs-scaffold.js: reads profile from manifest, auto-detects on first
  run, persists to manifest, filters SCAFFOLD_FILES to active profile.

Phase 4 — migrate command
- commands-scaffold-migrate.js: sf scaffold migrate --profile <name>
  Re-enables pending files entering the new profile; stamps state=disabled
  (or prunes with --prune) files leaving it; warns on editing/completed files.
- commands/handlers/ops.js, commands/catalog.js: registered and tab-completed.

Phase 5 — custom profiles + PREFERENCES.md frontmatter
- scaffold-profiles.js: readPreferencesProfile(), loadCustomProfileSet()
  (~/.sf/profiles/<name>.yaml with extends/add/remove), resolveActiveProfileSet()
  implementing full ADR-022 §6 precedence.
- All callers updated to use resolveActiveProfileSet as the single source of truth.

Tests: 28 new tests in adr-022-scaffold-profiles.test.mjs — all passing.
Pre-existing node:test stubs (3 files) unaffected.

ADR: docs/dev/ADR-022-scaffold-profiles.md

Misc: triage TODO.md dump into BACKLOG.md (phases-helpers export error T1,
/todo triage typed-handler gap T1, structured triage tiers T2, sha-track
markdown files T2, cross-repo triage T3). Reset TODO.md to empty template.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-12 15:28:03 +02:00
Mikael Hugo
ad53b792fb docs(.agents): add AGENTS.md — directory map and override pattern
Documents every folder under .agents/, what it contains, and the
override-by-same-name pattern. Explains YOLO as a flag not a mode.

is globally ignored but the spec file under .agents/ must be tracked.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 23:48:36 +02:00
Mikael Hugo
4f04fb4c34 chore(.agents): keep lean — remove default mode files, no modes list
.agents/ is an override layer. Default modes (ask/build/autonomous)
and default skills come from SF's built-in config. Project files only
exist when overriding or adding something project-specific.

- Remove modes/ask.md, modes/build.md, modes/autonomous.md (defaults)
- Remove enabled.modes from manifest (nothing project-defined)
- Policies and skills stay: they are project-specific overrides

To override a mode or skill, add a file with the same name.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 23:47:29 +02:00
Mikael Hugo
82d629c3ee feat(.agents): add autonomous mode; clarify yolo is a flag not a mode
- Add modes/autonomous.md — third SF mode (ask/build/autonomous).
  Describes UOK dispatch loop, bash 120s timeout, fresh-context-per-unit,
  recovery/runaway-guard, and when to use vs Build.
- Add autonomous to enabled.modes in manifest.yaml.
- Update policies/yolo.yaml description: YOLO is a flag on Build or
  Autonomous, not a mode, not a Shift+Tab stop.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 23:45:24 +02:00
Mikael Hugo
8ea4b0745d fix(.agents): list all 5 skills in manifest.yaml enabled.skills
sf-wiki, forge-autonomous-runtime, forge-command-surface, nix-build,
and smoke-test are all present in .agents/skills/ and must be declared
in enabled.skills per the AGENTS-1 spec.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 20:12:37 +02:00
Mikael Hugo
a9ebfb4442 fix(skills): move sf-wiki project override to .agents/skills/ (standard location)
.agents/skills/ is the documented standard for project-level skill overrides
(docs/user-docs/skills.md). .sf/skills/ is also searched but .agents/skills/
is the ecosystem-standard path used across all compatible agents.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 20:10:21 +02:00
Mikael Hugo
f3d84cd116 .agents: adopt agentsfolder/spec v0.1 as canonical agent configuration
Some checks failed
CI / detect-changes (push) Has been cancelled
CI / docs-check (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / build (push) Has been cancelled
CI / integration-tests (push) Has been cancelled
CI / windows-portability (push) Has been cancelled
CI / rtk-portability (linux, blacksmith-4vcpu-ubuntu-2404) (push) Has been cancelled
CI / rtk-portability (macos, macos-15) (push) Has been cancelled
CI / rtk-portability (windows, blacksmith-4vcpu-windows-2025) (push) Has been cancelled
Replaces the fragmented (AGENTS.md + CLAUDE.md + .github/copilot-instructions.md
+ .sf/STYLE.md + .sf/PRINCIPLES.md + .sf/NON-GOALS.md) surface with a
single canonical .agents/ tree per https://github.com/agentsfolder/spec.

Structure:
  .agents/manifest.yaml         spec metadata + defaults + project info
  .agents/prompts/
    base.md                     project-agnostic base prompt
    project.md                  SF-specific: purpose-first, DB-first,
                                build pipeline, Ask/Build/YOLO model
    snippets/{style,principles,non-goals}.md
                                short pointers into .sf/{STYLE,PRINCIPLES,
                                NON-GOALS}.md for composition
  .agents/modes/{ask,build}.md  YAML front matter + human-readable body
  .agents/policies/{default-safe,yolo}.yaml
                                conservative default + YOLO override
  .agents/skills/.gitkeep       empty per spec — SF's own skills not yet
                                migrated to agentskills.io format
  .agents/scopes/.gitkeep       single-tree, no scopes yet
  .agents/profiles/.gitkeep     no overlays yet
  .agents/schemas/.gitkeep      generated by validators
  .agents/state/.gitignore      excludes state.yaml from VCS per spec

Status: spec is pre-1.0 (specVersion 0.1.0 pinned). No agent runtime
currently reads .agents/ — this is structural adoption ahead of
ecosystem support. Legacy files (AGENTS.md, CLAUDE.md, etc.) kept
during the transition; .agents/ is now the canonical surface and they
will eventually point here.

This is the reference template; centralcloud/infra, operations-memory,
oncall-mobile-android to follow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 20:04:35 +02:00
Mikael Hugo
edd0eb22ac feat(skills): add project-level sf-wiki skill override with UPPERCASE convention
.sf/skills/ is the project-local skill override directory. This override
inherits all sf-wiki defaults and adds one project-specific rule: wiki
pages use UPPERCASE filenames (INDEX.md, ARCHITECTURE.md, etc.) to match
the .sf/ operational file convention (DECISIONS.md, KNOWLEDGE.md, etc.).

The built-in src/resources/skills/sf-wiki/SKILL.md stays generic (lowercase).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 19:54:18 +02:00
Mikael Hugo
385cc8a18b revert(skills): restore lowercase defaults in sf-wiki SKILL.md
sf-wiki is a built-in read-only skill — its page name defaults must
stay generic (lowercase). The uppercase convention is this repo's
project-level choice, documented in system.md and the wiki itself.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 19:52:15 +02:00
Mikael Hugo
0d187e53d7 chore(wiki): rename wiki pages to UPPERCASE to match .sf/ convention
All .sf/ operational files use UPPERCASE (DECISIONS.md, KNOWLEDGE.md, etc.).
Wiki pages now follow the same convention: INDEX.md, ARCHITECTURE.md,
WORKFLOWS.md, SUBSYSTEMS.md, GLOSSARY.md.

Also updates sf-wiki SKILL.md and system.md prompt references.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 19:50:06 +02:00
Mikael Hugo
eacbbaac82 TODO: simplify md-tracking — drop snapshot blob, accept mid-edit corner
Some checks are pending
CI / detect-changes (push) Waiting to run
CI / docs-check (push) Blocked by required conditions
CI / lint (push) Blocked by required conditions
CI / build (push) Blocked by required conditions
CI / integration-tests (push) Blocked by required conditions
CI / windows-portability (push) Blocked by required conditions
CI / rtk-portability (linux, blacksmith-4vcpu-ubuntu-2404) (push) Blocked by required conditions
CI / rtk-portability (macos, macos-15) (push) Blocked by required conditions
CI / rtk-portability (windows, blacksmith-4vcpu-windows-2025) (push) Blocked by required conditions
Final settled design: sha + git ref only, no DB content snapshots at
all. The mid-edit case (file observed dirty) loses the ability to
reconstruct the intermediate working-tree state, but the change-
detection signal is preserved and the operator can commit first if
intermediate fidelity matters.

Trades a corner-case fidelity loss for a much simpler schema and
no DB-vs-disk content duplication. Git remains the only version
store; the DB row is a pure "where I left off" pointer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:49:25 +02:00
Mikael Hugo
76923afb91 TODO: md-tracking needs a version reference, not just a content sha
Some checks are pending
CI / detect-changes (push) Waiting to run
CI / docs-check (push) Blocked by required conditions
CI / lint (push) Blocked by required conditions
CI / build (push) Blocked by required conditions
CI / integration-tests (push) Blocked by required conditions
CI / windows-portability (push) Blocked by required conditions
CI / rtk-portability (linux, blacksmith-4vcpu-ubuntu-2404) (push) Blocked by required conditions
CI / rtk-portability (macos, macos-15) (push) Blocked by required conditions
CI / rtk-portability (windows, blacksmith-4vcpu-windows-2025) (push) Blocked by required conditions
Without storing snapshots we lose the ability to diff against
"what SF last saw". The fix is hybrid: store the git commit SHA1
that contained the observed content (cheap, no DB blob), and only
fall back to a gzipped snapshot when the file was observed with
uncommitted changes (no git ref exists for that exact content).

For ".sf/-generated, untracked, in .gitignore" the right answer is
to not track them in this table at all.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:46:38 +02:00
Mikael Hugo
296054b1d4 TODO: drop snapshot blob from md-tracking; use git for diff source
Some checks are pending
CI / detect-changes (push) Waiting to run
CI / docs-check (push) Blocked by required conditions
CI / lint (push) Blocked by required conditions
CI / build (push) Blocked by required conditions
CI / integration-tests (push) Blocked by required conditions
CI / windows-portability (push) Blocked by required conditions
CI / rtk-portability (linux, blacksmith-4vcpu-ubuntu-2404) (push) Blocked by required conditions
CI / rtk-portability (macos, macos-15) (push) Blocked by required conditions
CI / rtk-portability (windows, blacksmith-4vcpu-windows-2025) (push) Blocked by required conditions
Per follow-up: SF generates many of these .md files itself (.sf/wiki/*,
.sf/milestones/**/*.md, docs/plans/**), so storing gzipped snapshots in
the DB would duplicate disk + git for no benefit.

Simpler design: store only the sha + meta in sf.db; compute diffs
on demand against `git show HEAD:<path>`. Naturally handles both
"working-tree edit not yet committed" and "another agent committed
while SF wasn't running".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:46:06 +02:00
Mikael Hugo
faecdc828c TODO: generalise sha-tracking from milestones to all source-of-truth .md
Some checks are pending
CI / detect-changes (push) Waiting to run
CI / docs-check (push) Blocked by required conditions
CI / lint (push) Blocked by required conditions
CI / build (push) Blocked by required conditions
CI / integration-tests (push) Blocked by required conditions
CI / windows-portability (push) Blocked by required conditions
CI / rtk-portability (linux, blacksmith-4vcpu-ubuntu-2404) (push) Blocked by required conditions
CI / rtk-portability (macos, macos-15) (push) Blocked by required conditions
CI / rtk-portability (windows, blacksmith-4vcpu-windows-2025) (push) Blocked by required conditions
Per follow-up: not just .sf/milestones/**/*.md but the broader set of
markdown files that SF (or humans) treat as authoritative — AGENTS.md,
.github/copilot-instructions.md, .sf/wiki/**, docs/adr/**,
docs/plans/**, and root-level meta files.

Explicit out-of-scope list: TODO.md (reset every cycle by triage),
CHANGELOG.md / BUILD_PLAN.md (append-only by design), vendored or
generated content. Tracking those would just be noise.

Spec includes a tracked_md_files schema, the walk/diff/surface flow,
and an honest accounting of storage cost (~40 bytes per file + optional
gzipped snapshot).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:45:39 +02:00
Mikael Hugo
902be6d1de TODO: SF should sha-track milestone files and diff on change
Some checks are pending
CI / detect-changes (push) Waiting to run
CI / docs-check (push) Blocked by required conditions
CI / lint (push) Blocked by required conditions
CI / build (push) Blocked by required conditions
CI / integration-tests (push) Blocked by required conditions
CI / windows-portability (push) Blocked by required conditions
CI / rtk-portability (linux, blacksmith-4vcpu-ubuntu-2404) (push) Blocked by required conditions
CI / rtk-portability (macos, macos-15) (push) Blocked by required conditions
CI / rtk-portability (windows, blacksmith-4vcpu-windows-2025) (push) Blocked by required conditions
Captures a real bug class observed during today's session: nothing
notices when a milestone file (CONTEXT.md, ROADMAP.md, slice PLAN.md,
etc.) is edited out of band — by a human, another agent, or a git pull.
SF keeps using the cached state and drifts.

Wanted: per-file sha tracking in sf.db, diff surface on change, +
hooks for accept/reject/import/archive. Storage cost negligible.

Useful in concert with the cross-repo triage and slash-command routing
gaps already in this TODO.md — together they close most of the
"unattended SF actually works" surface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:45:05 +02:00
Mikael Hugo
41b7842fd8 TODO: cross-repo triage + slash-command routing + structured tiers (redo)
Some checks are pending
CI / detect-changes (push) Waiting to run
CI / docs-check (push) Blocked by required conditions
CI / lint (push) Blocked by required conditions
CI / build (push) Blocked by required conditions
CI / integration-tests (push) Blocked by required conditions
CI / windows-portability (push) Blocked by required conditions
CI / rtk-portability (linux, blacksmith-4vcpu-ubuntu-2404) (push) Blocked by required conditions
CI / rtk-portability (macos, macos-15) (push) Blocked by required conditions
CI / rtk-portability (windows, blacksmith-4vcpu-windows-2025) (push) Blocked by required conditions
Previous commit (1fb4b9882) captured only the reset and lost my intended
additions due to a Read/Write race. Re-applying the four feature
requests from today's dogfooding session:

- Cross-repo `triage-all-repos` (real fix for the "many TODO.md files"
  surface area — single tool, per-repo SF dbs, unified read-only
  aggregation view).

- Slash-command routing fix (`/todo triage` is currently re-implemented
  by the agent's LLM, bypassing the typed backend; patches to
  commands-todo.js were silently inert).

- Structured tier/priority per triage item (today tiers exist only in
  LLM-prose appended to BUILD_PLAN.md; no parser-friendly field for
  "promote Tier 1 items").

- Phases-helpers stale-export error that fires on every SF run; needs
  either the missing export restored or a test that catches it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:34:49 +02:00
Mikael Hugo
1fb4b98820 TODO: cross-repo triage + slash-command routing + structured tiers
Some checks are pending
CI / detect-changes (push) Waiting to run
CI / docs-check (push) Blocked by required conditions
CI / lint (push) Blocked by required conditions
CI / build (push) Blocked by required conditions
CI / integration-tests (push) Blocked by required conditions
CI / windows-portability (push) Blocked by required conditions
CI / rtk-portability (linux, blacksmith-4vcpu-ubuntu-2404) (push) Blocked by required conditions
CI / rtk-portability (macos, macos-15) (push) Blocked by required conditions
CI / rtk-portability (windows, blacksmith-4vcpu-windows-2025) (push) Blocked by required conditions
Four feature requests captured from today's dogfooding session:

- Cross-repo `triage-all-repos` (real fix for the "many TODO.md files"
  surface area — single tool, per-repo SF dbs, unified read-only
  aggregation view).

- Slash-command routing fix (`/todo triage` is currently re-implemented
  by the agent's LLM, bypassing the typed backend; patches to
  commands-todo.js were silently inert).

- Structured tier/priority per triage item (today tiers exist only in
  LLM-prose appended to BUILD_PLAN.md; no parser-friendly field for
  "promote Tier 1 items").

- Phases-helpers stale-export error that fires on every SF run; needs
  either the missing export restored or a test that catches it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:34:07 +02:00
Mikael Hugo
b818ae2c5a docs(wiki): add subsystems.md and glossary.md wiki pages
Complete the standard wiki page set from sf-wiki SKILL.md:
- subsystems.md: table of all subsystems with path, purpose, tests
- glossary.md: project-specific terms (ADR, UOK, PDD, YOLO, wiki, etc.)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 19:27:01 +02:00
Mikael Hugo
e679478d1b feat(wiki): wire .sf/wiki/ as tracked context source
- auto-bootstrap-context.js: scan .sf/wiki/*.md in collectAutoBootstrapFiles
  so wiki pages load as priority context in headless autonomous bootstrap
- headless-context.ts: same fix for the TS bootstrap path
- system-context.js: loadWikiBlock already existed and was wired into
  fullSystem; add .sf/wiki/ to Tier 1 escalation policy lookup sources
- system.md: add wiki/ to .sf/ directory structure; add Conventions entry
  explaining wiki is tracked in git (hand edits persist) and injected
  automatically when present
- git-runtime-patterns.js: do NOT gitignore .sf/wiki/ — wiki pages are
  tracked like DECISIONS.md so hand edits survive commits and clones
- .sf/wiki/: seed index.md, architecture.md, workflows.md for this repo

Wiki filenames follow sf-wiki SKILL.md convention: lowercase (index.md,
architecture.md, workflows.md, subsystems.md, glossary.md).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 19:24:23 +02:00
Mikael Hugo
3e652a9fd6 TODO: triage should escalate Tier 1 items to real milestones
Some checks are pending
CI / detect-changes (push) Waiting to run
CI / docs-check (push) Blocked by required conditions
CI / lint (push) Blocked by required conditions
CI / build (push) Blocked by required conditions
CI / integration-tests (push) Blocked by required conditions
CI / windows-portability (push) Blocked by required conditions
CI / rtk-portability (linux, blacksmith-4vcpu-ubuntu-2404) (push) Blocked by required conditions
CI / rtk-portability (macos, macos-15) (push) Blocked by required conditions
CI / rtk-portability (windows, blacksmith-4vcpu-windows-2025) (push) Blocked by required conditions
Today's triage run confirmed the manual `/todo triage` workflow works,
but it stops at tier-listing items in BUILD_PLAN.md — doesn't scaffold
.sf/milestones/MNNN/ dirs for the Tier 1 ones. That's the gap that
needs closing for the autonomous flow to actually create milestones
from raw TODO dumps.

Also captures the non-fatal phases-helpers.js extension load error
that appeared at the top of the triage run output.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:15:33 +02:00
Mikael Hugo
ca7368e5f1 fix(bash): add 120s default timeout to prevent autonomous mode hangs
- Add BUILT_IN_DEFAULT_TIMEOUT_SECS = 120 constant to bash tool
- Compute effectiveTimeout = timeout ?? resolvedDefaultTimeout so LLM
  calls without a timeout get the 120s guard automatically
- Add defaultTimeoutSeconds? to BashToolOptions for override at creation
- Dynamic bashSchemaWithDefault describes the actual default in the LLM
  tool description, improving model awareness
- Add BashSettings interface + getBashDefaultTimeoutSeconds() to
  SettingsManager so users can override or disable via settings.json
- Wire defaultTimeoutSeconds into agent-session.ts _buildRuntime()

Root cause: npx sf --help triggered npm package download, hanging for
4+ minutes without timeout, consuming entire autonomous run budget.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 19:12:33 +02:00
Mikael Hugo
7ef58422b1 TODO: feature requests for batch backlog ingestion + probe-based resolution
Some checks are pending
CI / detect-changes (push) Waiting to run
CI / docs-check (push) Blocked by required conditions
CI / lint (push) Blocked by required conditions
CI / build (push) Blocked by required conditions
CI / integration-tests (push) Blocked by required conditions
CI / windows-portability (push) Blocked by required conditions
CI / rtk-portability (linux, blacksmith-4vcpu-ubuntu-2404) (push) Blocked by required conditions
CI / rtk-portability (macos, macos-15) (push) Blocked by required conditions
CI / rtk-portability (windows, blacksmith-4vcpu-windows-2025) (push) Blocked by required conditions
Real dogfood for the auto-triage feature: this is the unstructured dump
that the autonomous cycle should pick up and process into proper backlog
items the next time it runs. Until auto-triage is wired up, the contents
serve as a written spec for what's needed.

Two flagship features:

- Auto-triage TODO.md on each autonomous cycle. `commands-todo.js`
  already implements `/todo triage` (manual). Wire it to the autonomous
  orchestrator and skip when TODO.md == _EMPTY_TODO.

- When the LLM would ask a clarifying question, replace with parallel
  combatant + partner probes (adversarial-challenge + collaborative-
  research) and only fall back to asking a human if probes diverge AND
  interactive mode is available. This unblocks unattended
  `headless new-milestone` (the gap that blocked batch backlog
  ingestion today).

Plus five smaller items (headless milestone stall fix, bulk
import-roadmap, TTY-free plan list, hand-authorable milestone scaffold,
discoverable --answers schema) carried over from the
centralcloud-ops SF-IMPROVEMENTS.md observations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:09:26 +02:00
Mikael Hugo
4e5fc12e81 feat(sf): fix gate health — import, DB fallback, and enrich status uok
Three follow-up fixes from S03/T04:

1. gate-runner.js: add missing getDistinctGateIds import from sf-db.js.
   UokGateRunner.getHealthSummary() called it when registry was empty but
   it was never imported — runtime ReferenceError in headless contexts.

2. sf-db-gates.js: getDistinctGateIds + getGateRunStats fall back to the
   quality_gates DB table when no trace events are found (e.g. after trace
   file rotation). Ensures gate health survives trace cleanup.

3. headless-uok-status.ts: replace generic Type column with real Scope
   (task/slice/milestone) from quality_gates DB, and show actual Last
   Evaluated timestamp from DB even when outside the 24h stats window.
   Tests updated to match (21 pass).

Closes backlog items: bl-gate-runner-import-bug, bl-gate-stats-trace-vs-db,
bl-uok-status-enrich.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 18:47:42 +02:00
Mikael Hugo
797db16ae8 feat(sf): S03/T04 — add UOK gate health to sf headless status uok
Adds a new `sf headless status uok` subcommand that queries
gate-run stats and circuit-breaker state from sf.db and formats
them as a markdown table or JSON (--json flag).

- src/headless-uok-status.ts: handler that loads sf-db-gates
  directly (avoids the unimported getDistinctGateIds in gate-runner)
- src/headless.ts: bypass RPC, route 'status uok' to handler
- src/help-text.ts: document the new subcommand
- tests/headless-uok-status.test.mjs: 19 node:test coverage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 18:31:03 +02:00
Mikael Hugo
4132ecc1db feat(sf): S03/T03 — wire OutcomeLearningGate into adaptive verification policy
Adds adaptive-verification-policy.js which reads OutcomeLearningGate
trace events from the last 24h and adjusts verification_max_retries /
verification_auto_fix in project preferences:
- >60% verification/artifact/execution failures → reduce retries to 1, disable auto-fix
- 0% failures across ≥5 samples → bump retries (capped at 3)
- all other cases → no change (returns null)

Wires into auto-verification.js after OutcomeLearningGate runs when
outcomeLearning flag is enabled. Includes 12 node:test tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 17:40:22 +02:00
Mikael Hugo
7b225696cc feat(sf): add cross-slice and milestone integrity checks to post-execution checks
- Add checkCrossSliceConsistency() to detect key_file conflicts across slices
- Add checkMilestoneIntegrity() to verify completed slices have summaries
  and no active requirements are orphaned
- Extend runPostExecutionChecks() signature with optional milestoneId
  and allSliceTasks parameters
- Wire cross-slice task gathering into auto-verification.js call site
- Add comprehensive node:test suite for both new checks
2026-05-11 17:22:11 +02:00
Mikael Hugo
338c75fc6f refactor: complete rf-01/rf-02/rf-11 blocked todos
rf-01: add ECONNREFUSED to isTransientNetworkError in anthropic-shared.ts,
  aligning with the NETWORK_RE pattern in error-classifier.js

rf-02: add scripts/validate-model-cost-table.mjs to report coverage gaps
  and price divergence between model-cost-table.js and models.generated.ts;
  add 'validate-cost-table' script to package.json

rf-11: extract 10 pure resource-display utility functions from
  interactive-mode.ts into packages/coding-agent/src/modes/interactive/
  resource-display.ts, reducing interactive-mode.ts by ~282 lines

All 4375 tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 16:45:39 +02:00
Mikael Hugo
0aaf8f2c0e refactor: split state.js into state-shared/db/legacy modules
state.js was a 2012-line monolith combining shared helpers, DB-backed
derivation, and legacy filesystem derivation. Split into four files:

- state-shared.js (114 lines): helpers used by both DB and legacy paths
  isGhostMilestone, isSliceComplete, isMilestoneComplete, isValidationTerminal,
  readMilestoneValidationVerdict, loadTerminalSummary, stripMilestonePrefix,
  canonicalMilestonePrefix, extractContextTitle

- state-db.js (841 lines): deriveStateFromDb() and its exclusive helpers
  reconcileDiskToDb, buildRegistryAndFindActive, handleNoActiveMilestone,
  handleAllSlicesDone, resolveSliceDependencies, reconcileSliceTasks,
  detectBlockers, checkReplanTrigger, checkInterruptedWork

- state-legacy.js (895 lines): _deriveStateImpl() — filesystem-only path

- state.js (228 lines): thin barrel — invalidateStateCache, getActiveMilestoneId,
  deriveState, re-exports from sub-modules

All 1195 tests pass. No behavior change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 16:25:20 +02:00
Mikael Hugo
1adc7f119c refactor(rf-06): split auto/phases.js into per-phase modules
3538-line monolith → 6 focused modules + thin barrel:
- phases-helpers.js (223 lines): shared helpers (generateMilestoneReport,
  closeoutAndStop, emitCancelledUnitEnd, maybeFireProductAudit,
  _resolveReportBasePath, recordLearningOutcomeForUnit)
- phases-dispatch.js (486 lines): runDispatch + assessUokDiagnosticsDispatchGate
- phases-guards.js (497 lines): runGuards + guard helpers
- phases-pre-dispatch.js (760 lines): runPreDispatch
- phases-unit.js (1477 lines): runUnitPhase + session timeout state
- phases-finalize.js (542 lines): runFinalize
- phases.js (13 lines): barrel re-export preserving original import surface

Removed dead runPhaseReview export (zero callers confirmed).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 15:14:49 +02:00
Mikael Hugo
aa6ecce384 refactor: fix all remaining inline error ternaries across 20 files
Used perl regex to replace all patterns of the form
  X instanceof Error ? X.message : String(X)
with getErrorMessage(X) for any variable name.

Added getErrorMessage imports to 6 files that lacked it.
Leaves only 2 intentional .stack || .message variants unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 14:50:01 +02:00
Mikael Hugo
dac14043cd refactor: consolidate remaining error ternaries (error variable)
Replace all remaining inline error ternaries using the 'error' variable name
with getErrorMessage(error). Added imports to 3 files that lacked it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 14:48:28 +02:00
Mikael Hugo
04322f110a refactor: replace all inline error message ternaries with getErrorMessage()
Eliminates ~120 repetitions of `err instanceof Error ? err.message : String(err)`
across the entire extension source tree. All callers now import and use
`getErrorMessage` from the canonical `./error-utils.js`.

Files updated (56 files):
- auto.js, auto-worktree.js, auto-recovery.js, auto-dashboard.js, auto-timers.js
- auto-prompts.js, auto-start.js, auto-post-unit.js, auto-model-selection.js
- auto/phases.js, auto/loop.js, auto/infra-errors.js
- autonomous-solver-eval.js, bootstrap/agent-end-recovery.js, bootstrap/db-tools.js
- bootstrap/exec-tools.js, bootstrap/journal-tools.js, bootstrap/register-extension.js
- bootstrap/register-hooks.js, canonical-milestone-plan.js, changelog.js
- clean-root-preflight.js, code-intelligence.js, commands-add-tests.js
- commands-debug.js, commands-eval-review.js, commands-handlers.js
- commands-maintenance.js, commands-pr-branch.js, commands-scan.js, commands-ship.js
- commands-todo.js, commands-worktree.js, definition-io.js, doctor.js
- doctor-config-checks.js, doctor-engine-checks.js, ecosystem/loader.js
- eval-review-schema.js, exec-sandbox.js, execution-instruction-guard.js
- graph-context.js, hook-emitter.js, index.js, learning/runtime.js
- lifecycle-hooks.js, onboarding-state.js, orphan-worktree-sweep.js
- planning-depth.js, quick.js, scaffold-keeper.js, sf-db/sf-db-core.js
- slice-cadence.js, sm-client.js, spec-projections.js, subagent/background-jobs.js
- subagent/isolation.js, sync-scheduler.js, tools/exec-tool.js
- tools/sift-search-tool.js, tools/workflow-tool-executors.js, ui/index.js
- uok/a2a-agent-server.js, uok/auto-dispatch.js, uok/auto-unit-closeout.js
- uok/auto-verification.js, uok/chaos-monkey.js, uok/gate-runner.js
- vault-resolver.js, workflow-install.js, workflow-plugins.js, worktree-manager.js
- worktree-resolver.js

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 14:46:30 +02:00
Mikael Hugo
8a7f6de782 refactor: centralize skills directory constants in skill-discovery.js
Export SKILLS_DIR, CLAUDE_SKILLS_DIR, PI_SKILLS_DIR from skill-discovery.js
instead of repeating join(homedir(), ...) inline across 5 files.

Consumers updated:
- preferences-skills.js: replace 2 inline join(homedir()...) with SKILLS_DIR/CLAUDE_SKILLS_DIR
- skill-health.js: replace 2 inline join(homedir()...) with constants; remove homedir import
- skill-catalog.js: replace 2 inline join(homedir()...) with constants; remove homedir import
- skill-telemetry.js: replace 4 inline join(homedir()...) with constants; remove homedir import

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 14:39:10 +02:00
Mikael Hugo
ec224f96ac refactor: replace all process.env.HOME/.sf patterns with sfHome()
- guided-flow.js: SF-WORKFLOW.md path now uses sfHome()
- commands-config.js: both auth.json path sites use sfHome()

Eliminates the last 3 inline ~/.sf path patterns; all .sf paths
now route through sfHome() which respects SF_HOME env override
and uses the platform-safe homedir() fallback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 14:34:08 +02:00
Mikael Hugo
d3d7342370 refactor: use sfHome() for SF-WORKFLOW.md paths and skills dir; deduplicate errorMessage
- commands-handlers.js: replace process.env.HOME/.sf/agent/SF-WORKFLOW.md with sfHome() at both call sites (lines 62 and 412)
- skills/directory.js: replace process.env.HOME/.sf/skills with sfHome()
- tools/tool-helpers.js: remove duplicate errorMessage implementation; re-export getErrorMessage from error-utils.js under the errorMessage alias

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 14:32:08 +02:00
Mikael Hugo
181a19ac65 refactor: wire worktree-session-state.js and auto-runtime-state.js
Instead of deleting these planned-extraction modules, implement them
properly:

worktree-session-state.js:
- Upgraded to canonical module with JSDoc, node:path imports
- Fixed getActiveWorktreeName() to use normalize/join/basename (was
  using fragile string.replaceAll + split('/') approach)
- Fixed ensureWorktreeOriginalCwdFromPath() to use sep instead of regex
- worktree-command.js now imports/re-exports all state functions from
  this module and removes its local 'let originalCwd = null'
- registerWorktreeCommand() recovery logic replaced with
  ensureWorktreeOriginalCwdFromPath() call

auto-runtime-state.js:
- Fixed to use getAutoSession() singleton instead of 'new AutoSession()'
  (was creating an isolated instance disconnected from auto.js state)
- auto.js now re-exports isAutoActive, isAutoPaused, markToolStart,
  markToolEnd from this module, removing duplicate implementations
- All state reads in auto-runtime-state.js delegate to the same
  singleton that auto.js manages

Test: updated worktree-fixes.test.mjs guard to match clearWorktreeOriginalCwd()

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 14:24:50 +02:00
Mikael Hugo
5be5d6d438 refactor: remove two dead files never wired to any consumer
- worktree-session-state.js: planned extraction for worktree originalCwd
  state; worktree-command.js kept its own module-level var and never
  imported this file. Dead since creation in 47c806d73.

- auto-runtime-state.js: planned extraction of isAutoActive/isAutoPaused
  and AutoSession wrapper; auto.js already exports all the same functions.
  No file in the codebase imported auto-runtime-state.js.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 14:16:09 +02:00
Mikael Hugo
e18a0001bb refactor(sf-ext): remove local sfHome() clone in preferences.js
preferences.js had its own copy of sfHome() (without resolve() canonicalization).
Replace with import from sf-home.js — single source of truth.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 14:12:11 +02:00
Mikael Hugo
90dc3c6798 refactor(sf-ext): split sf-db.js (9073 lines) into 18 domain modules
sf-db.js is now a pure barrel re-export. All logic lives in sf-db/:

- sf-db-core.js       — adapter, schema, transactions, shared helpers
- sf-db-mode-state.js — Ask/Build/YOLO mode state
- sf-db-decisions.js  — ADR / decision records
- sf-db-artifacts.js  — file artifacts and attachments
- sf-db-milestones.js — milestone CRUD
- sf-db-slices.js     — slice CRUD
- sf-db-tasks.js      — task CRUD
- sf-db-worktree.js   — worktree state
- sf-db-evidence.js   — retrieval evidence
- sf-db-spec.js       — spec/contract records
- sf-db-gates.js      — UOK gate records
- sf-db-uok.js        — unit-of-knowledge state
- sf-db-session-store — session store / FTS
- sf-db-backlog.js    — backlog items
- sf-db-learning.js   — model learning / performance
- sf-db-memory.js     — memory / embeddings
- sf-db-profile.js    — user profile
- sf-db-self-feedback — self-feedback triage

sf-db/index.js re-exports sf-db.js for backward compat.
All 4375 tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 13:51:44 +02:00
Mikael Hugo
756355abf1 refactor(sf-ext): replace inline sfHome patterns with canonical sfHome()
Fix bug in auto.js where SF_HOME env var caused double '.sf' path segment.
Convert 11 files from inline homedir()/.sf or SF_HOME constructs to sfHome().

Files updated:
- auto.js: bug fix (join(SF_HOME, '.sf', 'agent') → join(sfHome(), 'agent'))
- key-manager.js: process.env.SF_HOME || join(HOME, '.sf') → sfHome()
- ui/color-band.js: os.homedir()/.sf → sfHome(); remove os import
- ui/prompt-history.js: homedir()/.sf → sfHome(); remove homedir import
- ui/usage-bar.js: homedir()/.sf/agent/auth.json → sfHome()
- ui/marketplace.js: 2 occurrences — extensions dir → sfHome()
- skill-telemetry.js: 2 occurrences — legacy skills dir → sfHome()
- preferences-skills.js: legacy skills dir → sfHome()
- preferences-models.js: models.json path → sfHome()
- memory-embeddings.js: auth.json path → sfHome(); remove homedir import
- commands/handlers/core.js: dynamic import homedir → static sfHome()

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 10:45:35 +02:00
Mikael Hugo
0ece0e5413 refactor(sf-ext): consolidate sfHome, counters, tool helpers, settings path, post-mutation hook
- rf2-01: replace 23 inline `process.env.SF_HOME || join(homedir(), '.sf')` patterns
  across 19 files with canonical `sfHome()` from sf-home.js; removes 5 private
  sfHome/getSfHome function definitions and unused os/homedir imports
- rf2-05: extract `ensureWritableParent` and `errorMessage` from complete-task.js
  and complete-slice.js into new tools/tool-helpers.js
- rf2-06: add `runPostMutationHook` to tool-helpers.js; replace 8 identical
  try/catch blocks (plan-task, plan-slice, plan-milestone, replan-slice,
  reassess-roadmap, reopen-slice, reopen-task, reopen-milestone) with single call
- rf2-09: add `makeDiskCounter` factory in auto-dispatch.js; consolidate 4 counter
  functions (rewrite/uat get/set/increment) from duplicated if/else DB-vs-disk
  logic into thin factory wrappers (~35 lines removed)
- rf2-10: export `getSfAgentSettingsPath()` from preferences.js; update
  notifications/notify.js and permissions/permission-core.js to use it

All 4375 unit tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 10:17:58 +02:00
Mikael Hugo
9dc244eb68 refactor: rf-10/rf-03 ask-gate wiring and skills frontmatter consolidation
- rf-10: Wire gateAskUserQuestions (ask-gate.js) into ask-user-questions execute() via dynamic import; blocks autonomous ask_user_questions calls at tool layer
- rf-03: Replace FRONTMATTER_RE + manual body extraction in skills/frontmatter.js with shared splitFrontmatter(); keep custom parseYaml() for skill-specific YAML handling

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 09:09:24 +02:00
Mikael Hugo
9756edfe0b refactor: rf-09/rf-08/rf-12/rf-05 cleanup and deduplication
- rf-09: Remove isTransientNetworkError from preferences-models.js/preferences.js/preferences-models.d.ts (canonical is error-classifier.js)
- rf-08: Extract Gemini token counting to google-gemini-token-counter.js; update register-hooks.js import
- rf-12: Remove 3 dead _allRequirements/_allDecisions fetch blocks from db-writer.js
- rf-05: Extract resolveSfBin() and monitorNdjsonStdout() to spawn-worker.js; both orchestrators now import from there

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 08:59:51 +02:00
Mikael Hugo
96d751555f fix(lint): fix all pre-existing lint warnings (unused vars/imports/params)
- Prefix unused params/vars with _ in db-writer.js, system-context.js,
  record-promoter.js, a2a-transport.js
- Remove unused imports: createServer (a2a-agent-server.js),
  dirname/join/resolve (a2a-transport.js), KNOWN_PREFERENCE_KEYS (preferences.js)
- Remove unused private field _lastInputAt from pty-chat-parser.ts
- Prefix unused test variable currentProject in uok-metrics-exposition.test.mjs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 08:32:30 +02:00
Mikael Hugo
64ddbd950f refactor(extensions): consolidate duplicate code into canonical modules
- Delete ghost package packages/pi-agent-core (no dist, no consumers,
  TS build errors; JS source sf-db.js had 3 commits not mirrored in TS)
- Remove build:pi-agent-core from root package.json build:pi pipeline
- Merge all models from MODEL_COST_PER_1K_INPUT into BUNDLED_COST_TABLE
  (model-cost-table.js is now the single canonical cost source)
- Remove duplicate MODEL_COST_PER_1K_INPUT object and getModelCost()
  from model-router.js; use lookupModelCost() from model-cost-table.js
- Replace hand-rolled isTransientNetworkError in preferences-models.js
  with delegation to classifyError() in error-classifier.js

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 08:28:49 +02:00
Mikael Hugo
5ea96143ca chore(todo): remove Cloudflare Workers AI provider task
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 04:16:01 +02:00
Mikael Hugo
0b5fa75c0d fix(lint): fix all pre-existing lint failures
- check-sf-extension-inventory.mjs: expand parseDirectRegisteredCommands()
  scan to include 7 more files (guards/inturn.js, notifications/notify.js,
  permissions/index.js, ui/usage-bar.js, commands/legacy/audit.js,
  commands/legacy/create-extension.js, commands/legacy/create-slash-command.js)
  and filter results by BASE_RUNTIME_COMMAND_NAMES to exclude doc-string false
  positives ("name" in create-slash-command.js template text)

- extension-manifest.json: remove 'clear' (subcommand of logs/notifications,
  never a top-level pi.registerCommand)

- packages/pi-agent-core/src/db/sf-db.ts: fix 23 noVoidTypeReturn errors
  - openDatabase: void → boolean (caller uses return value at line 5625)
  - claimEscalationOverride: void → boolean (caller checks at escalation.js:243)
  - resolveSelfFeedbackEntry: void → boolean (caller checks at self-feedback.js:387)
  - copyWorktreeDb: void → boolean (caller checks at reconcileWorktreeDb)
  - compactUokMessages: void → {before,after} (caller returns value at message-bus.js:238)
  - insertSessionTurn: void → bigint|null (caller uses id at session-recorder.js:104)
  - expireStaleMemories: void → number (caller uses count at auto-start.js:1047)
  - deleteMemorySourceRow: void → boolean (caller returns value at memory-source-store.js:107)
  - deleteMemoryEmbedding: void → boolean (caller returns value at memory-embeddings.js:328)
  - updateBacklogItemStatus: remove dead return expression (callers discard value)
  - removeBacklogItem: remove dead return expression (callers discard value)
  - updateGateCircuitBreaker: remove dead return {total,avgMs,...} (wrong-type
    code accidentally merged from getGateLatencyStats, never reachable)
  - markUokMessageRead: remove dead return true/false (callers discard value)

- Auto-fix formatting and organizeImports in ~30 source files (biome --write)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 04:02:31 +02:00
Mikael Hugo
65da855c5e refactor(state): extract loadTerminalSummary helper, dedup 5 fail-closed SUMMARY checks
The 'read SUMMARY → check if readable AND terminal' pattern appeared five
times in state.js after the Cluster F polarity fix. Extract it to a
private loadTerminalSummary(summaryFile, loadFn) helper so the fail-closed
semantics live in one place and can't drift between call sites.

- loadTerminalSummary returns the content if readable AND terminal, null otherwise
- All 5 call sites replaced: 2 in getActiveMilestoneId(), 3 in _deriveStateImpl()
- Phase 2 'no roadmap' case reuses returned content for parseSummary().title
- isTerminalMilestoneSummaryContent now only referenced inside the helper

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 03:46:36 +02:00