@jwilger/pi-development-system 0.65.0 → 0.66.0
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.
|
@@ -141,7 +141,7 @@ export function createDevelopmentSystem(pi: ExtensionAPI) {
|
|
|
141
141
|
pi.registerTool(createRequestApprovalTool({ pi, approvals }));
|
|
142
142
|
pi.registerTool(createModelsTool());
|
|
143
143
|
pi.registerTool(createRouteTaskTool({ jev: (ctx) => jevHolder.forContext(ctx) }));
|
|
144
|
-
pi.registerTool(createIntakeTool({ state, jev: (ctx) => jevHolder.forContext(ctx) }));
|
|
144
|
+
pi.registerTool(createIntakeTool({ pi, state, jev: (ctx) => jevHolder.forContext(ctx) }));
|
|
145
145
|
pi.registerTool(createTaskCheckTool({ jev: (ctx) => jevHolder.forContext(ctx) }));
|
|
146
146
|
pi.registerTool(createWorkItemTool({ exec }));
|
|
147
147
|
const reviewDeps = {
|
package/package.json
CHANGED
|
@@ -125,10 +125,8 @@ async function reviewNeeds(
|
|
|
125
125
|
ctx: ExtensionContext,
|
|
126
126
|
command: string,
|
|
127
127
|
): Promise<Need[]> {
|
|
128
|
-
const { phase, activeSlice
|
|
128
|
+
const { phase, activeSlice } = deps.state.get();
|
|
129
129
|
if ((phase !== "implementing" && phase !== "reviewing") || activeSlice === undefined) return [];
|
|
130
|
-
// A fix is sized without a review artifact (I8.1); the gate must not demand rounds the size never asked for.
|
|
131
|
-
if (sizing === "fix") return [];
|
|
132
130
|
const snap = await snapshotDiff(deps.exec, ctx.cwd, "HEAD");
|
|
133
131
|
// An unreadable diff cannot be compared; the gate asks for a departure rather than guessing.
|
|
134
132
|
const gap = reviewGap(
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ExtensionAPI,
|
|
3
|
+
ExtensionContext,
|
|
4
|
+
ToolDefinition,
|
|
5
|
+
} from "@earendil-works/pi-coding-agent";
|
|
2
6
|
import { type Static, Type } from "typebox";
|
|
7
|
+
import { parseDeparture } from "../core/departure.ts";
|
|
8
|
+
import { lookupGate } from "../core/gates.ts";
|
|
3
9
|
import { parseSizing } from "../core/sizing.ts";
|
|
4
10
|
import { type DevsysState, isParseError, type Sizing, type SliceRef } from "../core/types.ts";
|
|
11
|
+
import { DEPARTURE_ENTRY_TYPE } from "../gates/record-departure-tool.ts";
|
|
5
12
|
import type { Jev } from "../jev/client.ts";
|
|
6
13
|
import { judgeSizing } from "../jev/questions/sizing.ts";
|
|
14
|
+
import { appendDecision } from "../state/decision-log.ts";
|
|
7
15
|
import type { SessionState } from "../state/session-state.ts";
|
|
8
16
|
import { phaseFor, proposeArtifacts, renderProposal, sliceSlug, uniqueSlice } from "./intake.ts";
|
|
9
17
|
|
|
@@ -35,8 +43,40 @@ const slicesInUse = (state: DevsysState): Set<string> =>
|
|
|
35
43
|
...state.openDepartures.flatMap((d) => (d.scope.kind === "slice" ? [d.scope.slice] : [])),
|
|
36
44
|
]);
|
|
37
45
|
|
|
46
|
+
/**
|
|
47
|
+
* I8.1 sizes a fix without a review artifact, but the commit gate reviews every slice (I6.4). The user choosing
|
|
48
|
+
* "fix" is the explicit agreement, so it is recorded as a user-approved departure for this slice only.
|
|
49
|
+
*/
|
|
50
|
+
async function fixWithoutReview(
|
|
51
|
+
deps: { pi: ExtensionAPI; state: SessionState },
|
|
52
|
+
ctx: ExtensionContext,
|
|
53
|
+
slice: SliceRef,
|
|
54
|
+
): Promise<string | undefined> {
|
|
55
|
+
const info = lookupGate("review.unsatisfied");
|
|
56
|
+
const at = new Date();
|
|
57
|
+
const departure = parseDeparture({
|
|
58
|
+
id: `dep-${at.getTime()}-intake`,
|
|
59
|
+
gate: "review.unsatisfied",
|
|
60
|
+
tier: "soft",
|
|
61
|
+
default: info?.default ?? "finish the fresh-context review before release",
|
|
62
|
+
chosen: "no review rounds for this slice",
|
|
63
|
+
why: "the user confirmed this work as a fix, which the sizing table plans without a review artifact",
|
|
64
|
+
costIfWrong: "a defect in a small change ships without a fresh-context look",
|
|
65
|
+
approver: "user",
|
|
66
|
+
scope: { kind: "slice", slice },
|
|
67
|
+
revisitWhen: "the fix turns out to touch more than its task record named",
|
|
68
|
+
recordedAt: at.toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
69
|
+
});
|
|
70
|
+
if (isParseError(departure)) return undefined;
|
|
71
|
+
const { path } = await appendDecision(ctx.cwd, departure, at);
|
|
72
|
+
deps.pi.appendEntry(DEPARTURE_ENTRY_TYPE, departure);
|
|
73
|
+
deps.state.update((s) => ({ ...s, openDepartures: [...s.openDepartures, departure] }));
|
|
74
|
+
return path;
|
|
75
|
+
}
|
|
76
|
+
|
|
38
77
|
/** `devsys_intake`: Jev proposes a size and artifact set; the user confirms; state moves to the first phase. */
|
|
39
78
|
export function createIntakeTool(deps: {
|
|
79
|
+
pi: ExtensionAPI;
|
|
40
80
|
state: SessionState;
|
|
41
81
|
jev: (ctx: ExtensionContext) => Jev;
|
|
42
82
|
}): ToolDefinition<typeof Parameters> {
|
|
@@ -79,8 +119,13 @@ export function createIntakeTool(deps: {
|
|
|
79
119
|
sizing,
|
|
80
120
|
activeSlice: slice,
|
|
81
121
|
}));
|
|
122
|
+
const departed = sizing === "fix" ? await fixWithoutReview(deps, ctx, slice) : undefined;
|
|
82
123
|
const final = renderProposal({ sizing, basis, proposal: proposalFor(sizing) });
|
|
83
|
-
|
|
124
|
+
const note =
|
|
125
|
+
departed === undefined
|
|
126
|
+
? ""
|
|
127
|
+
: `\nRecorded in ${departed}: no review rounds for this fix (you sized it fix).`;
|
|
128
|
+
return reply(`${final}\nPhase: ${phaseFor(sizing)}. Active slice: ${slice}.${note}`);
|
|
84
129
|
},
|
|
85
130
|
};
|
|
86
131
|
}
|
|
@@ -26,7 +26,7 @@ const SECTIONS = [
|
|
|
26
26
|
] as const;
|
|
27
27
|
type Section = (typeof SECTIONS)[number];
|
|
28
28
|
|
|
29
|
-
const HEADER = /^## (\S+) [—-] (
|
|
29
|
+
const HEADER = /^## (\S+) [—-] (\S.*?)\s*$/;
|
|
30
30
|
const SECTION_LINE = /^\*\*([^:*]+)(?::\*\*|\*\*:)\s*(.*)$/;
|
|
31
31
|
/** Top-level list items only (CommonMark allows up to three spaces of indent); deeper items belong to the step above. */
|
|
32
32
|
const STEP = /^ {0,3}(?:\d+[.)]|[-*+])\s+(.*)$/;
|