@pmelab/gtd 2.8.0 → 3.1.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.
- package/README.md +61 -40
- package/bin/gtd-loop +43 -23
- package/dist/gtd.bundle.mjs +2511 -3481
- package/docs/cli.md +222 -281
- package/docs/configuration.md +350 -71
- package/docs/design/configurable-state-machine.md +1439 -0
- package/docs/design/delta-purity-plan.md +283 -0
- package/docs/design/pattern-machine-plan.md +164 -0
- package/docs/design/scripted-actor.md +289 -0
- package/docs/design/state-file-association.md +112 -0
- package/docs/design/state-machine-validation.md +437 -0
- package/docs/design/steering-file-loops.md +164 -0
- package/docs/design/work-packages.md +194 -0
- package/docs/development.md +55 -36
- package/docs/examples/advanced-workflow.md +428 -0
- package/docs/loop.md +60 -60
- package/docs/upgrading.md +125 -0
- package/package.json +2 -2
- package/schema.json +89 -65
- package/docs/upgrading-from-v1.md +0 -45
- package/docs/workflow.md +0 -428
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Upgrading to v3 (BREAKING CHANGE)
|
|
2
|
+
|
|
3
|
+
## v3 is a clean break
|
|
4
|
+
|
|
5
|
+
v3 ("the pattern machine") deletes the entire v2 definition model — gates, guard
|
|
6
|
+
functions, actor kinds, interrupt/fallback ladders, capture rules, turn and
|
|
7
|
+
routing rules, `Gtd-Counters` trailers, conflicts, the review checkout window —
|
|
8
|
+
and replaces it with the much smaller pattern machine described in
|
|
9
|
+
[STATES.md](../STATES.md): named states, each with one content kind and an
|
|
10
|
+
ordered map of change-patterns to next states.
|
|
11
|
+
|
|
12
|
+
**All pre-v3 history is unrecognized and resolves to the initial state, by
|
|
13
|
+
design — v1 and v2 subjects alike.** v3's `resolveState`
|
|
14
|
+
(`src/PatternMachine.ts`) only recognizes its own `gtd(<actor>): <state>`
|
|
15
|
+
subject naming a state and an actor the active workflow currently declares;
|
|
16
|
+
everything else — a v1 `gtd: grilling`, a v2 `gtd(agent): building`, a plain
|
|
17
|
+
`chore: …` commit, anything from a foreign repo — parses as unrecognized and
|
|
18
|
+
lands at the workflow's initial state (`idle` in the bundled default). There is
|
|
19
|
+
no special-casing for "this looks like an old gtd commit": the mechanism is
|
|
20
|
+
exactly the same one that already made v1 history inert to v2, and v2 history
|
|
21
|
+
inert to a differently-configured workflow — v3 just applies it uniformly to all
|
|
22
|
+
prior history instead of drawing a v1/v2 line.
|
|
23
|
+
|
|
24
|
+
**Finish or squash any in-flight cycle first.** A repo mid-cycle on v1 or v2 has
|
|
25
|
+
no v3-shaped commit backing its steering files; upgrading mid-cycle lands cold
|
|
26
|
+
at the initial state with those files still pending, which the new workflow's
|
|
27
|
+
`on` patterns will classify as ordinary pending changes, not as the phase they
|
|
28
|
+
used to represent. Finish the cycle (or squash it) on the old binary first, or
|
|
29
|
+
manually clean up the steering files and commit a plain boundary, before
|
|
30
|
+
upgrading.
|
|
31
|
+
|
|
32
|
+
## What died
|
|
33
|
+
|
|
34
|
+
- **Counters.** `Gtd-Counters` trailers, `fixAttemptCap`/`reviewThreshold`
|
|
35
|
+
config keys, and the counter-stamp machinery are gone. The only budget
|
|
36
|
+
affordance left is a state's own `retry: { max, otherwise }`
|
|
37
|
+
(§[STATES.md](../STATES.md#7-retry)) — a plain per-process entry cap
|
|
38
|
+
redirected at write time, with no trailer to read back.
|
|
39
|
+
- **`.gtdrc` keys.** The old `testCommand`, `fixAttemptCap`, `reviewThreshold`,
|
|
40
|
+
`agenticReview`, `squash`, `learning`, `decisionLog`, and `models` keys are
|
|
41
|
+
all gone — `workflow:` and `vars:` are the only two blessed keys (see
|
|
42
|
+
[Configuration](configuration.md)). A check's command now lives inline in its
|
|
43
|
+
own `script:` content, reading a workflow-declared `it.vars` entry (the
|
|
44
|
+
bundled default's `testCommand`, overridable via the top-level `vars:` key or
|
|
45
|
+
a `GTD_VAR_testCommand` environment variable — see
|
|
46
|
+
[Configuration's "Variables"](configuration.md#variables)) rather than a
|
|
47
|
+
blessed `testCommand` config key; squashing is a `commit:` state instead of a
|
|
48
|
+
boolean flag; there is no learning phase, no decision log, and no model
|
|
49
|
+
tiering — a workflow author is free to build any of that shape back with
|
|
50
|
+
states of their own, but gtd no longer bakes it in.
|
|
51
|
+
- **The review checkout window.** v2 rewound HEAD/index to the review base while
|
|
52
|
+
a human review rested, so editors would show the diff directly. Deleted in v3
|
|
53
|
+
(may return later as an explicit state property — not in scope now).
|
|
54
|
+
- **`forceApprove`, content-inspection verdicts.** FEEDBACK.md emptiness,
|
|
55
|
+
checkbox-only REVIEW.md diffs, and doc-structure validation are gone as ENGINE
|
|
56
|
+
mechanisms — verdicts are now expressed purely by which file a turn writes or
|
|
57
|
+
deletes, matched by an ordinary `on` pattern. The bundled default workflow's
|
|
58
|
+
own two validation loops (`todo-validating`/`review-validating`/
|
|
59
|
+
`review-deciding` — see
|
|
60
|
+
[docs/design/steering-file-loops.md](design/steering-file-loops.md)) rebuild
|
|
61
|
+
checkbox-review and doc-structure verdicts as ordinary WORKFLOW data
|
|
62
|
+
(`script`-content states + `on` patterns), not an engine hook: `D REVIEW.md` =
|
|
63
|
+
approve, `M REVIEW.md` = route to the deterministic decider, in the bundled
|
|
64
|
+
default.
|
|
65
|
+
- **`gtd questions` / `gtd changesets` / `gtd review <target>`.** Gone; the v3
|
|
66
|
+
command surface is `step` / `next` / `run` / `status` / `format` / `lsp` (see
|
|
67
|
+
[CLI reference](cli.md)).
|
|
68
|
+
- **Model tiers, the decision log.** No `models` config key, no `Gtd-Decisions`
|
|
69
|
+
trailer scan, no grilling/architecting "prior decisions" context assembled
|
|
70
|
+
from history.
|
|
71
|
+
- **LSP.** Deleted at the v3 rewrite, then resurrected file-format-keyed rather
|
|
72
|
+
than state-keyed (`gtd lsp` — see
|
|
73
|
+
[Development](development.md#the-lsp-server)). The `gtd.openSteeringFile`
|
|
74
|
+
command is BACK (see
|
|
75
|
+
[docs/design/state-file-association.md](design/state-file-association.md)) —
|
|
76
|
+
config-driven rather than the v2 hardcoded state→file map: any state may
|
|
77
|
+
declare a `file:` (an Eta template) and a `mode:` (`qa`/`review`, the file's
|
|
78
|
+
format), the LSP reads that same config the CLI does to build its path→mode
|
|
79
|
+
dispatch, and `gtd.openSteeringFile` resolves the current state and shows its
|
|
80
|
+
`file:`. No mapping declared at all (the bundled default's predecessor shape,
|
|
81
|
+
or any workflow with no `file:`/`mode:`) falls back to basename dispatch
|
|
82
|
+
(`TODO.md`/`REVIEW.md`), same as before this addition.
|
|
83
|
+
|
|
84
|
+
## How to adopt
|
|
85
|
+
|
|
86
|
+
`workflow:` is optional — with no config at all, the bundled default workflow
|
|
87
|
+
applies: a single-shot `grilling` plan, direct `building`, a `checking`/`fixing`
|
|
88
|
+
loop, and a direct-diff `await-review` that rests the cycle back at `idle` on
|
|
89
|
+
approval — no squash; every turn commit stays in history for you to squash
|
|
90
|
+
however you like, or not at all (see
|
|
91
|
+
[STATES.md §10](../STATES.md#10-the-bundled-default-workflow)) — expressed as
|
|
92
|
+
data rather than baked into the engine. The fuller grilling ⇄ grilling-answer /
|
|
93
|
+
architecting ⇄ architecting-answer / decompose / picking / reviewing shape v2
|
|
94
|
+
shipped — including a squash finale — is preserved as a copy-paste `.gtdrc`
|
|
95
|
+
example at [docs/examples/advanced-workflow.md](examples/advanced-workflow.md)
|
|
96
|
+
rather than the bundled default. Nothing to do for a repo that's happy with the
|
|
97
|
+
default shape beyond a normal `npm install -g @pmelab/gtd` upgrade, from a
|
|
98
|
+
settled `idle` boundary.
|
|
99
|
+
|
|
100
|
+
A repo that customized v2's `workflow:` key (actors, gates, guard vocabulary,
|
|
101
|
+
ladders) needs to rewrite it from scratch in the v3 schema — see
|
|
102
|
+
[Configuration](configuration.md) for the schema and a complete worked example.
|
|
103
|
+
There is no automatic migration: the two vocabularies don't map field-for-field
|
|
104
|
+
(guards become patterns, capture rules become `on` targets, counters become
|
|
105
|
+
`retry`), so a v2 `workflow:` config is simply invalid v3 config and fails
|
|
106
|
+
loudly at load time rather than silently misinterpreting.
|
|
107
|
+
|
|
108
|
+
**Re-copy the loop skill.** If you vendor `skills/loop/` into a consuming repo
|
|
109
|
+
or agent harness, upgrading the `gtd` binary also means re-copying that skill
|
|
110
|
+
from this release.
|
|
111
|
+
|
|
112
|
+
## Prior breaking change: the v1 → v2 label grammar (historical)
|
|
113
|
+
|
|
114
|
+
v2 replaced v1's undifferentiated turn labels with six labels carrying the
|
|
115
|
+
branch outcome at capture time, moved steering files under `.gtd/`, and
|
|
116
|
+
introduced `Gtd-Counters` commit trailers. All of that is itself now superseded
|
|
117
|
+
by v3's clean break above — a v1 or v2 repo upgrades to v3 the same way
|
|
118
|
+
(finish/squash any in-flight cycle, then upgrade from a settled boundary);
|
|
119
|
+
there's no reason to upgrade v1 → v2 → v3 in two hops.
|
|
120
|
+
|
|
121
|
+
For maintainers: this repo releases via `semantic-release` reading Conventional
|
|
122
|
+
Commits, and needs **no config change** for a major bump — but the release
|
|
123
|
+
commit/PR **must carry a `BREAKING CHANGE:` footer** (or a `!` after the type)
|
|
124
|
+
for `@semantic-release/commit-analyzer` to compute the next major version rather
|
|
125
|
+
than a minor/patch bump.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pmelab/gtd",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Git-aware CLI that emits the next prompt for an autonomous coding agent based on the current repository state",
|
|
6
6
|
"bin": {
|
|
@@ -84,6 +84,6 @@
|
|
|
84
84
|
"eta": "^4.6.0",
|
|
85
85
|
"prettier": "^3.8.1",
|
|
86
86
|
"vscode-languageserver": "^10.1.0",
|
|
87
|
-
"vscode-languageserver-textdocument": "^1.0.
|
|
87
|
+
"vscode-languageserver-textdocument": "^1.0.13"
|
|
88
88
|
}
|
|
89
89
|
}
|
package/schema.json
CHANGED
|
@@ -1,82 +1,106 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$defs": {
|
|
4
|
-
"Int": {
|
|
5
|
-
"type": "integer",
|
|
6
|
-
"description": "an integer",
|
|
7
|
-
"title": "int"
|
|
8
|
-
}
|
|
9
|
-
},
|
|
10
3
|
"type": "object",
|
|
11
4
|
"required": [],
|
|
12
5
|
"properties": {
|
|
13
|
-
"
|
|
14
|
-
"type": "string"
|
|
15
|
-
},
|
|
16
|
-
"models": {
|
|
6
|
+
"workflow": {
|
|
17
7
|
"type": "object",
|
|
18
|
-
"
|
|
8
|
+
"description": "The whole machine definition: named states (plus the workflow's own vars: defaults). Compiled and validated by gtd at load time; content strings starting with ./ or ../ are file references inlined from the config file's directory.",
|
|
9
|
+
"additionalProperties": false,
|
|
10
|
+
"required": ["states"],
|
|
19
11
|
"properties": {
|
|
20
|
-
"
|
|
21
|
-
"type": "
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
"vars": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"description": "Flat name -> scalar map merged into every template's it.vars. Scalars are coerced to strings.",
|
|
15
|
+
"additionalProperties": {
|
|
16
|
+
"type": ["string", "number", "boolean"]
|
|
17
|
+
}
|
|
25
18
|
},
|
|
26
19
|
"states": {
|
|
27
20
|
"type": "object",
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
21
|
+
"description": "The workflow's named states. At least one; exactly one with initial: true.",
|
|
22
|
+
"minProperties": 1,
|
|
23
|
+
"additionalProperties": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"description": "One workflow state. Declare exactly one content kind (script/prompt/message/commit). A commit state is final: no actor, no on.",
|
|
26
|
+
"additionalProperties": false,
|
|
27
|
+
"properties": {
|
|
28
|
+
"actor": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"description": "Who acts at this state. Required on every non-commit state; forbidden on a commit state."
|
|
31
|
+
},
|
|
32
|
+
"script": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"description": "Content kind: a shell script (Eta template). `gtd run` executes it verbatim via bash, then steps this state's actor."
|
|
35
|
+
},
|
|
36
|
+
"prompt": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"description": "Content kind: an agent prompt (Eta template), emitted by `gtd next`."
|
|
39
|
+
},
|
|
40
|
+
"message": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "Content kind: a human-facing message (Eta template), emitted by `gtd next`."
|
|
43
|
+
},
|
|
44
|
+
"commit": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"description": "Content kind: entering this state ends the process by squashing it into one commit with this message (Eta template). Final — no actor, no on."
|
|
47
|
+
},
|
|
48
|
+
"on": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"description": "Ordered map of change pattern -> target state. Patterns: \"C\" (clean tree) or \"<A|M|D|*> <glob>\" over the pending diff; first declared match wins. Every target must name a defined state, and every non-initial state must be reachable through these edges (or a retry.otherwise).",
|
|
51
|
+
"additionalProperties": {
|
|
52
|
+
"type": "string"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"initial": {
|
|
56
|
+
"type": "boolean",
|
|
57
|
+
"description": "Exactly one state in the workflow must declare initial: true (and it must not be a commit state)."
|
|
58
|
+
},
|
|
59
|
+
"retry": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"description": "Redirect transitions INTO this state once it has been entered `max` times in the current process.",
|
|
62
|
+
"additionalProperties": false,
|
|
63
|
+
"required": ["max", "otherwise"],
|
|
64
|
+
"properties": {
|
|
65
|
+
"max": {
|
|
66
|
+
"type": "integer",
|
|
67
|
+
"minimum": 0,
|
|
68
|
+
"description": "Entries allowed this process before redirecting."
|
|
69
|
+
},
|
|
70
|
+
"otherwise": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"description": "Defined state to redirect to once over the cap."
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"model": {
|
|
77
|
+
"type": "string",
|
|
78
|
+
"description": "Opaque harness hint passed through `gtd next --json`/`gtd status --json` (e.g. \"smart\"). Never interpreted by gtd. Forbidden on a commit state."
|
|
79
|
+
},
|
|
80
|
+
"file": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"description": "The state's steering file: an Eta template naming the file a human/editor should look at while the machine rests here. Forbidden on a commit state."
|
|
83
|
+
},
|
|
84
|
+
"mode": {
|
|
85
|
+
"type": "string",
|
|
86
|
+
"enum": ["qa", "review"],
|
|
87
|
+
"description": "The steering file's format, dispatched on by the LSP. Requires a sibling `file:`. Forbidden on a commit state."
|
|
88
|
+
}
|
|
50
89
|
}
|
|
51
|
-
}
|
|
52
|
-
"additionalProperties": false
|
|
90
|
+
}
|
|
53
91
|
}
|
|
54
|
-
}
|
|
55
|
-
"additionalProperties": false
|
|
92
|
+
}
|
|
56
93
|
},
|
|
57
|
-
"
|
|
58
|
-
"type": "
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"learning": {
|
|
64
|
-
"type": "boolean"
|
|
65
|
-
},
|
|
66
|
-
"decisionLog": {
|
|
67
|
-
"type": "boolean"
|
|
68
|
-
},
|
|
69
|
-
"fixAttemptCap": {
|
|
70
|
-
"$ref": "#/$defs/Int",
|
|
71
|
-
"description": "a non-negative number",
|
|
72
|
-
"title": "greaterThanOrEqualTo(0)",
|
|
73
|
-
"minimum": 0
|
|
94
|
+
"vars": {
|
|
95
|
+
"type": "object",
|
|
96
|
+
"description": "Flat name -> scalar map merged into every template's it.vars. Scalars are coerced to strings.",
|
|
97
|
+
"additionalProperties": {
|
|
98
|
+
"type": ["string", "number", "boolean"]
|
|
99
|
+
}
|
|
74
100
|
},
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"description": "
|
|
78
|
-
"title": "greaterThanOrEqualTo(1)",
|
|
79
|
-
"minimum": 1
|
|
101
|
+
"$schema": {
|
|
102
|
+
"type": "string",
|
|
103
|
+
"description": "Editor-only pointer at this schema. Stripped by gtd before validation."
|
|
80
104
|
}
|
|
81
105
|
},
|
|
82
106
|
"additionalProperties": false
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# Upgrading from v1 (BREAKING CHANGE)
|
|
2
|
-
|
|
3
|
-
v2 ships as a **major** semantic-release bump (`2.0.0`) so the binary and the
|
|
4
|
-
loop-driving text ([docs/loop.md](loop.md), `skills/loop/SKILL.md`) can never
|
|
5
|
-
skew against each other. There is **no backward compatibility with the v1
|
|
6
|
-
command surface**: the single mutating `gtd` command, marker/sentinel files, the
|
|
7
|
-
`autoAdvance` JSON field, and the `gtd: transport` handoff commit are all gone.
|
|
8
|
-
`gtd` bare now errors rather than driving a loop; use `gtd step-agent` /
|
|
9
|
-
`gtd next` / `gtd step` instead.
|
|
10
|
-
|
|
11
|
-
**Commit-history compatibility is one-way.** Any repo with v1-taxonomy history
|
|
12
|
-
in it (`gtd: new task`, `gtd: grilling`, `gtd: transport`, a bare
|
|
13
|
-
`gtd: reviewing` with no hash, …) upgrades cleanly: those subjects fall outside
|
|
14
|
-
v2's closed turn/routing grammar and parse as inert **boundary commits** — they
|
|
15
|
-
are never mistaken for v2 workflow state and never error.
|
|
16
|
-
|
|
17
|
-
**Finish or clean up any in-flight v1 cycle first.** If a repo has an
|
|
18
|
-
**in-progress** v1 cycle — steering files present (root-level `TODO.md`,
|
|
19
|
-
`REVIEW.md`, `FEEDBACK.md`, `ERRORS.md`, or `.gtd/`) whose HEAD carries v1-only
|
|
20
|
-
commit subjects — the v2 binary does not know how to resume it: v1 steering
|
|
21
|
-
files have no v2 turn commit backing them, so a cold v2 invocation on that tree
|
|
22
|
-
can land in an unrecognized state. Either finish the v1 cycle to a clean
|
|
23
|
-
boundary with your existing v1 binary before upgrading, or manually clean up
|
|
24
|
-
(remove the steering files / `.gtd/`, commit the result) so the upgrade starts
|
|
25
|
-
from a plain boundary HEAD.
|
|
26
|
-
|
|
27
|
-
**Steering files moved into `.gtd/`.** Earlier v2 builds kept `TODO.md`,
|
|
28
|
-
`REVIEW.md`, `FEEDBACK.md`, `ERRORS.md`, `HEALTH.md`, and `SQUASH_MSG.md` at the
|
|
29
|
-
repository root; they now live under `.gtd/`. Upgrade at a clean boundary (idle,
|
|
30
|
-
post-squash): a repo at rest needs nothing. Mid-cycle repos should either finish
|
|
31
|
-
the cycle on the old build first or move the root-level steering files into
|
|
32
|
-
`.gtd/` by hand and commit. History classification is backward-compatible — the
|
|
33
|
-
counter folds recognize both the old root paths and the new `.gtd/` paths in
|
|
34
|
-
existing commits.
|
|
35
|
-
|
|
36
|
-
**Re-copy the loop skill.** If you vendor `skills/loop/` into a consuming repo
|
|
37
|
-
or agent harness, upgrading the `gtd` binary also means re-copying that skill
|
|
38
|
-
from this release — the v1 skill text still describes the old single-command
|
|
39
|
-
loop and will drive the new binary incorrectly.
|
|
40
|
-
|
|
41
|
-
For maintainers: this repo releases via `semantic-release` reading Conventional
|
|
42
|
-
Commits, and needs **no config change** for a major bump — but the release
|
|
43
|
-
commit/PR **must carry a `BREAKING CHANGE:` footer** (or a `!` after the type)
|
|
44
|
-
for `@semantic-release/commit-analyzer` to compute `2.0.0` rather than a
|
|
45
|
-
minor/patch bump.
|