@deksden-com/dd-flow-cli 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +27 -4
  2. package/dist/build-info.json +6 -6
  3. package/dist/cli/help.js +14 -3
  4. package/dist/cli/run-cli.js +32 -16
  5. package/dist/domain/flow-contract.js +81 -2
  6. package/dist/domain/validation.js +56 -28
  7. package/dist/protocol/local-files.js +1 -16
  8. package/dist/schemas/code-stage-report.schema.json +2 -2
  9. package/dist/schemas/flow-contract.schema.json +150 -94
  10. package/dist/schemas/flow-run.schema.json +129 -23
  11. package/dist/schemas/mb-upgrade-review-data.schema.json +2 -2
  12. package/dist/schemas/memorybank-permissions-preflight.schema.json +13 -73
  13. package/dist/schemas/merge-stage-report.schema.json +2 -2
  14. package/dist/schemas/plan-stage-report.schema.json +38 -335
  15. package/dist/schemas/project-flow-pack-manifest.schema.json +4 -4
  16. package/dist/schemas/protocol-plan.schema.json +197 -0
  17. package/dist/schemas/release-impact.schema.json +9 -5
  18. package/dist/schemas/session-usage.schema.json +16 -0
  19. package/dist/schemas/stage-finish-input.schema.json +20 -0
  20. package/dist/schemas/stage-prompt.schema.json +31 -0
  21. package/dist/schemas/stage-report.schema.json +20 -0
  22. package/dist/schemas/stage-start-response.schema.json +29 -0
  23. package/dist/schemas/timeline-event.schema.json +29 -0
  24. package/dist/schemas/worktrunk-workspace.schema.json +19 -0
  25. package/dist/services/branch-context.js +9 -4
  26. package/dist/services/dashboard.js +51 -26
  27. package/dist/services/engines.js +84 -18
  28. package/dist/services/hooks.js +80 -246
  29. package/dist/services/memory-permissions.js +77 -69
  30. package/dist/services/plan-runtime.js +124 -0
  31. package/dist/services/plans.js +22 -84
  32. package/dist/services/projects.js +2 -1
  33. package/dist/services/prompts.js +26 -21
  34. package/dist/services/protocols.js +29 -25
  35. package/dist/services/run-projection.js +77 -11
  36. package/dist/services/runs.js +95 -61
  37. package/dist/services/schema-validation.js +168 -7
  38. package/dist/services/sessions.js +132 -68
  39. package/dist/services/stage-lifecycle.js +572 -0
  40. package/dist/services/tooling.js +285 -0
  41. package/dist/services/usage.js +183 -30
  42. package/dist/services/version-status.js +1 -1
  43. package/dist/services/worktrees.js +88 -39
  44. package/dist/storage/database.js +72 -30
  45. package/dist/storage/paths.js +0 -9
  46. package/package.json +14 -13
  47. package/tools/worktrunk-manifest.json +34 -0
  48. package/dist/schemas/flow-run-index-v3.schema.json +0 -203
  49. package/dist/schemas/flow-run-index.schema.json +0 -175
