@gr8ful/spf 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chains/steps.d.ts +17 -0
- package/dist/chains/steps.js +23 -1
- package/dist/cli/commands/watch.js +12 -0
- package/package.json +1 -1
package/dist/chains/steps.d.ts
CHANGED
|
@@ -316,6 +316,23 @@ export declare function refine(opts?: {
|
|
|
316
316
|
retries?: number;
|
|
317
317
|
extraGates?: string[];
|
|
318
318
|
}): Step;
|
|
319
|
+
/**
|
|
320
|
+
* `cli/commands/watch.ts`'s `runRefine()` reads BOTH `refine_publish.json`
|
|
321
|
+
* and `refine_questions.json` back, unconditionally, after this chain
|
|
322
|
+
* exits — it has no other way to learn what THIS run did, since a chain's
|
|
323
|
+
* return value is just an exit code. A resumed spec (`continue-refinement`)
|
|
324
|
+
* reruns this entire chain from `request` on, into the SAME deterministic
|
|
325
|
+
* `context_handoff_dir` a PRIOR round already wrote into. Without clearing
|
|
326
|
+
* the file this run is NOT about to write, a stale `refine_questions.json`
|
|
327
|
+
* from an earlier escalation round survives a LATER round's successful
|
|
328
|
+
* publish — `runRefine` then reports those old questions as if raised
|
|
329
|
+
* again THIS round, so `runSpec` escalates a second time even though real
|
|
330
|
+
* issues were already created on the tracker seconds earlier. Called
|
|
331
|
+
* before EITHER branch writes, so exactly one of the two files reflects
|
|
332
|
+
* this run when the phase returns, never a leftover from a previous one.
|
|
333
|
+
* `force: true` — a first-ever run has neither file yet, which is fine.
|
|
334
|
+
*/
|
|
335
|
+
export declare function clearStaleRefineOutputFiles(contextHandoffDir: string): void;
|
|
319
336
|
export declare function publishIssues(opts?: {
|
|
320
337
|
description?: string;
|
|
321
338
|
}): Step;
|
package/dist/chains/steps.js
CHANGED
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
* chain is a load-time problem the loader can report against a file and a
|
|
41
41
|
* line, never a phase that blows up ten minutes into an unattended run.
|
|
42
42
|
*/
|
|
43
|
-
import { writeFileSync } from "node:fs";
|
|
43
|
+
import { rmSync, writeFileSync } from "node:fs";
|
|
44
44
|
import path from "node:path";
|
|
45
45
|
import * as changesLib from "../core/changes.js";
|
|
46
46
|
import * as gates from "../core/gates.js";
|
|
@@ -723,6 +723,27 @@ function parsePriorityOption(raw) {
|
|
|
723
723
|
}
|
|
724
724
|
return raw;
|
|
725
725
|
}
|
|
726
|
+
/**
|
|
727
|
+
* `cli/commands/watch.ts`'s `runRefine()` reads BOTH `refine_publish.json`
|
|
728
|
+
* and `refine_questions.json` back, unconditionally, after this chain
|
|
729
|
+
* exits — it has no other way to learn what THIS run did, since a chain's
|
|
730
|
+
* return value is just an exit code. A resumed spec (`continue-refinement`)
|
|
731
|
+
* reruns this entire chain from `request` on, into the SAME deterministic
|
|
732
|
+
* `context_handoff_dir` a PRIOR round already wrote into. Without clearing
|
|
733
|
+
* the file this run is NOT about to write, a stale `refine_questions.json`
|
|
734
|
+
* from an earlier escalation round survives a LATER round's successful
|
|
735
|
+
* publish — `runRefine` then reports those old questions as if raised
|
|
736
|
+
* again THIS round, so `runSpec` escalates a second time even though real
|
|
737
|
+
* issues were already created on the tracker seconds earlier. Called
|
|
738
|
+
* before EITHER branch writes, so exactly one of the two files reflects
|
|
739
|
+
* this run when the phase returns, never a leftover from a previous one.
|
|
740
|
+
* `force: true` — a first-ever run has neither file yet, which is fine.
|
|
741
|
+
*/
|
|
742
|
+
export function clearStaleRefineOutputFiles(contextHandoffDir) {
|
|
743
|
+
for (const name of ["refine_questions.json", "refine_publish.json"]) {
|
|
744
|
+
rmSync(path.join(contextHandoffDir, name), { force: true });
|
|
745
|
+
}
|
|
746
|
+
}
|
|
726
747
|
export function publishIssues(opts = {}) {
|
|
727
748
|
preflightDescription("publish", opts.description);
|
|
728
749
|
const fn = async (run, state) => {
|
|
@@ -738,6 +759,7 @@ export function publishIssues(opts = {}) {
|
|
|
738
759
|
owner: "tracker",
|
|
739
760
|
description: opts.description ?? "Create the feature/story tree on the tracker, in dependency order, and link each to its parent",
|
|
740
761
|
}), async (ph) => {
|
|
762
|
+
clearStaleRefineOutputFiles(run.context_handoff_dir);
|
|
741
763
|
if (questions.length > 0) {
|
|
742
764
|
writeFileSync(path.join(run.context_handoff_dir, "refine_questions.json"), JSON.stringify(questions, null, 2));
|
|
743
765
|
ph.log({ escalated: questions.length });
|
|
@@ -457,6 +457,18 @@ export async function watchCommand(argv) {
|
|
|
457
457
|
catch {
|
|
458
458
|
// best-effort, same as above — no questions file means this run wasn't an escalation
|
|
459
459
|
}
|
|
460
|
+
// Defense in depth: `steps.publishIssues()` now clears whichever of
|
|
461
|
+
// these two files it's NOT about to write, so both should never be
|
|
462
|
+
// non-empty here — but if some future change (or an older worktree's
|
|
463
|
+
// leftover files, before that fix existed) ever produces both, a real
|
|
464
|
+
// completed publish must never be silently overridden by a stale
|
|
465
|
+
// question. `runSpec` checks `questions.length > 0` first, so without
|
|
466
|
+
// this it would re-escalate over issues that already landed on the
|
|
467
|
+
// tracker seconds earlier.
|
|
468
|
+
if (created.length > 0 && questions.length > 0) {
|
|
469
|
+
console.error(`watch: ${opts.adwId}: refine_publish.json AND refine_questions.json both had content — treating the ${created.length} published issue(s) as authoritative and discarding the stale question(s)`);
|
|
470
|
+
questions = [];
|
|
471
|
+
}
|
|
460
472
|
return { accepted: true, adwId: opts.adwId, detail: "", created, questions };
|
|
461
473
|
};
|
|
462
474
|
// `IssueAuthoringProvider`'s read-back half — `isAuthoringProvider()` is a
|