Commit graph

3082 commits

Author SHA1 Message Date
Mikael Hugo
70afabedb7 refactor(uok): move auto-dispatch, auto-verification, auto-runaway-guard, auto-unit-closeout into sf/uok/
Per checkpoint-008/009 next-steps: these 4 autonomous-loop modules belong in
the UOK subsystem alongside the other orchestration primitives.

- auto-dispatch.js → uok/auto-dispatch.js
  - Dispatch table + resolveDispatch() is a core UOK orchestration primitive
  - Updated 3 static importers + 1 dynamic await import + 3 test files
- auto-verification.js → uok/auto-verification.js
  - Post-unit verification gate delegates to UOK gates (ChaosMonkey, Security,
    CostGuard, OutcomeLearning, etc.)
  - Updated 1 importer (auto.js)
- auto-runaway-guard.js → uok/auto-runaway-guard.js
  - Diagnostic budget guard; no local relative imports
  - Updated 4 importers (auto-timers.js, preferences-models.js, auto/phases.js,
    auto/run-unit.js)
- auto-unit-closeout.js → uok/auto-unit-closeout.js
  - Unit metrics snapshot + activity log + memory extraction helper
  - Updated 3 importers (auto-timers.js, auto-post-unit.js, auto.js)

Each original file is now a 1-line re-export shim preserving public API.
All 4 are added to uok/index.js as the UOK barrel.

26 dispatch tests pass; full unit suite 4374 tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 03:02:52 +02:00
Mikael Hugo
adb449d642 fix: consolidate extensions into sf, migrate kernel.ts, fix test suite
- Fold sf-usage-bar, sf-notify, sf-inturn-guard, sf-permissions,
  slash-commands into sf extension (ui/, notifications/, guards/,
  permissions/, commands/legacy/)
- Delete vectordrive extension
- Migrate uok/kernel.js to TypeScript (kernel.ts) with full interfaces
- Add allowJs/checkJs:false to tsconfig.resources.json for incremental TS migration
- Add symlink dedup to extension-discovery.ts (seenRealPaths Set)
- Add before_provider_request delegate back to native-search.js so
  session budget tests exercise the middleware end-to-end
- Fix parseSfNativeTools() to return all SF manifest tools (drop sf_ filter)
- Fix test assertions: plan_milestone/complete_task/validate_milestone
- Remove subagent from app-smoke.test.ts (folded into sf/subagent/)
- Remove sf-permissions/sf-inturn-guard/subagent from features-inventory test
- Fix resolveSearchProvider autonomous mode test to pass 'auto' explicitly
- Remove legacy /clear slash command (conflicts with built-in clear_terminal)
- Update web-command-parity-contract.test.ts for clear removal

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-11 02:40:52 +02:00
Mikael Hugo
24592507c3 sf snapshot: uncommitted changes after 53m inactivity 2026-05-11 01:54:55 +02:00
Mikael Hugo
852bf8c5aa sf snapshot: uncommitted changes after 78m inactivity 2026-05-11 01:01:03 +02:00
Mikael Hugo
605cd712be refactor: capability-tier isHeavyModelId, search provider registry, frontmatter_version field, schema docs
- preferences-models.js: replace 6-regex isHeavyModelId() with MODEL_CAPABILITY_TIER
  lookup + regex fallback for unknown models; new models in model-router.js
  are automatically reflected without touching preferences-models.js
- search-the-web/provider.js: replace ~200-line per-provider waterfall with
  PROVIDER_REGISTRY array + firstAvailable()/resolveWithFallback() helpers;
  preserves Tavily→Brave→Serper→Exa→Ollama→MiniMax auto-fallback order
- sf-db.js: bump SCHEMA_VERSION 58→60 (v59 now reachable); add
  frontmatter_version column to tasks table via v60 migration and CREATE
  TABLE definition; wire frontmatter_version into upsertTaskPlanning() SQL
  and .run() params
- task-frontmatter.js: add frontmatterVersion:1 to DEFAULT_TASK_FRONTMATTER,
  add validation block in validateTaskFrontmatter(), add frontmatterVersion
  mapping in taskFrontmatterFromRecord()
- sf-db-migration.test.mjs: update hardcoded version assertion 58→60
- docs/specs/sf-operating-model.md: add Planning Schema section documenting
  the 3-table model (milestones/slices/tasks, their PKs, spec tables, and
  ID naming conventions)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 23:42:29 +02:00