@@ -0,0 +1,197 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "dd-flow/protocol-plan@1",
4
+ "title": "Canonical semantic protocol plan",
5
+ "description": "The repository-owned semantic plan. Runtime progress, workers, timestamps and mechanical evidence do not belong here.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["$schema", "schema_id", "plan_id", "protocol_id", "revision", "title", "summary", "specifications", "protocol_reference", "goal", "task_assessment", "route_decision", "policy_context", "sdlc_contours", "items", "verification", "code_handoff"],
9
+ "properties": {
10
+ "$schema": {"type": "string", "minLength": 1},
11
+ "schema_id": {"const": "dd-flow/protocol-plan@1"},
12
+ "plan_id": {"type": "string", "pattern": "^PLAN-[A-Za-z0-9][A-Za-z0-9._-]*$"},
13
+ "protocol_id": {"type": "string", "pattern": "^PRT-[A-Za-z0-9][A-Za-z0-9._-]*$"},
14
+ "revision": {"type": "integer", "minimum": 1},
15
+ "title": {"type": "string", "minLength": 1},
16
+ "summary": {"type": "string", "minLength": 1},
17
+ "specifications": {
18
+ "type": "array", "minItems": 1, "uniqueItems": true,
19
+ "items": {
20
+ "type": "object", "additionalProperties": false,
21
+ "required": ["id", "version", "path", "sha256", "requirements"],
22
+ "properties": {
23
+ "id": {"type": "string", "pattern": "^SPC-[0-9]{3}$"},
24
+ "version": {"type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"},
25
+ "path": {"type": "string", "pattern": "^\\.memory-bank/"},
26
+ "sha256": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
27
+ "requirements": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "string", "pattern": "^R-[0-9]+$"}}
28
+ }
29
+ }
30
+ },
31
+ "protocol_reference": {
32
+ "type": "object", "additionalProperties": false, "required": ["path", "sha256"],
33
+ "properties": {
34
+ "path": {"type": "string", "pattern": "^\\.memory-bank/protocol/"},
35
+ "sha256": {"type": "string", "pattern": "^[a-f0-9]{64}$"}
36
+ }
37
+ },
38
+ "goal": {
39
+ "type": "object", "additionalProperties": false, "required": ["outcome", "constraints", "non_goals"],
40
+ "properties": {
41
+ "outcome": {"type": "string", "minLength": 1},
42
+ "constraints": {"type": "array", "items": {"type": "string", "minLength": 1}},
43
+ "non_goals": {"type": "array", "items": {"type": "string", "minLength": 1}}
44
+ }
45
+ },
46
+ "task_assessment": {"$ref": "#/definitions/taskAssessment"},
47
+ "route_decision": {
48
+ "type": "object", "additionalProperties": false,
49
+ "required": ["initial_state", "selected_route", "selected_execution_mode", "reason", "capacity_probe", "packing"],
50
+ "properties": {
51
+ "initial_state": {"const": "orchestrator_local"},
52
+ "selected_route": {"enum": ["local_compact", "single_wave_grouped", "multi_wave_grouped", "external_handoff"]},
53
+ "selected_execution_mode": {"type": "string", "minLength": 1},
54
+ "reason": {"type": "string", "minLength": 1},
55
+ "capacity_probe": {"type": "string", "minLength": 1},
56
+ "packing": {"type": "string", "minLength": 1}
57
+ }
58
+ },
59
+ "policy_context": {
60
+ "type": "object", "additionalProperties": false,
61
+ "required": ["target_language", "source_authority", "current_truth_boundary", "validation", "lint"],
62
+ "properties": {
63
+ "target_language": {"type": "string", "minLength": 1},
64
+ "source_authority": {"type": "string", "minLength": 1},
65
+ "current_truth_boundary": {"type": "string", "minLength": 1},
66
+ "validation": {"type": "string", "minLength": 1},
67
+ "lint": {"type": "string", "minLength": 1}
68
+ }
69
+ },
70
+ "sdlc_contours": {
71
+ "type": "object", "additionalProperties": false, "required": ["summary", "contours"],
72
+ "properties": {
73
+ "summary": {"type": "string", "minLength": 1},
74
+ "contours": {
75
+ "type": "array", "minItems": 1,
76
+ "items": {
77
+ "type": "object", "additionalProperties": false,
78
+ "required": ["name", "applicability_status", "gate_status", "target_stage", "summary"],
79
+ "properties": {
80
+ "name": {"type": "string", "minLength": 1},
81
+ "applicability_status": {"enum": ["applicable", "external", "not_applicable"]},
82
+ "gate_status": {"enum": ["required", "deferred", "not_applicable"]},
83
+ "target_stage": {"enum": ["specify", "plan", "code", "readiness", "merge", "release"]},
84
+ "summary": {"type": "string", "minLength": 1}
85
+ }
86
+ }
87
+ }
88
+ }
89
+ },
90
+ "items": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"$ref": "#/definitions/planItem"}},
91
+ "verification": {
92
+ "type": "object", "additionalProperties": false, "required": ["scenario_seeds", "commands", "acceptance"],
93
+ "properties": {
94
+ "scenario_seeds": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
95
+ "commands": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
96
+ "acceptance": {"type": "string", "minLength": 1}
97
+ }
98
+ },
99
+ "code_handoff": {
100
+ "type": "object", "additionalProperties": false,
101
+ "required": ["must_read", "accepted_plan_ref", "companion_handoffs", "next_stage", "known_external_gate"],
102
+ "properties": {
103
+ "must_read": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
104
+ "accepted_plan_ref": {
105
+ "type": "object", "additionalProperties": false, "required": ["path", "revision", "sha256"],
106
+ "properties": {
107
+ "path": {"type": "string", "pattern": "^\\.memory-bank/protocol/[^/]+/plan\\.json$"},
108
+ "revision": {"type": "integer", "minimum": 1},
109
+ "sha256": {"enum": ["computed_by_stage_finish"]}
110
+ }
111
+ },
112
+ "companion_handoffs": {
113
+ "type": "array",
114
+ "items": {
115
+ "type": "object", "additionalProperties": false, "required": ["protocol_id", "repository", "scope"],
116
+ "properties": {
117
+ "protocol_id": {"type": "string", "pattern": "^PRT-[A-Za-z0-9][A-Za-z0-9._-]*$"},
118
+ "repository": {"type": "string", "minLength": 1},
119
+ "scope": {"type": "string", "minLength": 1}
120
+ }
121
+ }
122
+ },
123
+ "next_stage": {"enum": ["code", "readiness", "merge", "release"]},
124
+ "known_external_gate": {"type": "string", "minLength": 1}
125
+ }
126
+ }
127
+ },
128
+ "definitions": {
129
+ "semanticSpine": {
130
+ "type": "object", "additionalProperties": false,
131
+ "required": ["user_outcome", "component_responsibility", "must_preserve", "non_goals", "acceptance_contribution"],
132
+ "properties": {
133
+ "user_outcome": {"type": "string", "minLength": 1},
134
+ "component_responsibility": {"type": "string", "minLength": 1},
135
+ "must_preserve": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
136
+ "non_goals": {"type": "array", "items": {"type": "string", "minLength": 1}},
137
+ "acceptance_contribution": {"type": "string", "minLength": 1}
138
+ }
139
+ },
140
+ "executionContext": {
141
+ "type": "object", "additionalProperties": false,
142
+ "required": ["prompt_profile", "required_read", "discovery_boundary", "write_scope", "checks"],
143
+ "properties": {
144
+ "prompt_profile": {"enum": ["documentation_contract", "code_implementation", "verification"]},
145
+ "required_read": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
146
+ "discovery_boundary": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
147
+ "write_scope": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
148
+ "checks": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}}
149
+ }
150
+ },
151
+ "verificationContract": {
152
+ "type": "object", "additionalProperties": false, "required": ["checks", "evidence"],
153
+ "properties": {
154
+ "checks": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}},
155
+ "evidence": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}}
156
+ }
157
+ },
158
+ "planItem": {
159
+ "type": "object", "additionalProperties": false,
160
+ "required": ["id", "kind", "title", "summary", "depends_on", "owner", "target_stage", "required", "requirements", "semantic_spine", "execution_context", "verification_contract"],
161
+ "properties": {
162
+ "id": {"type": "string", "pattern": "^P[0-9]+$"},
163
+ "kind": {"type": "string", "minLength": 1},
164
+ "title": {"type": "string", "minLength": 1},
165
+ "summary": {"type": "string", "minLength": 1},
166
+ "details": {"type": "string", "minLength": 1},
167
+ "depends_on": {"type": "array", "uniqueItems": true, "items": {"type": "string", "pattern": "^P[0-9]+$"}},
168
+ "owner": {"type": "string", "minLength": 1},
169
+ "target_stage": {"enum": ["specify", "plan", "code", "readiness", "merge", "release"]},
170
+ "required": {"type": "boolean"},
171
+ "requirements": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "string", "pattern": "^SPC-[0-9]{3}@[0-9]+\\.[0-9]+\\.[0-9]+/R-[0-9]+$"}},
172
+ "semantic_spine": {"$ref": "#/definitions/semanticSpine"},
173
+ "execution_context": {"$ref": "#/definitions/executionContext"},
174
+ "verification_contract": {"$ref": "#/definitions/verificationContract"}
175
+ }
176
+ },
177
+ "taskAssessment": {
178
+ "type": "object", "additionalProperties": false,
179
+ "required": ["scope_breadth", "solution_novelty", "solution_uncertainty", "failure_impact", "plan_floor"],
180
+ "properties": {
181
+ "scope_breadth": {"$ref": "#/definitions/assessmentAxis"},
182
+ "solution_novelty": {"$ref": "#/definitions/assessmentAxis"},
183
+ "solution_uncertainty": {"$ref": "#/definitions/assessmentAxis"},
184
+ "failure_impact": {"$ref": "#/definitions/assessmentAxis"},
185
+ "plan_floor": {"$ref": "#/definitions/assessmentAxis"}
186
+ }
187
+ },
188
+ "assessmentAxis": {
189
+ "type": "object", "additionalProperties": false, "required": ["level", "surfaces", "reason"],
190
+ "properties": {
191
+ "level": {"type": "string", "minLength": 1},
192
+ "surfaces": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "string", "minLength": 1}},
193
+ "reason": {"type": "string", "minLength": 1}
194
+ }
195
+ }
196
+ }
197
+ }
@@ -7,10 +7,14 @@
7
7
  "required": ["schema_id", "from_version", "to_version", "minimum_upgrade_mode", "domains", "path_migration", "runtime_migration", "required_checks"],
