@deksden-com/dd-flow-cli 0.4.2 → 0.6.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 (53) hide show
  1. package/CHANGELOG.md +33 -4
  2. package/README.md +2 -2
  3. package/dist/build-info.json +6 -6
  4. package/dist/cli/help.js +16 -4
  5. package/dist/cli/run-cli.js +42 -17
  6. package/dist/domain/flow-contract.js +82 -3
  7. package/dist/domain/session-coverage.js +88 -0
  8. package/dist/domain/validation.js +56 -28
  9. package/dist/protocol/local-files.js +1 -16
  10. package/dist/schemas/code-stage-report.schema.json +2 -2
  11. package/dist/schemas/flow-contract.schema.json +152 -94
  12. package/dist/schemas/flow-run.schema.json +129 -23
  13. package/dist/schemas/mb-upgrade-review-data.schema.json +2 -2
  14. package/dist/schemas/memorybank-permissions-preflight.schema.json +13 -73
  15. package/dist/schemas/merge-stage-report.schema.json +2 -2
  16. package/dist/schemas/plan-stage-report.schema.json +38 -335
  17. package/dist/schemas/project-flow-pack-manifest.schema.json +4 -4
  18. package/dist/schemas/protocol-plan.schema.json +197 -0
  19. package/dist/schemas/release-impact.schema.json +9 -5
  20. package/dist/schemas/session-usage.schema.json +16 -0
  21. package/dist/schemas/stage-finish-input.schema.json +20 -0
  22. package/dist/schemas/stage-prompt.schema.json +35 -0
  23. package/dist/schemas/stage-report.schema.json +20 -0
  24. package/dist/schemas/stage-start-response.schema.json +30 -0
  25. package/dist/schemas/timeline-event.schema.json +29 -0
  26. package/dist/schemas/worktrunk-workspace.schema.json +19 -0
  27. package/dist/services/branch-context.js +9 -4
  28. package/dist/services/cleanup.js +77 -0
  29. package/dist/services/dashboard.js +51 -26
  30. package/dist/services/engines.js +84 -18
  31. package/dist/services/hooks.js +101 -250
  32. package/dist/services/memory-permissions.js +77 -69
  33. package/dist/services/migrations.js +1 -1
  34. package/dist/services/plan-runtime.js +124 -0
  35. package/dist/services/plans.js +22 -84
  36. package/dist/services/projects.js +2 -1
  37. package/dist/services/prompts.js +26 -21
  38. package/dist/services/protocols.js +29 -25
  39. package/dist/services/run-projection.js +93 -13
  40. package/dist/services/runs.js +128 -61
  41. package/dist/services/schema-validation.js +168 -7
  42. package/dist/services/sessions.js +97 -73
  43. package/dist/services/stage-lifecycle.js +737 -0
  44. package/dist/services/tooling.js +285 -0
  45. package/dist/services/usage.js +183 -30
  46. package/dist/services/version-status.js +1 -1
  47. package/dist/services/worktrees.js +88 -39
  48. package/dist/storage/database.js +72 -30
  49. package/dist/storage/paths.js +0 -9
  50. package/package.json +14 -13
  51. package/tools/worktrunk-manifest.json +34 -0
  52. package/dist/schemas/flow-run-index-v3.schema.json +0 -203
  53. 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,35 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "dd-flow/stage-prompt@2",
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", "authoritative_facts", "preflight", "required_context", "worker_prompt_markdown"],
20
+ "properties": {
21
+ "schema_id": {"const": "dd-flow/stage-prompt@2"},
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": 8, "maxItems": 8, "items": {"enum": ["stage_identity", "authoritative_runtime_facts", "preflight", "task_intake", "applicable_instructions", "required_context", "work_contract", "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
+ "authoritative_facts": {"type": "object", "additionalProperties": true},
31
+ "preflight": {"type": "object", "additionalProperties": true},
32
+ "required_context": {"type": "array", "items": {"type": "string", "minLength": 1}},
33
+ "worker_prompt_markdown": {"type": "string", "minLength": 1}
34
+ }
35
+ }
@@ -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,30 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "dd-flow/stage-start-response@2",
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", "worker_prompt_markdown"],
14
+ "properties": {
15
+ "schema_id": {"const": "dd-flow/stage-start-response@2"},
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": {"type": "string", "pattern": "^dd-flow stage finish "},
27
+ "permission_probe": {"type": "object", "additionalProperties": true},
28
+ "worker_prompt_markdown": {"type": "string", "minLength": 1}
29
+ }
30
+ }
@@ -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 @@ const actionKinds = new Set([
12
12
  "repair_protocol_state",
13
13
  "cancel_queue_job",
14
14
  "close_flow_session",
15
+ "discard_stale_run",
15
16
  "expire_lane_lock",
16
17
  "expire_lane_waiter",
17
18
  "close_worktree_record"
@@ -86,6 +87,36 @@ export function cleanupScan(context, input) {
86
87
  });
87
88
  }
88
89
  }