Mikael Hugo
b228bc9f5c feat(learning): weight failure_mode in Bayesian blender — rate_limit=0.7, quota=0.2, auth=0.0
- AGGREGATE_ONE/GROUPED_SQL: compute effective_success_rate with CASE WHEN failure_mode
- AggregatedStats: add effective_success_rate, hard_failure_count fields
- computeObservedScore: uses effective_success_rate when available; 0.5x penalty if >50% hard failures
- Tests: verify rate_limit ranked above quota_exhausted; hard failure penalty verified

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 23:20:33 +02:00
Mikael Hugo
2dea73398d fix(learning): add save_knowledge to manifest, failure_mode to aggregator SELECT + index
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 23:18:02 +02:00
Mikael Hugo
e50321b62b feat(selection): thread unitType + failure_mode into fallback outcome records
- FallbackResolver.setUnitContext() stores {unitType,unitId} from autonomous dispatch
- run-unit.js calls pi.setFallbackUnitContext() before/after each unit
- _findAnyAvailableFallback uses real unitType/unitId from context, not sentinel
- Schema v59: failure_mode column in llm_task_outcomes
- insertLlmTaskOutcome accepts failure_mode (rate_limit, quota_exhausted, auth_error)
- register-hooks.js passes event.classification.reason as failure_mode
- register-hooks.js uses real event.unitId when available
- ExtensionRuntimeActions.setFallbackUnitContext added to pi API surface

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 23:14:22 +02:00
Mikael Hugo
009651e86f feat(selection): wire before_model_select into FallbackResolver for outcome-aware fallback
When a model fails and FallbackResolver picks a replacement, it now:
1. Fires the before_model_select hook with reason='fallback' and the
   failing model's ID — the learning system records the failure outcome
   and returns the best Bayesian-blended replacement from llm_task_outcomes
2. Falls back to the existing heuristic sort (reasoning + context window)
   if the hook is unavailable or returns no override

Changes:
- BeforeModelSelectEvent: add optional currentModelId and reason fields
- FallbackResolver: accept emitBeforeModelSelect in constructor; make
  _findAnyAvailableFallback async; fire hook before heuristic fallback
- agent-session.ts: inject lazy emitBeforeModelSelect closure into resolver
- register-hooks.js: record failure outcome when reason='fallback' before
  returning selectLearnedModel result

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 23:05:33 +02:00
Mikael Hugo
fb1bd3e5fa refactor(shared): deduplicate shared/ utilities against coding-agent package exports
- Add packages/coding-agent/src/utils/format.ts as the canonical source
  for formatDuration, formatTokenCount, truncateWithEllipsis, sparkline,
  formatDateShort, fileLink, stripAnsi, normalizeStringArray — all already
  exported from @singularity-forge/coding-agent via index.ts.

- Convert shared/format-utils.js to a compatibility shim that re-exports
  the 8 functions from @singularity-forge/coding-agent. All 13 importers
  continue to work with no import changes required.

- Convert shared/path-display.js to a compatibility shim that re-exports
  toPosixPath from @singularity-forge/coding-agent. Implementation in
  packages/coding-agent/src/utils/path-display.ts was already canonical.

- shared/frontmatter.js is intentionally NOT shimmed: splitFrontmatter/
  parseFrontmatterMap have a different API from the package's parseFrontmatter/
  stripFrontmatter (flat-map vs {frontmatter, body} object).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 22:41:03 +02:00
