@khoavu882/doflow 2.0.0 → 2.0.2

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.
Files changed (176) hide show
  1. package/README.md +4 -5
  2. package/bin/doflow.js +1 -1
  3. package/core/.claude-plugin/plugin.json +1 -1
  4. package/core/.codex-plugin/plugin.json +1 -1
  5. package/core/.plugin/plugin.json +1 -1
  6. package/core/harnesses/claude/hooks/mcp-tool-guard.sh +4 -1
  7. package/core/harnesses/claude/hooks/pre-implement-gate.sh +4 -1
  8. package/core/harnesses/claude/hooks/session-end.sh +21 -1
  9. package/core/harnesses/claude/settings/settings.json +8 -9
  10. package/core/harnesses/codex/hooks/hooks.json +0 -10
  11. package/core/harnesses/codex/hooks/mcp-tool-guard.sh +4 -1
  12. package/core/harnesses/codex/hooks/pre-implement-gate.sh +4 -1
  13. package/core/harnesses/codex/hooks/session-end.impl.sh +21 -1
  14. package/core/harnesses/copilot/hooks/doflow-lib.sh +4 -3
  15. package/core/harnesses/copilot/hooks/doflow.json +0 -6
  16. package/core/harnesses/kiro/agents/doflow.json +0 -10
  17. package/core/harnesses/kiro/hooks/doflow.json +0 -10
  18. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +7 -3
  19. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +7 -3
  20. package/core/harnesses/pi/extensions/doflow-guard.js +3 -2
  21. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +1 -1
  22. package/core/harnesses/shared/hooks/adapters/antigravity.js +1 -1
  23. package/core/harnesses/shared/hooks/bridge/bridge.sh +35 -25
  24. package/core/harnesses/shared/hooks/bridge/envelope.jq +1 -1
  25. package/core/harnesses/shared/hooks/bridge/respond.sh +6 -0
  26. package/core/harnesses/shared/hooks/policies/capture-failure.sh +1 -1
  27. package/core/harnesses/shared/hooks/policies/compact-text.sh +8 -0
  28. package/core/harnesses/shared/hooks/policies/deny-json.sh +23 -1
  29. package/core/harnesses/shared/hooks/policies/mcp-policy.conf +1 -1
  30. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +4 -6
  31. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +20 -21
  32. package/core/harnesses/shared/hooks/policies/stop-check.sh +7 -6
  33. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +26 -2
  34. package/core/harnesses/shared/hooks/stream-hook-runner.js +11 -12
  35. package/core/registry/contracts.json +1 -1
  36. package/core/registry/contribution-points.json +5 -2
  37. package/core/registry/gates.json +5 -5
  38. package/core/registry/harnesses.json +9 -9
  39. package/core/registry/intake.json +53 -0
  40. package/core/registry/lifecycle.json +176 -503
  41. package/core/registry/package-templates/intake-category/files/category.json +8 -0
  42. package/core/registry/package-templates/intake-category/template.json +4 -0
  43. package/core/registry/packages/doflow/doflow-package.json +2 -2
  44. package/core/registry/rules.json +8 -8
  45. package/core/registry/settings.json +12 -16
  46. package/core/registry/storage.json +9 -2
  47. package/core/registry/telemetry.json +1 -1
  48. package/core/registry/verification.json +0 -5
  49. package/core/shared/guidance/references/ARTIFACT_FORMAT.md +7 -0
  50. package/core/shared/guidance/references/DOFLOW_CHAIN.md +4 -4
  51. package/core/shared/guidance/references/ORCHESTRATION.md +2 -0
  52. package/core/shared/guidance/references/WORKFLOW_HANDOFF.md +19 -1
  53. package/core/shared/guidance/rules/RULES.md +2 -2
  54. package/core/shared/scripts/doflow/bash/do-git-state.sh +12 -4
  55. package/core/shared/scripts/doflow/bash/do-parallel-check.sh +86 -33
  56. package/core/shared/scripts/doflow/bash/do-review-package.sh +36 -2
  57. package/core/shared/scripts/doflow/bash/validate-artifacts.sh +1 -1
  58. package/core/shared/scripts/doflow/bin/doflow-run +23 -2
  59. package/core/shared/skills/do/SKILL.md +3 -3
  60. package/core/shared/skills/do-brainstorm/SKILL.md +26 -24
  61. package/core/shared/skills/do-brainstorm/references/handoff_branches.md +2 -1
  62. package/core/shared/skills/do-code-review/SKILL.md +7 -4
  63. package/core/shared/skills/do-design/SKILL.md +11 -5
  64. package/core/shared/skills/do-design/references/data_model_example.md +167 -0
  65. package/core/shared/skills/do-diagnose/SKILL.md +4 -2
  66. package/core/shared/skills/do-document/SKILL.md +1 -0
  67. package/core/shared/skills/do-document/references/embedded-research.md +1 -1
  68. package/core/shared/skills/do-execute-plan/SKILL.md +4 -2
  69. package/core/shared/skills/do-execute-plan/references/run_position.md +3 -1
  70. package/core/shared/skills/do-execute-plan/references/scaffold.md +1 -1
  71. package/core/shared/skills/do-flow/SKILL.md +4 -4
  72. package/core/shared/skills/do-flow/references/task_classes.md +5 -5
  73. package/core/shared/skills/do-implement/SKILL.md +9 -7
  74. package/core/shared/skills/do-plan/SKILL.md +9 -6
  75. package/core/shared/templates/doflow/data-model-template.md +136 -102
  76. package/core/shared/workflows/do-flow-chain.md +2 -0
  77. package/package.json +1 -1
  78. package/src/adapters/antigravity/index.js +54 -12
  79. package/src/adapters/claude/index.js +23 -2
  80. package/src/adapters/codex/agents.js +15 -1
  81. package/src/adapters/codex/config.js +5 -2
  82. package/src/adapters/codex/hooks.js +39 -3
  83. package/src/adapters/codex/index.js +13 -0
  84. package/src/adapters/copilot/index.js +11 -4
  85. package/src/adapters/copy-tree.js +8 -2
  86. package/src/adapters/created-file.js +41 -0
  87. package/src/adapters/mcp-entries.js +11 -1
  88. package/src/adapters/opencode/index.js +32 -5
  89. package/src/adapters/pi/index.js +15 -4
  90. package/src/adapters/pi/mcp.js +44 -18
  91. package/src/cli/commands/remove.js +3 -1
  92. package/src/cli/commands/status.js +1 -1
  93. package/src/cli/commands/update.js +7 -3
  94. package/src/cli/index.js +43 -19
  95. package/src/cli/runtime-commands.js +13 -5
  96. package/src/cli/shared.js +5 -3
  97. package/src/helper/marker-merge.js +11 -7
  98. package/src/install/backup-set.js +7 -3
  99. package/src/install/backup.js +13 -7
  100. package/src/install/context.js +2 -1
  101. package/src/install/manifest.js +35 -11
  102. package/src/install/paths.js +38 -0
  103. package/src/install/targets.js +11 -2
  104. package/src/lifecycle/coverage.js +1 -30
  105. package/src/lifecycle/hook-tier.js +2 -2
  106. package/src/lifecycle/index.js +80 -3
  107. package/src/lifecycle/retired-harnesses.js +16 -1
  108. package/src/lifecycle/view.js +3 -2
  109. package/src/registry/index.js +21 -4
  110. package/src/release/docs-blocks.js +37 -53
  111. package/src/runtime/advice/doctor.js +2 -2
  112. package/src/runtime/brief-tools.js +2 -1
  113. package/src/runtime/capability-router.js +26 -3
  114. package/src/runtime/context/pointer.js +84 -17
  115. package/src/runtime/context-pack.js +52 -9
  116. package/src/runtime/dispatch/integrate.js +27 -8
  117. package/src/runtime/dispatch/loop.js +7 -0
  118. package/src/runtime/dispatch/verify-run.js +9 -3
  119. package/src/runtime/evidence-ledger.js +18 -0
  120. package/src/runtime/feature-pointer.js +10 -1
  121. package/src/runtime/health.js +68 -5
  122. package/src/runtime/implementation-gate.js +14 -6
  123. package/src/runtime/intake/engine.js +679 -0
  124. package/src/runtime/intake/index.js +136 -0
  125. package/src/runtime/intake/manifest.js +140 -0
  126. package/src/runtime/intake/taxonomy.js +206 -0
  127. package/src/runtime/knowledge/code-retrieval.js +11 -5
  128. package/src/runtime/knowledge/semble-provider.js +12 -2
  129. package/src/runtime/lifecycle/cli.js +76 -6
  130. package/src/runtime/lifecycle/followup.js +32 -6
  131. package/src/runtime/lifecycle/overview.js +34 -3
  132. package/src/runtime/lifecycle/release.js +16 -3
  133. package/src/runtime/lifecycle/retention.js +12 -15
  134. package/src/runtime/lifecycle/store-upkeep.js +13 -3
  135. package/src/runtime/mask.js +3 -3
  136. package/src/runtime/mcp-consultations.js +11 -5
  137. package/src/runtime/packages/points.js +10 -0
  138. package/src/runtime/research-request.js +3 -1
  139. package/src/runtime/review/command.js +3 -7
  140. package/src/runtime/review/start.js +24 -10
  141. package/src/runtime/roadmap/attendance.js +137 -0
  142. package/src/runtime/roadmap/gates.js +6 -3
  143. package/src/runtime/roadmap/loop.js +127 -33
  144. package/src/runtime/roadmap/merge.js +17 -8
  145. package/src/runtime/roadmap/rows.js +15 -5
  146. package/src/runtime/roadmap/seed.js +153 -41
  147. package/src/runtime/roadmap/worktree-cleanup.js +147 -0
  148. package/src/runtime/settings/files.js +3 -1
  149. package/src/runtime/settings/prepared-run.js +12 -2
  150. package/src/runtime/settings/registry.js +1 -1
  151. package/src/runtime/settings/resolve.js +13 -1
  152. package/src/runtime/settings/write.js +73 -2
  153. package/src/runtime/slots.js +1 -1
  154. package/src/runtime/storage/registry.js +2 -2
  155. package/src/runtime/storage/roots.js +4 -18
  156. package/src/runtime/storage/rules.js +22 -46
  157. package/src/runtime/storage/work-status.js +14 -4
  158. package/src/runtime/task-classifier.js +39 -4
  159. package/src/runtime/task-scope.js +17 -2
  160. package/src/runtime/task-state.js +13 -7
  161. package/src/runtime/trace/analyze.js +4 -2
  162. package/src/runtime/trace/indicators.js +25 -20
  163. package/src/runtime/trace/spans.js +4 -5
  164. package/src/runtime/verb-view.js +268 -0
  165. package/src/runtime/verification/engine.js +0 -45
  166. package/src/runtime/worker/command.js +4 -2
  167. package/src/runtime/worker/launch.js +5 -37
  168. package/src/runtime/worker/readiness.js +10 -6
  169. package/src/runtime/worker/supervisor.js +18 -6
  170. package/src/runtime/workflow-orchestrator.js +39 -7
  171. package/core/harnesses/claude/hooks/pre-bash-guard.sh +0 -10
  172. package/core/harnesses/codex/hooks/pre-bash-guard.sh +0 -10
  173. package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +0 -9
  174. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +0 -25
  175. package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +0 -105
  176. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +0 -579
