@pmelab/gtd 29.2.0 → 29.2.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/.claude-plugin/plugin.json +1 -1
- package/claude/hooks/judge.ts +3 -1
- package/dist/flows.mjs +1 -1
- package/dist/gtd.bundle.mjs +4 -2
- package/dist/testkit.mjs +3 -1
- package/dist/workflows.mjs +1 -1
- package/package.json +1 -1
- package/src/cli/Program.ts +3 -1
- package/src/flows/helpers.ts +1 -1
- package/src/replay/judgeDiffShape.test.ts +14 -0
- package/src/workflows/feature/review.test.ts +25 -3
package/claude/hooks/judge.ts
CHANGED
|
@@ -83,7 +83,9 @@ export function judgePrompt(j: Judgment): string {
|
|
|
83
83
|
"Answer every question below from the evidence alone. For each, give your answer and p, your confidence between 0 and 1 that the given answer is correct.",
|
|
84
84
|
]
|
|
85
85
|
for (const [key, body] of Object.entries(j.state ?? {}))
|
|
86
|
-
parts.push(
|
|
86
|
+
parts.push(
|
|
87
|
+
`<evidence key="${key}">\n${body.replace(/<\/evidence/gi, "<\\/evidence")}\n</evidence>`,
|
|
88
|
+
)
|
|
87
89
|
for (const q of j.questions) {
|
|
88
90
|
const levels = levelsOf(q)
|
|
89
91
|
if (typeof levels === "string") throw new Error(levels)
|
package/dist/flows.mjs
CHANGED
|
@@ -370,7 +370,7 @@ const SHAPE_CRITERIA = [
|
|
|
370
370
|
"land: every stray is incidental (a needed import, a test fixture, a renamed call site).",
|
|
371
371
|
"refactor-first: the strays are a seam change the task needs; land them first as a behaviour-neutral refactor.",
|
|
372
372
|
"reject: the naive pass solved the task the wrong way; discard it and re-implement, no human; strays a refactor could absorb never count as reject."
|
|
373
|
-
].join("
|
|
373
|
+
].join(" ");
|
|
374
374
|
const readable = (c) => (c.status === "deleted" || c.after !== void 0) && (c.status === "added" || c.before !== void 0);
|
|
375
375
|
const strayContent = (strays) => strays.map((c) => [
|
|
376
376
|
`=== ${c.path} (${c.status})`,
|
package/dist/gtd.bundle.mjs
CHANGED
|
@@ -22014,7 +22014,7 @@ const SHAPE_CRITERIA = [
|
|
|
22014
22014
|
"land: every stray is incidental (a needed import, a test fixture, a renamed call site).",
|
|
22015
22015
|
"refactor-first: the strays are a seam change the task needs; land them first as a behaviour-neutral refactor.",
|
|
22016
22016
|
"reject: the naive pass solved the task the wrong way; discard it and re-implement, no human; strays a refactor could absorb never count as reject."
|
|
22017
|
-
].join("
|
|
22017
|
+
].join(" ");
|
|
22018
22018
|
const readable = (c) => (c.status === "deleted" || c.after !== void 0) && (c.status === "added" || c.before !== void 0);
|
|
22019
22019
|
const strayContent = (strays) => strays.map((c) => [
|
|
22020
22020
|
`=== ${c.path} (${c.status})`,
|
|
@@ -41238,7 +41238,9 @@ const planLanding = (opts = {}, current) => gen(function* () {
|
|
|
41238
41238
|
* closes or sits inside its own closing message. Writes nothing — no git, no
|
|
41239
41239
|
* state transition, no file. Refuses (throws, mapped to the runtime-error
|
|
41240
41240
|
* exit code) when the workflow declares no `summary` prompt, or the resolved
|
|
41241
|
-
* run has no commits to name.
|
|
41241
|
+
* run has no commits to name. `--slice` also refuses an unknown slice name
|
|
41242
|
+
* (listing the valid slices), a config edited since the process closed, and a
|
|
41243
|
+
* replay that diverged. The edge never narrates: the CLI prints the
|
|
41242
41244
|
* version warning, before the unknown-slice refusal; a replay refusal carries
|
|
41243
41245
|
* it in its message.
|
|
41244
41246
|
*/
|
package/dist/testkit.mjs
CHANGED
|
@@ -10341,7 +10341,9 @@ const planLanding = (opts = {}, current) => gen(function* () {
|
|
|
10341
10341
|
* closes or sits inside its own closing message. Writes nothing — no git, no
|
|
10342
10342
|
* state transition, no file. Refuses (throws, mapped to the runtime-error
|
|
10343
10343
|
* exit code) when the workflow declares no `summary` prompt, or the resolved
|
|
10344
|
-
* run has no commits to name.
|
|
10344
|
+
* run has no commits to name. `--slice` also refuses an unknown slice name
|
|
10345
|
+
* (listing the valid slices), a config edited since the process closed, and a
|
|
10346
|
+
* replay that diverged. The edge never narrates: the CLI prints the
|
|
10345
10347
|
* version warning, before the unknown-slice refusal; a replay refusal carries
|
|
10346
10348
|
* it in its message.
|
|
10347
10349
|
*/
|
package/dist/workflows.mjs
CHANGED
|
@@ -349,7 +349,7 @@ const SHAPE_CRITERIA = [
|
|
|
349
349
|
"land: every stray is incidental (a needed import, a test fixture, a renamed call site).",
|
|
350
350
|
"refactor-first: the strays are a seam change the task needs; land them first as a behaviour-neutral refactor.",
|
|
351
351
|
"reject: the naive pass solved the task the wrong way; discard it and re-implement, no human; strays a refactor could absorb never count as reject."
|
|
352
|
-
].join("
|
|
352
|
+
].join(" ");
|
|
353
353
|
const readable = (c) => (c.status === "deleted" || c.after !== void 0) && (c.status === "added" || c.before !== void 0);
|
|
354
354
|
const strayContent = (strays) => strays.map((c) => [
|
|
355
355
|
`=== ${c.path} (${c.status})`,
|
package/package.json
CHANGED
package/src/cli/Program.ts
CHANGED
|
@@ -326,7 +326,9 @@ const planLanding = (
|
|
|
326
326
|
* closes or sits inside its own closing message. Writes nothing — no git, no
|
|
327
327
|
* state transition, no file. Refuses (throws, mapped to the runtime-error
|
|
328
328
|
* exit code) when the workflow declares no `summary` prompt, or the resolved
|
|
329
|
-
* run has no commits to name.
|
|
329
|
+
* run has no commits to name. `--slice` also refuses an unknown slice name
|
|
330
|
+
* (listing the valid slices), a config edited since the process closed, and a
|
|
331
|
+
* replay that diverged. The edge never narrates: the CLI prints the
|
|
330
332
|
* version warning, before the unknown-slice refusal; a replay refusal carries
|
|
331
333
|
* it in its message.
|
|
332
334
|
*/
|
package/src/flows/helpers.ts
CHANGED
|
@@ -174,7 +174,7 @@ const SHAPE_CRITERIA = [
|
|
|
174
174
|
"land: every stray is incidental (a needed import, a test fixture, a renamed call site).",
|
|
175
175
|
"refactor-first: the strays are a seam change the task needs; land them first as a behaviour-neutral refactor.",
|
|
176
176
|
"reject: the naive pass solved the task the wrong way; discard it and re-implement, no human; strays a refactor could absorb never count as reject.",
|
|
177
|
-
].join("
|
|
177
|
+
].join(" ")
|
|
178
178
|
|
|
179
179
|
const readable = (c: Change): boolean =>
|
|
180
180
|
(c.status === "deleted" || c.after !== undefined) &&
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type JudgeAnswer,
|
|
8
8
|
type StepRequest,
|
|
9
9
|
} from "../flows/index.js"
|
|
10
|
+
import { toRequest } from "../judges/index.js"
|
|
10
11
|
import { globMatches } from "../wire/index.js"
|
|
11
12
|
|
|
12
13
|
// Lives in replay, which wires `matches: globMatches` (flows tests may not import wire).
|
|
@@ -123,6 +124,19 @@ describe("judgeDiffShape", () => {
|
|
|
123
124
|
expect(q?.criteria).toContain("strays a refactor could absorb never count as reject")
|
|
124
125
|
})
|
|
125
126
|
|
|
127
|
+
it("is sendable to jev with exactly its three labels", async () => {
|
|
128
|
+
const requests = install({ shape: { answer: "land", p: 0.9 } })
|
|
129
|
+
await judgeDiffShape("shape", { changes: naive, allow: ["src/**"] })
|
|
130
|
+
const request = requests[0]
|
|
131
|
+
if (request?.kind !== "judge") throw new Error("expected a judge step")
|
|
132
|
+
const { questions } = toRequest(request.questions, {})
|
|
133
|
+
expect(Object.keys((questions["shape"] as { criteria: object }).criteria)).toEqual([
|
|
134
|
+
"land",
|
|
135
|
+
"refactor-first",
|
|
136
|
+
"reject",
|
|
137
|
+
])
|
|
138
|
+
})
|
|
139
|
+
|
|
126
140
|
it("carries exactly four evidence keys, with stray content before/after and in-allowlist paths only", async () => {
|
|
127
141
|
const requests = install({ shape: { answer: "land", p: 0.9 } })
|
|
128
142
|
await judgeDiffShape("shape", { changes: naive, allow: ["src/**"] })
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
installContext,
|
|
4
4
|
type Change,
|
|
5
5
|
type JudgeAnswer,
|
|
6
|
+
type JudgeQuestion,
|
|
6
7
|
type ScopeOptions,
|
|
7
8
|
type StepRequest,
|
|
8
9
|
} from "../../flows/index.js"
|
|
@@ -18,6 +19,7 @@ import type { ReviewNote } from "../../steering/index.js"
|
|
|
18
19
|
import * as bundled from "../index.js"
|
|
19
20
|
import type { BuildTailOptions as PublicBuildTailOptions } from "./index.js"
|
|
20
21
|
import { fixtureContext } from "../shared/text.fixture.js"
|
|
22
|
+
import { toRequest } from "../../judges/index.js"
|
|
21
23
|
|
|
22
24
|
afterEach(() => installContext(undefined))
|
|
23
25
|
|
|
@@ -65,6 +67,7 @@ interface Run {
|
|
|
65
67
|
readonly prompts: Map<string, string>
|
|
66
68
|
readonly result: ReviewOutcome | "stopped"
|
|
67
69
|
readonly judged: readonly string[]
|
|
70
|
+
readonly asked: readonly JudgeQuestion[]
|
|
68
71
|
/** `acceptClean` of each `review.await-review` gate, in order. */
|
|
69
72
|
readonly gates: readonly boolean[]
|
|
70
73
|
readonly gateMessages: readonly string[]
|
|
@@ -78,7 +81,7 @@ interface Run {
|
|
|
78
81
|
const drive = async (d: Drive): Promise<Run> => {
|
|
79
82
|
const log: string[] = []
|
|
80
83
|
const prompts = new Map<string, string>()
|
|
81
|
-
const
|
|
84
|
+
const asked: JudgeQuestion[] = []
|
|
82
85
|
const gates: boolean[] = []
|
|
83
86
|
const gateMessages: string[] = []
|
|
84
87
|
const since: string[] = []
|
|
@@ -113,7 +116,7 @@ const drive = async (d: Drive): Promise<Run> => {
|
|
|
113
116
|
gateMessages.push(request.options.message ?? "")
|
|
114
117
|
}
|
|
115
118
|
if (request.kind === "agent") prompts.set(request.name, request.prompt)
|
|
116
|
-
if (request.kind === "judge")
|
|
119
|
+
if (request.kind === "judge") asked.push(...request.questions)
|
|
117
120
|
}
|
|
118
121
|
const context = fixtureContext(
|
|
119
122
|
{ vars: d.vars ?? {} },
|
|
@@ -159,7 +162,17 @@ const drive = async (d: Drive): Promise<Run> => {
|
|
|
159
162
|
if (!(error instanceof Stop)) throw error
|
|
160
163
|
result = "stopped"
|
|
161
164
|
}
|
|
162
|
-
return {
|
|
165
|
+
return {
|
|
166
|
+
log,
|
|
167
|
+
prompts,
|
|
168
|
+
result,
|
|
169
|
+
judged: asked.map((q) => q.id),
|
|
170
|
+
asked,
|
|
171
|
+
gates,
|
|
172
|
+
gateMessages,
|
|
173
|
+
since,
|
|
174
|
+
scopes,
|
|
175
|
+
}
|
|
163
176
|
}
|
|
164
177
|
|
|
165
178
|
const verdict = (answer: string, p = 0.9): JudgeAnswer => ({ answer, p })
|
|
@@ -418,6 +431,15 @@ describe("review verdict routing", () => {
|
|
|
418
431
|
expect(run.log.filter((n) => n === "review.triage")).toHaveLength(1)
|
|
419
432
|
})
|
|
420
433
|
|
|
434
|
+
it("asks only questions sendable to jev, each choice with its labels", async () => {
|
|
435
|
+
const run = await drive({ notes: ["add — a"] })
|
|
436
|
+
const { questions } = toRequest(run.asked, {})
|
|
437
|
+
const labels = (id: string) => Object.keys((questions[id] as { criteria: object }).criteria)
|
|
438
|
+
expect(labels("note-1")).toEqual(["edit", "question", "nit", "praise"])
|
|
439
|
+
expect(labels("note-1.reversibility")).toEqual(["one-way", "two-way"])
|
|
440
|
+
expect(labels("note-1.blastRadius")).toEqual(["local", "module", "contract"])
|
|
441
|
+
})
|
|
442
|
+
|
|
421
443
|
it("an unanswered note counts as edit", async () => {
|
|
422
444
|
const run = await drive({ notes: ["add — typo", "sub", "mul"], answers: {} })
|
|
423
445
|
expect(run.log).toEqual([
|