@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.
- package/README.md +4 -5
- package/bin/doflow.js +1 -1
- package/core/.claude-plugin/plugin.json +1 -1
- package/core/.codex-plugin/plugin.json +1 -1
- package/core/.plugin/plugin.json +1 -1
- package/core/harnesses/claude/hooks/mcp-tool-guard.sh +4 -1
- package/core/harnesses/claude/hooks/pre-implement-gate.sh +4 -1
- package/core/harnesses/claude/hooks/session-end.sh +21 -1
- package/core/harnesses/claude/settings/settings.json +8 -9
- package/core/harnesses/codex/hooks/hooks.json +0 -10
- package/core/harnesses/codex/hooks/mcp-tool-guard.sh +4 -1
- package/core/harnesses/codex/hooks/pre-implement-gate.sh +4 -1
- package/core/harnesses/codex/hooks/session-end.impl.sh +21 -1
- package/core/harnesses/copilot/hooks/doflow-lib.sh +4 -3
- package/core/harnesses/copilot/hooks/doflow.json +0 -6
- package/core/harnesses/kiro/agents/doflow.json +0 -10
- package/core/harnesses/kiro/hooks/doflow.json +0 -10
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +7 -3
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +7 -3
- package/core/harnesses/pi/extensions/doflow-guard.js +3 -2
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +1 -1
- package/core/harnesses/shared/hooks/adapters/antigravity.js +1 -1
- package/core/harnesses/shared/hooks/bridge/bridge.sh +35 -25
- package/core/harnesses/shared/hooks/bridge/envelope.jq +1 -1
- package/core/harnesses/shared/hooks/bridge/respond.sh +6 -0
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +1 -1
- package/core/harnesses/shared/hooks/policies/compact-text.sh +8 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +23 -1
- package/core/harnesses/shared/hooks/policies/mcp-policy.conf +1 -1
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +4 -6
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +20 -21
- package/core/harnesses/shared/hooks/policies/stop-check.sh +7 -6
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +26 -2
- package/core/harnesses/shared/hooks/stream-hook-runner.js +11 -12
- package/core/registry/contracts.json +1 -1
- package/core/registry/contribution-points.json +5 -2
- package/core/registry/gates.json +5 -5
- package/core/registry/harnesses.json +9 -9
- package/core/registry/intake.json +53 -0
- package/core/registry/lifecycle.json +176 -503
- package/core/registry/package-templates/intake-category/files/category.json +8 -0
- package/core/registry/package-templates/intake-category/template.json +4 -0
- package/core/registry/packages/doflow/doflow-package.json +2 -2
- package/core/registry/rules.json +8 -8
- package/core/registry/settings.json +12 -16
- package/core/registry/storage.json +9 -2
- package/core/registry/telemetry.json +1 -1
- package/core/registry/verification.json +0 -5
- package/core/shared/guidance/references/ARTIFACT_FORMAT.md +7 -0
- package/core/shared/guidance/references/DOFLOW_CHAIN.md +4 -4
- package/core/shared/guidance/references/ORCHESTRATION.md +2 -0
- package/core/shared/guidance/references/WORKFLOW_HANDOFF.md +19 -1
- package/core/shared/guidance/rules/RULES.md +2 -2
- package/core/shared/scripts/doflow/bash/do-git-state.sh +12 -4
- package/core/shared/scripts/doflow/bash/do-parallel-check.sh +86 -33
- package/core/shared/scripts/doflow/bash/do-review-package.sh +36 -2
- package/core/shared/scripts/doflow/bash/validate-artifacts.sh +1 -1
- package/core/shared/scripts/doflow/bin/doflow-run +23 -2
- package/core/shared/skills/do/SKILL.md +3 -3
- package/core/shared/skills/do-brainstorm/SKILL.md +26 -24
- package/core/shared/skills/do-brainstorm/references/handoff_branches.md +2 -1
- package/core/shared/skills/do-code-review/SKILL.md +7 -4
- package/core/shared/skills/do-design/SKILL.md +11 -5
- package/core/shared/skills/do-design/references/data_model_example.md +167 -0
- package/core/shared/skills/do-diagnose/SKILL.md +4 -2
- package/core/shared/skills/do-document/SKILL.md +1 -0
- package/core/shared/skills/do-document/references/embedded-research.md +1 -1
- package/core/shared/skills/do-execute-plan/SKILL.md +4 -2
- package/core/shared/skills/do-execute-plan/references/run_position.md +3 -1
- package/core/shared/skills/do-execute-plan/references/scaffold.md +1 -1
- package/core/shared/skills/do-flow/SKILL.md +4 -4
- package/core/shared/skills/do-flow/references/task_classes.md +5 -5
- package/core/shared/skills/do-implement/SKILL.md +9 -7
- package/core/shared/skills/do-plan/SKILL.md +9 -6
- package/core/shared/templates/doflow/data-model-template.md +136 -102
- package/core/shared/workflows/do-flow-chain.md +2 -0
- package/package.json +1 -1
- package/src/adapters/antigravity/index.js +54 -12
- package/src/adapters/claude/index.js +23 -2
- package/src/adapters/codex/agents.js +15 -1
- package/src/adapters/codex/config.js +5 -2
- package/src/adapters/codex/hooks.js +39 -3
- package/src/adapters/codex/index.js +13 -0
- package/src/adapters/copilot/index.js +11 -4
- package/src/adapters/copy-tree.js +8 -2
- package/src/adapters/created-file.js +41 -0
- package/src/adapters/mcp-entries.js +11 -1
- package/src/adapters/opencode/index.js +32 -5
- package/src/adapters/pi/index.js +15 -4
- package/src/adapters/pi/mcp.js +44 -18
- package/src/cli/commands/remove.js +3 -1
- package/src/cli/commands/status.js +1 -1
- package/src/cli/commands/update.js +7 -3
- package/src/cli/index.js +43 -19
- package/src/cli/runtime-commands.js +13 -5
- package/src/cli/shared.js +5 -3
- package/src/helper/marker-merge.js +11 -7
- package/src/install/backup-set.js +7 -3
- package/src/install/backup.js +13 -7
- package/src/install/context.js +2 -1
- package/src/install/manifest.js +35 -11
- package/src/install/paths.js +38 -0
- package/src/install/targets.js +11 -2
- package/src/lifecycle/coverage.js +1 -30
- package/src/lifecycle/hook-tier.js +2 -2
- package/src/lifecycle/index.js +80 -3
- package/src/lifecycle/retired-harnesses.js +16 -1
- package/src/lifecycle/view.js +3 -2
- package/src/registry/index.js +21 -4
- package/src/release/docs-blocks.js +37 -53
- package/src/runtime/advice/doctor.js +2 -2
- package/src/runtime/brief-tools.js +2 -1
- package/src/runtime/capability-router.js +26 -3
- package/src/runtime/context/pointer.js +84 -17
- package/src/runtime/context-pack.js +52 -9
- package/src/runtime/dispatch/integrate.js +27 -8
- package/src/runtime/dispatch/loop.js +7 -0
- package/src/runtime/dispatch/verify-run.js +9 -3
- package/src/runtime/evidence-ledger.js +18 -0
- package/src/runtime/feature-pointer.js +10 -1
- package/src/runtime/health.js +68 -5
- package/src/runtime/implementation-gate.js +14 -6
- package/src/runtime/intake/engine.js +679 -0
- package/src/runtime/intake/index.js +136 -0
- package/src/runtime/intake/manifest.js +140 -0
- package/src/runtime/intake/taxonomy.js +206 -0
- package/src/runtime/knowledge/code-retrieval.js +11 -5
- package/src/runtime/knowledge/semble-provider.js +12 -2
- package/src/runtime/lifecycle/cli.js +76 -6
- package/src/runtime/lifecycle/followup.js +32 -6
- package/src/runtime/lifecycle/overview.js +34 -3
- package/src/runtime/lifecycle/release.js +16 -3
- package/src/runtime/lifecycle/retention.js +12 -15
- package/src/runtime/lifecycle/store-upkeep.js +13 -3
- package/src/runtime/mask.js +3 -3
- package/src/runtime/mcp-consultations.js +11 -5
- package/src/runtime/packages/points.js +10 -0
- package/src/runtime/research-request.js +3 -1
- package/src/runtime/review/command.js +3 -7
- package/src/runtime/review/start.js +24 -10
- package/src/runtime/roadmap/attendance.js +137 -0
- package/src/runtime/roadmap/gates.js +6 -3
- package/src/runtime/roadmap/loop.js +127 -33
- package/src/runtime/roadmap/merge.js +17 -8
- package/src/runtime/roadmap/rows.js +15 -5
- package/src/runtime/roadmap/seed.js +153 -41
- package/src/runtime/roadmap/worktree-cleanup.js +147 -0
- package/src/runtime/settings/files.js +3 -1
- package/src/runtime/settings/prepared-run.js +12 -2
- package/src/runtime/settings/registry.js +1 -1
- package/src/runtime/settings/resolve.js +13 -1
- package/src/runtime/settings/write.js +73 -2
- package/src/runtime/slots.js +1 -1
- package/src/runtime/storage/registry.js +2 -2
- package/src/runtime/storage/roots.js +4 -18
- package/src/runtime/storage/rules.js +22 -46
- package/src/runtime/storage/work-status.js +14 -4
- package/src/runtime/task-classifier.js +39 -4
- package/src/runtime/task-scope.js +17 -2
- package/src/runtime/task-state.js +13 -7
- package/src/runtime/trace/analyze.js +4 -2
- package/src/runtime/trace/indicators.js +25 -20
- package/src/runtime/trace/spans.js +4 -5
- package/src/runtime/verb-view.js +268 -0
- package/src/runtime/verification/engine.js +0 -45
- package/src/runtime/worker/command.js +4 -2
- package/src/runtime/worker/launch.js +5 -37
- package/src/runtime/worker/readiness.js +10 -6
- package/src/runtime/worker/supervisor.js +18 -6
- package/src/runtime/workflow-orchestrator.js +39 -7
- package/core/harnesses/claude/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +0 -10
- package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +0 -9
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +0 -25
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +0 -105
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +0 -579
package/core/registry/rules.json
CHANGED
|
@@ -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": "
|
|
12
|
-
"enforcementRef":
|
|
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": "
|
|
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
|
|
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": "
|
|
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": "
|
|
177
|
-
"enforcementRef":
|
|
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":
|
|
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":
|
|
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":
|
|
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":
|
|
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":
|
|
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":
|
|
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":
|
|
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":
|
|
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":
|
|
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":
|
|
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":
|
|
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":
|
|
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": ["
|
|
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;
|
|
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; `
|
|
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 (
|
|
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
|
|
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
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
#
|
|
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
|
-
|
|
79
|
-
if (match(
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|