@@ -0,0 +1,8 @@
1
+ {
2
+ "id": "{{local}}",
3
+ "names": [],
4
+ "summary": "Replace this with what files this category holds.",
5
+ "rules": [
6
+ { "extensions": ["pdf"], "namePattern": "replace-with-a-name-pattern" }
7
+ ]
8
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "template": 1,
3
+ "path": "categories/{{local}}.json"
4
+ }
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "manifest": 1,
3
3
  "id": "doflow",
4
- "version": "2.0.0",
4
+ "version": "2.0.2",
5
5
  "summary": "DoFlow first-party presets",
6
- "doflow": "=2.0.0",
6
+ "doflow": "=2.0.2",
7
7
  "contributions": [
8
8
  {
9
9
  "point": "debate-preset",
@@ -8,8 +8,8 @@
8
8
  "statement": "Conflict order: Safety > Scope > Quality > Speed. Prototype != Production requirements.",
9
9
  "sourceFile": "core/shared/guidance/rules/RULES.md",
10
10
  "category": "safety",
11
- "enforcement": "mechanical",
12
- "enforcementRef": "core/harnesses/shared/hooks/policies/pre-bash-guard.sh",
11
+ "enforcement": "advisory",
12
+ "enforcementRef": null,
13
13
  "neverAblate": true,
14
14
  "ablationCandidate": false
15
15
  },
@@ -64,7 +64,7 @@
64
64
  "sourceFile": "core/shared/guidance/PRINCIPLES.md",
65
65
  "category": "workflow",
66
66
  "enforcement": "mechanical",
67
- "enforcementRef": "core/harnesses/shared/hooks/policies/pre-implementation-gate.sh",
67
+ "enforcementRef": "src/runtime/workflow-orchestrator.js",
68
68
  "neverAblate": false,
69
69
  "ablationCandidate": true
70
70
  },
