vinta-ai-workflows 0.7.0-alpha2 → 0.7.0-alpha21
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/CHANGELOG.md +1704 -3
- package/README.md +16 -0
- package/package.json +11 -9
- package/schemas/README.md +9 -2
- package/schemas/intervention.v1.schema.json +170 -0
- package/schemas/postmortem.v1.schema.json +268 -3
- package/schemas/prs-context-frontmatter.v1.schema.json +18 -7
- package/schemas/vinta-ai-workflows-config.v1.schema.json +12 -0
- package/schemas/workflow.v1.schema.json +388 -24
- package/skills/vinta-analyze-codebase/SKILL.md +1 -0
- package/skills/vinta-bootstrap-ai-tools/SKILL.md +18 -2
- package/skills/vinta-derive-skills/SKILL.md +5 -0
- package/skills/vinta-derive-skills/resources/foundation-skills/open-pr-from-context/scripts/open-pr.sh +0 -0
- package/skills/vinta-derive-skills/resources/foundation-skills/plan-feature/SKILL.md +100 -50
- package/skills/vinta-derive-skills/resources/foundation-skills/plan-feature/resources/ai-models.yaml +9 -10
- package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/SKILL.md +10 -2
- package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/scripts/claude-worktree-write-guard.py +0 -0
- package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/scripts/gen-claude-sandbox-settings.sh +0 -0
- package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/scripts/gen-compose-worktree-override.sh +0 -0
- package/skills/vinta-derive-skills/resources/foundation-skills/prepare-worktree/scripts/sandbox-run.sh +0 -0
- package/skills/vinta-derive-skills/resources/plan-execution/README.md +11 -0
- package/skills/vinta-derive-skills/resources/plan-execution/partials/commit-strategy/stacked.md +18 -0
- package/skills/vinta-derive-skills/resources/plan-execution/partials/dispatched-agent.md +31 -0
- package/skills/vinta-derive-skills/resources/plan-execution/partials/implementer-prompt.md +2 -0
- package/skills/vinta-derive-skills/resources/plan-execution/partials/parallel-lanes.md +10 -3
- package/skills/vinta-derive-skills/resources/plan-execution/partials/pr-context.md +3 -1
- package/skills/vinta-derive-skills/resources/plan-execution/shell/amend-plan-template.md +3 -0
- package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-phase-template.md +3 -0
- package/skills/vinta-derive-skills/resources/plan-execution/shell/implement-plan-template.md +7 -3
- package/skills/vinta-derive-skills/resources/plan-execution/shell/integrate-phase-template.md +2 -1
- package/skills/vinta-derive-skills/resources/plan-execution/shell/review-phase-template.md +3 -0
- package/skills/vinta-derive-skills/resources/prs-context-template.md +5 -4
- package/skills/vinta-install-ai-tools-setup/SKILL.md +28 -1
- package/skills/vinta-install-ai-tools-setup/resources/setup-ai-tools.mjs +0 -0
- package/skills/vinta-sync-ai-tools/SKILL.md +2 -1
- package/skills/vinta-update-project-skills/SKILL.md +1 -0
package/README.md
CHANGED
|
@@ -292,6 +292,22 @@ Land at `ai-tools/agents/<name>.yaml` (canonical YAML; `setup-ai-tools.mjs` emit
|
|
|
292
292
|
|
|
293
293
|
Stack templates may add specialists like `migration-author` (Django) or `deploy-author` (Medplum) — see disclaimer above.
|
|
294
294
|
|
|
295
|
+
### Integrations (external tools)
|
|
296
|
+
|
|
297
|
+
Integrations are external tools that ship their own skill. The bootstrap asks about each one. When one is enabled, the tool's own CLI installs and updates its skill. This package never copies, renders or edits that skill. Enabling one is recorded under `integrations` in `.vinta-ai-workflows.yaml`.
|
|
298
|
+
|
|
299
|
+
| Integration | What it adds |
|
|
300
|
+
|---|---|
|
|
301
|
+
| [`pr-review-canvas`](https://github.com/vintasoftware/pr-review-canvas) | A review canvas for every PR an agent opens: the diff grouped by topic, with attention points for the reviewer, shared as a PR comment and read in a local app (`pr-review serve`). `integrate-phase` runs `/pr-review-canvas <n>` after `open-pr.sh` publishes the PR. `plan-feature` adds an `after_pr` `review-canvas` chore so that [vinta-ai-maestro](packages/vinta-ai-maestro/README.md#chores-about-the-pr--when-after_pr) does the same on each phase PR. Offered only for GitHub / GitLab projects where agents open PRs. |
|
|
302
|
+
|
|
303
|
+
The tool itself is a machine-level install, like `gh`, and the bootstrap never installs it:
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
npm install -g @vintasoftware/pr-review-canvas
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
When the CLI is present, the bootstrap runs `pr-review install-skill --claude-dir ai-tools/skills --codex-dir ai-tools/skills`, so one copy in `ai-tools/skills/pr-review-canvas/` serves every vendor. Refresh it with `pr-review upgrade`, and check it with `pr-review doctor`. Do not edit the copy: the tool stamps a hash into it and reports an edited copy as stale. Sharing and other settings live in the tool's own `pr-review.config.yml`.
|
|
310
|
+
|
|
295
311
|
## Staying in sync with upstream
|
|
296
312
|
|
|
297
313
|
Bootstrap is a snapshot. `vinta-ai-workflows` keeps shipping — new foundation skills, refined templates, sharper agent prompts, schema additions, stack support, best-practice updates lifted from real projects. Pulling those into a previously-bootstrapped repo is a first-class flow, not an afterthought. **This is one of the most important capabilities of the package.**
|
package/package.json
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vinta-ai-workflows",
|
|
3
|
-
"version": "0.7.0-
|
|
3
|
+
"version": "0.7.0-alpha21",
|
|
4
4
|
"description": "Bootstrap AI tooling (AGENTS.md, skills, sub-agents) across Claude Code, Codex, Cursor, and VS Code + GitHub Copilot.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"packageManager": "pnpm@10.33.0",
|
|
7
6
|
"bin": {
|
|
8
7
|
"vinta-ai-workflows": "./vinta-ai-workflows.mjs"
|
|
9
8
|
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"check-ai-models": "node scripts/check-ai-models.mjs",
|
|
12
|
-
"test": "node --test tests/*.test.mjs"
|
|
13
|
-
},
|
|
14
9
|
"devDependencies": {
|
|
10
|
+
"ajv": "^8.20.0",
|
|
11
|
+
"ajv-formats": "^3.0.1",
|
|
15
12
|
"yaml": "^2.4.0"
|
|
16
13
|
},
|
|
17
14
|
"files": [
|
|
@@ -26,10 +23,15 @@
|
|
|
26
23
|
},
|
|
27
24
|
"repository": {
|
|
28
25
|
"type": "git",
|
|
29
|
-
"url": "git+
|
|
26
|
+
"url": "git+https://github.com/vintasoftware/vinta-ai-workflows.git"
|
|
30
27
|
},
|
|
31
28
|
"publishConfig": {
|
|
32
29
|
"access": "public"
|
|
33
30
|
},
|
|
34
|
-
"license": "MIT"
|
|
35
|
-
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"scripts": {
|
|
33
|
+
"check-ai-models": "node scripts/check-ai-models.mjs",
|
|
34
|
+
"validate-schemas": "node scripts/validate-schemas.mjs",
|
|
35
|
+
"test": "node --test tests/*.test.mjs"
|
|
36
|
+
}
|
|
37
|
+
}
|
package/schemas/README.md
CHANGED
|
@@ -12,10 +12,13 @@ JSON Schema (Draft 2020-12) contracts for every YAML or JSON payload the vinta-a
|
|
|
12
12
|
| PR-context inline comments | YAML inside the ` ```yaml ... ``` ` fence under `# Comments` of the same file | [`prs-context-comments.v1.schema.json`](prs-context-comments.v1.schema.json) | `implement-plan` / `amend-plan` | `open-pr.sh` |
|
|
13
13
|
| MCP preflight cache | `.vinta-ai-workflows/cache.yaml` (gitignored — per-developer-machine state) | [`mcp-preflight-cache.v1.schema.json`](mcp-preflight-cache.v1.schema.json) | rendered `systematic-debugging` SKILL.md (writes during Phase 0) | rendered `systematic-debugging` SKILL.md (reads at Phase 0 start) |
|
|
14
14
|
| AI model tier table | `ai-tools/skills/plan-feature/resources/ai-models.yaml` (shipped verbatim with the `plan-feature` foundation skill; source: `skills/vinta-derive-skills/resources/foundation-skills/plan-feature/resources/ai-models.yaml`) | [`ai-models.v1.schema.json`](ai-models.v1.schema.json) | nightly `check-ai-models` job ([`scripts/check-ai-models.mjs`](../scripts/check-ai-models.mjs)) → auto-PR | `plan-feature` (per-phase model suggestion) |
|
|
15
|
-
| Workflow (executable plan) | `ai-plans
|
|
15
|
+
| Workflow (executable plan) | `ai-plans/YYYY-MM-DD-<feature-kebab>.workflow.json` — **JSON, not YAML** | [`workflow.v1.schema.json`](workflow.v1.schema.json) | `plan-feature` (emitted alongside the human-readable plan) | `vinta-ai-maestro` (the orchestrator daemon) |
|
|
16
16
|
| Plan post-mortem | `.vinta-ai-maestro/runs/<run-id>/postmortem.json` — **JSON, not YAML**; per-run state beside the frozen workflow, copied to `ai-plans/<feature-kebab>.postmortem.json` when a team wants it committed | [`postmortem.v1.schema.json`](postmortem.v1.schema.json) | `vinta-ai-maestro` (once a run has ended) | `plan-feature` (when planning the next feature in the same repo) |
|
|
17
|
+
| Monitor intervention | not a file the user writes — the document the run's monitor answers with when a watchdog wakes it, recorded per attempt at `.vinta-ai-maestro/runs/<run-id>/interventions.jsonl` | [`intervention.v1.schema.json`](intervention.v1.schema.json) | the run monitor (`vinta-ai-maestro`, when a phase or a gate crosses a threshold) | `vinta-ai-maestro` (validates it, then applies it through §9's amend path) |
|
|
17
18
|
|
|
18
|
-
>
|
|
19
|
+
> **Three of these are generated, not hand-written**: `workflow.v1`, `postmortem.v1` and `intervention.v1`. The workflow's source of truth is `packages/vinta-ai-maestro/src/types.ts`; regenerate with `pnpm --filter vinta-ai-maestro schema:gen` and verify with `schema:check`. The post-mortem's is `src/postmortem/postmortem.ts` and the intervention's is `src/intervention/intervention.ts`; regenerate either with `pnpm --filter vinta-ai-maestro <postmortem|intervention>:schema:gen`, verify with `:schema:check` — `tests/postmortem.test.ts` and `tests/intervention.test.ts` run those byte comparisons too. Edit the zod schemas, never the JSON.
|
|
20
|
+
>
|
|
21
|
+
> The intervention schema is the odd one: it validates a document a *model* writes rather than one a skill or a person does, and it is the boundary that decides what an unattended run may change about itself. Its verbs are deliberately few and deliberately cannot express a change to what a phase builds — see [`docs/monitor-intervention.md`](../packages/vinta-ai-maestro/docs/monitor-intervention.md).
|
|
19
22
|
|
|
20
23
|
## Versioning rules
|
|
21
24
|
|
|
@@ -87,3 +90,7 @@ done
|
|
|
87
90
|
```
|
|
88
91
|
|
|
89
92
|
For PR-context frontmatter, extract the YAML block first (`yq` does this cleanly), then validate.
|
|
93
|
+
|
|
94
|
+
## Checking the schemas themselves (this repo only)
|
|
95
|
+
|
|
96
|
+
`npm run validate-schemas` (also part of `npm test`) compiles every schema here under Ajv's strict Draft 2020-12 mode and checks the fixtures in `tests/schema-fixtures/<schema-file-stem>/`: everything under `valid/` must pass, everything under `invalid/` must fail. A fixture is `.json`, `.yaml` / `.yml`, or a `.md` file whose YAML frontmatter is the payload. **A conditional rule (`if` / `then`) needs an `invalid/` fixture for each branch it enforces** — a lenient validator ignores a broken one silently. Pass file paths instead to validate payloads against the schema their `$schema` key or `yaml-language-server` comment names.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/vintasoftware/vinta-ai-workflows/schemas/intervention.v1.schema.json",
|
|
4
|
+
"title": "vinta-ai-maestro monitor intervention",
|
|
5
|
+
"description": "The monitor’s proposal for a run it was woken up about: a closed set of execution-tuning verbs, applied by the host through §9’s amend path. It cannot express a change to what a phase builds — only to how the run executes it. Written by the run monitor when a watchdog wakes it about a phase that has outrun its threshold, and applied by `vinta-ai-maestro` through §9’s amend path. GENERATED from packages/vinta-ai-maestro/src/intervention/intervention.ts — edit there, not here. See schemas/README.md for versioning rules.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"$schema": {
|
|
9
|
+
"description": "Optional URL of this schema, for editor validation. Ignored at runtime.",
|
|
10
|
+
"type": "string"
|
|
11
|
+
},
|
|
12
|
+
"schema_version": {
|
|
13
|
+
"type": "number",
|
|
14
|
+
"const": 1,
|
|
15
|
+
"description": "Schema major version. Bumped only on breaking changes."
|
|
16
|
+
},
|
|
17
|
+
"changes": {
|
|
18
|
+
"maxItems": 4,
|
|
19
|
+
"type": "array",
|
|
20
|
+
"items": {
|
|
21
|
+
"oneOf": [
|
|
22
|
+
{
|
|
23
|
+
"type": "object",
|
|
24
|
+
"properties": {
|
|
25
|
+
"verb": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"const": "retune_gate"
|
|
28
|
+
},
|
|
29
|
+
"gate": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"minLength": 1,
|
|
32
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$",
|
|
33
|
+
"description": "The gate id, as the workflow declares it."
|
|
34
|
+
},
|
|
35
|
+
"cmd": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"minLength": 1,
|
|
38
|
+
"description": "The proposed command. Must be the current command’s argv tokens, in order, plus tokens drawn from this gate’s `tuning.allowed_flags`. Nothing may be removed or rewritten."
|
|
39
|
+
},
|
|
40
|
+
"evidence": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"minLength": 1,
|
|
43
|
+
"maxLength": 600,
|
|
44
|
+
"description": "What was observed and where — the gate log line, the durations, the transcript. Unverifiable by construction and required regardless: it is what an intervention is audited and scored against, and a change made with nobody watching that cannot say why it was made is not auditable at all."
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"required": [
|
|
48
|
+
"verb",
|
|
49
|
+
"gate",
|
|
50
|
+
"cmd",
|
|
51
|
+
"evidence"
|
|
52
|
+
],
|
|
53
|
+
"additionalProperties": false
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"type": "object",
|
|
57
|
+
"properties": {
|
|
58
|
+
"verb": {
|
|
59
|
+
"type": "string",
|
|
60
|
+
"const": "retime_gate"
|
|
61
|
+
},
|
|
62
|
+
"gate": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"minLength": 1,
|
|
65
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$"
|
|
66
|
+
},
|
|
67
|
+
"timeout_s": {
|
|
68
|
+
"type": "integer",
|
|
69
|
+
"minimum": 1,
|
|
70
|
+
"maximum": 9007199254740991,
|
|
71
|
+
"description": "The proposed timeout, in seconds."
|
|
72
|
+
},
|
|
73
|
+
"evidence": {
|
|
74
|
+
"type": "string",
|
|
75
|
+
"minLength": 1,
|
|
76
|
+
"maxLength": 600,
|
|
77
|
+
"description": "What was observed and where — the gate log line, the durations, the transcript. Unverifiable by construction and required regardless: it is what an intervention is audited and scored against, and a change made with nobody watching that cannot say why it was made is not auditable at all."
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"required": [
|
|
81
|
+
"verb",
|
|
82
|
+
"gate",
|
|
83
|
+
"timeout_s",
|
|
84
|
+
"evidence"
|
|
85
|
+
],
|
|
86
|
+
"additionalProperties": false
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"type": "object",
|
|
90
|
+
"properties": {
|
|
91
|
+
"verb": {
|
|
92
|
+
"type": "string",
|
|
93
|
+
"const": "rebudget_fixes"
|
|
94
|
+
},
|
|
95
|
+
"node": {
|
|
96
|
+
"type": "string",
|
|
97
|
+
"minLength": 1,
|
|
98
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$",
|
|
99
|
+
"description": "The phase id."
|
|
100
|
+
},
|
|
101
|
+
"max_fix_rounds": {
|
|
102
|
+
"type": "integer",
|
|
103
|
+
"minimum": 0,
|
|
104
|
+
"maximum": 10
|
|
105
|
+
},
|
|
106
|
+
"evidence": {
|
|
107
|
+
"type": "string",
|
|
108
|
+
"minLength": 1,
|
|
109
|
+
"maxLength": 600,
|
|
110
|
+
"description": "What was observed and where — the gate log line, the durations, the transcript. Unverifiable by construction and required regardless: it is what an intervention is audited and scored against, and a change made with nobody watching that cannot say why it was made is not auditable at all."
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"required": [
|
|
114
|
+
"verb",
|
|
115
|
+
"node",
|
|
116
|
+
"max_fix_rounds",
|
|
117
|
+
"evidence"
|
|
118
|
+
],
|
|
119
|
+
"additionalProperties": false
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"type": "object",
|
|
123
|
+
"properties": {
|
|
124
|
+
"verb": {
|
|
125
|
+
"type": "string",
|
|
126
|
+
"const": "retier_phase"
|
|
127
|
+
},
|
|
128
|
+
"node": {
|
|
129
|
+
"type": "string",
|
|
130
|
+
"minLength": 1,
|
|
131
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$"
|
|
132
|
+
},
|
|
133
|
+
"model": {
|
|
134
|
+
"type": "string",
|
|
135
|
+
"minLength": 1,
|
|
136
|
+
"description": "The model to hand this phase. Must already be on the run’s roster — a monitor that could name any string could spend money on a model the plan never staffed, and could name one that does not exist."
|
|
137
|
+
},
|
|
138
|
+
"evidence": {
|
|
139
|
+
"type": "string",
|
|
140
|
+
"minLength": 1,
|
|
141
|
+
"maxLength": 600,
|
|
142
|
+
"description": "What was observed and where — the gate log line, the durations, the transcript. Unverifiable by construction and required regardless: it is what an intervention is audited and scored against, and a change made with nobody watching that cannot say why it was made is not auditable at all."
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"required": [
|
|
146
|
+
"verb",
|
|
147
|
+
"node",
|
|
148
|
+
"model",
|
|
149
|
+
"evidence"
|
|
150
|
+
],
|
|
151
|
+
"additionalProperties": false
|
|
152
|
+
}
|
|
153
|
+
]
|
|
154
|
+
},
|
|
155
|
+
"description": "What to change, or nothing. Empty is a real and expected answer: a phase that is slow because its work is hard needs no amendment, and a monitor that cannot say so will invent one."
|
|
156
|
+
},
|
|
157
|
+
"summary": {
|
|
158
|
+
"type": "string",
|
|
159
|
+
"minLength": 1,
|
|
160
|
+
"maxLength": 1000,
|
|
161
|
+
"description": "One paragraph on what was found, whether or not anything is being changed."
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
"required": [
|
|
165
|
+
"schema_version",
|
|
166
|
+
"changes",
|
|
167
|
+
"summary"
|
|
168
|
+
],
|
|
169
|
+
"additionalProperties": false
|
|
170
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://github.com/vintasoftware/vinta-ai-workflows/schemas/postmortem.v1.schema.json",
|
|
4
4
|
"title": "vinta-ai-maestro plan post-mortem",
|
|
5
|
-
"description": "What one finished `vinta-ai-maestro` run learned about the plan that produced it: dependencies that were never used, dependencies discovered missing, same-wave phases that conflicted,
|
|
5
|
+
"description": "What one finished `vinta-ai-maestro` run learned about the plan that produced it: dependencies that were never used, dependencies discovered missing, same-wave phases that conflicted, phases whose duration diverged from their wave, what each gate cost, and changes the run made to its own plan while it ran. Ids, waves, paths, durations and counts only. Lives at `.vinta-ai-maestro/runs/<run-id>/postmortem.json` in a target project, emitted by `vinta-ai-maestro` after a run ends and read by `plan-feature` when planning the next feature in the same repo. GENERATED from packages/vinta-ai-maestro/src/postmortem/postmortem.ts — edit there, not here. See schemas/README.md for versioning rules.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"properties": {
|
|
8
8
|
"$schema": {
|
|
@@ -203,6 +203,73 @@
|
|
|
203
203
|
"description": "Two same-wave phases that actually fought over the same code at integration."
|
|
204
204
|
}
|
|
205
205
|
},
|
|
206
|
+
"gate_costs": {
|
|
207
|
+
"type": "array",
|
|
208
|
+
"items": {
|
|
209
|
+
"type": "object",
|
|
210
|
+
"properties": {
|
|
211
|
+
"gate": {
|
|
212
|
+
"type": "string",
|
|
213
|
+
"minLength": 1,
|
|
214
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$"
|
|
215
|
+
},
|
|
216
|
+
"runs": {
|
|
217
|
+
"type": "integer",
|
|
218
|
+
"minimum": 1,
|
|
219
|
+
"maximum": 9007199254740991,
|
|
220
|
+
"description": "Verdicts recorded for this gate, over every phase and every fix round."
|
|
221
|
+
},
|
|
222
|
+
"cached_runs": {
|
|
223
|
+
"type": "integer",
|
|
224
|
+
"minimum": 0,
|
|
225
|
+
"maximum": 9007199254740991,
|
|
226
|
+
"description": "Of those, the ones the result cache served. They cost this run nothing."
|
|
227
|
+
},
|
|
228
|
+
"ran_ms": {
|
|
229
|
+
"type": "integer",
|
|
230
|
+
"minimum": 0,
|
|
231
|
+
"maximum": 9007199254740991,
|
|
232
|
+
"description": "Summed runtime of the verdicts the run actually paid for — the runner’s own measurement, not the distance between two journal rows. Cache hits are excluded."
|
|
233
|
+
},
|
|
234
|
+
"slowest_ms": {
|
|
235
|
+
"type": "integer",
|
|
236
|
+
"minimum": 0,
|
|
237
|
+
"maximum": 9007199254740991,
|
|
238
|
+
"description": "The slowest single paid run. What a `timeout_s` is set against."
|
|
239
|
+
},
|
|
240
|
+
"cache_saved_ms": {
|
|
241
|
+
"type": "integer",
|
|
242
|
+
"minimum": 0,
|
|
243
|
+
"maximum": 9007199254740991,
|
|
244
|
+
"description": "Summed runtime of the cache hits: what those gates cost the last time they really ran. An estimate of time saved, never additive with `ran_ms`."
|
|
245
|
+
},
|
|
246
|
+
"failed_runs": {
|
|
247
|
+
"type": "integer",
|
|
248
|
+
"minimum": 0,
|
|
249
|
+
"maximum": 9007199254740991,
|
|
250
|
+
"description": "Verdicts that came back red."
|
|
251
|
+
},
|
|
252
|
+
"timed_out_runs": {
|
|
253
|
+
"type": "integer",
|
|
254
|
+
"minimum": 0,
|
|
255
|
+
"maximum": 9007199254740991,
|
|
256
|
+
"description": "Verdicts killed at `timeout_s`. The most expensive kind of run there is."
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
"required": [
|
|
260
|
+
"gate",
|
|
261
|
+
"runs",
|
|
262
|
+
"cached_runs",
|
|
263
|
+
"ran_ms",
|
|
264
|
+
"slowest_ms",
|
|
265
|
+
"cache_saved_ms",
|
|
266
|
+
"failed_runs",
|
|
267
|
+
"timed_out_runs"
|
|
268
|
+
],
|
|
269
|
+
"additionalProperties": false,
|
|
270
|
+
"description": "What one gate cost the run. A plan is sized against this: the gate that dominates `ran_ms` is the one whose pool capacity decides the wall clock, and a gate whose `runs` far exceeds its phases is a fix loop re-paying for the same suite."
|
|
271
|
+
}
|
|
272
|
+
},
|
|
206
273
|
"duration_divergences": {
|
|
207
274
|
"type": "array",
|
|
208
275
|
"items": {
|
|
@@ -254,13 +321,199 @@
|
|
|
254
321
|
"additionalProperties": false,
|
|
255
322
|
"description": "A phase whose real duration was wildly out of line with its wave placement — which is exactly what makes the next plan’s parallelism estimate wrong."
|
|
256
323
|
}
|
|
324
|
+
},
|
|
325
|
+
"critical_path": {
|
|
326
|
+
"anyOf": [
|
|
327
|
+
{
|
|
328
|
+
"type": "object",
|
|
329
|
+
"properties": {
|
|
330
|
+
"nodes": {
|
|
331
|
+
"type": "array",
|
|
332
|
+
"items": {
|
|
333
|
+
"type": "object",
|
|
334
|
+
"properties": {
|
|
335
|
+
"node": {
|
|
336
|
+
"type": "string",
|
|
337
|
+
"minLength": 1,
|
|
338
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$"
|
|
339
|
+
},
|
|
340
|
+
"wave": {
|
|
341
|
+
"type": "integer",
|
|
342
|
+
"minimum": -9007199254740991,
|
|
343
|
+
"maximum": 9007199254740991
|
|
344
|
+
},
|
|
345
|
+
"span_ms": {
|
|
346
|
+
"type": "integer",
|
|
347
|
+
"minimum": 0,
|
|
348
|
+
"maximum": 9007199254740991
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
"required": [
|
|
352
|
+
"node",
|
|
353
|
+
"wave",
|
|
354
|
+
"span_ms"
|
|
355
|
+
],
|
|
356
|
+
"additionalProperties": false
|
|
357
|
+
},
|
|
358
|
+
"description": "The chain in the order it ran, each with the time it was running."
|
|
359
|
+
},
|
|
360
|
+
"span_ms": {
|
|
361
|
+
"type": "integer",
|
|
362
|
+
"minimum": 0,
|
|
363
|
+
"maximum": 9007199254740991,
|
|
364
|
+
"description": "The chain’s total. The floor this graph could reach."
|
|
365
|
+
},
|
|
366
|
+
"share_of_elapsed": {
|
|
367
|
+
"type": "number",
|
|
368
|
+
"minimum": 0,
|
|
369
|
+
"maximum": 1,
|
|
370
|
+
"description": "How much of the run’s wall clock this one chain accounts for."
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
"required": [
|
|
374
|
+
"nodes",
|
|
375
|
+
"span_ms",
|
|
376
|
+
"share_of_elapsed"
|
|
377
|
+
],
|
|
378
|
+
"additionalProperties": false,
|
|
379
|
+
"description": "The dependency chain that decided how long the run took. A plan is re-drawn against this: shortening any other phase changes nothing, and the only way to a faster run is a shallower graph or a cheaper phase on this list."
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
"type": "null"
|
|
383
|
+
}
|
|
384
|
+
]
|
|
385
|
+
},
|
|
386
|
+
"idle_capacity": {
|
|
387
|
+
"anyOf": [
|
|
388
|
+
{
|
|
389
|
+
"type": "object",
|
|
390
|
+
"properties": {
|
|
391
|
+
"lane_capacity": {
|
|
392
|
+
"type": "integer",
|
|
393
|
+
"minimum": 1,
|
|
394
|
+
"maximum": 9007199254740991,
|
|
395
|
+
"description": "Lanes the plan asked to be provisioned."
|
|
396
|
+
},
|
|
397
|
+
"peak_concurrency": {
|
|
398
|
+
"type": "integer",
|
|
399
|
+
"minimum": 0,
|
|
400
|
+
"maximum": 9007199254740991,
|
|
401
|
+
"description": "The most phases that were ever running at once. The width the graph reached."
|
|
402
|
+
},
|
|
403
|
+
"lane_ms_provisioned": {
|
|
404
|
+
"type": "integer",
|
|
405
|
+
"minimum": 0,
|
|
406
|
+
"maximum": 9007199254740991
|
|
407
|
+
},
|
|
408
|
+
"lane_ms_used": {
|
|
409
|
+
"type": "integer",
|
|
410
|
+
"minimum": 0,
|
|
411
|
+
"maximum": 9007199254740991,
|
|
412
|
+
"description": "Summed phase spans — one lane each, while running."
|
|
413
|
+
},
|
|
414
|
+
"idle_share": {
|
|
415
|
+
"type": "number",
|
|
416
|
+
"minimum": 0,
|
|
417
|
+
"maximum": 1,
|
|
418
|
+
"description": "Provisioned lane time no phase occupied."
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
"required": [
|
|
422
|
+
"lane_capacity",
|
|
423
|
+
"peak_concurrency",
|
|
424
|
+
"lane_ms_provisioned",
|
|
425
|
+
"lane_ms_used",
|
|
426
|
+
"idle_share"
|
|
427
|
+
],
|
|
428
|
+
"additionalProperties": false,
|
|
429
|
+
"description": "What the run paid for and did not use. A lane is a worktree, a forked database and a desk for a crew member, and a plan that is never as wide as its lane count bought all three for nothing — which is a fact about the graph, not about the machine."
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
"type": "null"
|
|
433
|
+
}
|
|
434
|
+
]
|
|
435
|
+
},
|
|
436
|
+
"interventions": {
|
|
437
|
+
"type": "array",
|
|
438
|
+
"items": {
|
|
439
|
+
"type": "object",
|
|
440
|
+
"properties": {
|
|
441
|
+
"amendment": {
|
|
442
|
+
"type": "integer",
|
|
443
|
+
"minimum": 1,
|
|
444
|
+
"maximum": 9007199254740991,
|
|
445
|
+
"description": "The amendment ordinal, matching the run’s `workflow_amended` history."
|
|
446
|
+
},
|
|
447
|
+
"at_ms": {
|
|
448
|
+
"type": "integer",
|
|
449
|
+
"minimum": -9007199254740991,
|
|
450
|
+
"maximum": 9007199254740991,
|
|
451
|
+
"description": "When the run amended itself."
|
|
452
|
+
},
|
|
453
|
+
"target": {
|
|
454
|
+
"type": "string",
|
|
455
|
+
"minLength": 1,
|
|
456
|
+
"description": "What it changed, as the ledger keys it — `gate:<id>` or `node:<id>`."
|
|
457
|
+
},
|
|
458
|
+
"effect": {
|
|
459
|
+
"type": "string",
|
|
460
|
+
"enum": [
|
|
461
|
+
"cheaper",
|
|
462
|
+
"dearer",
|
|
463
|
+
"unchanged",
|
|
464
|
+
"unmeasured"
|
|
465
|
+
],
|
|
466
|
+
"description": "`cheaper` / `dearer` / `unchanged` compare the gate’s mean uncached duration before and after. `unmeasured` means the journal cannot answer — a phase-level change, or a gate with no runs on one side of the amendment."
|
|
467
|
+
},
|
|
468
|
+
"before_ms": {
|
|
469
|
+
"description": "Mean uncached duration of this gate before the amendment. Absent when unmeasured.",
|
|
470
|
+
"type": "integer",
|
|
471
|
+
"minimum": -9007199254740991,
|
|
472
|
+
"maximum": 9007199254740991
|
|
473
|
+
},
|
|
474
|
+
"after_ms": {
|
|
475
|
+
"description": "The same, after. Absent when unmeasured.",
|
|
476
|
+
"type": "integer",
|
|
477
|
+
"minimum": -9007199254740991,
|
|
478
|
+
"maximum": 9007199254740991
|
|
479
|
+
},
|
|
480
|
+
"runs_before": {
|
|
481
|
+
"type": "integer",
|
|
482
|
+
"minimum": 0,
|
|
483
|
+
"maximum": 9007199254740991
|
|
484
|
+
},
|
|
485
|
+
"runs_after": {
|
|
486
|
+
"type": "integer",
|
|
487
|
+
"minimum": 0,
|
|
488
|
+
"maximum": 9007199254740991
|
|
489
|
+
},
|
|
490
|
+
"record_ref": {
|
|
491
|
+
"type": "string",
|
|
492
|
+
"description": "Where the monitor’s own account of this change lives, relative to the run directory. The reasoning, the evidence and the command are prose about a repository and stay there; this artifact carries what can be counted."
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
"required": [
|
|
496
|
+
"amendment",
|
|
497
|
+
"at_ms",
|
|
498
|
+
"target",
|
|
499
|
+
"effect",
|
|
500
|
+
"record_ref"
|
|
501
|
+
],
|
|
502
|
+
"additionalProperties": false,
|
|
503
|
+
"description": "One amendment a run made to itself, and what happened to the thing it changed. The effect is a comparison of measured durations, not a claim of cause: a gate that got cheaper across an amendment did so while other things were also changing."
|
|
504
|
+
},
|
|
505
|
+
"description": "Amendments the run made to itself (§9.2), oldest first. Empty means it made none, which is a fact rather than a gap: `workflow_amended` rows say who wrote them."
|
|
257
506
|
}
|
|
258
507
|
},
|
|
259
508
|
"required": [
|
|
260
509
|
"unused_dependencies",
|
|
261
510
|
"missing_dependencies",
|
|
262
511
|
"wave_conflicts",
|
|
263
|
-
"
|
|
512
|
+
"gate_costs",
|
|
513
|
+
"duration_divergences",
|
|
514
|
+
"critical_path",
|
|
515
|
+
"idle_capacity",
|
|
516
|
+
"interventions"
|
|
264
517
|
],
|
|
265
518
|
"additionalProperties": false
|
|
266
519
|
},
|
|
@@ -272,9 +525,12 @@
|
|
|
272
525
|
"kind": {
|
|
273
526
|
"type": "string",
|
|
274
527
|
"enum": [
|
|
528
|
+
"blocking_cause_unrecorded",
|
|
275
529
|
"dependency_use_unrecorded",
|
|
276
530
|
"gate_result_unrecorded",
|
|
277
|
-
"
|
|
531
|
+
"gate_durations_unrecorded",
|
|
532
|
+
"integration_record_unavailable",
|
|
533
|
+
"intervention_effect_unmeasured"
|
|
278
534
|
]
|
|
279
535
|
},
|
|
280
536
|
"needs": {
|
|
@@ -316,6 +572,15 @@
|
|
|
316
572
|
"minLength": 1,
|
|
317
573
|
"pattern": "^[a-z0-9][a-z0-9-]*$"
|
|
318
574
|
}
|
|
575
|
+
},
|
|
576
|
+
"gates": {
|
|
577
|
+
"description": "The gates the gap applies to.",
|
|
578
|
+
"type": "array",
|
|
579
|
+
"items": {
|
|
580
|
+
"type": "string",
|
|
581
|
+
"minLength": 1,
|
|
582
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$"
|
|
583
|
+
}
|
|
319
584
|
}
|
|
320
585
|
},
|
|
321
586
|
"required": [
|
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
"schema_version",
|
|
9
9
|
"plan_id",
|
|
10
10
|
"feature_name",
|
|
11
|
-
"phase_id",
|
|
12
|
-
"phase_title",
|
|
13
11
|
"branch",
|
|
14
12
|
"base",
|
|
15
13
|
"created_at",
|
|
@@ -34,24 +32,30 @@
|
|
|
34
32
|
"pattern": "^[A-Z][A-Z0-9_]*$",
|
|
35
33
|
"minLength": 1
|
|
36
34
|
},
|
|
35
|
+
"kind": {
|
|
36
|
+
"description": "Which PR this file opens. `phase` (the default when absent): one phase's branch into its dependency-derived base. `integration`: a multi-dependency phase's `plan/<plan-id>/integ-<phase-id>` branch into the default branch, so the phase PR stacked on it can land. `plan`: the final wave branch (or, under modular commits, the plan branch) into the default branch — the PR that lands the whole plan.",
|
|
37
|
+
"type": "string",
|
|
38
|
+
"enum": ["phase", "integration", "plan"],
|
|
39
|
+
"default": "phase"
|
|
40
|
+
},
|
|
37
41
|
"phase_id": {
|
|
38
|
-
"description": "Phase id verbatim from the plan's `Phased Rollout` section (e.g. `1`, `4a`, `2.5`).",
|
|
42
|
+
"description": "Phase id verbatim from the plan's `Phased Rollout` section (e.g. `1`, `4a`, `2.5`). Required unless `kind` is `plan`; for `kind: integration` it is the phase whose `integ-` branch this is.",
|
|
39
43
|
"type": "string",
|
|
40
44
|
"pattern": "^[0-9]+(\\.[0-9]+)?[a-z]?$",
|
|
41
45
|
"minLength": 1
|
|
42
46
|
},
|
|
43
47
|
"phase_title": {
|
|
44
|
-
"description": "Phase title verbatim from the plan's `Phased Rollout` section.",
|
|
48
|
+
"description": "Phase title verbatim from the plan's `Phased Rollout` section. Required unless `kind` is `plan`.",
|
|
45
49
|
"type": "string",
|
|
46
50
|
"minLength": 1
|
|
47
51
|
},
|
|
48
52
|
"branch": {
|
|
49
|
-
"description": "Source branch the PR opens from.
|
|
53
|
+
"description": "Source branch the PR opens from. `plan/<plan-id-kebab>/phase-<phase.id>` for a phase, `plan/<plan-id-kebab>/integ-<phase.id>` for an integration PR, the final `plan/<plan-id-kebab>/wave-<N>` (or `plan/<plan-id-kebab>` under modular commits) for the plan PR.",
|
|
50
54
|
"type": "string",
|
|
51
55
|
"minLength": 1
|
|
52
56
|
},
|
|
53
57
|
"base": {
|
|
54
|
-
"description": "Target branch.
|
|
58
|
+
"description": "Target branch. A phase PR targets its dependency-derived base (the default branch, a dependency's phase branch, or its `integ-` branch); integration and plan PRs target the default branch.",
|
|
55
59
|
"type": "string",
|
|
56
60
|
"minLength": 1
|
|
57
61
|
},
|
|
@@ -73,5 +77,12 @@
|
|
|
73
77
|
{ "const": "" }
|
|
74
78
|
]
|
|
75
79
|
}
|
|
76
|
-
}
|
|
80
|
+
},
|
|
81
|
+
"allOf": [
|
|
82
|
+
{
|
|
83
|
+
"description": "Every PR but the plan PR belongs to one phase.",
|
|
84
|
+
"if": { "not": { "properties": { "kind": { "const": "plan" } }, "required": ["kind"] } },
|
|
85
|
+
"then": { "required": ["phase_id", "phase_title"] }
|
|
86
|
+
}
|
|
87
|
+
]
|
|
77
88
|
}
|
|
@@ -250,6 +250,18 @@
|
|
|
250
250
|
"fixer": { "type": "string", "enum": ["enabled", "disabled"] }
|
|
251
251
|
}
|
|
252
252
|
},
|
|
253
|
+
"integrations": {
|
|
254
|
+
"description": "Per-integration opt-in for external tools that ship their own skills, as opposed to foundation skills this package bundles. An integration's skill is installed by that tool's own CLI into `ai-tools/skills/<name>/`, carries the tool's own ownership marker, and is never copied, rendered or refreshed by `vinta-derive-skills` / `vinta-update-project-skills`. Use `enabled` / `disabled`; `disabled` is a sticky opt-out, as for `foundation_skills`. Absent means the integration was never offered (projects bootstrapped before it existed).",
|
|
255
|
+
"type": "object",
|
|
256
|
+
"additionalProperties": { "type": "string", "enum": ["enabled", "disabled"] },
|
|
257
|
+
"properties": {
|
|
258
|
+
"pr-review-canvas": {
|
|
259
|
+
"description": "PR Review Canvas (`@vintasoftware/pr-review-canvas`, CLI `pr-review`): a topic-grouped review canvas for each PR, posted as a PR comment and read in a local review app. When `enabled`: `vinta-install-ai-tools-setup` runs `pr-review install-skill` into `ai-tools/skills/` (the CLI itself is a global install the user does, never auto-installed); the `integrate-phase` PR step runs the `pr-review-canvas` skill after `open-pr.sh` publishes a PR; and `plan-feature` adds an `after_pr` `review-canvas` chore to the maestro workflow it writes. Posting the canvas comment follows the tool's own `sharing.canvasComment` setting in `pr-review.config.yml`.",
|
|
260
|
+
"type": "string",
|
|
261
|
+
"enum": ["enabled", "disabled"]
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
},
|
|
253
265
|
"agent_models": {
|
|
254
266
|
"description": "Per-role / per-task model selection for the plan-execution family, expressed as **tiers** (1–4) into the same table `plan-feature` uses — `ai-tools/skills/plan-feature/resources/ai-models.yaml`. Each consuming skill resolves a tier to a concrete model at runtime: take that tier's `models`, filter to the vendors the runtime exposes, pick the cheapest survivor. This keeps the config free of hard-coded model IDs (which go stale) — the nightly `check-ai-models` job keeps the tier table fresh. Every key is optional; an unset key (or the whole section absent) means the spawn uses the runtime's default model, i.e. today's behavior. These are the **project defaults**: the `reviewer` / `fixer` tiers here can be overridden per phase by a plan's optional `**Review models**:` line (a phase override wins over the value here for that phase only — see `plan-feature`). The per-phase implementer model is NOT here — that stays owned by each plan phase's `**Suggested AI model**:` line. The mechanical steps (`worktree_prep`, `integrate`) are never plan-named.",
|
|
255
267
|
"type": "object",
|