vinta-ai-workflows 0.7.0-alpha2 → 0.7.0-alpha4
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
CHANGED
|
@@ -7,6 +7,8 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
## [0.7.0] — YYYY-MM-DD
|
|
9
9
|
|
|
10
|
+
<!-- pre-release: 0.7.0-alpha4 on 2026-09-13 -->
|
|
11
|
+
<!-- pre-release: 0.7.0-alpha3 on 2026-09-12 -->
|
|
10
12
|
<!-- pre-release: 0.7.0-alpha2 on 2026-09-12 -->
|
|
11
13
|
<!-- pre-release: 0.7.0-alpha1 on 2026-09-11 -->
|
|
12
14
|
|
|
@@ -217,6 +219,105 @@ the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
217
219
|
|
|
218
220
|
### Fixed
|
|
219
221
|
|
|
222
|
+
- **An agent can write in its own lane.** Every adapter declared
|
|
223
|
+
`permissionControl: true` and passed no policy at all: claude-code was spawned
|
|
224
|
+
as `-p --output-format stream-json …` with no `--permission-mode`, and codex as
|
|
225
|
+
`exec --json` with no sandbox and no approval flag, under a comment saying it
|
|
226
|
+
took both on the command line. So a headless run asked before its first write,
|
|
227
|
+
emitted a `permission_request`, rendered it in the transcript — and nothing
|
|
228
|
+
anywhere answered. The phase failed reporting a permission system it could not
|
|
229
|
+
see, having written nothing.
|
|
230
|
+
|
|
231
|
+
`run` and `serve` now take `--permission <ask|auto|full>`, defaulting to
|
|
232
|
+
`auto`. **The operator sets it, never the workflow document**: the document is
|
|
233
|
+
committed and shared, and a file in a repository should not be able to tell
|
|
234
|
+
someone else's machine to run agents without approvals.
|
|
235
|
+
|
|
236
|
+
Both CLIs refuse combinations that looked reasonable, and both were found by
|
|
237
|
+
running them rather than reading them: codex rejects `--sandbox` alongside
|
|
238
|
+
`--approve-for-me`, and `codex exec resume` accepts neither — passing the
|
|
239
|
+
fresh-spawn policy there turned a `stale_session` refusal, which the scheduler
|
|
240
|
+
retries cold, into a `fatal` one that fails the node.
|
|
241
|
+
|
|
242
|
+
This reverses a documented decision. The README said the daemon passed no
|
|
243
|
+
permission flags and that a committed `.claude/settings.json` was what made a
|
|
244
|
+
run able to write; that is now the narrowing layer on top of a mode, not the
|
|
245
|
+
only thing standing between an agent and a blocked lane.
|
|
246
|
+
- **`purge --lanes --branches` clears what a failed run leaves behind.** A run's
|
|
247
|
+
lane worktrees are not under `runs/`, so no purge ever reached them: three
|
|
248
|
+
failed attempts at one plan left twelve worktrees, twelve summaries, and a set
|
|
249
|
+
of `plan/<id>/phase-*` branches still checked out — which is what makes the
|
|
250
|
+
*next* run of that plan impossible, since git refuses to check out a branch a
|
|
251
|
+
second worktree holds.
|
|
252
|
+
|
|
253
|
+
One rule decides both: **never delete the only copy of work.** A phase can
|
|
254
|
+
leave work committed on its branch or uncommitted in its worktree, and both
|
|
255
|
+
count — so a branch carrying commits no other branch has is kept, a lane with
|
|
256
|
+
a dirty tree is kept, and each is listed with the command to remove it by
|
|
257
|
+
hand. Untracked files count as work: a phase that wrote three new modules and
|
|
258
|
+
never committed them is exactly the case worth protecting.
|
|
259
|
+
|
|
260
|
+
Emptiness is measured as "carries no commit another branch does not already
|
|
261
|
+
have", not as "merged into HEAD". The latter is relative to wherever the
|
|
262
|
+
operator is standing, so on a feature branch — the checkout this is most often
|
|
263
|
+
run from — every empty phase branch looked unmerged and nothing was ever
|
|
264
|
+
cleaned up.
|
|
265
|
+
|
|
266
|
+
`--dry-run` and `--yes` work as they already did, and a worktree that will not
|
|
267
|
+
go is named rather than counted.
|
|
268
|
+
|
|
269
|
+
- **`doctor` now catches, before a lane exists, the failures that used to take a
|
|
270
|
+
run each.** Four consecutive runs of one plan failed four different ways, and
|
|
271
|
+
every one of them was knowable at minute zero from the workflow and the repo.
|
|
272
|
+
- **Every `prompt_ref` and `plan_context_ref` is resolved against
|
|
273
|
+
`base_branch`**, with `git cat-file -e`, which reads the branch's tree and
|
|
274
|
+
so cannot be satisfied by a working-tree copy — the working tree being
|
|
275
|
+
exactly what a lane will not have. It distinguishes the three cases, because
|
|
276
|
+
they have three different fixes: never committed, **staged but never
|
|
277
|
+
committed** (`git add` alone does not put a file in a branch), and committed
|
|
278
|
+
on a different branch. The last offers both routes out — merge it, or point
|
|
279
|
+
`base_branch` at the branch that has it — since only the operator knows
|
|
280
|
+
which they want.
|
|
281
|
+
- **Phase branches held by another worktree** are reported with the directory
|
|
282
|
+
to remove. Lane directories are named per run and phase branches per
|
|
283
|
+
workflow, so a failed run leaves worktrees holding the branch names the next
|
|
284
|
+
run will try to cut, and git refuses to check out a branch twice. Nothing
|
|
285
|
+
cleans those up: `purge` deletes run state under `.vinta-ai-maestro/runs/`,
|
|
286
|
+
and lane worktrees are not there. `doctor` does not delete them either — a
|
|
287
|
+
failed lane's worktree is the only place its state survives — but it now
|
|
288
|
+
names each one and the exact command.
|
|
289
|
+
|
|
290
|
+
- **A failed node records why.** `node_status` carried `{"status":"failed"}` and
|
|
291
|
+
nothing else, so two runs failing for two unrelated reasons produced identical
|
|
292
|
+
journal rows and the cause survived only in the operator's terminal. The
|
|
293
|
+
reason is now persisted — **sanitized**, not raw: errors the package builds
|
|
294
|
+
itself are identifiers by construction and are kept verbatim, and anything
|
|
295
|
+
else is reduced to its kind, the rule `recycleStage` already followed. The
|
|
296
|
+
journal is durable and API-served, and an exception message from a dependency
|
|
297
|
+
is exactly how repository content gets into one. Blocked dependents record
|
|
298
|
+
which node blocked them.
|
|
299
|
+
|
|
300
|
+
- **Two diagnostics that named the symptom and hid the cause.** Both came out of
|
|
301
|
+
one real run, and neither was a wrong answer — just an unusable one.
|
|
302
|
+
- **A phase whose plan is not committed** failed with `prompt_ref "…" names no
|
|
303
|
+
readable file`, against a path that was spelled correctly. A lane is a fresh
|
|
304
|
+
worktree of the base branch, so a plan sitting untracked in the operator's
|
|
305
|
+
checkout is in the one place the run cannot look. The message now names the
|
|
306
|
+
directory it resolved against and says why a plan is often not in it.
|
|
307
|
+
- **A lane summary the daemon could not accept** reported `summary unreadable`
|
|
308
|
+
and advised deleting the lane — which takes its forked databases with it,
|
|
309
|
+
over what turned out to be two wrong fields. `doctor` now names the fields
|
|
310
|
+
and what was expected, and offers repair before deletion. A file that does
|
|
311
|
+
not parse at all still reports as unreadable, because there is no field to
|
|
312
|
+
name. Neither message carries the offending *value*: a summary holds
|
|
313
|
+
database names and connection variables, and the value that failed
|
|
314
|
+
validation is the likeliest thing in it to be one.
|
|
315
|
+
|
|
316
|
+
`prepare-worktree` gained the rule that would have prevented the summary in
|
|
317
|
+
the first place — its `|` alternatives are closed sets read by machine, `null`
|
|
318
|
+
is how you say "none", and a strategy outside the set should be the closest
|
|
319
|
+
one plus a `note:` rather than a fourth word.
|
|
320
|
+
|
|
220
321
|
- **`npx vinta-ai-maestro` now runs.** The published package pointed its `bin` at
|
|
221
322
|
`src/cli/bin.ts` and relied on Node stripping the types at startup — which Node
|
|
222
323
|
refuses to do anywhere under `node_modules`, unconditionally and with no flag to
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vinta-ai-workflows",
|
|
3
|
-
"version": "0.7.0-
|
|
3
|
+
"version": "0.7.0-alpha4",
|
|
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
6
|
"packageManager": "pnpm@10.33.0",
|
|
@@ -355,7 +355,7 @@ state:
|
|
|
355
355
|
files: [.env, .envrc, ...]
|
|
356
356
|
dev_db:
|
|
357
357
|
engine: postgres | mysql | sqlite | mongo | redis
|
|
358
|
-
strategy: fork | share | stub
|
|
358
|
+
strategy: fork | share | stub # closed set — see "The summary is read by machine"
|
|
359
359
|
forked_name: <forked db name> # null when share / stub
|
|
360
360
|
connection_url_var: DATABASE_URL
|
|
361
361
|
reset_cmd: <shell command that returns this DB to the state of a fresh checkout>
|
|
@@ -365,7 +365,7 @@ state:
|
|
|
365
365
|
# instead of reusing this worktree across a migration boundary.
|
|
366
366
|
test_db:
|
|
367
367
|
engine: ...
|
|
368
|
-
strategy: fork | share
|
|
368
|
+
strategy: fork | share # closed set, as above
|
|
369
369
|
forked_name: ...
|
|
370
370
|
connection_url_var: TEST_DATABASE_URL
|
|
371
371
|
reset_cmd: <same idea, for the test DB> # null when not resettable
|
|
@@ -467,6 +467,14 @@ Every step gated on user confirmation when the worktree has un-pushed branches.
|
|
|
467
467
|
|
|
468
468
|
- **Symlink for reads, copy for writes, fork for state.** This is the only mental model that scales. Default to fork when unsure — disk is cheap, corrupted main-checkout DBs are not.
|
|
469
469
|
- **Never share a writable DB across worktrees by default.** The race conditions are subtle and the failure mode is silent data corruption.
|
|
470
|
+
### The summary is read by machine
|
|
471
|
+
|
|
472
|
+
This file is not a note to the next human. [vinta-ai-maestro](https://github.com/vintasoftware/vinta-ai-workflows/tree/main/packages/vinta-ai-maestro) parses it to decide whether a lane can be handed to another phase, so **every `|` above is a closed set and every key is required** — `null` is how you say "none", and omitting the key is not the same thing.
|
|
473
|
+
|
|
474
|
+
A value outside the set makes the lane unusable by the daemon. It does not fail loudly at write time; it fails later, as `doctor` reporting the lane and the field it could not accept, long after the worktree and its forked databases exist. Seen in the wild: a `strategy: create-empty-and-migrate` invented to describe a database that had been created empty and migrated by hand, with `reset_cmd` left off entirely. Neither is in the spec, and the pair cost eleven lanes their readability.
|
|
475
|
+
|
|
476
|
+
If none of the listed strategies describes what you did, **the honest record is the closest one plus a `note:`** — the schema keeps `note` free-form for exactly this. A database created empty and migrated is a `fork` whose `reset_cmd` re-runs that same creation; a database you could not provision at all is a `stub`. What it must never be is a fourth word.
|
|
477
|
+
|
|
470
478
|
- **`COMPOSE_PROJECT_NAME` does not isolate `external:`/fixed-`name:` volumes or fixed host ports — always run the [4a](#4a--neutralize-compose-isolation-leaks) generator.** For a compose-delivered DB the data volumes are forked *unconditionally* (independent of `schema_change` and `test_db_strategy`): the worktree runs its own server, and two servers on one volume is corruption. "Share the DB" only ever means share a connection to a single already-running server, never a second server on shared storage. The fix is a generated, out-of-tree override + a `COMPOSE_FILE` line in the copied `.env` — never an edit to any tracked compose file. Verify the volume-differs invariant before declaring the worktree runnable.
|
|
471
479
|
- **Every fork decision lands in `.vinta-ai-workflows/worktrees/<name>.yaml`.** Teardown reads it; humans grep it; agents resuming a stalled plan read it. No decision lives only in conversation memory.
|
|
472
480
|
- **Worktree root governed by runtime conventions.** claude-code uses `.claude/worktrees/`; other harnesses use sibling dirs. Don't fight the harness — match it.
|