duaer-spec 0.1.1 → 0.2.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.
@@ -32,3 +32,4 @@ Forbidden: jump from chat to code with no `spec.md` / feature directory under `.
32
32
  - Implement only from `tasks.md` when it exists
33
33
  - Update `.duaer/memory/project-context.md` when architecture changes
34
34
  - Run the verification named in the feature docs before merge
35
+ - Before merge: `duaer check . --gate` (Spec present, no open tasks, `delivery.json` accepted)
@@ -71,10 +71,17 @@ of remaining work as a new, traceable task** at the bottom of `tasks.md` so that
71
71
  This is **not** a diff tool and does **not** track changes. It assesses the present state
72
72
  of the code relative to the feature's artifacts — no git, no branch comparison, no history.
73
73
 
74
+ **Delivery stamp:** Always update `FEATURE_DIR/delivery.json` after assessment (see Step 7).
75
+ That file is what `duaer check --gate` reads for machine-checkable acceptance.
76
+
74
77
  ## Operating Constraints
75
78
 
76
- **APPEND-ONLY, NEVER REWRITE**: The command's **only** write is appending a new
77
- `## Phase N: Convergence` section to `tasks.md`. It MUST NOT:
79
+ **APPEND-ONLY FOR TASKS, STAMP FOR DELIVERY**: The command's writes are limited to:
80
+
81
+ 1. appending a new `## Phase N: Convergence` section to `tasks.md` when findings exist;
82
+ 2. writing / overwriting `FEATURE_DIR/delivery.json` (the machine-readable accept stamp).
83
+
84
+ It MUST NOT:
78
85
 
79
86
  - modify `spec.md` or `plan.md` in any way;
