@tea-agent/loop-agent 0.26.0 → 0.26.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/CHANGELOG.md +1056 -1020
  2. package/README.md +8 -3
  3. package/bin/loop-agent.js +21 -21
  4. package/dist/application/dag/generate-task-dag.js +33 -0
  5. package/dist/cli/command-definitions.js +25 -10
  6. package/dist/cli/help.js +4 -3
  7. package/dist/cli/program.js +43 -17
  8. package/dist/commands/cursor-prompt.js +6 -6
  9. package/dist/commands/import-prd.js +7 -2
  10. package/dist/commands/init.js +7 -5
  11. package/dist/commands/loop-benchmark.js +11 -11
  12. package/dist/commands/pi-reuse-benchmark.js +16 -16
  13. package/dist/commands/task-source-prepare.js +474 -0
  14. package/dist/executors/dag-pi-executor.js +40 -5
  15. package/dist/executors/shell-executor.js +111 -0
  16. package/dist/executors/shell-presets.js +12 -4
  17. package/dist/executors/shell-write-guard.js +161 -25
  18. package/dist/sidecars/cursor-prompt/executor.js +1 -1
  19. package/dist/task/config-types.js +6 -0
  20. package/dist/task/contract/constants.js +1 -0
  21. package/dist/task/contract/project.js +8 -0
  22. package/dist/task/contract/schema.js +1 -0
  23. package/dist/task/frontend-preflight.js +131 -0
  24. package/dist/task/runtime.js +2 -4
  25. package/dist/task/source-prepare/build-draft.js +224 -0
  26. package/dist/task/source-prepare/completeness.js +195 -0
  27. package/dist/task/source-prepare/index.js +7 -0
  28. package/dist/task/source-prepare/parse-intent.js +373 -0
  29. package/dist/task/source-prepare/path-policy.js +197 -0
  30. package/dist/task/source-prepare/prepare.js +506 -0
  31. package/dist/task/source-prepare/reference-integrity.js +274 -0
  32. package/dist/task/source-prepare/types.js +7 -0
  33. package/dist/worker/observability/read-model.js +134 -0
  34. package/dist/worker/observe/static/copy.js +67 -67
  35. package/dist/worker/observe/static/dag-layout.d.ts +31 -31
  36. package/dist/worker/observe/static/dag-layout.js +83 -83
  37. package/dist/worker/observe/static/dom.js +220 -220
  38. package/dist/worker/observe/static/relations.js +133 -133
  39. package/dist/worker/observe/static/router.js +93 -93
  40. package/dist/worker/observe/static/run-processing.js +148 -148
  41. package/dist/worker/observe/static/state.js +61 -0
  42. package/dist/worker/observe/static/styles.css +8 -0
  43. package/dist/worker/observe/static/views/batch.js +227 -227
  44. package/dist/worker/observe/static/views/dag-graph.js +248 -172
  45. package/dist/worker/observe/static/views/dag-inspector.js +374 -157
  46. package/dist/worker/observe/static/views/dag.js +4 -11
  47. package/dist/worker/observe/static/views/failures.js +143 -143
  48. package/dist/worker/observe/static/views/feature.js +492 -492
  49. package/dist/worker/observe/static/views/run.js +453 -453
  50. package/dist/worker/observe/static/views/shell.js +7 -7
  51. package/dist/worker/observe/static/views/timeline.js +163 -163
  52. package/dist/workflows/dag/backend-test-pytest-collection.js +277 -0
  53. package/dist/workflows/dag/canvas-observer.js +275 -275
  54. package/dist/workflows/dag/convergence/controller.js +110 -21
  55. package/dist/workflows/dag/frontend-implementation-contract.js +218 -17
  56. package/dist/workflows/dag/frontend-review-context.js +7 -1
  57. package/dist/workflows/dag/frontend-verification-trace.js +14 -3
  58. package/dist/workflows/dag/frontend-worktree-diff.js +14 -3
  59. package/dist/workflows/dag/init-hybrid.js +96 -34
  60. package/dist/workflows/dag/output-protocol.js +180 -7
  61. package/dist/workflows/dag/runner.js +141 -52
  62. package/dist/workflows/dag/types.js +4 -0
  63. package/dist/workflows/dag/validate.js +3 -2
  64. package/docs/skills/README.md +7 -7
  65. package/docs/templates/adr.md +60 -60
  66. package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -94
  67. package/docs/templates/agent-dag-decision-envelope.schema.json +213 -213
  68. package/docs/templates/agent-dag-decision-gate.prompt.md +246 -246
  69. package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -98
  70. package/docs/templates/agent-dag-report.schema.json +473 -473
  71. package/docs/templates/agent-dag-review-verdict.prompt.md +68 -68
  72. package/docs/templates/backend-test-dag.json +100 -8
  73. package/docs/templates/backend-test-result.schema.json +99 -99
  74. package/docs/templates/feature-spec.md +53 -53
  75. package/docs/templates/frontend-design-contract.md +42 -42
  76. package/docs/templates/frontend-eval/fixtures/failures/01-type-build-error.md +17 -17
  77. package/docs/templates/frontend-eval/fixtures/failures/02-unit-component-test-fail.md +16 -16
  78. package/docs/templates/frontend-eval/fixtures/failures/03-fixture-schema-drift.md +16 -16
  79. package/docs/templates/frontend-eval/fixtures/failures/04-missing-loading-empty-error-state.md +16 -16
  80. package/docs/templates/frontend-eval/fixtures/failures/05-forbidden-write-writeset-expansion.md +16 -16
  81. package/docs/templates/frontend-eval/fixtures/failures/06-unapproved-dependency-add.md +16 -16
  82. package/docs/templates/frontend-eval/fixtures/failures/07-mock-production-on.md +21 -21
  83. package/docs/templates/frontend-eval/fixtures/functional/01-simple-component-style.md +29 -29
  84. package/docs/templates/frontend-eval/fixtures/functional/02-form-validation.md +28 -28
  85. package/docs/templates/frontend-eval/fixtures/functional/03-list-detail-page.md +28 -28
  86. package/docs/templates/frontend-eval/fixtures/functional/04-api-mock.md +29 -29
  87. package/docs/templates/frontend-eval/fixtures/functional/05-permission-auth-gated-ui.md +27 -27
  88. package/docs/templates/frontend-eval/fixtures/functional/06-ssr-server-client-boundary.md +28 -28
  89. package/docs/templates/frontend-eval/fixtures/functional/07-shared-public-component-api.md +28 -28
  90. package/docs/templates/frontend-eval/fixtures/functional/08-pure-local-no-remote.md +27 -27
  91. package/docs/templates/frontend-eval/metrics.md +138 -138
  92. package/docs/templates/frontend-eval/smoke-targets.md +53 -53
  93. package/docs/templates/frontend-task-constraints.md +35 -35
  94. package/docs/templates/frontend-task-requirement.md +70 -70
  95. package/docs/templates/init-evolution-review.md +35 -35
  96. package/docs/templates/init-managed-agents.md +10 -5
  97. package/docs/templates/interactive-ui-round2-experiment.md +66 -66
  98. package/docs/templates/knowledge-graph-bootstrap-dag.json +118 -118
  99. package/docs/templates/knowledge-sync-dag.json +178 -178
  100. package/docs/templates/knowledge-sync-draft.schema.json +71 -71
  101. package/docs/templates/product-line/closeout.yaml +9 -9
  102. package/docs/templates/product-line/design.md +13 -13
  103. package/docs/templates/product-line/links.md +10 -10
  104. package/docs/templates/product-line/requirement.md +17 -17
  105. package/docs/templates/product-line/test-plan.md +7 -7
  106. package/docs/templates/production-readiness-checklist.md +57 -57
  107. package/docs/templates/project-start-checklist.md +9 -9
  108. package/docs/templates/qa-report.md +48 -48
  109. package/docs/templates/sprint-contract.md +29 -29
  110. package/docs/templates/worker-dogfood-evidence.md +80 -80
  111. package/docs/templates/worker-dogfood-setup.md +68 -68
  112. package/package.json +1 -1
  113. package/scripts/kb-bootstrap-init-skeleton.sh +0 -0
  114. package/scripts/kb-graph-incremental-prepare.mjs +386 -386
  115. package/scripts/kb-graph-materialize.mjs +105 -105
  116. package/scripts/kb-graph-promote.mjs +164 -164
  117. package/scripts/kb-query.mjs +554 -554
  118. package/skills/ai-engineering-context/SKILL.md +48 -48
  119. package/skills/analyze-product-dependencies/SKILL.md +67 -67
  120. package/skills/analyze-product-dependencies/agents/openai.yaml +4 -4
  121. package/skills/analyze-product-dependencies/references/api-documentation-schema.md +30 -30
  122. package/skills/analyze-product-dependencies/references/dependency-analysis-schema.md +28 -28
  123. package/skills/analyze-product-dependencies/references/example.md +76 -76
  124. package/skills/analyze-product-dependencies/references/forward-test-cases.md +35 -35
  125. package/skills/analyze-product-dependencies/references/input-contract.md +11 -11
  126. package/skills/analyze-product-dependencies/references/scouting-rules.md +61 -61
  127. package/skills/analyze-product-dependencies/scripts/test-validators.mjs +267 -267
  128. package/skills/analyze-product-dependencies/scripts/validate-api-documentation.mjs +101 -101
  129. package/skills/analyze-product-dependencies/scripts/validate-dependency-analysis.mjs +142 -142
  130. package/skills/analyze-product-dependencies/scripts/validate-product-requirement-input.mjs +76 -76
  131. package/skills/analyze-product-dependencies/scripts/validation-helpers.mjs +146 -146
  132. package/skills/analyze-product-requirements/SKILL.md +90 -90
  133. package/skills/analyze-product-requirements/agents/openai.yaml +4 -4
  134. package/skills/analyze-product-requirements/references/acceptance-criteria.md +91 -91
  135. package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +56 -56
  136. package/skills/analyze-product-requirements/references/example.md +86 -86
  137. package/skills/analyze-product-requirements/references/forward-test-cases.md +66 -66
  138. package/skills/analyze-product-requirements/references/product-analysis-schema.md +32 -32
  139. package/skills/analyze-product-requirements/references/product-requirement-schema.md +33 -33
  140. package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +35 -35
  141. package/skills/analyze-product-requirements/scripts/test-validators.mjs +193 -193
  142. package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +69 -69
  143. package/skills/analyze-product-requirements/scripts/validate-product-requirement.mjs +97 -97
  144. package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +98 -98
  145. package/skills/analyze-product-requirements/scripts/validation-helpers.mjs +156 -156
  146. package/skills/browser-tools/browser-content.js +103 -103
  147. package/skills/browser-tools/browser-cookies.js +35 -35
  148. package/skills/browser-tools/browser-eval.js +53 -53
  149. package/skills/browser-tools/browser-hn-scraper.js +108 -108
  150. package/skills/browser-tools/browser-nav.js +44 -44
  151. package/skills/browser-tools/browser-pick.js +162 -162
  152. package/skills/browser-tools/browser-screenshot.js +34 -34
  153. package/skills/browser-tools/browser-start.js +86 -86
  154. package/skills/browser-tools/package-lock.json +2556 -2556
  155. package/skills/browser-tools/package.json +19 -19
  156. package/skills/code-review-core/SKILL.md +20 -20
  157. package/skills/codebase-scout/SKILL.md +19 -19
  158. package/skills/grill-me/SKILL.md +10 -10
  159. package/skills/loop-agent/SKILL.md +5 -2
  160. package/skills/loop-agent/references/README.md +67 -67
  161. package/skills/loop-agent/references/command-reference.md +17 -15
  162. package/skills/loop-agent/references/docs-converge.md +126 -126
  163. package/skills/loop-agent/references/harness-policy.md +3 -4
  164. package/skills/loop-agent/references/hybrid-dag.md +1 -1
  165. package/skills/loop-agent/references/learned/README.md +21 -21
  166. package/skills/loop-agent/references/long-running-loop.md +57 -57
  167. package/skills/loop-agent/references/one-shot-runs.md +85 -85
  168. package/skills/loop-agent/references/pi-prompt.md +23 -23
  169. package/skills/loop-agent/references/pi-subagent-assisted-mode.md +84 -84
  170. package/skills/loop-agent/references/post-implementation-and-patterns.md +1 -1
  171. package/skills/loop-agent/references/source-and-plan-practice.md +3 -2
  172. package/skills/loop-agent/references/task-workflow.md +7 -5
  173. package/skills/playwright-cli/SKILL.md +420 -420
  174. package/skills/playwright-cli/references/element-attributes.md +23 -23
  175. package/skills/playwright-cli/references/playwright-tests.md +39 -39
  176. package/skills/playwright-cli/references/request-mocking.md +87 -87
  177. package/skills/playwright-cli/references/running-code.md +241 -241
  178. package/skills/playwright-cli/references/session-management.md +225 -225
  179. package/skills/playwright-cli/references/storage-state.md +275 -275
  180. package/skills/playwright-cli/references/test-generation.md +433 -433
  181. package/skills/playwright-cli/references/tracing.md +139 -139
  182. package/skills/playwright-cli/references/video-recording.md +143 -143
  183. package/skills/requesting-code-review/SKILL.md +101 -101
  184. package/skills/requesting-code-review/code-reviewer.md +168 -168
  185. package/skills/systematic-debugging/CREATION-LOG.md +119 -119
  186. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
  187. package/skills/systematic-debugging/condition-based-waiting.md +115 -115
  188. package/skills/systematic-debugging/defense-in-depth.md +122 -122
  189. package/skills/systematic-debugging/find-polluter.sh +63 -63
  190. package/skills/systematic-debugging/root-cause-tracing.md +169 -169
  191. package/skills/systematic-debugging/test-academic.md +14 -14
  192. package/skills/systematic-debugging/test-pressure-1.md +58 -58
  193. package/skills/systematic-debugging/test-pressure-2.md +68 -68
  194. package/skills/systematic-debugging/test-pressure-3.md +69 -69
  195. package/skills/using-git-worktrees/SKILL.md +215 -215
  196. package/skills/verification-before-completion/SKILL.md +154 -154
  197. package/skills/webapp-testing/SKILL.md +19 -19
