diff --git a/docs/ENV.md b/docs/ENV.md index 0df37339b..df9c8b29d 100644 --- a/docs/ENV.md +++ b/docs/ENV.md @@ -69,7 +69,7 @@ All directory variables are optional and have sensible defaults: - `SF_WORKSPACE_BASE` (default: `SF_STATE_DIR/workspace`) — User workspaces - `SF_HISTORY_BASE` (default: `SF_STATE_DIR/history`) — Session history - `SF_NOTIFICATIONS_BASE` (default: `SF_STATE_DIR/notifications`) — Notifications -- `SF_SCHEDULE_FILE` (default: `SF_STATE_DIR/schedule.jsonl`) — Schedule queue +- `SF_SCHEDULE_FILE` (default: `SF_STATE_DIR/schedule.jsonl`) — Versioned schedule queue - `SF_RECOVERY_BASE` (default: `SF_STATE_DIR/recovery`) — Recovery artifacts - `SF_FORENSICS_BASE` (default: `SF_STATE_DIR/forensics`) — Diagnostics - `SF_SETTINGS_BASE` (default: `SF_STATE_DIR/settings`) — User settings diff --git a/docs/adr/0002-sf-schedule-pull-based.md b/docs/adr/0002-sf-schedule-pull-based.md index fba4d69ab..a808986c8 100644 --- a/docs/adr/0002-sf-schedule-pull-based.md +++ b/docs/adr/0002-sf-schedule-pull-based.md @@ -27,7 +27,7 @@ Option 3 (pull-based) is what we adopted. The SF schedule system is **pull-based**: -- Schedule entries are stored as append-only JSONL in `.sf/schedule.jsonl` (project) or `~/.sf/schedule.jsonl` (global). +- Schedule entries are stored as versioned append-only JSONL in `.sf/schedule.jsonl` (project) or `~/.sf/schedule.jsonl` (global). Rows without `schemaVersion` are treated as legacy version 1 by the current reader. - There is no background daemon or timer process. - Entries are queried ("pulled") at defined integration points: 1. **Launch** — `loader.ts` calls `findDue()` and prints a banner if items are overdue @@ -59,7 +59,7 @@ These limitations are accepted trade-offs for the portability and simplicity ben ## Implementation Notes -- `schedule-store.js` — append-only JSONL store with `findDue()` and `findUpcoming()` queries +- `schedule-store.js` — versioned append-only JSONL store with `findDue()` and `findUpcoming()` queries - `loader.ts` — calls `findDue()` on both scopes at startup; prints banner if any items are due - `headless-query.ts` — populates `schedule: { due, upcoming }` in `QuerySnapshot` - `sf schedule` CLI — add, list, done, cancel, snooze, run subcommands diff --git a/src/resources/extensions/sf/auto-prompts.js b/src/resources/extensions/sf/auto-prompts.js index 495b15618..61a279a8c 100644 --- a/src/resources/extensions/sf/auto-prompts.js +++ b/src/resources/extensions/sf/auto-prompts.js @@ -1231,7 +1231,8 @@ export async function checkNeedsReassessment(base, mid, _state, prefs) { `checkNeedsReassessment DB lookup failed: ${err instanceof Error ? err.message : String(err)}`, ); } - // File-based fallback using roadmap checkboxes + // Unmigrated/recovery fallback using rendered roadmap checkboxes. The DB path + // above remains authoritative when slice rows exist. const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP"); if (!roadmapPath) return null; const roadmapContent = await loadFile(roadmapPath); @@ -1336,7 +1337,8 @@ export async function checkNeedsRunUat(base, mid, _state, prefs) { `checkNeedsRunUat DB lookup failed: ${err instanceof Error ? err.message : String(err)}`, ); } - // File-based fallback using roadmap checkboxes + // Unmigrated/recovery fallback using rendered roadmap checkboxes. The DB path + // above remains authoritative when slice rows exist. if (!prefs?.uat_dispatch) return null; const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP"); if (!roadmapPath) return null; diff --git a/src/resources/extensions/sf/dispatch-guard.js b/src/resources/extensions/sf/dispatch-guard.js index 7cdeaf239..8906d94f3 100644 --- a/src/resources/extensions/sf/dispatch-guard.js +++ b/src/resources/extensions/sf/dispatch-guard.js @@ -60,7 +60,8 @@ export function getPriorSliceCompletionBlocker( } } if (!slices) { - // File-based fallback: parse roadmap checkboxes + // Unmigrated/recovery fallback: parse rendered roadmap checkboxes only + // when the DB has no slice rows for this milestone. const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP"); if (!roadmapPath) continue; let roadmapContent; diff --git a/src/resources/extensions/sf/md-importer.js b/src/resources/extensions/sf/md-importer.js index 6b1a3a740..bca8d28dc 100644 --- a/src/resources/extensions/sf/md-importer.js +++ b/src/resources/extensions/sf/md-importer.js @@ -483,8 +483,8 @@ export function migrateHierarchyToDb(basePath) { let milestoneStatus = "active"; if (hasSummary) milestoneStatus = "complete"; else if (hasParked) milestoneStatus = "parked"; - // Import milestones with all-done roadmap slices as complete (#3390, #3379) - // even when SUMMARY.md is missing — the roadmap checkboxes are authoritative. + // Legacy import path: when no DB row exists yet, all-done roadmap slices can + // seed a complete milestone even if SUMMARY.md is missing (#3390, #3379). else if ( roadmap && roadmap.slices.length > 0 && diff --git a/src/resources/extensions/sf/prompts/forensics.md b/src/resources/extensions/sf/prompts/forensics.md index 5e58140ef..4054d703c 100644 --- a/src/resources/extensions/sf/prompts/forensics.md +++ b/src/resources/extensions/sf/prompts/forensics.md @@ -67,7 +67,7 @@ SF extension source code is at: `{{sfSourceDir}}` The journal is a structured event log for autonomous mode iterations. Each daily file contains JSONL entries: ``` -{ ts: "ISO-8601", flowId: "UUID", seq: 0, eventType: "iteration-start", rule?: "rule-name", causedBy?: { flowId, seq }, data?: { unitId, status, ... } } +{ schemaVersion: 1, ts: "ISO-8601", flowId: "UUID", seq: 0, eventType: "iteration-start", rule?: "rule-name", causedBy?: { flowId, seq }, data?: { unitId, status, ... } } ``` **Key event types:**