@@ -159,11 +159,11 @@
159
159
  {
160
160
  "id": "rule-workflow-doflow-chain",
161
161
  "tag": "workflow",
162
- "statement": "Feature delivery follows the doflow chain — see references/DOFLOW_CHAIN.md for the phase-gated flow, artifact contracts, and the enforced gate.",
162
+ "statement": "Feature delivery follows the doflow chain — see references/DOFLOW_CHAIN.md for the phase-gated flow, artifact contracts, and the advisory implement gate.",
163
163
  "sourceFile": "core/shared/guidance/rules/RULES.md",
164
164
  "category": "workflow",
165
165
  "enforcement": "mechanical",
166
- "enforcementRef": "core/harnesses/shared/hooks/policies/pre-implementation-gate.sh",
166
+ "enforcementRef": "src/runtime/workflow-orchestrator.js",
167
167
  "neverAblate": false,
168
168
  "ablationCandidate": true
169
169
  },
@@ -173,8 +173,8 @@
173
173
  "statement": "Start it = Finish it. No partial features, TODO stubs, mocks, or \"not implemented\" throws.",
174
174
  "sourceFile": "core/shared/guidance/rules/RULES.md",
175
175
  "category": "workflow",
176
- "enforcement": "mechanical",
177
- "enforcementRef": "core/harnesses/shared/hooks/policies/stop-check.sh",
176
+ "enforcement": "advisory",
177
+ "enforcementRef": null,
178
178
  "neverAblate": false,
179
179
  "ablationCandidate": true
180
180
  },
@@ -401,7 +401,7 @@
401
401
  {
402
402
  "id": "rule-questions-noninteractive-fallback",
403
403
  "tag": "questions",
404
- "statement": "Non-Interactive Fallback: When no native interactive question tool... is available: Do NOT generate {stage}-questions.md files... Adopt the recommended default choice, record the adopted decision and rationale in the stage notes or artifact, and proceed with execution.",
404
+ "statement": "Non-Interactive Fallback: When no native interactive question tool... is available, or attendance printed unattended for this stage: Do NOT generate {stage}-questions.md files... Adopt the recommended default choice, record the adopted decision and rationale in the stage notes or artifact, and proceed with execution.",
405
405
  "sourceFile": "core/shared/guidance/rules/RULES.md",
406
406
  "category": "questions",
407
407
  "enforcement": "advisory",
@@ -27,62 +27,62 @@
27
27
  { "layer": "project", "kind": "decision-model", "path": "decision-model.json", "field": "model" }],
28
28
  "consumers": ["src/runtime/advice/config.js"], "description": "Decision-model name; a project may choose it while advice is on." },
29
29
  "advice.kinds.tier": { "type": "enum", "values": ["off", "shadow", "act"], "default": null,
30
- "scope": "project-overridable", "combine": "override", "securityRelevant": false, "failSafe": "off",
30
+ "scope": "project-overridable", "combine": "override", "securityRelevant": true, "failSafe": "off",
31
31
  "env": null, "flag": null, "shellRead": null, "needsConfigHome": false, "legacy": [],
32
32
  "consumers": ["src/runtime/advice/shadow-config.js"],
33
33
  "description": "Advice mode for the tier kind: off, shadow (record only) or act; unset keeps the kind's own path, the highest layer that sets it decides and an invalid value is off." },
34
34
  "advice.kinds.task-class": { "type": "enum", "values": ["off", "shadow", "act"], "default": null,
35
- "scope": "project-overridable", "combine": "override", "securityRelevant": false, "failSafe": "off",
35
+ "scope": "project-overridable", "combine": "override", "securityRelevant": true, "failSafe": "off",
36
36
  "env": null, "flag": null, "shellRead": null, "needsConfigHome": false, "legacy": [],
37
37
  "consumers": ["src/runtime/advice/shadow-config.js"],
38
38
  "description": "Advice mode for the task-class kind: off, shadow (record only) or act; unset keeps the kind's own path, the highest layer that sets it decides and an invalid value is off." },
39
39
  "advice.kinds.verify-risk": { "type": "enum", "values": ["off", "shadow", "act"], "default": null,
40
- "scope": "project-overridable", "combine": "override", "securityRelevant": false, "failSafe": "off",
40
+ "scope": "project-overridable", "combine": "override", "securityRelevant": true, "failSafe": "off",
41
41
  "env": null, "flag": null, "shellRead": null, "needsConfigHome": false, "legacy": [],
42
42
  "consumers": ["src/runtime/advice/shadow-config.js"],
43
43
  "description": "Advice mode for the verify-risk kind: off, shadow (record only) or act; unset keeps the kind's own path, the highest layer that sets it decides and an invalid value is off." },
44
44
  "advice.kinds.review-depth": { "type": "enum", "values": ["off", "shadow", "act"], "default": null,
45
- "scope": "project-overridable", "combine": "override", "securityRelevant": false, "failSafe": "off",
45
+ "scope": "project-overridable", "combine": "override", "securityRelevant": true, "failSafe": "off",
46
46
  "env": null, "flag": null, "shellRead": null, "needsConfigHome": false, "legacy": [],
47
47
  "consumers": ["src/runtime/advice/shadow-config.js"],
48
48
  "description": "Advice mode for the review-depth kind: off, shadow (record only) or act; unset keeps the kind's own path, the highest layer that sets it decides and an invalid value is off." },
49
49
  "advice.kinds.mcp-need": { "type": "enum", "values": ["off", "shadow", "act"], "default": null,
50
- "scope": "project-overridable", "combine": "override", "securityRelevant": false, "failSafe": "off",
50
+ "scope": "project-overridable", "combine": "override", "securityRelevant": true, "failSafe": "off",
51
51
  "env": null, "flag": null, "shellRead": null, "needsConfigHome": false, "legacy": [],
52
52
  "consumers": ["src/runtime/advice/shadow-config.js"],
53
53
  "description": "Advice mode for the mcp-need kind: off, shadow (record only) or act; unset keeps the kind's own path, the highest layer that sets it decides and an invalid value is off." },
54
54
  "advice.kinds.followup-triage": { "type": "enum", "values": ["off", "shadow", "act"], "default": null,
55
- "scope": "project-overridable", "combine": "override", "securityRelevant": false, "failSafe": "off",
55
+ "scope": "project-overridable", "combine": "override", "securityRelevant": true, "failSafe": "off",
56
56
  "env": null, "flag": null, "shellRead": null, "needsConfigHome": false, "legacy": [],
57
57
  "consumers": ["src/runtime/advice/shadow-config.js"],
58
58
  "description": "Advice mode for the followup-triage kind: off, shadow (record only) or act; unset keeps the kind's own path, the highest layer that sets it decides and an invalid value is off." },