Mikael Hugo
7227912a29 perf(search): move web-search provider injection from extension hook to native middleware
- Create packages/coding-agent/src/core/providers/web-search-middleware.ts with
  WebSearchMiddleware class: injects web_search tool, enforces session budget (#1309),
  strips thinking blocks from history, and respects PREFERENCES.md search_provider.

- Wire webSearchMiddleware.applyToPayload into sdk.ts onPayload callback (before
  extension hook dispatch) so injection runs as compiled TypeScript with zero
  jiti-dispatch overhead.

- Export WebSearchMiddleware, webSearchMiddleware singleton, setPreferBraveResolver,
  CUSTOM_SEARCH_TOOL_NAMES, MAX_NATIVE_SEARCHES_PER_SESSION, and stripThinkingFromHistory
  from @singularity-forge/coding-agent so the extension can delegate to the same instance.

- Refactor search-the-web/native-search.js: remove self-contained injection logic;
  import and delegate before_provider_request to webSearchMiddleware singleton.
  Use tri-state isAnthropicProvider (null/false/true) to synthesize a provider hint
  when event.model is absent but model_select has already fired — prevents the
  model-name heuristic from wrongly injecting into Copilot claude-* requests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 22:37:42 +02:00
Mikael Hugo
a798aa1f6e feat(swarm): wire @a2a-js/sdk as real A2A transport for SF_A2A_ENABLED dispatch path
- Install @a2a-js/sdk v0.3.13 as a dependency
- Add a2a-transport.js: A2ATransport class with spawnAgent, dispatch,
  getOrSpawnAgent, and buildAgentCard; spawns pi subprocesses with
  SF_A2A_AGENT_* env vars and dispatches envelopes via A2A JSON-RPC
- Add a2a-agent-server.js: A2A HTTP server entrypoint for spawned agent
  processes; starts express + A2AExpressApp with DefaultRequestHandler,
  handles incoming DispatchEnvelopes via SwarmAgentExecutor, writes
  envelope to SQLite MessageBus, and signals readiness via stdout JSON
- Update swarm-dispatch.js: split dispatch() into _busDispatch()
  (existing SQLite path) and _a2aDispatch() (new A2A path); lazy-load
  A2ATransport singleton only when SF_A2A_ENABLED is set; default
  path unchanged for all existing callers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 22:33:01 +02:00
Mikael Hugo
3fba4bcb03 refactor(mcp): move MCP connection manager to packages/coding-agent/src/core/mcp/
- Create config.ts with McpServerConfig types and readMcpConfigs/getServerConfig
- Create auth.ts with buildHttpTransportOpts and createCliOAuthProvider
- Create connection-manager.ts with McpConnectionManager class
- Create index.ts re-exporting the public API
- Export McpConnectionManager and helpers from @singularity-forge/coding-agent
- Rewrite mcp-client extension as thin wrapper using McpConnectionManager
- Rewrite auth.js as re-export shim from @singularity-forge/coding-agent
- Update test to import buildHttpTransportOpts from @singularity-forge/coding-agent

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 22:19:46 +02:00
Mikael Hugo
9e484e67b7 refactor(sf): fold sf-tui extension into sf/ui/ — remove separate extension layer
sf-tui was a 'bundled' extension with zero features independent of the sf/
extension. Every hook, shortcut, tool, header and footer render depended
on sf/ internals (getAutoSession, isAutoActive, projectRoot,
getExperimentalFlag). The separation was artificial.

Changes:
- Moved all sf-tui/*.js into sf/ui/ (header, footer, git, color-band, emoji,
  prompt-history, marketplace, powerline, shared)
- Fixed imports: ../sf/ → ../ (one level up from ui/)
- Registered sf/ui/index.js from sf/index.js in a try/catch so a UI failure
  can't take out the core SF commands
- Merged sf-tui manifest entries (9 commands, 3 shortcuts, agent_start hook)
  into sf/extension-manifest.json
- Deleted src/resources/extensions/sf-tui/ entirely
- Fixed prompt-history.test.mjs import path

Result: one fewer extension to discover, load and validate at startup.
sf is now the single extension that owns both planning state and UI chrome.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 22:04:00 +02:00
Mikael Hugo
702ec3fc0e refactor(sf): rename guidance files TASTE.md→STYLE.md, ANTI-GOALS.md→NON-GOALS.md
More self-explanatory names. No behavioral change — same files, same purpose.

- .sf/TASTE.md → .sf/STYLE.md (# Taste → # Style)
- .sf/ANTI-GOALS.md → .sf/NON-GOALS.md (# Anti-goals → # Non-goals)
- All code references updated: auto-bootstrap-context, system-context,
  gitignore, milestone-framing-check, scaffold-constants, spec-projections
- Section headings injected into agent context updated to match

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 21:28:31 +02:00
Mikael Hugo
48a01dd764 refactor(prefs): remove all legacy PREFERENCES.md / preferences.md support
preferences.yaml is now the only preferences file. No fallback chains,
no .md parsing paths, no legacy path getters.

- preferences.js: remove globalPreferencesPath, globalPreferencesPathUppercase,
  legacyGlobalPreferencesPath, projectPreferencesPath, projectPreferencesPathUppercase,
  getLegacyGlobalSFPreferencesPath; simplify load functions to yaml-only;
  parsePreferencesMarkdown kept as thin deprecated shim over parsePreferencesYaml
- commands-prefs-wizard.js: remove parseFrontmatterMap/splitFrontmatter usage,
  .md branch in savePreferencesFile/ensurePreferencesFile, legacyGlobal display
- auto-dashboard.js: parsePreferencesMarkdown → parsePreferencesYaml
- guided-flow.js / worktree-root.js: remove PREFERENCES.md existence checks
- detection.js: remove .md fallbacks from all 3 detection functions
- auto-bootstrap-context.js: remove .sf/PREFERENCES.md from priority list
- auto-worktree.js: remove LEGACY_PREFERENCES_FILES array and all copy fallbacks
- deep-project-setup-policy.js: only check preferences.yaml
- gitignore.js: ensurePreferences checks yaml only
- planning-depth.js: returns plain string path (not {path,isYaml}); yaml-only
- preferences-template-upgrade.js: remove .md branch; always write raw YAML
- tests: update fixtures to preferences.yaml with plain YAML content
- docs/learning: update all remaining PREFERENCES.md references

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 21:14:43 +02:00
Mikael Hugo
48dbb175c0 feat(prefs): migrate canonical preferences file from PREFERENCES.md to preferences.yaml
New installations create .sf/preferences.yaml (pure YAML, no frontmatter
markers) and ~/.sf/preferences.yaml. Existing .md files are read as fallbacks
with no migration required for current users.

Changes:
- preferences.js: add yaml path getters, load chain tries .yaml first, add
  parsePreferencesYaml() for direct YAML parse without frontmatter extraction
- templates/preferences.yaml: new canonical template (pure YAML with comment
  header pointing to preferences-reference.md)
- gitignore.js: ensurePreferences() creates preferences.yaml; simplified by
  removing scaffold-versioning dependency
- init-wizard.js: buildPreferencesFile() produces pure YAML, writes preferences.yaml
- commands-prefs-wizard.js: savePreferencesFile() helper handles .yaml vs .md;
  ensurePreferencesFile uses yaml template for yaml paths
- preferences-template-upgrade.js: yaml files get raw YAML on upgrade
- planning-depth.js: returns {path, isYaml}, handles both formats
- deep-project-setup-policy.js: isWorkflowPrefsCaptured() tries all 3 paths
- detection.js: preferences.yaml added to all detection checks
- auto-worktree.js: canonical=yaml, LEGACY_PREFERENCES_FILES=["PREFERENCES.md","preferences.md"]
- auto-bootstrap-context.js: preferences.yaml before PREFERENCES.md in list
- guided-flow.js / worktree-root.js: existence checks include preferences.yaml
- User-visible strings / comments updated throughout

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 21:05:10 +02:00
Mikael Hugo
61b4fecdaf fix(notices+db): complete NOTICE_KIND tagging, fix slice-dep query, cap error storage
NOTICE_KIND tagging:
- auto.js: ctrl-c-pause (USER_VISIBLE), auto-start-failed/session-lock-lost/
  stopAuto/debug-summary-written (SYSTEM_NOTICE), auto-no-command-ctx (USER_VISIBLE)
- loop.js: model-policy-blocked SYSTEM_NOTICE→BLOCKING_NOTICE (user must act),
  solver-eval results/infra-stop/consecutive-cooldowns (SYSTEM_NOTICE),
  phase-timeout/credential-cooldown-wait/iteration-error (TOOL_NOTICE); fix import order
- register-hooks.js: destructive-command (TOOL_NOTICE), gemini-preflight (SYSTEM_NOTICE)
- provider-error-pause.js: auto-resume (TOOL_NOTICE), scheduled-resume (SYSTEM_NOTICE),
  permanent-pause (BLOCKING_NOTICE)
- uok-parity-summary.js: parity warning (SYSTEM_NOTICE)

sf-db fixes:
- getActiveSliceFromDb: use slice_dependencies junction table instead of
  json_each(s.depends) — junction table is kept in sync by syncSliceDependencies
- capErrorForStorage: cap UOK run error blobs at 4 KB; excess spills to
  .sf/runtime/errors/<runId>.txt to prevent DB bloat from large stack traces

ARCHITECTURE.md:
- Document DB-first invariant; remove .sf/DECISIONS.md/.REQUIREMENTS.md/.KNOWLEDGE.md
  from tracked-file list (they are rendered projections, not authoritative sources)
- Add .sf/traces/ and .sf/metrics.db to gitignored list
- Update system-context assembly order to show DB-sourced decisions/requirements
- Correct system-context.ts → system-context.js

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 20:26:18 +02:00
Mikael Hugo
ad380d5602 fix(db-first): remove all .sf/*.md direct-write instructions from prompts; requirement-promoter uses DB only
- Prompts: replace 'append to .sf/DECISIONS.md' → 'call save_decision' in
  plan-slice, heal-skill (KNOWLEDGE.md), refine-slice, queue, guided-execute-task
- Prompts: replace 'Read .sf/DECISIONS.md if it exists' / 'Read .sf/REQUIREMENTS.md if it exists'
  with 'injected from DB into system context' in guided-plan-slice, guided-research-slice
- requirement-promoter: remove dead appendRequirementRow() and readHighestRNumber(file)
  that read/wrote REQUIREMENTS.md; replace with DB-only readHighestRNumber() using
  getActiveRequirements(); remove sfRoot import, mkdirSync, writeFileSync
- requirement-promoter: pre-compute highestNum once per sweep loop instead of
  re-reading for each cluster (fixes ID collision when promoting multiple at once)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 20:22:55 +02:00
Mikael Hugo
62fcf8fd20 feat(notifications): tag remaining auto/loop/register-hooks notices + trace-writer
- auto.js, auto/loop.js, bootstrap/register-hooks.js: tag all
  autonomous-mode system notices with NOTICE_KIND.SYSTEM_NOTICE;
  add dedupe_key to loop-level model-policy and flow-audit notices
- web/notifications-service.ts: add repeatCount/lastTs/noticeKind to
  Notification type (schema v2 fields)
- uok/trace-writer.js: new unit trace writer
- tests/notification-store-grouping.test.mjs: grouping test coverage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 20:14:22 +02:00
Mikael Hugo
d33e30e885 feat(notifications): NOTICE_KIND enum, schema v2 dedup, sf-db cleanup
- notification-store: schema v2 — repeatCount/lastTs merge for non-blocking
  notices; NOTICE_KIND enum (SYSTEM_NOTICE, TOOL_NOTICE, BLOCKING_NOTICE,
  USER_VISIBLE) for renderer classification without message parsing
- sf-db: remove gate_runs and audit_events tables (replaced by uok audit.js
  and trace-writer); schema reduced by ~370 lines
- notify-interceptor: tag auto-mode system notices with NOTICE_KIND.SYSTEM_NOTICE
- auto-prompts, guided-flow, system-context: use NOTICE_KIND on emit calls
- cli-status: expanded headless status surface + test coverage
- headless-types: new status fields
- Makefile/justfile: dev workflow improvements
- record-promoter, requirement-promoter: minor cleanup
- sf-db-migration tests: updated for dropped tables
- uok-gate-runner, uok-metrics, uok-outcome, uok-status tests: updated

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 20:13:58 +02:00
Mikael Hugo
5c2e3eec24 fix(memory): add missing readGatewayFromAuthJson to source + update tests
The function and node:fs/os/path imports were dropped from the source
during editing. Added them back. Updated memory-embeddings-llm-gateway
test to cover auth.json-only behavior (no env var aliases).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 18:28:18 +02:00
Mikael Hugo
6f6ad76a77 feat(memory): load LLM gateway key from auth.json only, not env vars
Gateway key and URL are now read exclusively from ~/.sf/agent/auth.json
under the 'llm-gateway' entry. Removed env var support for the API key
(SF_LLM_GATEWAY_KEY, LLM_MUX_API_KEY, etc.) — credentials belong in
auth.json alongside all other provider keys, not in the environment.

Model/instruction overrides (SF_LLM_GATEWAY_EMBED_MODEL etc.) still
read from env vars as they are tuning knobs, not secrets.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 18:25:23 +02:00
Mikael Hugo
a77e1551d2 refactor(memory): consolidate memory system, remove dead code
- Delete memory-backfill.js — not imported anywhere, dead code
- Rename memory-sleeper.js → tool-watchdog.js — misnamed; it is a
  tool-output watchdog with no relation to the memory store
- Collapse memory-embeddings-llm-gateway.js into memory-embeddings.js —
  removes the lazy-import split; loadGatewayConfigFromEnv,
  createGatewayEmbedFn, and rerankCandidates are now direct exports
- Remove buildEmbeddingFn() dead stub (always returned null)
- Enable packages/coding-agent memory extraction extension by default
  (memory.enabled ?? true) so session-level extraction is active
- Update all import sites and tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 18:17:49 +02:00
Mikael Hugo
2a1309d127 fix(memory): make SM opt-in (SM_ENABLED=true) instead of opt-out
Local SQLite is the memory system. External Singularity Memory is an
optional cross-project enhancement, not a dependency. Flip the default
so SM is disabled unless explicitly opted in via SM_ENABLED=true:
- sm-client.js: return disconnected early unless SM_ENABLED=true
- memory-store.js: only pass smConnected=true when SM_ENABLED=true
- doctor-config-checks.js: skip SM health check when not opted in
- sm-client.test.ts: update test to reflect opt-in behaviour

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 18:03:13 +02:00
Mikael Hugo
a3019e5402 fix(knowledge): db-first dedup, numeric confidence, consistent IDs
- knowledge-compounding.js: replace KNOWLEDGE.md file-read dedup with
  getActiveMemories() DB query; file was never written so dedup was
  always empty, causing duplicates to accumulate on every milestone close
- knowledge-compounding.js + save_knowledge tool: map confidence strings
  ('high'/'medium'/'low') to numeric scores (0.9/0.6/0.3) for the
  memories.confidence REAL column; string values coerced to 0.0 by
  SQLite, silently making all knowledge entries rank last and never
  appear in system context
- save_knowledge: use K-${randomUUID()} (full UUID) instead of
  K-${randomUUID().slice(0,8)} to match knowledge-compounding.js and
  avoid collision risk
- complete-milestone.md: replace '.sf/DECISIONS.md' file reference with
  'decisions inlined from DB' — the file is not generated anymore

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 17:57:11 +02:00
Mikael Hugo
3ffd882c8c sf snapshot: uncommitted changes after 56m inactivity 2026-05-10 17:16:30 +02:00
Mikael Hugo
37ebfcf53a test(summary-helpers): add regression tests for extractSliceExecutionExcerpt
Verifies the function handles null/undefined content gracefully and
correctly extracts goal, demo, verification, and observability sections
from slice plan content. Addresses sf-mozutl5d-ei3ec6 by ensuring the
function is importable and behaves correctly end-to-end.
2026-05-10 16:20:15 +02:00
Mikael Hugo
924383b6f7 sf snapshot: uncommitted changes after 197m inactivity 2026-05-10 15:59:33 +02:00
Mikael Hugo
280303ef9a fix(lint): reformat 6 files touched during web dep upgrade
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 12:10:10 +02:00
Mikael Hugo
02a4339a51 refactor: rename pi-* packages to forge-native names (Phase 1)
Rename all four packages/pi-* directories to forge-native names,
stripping the 'pi' identity and establishing forge's own:

- packages/pi-coding-agent → packages/coding-agent
- packages/pi-ai → packages/ai
- packages/pi-agent-core → packages/agent-core
- packages/pi-tui → packages/tui

Package names updated:
- @singularity-forge/pi-coding-agent → @singularity-forge/coding-agent
- @singularity-forge/pi-ai → @singularity-forge/ai
- @singularity-forge/pi-agent-core → @singularity-forge/agent-core
- @singularity-forge/pi-tui → @singularity-forge/tui

All import references, bare string references, path references,
internal variable names (_bundledPi*), and dist files updated.
@mariozechner/pi-* third-party compat aliases preserved.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 11:28:01 +02:00
Mikael Hugo
05953e9599 fix(lint): restore 0 Biome diagnostics and fix web-mode-onboarding test timeout
- Remove/prefix unused imports and variables across 11 src/ files to clear
  74 diagnostics introduced by 37 subsequent commits since run #3
- Fix pre-existing timeout in web-mode-onboarding integration test:
  - Add timeoutMs: 120_000 to launchPackagedWebHost call (was unbounded)
  - Raise AbortSignal.timeout on simple fetches 10s → 30s (under parallel load)
  - Raise overall test timeout 180s → 420s (budget: 120+60+30+30+120+30=390s)
- Log autoresearch run #4 and update lessons in autoresearch.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 11:01:43 +02:00
Mikael Hugo
b2bcb922de sf snapshot: uncommitted changes after 37m inactivity 2026-05-10 09:56:56 +02:00
Mikael Hugo
7e8e3aa846 sf snapshot: pre-dispatch, uncommitted changes after 30m inactivity 2026-05-10 09:19:51 +02:00
Mikael Hugo
e58e138457 feat(db): DB-only UAT verdicts — backfill on open, write on ASSESSMENT save, no file fallbacks
- sf-db.js: add backfillUatVerdicts(basePath) that scans ASSESSMENT/UAT_RESULT
  files for slices with no uat_verdict in DB and populates them on open
- dynamic-tools.js: call backfillUatVerdicts after openDatabase succeeds so
  all 3 repos with existing verdict files are covered on next launch
- workflow-tool-executors.js: call setSliceUatVerdict when saving ASSESSMENT
  at slice scope so future verdicts are written directly to DB
- workflow-helpers.js: remove all file fallbacks from checkNeedsRunUat;
  verdict check is DB-only (backfill guarantees DB is populated on open)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 08:49:45 +02:00
Mikael Hugo
6c113be473 fix(uat): treat ASSESSMENT file with verdict as completed UAT result
checkNeedsRunUat only checked for UAT_RESULT file, but the autonomous
runner writes ASSESSMENT files. This caused run-uat to dispatch 5x with
no verdict when only an ASSESSMENT (with verdict: PASS) existed.

Now ASSESSMENT file with any verdict counts as a completed UAT result,
stopping the infinite dispatch loop.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 08:32:21 +02:00
Mikael Hugo
d8c687702b fix(auto): cache lastCommandCtx from any SF command so Ctrl+Y works immediately
Previously required /autonomous first. Now any slash command (/next, /chat,
/clear etc.) caches the ExtensionCommandContext, so Ctrl+Y YOLO shortcut
works on first press after any command interaction.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 08:10:27 +02:00
Mikael Hugo
d56e68c789 fix(auto): revert YOLO shortcut to ctrl+y
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 07:59:10 +02:00
Mikael Hugo
60ee46aebb fix(auto): cache lastCommandCtx to survive shortcut-handler restarts
Shortcut handlers (registerShortcut) receive ExtensionContext which has
no newSession(). This caused autonomous mode started via Ctrl+Y to
always crash with 'newSession is not a function'.

- AutoSession.lastCommandCtx: new field that persists across stopAuto/reset
  so shortcut handlers can fall back to the last valid command context
- startAuto(): cache valid command ctx; fall back and notify user if ctx
  has no newSession; return early with actionable message if no cache yet
- dispatchHookUnit(): same guard — resolve hookCtx before s.cmdCtx = ctx
- run-unit.js: last-resort guard before newSession() call returns clean
  error category instead of TypeError
- steerable-autonomous-extension.js: rename ctrl+y → ctrl+alt+y to avoid
  conflict with terminal yank built-in

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 07:56:31 +02:00
Mikael Hugo
529138db9a sf snapshot: uncommitted changes after 33m inactivity 2026-05-10 07:54:07 +02:00
Mikael Hugo
7085ad850d refactor(tools): remove sf_ prefix from all remaining tool names
plan_milestone, plan_slice, plan_task, complete_task, complete_slice,
complete_milestone, skip_slice, replan_slice, reassess_roadmap,
validate_milestone, save_requirement, update_requirement, milestone_status

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 07:20:56 +02:00
Mikael Hugo
e7bd6a76b9 refactor(tools): improve description fields to be action-oriented and agent-facing
Rewrite all 13 renamed tool descriptions to follow Copilot tool conventions:
- Imperative verb opening
- One sentence on what it returns
- One sentence on when to use it
- No internal jargon or SF-specific acronyms

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 07:13:59 +02:00
Mikael Hugo
ac371926cb refactor(tools): rename SF tools to cleaner action-oriented names
Align tool names with Copilot coding agent conventions:
- sf_exec → run_command
- sf_exec_search → read_output
- sf_resume → resume_agent
- capture_thought → log_reasoning
- sf_log_judgment → log_decision
- sf_self_report → report_issue
- sf_self_feedback_resolve → resolve_issue
- sf_save_gate_result → record_gate
- sf_autonomous_checkpoint → checkpoint
- sf_milestone_generate_id → new_milestone_id
- sf_graph → memory_graph
- memory_query → memory_search
- sf_retrieval_evidence → search_evidence

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 07:10:41 +02:00
Mikael Hugo
1322bc7d9a feat: implement Copilot coding agent lessons in SF
- fix(compaction): tokensBefore undefined crash on reload
  compaction-orchestrator now falls back to preparation.totalTokens when
  extension returns tokensBefore: undefined; compaction-summary-message
  guards with ?? 0 defensively

- feat(exec): inline truncation notice in sf_exec digest
  appends [stdout truncated — read full output: <path>] when
  stdout_truncated=true so agent knows to use sf_exec_search

- feat(exec): wire onUpdate progress for sf_exec
  calls onUpdate before execution starts with status/command so TUI
  shows live feedback during long-running commands

- feat(security): prompt injection defense for external content
  new sanitize-external-content.js utility: strips HTML comments,
  detects 15 injection patterns (instruction override, role reassignment,
  fake system messages, encoded payloads); wired into exec-tool digest

- feat(tools): sf_session_todo tool (persisted cross-compaction)
  add/check/list ops; persists to .sf/session_todo.json; pending todos
  injected into compaction summary block for context continuity

- feat(hooks): shell hooks surface (.sf/hooks/pre-tool/*.sh, post-tool/*.sh)
  pre-tool hooks block tool execution (exit≠0 = block with stdout reason)
  post-tool hooks fire-and-forget; JSON context piped to stdin; 5s timeout

- fix(db): WAL autocheckpoint disabled to prevent corruption
  PRAGMA wal_autocheckpoint=0 in initSchema(); explicit checkpointWal()
  after successful finalize verification — the only safe checkpoint point

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 07:01:28 +02:00
Mikael Hugo
97619cbc74 fix: resolve 3 test failures and 1 pre-existing code bug
- unit-runtime: fall back to STATE.md for nextActionAdvanced when DB is
  unavailable (restores test compat for reconcileDurableCompleteUnitRuntime-
  Records; DB path still preferred in production)
- browser-slash-command-dispatch: remove 'stop' from SF_PASSTHROUGH_COMMANDS
  so /stop correctly returns { kind: 'reject' } in browser mode (was falling
  through to prompt/rpc instead of builtin-reject)
- bg-events: export MAX_PENDING_ALERTS so process-manager can re-export it;
  satisfies session-memory-leaks contract test
- commands-handlers: guard effectiveScope assignment — only use requestedScope
  when mode=audit AND requestedScope is truthy (avoids undefined propagation)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 04:55:56 +02:00
Mikael Hugo
be785ea13f fix(tui): restore auto mode bottom banner
Remove setFooter(hideFooter) calls in auto-start.js and auto.js that were
overriding the sf-tui footer with a near-invisible stub. The sf-tui footer
already checks isAutoActive() and routes to renderAutoFooter — no override
needed. Also remove now-unused hideFooter import from auto.js.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 04:33:54 +02:00
Mikael Hugo
01d58c570d sf snapshot: uncommitted changes after 36m inactivity 2026-05-10 04:27:43 +02:00
Mikael Hugo
1a0222fc71 fix(uok): reclassify 'tool unavailable' when checkpoint tool IS registered
The repair loop was classifying agent reports of 'tool unavailable' as
'checkpoint-tool-unavailable' even when sf_autonomous_checkpoint IS
registered in the manifest. This caused a self-referential loop: the
repair prompt re-requested the same tool call, the agent re-reported
unavailability, and the cycle repeated (4 repair attempts).

Fix: before classifying as 'checkpoint-tool-unavailable', verify the tool
is in the manifest. If it IS registered, reclassify as
'mentioned-checkpoint-without-tool' — the tool exists, the agent just
didn't call it. Also added existsSync to the ES module fs import in
autonomous-solver.js.

Test: new case in autonomous-solver.test.mjs verifies the reclassification
when tool IS in manifest.
2026-05-10 03:51:25 +02:00
Mikael Hugo
6b7d327672 sf snapshot: uncommitted changes after 30m inactivity 2026-05-10 03:21:24 +02:00
Mikael Hugo
1a681caa86 fix(auto): repair retries reuse session context instead of starting cold
When the autonomous solver fails to produce a checkpoint and enters the
repair loop, subsequent retries previously called newSession() each time,
wiping the conversation history. The agent restarted cold with no memory
of what it had tried, what tools it had called, or why it failed — making
meaningful repair nearly impossible.

This change adds a keepSession option to runUnit(). When true, the
newSession() call and session-switch guard logic are skipped; the repair
prompt is sent as a follow-up in the existing conversation. The agent can
now see its prior tool calls, file reads, and failure context when deciding
how to fix the issue.

Policy:
- First attempt at each unit: keepSession=false (clean session, correct
  for independent slice boundaries — system prompt carries project state)
- Repair retries within the same unit: keepSession=true (agent carries
  full context of what it already tried)
- Next unit after success/failure: keepSession=false (clean boundary)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 02:50:57 +02:00