sf snapshot: pre-dispatch, uncommitted changes after 30m inactivity
This commit is contained in:
parent
e58e138457
commit
7e8e3aa846
10 changed files with 132 additions and 5 deletions
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"lastFullVacuumAt": "2026-05-09T23:40:22.903Z"
|
||||
"lastFullVacuumAt": "2026-05-10T05:57:58.807Z"
|
||||
}
|
||||
|
|
|
|||
BIN
.sf/backups/db/sf.db.2026-05-10T05-57-58-732Z
Normal file
BIN
.sf/backups/db/sf.db.2026-05-10T05-57-58-732Z
Normal file
Binary file not shown.
BIN
.sf/backups/db/sf.db.2026-05-10T07-05-24-192Z
Normal file
BIN
.sf/backups/db/sf.db.2026-05-10T07-05-24-192Z
Normal file
Binary file not shown.
BIN
.sf/metrics.db
BIN
.sf/metrics.db
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -9,6 +9,16 @@
|
|||
"lastUsed": "2026-05-08T13:36:05.865Z",
|
||||
"successRate": 1,
|
||||
"total": 4
|
||||
},
|
||||
"minimax/MiniMax-M2.7": {
|
||||
"successes": 1,
|
||||
"failures": 0,
|
||||
"timeouts": 0,
|
||||
"totalTokens": 1101124,
|
||||
"totalCost": 0.6158798199999999,
|
||||
"lastUsed": "2026-05-10T07:19:50.702Z",
|
||||
"successRate": 1,
|
||||
"total": 1
|
||||
}
|
||||
},
|
||||
"plan-slice": {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,15 @@ Then:
|
|||
6. If this slice produced evidence that a requirement changed status (Active → Validated, Active → Deferred, etc.), call `update_requirement` with the requirement ID, updated `status`, and `validation` evidence. Do NOT write `.sf/REQUIREMENTS.md` directly — the engine renders it from the database.
|
||||
7. Prepare the slice completion content you will pass to `complete_slice` using the camelCase fields `milestoneId`, `sliceId`, `sliceTitle`, `oneLiner`, `narrative`, `verification`, and `uatContent`. Do **not** manually write `{{sliceSummaryPath}}`. Do **not** manually write `{{sliceUatPath}}` — the DB-backed tool is the canonical write path for both artifacts.
|
||||
8. Draft the UAT content you will pass as `uatContent` — a concrete UAT script with real test cases derived from the slice plan and task summaries. Include preconditions, numbered steps with expected outcomes, and edge cases. This must NOT be a placeholder or generic template — tailor every test case to what this slice actually built.
|
||||
|
||||
**Whenever the slice uses a mode other than `artifact-driven`, start `uatContent` with a parsable `## UAT Type` section** (`extractUatType` must recognise the bullet). Typical shape:
|
||||
```
|
||||
## UAT Type
|
||||
|
||||
- UAT mode: artifact-driven | browser-executable | runtime-executable | live-runtime | human-experience | mixed
|
||||
- Why this mode is sufficient: <one sentence>
|
||||
```
|
||||
The mode determines how the run-uat agent executes checks. For slices verified only by build commands, grep checks, and automated tests you may omit this block — `complete_slice` then injects a default `artifact-driven` section ahead of your body so parsers still classify the artifact.
|
||||
9. Review task summaries for `key_decisions`. Append any significant decisions to `.sf/DECISIONS.md` if missing.
|
||||
10. Review task summaries for patterns, gotchas, or non-obvious lessons learned. If any would save future agents from repeating investigation or hitting the same issues, append them to `.sf/KNOWLEDGE.md`. Only add entries that are genuinely useful — don't pad with obvious observations.
|
||||
10b. Scan task summaries and the slice's activity log for sf-internal anomalies that the per-task agents may not have reported individually — repeated `Git stage failed`, `Verification failed … advisory`, `Safety: N unexpected file change(s)`, brittle gate predicates, etc. For any genuine sf-the-tool defect that surfaced during this slice but was NOT already filed via `report_issue`, file it now via `report_issue` with appropriate severity. This is the slice-level sweep — task-level agents file individual reports during execution; the slice-close agent catches systemic issues only visible across multiple tasks.
|
||||
|
|
|
|||
|
|
@ -82,3 +82,92 @@ test("handleCompleteSlice_when_successful_records_completion_summary_evidence",
|
|||
assert.match(trail[0].content, /Slice finished with evidence/);
|
||||
assert.match(trail[0].content, /Keep slice evidence in DB/);
|
||||
});
|
||||
|
||||
test("handleCompleteSlice_writes_uat_with_UAT_Type_header_so_downstream_can_parse_mode", async () => {
|
||||
const project = makeProject();
|
||||
const uatPath = join(
|
||||
project,
|
||||
".sf",
|
||||
"milestones",
|
||||
"M001",
|
||||
"slices",
|
||||
"S01",
|
||||
"S01-UAT.md",
|
||||
);
|
||||
|
||||
const result = await handleCompleteSlice(
|
||||
{
|
||||
milestoneId: "M001",
|
||||
sliceId: "S01",
|
||||
sliceTitle: "Slice",
|
||||
verification: "Verification passed.",
|
||||
uatContent: "UAT passed.",
|
||||
oneLiner: "Slice finished with evidence",
|
||||
narrative: "All tasks are closed.",
|
||||
keyDecisions: [],
|
||||
keyFiles: [],
|
||||
},
|
||||
project,
|
||||
);
|
||||
|
||||
assert.equal(result.error, undefined);
|
||||
assert.equal(result.uatPath, uatPath);
|
||||
const { readFileSync } = await import("node:fs");
|
||||
const diskUat = readFileSync(uatPath, "utf8");
|
||||
assert.match(
|
||||
diskUat,
|
||||
/^## UAT Type$/m,
|
||||
"UAT.md must contain ## UAT Type section",
|
||||
);
|
||||
assert.match(diskUat, /- UAT mode: artifact-driven/m);
|
||||
assert.match(diskUat, /## UAT Type\n\n- UAT mode: artifact-driven/m);
|
||||
const { extractUatType } = await import("../files.js");
|
||||
assert.equal(extractUatType(diskUat), "artifact-driven");
|
||||
});
|
||||
|
||||
test("handleCompleteSlice_when_uat_declares_non_artifact_mode_does_not_duplicate_UAT_Type", async () => {
|
||||
const project = makeProject();
|
||||
const uatPath = join(
|
||||
project,
|
||||
".sf",
|
||||
"milestones",
|
||||
"M001",
|
||||
"slices",
|
||||
"S01",
|
||||
"S01-UAT.md",
|
||||
);
|
||||
const agentBody = `## UAT Type
|
||||
|
||||
- UAT mode: browser-executable
|
||||
- Why this mode is sufficient: Playwright validates the UX flow end-to-end
|
||||
|
||||
## Steps
|
||||
|
||||
1. Run the headed Playwright smoke and confirm the dashboard renders.`;
|
||||
|
||||
const result = await handleCompleteSlice(
|
||||
{
|
||||
milestoneId: "M001",
|
||||
sliceId: "S01",
|
||||
sliceTitle: "Slice",
|
||||
verification: "Verification passed.",
|
||||
uatContent: agentBody,
|
||||
oneLiner: "Slice finished with evidence",
|
||||
narrative: "All tasks are closed.",
|
||||
keyDecisions: [],
|
||||
keyFiles: [],
|
||||
},
|
||||
project,
|
||||
);
|
||||
|
||||
assert.equal(result.error, undefined);
|
||||
const { readFileSync } = await import("node:fs");
|
||||
const diskUat = readFileSync(uatPath, "utf8");
|
||||
assert.equal(
|
||||
(diskUat.match(/^## UAT Type$/gm) ?? []).length,
|
||||
1,
|
||||
"handler must not prepend a second ## UAT Type when the agent already declared one",
|
||||
);
|
||||
const { extractUatType } = await import("../files.js");
|
||||
assert.equal(extractUatType(diskUat), "browser-executable");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
import { promises as fs, constants as fsConstants, mkdirSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
import { atomicWriteAsync } from "../atomic-write.js";
|
||||
import { clearParseCache } from "../files.js";
|
||||
import { clearParseCache, extractUatType } from "../files.js";
|
||||
import { getGatesForTurn } from "../gate-registry.js";
|
||||
import { renderRoadmapCheckboxes } from "../markdown-renderer.js";
|
||||
import { clearPathCache, resolveSlicePath } from "../paths.js";
|
||||
|
|
@ -341,15 +341,34 @@ ${filesMod}
|
|||
}
|
||||
/**
|
||||
* Render UAT markdown matching the template format.
|
||||
*
|
||||
* When `uatContent` already contains a parsable `## UAT Type` block (validated
|
||||
* via `extractUatType`), the handler does **not** inject a second section so
|
||||
* the agent-chosen mode is what downstream tools observe. Otherwise the
|
||||
* handler prepends canonical `artifact-driven` defaults before `uatContent`.
|
||||
*
|
||||
* Purpose: preserve single-source truth for run-uat / verdict parsers without
|
||||
* forcing every caller to duplicate boilerplate when `artifact-driven` applies.
|
||||
*/
|
||||
function renderUatMarkdown(params) {
|
||||
const now = new Date().toISOString();
|
||||
const uatBodyRaw =
|
||||
typeof params.uatContent === "string" ? params.uatContent : "";
|
||||
const injectDefaultType = extractUatType(uatBodyRaw.trim()) === undefined;
|
||||
const typePreface = injectDefaultType
|
||||
? `## UAT Type
|
||||
|
||||
- UAT mode: artifact-driven
|
||||
- Why this mode is sufficient: automated build + test verification
|
||||
|
||||
`
|
||||
: "";
|
||||
return `# ${params.sliceId}: ${params.sliceTitle} — UAT
|
||||
|
||||
**Milestone:** ${params.milestoneId}
|
||||
**Written:** ${new Date().toISOString()}
|
||||
**Written:** ${now}
|
||||
|
||||
${params.uatContent}
|
||||
`;
|
||||
${typePreface}${uatBodyRaw}`;
|
||||
}
|
||||
/**
|
||||
* Handle the complete_slice operation end-to-end.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue