@pmelab/gtd 65.0.0 → 66.0.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.
Files changed (63) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +3 -2
  3. package/claude/hooks/drive.ts +10 -1
  4. package/claude/hooks/git.ts +2 -2
  5. package/claude/hooks/pr.ts +80 -20
  6. package/claude/hooks/register.tsx +19 -4
  7. package/dist/chunks/{flows-DGs0W3HI.mjs → flows-CmQplvh1.mjs} +134 -40
  8. package/dist/chunks/{steering-DEh_MS40.mjs → steering-CMrePUiY.mjs} +115 -17
  9. package/dist/chunks/{testkit-CgrLmpdJ.mjs → testkit-BJhAAVGt.mjs} +179 -34
  10. package/dist/chunks/{wire-BNS8ciNu.mjs → wire-xTbzFc26.mjs} +7 -3
  11. package/dist/flows.mjs +1 -1
  12. package/dist/gtd.bundle.mjs +430 -100
  13. package/dist/gtd.core.mjs +147 -134
  14. package/dist/steering.mjs +1 -1
  15. package/dist/testkit/gherkin.mjs +1 -1
  16. package/dist/testkit.mjs +2 -2
  17. package/dist/web/main.css +1 -1
  18. package/dist/web/main.js +185 -18
  19. package/dist/wire.mjs +1 -1
  20. package/dist/workflow-kit.mjs +9 -3
  21. package/package.json +1 -1
  22. package/src/cli/Cli.ts +1 -1
  23. package/src/cli/Program.ts +18 -2
  24. package/src/edge/Edge.ts +93 -11
  25. package/src/flows/helpers.ts +36 -18
  26. package/src/flows/index.test.ts +3 -0
  27. package/src/flows/index.ts +5 -0
  28. package/src/flows/runtime.ts +123 -16
  29. package/src/install/Install.ts +9 -2
  30. package/src/platform/Git.test.ts +7 -11
  31. package/src/platform/NodeSync.ts +2 -0
  32. package/src/platform/SyncIo.ts +2 -0
  33. package/src/platform/Workspace.test.ts +37 -0
  34. package/src/platform/Workspace.ts +36 -0
  35. package/src/replay/Replay.ts +33 -1
  36. package/src/replay/Trailers.ts +3 -0
  37. package/src/replay/Tree.ts +5 -0
  38. package/src/steering/FORMATS.md +3 -1
  39. package/src/steering/SteeringFormat.ts +13 -0
  40. package/src/steering/fixtures/review/sectioned.md +1 -1
  41. package/src/steering/fixtures/review/sectioned.read.json +0 -1
  42. package/src/steering/index.ts +1 -0
  43. package/src/steering/review.test.ts +181 -5
  44. package/src/steering/review.ts +160 -18
  45. package/src/step/RepoSnapshot.ts +4 -0
  46. package/src/step/planStep.ts +6 -6
  47. package/src/twins/EmittedScriptRecognizer.ts +1 -1
  48. package/src/twins/InMemRepo.ts +24 -3
  49. package/src/twins/Layers.ts +1 -0
  50. package/src/twins/SyncIo.test.ts +19 -4
  51. package/src/twins/SyncIo.ts +13 -1
  52. package/src/wire/BeatDocument.test.ts +15 -0
  53. package/src/wire/BeatDocument.ts +8 -2
  54. package/src/wire/Demand.ts +4 -2
  55. package/src/wire/golden/1/beat-capture.json +1 -0
  56. package/src/wire/golden/1/beat-message.json +1 -0
  57. package/src/wire/golden/1/beat-prompt.json +1 -0
  58. package/src/wire/golden/1/beat-script.json +1 -0
  59. package/src/wire/golden/1/beat-stalled.json +1 -0
  60. package/src/workflow/Workflow.ts +2 -0
  61. package/src/workflow-kit/index.test.ts +1 -0
  62. package/src/workflow-kit/index.ts +1 -0
  63. package/src/workflow-kit/scenarios.ts +8 -0