59
59
  "advice.models.tier": { "type": "string", "default": null,
60
- "scope": "project-overridable", "combine": "override", "securityRelevant": false,
60
+ "scope": "project-overridable", "combine": "override", "securityRelevant": true, "failSafe": { "gate": "advice.enabled" },
61
61
  "env": null, "flag": null, "shellRead": null, "needsConfigHome": false, "legacy": [],
62
62
  "consumers": ["src/runtime/advice/shadow-config.js"],
63
63
  "description": "Decision-model name for the tier kind; unset uses advice.model." },
64
64
  "advice.models.task-class": { "type": "string", "default": null,
65
- "scope": "project-overridable", "combine": "override", "securityRelevant": false,
65
+ "scope": "project-overridable", "combine": "override", "securityRelevant": true, "failSafe": { "gate": "advice.enabled" },
66
66
  "env": null, "flag": null, "shellRead": null, "needsConfigHome": false, "legacy": [],
67
67
  "consumers": ["src/runtime/advice/shadow-config.js"],
68
68
  "description": "Decision-model name for the task-class kind; unset uses advice.model." },
69
69
  "advice.models.verify-risk": { "type": "string", "default": null,
70
- "scope": "project-overridable", "combine": "override", "securityRelevant": false,
70
+ "scope": "project-overridable", "combine": "override", "securityRelevant": true, "failSafe": { "gate": "advice.enabled" },
71
71
  "env": null, "flag": null, "shellRead": null, "needsConfigHome": false, "legacy": [],
72
72
  "consumers": ["src/runtime/advice/shadow-config.js"],
73
73
  "description": "Decision-model name for the verify-risk kind; unset uses advice.model." },
74
74
  "advice.models.review-depth": { "type": "string", "default": null,
75
- "scope": "project-overridable", "combine": "override", "securityRelevant": false,
75
+ "scope": "project-overridable", "combine": "override", "securityRelevant": true, "failSafe": { "gate": "advice.enabled" },
76
76
  "env": null, "flag": null, "shellRead": null, "needsConfigHome": false, "legacy": [],
77
77
  "consumers": ["src/runtime/advice/shadow-config.js"],
78
78
  "description": "Decision-model name for the review-depth kind; unset uses advice.model." },
79
79
  "advice.models.mcp-need": { "type": "string", "default": null,
80
- "scope": "project-overridable", "combine": "override", "securityRelevant": false,
80
+ "scope": "project-overridable", "combine": "override", "securityRelevant": true, "failSafe": { "gate": "advice.enabled" },
81
81
  "env": null, "flag": null, "shellRead": null, "needsConfigHome": false, "legacy": [],
82
82
  "consumers": ["src/runtime/advice/shadow-config.js"],
83
83
  "description": "Decision-model name for the mcp-need kind; unset uses advice.model." },
84
84
  "advice.models.followup-triage": { "type": "string", "default": null,
85
- "scope": "project-overridable", "combine": "override", "securityRelevant": false,
85
+ "scope": "project-overridable", "combine": "override", "securityRelevant": true, "failSafe": { "gate": "advice.enabled" },
86
86
  "env": null, "flag": null, "shellRead": null, "needsConfigHome": false, "legacy": [],
87
87
  "consumers": ["src/runtime/advice/shadow-config.js"],
88
88
  "description": "Decision-model name for the followup-triage kind; unset uses advice.model." },
@@ -110,10 +110,6 @@
110
110
  "legacy": [{ "layer": "user", "kind": "sentinel", "path": "autopilot/off", "value": "off" }],
111
111
  "consumers": ["src/runtime/settings/autopilot.js"],
112
112
  "description": "Roadmap autopilot kill switch, off until the owner turns it on; the env and the autopilot/off sentinel can only turn it off." },