80
87
  - rewrite, renumber, reorder, or delete any existing task (including tasks from a prior
@@ -83,7 +90,8 @@ of the code relative to the feature's artifacts — no git, no branch comparison
83
90
  job of `/duaer-implement`.
84
91
 
85
92
  When the codebase already satisfies everything, the command MUST leave `tasks.md`
86
- **byte-for-byte unchanged** (no empty Convergence header) and report a clean result.
93
+ **byte-for-byte unchanged** (no empty Convergence header) and still write
94
+ `delivery.json` with `status: "accepted"`.
87
95
 
88
96
  **Constitution Authority**: The project constitution (`.duaer/memory/constitution.md`) is
89
97
  **non-negotiable**. Code that violates a MUST principle is the highest-severity finding and
@@ -221,20 +229,51 @@ Append to the **end** of `tasks.md`, per the append contract:
221
229
  `CRITICAL`.
222
230
  4. Never reuse or renumber existing IDs. If a prior Convergence phase exists, add a new,
223
231
  separately-numbered one below it — do not touch the old one.
232
+ 5. **Write / overwrite** `FEATURE_DIR/delivery.json`:
233
+
234
+ ```json
235
+ {
236
+ "schemaVersion": 1,
237
+ "status": "open",
238
+ "outcome": "tasks_appended",
239
+ "checkedAt": "<ISO-8601 UTC>",
240
+ "openTasks": <count of - [ ] in tasks.md after append>,
241
+ "findings": <number of findings appended>,
242
+ "source": "duaer-converge"
243
+ }
244
+ ```
224
245
 
225
246
  **If there are no actionable findings** (`converged` outcome):
226
247
 
227
248
  - Do **not** modify `tasks.md` at all — no empty phase header.
249
+ - **Write / overwrite** `FEATURE_DIR/delivery.json` with:
250
+
251
+ ```json
252
+ {
253
+ "schemaVersion": 1,
254
+ "status": "accepted",
255
+ "outcome": "converged",
256
+ "checkedAt": "<ISO-8601 UTC>",
257
+ "openTasks": 0,
258
+ "findings": 0,
259
+ "source": "duaer-converge"
260
+ }
261
+ ```
262
+
263
+ Set `openTasks` to the count of unchecked `- [ ]` lines currently in `tasks.md`
264
+ (must be `0` for `status: "accepted"`; if any remain, treat as `tasks_appended`
265
+ path instead — do not stamp accepted).
228
266
  - Report: **"✅ Converged — the implementation satisfies the spec, plan, and tasks."**
229
267
  - Include the summary counts of what was checked.
230
-
268
+ - Tell the user they can run `duaer check . --gate` to verify the machine gate.
231
269
  ### 8. Provide Next Actions (Handoff)
232
270
 
233
271
  - On `tasks_appended`: state how many tasks were appended under which phase, and recommend
234
272
  running `/duaer-implement` to complete them; note that a follow-up converge
235
- run will find fewer or no remaining items.
236
- - On `converged`: recommend proceeding to review / opening a PR. No further implement pass
237
- is needed for this feature's specified scope.
273
+ run will find fewer or no remaining items. Remind: `delivery.json` is `open` —
274
+ `duaer check --gate` will fail until accepted.
275
+ - On `converged`: recommend `duaer check . --gate`, then review / opening a PR. No further
276
+ implement pass is needed for this feature's specified scope.
238
277
 
239
278
  ### 9. Check for extension hooks
240
279
 
@@ -35,3 +35,8 @@ Do not expand scope without updating the spec and tasks.
35
35
 
36
36
  Mark tasks complete only when the stated verification (tests, manual checks,
37
37
  or converge) has been performed or explicitly waived in the feature docs.
38
+
39
+ Before merge, run `duaer check . --gate`. It fails on missing Spec, open
40
+ `- [ ]` tasks, or `delivery.json` that is not `status: "accepted"`. Converge
41
+ assessment is still agent-assisted; the gate makes the **handoff state**
42
+ machine-checkable.
package/ADOPT.md CHANGED
@@ -11,7 +11,7 @@ From the target project (Node 18+):
11
11
  npx duaer-spec init --here
12
12
 
13
13
  # Pin a release
14
- npx github:fujiezee/duaer-spec@v0.1.1 duaer init --here
14
+ npx github:fujiezee/duaer-spec@v0.2.0 duaer init --here
15
15
 
16
16
  # Lite (method only) / ops only / other integration branch
17
17
  npx duaer-spec init --here --method
@@ -31,9 +31,10 @@ Then:
31
31
 
32
32
  1. **Orient** — edit `.duaer/memory/constitution.md` and `project-context.md`
33
33
  2. **Confirm workplace** — `docs/baseline.md` (integration branch)
34
- 3. **Assign → work → accept** — `/duaer-specify` → `/duaer-plan` → `/duaer-tasks` → `/duaer-implement` → `/duaer-converge`
34
+ 3. **Assign → work → accept → gate** — `/duaer-specify` → `/duaer-plan` → `/duaer-tasks` → `/duaer-implement` → `/duaer-converge` → `duaer check . --gate`
35
35
 
36
- A job without Spec is not assigned. A change that fails converge is not accepted.
36
+ A job without Spec is not assigned. Open tasks or a non-`accepted` `delivery.json`
37
+ is not accepted. `duaer check --gate` is the machine merge check.
37
38
 
38
39
  ## Manual copy (optional)
39
40
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0 — 2026-09-15
4
+
5
+ ### Controllable delivery (machine gate)
6
+
7
+ - `/duaer-converge` writes `.duaer/specs/<feature>/delivery.json`
8
+ (`accepted` | `open`)
9
+ - `duaer check` reports workplace **and** delivery status
10
+ - `duaer check --gate` fails on missing Spec, open tasks, or non-accepted stamp
11
+ - Constitution, DUADER, README, ADOPT, and merge checklist require the gate
12
+ - Honest scope: gate checks **handoff state**; Spec↔code judgment remains
13
+ agent-assisted
14
+
15
+ ### E2E
16
+
17
+ - E2E-003 / E2E-004 / E2E-005 for delivery report and gate
18
+
3
19
  ## 0.1.1 — 2026-09-15
4
20
 
5
21
  ### Docs / product identity
package/DUADER.md CHANGED
@@ -28,12 +28,13 @@ over anything here for isolation, commits, and Issue/PR gates.
28
28
  6. `/duaer-tasks` — checkbox task breakdown
29
29
  7. `/duaer-analyze` — optional consistency check
30
30
  8. `/duaer-implement` — implement tasks only
31
- 9. `/duaer-converge` — **Accept**: compare result to Spec; append remaining tasks if gaps
31
+ 9. `/duaer-converge` — **Accept**: compare result to Spec; write `delivery.json`; append remaining tasks if gaps
32
+ 10. `duaer check . --gate` — **Gate**: fail merge if Spec missing, tasks open, or stamp not accepted
32
33
 
33
- **Small feature:** specify → plan → tasks → implement → converge
34
+ **Small feature:** specify → plan → tasks → implement → converge → `check --gate`
34
35
 
35
- **Hotfix:** specify (mark hotfix) → tasks → implement → converge
36
- Never skip specify (assign) or converge (accept).
36
+ **Hotfix:** specify (mark hotfix) → tasks → implement → converge → `check --gate`
37
+ Never skip specify (assign), converge (accept), or the gate.
37
38
 
38
39
  ## Install
39
40
 
@@ -48,10 +49,11 @@ installs this repository's method files into the target project.
48
49
 
49
50
  ```text
50
51
  .duaer/specs/<nnn-slug>/
51
- spec.md # Brief
52
- plan.md # after /duaer-plan
53
- tasks.md # after /duaer-tasks
54
- # research / contracts as needed
52
+ spec.md # Brief
53
+ plan.md # after /duaer-plan
54
+ tasks.md # after /duaer-tasks
55
+ delivery.json # after /duaer-converge (accepted | open)
56
+ … # research / contracts as needed
55
57
  ```
56
58
 
57
59
  ## Related
package/README.md CHANGED
@@ -4,8 +4,8 @@
4
4
  **digital employees**.
5
5
 
6
6
  Agents write the code. Duaer makes the **handoff controllable**: every job has a
7
- Brief (Spec), follows a fixed work order, and only counts as done after
8
- acceptance — not after a lucky chat.
7
+ Brief (Spec), follows a fixed work order, and only counts as done when the
8
+ **delivery gate** passes — not after a lucky chat.
9
9
 
10
10
  Cursor is the labor. Duaer is hire → assign → accept.
11
11
 
@@ -13,7 +13,7 @@ Cursor is the labor. Duaer is hire → assign → accept.
13
13
 
14
14
  ```bash
15
15
  npx duaer-spec init --here
16
- # or pin: npx github:fujiezee/duaer-spec@v0.1.1 duaer init --here
16
+ # or pin: npx github:fujiezee/duaer-spec@v0.2.0 duaer init --here
17
17
  ```
18
18
 
19
19
  ```bash
@@ -25,6 +25,7 @@ npx duaer-spec init --here --ops --branch develop
25
25
  ```
26
26
 
27
27
  Verify: `npx duaer-spec check .`
28
+ Merge gate: `npx duaer-spec check . --gate`
28
29
  Details: [`ADOPT.md`](ADOPT.md)
29
30
 
30
31
  ## How you run them
@@ -34,10 +35,22 @@ Details: [`ADOPT.md`](ADOPT.md)
34
35
  | **Hire** | Install SOP + gates into the repo | `duaer init` |
35
36
  | **Assign** | Write the Brief (what / why / acceptance) | `/duaer-specify` |
36
37
  | **Work** | Plan, break down, implement | `/duaer-plan` → `/duaer-tasks` → `/duaer-implement` |
37
- | **Accept** | Compare result to Spec; gaps become more work | `/duaer-converge` + `duaer check` |
38
+ | **Accept** | Converge stamps `delivery.json`; gaps stay open | `/duaer-converge` |
39
+ | **Gate** | Machine check before merge | `duaer check . --gate` |
38
40
 
39
- Never skip **Assign** or **Accept**. Hotfix may shorten the middle; Spec and
40
- converge stay.
41
+ Never skip **Assign** or **Accept**. Hotfix may shorten the middle; Spec,
42
+ converge, and the gate stay.
43
+
44
+ ### What the gate actually enforces
45
+
46
+ `duaer check --gate` fails when any feature under `.duaer/specs/` has:
47
+
48
+ - missing `spec.md`
49
+ - open `- [ ]` tasks in `tasks.md`
50
+ - missing `delivery.json`, or `status` other than `accepted`
51
+
52
+ Converge judgment is still agent-assisted. The gate makes **handoff state**
53
+ checkable (and CI-friendly), not a substitute for tests.
41
54
 
42
55
  ## Two layers
43
56
 
@@ -51,7 +64,7 @@ When they conflict, **agent ops win** — process beats improvisation.
51
64
  ## Default loop
52
65
 
53
66
  ```text
54
- constitution → specify → plan → tasks → implement → converge
67
+ constitution → specify → plan → tasks → implement → converge → check --gate
55
68
  ```
56
69
 
57
70
  Small change: `specify → plan → tasks → implement → converge`
@@ -69,6 +82,7 @@ AGENTS.md Agent-ops contract (how employees operate)
69
82
  DUADER.md Methodology conventions (how work is briefed)
70
83
  ADOPT.md Onboarding guide
71
84
  .duaer/ Memory, templates, workflows, scripts
85
+ .duaer/specs/<feature>/ Brief + tasks + delivery.json stamp
72
86
  .cursor/rules/ Agent-ops + Duaer rules
73
87
  .cursor/skills/ duaer-* skills
74
88
  docs/agent/ Workflow detail + checklists
package/bin/duaer.mjs CHANGED
@@ -3,7 +3,7 @@
3
3
  * duaer — CLI for the duaer-spec delivery OS (digital employees)
4
4
  *
5
5
  * duaer init [dir] [--all|--method|--ops] [--force] [--branch <name>]
6
- * duaer check [dir]
6
+ * duaer check [dir] [--workplace|--delivery|--gate]
7
7
  * duaer version
8
8
  * duaer help
9
9
  */
@@ -26,13 +26,13 @@ const PKG = JSON.parse(readFileSync(join(PKG_ROOT, 'package.json'), 'utf8'))
26
26
 
27
27
  const USAGE = `duaer — Delivery OS for AI digital employees (duaer-spec ${PKG.version})
28
28
 
29
- Hire agents into a repo, assign Briefs (Specs), accept only when converge passes.
29
+ Hire agents into a repo, assign Briefs (Specs), accept only when the gate passes.
30
30
 
31
31
  Usage:
32
- duaer init [dir] [options] Hire / onboard into a project
33
- duaer check [dir] Verify install (workplace ready)
34
- duaer version Print version
35
- duaer help Show this help
32
+ duaer init [dir] [options] Hire / onboard into a project
33
+ duaer check [dir] [options] Workplace + delivery status
34
+ duaer version Print version
35
+ duaer help Show this help
36
36
 
37
37
  Init options:
38
38
  --all Full hire: agent ops + method (default)
@@ -42,11 +42,17 @@ Init options:
42
42
  --branch <n> Integration branch for baseline note (default: main)
43
43
  --here Same as dir=.
44
44
 
45
+ Check options:
46
+ --workplace Only verify install files
47
+ --delivery Only report feature Brief / open tasks / delivery.json
48
+ --gate Fail unless every feature is accepted (merge gate)
49
+ (default) Workplace + delivery report; exit 1 on workplace miss
50
+ or delivery blockers (missing Spec / open tasks / open stamp)
51
+
45
52
  Examples:
46
53
  npx duaer-spec init --here
47
- npx github:fujiezee/duaer-spec duaer init --here
48
- node bin/duaer.mjs init ../my-app --all
49
54
  duaer check .
55
+ duaer check . --gate
50
56
  `
51
57
 
52
58
  function parseArgs(argv) {
@@ -57,6 +63,9 @@ function parseArgs(argv) {
57
63
  mode: 'all',
58
64
  force: false,
59
65
  branch: 'main',
66
+ workplace: false,
67
+ delivery: false,
68
+ gate: false,
60
69
  }
61
70
  const rest = args.slice(1)
62
71
  for (let i = 0; i < rest.length; i++) {
@@ -66,6 +75,9 @@ function parseArgs(argv) {
66
75
  else if (a === '--ops') out.mode = 'ops'
67
76
  else if (a === '--force') out.force = true
68
77
  else if (a === '--here') out.dir = '.'
78
+ else if (a === '--workplace') out.workplace = true
79
+ else if (a === '--delivery') out.delivery = true
80
+ else if (a === '--gate') out.gate = true
69
81
  else if (a === '--branch') {
70
82
  out.branch = rest[++i]
71
83
  if (!out.branch) throw new Error('--branch requires a value')
@@ -89,7 +101,6 @@ function copyPath(from, to, { force }) {
89
101
  if (existsSync(to) && !force) {
90
102
  const st = statSync(to)
91
103
  if (st.isDirectory()) {
92
- // merge: copy children carefully
93
104
  for (const name of readdirSync(from)) {
94
105
  copyPath(join(from, name), join(to, name), { force })
95
106
  }
@@ -228,16 +239,94 @@ Next (digital employee loop):
228
239
  2. Workplace — confirm docs/baseline.md (integration branch: ${opts.branch})
229
240
  3. Assign — /duaer-specify (Brief: what / why / acceptance)
230
241
  4. Work — /duaer-plan → /duaer-tasks → /duaer-implement
231
- 5. Accept — /duaer-converge (gaps are not done; append tasks)
232
- 6. Gates AGENTS.md wins over DUADER.md when they conflict
242
+ 5. Accept — /duaer-converge (writes delivery.json; gaps stay open)
243
+ 6. Gate duaer check . --gate before merge
244
+ 7. Policy — AGENTS.md wins over DUADER.md when they conflict
233
245
 
234
- No Spec = not assigned. Failed converge = not accepted.
246
+ No Spec = not assigned. Open tasks or open delivery.json = not accepted.
235
247
  See DUADER.md and AGENTS.md in the target project.
236
248
  `)
237
249
  }
238
250
 
239
- function cmdCheck(dir) {
240
- const target = resolve(dir)
251
+ function countOpenTasks(tasksPath) {
252
+ if (!existsSync(tasksPath)) return null
253
+ const text = readFileSync(tasksPath, 'utf8')
254
+ const open = (text.match(/^\s*-\s*\[\s\]/gm) || []).length
255
+ const done = (text.match(/^\s*-\s*\[x\]/gim) || []).length
256
+ return { open, done }
257
+ }
258
+
259
+ function readDelivery(path) {
260
+ if (!existsSync(path)) return null
261
+ try {
262
+ return JSON.parse(readFileSync(path, 'utf8'))
263
+ } catch {
264
+ return { status: 'invalid', error: 'unreadable delivery.json' }
265
+ }
266
+ }
267
+
268
+ function listFeatures(target) {
269
+ const root = join(target, '.duaer', 'specs')
270
+ if (!existsSync(root)) return []
271
+ return readdirSync(root)
272
+ .filter((name) => {
273
+ try {
274
+ return statSync(join(root, name)).isDirectory()
275
+ } catch {
276
+ return false
277
+ }
278
+ })
279
+ .sort()
280
+ .map((name) => {
281
+ const dir = join(root, name)
282
+ const specPath = join(dir, 'spec.md')
283
+ const tasksPath = join(dir, 'tasks.md')
284
+ const deliveryPath = join(dir, 'delivery.json')
285
+ const tasks = countOpenTasks(tasksPath)
286
+ const delivery = readDelivery(deliveryPath)
287
+ const hasSpec = existsSync(specPath)
288
+ const stamp = delivery?.status || 'none'
289
+ let verdict = 'accepted'
290
+ const blockers = []
291
+
292
+ if (!hasSpec) {
293
+ verdict = 'blocked'
294
+ blockers.push('missing spec.md')
295
+ }
296
+ if (tasks && tasks.open > 0) {
297
+ verdict = 'blocked'
298
+ blockers.push(`${tasks.open} open task(s)`)
299
+ }
300
+ if (stamp === 'open' || stamp === 'invalid') {
301
+ verdict = 'blocked'
302
+ blockers.push(`delivery.json status=${stamp}`)
303
+ }
304
+ if (hasSpec && tasks && tasks.open === 0 && (stamp === 'none' || !delivery)) {
305
+ // Brief exists and tasks clear, but never stamped by converge
306
+ verdict = 'unaccepted'
307
+ blockers.push('no delivery.json (run /duaer-converge)')
308
+ }
309
+ if (hasSpec && !tasks && (stamp === 'none' || !delivery)) {
310
+ verdict = 'unaccepted'
311
+ blockers.push('no tasks.md / no delivery.json')
312
+ }
313
+ if (stamp === 'accepted' && tasks && tasks.open === 0 && hasSpec) {
314
+ verdict = 'accepted'
315
+ blockers.length = 0
316
+ }
317
+
318
+ return {
319
+ name,
320
+ hasSpec,
321
+ tasks,
322
+ stamp,
323
+ verdict,
324
+ blockers,
325
+ }
326
+ })
327
+ }
328
+
329
+ function checkWorkplace(target) {
241
330
  const checks = [
242
331
  ['.duaer/memory/constitution.md', 'method'],
243
332
  ['DUADER.md', 'method'],
@@ -248,17 +337,95 @@ function cmdCheck(dir) {
248
337
  ['docs/agent/workflow.md', 'ops'],
249
338
  ]
250
339
  let missing = 0
251
- console.log(`Checking ${target}\n`)
340
+ console.log('## Workplace\n')
252
341
  for (const [rel, kind] of checks) {
253
342
  const ok = existsSync(join(target, rel))
254
343
  console.log(`${ok ? 'ok ' : 'MISS'} [${kind}] ${rel}`)
255
344
  if (!ok) missing++
256
345
  }
257
- if (missing) {
258
- console.log(`\n${missing} missing — run: duaer init ${dir} --all`)
259
- process.exitCode = 1
346
+ return missing
347
+ }
348
+
349
+ function checkDelivery(target, { gate }) {
350
+ const features = listFeatures(target)
351
+ console.log('\n## Delivery\n')
352
+ if (features.length === 0) {
353
+ console.log('ok no features under .duaer/specs/ (nothing to accept)')
354
+ return { blocked: 0, unaccepted: 0, features: 0 }
355
+ }
356
+
357
+ let blocked = 0
358
+ let unaccepted = 0
359
+ for (const f of features) {
360
+ const taskInfo = f.tasks
361
+ ? `tasks open=${f.tasks.open} done=${f.tasks.done}`
362
+ : 'no tasks.md'
363
+ const line = `${f.verdict.padEnd(10)} ${f.name} spec=${f.hasSpec ? 'yes' : 'NO'} stamp=${f.stamp} ${taskInfo}`
364
+ console.log(line)
365
+ if (f.blockers.length) {
366
+ for (const b of f.blockers) console.log(` · ${b}`)
367
+ }
368
+ if (f.verdict === 'blocked') blocked++
369
+ if (f.verdict === 'unaccepted') unaccepted++
370
+ }
371
+
372
+ if (gate) {
373
+ console.log('\nGate mode: every feature must be accepted.')
374
+ }
375
+ return { blocked, unaccepted, features: features.length }
376
+ }
377
+
378
+ function cmdCheck(opts) {
379
+ const target = resolve(opts.dir)
380
+ // Default: workplace + delivery. --gate alone is delivery-only (merge gate).
381
+ const runWorkplace = opts.workplace || (!opts.delivery && !opts.gate)
382
+ const runDelivery = opts.delivery || opts.gate || !opts.workplace
383
+
384
+ console.log(`Checking ${target}${opts.gate ? ' (gate)' : ''}\n`)
385
+
386
+ let workplaceMissing = 0
387
+ if (runWorkplace) {
388
+ workplaceMissing = checkWorkplace(target)
389
+ }
390
+
391
+ let delivery = { blocked: 0, unaccepted: 0, features: 0 }
392
+ if (runDelivery) {
393
+ delivery = checkDelivery(target, { gate: opts.gate })
394
+ }
395
+
396
+ let fail = false
397
+ if (workplaceMissing) {
398
+ console.log(`\n${workplaceMissing} workplace file(s) missing — run: duaer init ${opts.dir} --all`)
399
+ fail = true
400
+ }
401
+
402
+ if (runDelivery) {
403
+ if (delivery.blocked) {
404
+ console.log(`\n${delivery.blocked} feature(s) blocked — close open tasks / fix Spec / re-run converge`)
405
+ fail = true
406
+ }
407
+ if (opts.gate && delivery.unaccepted) {
408
+ console.log(
409
+ `\n${delivery.unaccepted} feature(s) not accepted — run /duaer-converge until delivery.json status=accepted`,
410
+ )
411
+ fail = true
412
+ } else if (!opts.gate && delivery.unaccepted) {
413
+ console.log(
414
+ `\n${delivery.unaccepted} feature(s) not yet accepted (warning). Use --gate to fail the merge check.`,
415
+ )
416
+ }
417
+ }
418
+
419
+ if (!fail) {
420
+ if (opts.gate) {
421
+ console.log('\nGate passed — deliverable.')
422
+ } else if (runWorkplace && !workplaceMissing) {
423
+ console.log('\nWorkplace ready. Use --gate before merge when features exist.')
424
+ } else {
425
+ console.log('\nDelivery report done.')
426
+ }
260
427
  } else {
261
- console.log('\nWorkplace ready — assign with /duaer-specify, accept with /duaer-converge.')
428
+ process.exitCode = 1
262
429
  }
263
430
  }
264
431
 
@@ -278,7 +445,7 @@ function main() {
278
445
  cmdInit(opts)
279
446
  break
280
447
  case 'check':
281
- cmdCheck(opts.dir)
448
+ cmdCheck(opts)
282
449
  break
283
450
  case 'version':
284
451
  case '--version':
@@ -94,6 +94,7 @@ See [spec update guidance](workflow.md#3-spec-update-guidance).
94
94
 
95
95
  ## 6. Merge / PR
96
96
 
97
+ - [ ] `duaer check . --gate` passed (or no `.duaer/specs/` features yet).
97
98
  - [ ] Branch refreshed against latest `main`.
98
99
  - [ ] Merged into local `main` (and/or remote PR/MR when required).
99
100
  - [ ] Only this request's logical changes included.
@@ -121,6 +122,7 @@ See [spec update guidance](workflow.md#3-spec-update-guidance).
121
122
  | 3 | Impacted specs updated | [R1](workflow.md#r1--spec-first--spec-sync) |
122
123
  | 4 | E2E docs updated or confirmed unnecessary | [R3](workflow.md#r3--e2e-coverage-doc) |
123
124
  | 5 | Targeted validation done or waived with reason | Development loop |
125
+ | 5a | `duaer check . --gate` when features exist | Delivery OS gate |
124
126
  | 6 | Conventional commits | [R2](workflow.md#r2--commit-per-change) |
125
127
  | 7 | No secrets or local data | [§4](workflow.md#4-what-never-to-commit) |
126
128
  | 8 | Merged into `main`; worktree and branch removed | [R4](workflow.md#r4--request-branch--worktree--merge-gate) · [§6.1](#61-merge-cleanup) |
@@ -27,6 +27,9 @@ the table for their app.
27
27
  |---|---|---|
28
28
  | E2E-001 | After `duaer init`, CLI prints hire → assign → accept next steps | manual |
29
29
  | E2E-002 | README positions Duaer as delivery OS for digital employees | manual |
30
+ | E2E-003 | `duaer check --delivery` reports open tasks as blocked | manual |
31
+ | E2E-004 | `duaer check --gate` fails without `delivery.json` accepted | manual |
32
+ | E2E-005 | After converge stamps `accepted`, `--gate` passes | manual |
30
33
 
31
34
  ## Traceability
32
35
 
@@ -36,3 +39,6 @@ Keep this matrix current when scenarios or specs change.
36
39
  |---|---|---|
37
40
  | E2E-001 | `bin/duaer.mjs` init banner | Controllable-delivery onboarding |
38
41
  | E2E-002 | `README.md` / `ADOPT.md` | Product identity |
42
+ | E2E-003 | `bin/duaer.mjs` delivery check | Open `- [ ]` tasks |
43
+ | E2E-004 | `bin/duaer.mjs` `--gate` | Merge gate |
44
+ | E2E-005 | `/duaer-converge` → `delivery.json` | Accept stamp |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "duaer-spec",
3
- "version": "0.1.1",
4
- "description": "Delivery OS for AI digital employees: Spec briefs, agent ops, controllable handoffs",
3
+ "version": "0.2.0",
4
+ "description": "Delivery OS for AI digital employees: Spec briefs, agent ops, check --gate handoffs",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "duaer": "bin/duaer.mjs",