@@ -33,6 +33,7 @@ const dispatchFieldsOf = (
33
33
  ): {
34
34
  readonly session: DemandSession | undefined
35
35
  readonly validate: string | undefined
36
+ readonly fresh: string | null
36
37
  readonly skills: readonly string[] | undefined
37
38
  readonly access: StepAccess | undefined
38
39
  readonly pin: PinMark | undefined
@@ -40,6 +41,7 @@ const dispatchFieldsOf = (
40
41
  const none = {
41
42
  session: undefined,
42
43
  validate: undefined,
44
+ fresh: null,
43
45
  skills: undefined,
44
46
  access: undefined,
45
47
  pin: undefined,
@@ -50,6 +52,7 @@ const dispatchFieldsOf = (
50
52
  ...none,
51
53
  session: demand.session,
52
54
  validate: demand.validate,
55
+ fresh: demand.fresh,
53
56
  skills: demand.skills,
54
57
  access: demand.access,
55
58
  }
@@ -75,6 +78,8 @@ export interface BeatDocument {
75
78
  readonly model: string | undefined
76
79
  readonly system: string | undefined
77
80
  readonly validate: string | undefined
81
+ /** A validator retry with `session.resume`: the original prompt plus the correction, for a driver that starts a new session. `null` otherwise. */
82
+ readonly fresh: string | null
78
83
  /** On a pin or unpin beat only: what its script does to `.gtd/process.gtdpin`. */
79
84
  readonly pin: PinMark | undefined
80
85
  readonly log: string
@@ -96,13 +101,13 @@ export interface BeatDocument {
96
101
  }
97
102
 
98
103
  /**
99
- * Flatten a `Demand` plus its `BeatStatus` into the single 26-key document
104
+ * Flatten a `Demand` plus its `BeatStatus` into the single 27-key document
100
105
  * `gtd next --json` emits — the ONLY place the two are joined. `cost`/
101
106
  * `costByModel` are omitted together, exactly when no cost was recorded
102
107
  * (`cost <= 0`).
103
108
  */
104
109
  export const beatDocument = (demand: Demand, status: BeatStatus): BeatDocument => {
105
- const { session, validate, skills, access, pin } = dispatchFieldsOf(demand)
110
+ const { session, validate, fresh, skills, access, pin } = dispatchFieldsOf(demand)
106
111
  const hasCost = status.cost > 0
107
112
  return {
108
113
  schema: WIRE_SCHEMA,
@@ -114,6 +119,7 @@ export const beatDocument = (demand: Demand, status: BeatStatus): BeatDocument =
114
119
  model: status.model,
115
120
  system: status.system,
116
121
  validate,
122
+ fresh,
117
123
  pin,
118
124
  log: status.log,
119
125
  state: status.state,
@@ -96,6 +96,7 @@ export type Demand =
96
96
  readonly content: string
97
97
  readonly session: DemandSession | undefined
98
98
  readonly validate: string | undefined
99
+ readonly fresh: string | null
99
100
  readonly skills: readonly string[] | undefined
100
101
  readonly access: StepAccess
101
102
  }
@@ -115,8 +116,9 @@ export const demandOf = (input: {
115
116
  readonly kind: BeatKind
116
117
  readonly session?: DemandSession
117
118
  readonly validate?: string
119
+ readonly fresh?: string | null
118
120
  }): Demand => {
119
- const { rendered, kind, session, validate } = input
121
+ const { rendered, kind, session, validate, fresh = null } = input
120
122
  const content =
121
123
  kind === "stalled"
122
124
  ? stallDiagnosis(rendered.state, rendered.actor, rendered.failure, rendered.blocked)
@@ -124,7 +126,7 @@ export const demandOf = (input: {
124
126
  const skills =
125
127
  rendered.skills !== undefined && rendered.skills.length > 0 ? rendered.skills : undefined
126
128
  const access = rendered.access ?? { read: null, write: null }
127
- if (kind === "prompt") return { kind, content, session, validate, skills, access }
129
+ if (kind === "prompt") return { kind, content, session, validate, fresh, skills, access }
128
130
  if (kind === "script" && rendered.pin !== undefined) return { kind, content, pin: rendered.pin }
129
131
  return { kind, content }
130
132
  }
@@ -6,6 +6,7 @@
6
6
  "initial": false,
7
7
  "model": "opus",
8
8
  "system": "You are a careful senior engineer.",
9
+ "fresh": null,
9
10
  "log": ".git/gtd-loop.log",
10
11
  "state": "build.fixing",
11
12
  "actor": "agent",
@@ -6,6 +6,7 @@
6
6
  "initial": false,
7
7
  "model": "opus",
8
8
  "system": "You are a careful senior engineer.",
9
+ "fresh": null,
9
10
  "log": ".git/gtd-loop.log",
10
11
  "state": "build.fixing",
11
12
  "actor": "agent",
@@ -11,6 +11,7 @@
11
11
  "model": "opus",
12
12
  "system": "You are a careful senior engineer.",
13
13
  "validate": "gtd check qa 'TODO.md'",
14
+ "fresh": null,
14
15
  "log": ".git/gtd-loop.log",
15
16
  "state": "build.fixing",
16
17
  "actor": "agent",
@@ -6,6 +6,7 @@
6
6
  "initial": false,
7
7
  "model": "opus",
8
8
  "system": "You are a careful senior engineer.",
9
+ "fresh": null,
9
10
  "log": ".git/gtd-loop.log",
10
11
  "state": "build.fixing",
11
12
  "actor": "agent",
@@ -6,6 +6,7 @@
6
6
  "initial": false,
7
7
  "model": "opus",
8
8
  "system": "You are a careful senior engineer.",
9
+ "fresh": null,
9
10
  "log": ".git/gtd-loop.log",
10
11
  "state": "build.fixing",
11
12
  "actor": "agent",
@@ -42,6 +42,8 @@ export interface StepDef {
42
42
  readonly requireRevert?: boolean
43
43
  readonly allowEmpty?: boolean
44
44
  readonly keep?: boolean
45
+ /** A validator retry: the step's original prompt, `content` being the correction. */
46
+ readonly original?: string
45
47
  readonly onFail?: "rest"
46
48
  readonly acceptClean?: boolean
47
49
  readonly approve?: boolean
@@ -5,6 +5,7 @@ describe("@pmelab/gtd/workflow-kit", () => {
5
5
  it("publishes its helpers by name", () => {
6
6
  expect(Object.keys(kit).sort()).toEqual(
7
7
  [
8
+ "assertionErrors",
8
9
  "assertionLines",
9
10
  "escalation",
10
11
  "fixFindings",
@@ -23,6 +23,7 @@ export {
23
23
  } from "./quality.js"
24
24
  export {
25
25
  assertionLines,
26
+ assertionErrors,
26
27
  removedAssertions,
27
28
  type Assertion,
28
29
  type RemovedAssertions,
@@ -68,3 +68,11 @@ export const removedAssertions = (changes: readonly Change[]): readonly RemovedA
68
68
  return { path: c.path, lines }
69
69
  })
70
70
  .filter((r) => r.lines.length > 0)
71
+
72
+ /** Validator form of `removedAssertions`: lists each lost line by path and scenario, or `undefined` when none. */
73
+ export const assertionErrors = (changes: readonly Change[]): string | undefined => {
74
+ const lost = removedAssertions(changes)
75
+ if (lost.length === 0) return undefined
76
+ const lines = lost.flatMap((r) => r.lines.map((a) => ` - ${r.path}: ${a.scenario}: ${a.line}`))
77
+ return `Restore the assertions these .feature files lost:\n${lines.join("\n")}`
78
+ }