8
8
  "properties": {
9
9
  "schema_id": {"const": "dd-flow/release-impact@1"},
10
- "from_version": {"type": "string"}, "to_version": {"type": "string"},
10
+ "from_version": {"type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"},
11
+ "to_version": {"type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"},
11
12
  "minimum_upgrade_mode": {"enum": ["patch", "targeted_minor", "full_migration"]},
12
- "domains": {"type": "array", "items": {"type": "string"}},
13
- "path_migration": {"type": "boolean"}, "runtime_migration": {"type": "boolean"},
14
- "required_checks": {"type": "array", "items": {"type": "string"}, "minItems": 1}, "notes": {"type": "string"}
15
- }
13
+ "domains": {"type": "array", "items": {"enum": ["canonical_layer", "root_structure", "product", "system", "engineering", "operations", "adr", "plans", "scenarios", "ui", "guides", "protocol_evidence", "skills", "archive", "code_traceability"]}, "uniqueItems": true},
14
+ "path_migration": {"type": "boolean"},
15
+ "runtime_migration": {"type": "boolean"},
16
+ "required_checks": {"type": "array", "items": {"type": "string"}, "minItems": 1},
17
+ "notes": {"type": "string"}
18
+ },
19
+ "allOf": [{"if": {"properties": {"minimum_upgrade_mode": {"const": "patch"}}}, "then": {"properties": {"path_migration": {"const": false}, "runtime_migration": {"const": false}}}}]
16
20
  }
