prompts/system.md:106 told agents the isolation mode lives in
PREFERENCES.md under `taskIsolation.mode`. The preferences validator
(preferences-validation.ts:84-88) explicitly REJECTS that key — along
with task_isolation and bare isolation — with the error
'use "git.isolation" instead'. The canonical field is git.isolation
(verified in PREFERENCES.md template line 22 and preferences.ts:897).
Anyone following the system-prompt instruction would write the wrong
config, the validator would discard it, and isolation would silently
fall back to default 'none'.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final sweep after the prompt + script + README sweep for stale repo
references. These are pure code comments, not active behavior, but they
mislead readers about what repo this code lives in:
- src/resource-loader.ts: "sf-2 repo's working tree" → "sf-run repo's"
- src/web/safe-import-meta-resolve.ts: example URL hostname
- src/resources/extensions/sf/schemas/parsers.ts: dropped "sf-2 /" prefix
- src/resources/extensions/sf/schemas/validate.ts: same
- scripts/parallel-monitor.mjs: comment about "sf-2 repo itself"
Tests intentionally not touched — the test fixtures use @sf-build as a
generic scope name to exercise the symlink-merge logic, and the test
tmpdir prefixes (sf-2821-, sf-2945-) are just numeric tags from issue
numbers, not repo refs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same pattern fixed in scan.md last fire. The {{skillActivation}}
placeholder was the very last line of add-tests.md, after the
'Report sf-internal observations' section, so the default activation
sentence the prompt-loader injects landed where the agent only reads
it AFTER finishing test generation. Move to Instructions step 0 so
skills are activated before code reading begins.
Confirmed via sweep: no more prompts have a dangling {{skillActivation}}
at end-of-file.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
prompts/parallel-research-slices.md step 3 told the dispatcher to verify
research at `.sf/{{mid}}/`, but slice research files actually live at
`.sf/milestones/{{mid}}/slices/<sliceId>/<sliceId>-RESEARCH.md`. Step 3
verification could only ever fail.
prompts/validate-milestone.md sent the three milestone-validation reviewer
agents to wrong paths:
- parentTrace pointed at `.sf/{{milestoneId}}/S0X-SUMMARY.md` (slice
summaries actually live at `.sf/milestones/{{milestoneId}}/slices/S0X/`)
- Reviewer A read `.sf/{{milestoneId}}/REQUIREMENTS.md` (the file is at
project-level `.sf/REQUIREMENTS.md`)
- Reviewer A scanned `.sf/{{milestoneId}}/` for slice SUMMARYs (wrong dir)
- Reviewer C read `.sf/{{milestoneId}}/CONTEXT.md` (actual file is
`.sf/milestones/{{milestoneId}}/{{milestoneId}}-CONTEXT.md`)
Reviewers would either return false MISSING / FAIL verdicts or have to
re-discover the layout.
docs/dev/ADR-{008,009}-IMPLEMENTATION-PLAN.md "Related ADR" links pointed
to absolute paths inside a contributor's old Mac (`/Users/jeremymcspadden/
Github/sf-2/...`). Replaced with sibling-file relative paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After fixing forensics.md and error-classifier.ts last fire, swept the
rest of the tree for the same class of stale reference:
- scripts/validate-pack.js: criticalPackages list used \`@sf\` and
\`@sf-build\` scopes — neither exists in node_modules; this is in CI
(.github/workflows/ci.yml) + prepublishOnly, so the validation step
was failing to find anything. Now \`@singularity-forge/pi-coding-agent\`
and \`@singularity-forge/rpc-client\` (the actual scope).
- src/resources/skills/github-workflows/references/gh/SKILL.md: same
GraphQL bug as forensics.md — owner:"sf-build" name:"sf-2" — and
three \`gh project\` commands using owner sf-build. The gh issue
create command above already used singularity-forge/sf-run, so the
follow-up calls always failed. Also retitled "sf-2 Backlog" to
"sf-run Backlog".
- src/resources/extensions/sf/bootstrap/system-context.ts: deprecation
warning linked to https://github.com/sf-build/SF/issues/1492.
- packages/mcp-server/README.md, packages/rpc-client/README.md: 9 refs
to \`@sf-build/...\` for installable package names — would mislead
anyone copy-pasting into npm install.
- docs/user-docs/troubleshooting.md (+ zh-CN): GitHub Issues link
pointed at github.com/sf-build/SF/issues.
- docs/user-docs/getting-started.md (+ zh-CN): clone URL was correct
but the next \`cd\` was \`cd sf-2/docker\` — won't exist after a
fresh clone of sf-run.
- docs/dev/ci-cd-pipeline.md: GHCR org was \`sf-build\`.
Code comments containing "sf-2" / "sf-build" in non-active places
(parsers.ts banner, error message URLs in tests, dev-doc absolute
paths from a contributor's Mac) left alone — they're informational
and not addressed by users or runtime.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
forensics.md: GraphQL queries used owner:"sf-build" name:"sf-2" while
the gh issue create command above them correctly used
--repo singularity-forge/sf-run. This meant /sf forensics could create
the issue but the follow-up calls to set issue type would silently fail
against a non-existent repo. Both GraphQL queries now match the canonical
singularity-forge/sf-run.
error-classifier.ts: doc-comment @see link pointed to the old
sf-build/sf repo URL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The {{skillActivation}} placeholder was at the very bottom of scan.md,
after the 'Report sf-internal observations' section, with no header or
context. Since the default prompt-loader provides a one-sentence
'use the SF Skill Preferences block...' instruction, it landed as an
orphan footer the agent only encountered AFTER finishing the scan.
Move it to step 0 of the numbered Instructions so the agent activates
skills before exploring the codebase, matching the research-slice and
plan-milestone pattern.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`/sf debug` was ported in 360208cba but never wired up:
- handleDebug exported but no caller anywhere in the tree
- not in commands/catalog.ts
- loadPrompt("debug-session-manager") and loadPrompt("debug-diagnose")
referenced prompts that never existed in prompts/ — guaranteed
runtime crash if the dispatch path were ever hit
- debug-session-store.ts only consumed by commands-debug.ts
- no tests reference any of it
887 LOC of dead code with a latent crash. Removing both files
eliminates the orphan-prompt callsite that gap-audit kept flagging
and the broken dispatch path. Resolves sf-moohvyzc-ll5bd0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror the tiered Deep/Targeted/Light breakdown that research-slice.md
already had — same structure, milestone-scoped wording. Add explicit
'## Steps' header so the numbered steps no longer flow visually out of
the calibration paragraph.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Orphan-prompt detection only checked loadPrompt() callsites. Three
prompts (heal-skill, product-audit, review-migration) are loaded by
direct readFileSync of "<name>.md" — they got false-flagged as orphans.
Add a literal-filename check so any source file containing "<name>.md"
counts as a load. Cheap one-pass grep, same shape as the existing
loadPrompt patterns.
Verified with live runGapAudit: 0 new findings (was previously logging
the 3 false positives every session_start).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Auto-mode prompts called legacy aliases (sf_complete_task, sf_complete_slice)
while guided used canonical (sf_task_complete, sf_slice_complete). The
divergence was locked in by the test 'auto execute-task requires legacy
completion alias until prompt contract is aligned' — explicit tech debt
marker.
Migrated:
- workflow-mcp.ts getRequiredWorkflowToolsForAutoUnit: returns canonical
- prompts/execute-task.md: 4 callsites
- prompts/complete-slice.md: 3 callsites
- prompts/reactive-execute.md: any (none on this file)
- workflow-mcp.test.ts: assertion + transport-error fixtures
- Test rename: 'requires legacy completion alias' → 'requires canonical'
The aliases stay registered (sf_complete_task → sf_task_complete) so
external callers and old session resumes don't break. Tool-naming.test.ts
still asserts both names route to the same handler.
Resolves: sf-moohqbza-yyq8sd.
Tests: workflow-mcp + tool-naming 29/29 pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
29-line template with zero callers. inlineTemplate("reassessment")
isn't called anywhere; reassess-roadmap.md prompt has its own inline
structure. Removing prevents drift between dead template and live
prompt.
Resolves: orphan-template-reassessment.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
plan-slice was force-deep on every dispatch — full multi-task
decomposition + long architectural narration regardless of slice
complexity. research-slice has a 3-tier Calibrate Depth section
(Deep / Targeted / Light) that lets the agent right-size; plan-slice
now mirrors it.
Light tier explicitly authorizes 1-task plans for well-understood
work (CRUD, config changes, established-pattern wiring) — preventing
the synthesized 4-task decompositions that were a likely contributor
to recurring runaway-guard pauses on planning units.
Resolves: sf-moohebyg-y0hnhq.
Tests: plan-slice-prompt 16/16 still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
acquireSessionLock now accepts an optional sessionInfo arg (sessionId,
sessionFile) and writes both into the initial lockData JSON. The
caller in auto-start.ts:382 reads them from ctx.sessionManager.
updateSessionLock already writes these fields per-dispatch; this
closes the gap at acquire time.
Lets observers correlate the live auto.lock with the .sf/sessions/
event log (e.g. flow-auditor agents, dashboard, doctor).
Resolves: sf-moocx6lv-9grpvt (active-auto-session-pointer-missing).
Tests: 32/32 in session-lock + auto-start.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The auto-drain shipped hook-emitter.ts:80,93 logWarning calls with
component "hook-emitter" but that string wasn't in the LogComponent
union, blocking tsc compilation. Add 'hook' to the union (consistent
with the existing short component names like 'tool', 'dispatch',
'timer') and update the two callsites.
Without this, tsc fails and dist/resource-loader.js (which contains
the new verifyManifestFilesExist fix) can't update — leaving the
ask-user-questions.js boot failure unresolved despite the source-side
fix landing in aa7d3f10a.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- gap-audit prompt detection: Add DYNAMICALLY_LOADED_PROMPTS set for prompts
loaded through wrappers (research-slice, plan-slice, execute-task, etc.)
and detect loadPrompt calls with comma-separated args (#sf-moobj36l-ewu7js)
- gap-audit command detection: Detect exact match, prefix match, and
switch/case patterns for command dispatch (#sf-moobj36o-n8b7g9)
- empty task summary: Add isValidTaskSummary() to require non-empty content
with frontmatter or H1 before reconciliation marks task complete
(#sf-moobj36o-6rxy6e)
- journal write failures: Emit bounded health warning to .write-failures.jsonl
on journal write failure with per-session dedup (#sf-moobj36p-ikq3b2)
- resource sync manifest divergence: Add verifyManifestFilesExist() to check
all manifest-listed files exist on disk after hash match (#sf-moody5qi-8gbwp2)
- self-feedback markdown stale: Regenerate SELF-FEEDBACK.md from jsonl on
markResolved with resolved entries section (#sf-moobj36p-rlo95i)
- self-feedback context bloat: Cap entries to 20 max, 4000 chars, inject
compact summaries only with pointer to jsonl for full evidence
(#sf-moobj36p-ko6snt)
- hook-emitter types: Replace unknown with EventResult discriminated union,
implement emitExtensionEvent call with fallback warning when _pi missing
(#sf-moobmhwt-bxejb6, #sf-moobmhx4-gk9g83)
- export visualizer types: Add VisualizerExportData interface with proper
PhaseAggregate/SliceAggregate/ModelAggregate/ProjectTotals types
replacing any (#sf-moobmhx0-ow5fhy)
- native-edit-bridge: Already resolved (artifact removed from repo)
(#sf-moobj36q-z4id3u)
Switches the per-project sift warmup runtime dir field from cacheHome
(generic XDG_CACHE_HOME) to searchCache (specific SIFT_SEARCH_CACHE).
Narrower env var only redirects sift's search index, leaving sift's
other XDG_CACHE_HOME consumers (model downloads etc.) on the global
~/.cache/sift path so models are shared across projects.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
/sf rate was advertised in commands/catalog.ts and reachable from auto-mode
but had no branch in the manual ops handler — typing /sf rate outside
auto-mode silently no-op'd because ops.ts had no trimmed.startsWith("rate ")
branch. Add the dispatch alongside the existing /sf todo branch using the
same lazy-import pattern. handleRate from commands-rate.ts already exists.
Resolves: sf-monzctqn-m42nlq (command-dispatch-gap).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The forge-local human-readable file was misnamed — it's sf-internal self-
reports, not a generic project backlog. The jsonl source-of-truth is
already self-feedback.jsonl; the markdown should match.
Renames:
- File: BACKLOG.md → SELF-FEEDBACK.md
- Constant: BACKLOG_HEADER → SELF_FEEDBACK_HEADER
- Constant: BACKLOG_MAX_CHARS → SELF_FEEDBACK_MAX_CHARS
- Function: appendBacklogRow → appendSelfFeedbackRow
- Function: loadBacklogBlock → loadSelfFeedbackBlock (parallel session)
- Prompt file: prompts/triage-backlog.md → prompts/triage-self-feedback.md (parallel session)
- Module: triage-backlog.ts → triage-self-feedback.ts (parallel session)
- Header: "# SF Self-Feedback Backlog" → "# SF Self-Feedback"
Doc/text refs across prompts (execute-task, complete-milestone,
triage-self-feedback) and helper modules (gap-audit, requirement-promoter,
db-tools, system-context) updated to .sf/SELF-FEEDBACK.md.
Migration: new exported migrateLegacyBacklogFilename() in self-feedback.ts
runs at session_start (wired in register-hooks.ts) — renames the legacy
BACKLOG.md → SELF-FEEDBACK.md once, idempotent + non-fatal. system-context's
loadSelfFeedbackBlock also reads either name during the transition.
system-context.ts: BACKLOG_MAX_CHARS retained but raised earlier from 2000
to 8000 with all-entries-fit-or-truncate-tail (separate commit). The SoT
mtime-cache and per-severity rendering remain as before.
Tests: 77/77 pass across UOK + upstream-bridge + triage-self-feedback.
Not done in this commit (next iteration):
- Direct-drain dispatch at session_start for high/critical (subprocess spawn).
- Queue promotion for medium severity.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When SF starts and the still-blocked self-feedback drain finds entries
at severity high/critical, emit a separate warning notification listing
the candidate IDs + kinds. Visible in the SF UI on session start;
operator (or a follow-up auto-dispatcher) can drain them without
leaving the session.
Read-only signal for now — no auto-dispatch yet. The hook lives next
to the existing still-blocked summary in register-hooks.ts session_start.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
deferred-commit.test.ts: stagedPendingCommit-to-commitStaged proximity
threshold bumped 500 → 1500 chars. Recent refactors added ~95 chars of
pre-commit code between the false-assignment and the call. Invariant
preserved (false assigned BEFORE commit); the proximity check is
informational, not load-bearing.
skipped-validation-completion.test.ts: regex assertion updated to match
the source's [\s-] character class (no \\-). The test was checking for
[\\s\\-] but the actual regex at auto-dispatch.ts:1369 uses [\s-]
(legal — hyphen at end of char class). Same semantic, correct shape.
UOK + skip-by-preference behavior unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three production gaps Codex's adversarial review flagged are now closed:
1. Real legacy-vs-UOK parity diff (per turn, per plane):
- parity-diff-capture.ts captures plan / graph / model-policy /
audit-envelope / gitops decisions for both paths and emits
ParityDiffEvent records to .sf/runtime/uok-parity.jsonl.
- parity-report.ts aggregates divergencesByPlane, populates
criticalMismatches with real divergence summaries, and tracks
enterEvents / exitEvents / missingExitEvents for symmetry.
2. Exit-event symmetry:
- sessionId / turnId now flow through enter+exit parity events.
- writeParityHeartbeat lets kernel/loop-adapter emit best-effort
diagnostics on plane failure paths so missing-exit gaps shrink.
3. Commit-gating on divergence or missing-exit:
- resolveParitySafeGitAction (in uok/gitops.ts) reads the parity
report and downgrades turn_action to status-only when divergence
count > 0 or missing-exit count > 0 — UOK can no longer commit
on top of unverified state.
- auto-post-unit.ts now resolves a configuredTurnAction from UOK
flags then asks the parity gate for the safe action; the gate's
decision is what flows to the actual git op.
- new test: tests/uok-gitops-commit-gate.test.ts.
- existing gitops-wiring assertion updated for the renamed
configuredTurnAction (semantic preserved).
Tests: 53/53 UOK pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
verification-gate "real lint fails → gate fails with exit code 1" was
asserting biome exits 1, but biome currently exits 0 (warnings only, no
errors). Reframe to verify the gate captures the lint exit code faithfully
regardless of biome's verdict — that's the contract we actually care
about, not whether the codebase happens to have lint errors.
workflow-mcp client timeouts bumped 30s → 60s. Test passes in isolation
in 8.5s but flakes under full-suite cold-cache load when the MCP stdio
round-trip exceeds 30s. 60s gives breathing room without losing real-bug
signal.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cold vitest+esbuild module-graph imports take 16-25s on this repo (dynamic
imports of captures.js and friends). The 30s testTimeout was racing the
import phase, producing 30s spurious failures across dev-engine-wrapper,
ensure-db-open, workflow-mcp, sf-tools, verification-gate, hook-key-parsing,
visualizer-overlay, and others — all timing out at exactly ~30s with no
real assertion failure.
Also bumps hookTimeout symmetrically.
Re-running the affected files: 147/147 pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>