@brunosps00/dev-workflow 1.7.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +2 -2
- package/CHANGELOG.md +201 -0
- package/README.md +2 -2
- package/lib/agents.js +6 -1
- package/lib/init.js +14 -0
- package/lib/install-deps.js +15 -0
- package/lib/install-open-design.js +39 -0
- package/lib/install-state.js +8 -4
- package/lib/utils.js +46 -6
- package/package.json +3 -2
- package/scaffold/agents/core/dw-build-fixer.md +0 -1
- package/scaffold/agents/core/dw-code-explorer.md +0 -1
- package/scaffold/agents/core/dw-code-reviewer.md +0 -1
- package/scaffold/agents/core/dw-planner.md +0 -1
- package/scaffold/agents/core/dw-test-author.md +0 -1
- package/scaffold/agents/csharp/dw-csharp-reviewer.md +0 -1
- package/scaffold/agents/csharp/dw-dotnet-build-fixer.md +0 -1
- package/scaffold/agents/frontend/dw-qa-runner.md +0 -1
- package/scaffold/agents/python/dw-python-build-fixer.md +0 -1
- package/scaffold/agents/python/dw-python-reviewer.md +0 -1
- package/scaffold/agents/rust/dw-rust-build-fixer.md +0 -1
- package/scaffold/agents/rust/dw-rust-reviewer.md +0 -1
- package/scaffold/agents/security/dw-security-reviewer.md +0 -1
- package/scaffold/agents/security/dw-silent-failure-hunter.md +0 -1
- package/scaffold/agents/typescript/dw-typescript-build-fixer.md +0 -1
- package/scaffold/agents/typescript/dw-typescript-reviewer.md +0 -1
- package/scaffold/config/routing.json +60 -0
- package/scaffold/en/commands/dw-claude-run.md +5 -1
- package/scaffold/en/commands/dw-codex-run.md +5 -1
- package/scaffold/en/commands/dw-dockerize.md +14 -7
- package/scaffold/en/commands/dw-generate-pr.md +13 -0
- package/scaffold/en/commands/dw-new-project.md +34 -30
- package/scaffold/en/commands/dw-run.md +1 -1
- package/scaffold/en/templates/project-onepager.md +16 -14
- package/scaffold/en/templates/task-template.md +1 -1
- package/scaffold/en/templates/tasks-template.md +7 -5
- package/scaffold/pt-br/commands/dw-claude-run.md +5 -1
- package/scaffold/pt-br/commands/dw-codex-run.md +5 -1
- package/scaffold/pt-br/commands/dw-dockerize.md +14 -7
- package/scaffold/pt-br/commands/dw-generate-pr.md +13 -0
- package/scaffold/pt-br/commands/dw-new-project.md +34 -30
- package/scaffold/pt-br/commands/dw-plan.md +1 -1
- package/scaffold/pt-br/commands/dw-run.md +1 -1
- package/scaffold/pt-br/templates/project-onepager.md +16 -14
- package/scaffold/pt-br/templates/task-template.md +1 -1
- package/scaffold/pt-br/templates/tasks-template.md +7 -5
- package/scaffold/skills/docker-compose-recipes/SKILL.md +4 -5
- package/scaffold/skills/docker-compose-recipes/references/compose-composition.md +6 -6
- package/scaffold/skills/docker-compose-recipes/references/env-conventions.md +18 -9
- package/scaffold/skills/docker-compose-recipes/references/healthcheck-patterns.md +4 -5
- package/scaffold/skills/docker-compose-recipes/references/prod-vs-dev.md +7 -2
- package/scaffold/skills/docker-compose-recipes/services/elasticsearch.yml +3 -3
- package/scaffold/skills/docker-compose-recipes/services/mailpit.yml +4 -4
- package/scaffold/skills/docker-compose-recipes/services/meilisearch.yml +2 -2
- package/scaffold/skills/docker-compose-recipes/services/minio.yml +8 -4
- package/scaffold/skills/docker-compose-recipes/services/mysql.yml +4 -4
- package/scaffold/skills/docker-compose-recipes/services/postgres.yml +2 -2
- package/scaffold/skills/docker-compose-recipes/services/rabbitmq.yml +3 -3
- package/scaffold/skills/docker-compose-recipes/services/typesense.yml +3 -3
- package/scaffold/skills/dw-cli-run/SKILL.md +42 -38
- package/scaffold/skills/dw-cli-run/references/dispatch-tuning.md +85 -0
- package/scaffold/skills/dw-execute-phase/SKILL.md +4 -2
- package/scaffold/skills/dw-execute-phase/agents/executor.md +4 -2
- package/scaffold/skills/dw-execute-phase/agents/plan-checker.md +9 -7
- package/scaffold/skills/dw-execute-phase/references/atomic-commits.md +12 -8
- package/scaffold/skills/dw-execute-phase/references/plan-verification.md +6 -4
- package/scaffold/skills/docker-compose-recipes/services/mailhog.yml +0 -23
|
@@ -16,6 +16,10 @@ The recipes default to **dev**. When `/dw-dockerize --prod` (or `--both`) emits
|
|
|
16
16
|
| Logging | Default driver | `json-file` with rotation, or ship to a log aggregator (Loki, CloudWatch) |
|
|
17
17
|
| Resource limits | None (machine has plenty) | `mem_limit`, `cpus` set per service to prevent runaway containers |
|
|
18
18
|
|
|
19
|
+
Every recipe tracks a tag rather than a digest on purpose: a digest freezes the base image and accrues CVEs with no upgrade path, since only a rebuild clears them. Tracking a tag is not a production approval either — a dev recipe still needs its own review before any non-local environment.
|
|
20
|
+
|
|
21
|
+
Every credential-bearing dev recipe binds its host port to `127.0.0.1` so apps on the same workstation can connect without publishing the service on every host interface. [source: https://docs.docker.com/reference/compose-file/services/#ports, version: Compose Specification, retrieved: 2026-07-15]
|
|
22
|
+
|
|
19
23
|
## Per-service prod transforms
|
|
20
24
|
|
|
21
25
|
### Postgres / MySQL / Redis (data tier)
|
|
@@ -42,7 +46,7 @@ The recipes default to **dev**. When `/dw-dockerize --prod` (or `--both`) emits
|
|
|
42
46
|
- **Remove entirely from prod compose.** LocalStack is dev-only.
|
|
43
47
|
- Replace with the real AWS endpoints; `AWS_ENDPOINT_URL` becomes unset (default to AWS).
|
|
44
48
|
|
|
45
|
-
###
|
|
49
|
+
### Mailpit / smtp4dev
|
|
46
50
|
|
|
47
51
|
- **Remove entirely from prod compose.** Email-in-dev tools are dev-only.
|
|
48
52
|
- Replace with a real provider via `SMTP_*` or API client (SendGrid, Resend, Postmark, SES).
|
|
@@ -80,6 +84,7 @@ The recipes default to **dev**. When `/dw-dockerize --prod` (or `--both`) emits
|
|
|
80
84
|
- Ship secrets in committed files.
|
|
81
85
|
- Use `:latest` tags.
|
|
82
86
|
- Expose data-tier ports publicly.
|
|
83
|
-
-
|
|
87
|
+
- Pin any image by digest (it blocks the base-image security rebuilds prod depends on).
|
|
88
|
+
- Include Mailpit / smtp4dev / LocalStack.
|
|
84
89
|
- Skip healthchecks.
|
|
85
90
|
- Forget to set non-root `USER` in the application Dockerfile.
|
|
@@ -8,14 +8,14 @@ elasticsearch:
|
|
|
8
8
|
environment:
|
|
9
9
|
discovery.type: single-node
|
|
10
10
|
xpack.security.enabled: "true"
|
|
11
|
-
ELASTIC_PASSWORD: ${ELASTIC_PASSWORD
|
|
11
|
+
ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:?Set ELASTIC_PASSWORD in .env}
|
|
12
12
|
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
|
|
13
13
|
ports:
|
|
14
|
-
- "
|
|
14
|
+
- "127.0.0.1:${ELASTICSEARCH_PORT:-9200}:9200"
|
|
15
15
|
volumes:
|
|
16
16
|
- elasticsearch_data:/usr/share/elasticsearch/data
|
|
17
17
|
healthcheck:
|
|
18
|
-
test: ["CMD-SHELL", "curl -fsu elastic:${ELASTIC_PASSWORD
|
|
18
|
+
test: ["CMD-SHELL", "curl -fsu elastic:${ELASTIC_PASSWORD} http://localhost:9200/_cluster/health | grep -E 'green|yellow'"]
|
|
19
19
|
interval: 10s
|
|
20
20
|
timeout: 5s
|
|
21
21
|
retries: 10
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
# Mailpit —
|
|
1
|
+
# Mailpit — maintained email capture for local development
|
|
2
2
|
# Env vars: MAILPIT_SMTP_PORT (default 1025), MAILPIT_UI_PORT (default 8025)
|
|
3
|
-
# Application-side SMTP config:
|
|
3
|
+
# Application-side SMTP config: SMTP_HOST=mailpit; SMTP_PORT=1025
|
|
4
4
|
|
|
5
5
|
mailpit:
|
|
6
|
-
image: axllent/mailpit:
|
|
6
|
+
image: axllent/mailpit:v1.30
|
|
7
7
|
restart: unless-stopped
|
|
8
8
|
ports:
|
|
9
9
|
- "${MAILPIT_SMTP_PORT:-1025}:1025"
|
|
@@ -24,4 +24,4 @@ mailpit:
|
|
|
24
24
|
# mailpit_data: {}
|
|
25
25
|
#
|
|
26
26
|
# Prod notes:
|
|
27
|
-
# - DEV ONLY. Do not ship to prod
|
|
27
|
+
# - DEV ONLY. Do not ship to prod; replace it with a real SMTP relay or transactional email API.
|
|
@@ -6,11 +6,11 @@ meilisearch:
|
|
|
6
6
|
image: getmeili/meilisearch:v1.10
|
|
7
7
|
restart: unless-stopped
|
|
8
8
|
environment:
|
|
9
|
-
MEILI_MASTER_KEY: ${MEILI_MASTER_KEY
|
|
9
|
+
MEILI_MASTER_KEY: ${MEILI_MASTER_KEY:?Set MEILI_MASTER_KEY in .env}
|
|
10
10
|
MEILI_ENV: ${MEILI_ENV:-development}
|
|
11
11
|
MEILI_NO_ANALYTICS: "true"
|
|
12
12
|
ports:
|
|
13
|
-
- "
|
|
13
|
+
- "127.0.0.1:${MEILISEARCH_PORT:-7700}:7700"
|
|
14
14
|
volumes:
|
|
15
15
|
- meilisearch_data:/meili_data
|
|
16
16
|
healthcheck:
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
# MinIO — S3-compatible object storage (dev replacement for AWS S3)
|
|
2
|
+
# Pinned, not `latest`: a floating tag makes a scan result describe what was pulled
|
|
3
|
+
# that day rather than what the next user runs. This is the newest release published
|
|
4
|
+
# to Docker Hub (2025-09-07); MinIO stopped pushing there afterwards, so `latest`
|
|
5
|
+
# currently resolves to the same image — the pin buys reproducibility, not fewer CVEs.
|
|
2
6
|
# Env vars: MINIO_ROOT_USER, MINIO_ROOT_PASSWORD, MINIO_API_PORT (default 9000), MINIO_UI_PORT (default 9001)
|
|
3
7
|
# Application-side: AWS_ENDPOINT_URL=http://minio:9000; AWS_ACCESS_KEY_ID=${MINIO_ROOT_USER}; AWS_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}
|
|
4
8
|
|
|
5
9
|
minio:
|
|
6
|
-
image: minio/minio:
|
|
10
|
+
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
|
|
7
11
|
restart: unless-stopped
|
|
8
12
|
command: server /data --console-address ":9001"
|
|
9
13
|
environment:
|
|
10
14
|
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minio}
|
|
11
|
-
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD
|
|
15
|
+
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:?Set MINIO_ROOT_PASSWORD in .env}
|
|
12
16
|
ports:
|
|
13
|
-
- "
|
|
14
|
-
- "
|
|
17
|
+
- "127.0.0.1:${MINIO_API_PORT:-9000}:9000"
|
|
18
|
+
- "127.0.0.1:${MINIO_UI_PORT:-9001}:9001"
|
|
15
19
|
volumes:
|
|
16
20
|
- minio_data:/data
|
|
17
21
|
healthcheck:
|
|
@@ -7,16 +7,16 @@ mysql:
|
|
|
7
7
|
restart: unless-stopped
|
|
8
8
|
command: --default-authentication-plugin=mysql_native_password
|
|
9
9
|
environment:
|
|
10
|
-
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD
|
|
10
|
+
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?Set MYSQL_ROOT_PASSWORD in .env}
|
|
11
11
|
MYSQL_USER: ${MYSQL_USER:-app}
|
|
12
|
-
MYSQL_PASSWORD: ${MYSQL_PASSWORD
|
|
12
|
+
MYSQL_PASSWORD: ${MYSQL_PASSWORD:?Set MYSQL_PASSWORD in .env}
|
|
13
13
|
MYSQL_DATABASE: ${MYSQL_DATABASE:-app}
|
|
14
14
|
ports:
|
|
15
|
-
- "
|
|
15
|
+
- "127.0.0.1:${MYSQL_PORT:-3306}:3306"
|
|
16
16
|
volumes:
|
|
17
17
|
- mysql_data:/var/lib/mysql
|
|
18
18
|
healthcheck:
|
|
19
|
-
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u${MYSQL_USER:-app} -p${MYSQL_PASSWORD
|
|
19
|
+
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u${MYSQL_USER:-app} -p${MYSQL_PASSWORD}"]
|
|
20
20
|
interval: 5s
|
|
21
21
|
timeout: 3s
|
|
22
22
|
retries: 10
|
|
@@ -7,10 +7,10 @@ postgres:
|
|
|
7
7
|
restart: unless-stopped
|
|
8
8
|
environment:
|
|
9
9
|
POSTGRES_USER: ${POSTGRES_USER:-app}
|
|
10
|
-
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD
|
|
10
|
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
|
|
11
11
|
POSTGRES_DB: ${POSTGRES_DB:-app}
|
|
12
12
|
ports:
|
|
13
|
-
- "
|
|
13
|
+
- "127.0.0.1:${POSTGRES_PORT:-5432}:5432"
|
|
14
14
|
volumes:
|
|
15
15
|
- postgres_data:/var/lib/postgresql/data
|
|
16
16
|
healthcheck:
|
|
@@ -7,10 +7,10 @@ rabbitmq:
|
|
|
7
7
|
restart: unless-stopped
|
|
8
8
|
environment:
|
|
9
9
|
RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER:-app}
|
|
10
|
-
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD
|
|
10
|
+
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD:?Set RABBITMQ_PASSWORD in .env}
|
|
11
11
|
ports:
|
|
12
|
-
- "
|
|
13
|
-
- "
|
|
12
|
+
- "127.0.0.1:${RABBITMQ_PORT:-5672}:5672"
|
|
13
|
+
- "127.0.0.1:${RABBITMQ_UI_PORT:-15672}:15672"
|
|
14
14
|
volumes:
|
|
15
15
|
- rabbitmq_data:/var/lib/rabbitmq
|
|
16
16
|
healthcheck:
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
# Application-side: TYPESENSE_HOST=typesense; TYPESENSE_PORT=8108; TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
|
|
4
4
|
|
|
5
5
|
typesense:
|
|
6
|
-
image: typesense/typesense:
|
|
6
|
+
image: typesense/typesense:30.2
|
|
7
7
|
restart: unless-stopped
|
|
8
|
-
command: --data-dir /data --api-key=${TYPESENSE_API_KEY
|
|
8
|
+
command: --data-dir /data --api-key=${TYPESENSE_API_KEY:?Set TYPESENSE_API_KEY in .env} --enable-cors
|
|
9
9
|
ports:
|
|
10
|
-
- "
|
|
10
|
+
- "127.0.0.1:${TYPESENSE_PORT:-8108}:8108"
|
|
11
11
|
volumes:
|
|
12
12
|
- typesense_data:/data
|
|
13
13
|
healthcheck:
|
|
@@ -27,7 +27,11 @@ with these slots. Substitute them into the steps below — everything else is id
|
|
|
27
27
|
|---|---|
|
|
28
28
|
| `DISPATCH` | first-run command (background, streaming → audit log, `</dev/null`) |
|
|
29
29
|
| `STREAM` | flag(s) that make it emit parseable streaming events (JSON/JSONL) |
|
|
30
|
-
| `
|
|
30
|
+
| `MODEL` | flag that selects the model tier for this dispatch |
|
|
31
|
+
| `EFFORT` | flag that selects the reasoning budget for this dispatch |
|
|
32
|
+
| `AUTO` | auto-approve / non-interactive flag for a **WRITE** dispatch (justified by the isolated worktree) |
|
|
33
|
+
| `AUTO_READONLY` | plan/sandbox flag for a **READ-ONLY** dispatch (makes edits impossible; no worktree needed) |
|
|
34
|
+
| `NO_MCP` | flag that starts the CLI with no MCP servers (cold-start trim) |
|
|
31
35
|
| `RESUME <id>` | re-run command that **continues the SAME session/context** by id |
|
|
32
36
|
| `RESUME_LAST` | "resume the last session of this cwd" fallback |
|
|
33
37
|
| `SESSION_ID` | how to obtain/fix the session-id (from the stream, a sidecar, or a flag) |
|
|
@@ -36,20 +40,30 @@ with these slots. Substitute them into the steps below — everything else is id
|
|
|
36
40
|
|
|
37
41
|
If the command did not supply an adapter table, return `BLOCKED` — this skill is not invoked standalone.
|
|
38
42
|
|
|
39
|
-
##
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
## Dispatch modes — WRITE and READ-ONLY
|
|
44
|
+
|
|
45
|
+
Every dispatch declares its mode up front; it decides isolation, permission flags, and gate. **WRITE** edits and
|
|
46
|
+
commits → worktree mandatory, `AUTO`, one per worktree, full 0–10 gate. **READ-ONLY** reads/reviews/reports → no
|
|
47
|
+
worktree, `AUTO_READONLY`, freely parallel, score the report without a re-run gate. Per-mode detail:
|
|
48
|
+
`references/dispatch-tuning.md`.
|
|
49
|
+
|
|
50
|
+
<critical>RULE (hard) — a WRITE dispatch runs **only** inside a **dedicated git worktree** (off main). **NEVER** fire a WRITE dispatch in the repo's **main checkout** (the root tree on the primary branch, e.g. `~/code/<project>` on `main`) — the CLI edits autonomously and that would corrupt the owner's active tree. If the target is the main checkout, **ABORT** (`BLOCKED`) and instruct: create/use a worktree first (`git worktree add ../<project>-<slug> -b <branch> main`). Mandatory check before running: `git worktree list` → the target is a secondary worktree (not the one flagged as main) **AND** the CLI's `cwd` is that worktree.</critical>
|
|
51
|
+
|
|
52
|
+
<critical>A READ-ONLY dispatch MAY run in the main checkout, because the read-only flags make autonomous edits impossible — that is the entire reason the worktree rule exists. This is NOT a relaxation of the WRITE rule: if the dispatch can write, it is a WRITE dispatch and the worktree is mandatory. When in doubt, treat it as WRITE. Never pass an auto-approve flag on a dispatch declared READ-ONLY.</critical>
|
|
46
53
|
|
|
47
54
|
## Pre-flight (fail early > run in the wrong place)
|
|
48
55
|
1. `<cli> --version` answers (CLI + credentials present). Missing → `BLOCKED`.
|
|
49
|
-
2.
|
|
50
|
-
3.
|
|
56
|
+
2. **Declare the mode** (WRITE or READ-ONLY) and pick the matching `AUTO`/`AUTO_READONLY` slot.
|
|
57
|
+
3. WRITE only: `git worktree list` — confirm the target worktree and that it is **not** the main checkout.
|
|
58
|
+
4. A **prompt/spec** exists (what the CLI receives). Common conventions: `.dw/spec/<slug>/codex-prompt.md`
|
|
51
59
|
(dev-workflow), `PROMPT.md`, `TASK.md`, or a path the owner names. **Read it** — it is the scope/fence/gate.
|
|
52
60
|
|
|
61
|
+
## Cold-start cost
|
|
62
|
+
A spawned CLI does not share the parent session's prompt cache and boots every configured MCP server first — on a
|
|
63
|
+
small task that startup can outweigh the task. **Default: pass the adapter's `NO_MCP` slot on every dispatch whose
|
|
64
|
+
prompt does not name an MCP capability.** Tool restriction, config trimming, and how to bind
|
|
65
|
+
`.dw/config/routing.json` are in `references/dispatch-tuning.md` — read it when a dispatch feels overpriced.
|
|
66
|
+
|
|
53
67
|
## Where to run — pick the VEHICLE (the main thread NEVER runs inline)
|
|
54
68
|
The main thread **orchestrates**; execution goes to one of these:
|
|
55
69
|
- **Workflow (PREFERRED)** — when there is a pipeline (CLI implements → evaluate 0–10 → **gate fan-out**). One
|
|
@@ -66,11 +80,12 @@ The main thread **orchestrates**; execution goes to one of these:
|
|
|
66
80
|
## Protocol
|
|
67
81
|
1. **Resolve target.** Worktree + prompt path (from the user or inferred). Derive a `<slug>` (the spec slug or
|
|
68
82
|
worktree tag) — it keys the durable audit log and the session sidecar.
|
|
69
|
-
2. **Model + effort —
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
(
|
|
73
|
-
E2E/secure-audit in the gate
|
|
83
|
+
2. **Model + effort — routing table first, size only what it misses.** `.dw/config/routing.json` is the default
|
|
84
|
+
source: commit type → tier (`by_commit_type`), raised if the fence matches `escalate_on_surface`, then tier →
|
|
85
|
+
`MODEL`/`EFFORT` for the brand. Costs no reasoning, reproducible. Absent, or no declared type? Size it: propose
|
|
86
|
+
with a one-line rationale (signals: task count, fence breadth, sensitive surface, architectural vs mechanical,
|
|
87
|
+
E2E/secure-audit in the gate); the owner confirms or overrides. Either way start **one notch below the
|
|
88
|
+
ceiling**. A tier that keeps escalating is a routing bug — name the entry that mis-sized it.
|
|
74
89
|
3. **Set up the durable audit dir (OUTSIDE the worktree).** `<AUDIT>=<main-repo-or-home>/.dw/cli-run/` — it MUST
|
|
75
90
|
survive `git worktree remove` (the worktree's `QA/` is wiped on merge). Files: `<AUDIT>/<slug>.log` (stream),
|
|
76
91
|
`<AUDIT>/<slug>.session` (session-id sidecar), optionally `<AUDIT>/<slug>.last.md` (final message).
|
|
@@ -129,32 +144,21 @@ without reaching 9. Always show the score + a short per-criterion rationale —
|
|
|
129
144
|
ceiling, **cite the specific gap** (the failing test, the file left out of fence), not just a label. Same
|
|
130
145
|
evidence discipline as the five-axis rubric in `dw-review-rigor/references/self-eval-rubric.md`.
|
|
131
146
|
|
|
132
|
-
## Dual evaluation: the
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
2. **Parent re-gate (independent).** When the CLI declares pass, the parent/orchestrator **re-runs the SAME gate**
|
|
141
|
-
(fan-out, prefer Workflow → `/workflows`) and gives its **own 0–10** — without trusting the self-score (a worker
|
|
142
|
-
never signs off on its own exam, even when that worker is Claude).
|
|
143
|
-
3. **Compare + decide.** Parent ≥9 and small gap → **PASS** (ready for the owner's merge decision). Parent <9 OR a
|
|
144
|
-
large gap (CLI overestimated) → re-execute: hand the gaps back via **session resume** (step 5, max effort) and
|
|
145
|
-
repeat 1→2→3 until it converges. **The score that counts for acceptance is the parent's**; the self-score is
|
|
146
|
-
signal + an inflation detector. Always record both scores + the gap in the Structured Return.
|
|
147
|
+
## Dual evaluation: the worker auto-gates → the parent re-gates
|
|
148
|
+
A worker **can't grade its own exam alone** — hence two layers. (1) The worker loops at max effort until its own
|
|
149
|
+
score is ≥9 with a green gate. (2) The parent independently re-runs the SAME gate and scores it, without trusting
|
|
150
|
+
the self-score — **a worker never signs off on its own exam, even when that worker is Claude**. (3) The parent's
|
|
151
|
+
score is what counts; a large gap means the worker overestimated → hand the gaps back via session resume and
|
|
152
|
+
repeat. Record both scores and the gap. Prefer a **different brand** for the re-gate when one is installed; with
|
|
153
|
+
only one, re-gate in a separate dispatch at a different tier — never reuse the executing session. Full protocol:
|
|
154
|
+
`references/dispatch-tuning.md`.
|
|
147
155
|
|
|
148
156
|
## Graded escalation on failure
|
|
149
157
|
If the score is low / the gate failed / the CLI didn't finish, **re-run the SAME task one notch up** — gradual, no
|
|
150
|
-
giving up on the first stumble, no jumping to the top.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
-
|
|
154
|
-
worktree → **reset first** (`git -C <worktree> reset --hard && git clean -fd`) and run fresh at the higher notch
|
|
155
|
-
— don't stack error on error.
|
|
156
|
-
- **Stop:** at **score ≥9** (ready for the gate) OR when **exhausted** (strongest model + max effort still below)
|
|
157
|
-
→ `BLOCKED` with evidence. Announce each notch; with autonomy, escalate to the ceiling yourself.
|
|
158
|
+
giving up on the first stumble, no jumping to the top. **Ladder:** effort first (`low`→`max`), then bump the model
|
|
159
|
+
one tier and reset effort. **Stop** at score ≥9, or `BLOCKED` with evidence when the ceiling is exhausted.
|
|
160
|
+
Announce each notch. Full ladder, continue-vs-restart rules, and how to read escalation as a routing-table signal:
|
|
161
|
+
`references/dispatch-tuning.md`.
|
|
158
162
|
|
|
159
163
|
## Detailed output → direct the next step
|
|
160
164
|
The delivery must be **detailed enough for the parent to decide the next step** — not an opaque "done". When it
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Dispatch tuning — trim the cold start, bind the routing table
|
|
2
|
+
|
|
3
|
+
Read this when a dispatch feels disproportionately expensive for the size of the task, or when wiring
|
|
4
|
+
`.dw/config/routing.json` for the first time. The protocol in `SKILL.md` works without it.
|
|
5
|
+
|
|
6
|
+
## Cold-start cost
|
|
7
|
+
|
|
8
|
+
A spawned CLI does not share the parent session's prompt cache and boots every configured MCP server before doing
|
|
9
|
+
any work. On a small task that startup can cost more than the task itself. This is the one real advantage an
|
|
10
|
+
in-session subagent keeps over a spawned CLI — so trim the spawn until the gap stops mattering.
|
|
11
|
+
|
|
12
|
+
| | Claude | Codex |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| No MCP servers | `--strict-mcp-config` (with no `--mcp-config`) | `-c mcp_servers='{}'` |
|
|
15
|
+
| Drop all user config | — | `--ignore-user-config` (blunt: also drops model defaults, so `MODEL` + `EFFORT` become mandatory) |
|
|
16
|
+
| Restrict tools | `--tools ""` disables all; `--allowedTools <list>` for a subset | `--sandbox read-only` |
|
|
17
|
+
|
|
18
|
+
**Default:** pass the MCP kill switch on every dispatch whose prompt does not name an MCP capability. If the
|
|
19
|
+
prompt needs docs lookup (`context7`) or browser automation (`playwright`), keep them and say so in the dispatch
|
|
20
|
+
rationale.
|
|
21
|
+
|
|
22
|
+
Verified against `claude` 2.1.220 and `codex-cli` 0.144.4. Re-check with `claude --help` / `codex exec --help`
|
|
23
|
+
after a CLI upgrade — these flags are not covered by either project's stability guarantees.
|
|
24
|
+
|
|
25
|
+
## Binding `.dw/config/routing.json`
|
|
26
|
+
|
|
27
|
+
Resolution order for a dispatch:
|
|
28
|
+
|
|
29
|
+
1. Read the task's Conventional-Commit type (already in `tasks.md` and in the atomic commit subject).
|
|
30
|
+
2. `by_commit_type[<type>]` → tier name.
|
|
31
|
+
3. If the fence or the task title matches any `escalate_on_surface.patterns`, override the tier upward.
|
|
32
|
+
4. `tiers[<tier>][<brand>]` → concrete `MODEL` + `EFFORT`.
|
|
33
|
+
5. Substitute into the adapter's `MODEL`/`EFFORT` slots.
|
|
34
|
+
|
|
35
|
+
If the file is absent, or the task has no declared type, fall back to the sizing heuristic in `SKILL.md` step 2.
|
|
36
|
+
|
|
37
|
+
`routing.json` is seeded once on init and never reconciled on update, because model availability differs per
|
|
38
|
+
account. Treat the shipped model ids as defaults to verify, not as a guarantee of access — a dispatch that fails
|
|
39
|
+
with an unknown-model error is a routing-table edit, not a protocol bug.
|
|
40
|
+
|
|
41
|
+
## Dual evaluation in full
|
|
42
|
+
|
|
43
|
+
The CLI auto-gates cheaply (close to the work); the parent/orchestrator audits independently and compares the
|
|
44
|
+
scores, which is what catches an inflated self-score. The parent is the dispatching session — possibly Claude
|
|
45
|
+
itself; the re-gate is provider-neutral.
|
|
46
|
+
|
|
47
|
+
1. **Worker auto-gate (loop, MAX effort).** The prompt MUST instruct: after implementing, **run the SAME gate**
|
|
48
|
+
and give a **self-score 0–10**; **fix and re-run while the self-score <9 or the gate isn't green**, at max
|
|
49
|
+
effort. Stop at self-score ≥9 + green gate (or report `blockers`). The final report carries the self-score plus
|
|
50
|
+
a per-criterion breakdown.
|
|
51
|
+
2. **Parent re-gate (independent).** When the worker declares pass, the parent **re-runs the SAME gate** (fan-out,
|
|
52
|
+
prefer Workflow → `/workflows`) and gives its **own 0–10**, without trusting the self-score. Prefer a different
|
|
53
|
+
brand for this step when both are installed — a second vendor fails differently, which is the whole point of
|
|
54
|
+
the second layer. With one brand installed, re-gate in a separate dispatch at a different tier.
|
|
55
|
+
3. **Compare + decide.** Parent ≥9 and small gap → **PASS** (ready for the owner's merge decision). Parent <9 OR a
|
|
56
|
+
large gap (worker overestimated) → re-execute: hand the gaps back via **session resume** at max effort and
|
|
57
|
+
repeat 1→2→3 until it converges. **The score that counts for acceptance is the parent's**; the self-score is
|
|
58
|
+
signal plus an inflation detector. Always record both scores and the gap in the Structured Return.
|
|
59
|
+
|
|
60
|
+
## The escalation ladder in full
|
|
61
|
+
|
|
62
|
+
If the score is low, the gate failed, or the CLI didn't finish, re-run the SAME task one notch up — gradual, no
|
|
63
|
+
giving up on the first stumble, no jumping to the top.
|
|
64
|
+
|
|
65
|
+
- **Ladder (one at a time):** first **effort** `low`→`medium`→`high`→`xhigh`(→`max` on Claude; the adapter names
|
|
66
|
+
the levels it supports); once exhausted, bump the **model** one tier and reset effort to `high`. Re-run and
|
|
67
|
+
**re-score**.
|
|
68
|
+
- **Continue vs restart:** coherent partial edits → **resume the session** (keeps its context and the files it
|
|
69
|
+
already touched). Broken or dirty worktree → **reset first**
|
|
70
|
+
(`git -C <worktree> reset --hard && git clean -fd`) and run fresh at the higher notch. Don't stack error on
|
|
71
|
+
error.
|
|
72
|
+
- **Stop:** at **score ≥9** (ready for the owner's gate), OR when **exhausted** (strongest model at max effort
|
|
73
|
+
still below the bar) → `BLOCKED` with evidence. Announce each notch; with autonomy, escalate to the ceiling
|
|
74
|
+
yourself rather than asking at every step.
|
|
75
|
+
|
|
76
|
+
## Reading escalation as a routing signal
|
|
77
|
+
|
|
78
|
+
A task that routinely needs two or more notches above what the table assigned is telling you the table is wrong
|
|
79
|
+
for that commit type. Report which
|
|
80
|
+
entry mis-sized it in the Structured Return, so the next edit to `routing.json` is grounded in a real run rather
|
|
81
|
+
than a guess.
|
|
82
|
+
|
|
83
|
+
The reverse also matters and is easier to miss: a tier that never escalates may be over-provisioned. If `feat`
|
|
84
|
+
work consistently lands at score ≥9 on the first try, try the tier below it before assuming the current one is
|
|
85
|
+
load-bearing.
|
|
@@ -80,7 +80,7 @@ Detail in `references/atomic-commits.md` (deviation entry format) and `reference
|
|
|
80
80
|
Every task ends with exactly one git commit:
|
|
81
81
|
|
|
82
82
|
```
|
|
83
|
-
feat(<scope>): <task title> (
|
|
83
|
+
feat(<scope>): <task title> (#<task-id>)
|
|
84
84
|
|
|
85
85
|
<one-line summary of what this commit delivers>
|
|
86
86
|
|
|
@@ -89,9 +89,11 @@ feat(<scope>): <task title> (RF-XX)
|
|
|
89
89
|
- Tests added/updated: <list>
|
|
90
90
|
- Deviations: <link to deviations.md entry, if any>
|
|
91
91
|
|
|
92
|
-
Closes
|
|
92
|
+
Closes <REQ-ID> (partial — see tasks.md).
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
+
`<REQ-ID>` is copied verbatim from `tasks.md` — `FR-N.M` in English projects, `RF-N.M` in Portuguese ones. This skill is shared by both; never hardcode a prefix.
|
|
96
|
+
|
|
95
97
|
The commit message format is consistent across waves so `/dw-generate-pr` can build a clean PR body. See `references/atomic-commits.md`.
|
|
96
98
|
|
|
97
99
|
## De-Sloppify Pass (optional cleanup task)
|
|
@@ -88,7 +88,7 @@ For each wave:
|
|
|
88
88
|
Format (per `references/atomic-commits.md`):
|
|
89
89
|
|
|
90
90
|
```
|
|
91
|
-
<type>(<scope>): <task title> (
|
|
91
|
+
<type>(<scope>): <task title> (#<task-id>)
|
|
92
92
|
|
|
93
93
|
<one-line summary>
|
|
94
94
|
|
|
@@ -97,9 +97,11 @@ Format (per `references/atomic-commits.md`):
|
|
|
97
97
|
- Tests added/updated: <list>
|
|
98
98
|
- Deviations: <link or "none">
|
|
99
99
|
|
|
100
|
-
Closes
|
|
100
|
+
Closes <REQ-ID> (partial — full close on tasks.md completion).
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
+
`<REQ-ID>` is copied verbatim from `tasks.md` (`FR-N.M` in English projects, `RF-N.M` in Portuguese ones) — never assume a prefix.
|
|
104
|
+
|
|
103
105
|
Type from Conventional Commits (`feat`, `fix`, `refactor`, `test`, `docs`, `chore`). Scope from task domain. Run `git add -A && git commit` once.
|
|
104
106
|
|
|
105
107
|
### Step 5: Deviation handling (when implementation diverges from plan)
|
|
@@ -50,7 +50,7 @@ Then verify each level against the actual plan files.
|
|
|
50
50
|
|
|
51
51
|
- `prd_path` (from spawn prompt): `.dw/spec/prd-<slug>/`
|
|
52
52
|
- Reads:
|
|
53
|
-
- `<prd_path>/prd.md` — the goal, requirements (
|
|
53
|
+
- `<prd_path>/prd.md` — the goal, requirements (`<REQ-ID>`, see Dimension 1), acceptance criteria
|
|
54
54
|
- `<prd_path>/techspec.md` — architecture decisions
|
|
55
55
|
- `<prd_path>/tasks.md` — the plan to verify
|
|
56
56
|
- `<prd_path>/<NN>_task.md` — per-task detail
|
|
@@ -62,15 +62,17 @@ Then verify each level against the actual plan files.
|
|
|
62
62
|
|
|
63
63
|
### Dimension 1: Requirement Coverage
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
<critical>Requirement IDs are language-dependent. English projects number them `FR-N.M`; Portuguese projects `RF-N.M`. This skill is shared by both — NEVER hardcode a prefix. Read the IDs verbatim from `prd.md` and match those exact strings in `tasks.md`. `<REQ-ID>` below stands for whichever form the project uses.</critical>
|
|
66
|
+
|
|
67
|
+
**Question:** Does every PRD requirement have at least one task addressing it?
|
|
66
68
|
|
|
67
69
|
**Process:**
|
|
68
|
-
1. Extract every numbered requirement from `prd.md`
|
|
69
|
-
2. For each
|
|
70
|
+
1. Extract every numbered requirement ID from `prd.md` verbatim (`FR-1.1`, `RF-1.1`, …)
|
|
71
|
+
2. For each `<REQ-ID>`, search `tasks.md` for tasks tagged with that exact ID
|
|
70
72
|
3. List uncovered requirements
|
|
71
73
|
|
|
72
|
-
**Pass:** every
|
|
73
|
-
**Revise:** ≥1
|
|
74
|
+
**Pass:** every requirement has at least one task.
|
|
75
|
+
**Revise:** ≥1 requirement has no task; the planner missed it.
|
|
74
76
|
**Block:** the PRD has no requirements at all (plan can't verify).
|
|
75
77
|
|
|
76
78
|
### Dimension 2: Task Completeness
|
|
@@ -174,7 +176,7 @@ After running all 6 dimensions:
|
|
|
174
176
|
|
|
175
177
|
### REVISE — Dimension 1: Requirement Coverage
|
|
176
178
|
|
|
177
|
-
- **
|
|
179
|
+
- **FR-4.1** ("user can reset password via email link") has no task. Suggested: add task between 05 and 06.
|
|
178
180
|
|
|
179
181
|
### REVISE — Dimension 2: Task Completeness
|
|
180
182
|
|
|
@@ -7,7 +7,7 @@ Every task in a phase commits exactly once. This drives traceability (a task's d
|
|
|
7
7
|
Strict format. No deviations.
|
|
8
8
|
|
|
9
9
|
```
|
|
10
|
-
<type>(<scope>): <task title> (
|
|
10
|
+
<type>(<scope>): <task title> (#<task-id>)
|
|
11
11
|
|
|
12
12
|
<one-line summary>
|
|
13
13
|
|
|
@@ -16,9 +16,11 @@ Strict format. No deviations.
|
|
|
16
16
|
- Tests added/updated: <comma-separated list, or "none">
|
|
17
17
|
- Deviations: <link to deviations.md entry, or "none">
|
|
18
18
|
|
|
19
|
-
Closes
|
|
19
|
+
Closes <REQ-ID> (partial — full close on tasks.md completion).
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
<critical>`<REQ-ID>` is the requirement ID exactly as `prd.md` and `tasks.md` write it. English projects use `FR-N.M`; Portuguese projects use `RF-N.M`. This skill is shared by both — copy the ID verbatim, never assume a prefix.</critical>
|
|
23
|
+
|
|
22
24
|
### Field rules
|
|
23
25
|
|
|
24
26
|
**`<type>`** — Conventional Commits:
|
|
@@ -36,7 +38,7 @@ Closes RF-XX (partial — full close on tasks.md completion).
|
|
|
36
38
|
|
|
37
39
|
**`<task title>`** — copy from `tasks.md` task line. Imperative, concise. "Add login endpoint", not "Added login endpoint" or "Adding login endpoint".
|
|
38
40
|
|
|
39
|
-
**`(
|
|
41
|
+
**`(#<task-id>)`** — the task ID from `tasks.md` (`#1.0`, `#2.3`). This is what makes the commit findable from the task index — `/dw-run` writes the resulting SHA back into the `tasks.md` Commit column.
|
|
40
42
|
|
|
41
43
|
**`<one-line summary>`** — one sentence answering "what does this commit deliver?". Different from the title (which is the task title); this is the OUTCOME.
|
|
42
44
|
|
|
@@ -44,12 +46,14 @@ Closes RF-XX (partial — full close on tasks.md completion).
|
|
|
44
46
|
|
|
45
47
|
**Deviations link** — `.dw/spec/prd-<slug>/deviations.md#deviation-03-1` if the task triggered a Rule 1 or 2 deviation.
|
|
46
48
|
|
|
47
|
-
**Closes line** — `Closes
|
|
49
|
+
**Closes line** — `Closes <REQ-ID> (partial — full close on tasks.md completion)` because one task usually doesn't fully close a requirement; the whole phase does. Final task in the phase changes "(partial — ...)" to "(full)". If the task closes more than one requirement, list them comma-separated.
|
|
48
50
|
|
|
49
51
|
## Examples
|
|
50
52
|
|
|
53
|
+
Both examples come from an English project (`FR-`); a Portuguese project writes `RF-` in the same slots.
|
|
54
|
+
|
|
51
55
|
```
|
|
52
|
-
feat(auth): wire JWT middleware to all /api/* routes (
|
|
56
|
+
feat(auth): wire JWT middleware to all /api/* routes (#4.0)
|
|
53
57
|
|
|
54
58
|
Authenticated routes now reject requests without valid Bearer tokens.
|
|
55
59
|
|
|
@@ -58,11 +62,11 @@ Authenticated routes now reject requests without valid Bearer tokens.
|
|
|
58
62
|
- Tests added/updated: src/middleware/auth.test.ts (12 cases — happy path, expired, malformed, missing)
|
|
59
63
|
- Deviations: none
|
|
60
64
|
|
|
61
|
-
Closes
|
|
65
|
+
Closes FR-4.1 (partial — full close on tasks.md completion).
|
|
62
66
|
```
|
|
63
67
|
|
|
64
68
|
```
|
|
65
|
-
test(orders): add integration tests for order creation flow (
|
|
69
|
+
test(orders): add integration tests for order creation flow (#7.0)
|
|
66
70
|
|
|
67
71
|
Covers happy path, payment failure, inventory mismatch.
|
|
68
72
|
|
|
@@ -71,7 +75,7 @@ Covers happy path, payment failure, inventory mismatch.
|
|
|
71
75
|
- Tests added/updated: tests/integration/orders.test.ts (8 cases)
|
|
72
76
|
- Deviations: .dw/spec/prd-checkout-v2/deviations.md#deviation-08-1
|
|
73
77
|
|
|
74
|
-
Closes
|
|
78
|
+
Closes FR-7.2 (partial — full close on tasks.md completion).
|
|
75
79
|
```
|
|
76
80
|
|
|
77
81
|
## Verification before commit
|
|
@@ -10,12 +10,14 @@ Mid-execution discovery of plan flaws is expensive: context burned, partial comm
|
|
|
10
10
|
|
|
11
11
|
### 1. Requirement Coverage
|
|
12
12
|
|
|
13
|
-
**Goal:** every PRD requirement (
|
|
13
|
+
**Goal:** every PRD requirement (`<REQ-ID>`) has at least one task addressing it.
|
|
14
|
+
|
|
15
|
+
<critical>`<REQ-ID>` is language-dependent: `FR-N.M` in English projects, `RF-N.M` in Portuguese ones. This skill is shared by both — extract the IDs verbatim from `prd.md` and match those exact strings downstream; never hardcode a prefix.</critical>
|
|
14
16
|
|
|
15
17
|
**Steps:**
|
|
16
|
-
1. Extract numbered requirements from `prd.md`. Pattern: `###
|
|
17
|
-
2. Build `Set<
|
|
18
|
-
3. Scan `tasks.md` and `<NN>_task.md` files for `Closes
|
|
18
|
+
1. Extract numbered requirements from `prd.md`. Pattern: `### <REQ-ID>` or `**<REQ-ID>:**` or numbered list under "Functional Requirements" / "Requisitos Funcionais".
|
|
19
|
+
2. Build `Set<REQ-ID>` of expected requirements.
|
|
20
|
+
3. Scan `tasks.md` and `<NN>_task.md` files for `Closes <REQ-ID>` / the `FRs`/`RFs` table column / `Requirement: <REQ-ID>` markers.
|
|
19
21
|
4. Compute `uncovered = expected - addressed`.
|
|
20
22
|
|
|
21
23
|
**Pass:** `uncovered` is empty.
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# MailHog — DEV-ONLY email capture (NEVER sends real mail)
|
|
2
|
-
# This is the dev-workflow DEFAULT for email-in-dev so the project cannot accidentally email real users.
|
|
3
|
-
# Env vars: MAILHOG_SMTP_PORT (default 1025), MAILHOG_UI_PORT (default 8025)
|
|
4
|
-
# Application-side SMTP config:
|
|
5
|
-
# SMTP_HOST=mailhog ; SMTP_PORT=1025 ; SMTP_USER= ; SMTP_PASSWORD= ; SMTP_SECURE=false
|
|
6
|
-
|
|
7
|
-
mailhog:
|
|
8
|
-
image: mailhog/mailhog:latest
|
|
9
|
-
restart: unless-stopped
|
|
10
|
-
ports:
|
|
11
|
-
- "${MAILHOG_SMTP_PORT:-1025}:1025"
|
|
12
|
-
- "${MAILHOG_UI_PORT:-8025}:8025"
|
|
13
|
-
healthcheck:
|
|
14
|
-
test: ["CMD", "wget", "--quiet", "--spider", "http://localhost:8025"]
|
|
15
|
-
interval: 10s
|
|
16
|
-
timeout: 3s
|
|
17
|
-
retries: 5
|
|
18
|
-
start_period: 5s
|
|
19
|
-
|
|
20
|
-
# Prod notes:
|
|
21
|
-
# - MailHog must NEVER ship to prod. Replace with a real SMTP relay or transactional email API
|
|
22
|
-
# (SendGrid, Resend, Postmark, SES) when promoting docker-compose.dev.yml to prod.
|
|
23
|
-
# - Open the UI at http://localhost:8025 to inspect captured mail; nothing leaves the host.
|