@@ -0,0 +1,16 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "dd-flow/session-usage@1",
4
+ "title": "Session and usage coverage",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_id", "session_coverage", "usage_coverage", "sessions", "usage"],
8
+ "properties": {
9
+ "schema_id": {"const": "dd-flow/session-usage@1"},
10
+ "session_coverage": {"$ref": "#/definitions/coverage"},
11
+ "usage_coverage": {"$ref": "#/definitions/coverage"},
12
+ "sessions": {"type": "array", "items": {"type": "object", "required": ["session_id", "kind", "status"], "properties": {"session_id": {"type": "string"}, "kind": {"type": "string"}, "status": {"type": "string"}}, "additionalProperties": true}},
13
+ "usage": {"type": "object", "required": ["input_tokens", "cache_read_input_tokens", "cache_write_input_tokens", "output_tokens", "reasoning_output_tokens"], "properties": {"input_tokens": {"type": ["integer", "null"]}, "cache_read_input_tokens": {"type": ["integer", "null"]}, "cache_write_input_tokens": {"type": ["integer", "null"]}, "output_tokens": {"type": ["integer", "null"]}, "reasoning_output_tokens": {"type": ["integer", "null"]}}, "additionalProperties": true}
14
+ },
15
+ "definitions": {"coverage": {"type": "object", "required": ["status", "expected", "observed", "missing_sessions"], "properties": {"status": {"enum": ["complete", "partial", "unavailable"]}, "expected": {"type": "array"}, "observed": {"type": "array"}, "missing_sessions": {"type": "array"}}, "additionalProperties": true}}
16
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "dd-flow/stage-finish-input@1",
4
+ "title": "Semantic stage finish input",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_id", "status", "result", "acceptance", "changed_files", "checks", "evidence", "next_action"],
8
+ "properties": {
9
+ "schema_id": {"const": "dd-flow/stage-finish-input@1"},
10
+ "status": {"enum": ["done", "blocked", "failed"]},
11
+ "result": {"type": "string", "minLength": 1},
12
+ "acceptance": {"type": "array", "items": {"type": "string", "minLength": 1}},
13
+ "changed_files": {"type": "array", "items": {"type": "string", "minLength": 1}},
14
+ "checks": {"type": "array", "items": {"type": "string", "minLength": 1}},
15
+ "evidence": {"type": "array", "items": {"type": "string", "minLength": 1}},
16
+ "reviewer_findings": {"type": "array", "items": {"type": "string"}},
17
+ "def_outcomes": {"type": "array", "items": {"type": "string"}},
18
+ "next_action": {"type": "string", "minLength": 1}
19
+ }
20
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "dd-flow/stage-prompt@1",
4
+ "title": "Generated stage prompt",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "allOf": [
8
+ {
9
+ "if": {"properties": {"stage": {"const": "plan"}}, "required": ["stage"]},
10
+ "then": {
11
+ "properties": {
12
+ "aliases": {
13
+ "required": ["plan", "aspect-map"]
14
+ }
15
+ }
16
+ }
17
+ }
18
+ ],
19
+ "required": ["schema_id", "run_id", "stage", "generated_at", "prompt_path", "sections", "aliases", "write_boundary", "source_fragments"],
20
+ "properties": {
21
+ "schema_id": {"const": "dd-flow/stage-prompt@1"},
22
+ "run_id": {"type": "string", "minLength": 1},
23
+ "stage": {"type": "string", "minLength": 1},
24
+ "generated_at": {"type": "string", "format": "date-time"},
25
+ "prompt_path": {"type": "string", "minLength": 1},
26
+ "sections": {"type": "array", "minItems": 7, "maxItems": 7, "items": {"enum": ["stage_identity", "runtime_context", "intake", "applicable_instructions", "stage_cli", "file_boundaries", "completion_contract"]}},
27
+ "aliases": {"type": "object", "required": ["project", "workspace", "run", "stage", "protocol", "intake"], "additionalProperties": {"type": "string"}},
28
+ "write_boundary": {"type": "object", "required": ["current", "archive", "archive_writable"], "properties": {"current": {"const": "@stage"}, "archive": {"pattern": "try-[0-9]{3}"}, "archive_writable": {"const": false}}, "additionalProperties": false},
29
+ "source_fragments": {"type": "array", "items": {"type": "string", "minLength": 1}}
30
+ }
31
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "dd-flow/stage-report@1",
4
+ "title": "Generated stage report",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_id", "run_id", "stage", "generated_at", "verdict", "semantic", "mechanical", "artifacts", "validation"],
8
+ "properties": {
9
+ "schema_id": {"const": "dd-flow/stage-report@1"},
10
+ "run_id": {"type": "string", "minLength": 1},
11
+ "stage": {"type": "string", "minLength": 1},
12
+ "generated_at": {"type": "string", "format": "date-time"},
13
+ "verdict": {"enum": ["done", "blocked", "failed", "degraded"]},
14
+ "semantic": {"type": "object", "required": ["result", "acceptance", "changed_files", "checks", "evidence", "next_action"], "additionalProperties": true, "properties": {"result": {"type": "string"}, "acceptance": {"type": "array"}, "changed_files": {"type": "array"}, "checks": {"type": "array"}, "evidence": {"type": "array"}, "next_action": {"type": "string"}}},
15
+ "mechanical": {"type": "object", "required": ["started_at", "finished_at", "wall_clock_ms", "git", "session_coverage", "usage_coverage"], "additionalProperties": true, "properties": {"started_at": {"type": "string", "format": "date-time"}, "finished_at": {"type": "string", "format": "date-time"}, "wall_clock_ms": {"type": "integer", "minimum": 0}, "git": {"type": "object"}, "session_coverage": {"type": "object"}, "usage_coverage": {"type": "object"}}},
16
+ "artifacts": {"type": "object", "required": ["json", "markdown", "html", "summary"], "additionalProperties": false, "properties": {"json": {"type": "string"}, "markdown": {"type": "string"}, "html": {"type": "string"}, "summary": {"type": "string"}}},
17
+ "validation": {"type": "object", "required": ["permission_scope", "memory_bank_scope", "status"], "additionalProperties": true, "properties": {"permission_scope": {"const": "known_targets_only"}, "memory_bank_scope": {"const": "changed_files_and_links_only"}, "status": {"type": "string"}}},
18
+ "breadcrumbs": {"type": "array", "items": {"type": "object"}}
19
+ }
20
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "dd-flow/stage-start-response@1",
4
+ "title": "Stage start response",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "allOf": [
8
+ {
9
+ "if": {"properties": {"stage": {"const": "plan"}}, "required": ["stage"]},
10
+ "then": {"required": ["plan_ref", "aspect_map_ref"]}
11
+ }
12
+ ],
13
+ "required": ["schema_id", "run_id", "stage", "attempt_number", "stage_root", "prompt_path", "aliases", "next_command"],
14
+ "properties": {
15
+ "schema_id": {"const": "dd-flow/stage-start-response@1"},
16
+ "run_id": {"type": "string", "minLength": 1},
17
+ "stage": {"type": "string", "minLength": 1},
18
+ "attempt_number": {"type": "integer", "minimum": 1},
19
+ "stage_root": {"type": "string", "minLength": 1},
20
+ "archive_path": {"type": ["string", "null"]},
21
+ "prompt_path": {"type": "string", "minLength": 1},
22
+ "aliases": {"type": "object", "additionalProperties": {"type": "string"}},
23
+ "resolved_context": {"type": "object", "additionalProperties": true},
24
+ "plan_ref": {"type": "string", "pattern": "^\\.memory-bank/protocol/[^/]+/plan\\.json$"},
25
+ "aspect_map_ref": {"type": "string", "minLength": 1},
26
+ "next_command": {"const": "dd-flow stage finish"},
27
+ "permission_probe": {"type": "object", "additionalProperties": true}
28
+ }
29
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "dd-flow/timeline-event@1",
4
+ "title": "Safe RUN timeline event",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_id", "timestamp", "event", "run_id", "payload"],
8
+ "properties": {
9
+ "schema_id": {"const": "dd-flow/timeline-event@1"},
10
+ "timestamp": {"type": "string", "format": "date-time"},
11
+ "event": {"type": "string", "minLength": 1},
12
+ "run_id": {"type": "string", "minLength": 1},
13
+ "stage": {"type": ["string", "null"]},
14
+ "attempt_number": {"type": ["integer", "null"], "minimum": 1},
15
+ "session_id": {"type": ["string", "null"]},
16
+ "job_id": {"type": ["string", "null"]},
17
+ "status": {"type": ["string", "null"]},
18
+ "payload": {
19
+ "type": "object",
20
+ "additionalProperties": false,
21
+ "properties": {
22
+ "reason": {"type": "string"},
23
+ "source": {"type": "string"},
24
+ "count": {"type": "integer", "minimum": 0},
25
+ "redaction": {"const": "safe-structured-only"}
26
+ }
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "dd-flow/worktrunk-workspace@1",
4
+ "title": "Verified Worktrunk workspace result",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_id", "command", "official_pinned_artifact", "workspace_path", "branch", "base", "tool_version", "bootstrap_status"],
8
+ "properties": {
9
+ "schema_id": {"const": "dd-flow/worktrunk-workspace@1"},
10
+ "command": {"const": "wt switch --create <branch> --base <base> --format json"},
11
+ "official_pinned_artifact": {"type": "object", "required": ["url", "version", "sha256", "verified"], "properties": {"url": {"type": "string"}, "version": {"type": "string"}, "sha256": {"pattern": "^[a-f0-9]{64}$"}, "verified": {"const": true}}, "additionalProperties": false},
12
+ "workspace_path": {"type": "string", "minLength": 1},
13
+ "branch": {"type": "string", "minLength": 1},
14
+ "base": {"type": "string", "minLength": 1},
15
+ "tool_version": {"type": "string", "minLength": 1},
16
+ "bootstrap_status": {"type": "string", "minLength": 1},
17
+ "hooks": {"type": "array", "items": {"type": "string"}}
18
+ }
19
+ }
@@ -136,7 +136,7 @@ function summarizeProtocol(row, queue, worktree, activeSessions) {
136
136
  const worktreePath = normalizePath(stringValue(worktree?.worktree_path) ?? stringValue(workspace.worktree_path));
137
137
  const activeDef = parseJsonArray(row.active_def_json);
138
138
  const queueStatus = queue?.status ?? null;
139
- const bundleBlockers = bundleBlockersFor(row.stage, queueStatus, activeDef.length);
139
+ const bundleBlockers = bundleBlockersFor(row.stage, queueStatus, activeDef.some(isBlockingDeferral));
140
140
  return {
141
141
  id: row.id,
142
142
  stage: row.stage,
@@ -148,7 +148,7 @@ function summarizeProtocol(row, queue, worktree, activeSessions) {
148
148
  integration_branch: stringValue(worktree?.integration_branch) ?? stringValue(workspace.integration_branch),
149
149
  feature_branch: featureBranch,
150
150
  worktree_path: worktreePath,
151
- bundle_eligible: ["ready_for_merge", "queued_for_merge"].includes(row.stage) && ["ready", "requeued"].includes(queueStatus ?? "") && activeDef.length === 0,
151
+ bundle_eligible: ["ready_for_merge", "queued_for_merge"].includes(row.stage) && ["ready", "requeued"].includes(queueStatus ?? "") && !activeDef.some(isBlockingDeferral),
152
152
  bundle_blockers: bundleBlockers,
153
153
  updated_at: row.updated_at
154
154
  };
@@ -192,9 +192,9 @@ function branchDiagnostics(resolved) {
192
192
  }
193
193
  return diagnostics;
194
194
  }
195
- function bundleBlockersFor(stage, queueStatus, activeDefCount) {
195
+ function bundleBlockersFor(stage, queueStatus, hasBlockingDef) {
196
196
  const blockers = [];
197
- if (activeDefCount > 0)
197
+ if (hasBlockingDef)
198
198
  blockers.push("active_def");
199
199
  if (stage === "blocked")
200
200
  blockers.push("protocol_blocked");
@@ -202,6 +202,11 @@ function bundleBlockersFor(stage, queueStatus, activeDefCount) {
202
202
  blockers.push("already_claimed");
203
203
  return blockers;
204
204
  }
205
+ function isBlockingDeferral(value) {
206
+ if (!value || typeof value !== "object" || Array.isArray(value))
207
+ return true;
208
+ return value.blocking !== false;
209
+ }
205
210
  function bundleReason(eligible, notReady, claimed, blocked, diagnostics) {
206
211
  if (diagnostics.length > 0)
207
212
  return diagnostics[0] ?? "diagnostic_blocker";
@@ -12,6 +12,7 @@ import { protocolRunDiagnostics, protocolSetBoardForProtocol, protocolSetBoardsF
12
12
  import { activeFlowSessionsForProject } from "./sessions.js";
13
13
  import { usageForRun } from "./usage.js";
14
14
  import { normalizeProtocolLifecycle } from "./protocol-lifecycle.js";
15
+ import { planSummary, planWithProgress } from "./plan-runtime.js";
15
16
  export function getCmuxStatus(context, input) {
16
17
  const project = requireProjectByRoot(context, resolveProjectRoot(input.projectRoot));
17
18
  return { ok: true, project_root: project.root, cmux: detectCmux(context) };
@@ -626,14 +627,20 @@ function buildProtocolDashboardData(context, project, protocolId, htmlPath) {
626
627
  const runs = context.db.all(`SELECT id, short_id, flow_kind, subject_type, subject_id, status, verdict, next_action, run_index_path, index_json, created_at, updated_at, completed_at
627
628
  FROM flow_runs WHERE project_id = ? AND subject_type = 'protocol' AND subject_id = ?
628
629
  ORDER BY updated_at DESC, id DESC`, [project.id, protocolId]);
629
- const primaryRun = runs.find((run) => run.status === "running") ?? runs[0];
630
+ const warnings = [...runDiagnostics.diagnostics];
631
+ const canonicalRuns = runs.filter((run) => {
632
+ if (isCanonicalRun(run))
633
+ return true;
634
+ warnings.push({ code: "legacy_run_ignored", run_id: run.id });
635
+ return false;
636
+ });
637
+ const primaryRun = lifecycle.terminal ? canonicalRuns[0] : canonicalRuns.find((run) => run.status === "running") ?? canonicalRuns[0];
630
638
  const observability = primaryRun
631
639
  ? usageForRun(context, { projectId: project.id, runId: primaryRun.id, groupBy: "role" })
632
640
  : { status: "not_observable", reason: "no_protocol_run" };
633
- const warnings = [...runDiagnostics.diagnostics];
634
641
  const protocolSetBoard = protocolSetBoardForProtocol(context, project.id, project.root, protocolId);
635
642
  const reviewRuns = recentReviewRunsForProtocol(context, project.id, protocolId, 8);
636
- const runHistory = runs.map((run) => {
643
+ const runHistory = canonicalRuns.map((run) => {
637
644
  const index = safeRunIndex(run.index_json, warnings, run.id);
638
645
  return {
639
646
  id: run.id,
@@ -707,10 +714,10 @@ function buildProtocolDashboardData(context, project, protocolId, htmlPath) {
707
714
  run_history: runHistory,
708
715
  stage_pipeline: latestStages(runHistory),
709
716
  metrics: [
710
- metric("runs", runs.length, "known", "flow_runs"),
717
+ metric("runs", canonicalRuns.length, "known", "flow_runs"),
711
718
  metric("open_defs", jsonArray(protocol.active_def_json).length, "known", "protocols.active_def_json"),
712
719
  metric("blockers", jsonArray(protocol.blockers_json).length, "known", "protocols.blockers_json"),
713
- metric("completed_runs", runs.filter((run) => run.status === "done").length, "known", "flow_runs"),
720
+ metric("completed_runs", canonicalRuns.filter((run) => run.status === "done").length, "known", "flow_runs"),
714
721
  metric("review_runs", reviewRuns.length, "known", "flow_runs.flow_kind=mb-sdlc-review|review")
715
722
  ],
716
723
  warnings
@@ -881,34 +888,39 @@ function protocolsForProject(context, projectId) {
881
888
  FROM protocols WHERE project_id = ? ORDER BY updated_at DESC`, [projectId]);
882
889
  }
883
890
  function planSummaryForProtocol(context, projectId, protocolId) {
884
- const row = context.db.get("SELECT plan_json FROM plans WHERE project_id = ? AND protocol_id = ?", [projectId, protocolId]);
885
- if (!row) {
891
+ const protocol = context.db.get("SELECT plan_path FROM protocols WHERE project_id = ? AND id = ?", [projectId, protocolId]);
892
+ if (!protocol) {
886
893
  return { plan_id: "none", total: 0, done: 0, blocked: 0 };
887
894
  }
888
- const plan = JSON.parse(row.plan_json);
889
- const items = Array.isArray(plan.items) ? plan.items : [];
890
- return {
891
- plan_id: plan.plan_id ?? "unknown",
892
- total: items.length,
893
- done: items.filter((item) => item.status === "done").length,
894
- blocked: items.filter((item) => item.status === "blocked").length
895
- };
895
+ try {
896
+ const summary = planSummary(context, { projectId, protocolId, planPath: protocol.plan_path });
897
+ return { plan_id: summary.plan_id, total: summary.total, done: summary.done, blocked: summary.blocked };
898
+ }
899
+ catch {
900
+ return { plan_id: "stale", total: 0, done: 0, blocked: 0 };
901
+ }
896
902
  }
897
903
  function activePlanItems(context, projectId, protocols) {
898
904
  const items = [];
899
905
  for (const protocol of protocols) {
900
- const row = context.db.get("SELECT plan_json FROM plans WHERE project_id = ? AND protocol_id = ?", [projectId, protocol.id]);
901
- if (!row) {
906
+ const record = context.db.get("SELECT plan_path FROM protocols WHERE project_id = ? AND id = ?", [projectId, protocol.id]);
907
+ if (!record) {
902
908
  continue;
903
909
  }
904
- const plan = JSON.parse(row.plan_json);
905
- for (const item of Array.isArray(plan.items) ? plan.items : []) {
910
+ let plan;
911
+ try {
912
+ plan = planWithProgress(context, { projectId, protocolId: protocol.id, planPath: record.plan_path }).plan;
913
+ }
914
+ catch {
915
+ continue;
916
+ }
917
+ for (const item of plan.items) {
906
918
  if (["in_progress", "blocked"].includes(item.status ?? "")) {
907
919
  items.push({
908
920
  protocol: protocol.id,
909
- id: item.id ?? "unknown",
910
- status: item.status ?? "unknown",
911
- summary: item.block_reason ?? item.summary ?? item.title ?? ""
921
+ id: item.id,
922
+ status: item.status,
923
+ summary: item.block_reason ?? item.summary ?? item.title
912
924
  });
913
925
  }
914
926
  }
@@ -1040,9 +1052,10 @@ function buildProtocolCard(context, project, protocol, generatePage) {
1040
1052
  const summary = planSummaryForProtocol(context, project.id, protocol.id);
1041
1053
  const activeDefCount = jsonArray(protocol.active_def_json).length;
1042
1054
  const blockerCount = jsonArray(protocol.blockers_json).length;
1043
- const latestRun = context.db.get(`SELECT id, short_id, flow_kind, subject_type, subject_id, status, verdict, next_action, run_index_path, index_json, created_at, updated_at, completed_at
1055
+ const runs = context.db.all(`SELECT id, short_id, flow_kind, subject_type, subject_id, status, verdict, next_action, run_index_path, index_json, created_at, updated_at, completed_at
1044
1056
  FROM flow_runs WHERE project_id = ? AND subject_type = 'protocol' AND subject_id = ?
1045
- ORDER BY updated_at DESC, id DESC LIMIT 1`, [project.id, protocol.id]);
1057
+ ORDER BY updated_at DESC, id DESC`, [project.id, protocol.id]);
1058
+ const latestRun = runs.find((run) => isCanonicalRun(run));
1046
1059
  let diagnostics = [];
1047
1060
  let lifecycle = normalizeProtocolLifecycle({ rawStage: protocol.stage, rawStatus: protocol.status });
1048
1061
  try {
@@ -1242,10 +1255,19 @@ function safeRunIndex(text, warnings, runId) {
1242
1255
  return null;
1243
1256
  }
1244
1257
  }
1258
+ function isCanonicalRun(run) {
1259
+ try {
1260
+ const parsed = JSON.parse(run.index_json);
1261
+ return parsed?.schema_id === "dd-flow/flow-run@2";
1262
+ }
1263
+ catch {
1264
+ return false;
1265
+ }
1266
+ }
1245
1267
  function stageLink(projectRoot, run, stage) {
1246
1268
  const item = stage && typeof stage === "object" && !Array.isArray(stage) ? stage : {};
1247
1269
  const report = typeof item.stage_report === "string" ? item.stage_report : undefined;
1248
- const href = report ? path.join(path.dirname(run.run_index_path), report) : "";
1270
+ const href = report ? runArtifactPath(run.run_index_path, report) : "";
1249
1271
  return {
1250
1272
  order: typeof item.order === "number" ? item.order : 0,
1251
1273
  stage: String(item.stage ?? "unknown"),
@@ -1253,10 +1275,13 @@ function stageLink(projectRoot, run, stage) {
1253
1275
  status: String(item.status ?? "unknown"),
1254
1276
  display_status: normalizeDisplayStatus(String(item.status ?? "unknown")),
1255
1277
  report: report ? link("Stage report", href, fs.existsSync(href) ? "available" : "missing", fs.existsSync(href) ? undefined : "stage_report_missing") : link("Stage report", "", "pending", "stage_report_not_created_yet"),
1256
- data: typeof item.data === "string" ? path.join(path.dirname(run.run_index_path), item.data) : null,
1278
+ data: typeof item.data === "string" ? runArtifactPath(run.run_index_path, item.data) : null,
1257
1279
  run_index: path.relative(projectRoot, run.run_index_path)
1258
1280
  };
1259
1281
  }
1282
+ function runArtifactPath(runIndexPath, artifact) {
1283
+ return path.isAbsolute(artifact) ? artifact : path.resolve(path.dirname(runIndexPath), artifact);
1284
+ }
1260
1285
  function latestStages(runHistory) {
1261
1286
  const byStage = new Map();
1262
1287
  for (const run of runHistory) {