90
+ const staleRuns = context.db.all(`SELECT id, runtime_path, run_dir, workspace_root FROM flow_runs
91
+ WHERE project_id = ? AND status = 'running'
92
+ ORDER BY updated_at ASC, id ASC`, [project.id]);
93
+ for (const run of staleRuns) {
94
+ const missingAuthority = !fs.existsSync(run.runtime_path);
95
+ const missingRunHome = !fs.existsSync(run.run_dir);
96
+ const missingWorkspace = !fs.existsSync(run.workspace_root);
97
+ if (!missingAuthority)
98
+ continue;
99
+ const details = {
100
+ code: "stale_run_missing_authority",
101
+ severity: "warning",
102
+ run_id: run.id,
103
+ runtime_path: run.runtime_path,
104
+ missing_run_home: missingRunHome,
105
+ missing_workspace: missingWorkspace
106
+ };
107
+ if (missingRunHome && missingWorkspace) {
108
+ pushFinding(findings, actions, details.code, details.severity, {
109
+ run_id: run.id,
110
+ runtime_path: run.runtime_path,
111
+ missing_run_home: missingRunHome,
112
+ missing_workspace: missingWorkspace,
113
+ action: { kind: "discard_stale_run", project_id: project.id, run_id: run.id }
114
+ });
115
+ }
116
+ else {
117
+ findings.push({ ...details, action: null, reason: "manual_reconciliation_required" });
118
+ }
119
+ }
89
120
  const now = context.now();
90
121
  const expiredLocks = context.db.all(`SELECT id, lane, worker_id, expires_at FROM lane_locks
91
122
  WHERE project_id = ? AND status = 'active' AND expires_at <= ?
@@ -289,6 +320,41 @@ function applyAction(context, action, reason, force, now) {
289
320
  });
290
321
  return { kind: action.kind, changed: true, session_id: action.session_id };
291
322
  }
323
+ if (action.kind === "discard_stale_run" && action.run_id) {
324
+ const run = context.db.get("SELECT status, runtime_path, run_dir, workspace_root, index_json FROM flow_runs WHERE project_id = ? AND id = ?", [action.project_id, action.run_id]);
325
+ if (!run) {
326
+ return { kind: action.kind, changed: false, skipped: true, reason: "run_not_found", run_id: action.run_id };
327
+ }
328
+ if (run.status === "discarded") {
329
+ const index = discardedRunIndex(run.index_json, reason, now);
330
+ context.db.run("UPDATE flow_runs SET verdict = 'discarded', next_action = ?, index_json = ?, updated_at = ?, completed_at = COALESCE(completed_at, ?) WHERE project_id = ? AND id = ?", [reason, JSON.stringify(index), now, now, action.project_id, action.run_id]);
331
+ if (fs.existsSync(run.runtime_path))
332
+ fs.writeFileSync(run.runtime_path, `${JSON.stringify(index, null, 2)}\n`);
333
+ return { kind: action.kind, changed: true, run_id: action.run_id };
334
+ }
335
+ if (run.status !== "running") {
336
+ return { kind: action.kind, changed: false, skipped: true, reason: `already_${run.status}`, run_id: action.run_id };
337
+ }
338
+ if (fs.existsSync(run.runtime_path) || fs.existsSync(run.run_dir) || fs.existsSync(run.workspace_root)) {
339
+ return { kind: action.kind, changed: false, skipped: true, reason: "runtime_or_workspace_reappeared", run_id: action.run_id };
340
+ }
341
+ const index = discardedRunIndex(run.index_json, reason, now);
342
+ context.db.run(`UPDATE flow_runs SET status = 'discarded', verdict = 'discarded', next_action = ?, index_json = ?, updated_at = ?, completed_at = ?
343
+ WHERE project_id = ? AND id = ? AND status = 'running'`, [reason, JSON.stringify(index), now, now, action.project_id, action.run_id]);
344
+ fs.mkdirSync(run.run_dir, { recursive: true });
345
+ fs.writeFileSync(run.runtime_path, `${JSON.stringify(index, null, 2)}\n`);
346
+ context.db.run(`UPDATE flow_sessions SET status = 'stopped', stop_reason = ?, updated_at = ?, stopped_at = ?
347
+ WHERE project_id = ? AND run_id = ? AND status IN ('pending', 'active', 'waiting_user', 'blocked', 'stopping')`, [reason, now, now, action.project_id, action.run_id]);
348
+ context.db.run("UPDATE flow_session_segments SET ended_at = ? WHERE project_id = ? AND run_id = ? AND ended_at IS NULL", [now, action.project_id, action.run_id]);
349
+ appendAudit(context, {
350
+ projectId: action.project_id,
351
+ eventType: "flow_run.discarded",
352
+ reason,
353
+ forced: force,
354
+ payload: { run_id: action.run_id, source: "cleanup.apply", authority_missing: true }
355
+ });
356
+ return { kind: action.kind, changed: true, run_id: action.run_id };
357
+ }
292
358
  if (action.kind === "expire_lane_lock" && typeof action.lock_id === "number") {
293
359
  const result = context.db.run("UPDATE lane_locks SET status = 'expired', reason = ?, updated_at = ? WHERE project_id = ? AND id = ? AND status = 'active'", [reason, now, action.project_id, action.lock_id]);
294
360
  return {
@@ -330,6 +396,17 @@ function applyAction(context, action, reason, force, now) {
330
396
  }
331
397
  return { kind: action.kind, changed: false, skipped: true, reason: "invalid_action" };
332
398
  }
399
+ function discardedRunIndex(indexJson, reason, now) {
400
+ const index = JSON.parse(indexJson);
401
+ index.schema_id = "dd-flow/flow-run@2";
402
+ index.status = "discarded";
403
+ index.verdict = "discarded";
404
+ index.next_action = reason;
405
+ index.updated_at = now;
406
+ index.completed_at ??= now;
407
+ index.finished_at ??= now;
408
+ return index;
409
+ }
333
410
  function dirtyGit(worktreePath) {
334
411
  if (!fs.existsSync(worktreePath)) {
335
412
  return false;