From 39382f7e54c9abab307cba2870815d99b382751e Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Thu, 7 May 2026 03:20:20 +0200 Subject: [PATCH] docs: clarify db-backed state guidance --- docs/specs/sf-schedule.md | 9 +++++---- .../extensions/sf/bootstrap/system-context.js | 7 ++++--- src/resources/extensions/sf/commands-schedule.js | 2 +- src/resources/extensions/sf/self-feedback-drain.js | 2 +- .../extensions/sf/skills/sf-headless/SKILL.md | 14 +++++++++----- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/docs/specs/sf-schedule.md b/docs/specs/sf-schedule.md index bebb09f93..368f6bf69 100644 --- a/docs/specs/sf-schedule.md +++ b/docs/specs/sf-schedule.md @@ -40,11 +40,11 @@ Schedule entries use [ULID](https://github.com/ulid/spec) (Universally Unique Le The `generateULID()` function in `schedule-ulid.js` is used for all new entries. -### Append-Only JSONL +### Versioned Append-Only JSONL -Each write appends a JSON line to `schedule.jsonl`. The latest entry per ID wins on read (via `created_at` comparison). This means status transitions (`pending` → `done`, `cancelled`, `snoozed`) are implemented as new entries, not mutations. The file is never rewritten — only appended to. +Each write appends a schema-versioned JSON line to `schedule.jsonl`. The latest entry per ID wins on read (via `created_at` comparison). This means status transitions (`pending` → `done`, `cancelled`, `snoozed`) are implemented as new entries, not mutations. The file is never rewritten — only appended to. -Corrupt lines are skipped with a warning, never fatal. +Rows without `schemaVersion` are treated as legacy version 1. Unsupported future schema versions are ignored by the current reader. Corrupt lines are skipped with a warning, never fatal. --- @@ -61,6 +61,7 @@ Corrupt lines are skipped with a warning, never fatal. ```json { + "schemaVersion": 1, "id": "01ARZ3NDEKTSV4RRFFQ69G5FAV", // ULID — 28 chars "kind": "reminder", // ScheduleKind enum "status": "pending", // pending | done | cancelled | snoozed @@ -76,7 +77,7 @@ Corrupt lines are skipped with a warning, never fatal. ### JSONL Line Example ``` -{"id":"01ARZ3NDEKTSV4RRFFQ69G5FAV","kind":"reminder","status":"pending","due_at":"2026-06-15T09:00:00.000Z","created_at":"2026-05-15T09:00:00.000Z","payload":{"message":"Review adoption metrics"},"created_by":"user","auto_dispatch":false} +{"schemaVersion":1,"id":"01ARZ3NDEKTSV4RRFFQ69G5FAV","kind":"reminder","status":"pending","due_at":"2026-06-15T09:00:00.000Z","created_at":"2026-05-15T09:00:00.000Z","payload":{"message":"Review adoption metrics"},"created_by":"user","auto_dispatch":false} ``` --- diff --git a/src/resources/extensions/sf/bootstrap/system-context.js b/src/resources/extensions/sf/bootstrap/system-context.js index 8db07f016..c2a743954 100644 --- a/src/resources/extensions/sf/bootstrap/system-context.js +++ b/src/resources/extensions/sf/bootstrap/system-context.js @@ -372,7 +372,8 @@ export function loadKnowledgeBlock(sfHomeDir, cwd) { const TACIT_SECTION_MAX_BYTES = 4096; // Cap self-feedback entries to prevent context bloat. High/critical entries // are always included; medium/low are truncated if needed. Evidence details -// are stored in jsonl only — the prompt gets compact summaries with IDs. +// live in the durable self-feedback store; the prompt gets compact summaries +// with IDs. // (#sf-moobj36p-ko6snt) const SELF_FEEDBACK_MAX_ENTRIES = 20; const SELF_FEEDBACK_MAX_CHARS = 4000; @@ -425,7 +426,7 @@ function loadSelfFeedbackBlock(cwd) { if (kept.length > SELF_FEEDBACK_MAX_ENTRIES) { kept = kept.slice(0, SELF_FEEDBACK_MAX_ENTRIES); } - // Render compact summaries — evidence is in jsonl, not injected here + // Render compact summaries — full evidence is not injected here const rows = kept .map((e) => `- **${e.severity}** \`${e.kind}\` — ${e.summary}`) .join("\n"); @@ -443,7 +444,7 @@ function loadSelfFeedbackBlock(cwd) { } // Add note about where to find full evidence if (entries.length > kept.length) { - block += `\n\n*(${entries.length - kept.length} more entries hidden to prevent context bloat. Full evidence in .sf/self-feedback.jsonl by entry ID.)*`; + block += `\n\n*(${entries.length - kept.length} more entries hidden to prevent context bloat. Use sf_self_feedback_resolve/read tools or .sf/SELF-FEEDBACK.md entry IDs to inspect full evidence.)*`; } return `\n\n[SELF-FEEDBACK — Recent sf-internal anomalies]\n\n${block}`; } diff --git a/src/resources/extensions/sf/commands-schedule.js b/src/resources/extensions/sf/commands-schedule.js index 2ffb432c2..ff41343cf 100644 --- a/src/resources/extensions/sf/commands-schedule.js +++ b/src/resources/extensions/sf/commands-schedule.js @@ -2,7 +2,7 @@ * SF Command — /sf schedule * * Schedule management: add, list, done, cancel, snooze, run. - * Entries stored as append-only JSONL in .sf/schedule.jsonl (project) + * Entries stored as versioned append-only JSONL in .sf/schedule.jsonl (project) * or ~/.sf/schedule.jsonl (global). */ diff --git a/src/resources/extensions/sf/self-feedback-drain.js b/src/resources/extensions/sf/self-feedback-drain.js index 5a81c77c4..7a436a128 100644 --- a/src/resources/extensions/sf/self-feedback-drain.js +++ b/src/resources/extensions/sf/self-feedback-drain.js @@ -155,7 +155,7 @@ function buildInlineFixPrompt(entries) { "4. Commit the fix with a conventional commit message.", "5. Call `sf_self_feedback_resolve` for each repaired entry with agent-fix evidence and the commit SHA.", "6. If an entry is already fixed, verify it and call `sf_self_feedback_resolve` with the verification evidence.", - "7. Do not hand-edit `.sf/self-feedback.jsonl`; use the resolver tool so markdown, JSONL, and reload detection stay consistent.", + "7. Do not hand-edit `.sf/self-feedback.jsonl` or `.sf/SELF-FEEDBACK.md`; use the resolver tool so the durable self-feedback store, markdown projection, and reload detection stay consistent.", "", "When done, say: Self-feedback inline fix complete.", ].join("\n"); diff --git a/src/resources/extensions/sf/skills/sf-headless/SKILL.md b/src/resources/extensions/sf/skills/sf-headless/SKILL.md index c4a07a4db..3f73fd367 100644 --- a/src/resources/extensions/sf/skills/sf-headless/SKILL.md +++ b/src/resources/extensions/sf/skills/sf-headless/SKILL.md @@ -207,21 +207,25 @@ See [references/answer-injection.md](references/answer-injection.md) for full de ## SF Project Structure -All state lives in `.sf/` as markdown files (version-controllable): +Durable project state lives in `.sf/sf.db` when the project has been migrated. +Markdown files in `.sf/` are version-controllable projections, exports, or +recovery/import inputs: ``` .sf/ milestones/M001/ M001-CONTEXT.md # Requirements, scope, decisions - M001-ROADMAP.md # Slices with tasks, dependencies, checkboxes + M001-ROADMAP.md # Rendered roadmap projection M001-SUMMARY.md # Completion summary slices/S01/ - S01-PLAN.md # Task list + S01-PLAN.md # Rendered slice plan projection S01-SUMMARY.md # Slice summary with frontmatter - tasks/T01-PLAN.md # Individual task spec + tasks/T01-PLAN.md # Rendered task plan projection ``` -State is derived from files on disk — checkboxes in ROADMAP.md are the source of truth for completion. +Use `sf headless query` or the SF DB-backed tools to inspect and mutate state. +Do not treat roadmap checkboxes as authoritative for completion when the DB is +available. ## All Headless Commands