@@ -0,0 +1,224 @@
1
+ import { TASK_CONTRACT_DRAFT_SCHEMA_VERSION } from "../contract/constants.js";
2
+ import { taskKindSchema } from "../config-types.js";
3
+ import { assessPreparePaths, mergeForbiddenPaths, normalizePreparePath, dedupeStable, } from "./path-policy.js";
4
+ function trimNonEmpty(value) {
5
+ if (value === undefined)
6
+ return undefined;
7
+ const trimmed = value.trim();
8
+ return trimmed ? trimmed : undefined;
9
+ }
10
+ function cleanStringList(values) {
11
+ if (values === undefined)
12
+ return undefined;
13
+ return values.map((v) => v.trim()).filter(Boolean);
14
+ }
15
+ function resolveTaskKind(flags, baseDraft, existing) {
16
+ const candidate = flags.taskKind ?? baseDraft?.taskKind ?? existing?.taskKind ?? "standard";
17
+ const parsed = taskKindSchema.safeParse(candidate);
18
+ return parsed.success ? parsed.data : String(candidate);
19
+ }
20
+ function resolveTitle(taskId, flags, facts, baseDraft, existing) {
21
+ return (trimNonEmpty(flags.title) ??
22
+ trimNonEmpty(baseDraft?.title) ??
23
+ trimNonEmpty(facts?.title) ??
24
+ trimNonEmpty(existing?.title) ??
25
+ taskId);
26
+ }
27
+ function resolveObjective(flags, facts, baseDraft, title) {
28
+ return (trimNonEmpty(flags.objective) ??
29
+ trimNonEmpty(baseDraft?.requirement.objective) ??
30
+ trimNonEmpty(facts?.objective) ??
31
+ title);
32
+ }
33
+ function resolveStringArrayField(input) {
34
+ if (input.explicit !== undefined) {
35
+ return cleanStringList(input.explicit) ?? [];
36
+ }
37
+ if (input.base && input.base.length > 0)
38
+ return [...input.base];
39
+ if (input.facts && input.facts.length > 0)
40
+ return [...input.facts];
41
+ return [];
42
+ }
43
+ function resolveAcceptance(input) {
44
+ if (input.explicit !== undefined) {
45
+ return input.explicit
46
+ .map((item) => ({ id: item.id.trim(), text: item.text.trim() }))
47
+ .filter((item) => item.id && item.text);
48
+ }
49
+ if (input.base && input.base.length > 0)
50
+ return [...input.base];
51
+ if (input.facts && input.facts.length > 0)
52
+ return [...input.facts];
53
+ return [];
54
+ }
55
+ function resolveAllowedPaths(input) {
56
+ const source = input.flags.allowedPaths !== undefined
57
+ ? (cleanStringList(input.flags.allowedPaths) ?? [])
58
+ : input.baseDraft?.constraints.allowedPaths?.length
59
+ ? [...input.baseDraft.constraints.allowedPaths]
60
+ : input.existing?.allowedPaths?.length
61
+ ? [...input.existing.allowedPaths]
62
+ : [];
63
+ return source.map((raw) => {
64
+ const n = normalizePreparePath(raw);
65
+ return n.ok ? n.value : raw.trim();
66
+ });
67
+ }
68
+ function resolveAllowedRoots(input) {
69
+ const roots = input.flags.allowedRoots ?? input.baseDraft?.constraints.allowedRoots ?? input.existing?.allowedRoots;
70
+ return roots === undefined ? undefined : (cleanStringList(roots) ?? []).map((raw) => {
71
+ const normalized = normalizePreparePath(raw);
72
+ return normalized.ok ? normalized.value : raw.trim();
73
+ });
74
+ }
75
+ function resolveVerifyCommands(input) {
76
+ if (input.flags.verifyCommands !== undefined) {
77
+ return input.flags.verifyCommands.map((cmd) => ({
78
+ label: cmd.label.trim(),
79
+ command: cmd.command.trim(),
80
+ ...(cmd.timeoutMs !== undefined ? { timeoutMs: cmd.timeoutMs } : {}),
81
+ }));
82
+ }
83
+ if (input.baseDraft?.verification.commands?.length) {
84
+ return input.baseDraft.verification.commands.map((cmd) => ({
85
+ label: cmd.label,
86
+ command: cmd.command,
87
+ ...(cmd.timeoutMs !== undefined ? { timeoutMs: cmd.timeoutMs } : {}),
88
+ }));
89
+ }
90
+ if (input.existing?.verifyCommands?.length) {
91
+ return input.existing.verifyCommands.map((cmd) => ({
92
+ label: cmd.label,
93
+ command: cmd.command,
94
+ ...(cmd.timeoutMs !== undefined ? { timeoutMs: cmd.timeoutMs } : {}),
95
+ }));
96
+ }
97
+ return [];
98
+ }
99
+ /**
100
+ * Build a thin TaskContractDraftV1 from draft base / PRD facts / flags / task config.
101
+ * Engineering fields never come from PRD facts.
102
+ */
103
+ export function buildPrepareDraft(input) {
104
+ const { taskId, existingTaskConfig, baseDraft, facts, flags, references } = input;
105
+ const title = resolveTitle(taskId, flags, facts, baseDraft, existingTaskConfig);
106
+ const taskKind = resolveTaskKind(flags, baseDraft, existingTaskConfig);
107
+ const featureId = trimNonEmpty(flags.featureId) ??
108
+ trimNonEmpty(baseDraft?.featureId) ??
109
+ trimNonEmpty(existingTaskConfig?.featureId) ??
110
+ undefined;
111
+ const objective = resolveObjective(flags, facts, baseDraft, title);
112
+ const scope = resolveStringArrayField({
113
+ explicit: flags.scope,
114
+ base: baseDraft?.requirement.scope,
115
+ facts: facts?.scope,
116
+ });
117
+ const nonGoals = resolveStringArrayField({
118
+ explicit: flags.nonGoals,
119
+ base: baseDraft?.requirement.nonGoals,
120
+ facts: facts?.nonGoals,
121
+ });
122
+ const acceptanceCriteria = resolveAcceptance({
123
+ explicit: flags.acceptanceCriteria,
124
+ base: baseDraft?.requirement.acceptanceCriteria,
125
+ facts: facts?.acceptanceCriteria,
126
+ });
127
+ const allowedPaths = resolveAllowedPaths({
128
+ flags,
129
+ baseDraft,
130
+ existing: existingTaskConfig,
131
+ });
132
+ const allowedRoots = resolveAllowedRoots({ flags, baseDraft, existing: existingTaskConfig });
133
+ // D12: protected defaults always merge unless explicitly disabled.
134
+ // Explicit --forbidden-path appends; does not replace defaults.
135
+ const finalForbidden = mergeForbiddenPaths({
136
+ existing: baseDraft?.constraints.forbiddenPaths ??
137
+ existingTaskConfig?.forbiddenPaths ??
138
+ [],
139
+ explicit: flags.forbiddenPaths,
140
+ noDefaultForbiddenPaths: flags.noDefaultForbiddenPaths,
141
+ });
142
+ const invariants = resolveStringArrayField({
143
+ explicit: flags.invariants,
144
+ base: baseDraft?.constraints.invariants,
145
+ facts: facts?.invariants,
146
+ });
147
+ const openQuestions = resolveStringArrayField({
148
+ explicit: flags.openQuestions,
149
+ base: baseDraft?.openQuestions,
150
+ facts: facts?.openQuestions,
151
+ });
152
+ const assumptions = resolveStringArrayField({
153
+ explicit: flags.assumptions,
154
+ base: baseDraft?.assumptions,
155
+ facts: facts?.assumptions,
156
+ });
157
+ const verifyCommands = resolveVerifyCommands({
158
+ flags,
159
+ baseDraft,
160
+ existing: existingTaskConfig,
161
+ });
162
+ const pathAssessment = assessPreparePaths({
163
+ allowedPaths,
164
+ forbiddenPaths: finalForbidden,
165
+ noDefaultForbiddenPaths: flags.noDefaultForbiddenPaths,
166
+ });
167
+ const draft = {
168
+ schemaVersion: TASK_CONTRACT_DRAFT_SCHEMA_VERSION,
169
+ taskId,
170
+ title,
171
+ taskKind: taskKind,
172
+ requirement: {
173
+ objective,
174
+ scope,
175
+ nonGoals,
176
+ acceptanceCriteria,
177
+ },
178
+ constraints: {
179
+ invariants,
180
+ allowedPaths: pathAssessment.allowed.length > 0
181
+ ? pathAssessment.allowed
182
+ : dedupeStable(allowedPaths),
183
+ forbiddenPaths: pathAssessment.forbidden.length > 0
184
+ ? pathAssessment.forbidden
185
+ : dedupeStable(finalForbidden),
186
+ ...(allowedRoots !== undefined ? { allowedRoots: dedupeStable(allowedRoots) } : {}),
187
+ },
188
+ verification: {
189
+ commands: verifyCommands
190
+ .filter((cmd) => cmd.label && cmd.command)
191
+ .map((cmd) => ({
192
+ label: cmd.label,
193
+ command: cmd.command,
194
+ ...(cmd.timeoutMs !== undefined ? { timeoutMs: cmd.timeoutMs } : {}),
195
+ })),
196
+ },
197
+ };
198
+ if (featureId)
199
+ draft.featureId = featureId;
200
+ if (references && references.length > 0) {
201
+ draft.references = references.map((ref) => ({
202
+ role: ref.role,
203
+ ref: ref.ref,
204
+ }));
205
+ }
206
+ else if (baseDraft?.references?.length) {
207
+ draft.references = [...baseDraft.references];
208
+ }
209
+ if (openQuestions.length > 0)
210
+ draft.openQuestions = openQuestions;
211
+ if (assumptions.length > 0)
212
+ draft.assumptions = assumptions;
213
+ if (baseDraft?.sourceResolutions?.length) {
214
+ draft.sourceResolutions = [...baseDraft.sourceResolutions];
215
+ }
216
+ if (baseDraft?.hardConstraints?.length && invariants.length === 0) {
217
+ draft.hardConstraints = [...baseDraft.hardConstraints];
218
+ }
219
+ return {
220
+ draft,
221
+ pathAssessment,
222
+ acceptanceConflicts: facts?.acceptanceConflicts ?? [],
223
+ };
224
+ }
@@ -0,0 +1,195 @@
1
+ const FRESH_SOURCE_MISSING_CODES = new Set([
2
+ "MISSING_REQUIREMENT",
3
+ "MISSING_CONSTRAINTS",
4
+ ]);
5
+ export function isFreshLegacyUnversioned(state, sourceFilesPresent) {
6
+ if (state.effectiveStatus !== "legacy-unversioned")
7
+ return false;
8
+ if (state.observationErrors.some((e) => e.code === "NOT_FOUND"))
9
+ return false;
10
+ // Fresh new-task: both source files missing/empty. Any non-empty source is dirty.
11
+ if (sourceFilesPresent &&
12
+ (sourceFilesPresent.requirement || sourceFilesPresent.constraints)) {
13
+ return false;
14
+ }
15
+ const codes = state.observationErrors.map((e) => e.code);
16
+ // Allow empty errors only when caller already proved both files are empty.
17
+ if (codes.length === 0) {
18
+ return Boolean(sourceFilesPresent);
19
+ }
20
+ return codes.every((code) => FRESH_SOURCE_MISSING_CODES.has(code));
21
+ }
22
+ export function observationBlocksPrepare(state) {
23
+ if (state.observationErrors.some((e) => e.code === "NOT_FOUND")) {
24
+ return {
25
+ code: "TASK_NOT_FOUND",
26
+ level: "blocking",
27
+ message: "task not found",
28
+ };
29
+ }
30
+ if (state.effectiveStatus === "transaction-incomplete") {
31
+ return {
32
+ code: "TRANSACTION_INCOMPLETE",
33
+ level: "blocking",
34
+ message: "unfinished contract transaction; run task contract recover before prepare",
35
+ };
36
+ }
37
+ const disallowed = state.observationErrors.filter((e) => !FRESH_SOURCE_MISSING_CODES.has(e.code));
38
+ // EXTERNALLY_MODIFIED alone is the dirty-source signal handled by C6.
39
+ // Any other observation code (e.g. MANIFEST_MISSING, OBSERVE_FAILED,
40
+ // INVALID_CONTRACT_JSON) is independently blocking; force cannot bypass it.
41
+ const hard = disallowed.filter((e) => e.code !== "EXTERNALLY_MODIFIED");
42
+ if (hard.length > 0) {
43
+ return {
44
+ code: "OBSERVATION_ERROR",
45
+ level: "blocking",
46
+ message: hard.map((e) => e.message).join("; "),
47
+ };
48
+ }
49
+ return null;
50
+ }
51
+ export function listPrepareGaps(input) {
52
+ const gaps = [];
53
+ const risks = [];
54
+ const { draft } = input;
55
+ const obs = observationBlocksPrepare(input.state);
56
+ if (obs)
57
+ gaps.push(obs);
58
+ if (!draft.requirement.objective?.trim()) {
59
+ gaps.push({
60
+ code: "EMPTY_OBJECTIVE",
61
+ level: "blocking",
62
+ field: "objective",
63
+ message: "requirement.objective is required",
64
+ });
65
+ }
66
+ if (draft.requirement.acceptanceCriteria.length === 0) {
67
+ gaps.push({
68
+ code: "EMPTY_ACCEPTANCE",
69
+ level: "blocking",
70
+ field: "acceptanceCriteria",
71
+ message: "at least one acceptance criterion is required",
72
+ });
73
+ }
74
+ if (input.acceptanceConflicts && input.acceptanceConflicts.length > 0) {
75
+ for (const conflict of input.acceptanceConflicts) {
76
+ gaps.push({
77
+ code: "ACCEPTANCE_ID_CONFLICT",
78
+ level: "blocking",
79
+ field: "acceptanceCriteria",
80
+ message: `acceptance criterion id ${conflict.id} has conflicting texts`,
81
+ });
82
+ }
83
+ }
84
+ if (draft.constraints.allowedPaths.length === 0 && !(draft.taskKind === "frontend-implementation" && (draft.constraints.allowedRoots?.length ?? 0) > 0)) {
85
+ gaps.push({
86
+ code: "EMPTY_ALLOWED_PATHS",
87
+ level: "blocking",
88
+ field: "allowedPaths",
89
+ message: "allowedPaths must be non-empty before apply",
90
+ });
91
+ }
92
+ if (!draft.taskKind?.trim()) {
93
+ gaps.push({
94
+ code: "EMPTY_TASK_KIND",
95
+ level: "blocking",
96
+ field: "taskKind",
97
+ message: "taskKind is required",
98
+ });
99
+ }
100
+ if (draft.taskKind === "knowledge-sync" && !draft.featureId) {
101
+ gaps.push({
102
+ code: "MISSING_FEATURE_ID",
103
+ level: "blocking",
104
+ field: "featureId",
105
+ message: "featureId is required when taskKind is knowledge-sync",
106
+ });
107
+ }
108
+ if (draft.requirement.scope.length === 0) {
109
+ gaps.push({
110
+ code: "EMPTY_SCOPE",
111
+ level: "warning",
112
+ field: "scope",
113
+ message: "scope is empty; projection will show (none)",
114
+ });
115
+ }
116
+ if (draft.requirement.nonGoals.length === 0) {
117
+ gaps.push({
118
+ code: "EMPTY_NON_GOALS",
119
+ level: "warning",
120
+ field: "nonGoals",
121
+ message: "nonGoals is empty; projection will show (none)",
122
+ });
123
+ }
124
+ if (draft.verification.commands.length === 0) {
125
+ gaps.push({
126
+ code: "EMPTY_VERIFY",
127
+ level: "warning",
128
+ field: "verifyCommands",
129
+ message: "verifyCommands is empty",
130
+ });
131
+ }
132
+ if (input.sourceIntegrity) {
133
+ for (const issue of input.sourceIntegrity.issues) {
134
+ if (issue.level === "blocking") {
135
+ gaps.push({
136
+ code: issue.code,
137
+ level: "blocking",
138
+ message: issue.message,
139
+ field: issue.path,
140
+ });
141
+ }
142
+ else {
143
+ gaps.push({
144
+ code: issue.code,
145
+ level: "warning",
146
+ message: issue.message,
147
+ field: issue.path,
148
+ });
149
+ }
150
+ }
151
+ }
152
+ if (input.pathAssessment) {
153
+ risks.push(...input.pathAssessment.blocking, ...input.pathAssessment.elevated, ...input.pathAssessment.warnings);
154
+ for (const risk of input.pathAssessment.blocking) {
155
+ gaps.push({
156
+ code: risk.code,
157
+ level: "blocking",
158
+ message: risk.message,
159
+ field: risk.path,
160
+ });
161
+ }
162
+ }
163
+ // Dirty source gate (C6 / D14)
164
+ const status = input.state.effectiveStatus;
165
+ const hasExistingSource = Boolean(input.sourceFilesPresent?.requirement ||
166
+ input.sourceFilesPresent?.constraints);
167
+ const hasDirtySource = status === "externally-modified" ||
168
+ (status === "legacy-unversioned" && hasExistingSource);
169
+ if (hasDirtySource && !input.forceOverwriteSource) {
170
+ gaps.push({
171
+ code: "DIRTY_SOURCE",
172
+ level: "blocking",
173
+ message: "existing non-empty or externally-modified source refuses --apply; use show/diff/adopt/recover or --force-overwrite-source",
174
+ });
175
+ risks.push({
176
+ code: "DIRTY_SOURCE",
177
+ level: "blocking",
178
+ message: "source is dirty/externally-modified; prepare will not overwrite without --force-overwrite-source",
179
+ });
180
+ }
181
+ if (!input.hasImportedPrd) {
182
+ gaps.push({
183
+ code: "NO_IMPORTED_PRD",
184
+ level: "warning",
185
+ message: "no imported PRD in use; prefer detailed PRD → import-prd → prepare (escape hatch only)",
186
+ });
187
+ }
188
+ return { gaps, risks };
189
+ }
190
+ export function hasBlockingGaps(gaps) {
191
+ return gaps.some((gap) => gap.level === "blocking");
192
+ }
193
+ export function hasBlockingRisks(risks) {
194
+ return risks.some((risk) => risk.level === "blocking");
195
+ }
@@ -0,0 +1,7 @@
1
+ export * from "./types.js";
2
+ export * from "./path-policy.js";
3
+ export * from "./parse-intent.js";
4
+ export * from "./reference-integrity.js";
5
+ export * from "./completeness.js";
6
+ export * from "./build-draft.js";
7
+ export * from "./prepare.js";