@hecer/yoke 0.2.0 → 0.8.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/CHANGELOG.md +130 -0
- package/README.md +583 -494
- package/bench/.runs/claude-2026-07-09T22-34-01/.yoke/config.yaml +6 -0
- package/bench/.runs/claude-2026-07-09T22-34-01/.yoke/context/DECISIONS.md +9 -0
- package/bench/.runs/claude-2026-07-09T22-34-01/.yoke/prd.yaml +38 -0
- package/bench/.runs/claude-2026-07-09T22-34-01/bench-verify.mjs +15 -0
- package/bench/.runs/claude-2026-07-09T22-34-01/package.json +9 -0
- package/bench/.runs/claude-2026-07-09T22-34-01/src/index.mjs +48 -0
- package/bench/.runs/claude-2026-07-09T22-34-01/tests/STORY-1.test.mjs +24 -0
- package/bench/.runs/claude-2026-07-09T22-34-01/tests/STORY-2.test.mjs +28 -0
- package/bench/.runs/claude-2026-07-09T22-34-01/tests/STORY-3.test.mjs +25 -0
- package/bench/.runs/gemini-2026-07-09T22-34-02/.yoke/config.yaml +6 -0
- package/bench/.runs/gemini-2026-07-09T22-34-02/.yoke/prd.yaml +32 -0
- package/bench/.runs/gemini-2026-07-09T22-34-02/bench-verify.mjs +15 -0
- package/bench/.runs/gemini-2026-07-09T22-34-02/package.json +9 -0
- package/bench/.runs/gemini-2026-07-09T22-34-02/src/index.mjs +3 -0
- package/bench/.runs/gemini-2026-07-09T22-34-02/tests/STORY-1.test.mjs +24 -0
- package/bench/.runs/gemini-2026-07-09T22-34-02/tests/STORY-2.test.mjs +28 -0
- package/bench/.runs/gemini-2026-07-09T22-34-02/tests/STORY-3.test.mjs +25 -0
- package/bench/README.md +42 -0
- package/bench/RESULTS.md +36 -0
- package/bench/fixtures/string-kit/.yoke/config.yaml +6 -0
- package/bench/fixtures/string-kit/.yoke/prd.yaml +32 -0
- package/bench/fixtures/string-kit/bench-verify.mjs +15 -0
- package/bench/fixtures/string-kit/package.json +9 -0
- package/bench/fixtures/string-kit/src/index.mjs +3 -0
- package/bench/fixtures/string-kit/tests/STORY-1.test.mjs +24 -0
- package/bench/fixtures/string-kit/tests/STORY-2.test.mjs +28 -0
- package/bench/fixtures/string-kit/tests/STORY-3.test.mjs +25 -0
- package/bench/results/claude-2026-07-09T22-34-01.json +40 -0
- package/bench/run.mjs +115 -0
- package/canon/loop/loop-spec.md +5 -1
- package/canon/manifest.yaml +3 -0
- package/canon/skills/yoke-retrofit/SKILL.md +1 -0
- package/canon/tools/claude-mem.md +15 -0
- package/canon/tools/ui-ux-pro-max.md +15 -0
- package/dist/cli.js +37 -6
- package/dist/loop/cleanup.js +44 -3
- package/dist/loop/loop.js +67 -2
- package/dist/loop/reporter.js +88 -4
- package/dist/loop/run-command.js +25 -8
- package/dist/loop/runner.js +133 -14
- package/dist/loop/watchdog.js +67 -17
- package/dist/retrofit/apply.js +10 -3
- package/dist/retrofit/config.js +9 -1
- package/dist/retrofit/gitignore.js +4 -0
- package/dist/retrofit/planners/claude.js +4 -1
- package/dist/retrofit/planners/gemini.js +2 -1
- package/dist/retrofit/preserve.js +51 -0
- package/dist/update/check.js +86 -0
- package/dist/update/refresh.js +28 -0
- package/dist/update/upgrade.js +30 -0
- package/docs/PUBLISHING.md +41 -0
- package/docs/superpowers/plans/2026-06-27-forge-a-canon.md +815 -0
- package/docs/superpowers/plans/2026-06-27-forge-b1-retrofit-claude.md +842 -0
- package/docs/superpowers/plans/2026-06-27-forge-b2-codex-gemini-tools.md +884 -0
- package/docs/superpowers/plans/2026-06-27-forge-c1-loop-engine.md +891 -0
- package/docs/superpowers/plans/2026-06-28-baustein-e-context-layer.md +981 -0
- package/docs/superpowers/plans/2026-06-28-forge-b3-settings-merge.md +364 -0
- package/docs/superpowers/plans/2026-06-28-forge-c2-loop-verify.md +502 -0
- package/docs/superpowers/plans/2026-06-28-forge-c3-multi-agent-runners.md +377 -0
- package/docs/superpowers/plans/2026-06-28-forge-c4-worktree-isolation.md +413 -0
- package/docs/superpowers/plans/2026-06-28-forge-c5-review-iteration.md +419 -0
- package/docs/superpowers/plans/2026-06-28-forge-cleanup-dep0190.md +233 -0
- package/docs/superpowers/plans/2026-06-28-forge-d-codegraph-choice-minimal-code.md +523 -0
- package/docs/superpowers/plans/2026-06-29-baustein-f-routing.md +258 -0
- package/docs/superpowers/plans/2026-06-29-baustein-g-loop-observability.md +1006 -0
- package/docs/superpowers/plans/2026-06-29-baustein-h-loop-robustness.md +374 -0
- package/docs/superpowers/plans/2026-06-30-baustein-i-visual-design-verification.md +450 -0
- package/docs/superpowers/plans/2026-07-02-baustein-j-cross-model-review-gstack-compose.md +645 -0
- package/docs/superpowers/plans/2026-07-02-baustein-k-zero-to-100-bootstrap.md +1024 -0
- package/docs/superpowers/plans/2026-07-02-baustein-m-flow-smoke-proofs.md +574 -0
- package/docs/superpowers/specs/2026-06-27-forge-cross-agent-harness-design.md +144 -0
- package/docs/superpowers/specs/2026-06-28-baustein-e-context-layer-design.md +146 -0
- package/docs/superpowers/specs/2026-06-29-baustein-f-routing-design.md +106 -0
- package/docs/superpowers/specs/2026-06-29-baustein-g-loop-observability-design.md +186 -0
- package/docs/superpowers/specs/2026-06-29-baustein-h-loop-robustness-design.md +113 -0
- package/docs/superpowers/specs/2026-06-30-baustein-i-visual-design-verification-design.md +98 -0
- package/docs/superpowers/specs/2026-07-02-baustein-j-cross-model-review-gstack-compose-design.md +137 -0
- package/docs/superpowers/specs/2026-07-02-baustein-k-zero-to-100-bootstrap-design.md +200 -0
- package/docs/superpowers/specs/2026-07-02-baustein-m-flow-smoke-proofs-design.md +155 -0
- package/docs/superpowers/specs/2026-07-10-companion-tools-decision.md +33 -0
- package/docs/superpowers/specs/2026-07-10-multi-agent-parallel-loop-design.md +99 -0
- package/package.json +8 -2
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Baustein M — `yoke flow-smoke`: built-in browser gate with screenshot/video proofs
|
|
2
|
+
|
|
3
|
+
Date: 2026-07-02
|
|
4
|
+
Status: approved (design delegated by user; proof concept approved in conversation: screenshots
|
|
5
|
+
always, video on failure, `.yoke/proof/` storage, loop links proofs to stories)
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
The verify gate is code-only unless an agent hand-rolls a Playwright smoke (the
|
|
10
|
+
`visual-verification` skill teaches it, but nothing enforces or standardizes it). A story can
|
|
11
|
+
pass unit tests and design-scan yet ship a blank page, an unwired route, or a runtime console
|
|
12
|
+
error. And when a story IS done, there is no visual evidence — "done with a photo" is the answer
|
|
13
|
+
to the loudest agentic-coding pain point ("agent says done, but it isn't").
|
|
14
|
+
|
|
15
|
+
## Goal
|
|
16
|
+
|
|
17
|
+
A built-in, mechanical browser gate that any project can add to `verify.command`:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
yoke flow-smoke [dir] [--url=<baseUrl>] [--label=<name>]
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
For every configured flow: load the route, optionally wait for a landmark selector, assert zero
|
|
24
|
+
console/page errors, and **always** save a screenshot to `.yoke/proof/<label>/<flow>.png`.
|
|
25
|
+
Record video per flow but **keep it only on failure** (`<flow>.webm`). Exit 0 = all flows green
|
|
26
|
+
(chainable in `verify.command`), 1 = failures, 2 = not runnable (no config / no playwright).
|
|
27
|
+
|
|
28
|
+
## Part 1: Config schema
|
|
29
|
+
|
|
30
|
+
Extend `YokeConfigSchema` (src/retrofit/config.ts) with an optional `smoke` section:
|
|
31
|
+
|
|
32
|
+
```yaml
|
|
33
|
+
smoke:
|
|
34
|
+
baseUrl: http://localhost:3000
|
|
35
|
+
flows:
|
|
36
|
+
- name: home
|
|
37
|
+
path: /
|
|
38
|
+
landmark: "main h1" # optional CSS selector
|
|
39
|
+
- name: login
|
|
40
|
+
path: /login
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Zod: `smoke: z.object({ baseUrl: z.string().min(1), flows: z.array(z.object({ name: z.string().min(1), path: z.string().min(1), landmark: z.string().optional() })).min(1) }).optional()` and the matching optional field on the `YokeConfig` interface. Existing configs (no `smoke`) stay valid.
|
|
44
|
+
|
|
45
|
+
## Part 2: `runFlowSmoke` (src/smoke/command.ts)
|
|
46
|
+
|
|
47
|
+
`export async function runFlowSmoke(targetDir: string, opts: FlowSmokeOptions = {}): Promise<number>`
|
|
48
|
+
|
|
49
|
+
Sequence:
|
|
50
|
+
1. Load config. No config file or no `smoke` section → print guidance (example YAML above) and
|
|
51
|
+
exit **2**. `--url` overrides `baseUrl`.
|
|
52
|
+
2. Resolve Playwright **from the target project** (never a Yoke dependency):
|
|
53
|
+
`createRequire(join(targetDir, 'package.json')).resolve('playwright')`, then dynamic
|
|
54
|
+
`import(pathToFileURL(resolved).href)`. Resolution failure → print
|
|
55
|
+
`Playwright not found in <dir>. Install it: npm i -D playwright && npx playwright install chromium`
|
|
56
|
+
and exit **2**.
|
|
57
|
+
3. Proof dir: `.yoke/proof/<label>/` where label = `opts.label` ?? `process.env.YOKE_STORY` ??
|
|
58
|
+
`'latest'`. Wipe the label dir before the run (`rmSync(recursive, force)` + mkdir) — each run
|
|
59
|
+
is fresh evidence, no stale screenshots.
|
|
60
|
+
4. Launch chromium headless once; per flow, create a **new context** with
|
|
61
|
+
`recordVideo: { dir: <proofDir>/.video-tmp }` and a page. Collect errors: `page.on('console')`
|
|
62
|
+
with `type() === 'error'`, and `page.on('pageerror')`.
|
|
63
|
+
5. Per flow, in order (fail-fast per flow, continue to the next flow):
|
|
64
|
+
- `page.goto(baseUrl + path, { waitUntil: 'load', timeout: 30_000 })`; a non-OK response
|
|
65
|
+
(`!response.ok()`) is a failure (`HTTP <status>`).
|
|
66
|
+
- if `landmark`: `page.waitForSelector(landmark, { timeout: 10_000 })`; timeout → failure
|
|
67
|
+
(`landmark "<sel>" not found`).
|
|
68
|
+
- collected errors non-empty → failure (`N console error(s): <first, truncated 200 chars>`).
|
|
69
|
+
- **always** `page.screenshot({ path: <proofDir>/<flow.name>.png, fullPage: true })` — also on
|
|
70
|
+
failure (the failure screenshot IS the evidence), inside try/catch (a crashed page must not
|
|
71
|
+
mask the original failure).
|
|
72
|
+
- close context; then: flow failed → move its video to `<proofDir>/<flow.name>.webm`
|
|
73
|
+
(`page.video().path()` after close); flow passed → delete the video file. Remove
|
|
74
|
+
`.video-tmp` at the end.
|
|
75
|
+
6. Report per flow: `✔ home (screenshot: .yoke/proof/latest/home.png)` /
|
|
76
|
+
`✘ login — 2 console error(s): ... (screenshot + video saved)`. Summary line
|
|
77
|
+
`Flow-smoke: N/M flows green — proof: .yoke/proof/<label>/`.
|
|
78
|
+
7. Close the browser in a `finally`. Exit 0 all green, 1 any failure.
|
|
79
|
+
|
|
80
|
+
Injectable seam for tests: `opts.browser?: () => Promise<SmokeBrowser>` — a minimal structural
|
|
81
|
+
interface defined in the module:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
export interface SmokePage {
|
|
85
|
+
goto(url: string, opts?: object): Promise<{ ok(): boolean; status(): number } | null>
|
|
86
|
+
waitForSelector(sel: string, opts?: object): Promise<unknown>
|
|
87
|
+
screenshot(opts: { path: string; fullPage?: boolean }): Promise<unknown>
|
|
88
|
+
on(event: 'console' | 'pageerror', handler: (arg: any) => void): void
|
|
89
|
+
video(): { path(): Promise<string> } | null
|
|
90
|
+
}
|
|
91
|
+
export interface SmokeContext { newPage(): Promise<SmokePage>; close(): Promise<void> }
|
|
92
|
+
export interface SmokeBrowser {
|
|
93
|
+
newContext(opts?: object): Promise<SmokeContext>
|
|
94
|
+
close(): Promise<void>
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The real path adapts Playwright's chromium to this interface; tests inject fakes (no Playwright
|
|
99
|
+
in Yoke's devDependencies — an fs-level fake writes marker files for "screenshot"/"video").
|
|
100
|
+
|
|
101
|
+
## Part 3: Loop linkage — proofs per story
|
|
102
|
+
|
|
103
|
+
- In `src/loop/loop.ts`, both verify call sites set `process.env.YOKE_STORY = story.id` before
|
|
104
|
+
`opts.verify(...)` and delete it in a `finally`. A `yoke flow-smoke` inside `verify.command`
|
|
105
|
+
then writes to `.yoke/proof/<story-id>/` automatically — every completed story has visual
|
|
106
|
+
evidence, every blocked story has failure evidence (screenshot + video).
|
|
107
|
+
- `.yoke/proof/` joins `YOKE_IGNORE_LINES` (runtime artifact; must not break the clean-tree gate).
|
|
108
|
+
|
|
109
|
+
## Part 4: CLI + async main
|
|
110
|
+
|
|
111
|
+
- `main()` in src/cli.ts becomes `number | Promise<number>`-returning; the isMain block awaits it
|
|
112
|
+
(top-level await, ESM). Existing sync cases are untouched.
|
|
113
|
+
- `case 'flow-smoke'`: `[dir]`, `--url=`, `--label=` → `return runFlowSmoke(targetDir, { url, label })`.
|
|
114
|
+
- Usage line gains `flow-smoke [dir] [--url=<baseUrl>] [--label=<name>]`.
|
|
115
|
+
|
|
116
|
+
## Part 5: Canon skill update
|
|
117
|
+
|
|
118
|
+
`canon/skills/visual-verification/SKILL.md`: replace the hand-rolled flow-smoke instruction with
|
|
119
|
+
the built-in — configure `smoke:` in `.yoke/config.yaml`, chain
|
|
120
|
+
`... && yoke design-scan . && yoke flow-smoke .` in `verify.command`; keep the Playwright-MCP
|
|
121
|
+
guidance for *debugging* a failed flow (watch the saved video first). Mention proofs land in
|
|
122
|
+
`.yoke/proof/<story>/`. Skill count stays 27 (content update, no new skill).
|
|
123
|
+
|
|
124
|
+
## Testing
|
|
125
|
+
|
|
126
|
+
- `tests/retrofit/config.test.ts` (extend): smoke section round-trips; config without smoke stays
|
|
127
|
+
valid; invalid smoke (empty flows) rejected.
|
|
128
|
+
- `tests/smoke/command.test.ts` (fake browser):
|
|
129
|
+
- exit 2 when no smoke config (message contains example);
|
|
130
|
+
- exit 2 when playwright is unresolvable: call without `opts.browser` (the seam bypasses
|
|
131
|
+
resolution) against a temp dir that has a smoke config but no playwright install;
|
|
132
|
+
- green flow: screenshot file written under `.yoke/proof/latest/<name>.png`, video deleted, exit 0;
|
|
133
|
+
- landmark timeout → exit 1, screenshot still written, video kept as `<name>.webm`;
|
|
134
|
+
- console error collected → exit 1;
|
|
135
|
+
- non-OK response → exit 1;
|
|
136
|
+
- label resolution: `--label` beats `YOKE_STORY` env beats `latest`;
|
|
137
|
+
- proof dir wiped between runs (stale file gone);
|
|
138
|
+
- one failing flow does not stop later flows (both reported).
|
|
139
|
+
- `tests/loop/*`: one test that a fake verifier observes `process.env.YOKE_STORY === story.id`
|
|
140
|
+
during verify and that it is unset afterwards (both normal and `--isolate` paths if cheap;
|
|
141
|
+
normal path suffices).
|
|
142
|
+
- `tests/retrofit/gitignore.test.ts`: `.yoke/proof/` ensured.
|
|
143
|
+
|
|
144
|
+
## Non-goals
|
|
145
|
+
|
|
146
|
+
- No dev-server startup/readiness management (`start-server-and-test` and friends exist; the
|
|
147
|
+
skill documents chaining). A connection-refused goto is an ordinary flow failure.
|
|
148
|
+
- No visual diffing/pixel comparison, no multi-browser matrix, no video-always mode (token/disk
|
|
149
|
+
cost; Ebene C in the Baustein-I backlog stays opt-in future work).
|
|
150
|
+
- Playwright stays a target-project dependency, never Yoke's.
|
|
151
|
+
|
|
152
|
+
## Attribution
|
|
153
|
+
|
|
154
|
+
Browser-QA-as-gate idea: gstack `/qa` (MIT © Garry Tan), natively re-implemented cross-agent with
|
|
155
|
+
a proof-artifact contract; no code copied. Extend the existing ATTRIBUTION.md gstack entry.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Companion tools — integration decision (2026-07-10)
|
|
2
|
+
|
|
3
|
+
Evaluated four ecosystem projects for integration into Yoke. Criteria: fit with the
|
|
4
|
+
curated-canon promise (small, cross-agent, token-lean), maintenance cost of a vendored
|
|
5
|
+
copy vs. a documented boundary, and overlap with existing canon skills.
|
|
6
|
+
|
|
7
|
+
## Decisions
|
|
8
|
+
|
|
9
|
+
| Project | Decision | Form |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| [claude-mem](https://github.com/thedotmack/claude-mem) (Apache-2.0) | **Integrate as optional companion** | `canon/tools/claude-mem.md` — documented wiring, external installer |
|
|
12
|
+
| [ui-ux-pro-max](https://github.com/nextlevelbuilder/ui-ux-pro-max-skill) (MIT) | **Integrate as optional companion** | `canon/tools/ui-ux-pro-max.md` — documented pairing, external installer |
|
|
13
|
+
| [ux-ui-mastery](https://github.com/phazurlabs/ux-ui-mastery) (MIT) | **Rejected** | — |
|
|
14
|
+
| [claude-plugins-official](https://github.com/anthropics/claude-plugins-official) | **Not an integration target — a distribution channel** | Yoke itself packaged as a plugin; see the plugin packaging work |
|
|
15
|
+
|
|
16
|
+
## Rationale
|
|
17
|
+
|
|
18
|
+
- **claude-mem**: mature, multi-agent, solves a real gap (interactive cross-session memory).
|
|
19
|
+
But it is a runtime service (worker + vector DB) — the opposite of Yoke's zero-runtime
|
|
20
|
+
philosophy — so it is wired like rtk/Serena/Playwright MCP: externally installed, boundary
|
|
21
|
+
documented. Hard rule recorded in the tool doc: **no automatic memory injection inside loop
|
|
22
|
+
runs** — the loop's memory is the versioned context layer + PRD, by design (reproducibility,
|
|
23
|
+
reviewability, git history).
|
|
24
|
+
- **ui-ux-pro-max**: complementary, not overlapping — it improves design *generation*; Yoke's
|
|
25
|
+
`unslop-ui`/`design-scan`/`visual-verification` do design *verification*. It ships its own
|
|
26
|
+
cross-agent installer and fast update cadence; a vendored copy would go stale. Documented
|
|
27
|
+
as a pairing, not vendored.
|
|
28
|
+
- **ux-ui-mastery**: rejected. ~310k words across 19 skills would break the curated-canon and
|
|
29
|
+
token-efficiency promises, it is Claude-only (no cross-agent story), largely redundant with
|
|
30
|
+
`unslop-ui` + ui-ux-pro-max, and low-traction/low-activity at evaluation time. Users who want
|
|
31
|
+
it can install it as a Claude plugin alongside Yoke with zero support from us. Revisit only
|
|
32
|
+
if a specific gap (e.g. an accessibility-audit skill) justifies porting a *single* skill with
|
|
33
|
+
attribution, as done for superpowers/gstack.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Multi-agent parallel loop — evaluation & design (2026-07-10)
|
|
2
|
+
|
|
3
|
+
**Question:** when a project is implemented via the Yoke loop, can we distribute the open
|
|
4
|
+
stories across *many* agents in parallel — and does that actually speed up completion?
|
|
5
|
+
|
|
6
|
+
**Answer:** yes, with a bounded design — the worktree machinery we already have does most of
|
|
7
|
+
the work — but parallelism pays off only under specific conditions, and it multiplies token
|
|
8
|
+
cost. This doc evaluates where it helps, where it hurts, and specifies a three-phase design.
|
|
9
|
+
|
|
10
|
+
## Where we start (current state)
|
|
11
|
+
|
|
12
|
+
- The loop is strictly **serial**: one story at a time, selected by lowest `priority`
|
|
13
|
+
(`src/loop/prd.ts` — `selectNextStory`), guarded by a single-flight lock (`.yoke/loop.lock`).
|
|
14
|
+
- `--isolate` already gives every story its **own detached git worktree** under
|
|
15
|
+
`.yoke/worktrees/<story-id>`, merged back with `git merge --ff-only` and discarded on failure.
|
|
16
|
+
This is exactly the isolation primitive a parallel dispatcher needs.
|
|
17
|
+
- The PRD schema has **no dependency field** — stories are implicitly assumed independent,
|
|
18
|
+
ordering is a soft hint via `priority`.
|
|
19
|
+
- Two multi-agent mechanisms already exist, both *role-parallel*, not *task-parallel*:
|
|
20
|
+
the reviewer can be a different agent (`--reviewer=codex`), and the
|
|
21
|
+
`dispatching-parallel-agents` / `subagent-driven-development` skills cover in-session
|
|
22
|
+
fan-out *within* one Claude Code session.
|
|
23
|
+
|
|
24
|
+
## When parallelism actually speeds things up (the honest part)
|
|
25
|
+
|
|
26
|
+
Parallel story execution helps iff:
|
|
27
|
+
|
|
28
|
+
1. **Stories are genuinely independent** — no shared files, no semantic coupling. Two stories
|
|
29
|
+
editing the same module produce merge conflicts or, worse, silently incompatible designs
|
|
30
|
+
that both pass their own acceptance tests.
|
|
31
|
+
2. **Verify is the short pole, not the long pole.** Each merged story must be verified against
|
|
32
|
+
the *integrated* tree (merge-queue semantics). If `npm test` takes 10 minutes, merges
|
|
33
|
+
serialize on verification and N workers approach 1× speedup (Amdahl).
|
|
34
|
+
3. **Wall-clock matters more than spend.** N parallel agents ≈ N× token cost for (at best)
|
|
35
|
+
~N× wall-clock speedup on the parallel portion. Parallelism buys *time*, never *efficiency*.
|
|
36
|
+
|
|
37
|
+
Typical PRDs from `yoke prd draft` (5–12 stories on one small codebase) violate (1) more often
|
|
38
|
+
than not — early stories scaffold what later stories touch. The realistic win is **2–4 workers
|
|
39
|
+
on the independent middle of a backlog**, not 12 workers on 12 stories.
|
|
40
|
+
|
|
41
|
+
## Design
|
|
42
|
+
|
|
43
|
+
### Phase 1 — dependency-aware parallel dispatch (`--parallel=N`)
|
|
44
|
+
|
|
45
|
+
- **PRD schema:** add optional `needs: [story-ids]` per story (default `[]`). `prd.schema.md`
|
|
46
|
+
documents: "list the stories whose *code* this story builds on." `yoke prd draft` prompts the
|
|
47
|
+
drafting agent to emit `needs` — it already reasons about ordering when assigning priorities.
|
|
48
|
+
- **Scheduler:** ready-set = open stories (`passes: false`) whose `needs` are all
|
|
49
|
+
`passes: true`. Dispatcher keeps ≤ N workers busy; each worker claims a story via a per-story
|
|
50
|
+
claim file (`.yoke/claims/<story-id>.json`, PID + timestamp — same liveness logic as the
|
|
51
|
+
existing loop lock), runs the full existing pipeline (implement → verify → review) inside its
|
|
52
|
+
own worktree.
|
|
53
|
+
- **Merge queue (the critical piece):** workers never integrate directly. Completed worktrees
|
|
54
|
+
enter a FIFO queue; a single integrator (a) rebases the worktree branch onto current HEAD,
|
|
55
|
+
(b) re-runs verify if HEAD moved since the story's base, (c) ff-merges, (d) marks
|
|
56
|
+
`passes: true`. Rebase conflict → story goes back to the ready-set with a `merge-conflict`
|
|
57
|
+
note for the next agent run; the loop's "nothing lands unverified" invariant survives
|
|
58
|
+
parallelism intact.
|
|
59
|
+
- **Single-flight lock** stays, reinterpreted: it now guards the *dispatcher*, not the story.
|
|
60
|
+
|
|
61
|
+
### Phase 2 — conflict avoidance (make parallelism worth turning on)
|
|
62
|
+
|
|
63
|
+
- Optional `area:` label per story (e.g. `area: api`, `area: ui`); the scheduler never runs two
|
|
64
|
+
stories with the same area concurrently. Cheap, no file-prediction magic, removes the most
|
|
65
|
+
common conflict class.
|
|
66
|
+
- Record per-story wall-clock + rebase-conflict counts (the benchmark harness in `bench/`
|
|
67
|
+
measures this from outside today; the reporter should own it) so `--parallel` can be
|
|
68
|
+
recommended or discouraged per project from *data*.
|
|
69
|
+
|
|
70
|
+
### Phase 3 — heterogeneous workers (cross-agent dispatch)
|
|
71
|
+
|
|
72
|
+
Workers need not be the same CLI. Assignment policies, in order of usefulness:
|
|
73
|
+
|
|
74
|
+
1. **Availability/cost:** cheap-fast model for low-priority stories, strongest model for
|
|
75
|
+
stories with many dependents (they block the most downstream work).
|
|
76
|
+
2. **Affinity:** per-story `agent:` hint (e.g. Gemini for large-context refactors).
|
|
77
|
+
3. **Review stays cross-model** — implementer ≠ reviewer per story, as today.
|
|
78
|
+
|
|
79
|
+
Prerequisite: token reporting for gemini/codex runners (today only claude reports usage via
|
|
80
|
+
stream-json), otherwise cost-based policies fly blind.
|
|
81
|
+
|
|
82
|
+
## What we deliberately do NOT build
|
|
83
|
+
|
|
84
|
+
- **No shared mutable context between concurrent agents.** Each worker reads the committed
|
|
85
|
+
context layer at its worktree's base commit. Context/DECISIONS updates land only through the
|
|
86
|
+
merge queue, serialized. Cross-talk between concurrent agents is how "overnight loop" horror
|
|
87
|
+
stories happen.
|
|
88
|
+
- **No optimistic file-overlap prediction.** Predicting which files a story will touch before
|
|
89
|
+
running it is guesswork; `needs` + `area` + rebase-verify catch the same failures mechanically.
|
|
90
|
+
- **No parallelism by default.** Serial stays the default; `--parallel` is opt-in per run,
|
|
91
|
+
consistent with every other Yoke escalation (`--isolate`, `--review`, loop itself).
|
|
92
|
+
|
|
93
|
+
## Verdict
|
|
94
|
+
|
|
95
|
+
Worth building, in this order, behind an opt-in flag: **Phase 1 is ~3 focused Bausteine**
|
|
96
|
+
(PRD `needs` + scheduler/claims + merge queue) on top of primitives that already exist and
|
|
97
|
+
are tested (worktrees, locks, gates). Phase 2 is small and makes Phase 1 honest. Phase 3 is
|
|
98
|
+
where "one harness, three agents" becomes a genuinely unique capability — no other harness
|
|
99
|
+
dispatches one backlog across Claude, Codex, and Gemini concurrently with mechanical gates.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hecer/yoke",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "One harness, three agents, zero trust in \"done\" — cross-agent coding harness for Claude Code, Codex CLI, and Gemini CLI: one skill canon, mechanical safety gates, an autonomous loop with screenshot/video proofs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"dist",
|
|
11
11
|
"canon",
|
|
12
|
+
"bench",
|
|
13
|
+
"docs",
|
|
14
|
+
"CHANGELOG.md",
|
|
12
15
|
"README.md",
|
|
13
16
|
"LICENSE"
|
|
14
17
|
],
|
|
@@ -35,7 +38,10 @@
|
|
|
35
38
|
"code-review",
|
|
36
39
|
"skills",
|
|
37
40
|
"agents-md",
|
|
38
|
-
"tdd"
|
|
41
|
+
"tdd",
|
|
42
|
+
"claude-plugin",
|
|
43
|
+
"claude-code-plugin",
|
|
44
|
+
"gemini-cli-extension"
|
|
39
45
|
],
|
|
40
46
|
"license": "MIT",
|
|
41
47
|
"publishConfig": {
|