113
- "guard.workerDropDatabases": { "type": "string", "default": null, "scope": "project-only", "combine": "override",
114
- "securityRelevant": true, "failSafe": { "state": "invalid" }, "env": null, "flag": null, "shellRead": null, "needsConfigHome": false, "legacy": [],
115
- "consumers": ["src/runtime/worker/launch.js"],
116
- "description": "Space-separated exact database names a worker session may DROP DATABASE; project layer only." },
117
113
  "hooks.harnesses": { "type": "string-list", "valuesRef": "harnesses.ids", "maxItems": 16, "maxLength": 32, "default": ["claude", "codex", "antigravity", "pi"],
118
114
  "scope": "global-only", "combine": "override", "securityRelevant": false,
119
115
  "env": null, "flag": null, "shellRead": null, "needsConfigHome": false, "legacy": [],
@@ -596,8 +596,7 @@
596
596
  "match": "^[0-9a-f]{12}$",
597
597
  "rule": "report-orphan",
598
598
  "bound": {
599
- "orphanGraceHours": 24,
600
- "foreignMaxAgeDays": 180
599
+ "orphanGraceHours": 24
601
600
  },
602
601
  "protect": null,
603
602
  "moments": [
@@ -1704,6 +1703,14 @@
1704
1703
  "file": "src/runtime/history-compactor.js",
1705
1704
  "reason": "agent-docs deliverable (decisions/history archive and artifact stub)"
1706
1705
  },
1706
+ {
1707
+ "file": "src/runtime/intake/engine.js",
1708
+ "reason": "user-provided files in a feature's additional-context folder: sorted into category subfolders and filed prompt text, each move journaled in the intake manifest"
1709
+ },
1710
+ {
1711
+ "file": "src/runtime/intake/manifest.js",
1712
+ "reason": "agent-docs deliverable (additional-context/.intake/manifest.json: the journaled record of every intake move)"
1713
+ },
1707
1714
  {
1708
1715
  "file": "src/runtime/lifecycle/intent-writer.js",
1709
1716
  "reason": "agent-docs deliverable (agent-docs/intent/*.md)"
@@ -143,7 +143,7 @@
143
143
  "kind": ["hook", "verify.run", "verify.tier", "verify.check", "review.run", "package.verb"],
144
144
  "harness": ["claude", "codex", "opencode", "pi", "copilot", "kiro", "antigravity"],
145
145
  "event": ["PreToolUse", "tool_call", "UserPromptSubmit", "PostToolUse", "PostToolUseFailure"],
146
- "policy": ["pre-bash-guard", "user-prompt-submit", "post-shell-observe"],
146
+ "policy": ["user-prompt-submit", "post-shell-observe"],
147
147
  "verdict": ["allow", "deny", "advisory", "error", "unsupported", "observe"],
148
148
  "source": ["verb", "dispatch"],
149
149
  "status": ["PASS", "FAIL", "UNRESOLVED", "SUBSUMED", "NOT_APPLICABLE", "SKIPPED", "NOT_RUN"],
@@ -15,11 +15,6 @@
15
15
  "testTargeted"
16
16
  ],
17
17
  "planOverrideBlock": "doflow-verification",
18
- "completion": {
19
- "requires": "all-required-tiers-pass",
20
- "override": "explicit-user-acceptance",
21
- "overrideRequiresAccepter": true
22
- },
23
18
  "unresolvedDetectionFailureClass": "CONTEXT_MISSING",
24
19
  "notes": [
25
20
  "A tier whose command could not be detected and was not explicitly declared absent resolves to UNRESOLVED, and an UNRESOLVED required tier forces the whole report to INCONCLUSIVE. A gate never answers PASS about a check it did not run.",
@@ -308,6 +308,13 @@ reading it whole:
308
308
  attributes, keys and cardinalities of that subdomain alone. The table catalog of entities, keys,
309
309
  indexes and ORM file references is unchanged and stays below the views. Splitting one diagram into
310
310
  views must not drop an entity, a key or a constraint the single diagram carried.
311
+ - **`data-model.md` §3 to §9 — lifecycles, coupling and the rest.** §3 Lifecycles holds every
312
+ entity state, one `#### Lifecycle: <entity>` block each, and nothing else restates a state; §4 to
313
+ §9 are Cross-entity coupling, Invariants, Ownership / CRUD matrix, Event catalogue, Retention, PII
314
+ and migration, and History. Each `#### Dictionary: <entity>` row and each row of the §3 to §8
315
+ tables ends in a `Source` cell holding one of `FR-###`, `NFR-###`, `IC-###`, `code:<path>:<line>`,
316
+ `proposed`, or `UNKNOWN -> Open Point`. A `proposed` or `UNKNOWN -> Open Point` row is copied to
317
+ `design.md` Open Points for Review. A conditional section takes `N/A: [why]`.
311
318
 
312
319
  ### The `N/A: [why]` escape
313
320
 
@@ -4,8 +4,8 @@ A spec-kit-style, phase-gated delivery loop (`do-brainstorm → do-design → do
4
4
  do-execute-plan → do-test → do-code-review`) on top of the rest of the `/do-*` skills and the seven
5
5
  specialist agents (`core-implementer`, `delivery-lead`, `quality-guardian`,
6
6
  `research-writer`, `reviewer`, `spec-analyst`, `system-architect`). Discipline borrowed from
7
- spec-kit; enforcement done with the harness's real hooks rather than a prompt-read registry.
8
- The implement-gate hook is registered in
7
+ spec-kit; hooks advise, and the runtime enforces order and readiness at handoff rather than a
8
+ prompt-read registry. The implement-gate hook is registered in
9
9
  `settings.json`. `do-constitution` sits outside the numbered chain — a standalone,
10
10
  still-invocable skill that maintains the persistent rules every phase inherits, not a phase
11
11
  itself. `do-implement` sits outside the numbered chain too, for the opposite reason: it is the
@@ -19,7 +19,7 @@ remains the chain's own implementation phase.
19
19
  |------|-------|
20
20
  | `skills/` | Chain skills: `do-brainstorm` (also creates the feature branch/dir, writes `requirement.md`, optionally captures a PBI/ticket ID), `do-design` (writes `design.md`), `do-plan` (also writes the dependency-ordered task checklist inside `plan.md`, plus a Repo Branch Plan for multi-repo features), `do-execute-plan` (also supports `--scaffold` to emit a reviewable code scaffold under the feature dir — the source layout, signatures and test stubs the three artifacts imply, plus a per-dependency-service contract frame — and lazily creates/tracks each repo's branch as it's first touched; a non-local dependency with a `external-contract:` field gets a frame generated from that doc instead of the default silent skip), `do-test`, `do-code-review`. `do-constitution` is standalone, not part of the numbered chain. |
21
21
  | `scripts/doflow/` | The runtime seam. Skills call `bin/doflow-run <verb>` and never name a helper: the dispatcher decides which `bash/*.sh` helper or Node command serves each verb. The dispatcher owns the verb namespace and is the only place it is written down — an inventory here is the drift this row used to produce. |
22
- | `shared/hooks/policies/` | `mcp-tool-guard.sh`: MCP policy; `pre-bash-guard.sh`: command denials; `pre-implementation-gate.sh`: artifact/readiness edit gate; `stop-check.sh`: blocks stopping on unfinished-work markers. Harnesses with hook support call these; support differs per harness. |
22
+ | `shared/hooks/policies/` | `mcp-tool-guard.sh`: MCP policy; `emission.sh`: nudges; `pre-implementation-gate.sh`: artifact/readiness edit advice; `stop-check.sh`: warns on unfinished-work markers. Harnesses with hook support call these; support differs per harness. |
23
23
  | `templates/doflow/` | `constitution-template.md` (tier-2); `data-model-template.md`; `design-template.md`; `external-contract-template.md`; `intent-template.md`; `plan-template.md`; `question-log-template.md`; `requirement-template.md`; `specs-template.md`; `state-template.md`. Chain skills seed feature dirs from a shared pool, not per-skill `assets/`. |
24
24
  | `guidance/references/` | `ARTIFACT_FORMAT.md`, `ARTIFACT_VOICE.md`, `CONSTITUTION_BASE.md` (global tier-1), `DIAGRAM_GUIDE.md`, `DOFLOW_CHAIN.md`, `EVIDENCE_LEDGER.md`, `MODEL_SELECTION.md`, `ORCHESTRATION.md`, `RESEARCH_CONFIG.md`, `WORKFLOW_HANDOFF.md`. |
25
25
 
@@ -52,7 +52,7 @@ remains the chain's own implementation phase.
52
52
  `requirement.md`'s `**Intent:**` field — the argument that does so is declared in `/do-brainstorm`'s
53
53
  own `argument-hint`, which is where a single-skill argument is owned. Discovery without an intent is
54
54
  unchanged and is the ordinary case.
55
- - **Implement gate (hard):** Denies source edits for started features missing `requirement.md`/`design.md`/`plan.md` (`missing-artifacts`), or held tasks without READY at their workflow's first source-mutating stage (`readiness-missing`, `readiness-not-ready`, `readiness-wrong-template`, or `readiness-store-conflict` when a linked worktree's copy differs from the main checkout's). Other blockers: `shared/hooks/policies/`, and the planning stage's exit, which refuses a constitution MUST conflict ("Two-tier constitution" below); all other chain gates are advisory/skippable.
55
+ - **Implement gate (advisory):** Advises on source edits for started features missing `requirement.md`/`design.md`/`plan.md` (`missing-artifacts`), or held tasks without READY at their workflow's first source-mutating stage (`readiness-missing`, `readiness-not-ready`, `readiness-wrong-template`, or `readiness-store-conflict` when a linked worktree's copy differs from the main checkout's). Only these block: an MCP tool call matching a blocked pattern, faults under the `strict-1` profile, and the planning stage's exit, which refuses a constitution MUST conflict ("Two-tier constitution" below); all other chain gates are advisory/skippable.
56
56
  - **Two-tier constitution — what is computed vs. what is convention.**
57
57
  `references/CONSTITUTION_BASE.md` (tier-1, global) is overlaid by
58
58
  `<repo>/agent-docs/constitution.md` (tier-2, per-repo), and tier-2 takes precedence. **That
@@ -33,3 +33,5 @@ items with the required author. After a failed/stopped agent, check status, log,
33
33
 
34
34
  Use sensible defaults. Ask one structured question only when a contract or behavior changes beyond
35
35
  the request; resolve agent questions rather than forwarding a list.
36
+ Unattended, that same case halts per RULE_04 (WORKFLOW_HANDOFF.md § Unattended stage): the
37
+ recommended default is not adopted.
@@ -40,6 +40,23 @@ With or without a register, record in this step each finding left unfixed, item
40
40
  and gate answer that defers work:
41
41
  `"$DOFLOW" followup --action add --stage <stage> --statement "<one line>"`.
42
42
 
43
+ ## Unattended stage
44
+
45
+ Per stage run `"$DOFLOW" attendance --task-id "<feature_slug>"`. Unless it printed a line starting
46
+ `unattended`, ask as the skill says. Otherwise RULE_04 applies and:
47
+
48
+ 1. **Ask nothing, decide no gate.** Gates are the loop's: no `decide-gate` (not even for zero
49
+ markers); hand off and stop. A gate is never a default, nor is closing a goal.
50
+ 2. **The run supplies** the class (`taskClass` in `orchestrate --action status --task-id
51
+ "<feature_slug>" --json`), the slug (the `feat/<slug>` branch) and `commit.branch`. A rejected
52
+ class, several slugs, a `--from` clash or readiness `ASK_USER` halts.
53
+ 3. **Record an adopted default** in the decision batch: `channel` `default`, `rationale`
54
+ `unattended:<source>`, `stage` the nearest accepted ownership-table name.
55
+ 4. **Halt, never hand off,** when no sensible default exists, a contract would change or a
56
+ permission is denied: `"$DOFLOW" followup --action add --stage <stage as in 3> --statement
57
+ "<why it blocks>"`, report and stop; the row is held.
58
+ 5. **Never apply `--fix` or send private context** (leave the request `unresolved`).
59
+
43
60
  ## The handoff call
44
61
 
45
62
  After a skill finishes its work, call the resolved runtime seam:
@@ -71,7 +88,8 @@ Use `--result passed|failed|unverified` to record the stage's outcome. Omission
71
88
  For bug reproduction, observing the expected failure can satisfy the stage contract; the command's
72
89
  nonzero exit alone is not the stage verdict. Reruns append a note and preserve the original outcome.
73
90
 
74
- Only handle an `awaitingGate` if the skill owns that gate and the handoff just completed its anchor.
91
+ Only handle an `awaitingGate` if the skill owns that gate and the handoff just completed its anchor;
92
+ unattended, never handle one (§ Unattended stage).
75
93
  Follow the owning skill's decision rules; this API supplies no authorization. A deferral is not a
76
94
  rejection. The lower-level `decide-gate` action remains explicit.
77
95
 
@@ -8,7 +8,7 @@ Conflict order: Safety > Scope > Quality > Speed. Prototype != Production requir
8
8
  - **Parallel when write sets are disjoint; otherwise sequential.** Use task lists past 3 tasks when available.
9
9
  - Run lint/typecheck before marking complete.
10
10
  - Check deps (package.json) and existing patterns before any changes.
11
- - Feature delivery follows the doflow chain — see `references/DOFLOW_CHAIN.md` for the phase-gated flow, artifact contracts, and the enforced gate.
11
+ - Feature delivery follows the doflow chain — see `references/DOFLOW_CHAIN.md` for the phase-gated flow, artifact contracts, and the advisory implement gate.
12
12
  - Start it = Finish it. No partial features, TODO stubs, mocks, or "not implemented" throws. <!-- must-carry -->
13
13
  - All generated code must work as specified; no scaffolding placeholders.
14
14
 
@@ -35,7 +35,7 @@ Conflict order: Safety > Scope > Quality > Speed. Prototype != Production requir
35
35
  - One question that unblocks beats three that re-litigate settled choices.
36
36
 
37
37
  **Non-Interactive Fallback**
38
- When no native interactive question tool (one that presents options and returns the user's choice) is available in the current session:
38
+ When no native interactive question tool (one that presents options and returns the user's choice) is available in the current session, or `"$DOFLOW" attendance` printed `unattended` for this stage (the rules in WORKFLOW_HANDOFF.md § Unattended stage apply; a gate is never a default):
39
39
  - Do NOT generate `{stage}-questions.md` files or halt execution waiting for manual file edits.
40
40
  - Adopt the recommended default choice, record the adopted decision and rationale in the stage notes or artifact, and proceed with execution.
41
41
  - If no sensible default exists and execution cannot safely proceed without user input, report the specific ambiguity and halt.
@@ -111,10 +111,14 @@ is_dirty() {
111
111
  # means anything. The local branch was used unconditionally, so a develop that had not been pulled
112
112
  # made every distance wrong by however stale it was — observed live at 22 reported against 32.
113
113
  #
114
- # The remote-tracking ref is preferred where it exists; the local branch is the fallback for a
115
- # repository with no remote or one never fetched. Preferring it does not make the answer *fresh* —
116
- # origin/develop is only as current as the last fetch — so the third field reports how far the
117
- # local branch trails, which is non-zero exactly when a `git fetch` would change the answer.
114
+ # The remote-tracking ref is preferred where it exists, unless the local branch is strictly ahead
115
+ # of it (origin/<branch> is an ancestor of the branch and they differ): then the local branch holds
116
+ # integrated work the remote has not seen yet, and measuring from origin would count that work as
117
+ # the caller's own. An equal, behind or diverged local branch keeps the remote preference; the local
118
+ # branch is the fallback for a repository with no remote or one never fetched. Preferring the remote
119
+ # does not make the answer *fresh* — origin/develop is only as current as the last fetch — so the
120
+ # third field reports how far the local branch trails, which is non-zero exactly when a `git fetch`
121
+ # would change the answer.
118
122
  #
119
123
  # Echoes: "<ref> <kind> <local-behind-remote>"
120
124
  resolve_integration_ref() {
@@ -125,6 +129,10 @@ resolve_integration_ref() {
125
129
  kind="remote-tracking"
126
130
  if git rev-parse --verify --quiet "refs/heads/${branch}" >/dev/null 2>&1; then
127
131
  behind="$(git rev-list --count "${branch}..origin/${branch}" 2>/dev/null || echo 0)"
132
+ if [ "$behind" = "0" ] && [ "$(git rev-list --count "origin/${branch}..${branch}" 2>/dev/null || echo 0)" -gt 0 ]; then
133
+ ref="$branch"
134
+ kind="local"
135
+ fi
128
136
  fi
129
137
  fi
130
138
  printf '%s %s %s\n' "$ref" "$kind" "$behind"
@@ -12,6 +12,10 @@ case "${OSTYPE:-}" in msys*|cygwin*|win32*) if command -v jq >/dev/null 2>&1; th
12
12
  # reducing dispatch overhead. The script forms groups from `owner:` fields, computes union
13
13
  # file sets per group, and reports overlaps across groups (`group_overlaps[]`).
14
14
  #
15
+ # Owner and files are read from the whole task block: the task line and a `Verify:` line under it. A task
16
+ # that declares no `files:` (`files: none` is a declaration), or declares different sets, is named in
17
+ # `files_missing[]` / `files_conflict[]`, and while one exists `parallel_safe` is `null`, never `true`.
18
+ #
15
19
  # Advisory by design: it reports overlaps and grouping decisions; the calling skill decides
16
20
  # to serialize or group dispatch. It never edits the plan and never dispatches anything.
17
21
  #
@@ -67,7 +71,70 @@ plan_abs="$repo_root/$plan_rel"
67
71
 
68
72
  # Pipeline: awk outputs tab-separated values (one per line), jq reads raw lines as strings,
69
73
  # aggregates them into an array, then processes each row.
74
+ #
75
+ # A task is its whole block: the `- [ ] <id>` line and the indented lines under it, up to the next task or
76
+ # heading. `owner:` and `files:` are read from the task line and from a `Verify:` line (the layout plans
77
+ # written with a verification line use); each is the text after the line's first em dash. A task that
78
+ # declares files twice must declare the same set twice: a different set is `conflict`, and a task that
79
+ # declares none is `missing`. `files: none` is a declaration of an empty set, not a missing one.
70
80
  awk -v want="$phase" '
81
+ # Sorted, de-duplicated, comma-joined form of a comma list, so two spellings of one set compare equal.
82
+ function normalize(list, n, parts, i, j, t, out, last) {
83
+ n = split(list, parts, ",")
84
+ for (i = 1; i <= n; i++) { gsub(/^[ \t]+|[ \t]+$/, "", parts[i]) }
85
+ for (i = 2; i <= n; i++) {
86
+ t = parts[i]
87
+ for (j = i - 1; j >= 1 && parts[j] > t; j--) parts[j + 1] = parts[j]
88
+ parts[j + 1] = t
89
+ }
90
+ out = ""; last = "\001"
91
+ for (i = 1; i <= n; i++) {
92
+ if (parts[i] == "" || parts[i] ~ /^none/ || parts[i] == last) continue
93
+ out = out (out == "" ? "" : ",") parts[i]
94
+ last = parts[i]
95
+ }
96
+ return out
97
+ }
98
+ # Reads one line'"'"'s owner:/files: fields into the current task.
99
+ function declare(line, dash_pos, section, o, f) {
100
+ dash_pos = index(line, "—")
101
+ if (dash_pos == 0) return
102
+ section = substr(line, dash_pos + length("—"))
103
+ if (match(section, /owner:[^;]*/)) {
104
+ o = substr(section, RSTART + 7, RLENGTH - 7)
105
+ gsub(/^[ \t]+|[ \t]+$/, "", o)
106
+ if (o != "") {
107
+ if (owner == "") owner = o
108
+ else if (owner != o) conflict = 1
109
+ }
110
+ }
111
+ if (match(section, /files:[^;]*/)) {
112
+ f = substr(section, RSTART + 6, RLENGTH - 6)
113
+ gsub(/^[ \t]+|[ \t]+$/, "", f)
114
+ if (f == "") return
115
+ declared++
116
+ set = normalize(f)
117
+ if (declared == 1) first = set
118
+ else if (set != first) conflict = 1
119
+ m = split(set, items, ",")
120
+ for (k = 1; k <= m; k++) if (items[k] != "" && !(items[k] in seen)) { seen[items[k]] = 1; order[++norder] = items[k] }
121
+ }
122
+ }
123
+ function flush( status, k) {
124
+ if (id == "") return
125
+ status = conflict ? "conflict" : (declared == 0 ? "missing" : "ok")
126
+ if (norder == 0) print id "\t" par "\t" owner "\t\t" status
127
+ for (k = 1; k <= norder; k++) print id "\t" par "\t" owner "\t" order[k] "\t" status
128
+ id = ""
129
+ }
130
+ function start(line) {
131
+ id = substr(line, RSTART + 6, RLENGTH - 6)
132
+ par = (line ~ /^- \[[ x]\] [^ ]+ \[P\]/) ? "P" : "S"
133
+ owner = ""; declared = 0; conflict = 0; first = ""; norder = 0
134
+ delete seen; delete order
135
+ declare(line)
136
+ }
137
+ /^#+ / { flush() }
71
138
  /^### Phase / {
72
139
  inphase = ($3 == want)
73
140
  next
@@ -75,43 +142,22 @@ awk -v want="$phase" '
75
142
  /^### / { inphase = 0; next }
76
143
  !inphase { next }
77
144
  /^- \[[ x]\] / {
78
- line = $0
79
- if (match(line, /^- \[[ x]\] [^ ]+/) == 0) next
80
- id = substr(line, RSTART + 6, RLENGTH - 6)
81
- par = (line ~ /^- \[[ x]\] [^ ]+ \[P\]/) ? "P" : "S"
82
- # Extract only the portion after em-dash for owner:/files: parsing
83
- dash_pos = index(line, "—")
84
- if (dash_pos == 0) {
85
- print id "\t" par "\t\t"
86
- next
87
- }
88
- field_section = substr(line, dash_pos + length("—"))
89
- owner = ""
90
- if (match(field_section, /owner:[^;]*/)) {
91
- owner = substr(field_section, RSTART + 7, RLENGTH - 7)
92
- gsub(/^[ \t]+|[ \t]+$/, "", owner)
93
- }
94
- if (match(field_section, /files:[^;]*/)) {
95
- files = substr(field_section, RSTART + 6, RLENGTH - 6)
96
- n = split(files, parts, ",")
97
- emitted = 0
98
- for (i = 1; i <= n; i++) {
99
- f = parts[i]
100
- gsub(/^[ \t]+|[ \t]+$/, "", f)
101
- if (f == "" || f ~ /^none/) continue
102
- print id "\t" par "\t" owner "\t" f
103
- emitted = 1
104
- }
105
- if (!emitted) print id "\t" par "\t" owner "\t"
106
- } else {
107
- print id "\t" par "\t" owner "\t"
108
- }
145
+ flush()
146
+ if (match($0, /^- \[[ x]\] [^ ]+/) == 0) next
147
+ start($0)
148
+ next
109
149
  }
150
+ id != "" && /^[ \t]*[-*]?[ \t]*\**Verify\**:/ { declare($0) }
151
+ END { flush() }
110
152
  ' "$plan_abs" | jq -R . | jq -s --arg phase "$phase" '
111
153
  # Convert array of tab-separated strings to array of objects
112
154
  map(split("\t")) |
113
- map({id: .[0], par: .[1], owner: (.[2] // ""), file: (.[3] // "")}) |
155
+ map({id: .[0], par: .[1], owner: (.[2] // ""), file: (.[3] // ""), meta: (.[4] // "missing")}) |
114
156
  . as $rows |
157
+
158
+ # Tasks whose file metadata cannot establish anything: none declared, or declarations that disagree
159
+ ([.[] | select(.meta == "missing") | .id] | unique) as $files_missing |
160
+ ([.[] | select(.meta == "conflict") | .id] | unique) as $files_conflict |
115
161
 
116
162
  # Separate by parallel/sequential
117
163
  ([.[] | select(.par == "P") | .id] | unique) as $par |
@@ -200,8 +246,15 @@ awk -v want="$phase" '
200
246
  # a non-empty `group_overlaps` naming the very file two groups both write. `do-execute-plan`
201
247
  # tells the reader to branch on this field and never on the exit code, so either shape
202
248
  # authorises concurrent writes to one file.
249
+ #
250
+ # A task with no `files:` (or two that disagree) has an unknown write set, so a phase containing one
251
+ # is never reported safe: `null` (unknown, run serially) unless a real overlap already makes it `false`.
203
252
  parallel_safe: (if (($par | length) == 0 and ($seq | length) == 0) then null
204
- else (($overlaps | length) == 0 and ($final_group_overlaps | length) == 0) end),
253
+ elif (($overlaps | length) > 0 or ($final_group_overlaps | length) > 0) then false
254
+ elif (($files_missing | length) > 0 or ($files_conflict | length) > 0) then null
255
+ else true end),
256
+ files_missing: $files_missing,
257
+ files_conflict: $files_conflict,
205
258
  serialize: ([$overlaps[] | .tasks[]] | unique),
206
259
  groups: $final_groups,
207
260
  group_overlaps: $final_group_overlaps,
@@ -21,7 +21,10 @@ case "${OSTYPE:-}" in msys*|cygwin*|win32*) if command -v jq >/dev/null 2>&1; th
21
21
  # Usage:
22
22
  # do-review-package.sh --task=<id> --base=<sha> --head=<sha> [--slug=<slug>] [--json]
23
23
  #
24
- # Emits: {path, commits, bytes, base, head}
24
+ # Emits: {path, commits, bytes, base, head}, plus missing[] when the rules block could not be built.
25
+ #
26
+ # Between the title and the commit list the package carries the "Rules that apply to you" block: the
27
+ # mustCarry lines of the rule ledger (core/registry/rules.json), as the task and group briefs print them.
25
28
 
26
29
  set -uo pipefail
27
30
 
@@ -96,10 +99,40 @@ if [ "${commits:-0}" -eq 0 ]; then
96
99
  exit 2
97
100
  fi
98
101
 
102
+ # The registry file `core/registry/<name>` of the runtime this script belongs to: next to the checkout or
103
+ # projected runtime found above this script, else the global projected runtime (as do-task-brief.sh).
104
+ registry_file() { # registry_file <name>
105
+ local d="$script_dir" c
106
+ while [ -n "$d" ] && [ "$d" != "/" ]; do
107
+ for c in "$d/core/registry/$1" "$d/runtime/core/registry/$1"; do
108
+ if [ -f "$c" ]; then printf '%s' "$c"; return 0; fi
109
+ done
110
+ d="$(dirname "$d")"
111
+ done
112
+ c="${HOME:-}/.doflow/runtime/core/registry/$1"
113
+ if [ -f "$c" ]; then printf '%s' "$c"; fi
114
+ return 0
115
+ }
116
+
117
+ missing='[]'
118
+ rules_block=""
119
+ rules_file="$(registry_file rules.json)"
120
+ if [ -z "$rules_file" ]; then
121
+ missing='["brief-rules: core/registry/rules.json is missing"]'
122
+ else
123
+ rule_lines="$(jq -r 'if (.mustCarry | type) == "array" and (.mustCarry | length) > 0 and (.mustCarry | all(type == "string")) then .mustCarry[] | "- " + . else empty end' "$rules_file" 2>/dev/null)"
124
+ if [ -z "$rule_lines" ]; then
125
+ missing='["brief-rules: core/registry/rules.json has no mustCarry array"]'
126
+ else
127
+ rules_block="$(printf '## Rules that apply to you\n\n%s\n' "$rule_lines")"
128
+ fi
129
+ fi
130
+
99
131
  write_ok=1
100
132
  {
101
133
  echo "# Review package: $base7..$head7"
102
134
  echo
135
+ if [ -n "$rules_block" ]; then printf '%s\n\n' "$rules_block"; fi
103
136
  echo "## Commits"
104
137
  git log --oneline "$base..$head_ref"
105
138
  echo
@@ -127,5 +160,6 @@ jq -n \
127
160
  --argjson commits "${commits:-0}" \
128
161
  --argjson bytes "${bytes:-0}" \
129
162
  --arg base "$base7" --arg head "$head7" \
130
- '{path:$path, commits:$commits, bytes:$bytes, base:$base, head:$head}'
163
+ --argjson missing "$missing" \
164
+ '{path:$path, commits:$commits, bytes:$bytes, base:$base, head:$head} + (if ($missing | length) > 0 then {missing:$missing} else {} end)'
131
165
  exit 0
@@ -216,7 +216,7 @@ for f in "${targets[@]}"; do
216
216
  esac
217
217
  fi
218
218
  # Paths reach awk through the environment: -v would process backslash escapes inside them.
219
- out=$(DF_ART_DIR="$(dirname "$f")" DF_HIST_ROOT="$hist_root" awk -v is_plan="$([ "$(basename "$f")" = "plan.md" ] && echo 1 || echo 0)" \
219
+ out=$(LC_ALL=C DF_ART_DIR="$(dirname "$f")" DF_HIST_ROOT="$hist_root" awk -v is_plan="$([ "$(basename "$f")" = "plan.md" ] && echo 1 || echo 0)" \
220
220
  -v stale_map="$file_stale_map" -v reg_ok="$file_reg_ok" -v known_ids="$file_known_ids" -v sq="'" '
221
221
  # Inline markup is presentation, not value: "**Superseded → X**" and "`Live`" mean the same as
222
222
  # their bare forms, so emphasis is stripped before any comparison.