@surlymochan/evalid 0.0.0-stage → 0.3.1

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 (192) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/LICENSE +21 -0
  3. package/README.md +315 -2
  4. package/RELEASE_NOTES.md +102 -0
  5. package/atoms/registry.json +758 -0
  6. package/bin/xflow.js +7208 -0
  7. package/docs/adoption/README.md +128 -0
  8. package/docs/adoption/openflow-release-hardening.md +61 -0
  9. package/docs/benchmark-openflow-vs-gstack-superpowers-openspec-2026-05-25.md +81 -0
  10. package/docs/compatibility.md +52 -0
  11. package/docs/competitive-benchmark.md +108 -0
  12. package/docs/corps-operator-guide.md +75 -0
  13. package/docs/demo-proof.md +126 -0
  14. package/docs/examples-gallery.md +115 -0
  15. package/docs/fixtures/tracker-item.json +7 -0
  16. package/docs/goal-vs-codex.md +129 -0
  17. package/docs/gstack-comparison.md +88 -0
  18. package/docs/install-upgrade.md +133 -0
  19. package/docs/integrations.md +49 -0
  20. package/docs/launch-demo.md +123 -0
  21. package/docs/launch-dossier.md +180 -0
  22. package/docs/methodology.md +149 -0
  23. package/docs/npm-publish-handoff.md +83 -0
  24. package/docs/openspec-migration.md +106 -0
  25. package/docs/public-benchmark.md +68 -0
  26. package/docs/public-release.md +145 -0
  27. package/docs/quality-assessment.md +139 -0
  28. package/docs/quickstart.md +156 -0
  29. package/docs/reviewer-guide.md +43 -0
  30. package/docs/spec-kit-benchmark.md +73 -0
  31. package/docs/super-assistant-comparison.md +106 -0
  32. package/docs/superpowers-comparison.md +82 -0
  33. package/docs/team-adoption.md +123 -0
  34. package/docs/tooling-matrix.md +91 -0
  35. package/docs/walkthrough.md +76 -0
  36. package/docs/workflow-manual.md +1125 -0
  37. package/package.json +83 -4
  38. package/prompts/pencil-draft.md +46 -0
  39. package/prompts/pencil-refine.md +46 -0
  40. package/prompts/phase-artifact.md +24 -0
  41. package/prompts/phase-execution.md +32 -0
  42. package/schemas/competitor-reconstruction-contract.schema.json +91 -0
  43. package/schemas/llm-design-review-aggregate.schema.json +83 -0
  44. package/schemas/visual-benchmark.schema.json +978 -0
  45. package/schemas/workflow.schema.json +100 -0
  46. package/scripts/run-tests.mjs +23 -0
  47. package/scripts/takein_digest.py +139 -0
  48. package/specs/README.md +118 -0
  49. package/specs/architecture.md +36 -0
  50. package/specs/product.md +33 -0
  51. package/specs/workflow.md +349 -0
  52. package/src/agent_team/atoms/codex-app-bridge.js +8 -0
  53. package/src/agent_team/atoms/design/competitor-reconstruction-review.js +44 -0
  54. package/src/agent_team/atoms/design/contract-freeze.js +8 -0
  55. package/src/agent_team/atoms/design/generation-contract.js +442 -0
  56. package/src/agent_team/atoms/design/reconstruction-pack.js +246 -0
  57. package/src/agent_team/atoms/design/reference-surface-lock.js +49 -0
  58. package/src/agent_team/atoms/patch-challenge.js +19 -0
  59. package/src/agent_team/atoms/pencil/accept.js +9 -0
  60. package/src/agent_team/atoms/pencil/draft.js +365 -0
  61. package/src/agent_team/atoms/pencil/refine.js +229 -0
  62. package/src/agent_team/atoms/phase-run-dispatch.js +8 -0
  63. package/src/agent_team/atoms/team-run.js +380 -0
  64. package/src/agent_team/atoms/visual/aesthetic-review.js +231 -0
  65. package/src/agent_team/atoms/visual/benchmark-repair-plan.js +291 -0
  66. package/src/agent_team/atoms/visual/benchmark.js +1652 -0
  67. package/src/agent_team/atoms/visual/design-selection.js +23 -0
  68. package/src/agent_team/atoms/visual/direction-synthesis.js +124 -0
  69. package/src/agent_team/atoms/visual/dom-rect-export.js +129 -0
  70. package/src/agent_team/atoms/visual/image-diff.js +186 -0
  71. package/src/agent_team/atoms/visual/layout-competition.js +8 -0
  72. package/src/agent_team/atoms/visual/page-evidence-capture.js +806 -0
  73. package/src/agent_team/atoms/visual/playwright-runtime.js +8 -0
  74. package/src/agent_team/atoms/visual/review-aggregate.js +87 -0
  75. package/src/agent_team/atoms/visual/review.js +77 -0
  76. package/src/agent_team/atoms/visual/visual-report.js +197 -0
  77. package/src/agent_team/atoms/visual/visual-token-export.js +417 -0
  78. package/src/agent_team/common.js +311 -0
  79. package/src/core/atoms/agent-invoke.js +684 -0
  80. package/src/core/atoms/event/ingest.js +30 -0
  81. package/src/core/atoms/experience/digest.js +55 -0
  82. package/src/core/atoms/gate/artifact-verify.js +83 -0
  83. package/src/core/atoms/gate/competitor-contract-validate.js +119 -0
  84. package/src/core/atoms/gate/evidence-collect.js +64 -0
  85. package/src/core/atoms/gate/payload-from-phase-run.js +78 -0
  86. package/src/core/atoms/gate/visual-benchmark-validate.js +183 -0
  87. package/src/core/atoms/gate/workbench.js +79 -0
  88. package/src/core/atoms/intake/classify.js +15 -0
  89. package/src/core/atoms/intake/design-skill-select.js +16 -0
  90. package/src/core/atoms/intake/preview-or-refine.js +19 -0
  91. package/src/core/atoms/intake/recommend.js +20 -0
  92. package/src/core/atoms/intake/research-same-category.js +19 -0
  93. package/src/core/atoms/intervention/create.js +30 -0
  94. package/src/core/atoms/memory/manage.js +27 -0
  95. package/src/core/atoms/memory/mount.js +13 -0
  96. package/src/core/atoms/mission/create.js +19 -0
  97. package/src/core/atoms/mission/list.js +14 -0
  98. package/src/core/atoms/mission/show.js +26 -0
  99. package/src/core/atoms/notify/checkpoint.js +46 -0
  100. package/src/core/atoms/phase-run/complete.js +42 -0
  101. package/src/core/atoms/phase-run/fail.js +40 -0
  102. package/src/core/atoms/phase-run/persist.js +39 -0
  103. package/src/core/atoms/phase-run/start.js +30 -0
  104. package/src/core/atoms/plugin/run.js +76 -0
  105. package/src/core/atoms/policy/gate.js +90 -0
  106. package/src/core/atoms/policy/resolve.js +11 -0
  107. package/src/core/atoms/storage/sync.js +57 -0
  108. package/src/core/atoms/workflow/advance.js +25 -0
  109. package/src/core/atoms/workflow/guard.js +127 -0
  110. package/src/core/atoms/workflow/state-load.js +37 -0
  111. package/src/core/bindings.js +37 -0
  112. package/src/core/change-artifacts.js +134 -0
  113. package/src/core/competitive-score.js +265 -0
  114. package/src/core/concurrency-policy.js +35 -0
  115. package/src/core/execution-log.js +108 -0
  116. package/src/core/intake-recommendation.js +134 -0
  117. package/src/core/memory-state.js +62 -0
  118. package/src/core/mission-state.js +780 -0
  119. package/src/core/pending-gates.js +66 -0
  120. package/src/core/phase-cost.js +116 -0
  121. package/src/core/phase-run-read.js +27 -0
  122. package/src/core/policy-overlay.js +180 -0
  123. package/src/core/state-store.js +201 -0
  124. package/src/core/workflow-executor.js +998 -0
  125. package/src/core/workflow-integrity.js +111 -0
  126. package/src/core/workflow-loader.js +163 -0
  127. package/src/core/workflow-rerun.js +84 -0
  128. package/src/core/workflow-state.js +119 -0
  129. package/src/server.js +1307 -0
  130. package/workflows/corps.yaml +479 -0
  131. package/workflows/yolo.yaml +233 -0
  132. package/xflow/README.md +76 -0
  133. package/xflow/aha/SKILL.md +82 -0
  134. package/xflow/aha/scripts/append_aha.py +50 -0
  135. package/xflow/archive/scripts/archive_change.py +338 -0
  136. package/xflow/archive/templates/merge-design.md +54 -0
  137. package/xflow/archive/templates/merge-workflow.md +26 -0
  138. package/xflow/atoms/__init__.py +6 -0
  139. package/xflow/atoms/a1_issue_create_or_patch.py +70 -0
  140. package/xflow/atoms/a2_branch_create_and_link.py +42 -0
  141. package/xflow/atoms/a3_repo_doctor.py +11 -0
  142. package/xflow/atoms/a4_project_set_status.py +11 -0
  143. package/xflow/atoms/a5_archive_commit_push_close.py +11 -0
  144. package/xflow/atoms/a6_pr_create.py +72 -0
  145. package/xflow/atoms/b1_change_scaffold.py +11 -0
  146. package/xflow/atoms/b2_status_read.py +16 -0
  147. package/xflow/atoms/b2b_status_write.py +20 -0
  148. package/xflow/atoms/b3_status_transition.py +27 -0
  149. package/xflow/atoms/b4_handoff_scaffold_or_refresh.py +11 -0
  150. package/xflow/atoms/b5_aha_append.py +11 -0
  151. package/xflow/atoms/b6_takein_digest.py +11 -0
  152. package/xflow/atoms/b6b_lookback_digest.py +11 -0
  153. package/xflow/atoms/b7_change_mission_bind.py +43 -0
  154. package/xflow/atoms/c1_mem.py +54 -0
  155. package/xflow/atoms/e6_gate_local_precheck.py +23 -0
  156. package/xflow/atoms/h1_design_lite_gate.py +74 -0
  157. package/xflow/atoms/i6a_tdd_run.py +104 -0
  158. package/xflow/atoms/i6b_tdd_proof_validate.py +165 -0
  159. package/xflow/atoms/i6c_tdd_quality_review.py +357 -0
  160. package/xflow/atoms/j1_tests_run.py +129 -0
  161. package/xflow/atoms/j4a_spec_consistency_structural.py +105 -0
  162. package/xflow/atoms/j4b_spec_delta_review.py +316 -0
  163. package/xflow/atoms/j4c_openspec_migration_map.py +121 -0
  164. package/xflow/atoms/j4d_spec_clarify_gate.py +373 -0
  165. package/xflow/atoms/j6_cr_review.py +443 -0
  166. package/xflow/atoms/k1_artifacts_complete_check.py +39 -0
  167. package/xflow/atoms/k2_merge_snippets_apply.py +11 -0
  168. package/xflow/atoms/k3_handoff_refresh.py +14 -0
  169. package/xflow/atoms/k4_mem_lesson_persist.py +65 -0
  170. package/xflow/atoms/k5_archive_publish.py +21 -0
  171. package/xflow/atoms/k6_aha_merge.py +42 -0
  172. package/xflow/atoms/tdd_lib.py +747 -0
  173. package/xflow/corps/SKILL.md +109 -0
  174. package/xflow/exec/scripts/set_issue_in_progress.py +159 -0
  175. package/xflow/goal/SKILL.md +150 -0
  176. package/xflow/handoff/SKILL.md +95 -0
  177. package/xflow/handoff/scripts/scaffold_handoff.py +304 -0
  178. package/xflow/lookback/SKILL.md +66 -0
  179. package/xflow/lookback/scripts/lookback_digest.py +46 -0
  180. package/xflow/openissue/scripts/open_issue_flow.py +438 -0
  181. package/xflow/plan/SKILL.md +38 -0
  182. package/xflow/ralph/SKILL.md +123 -0
  183. package/xflow/scripts/check_installed_xflow_skill_sync.sh +44 -0
  184. package/xflow/scripts/common.py +480 -0
  185. package/xflow/scripts/doctor.py +278 -0
  186. package/xflow/scripts/sync_installed_xflow_skill.sh +134 -0
  187. package/xflow/takein/SKILL.md +81 -0
  188. package/xflow/takein/scripts/takein_digest.py +88 -0
  189. package/xflow/yolo/SKILL.md +66 -0
  190. package/xflow/yolo/scripts/scaffold_specs_change.py +673 -0
  191. package/xflow/yolo/scripts/yolo_gate.py +312 -0
  192. package/xflow/yolo/templates/DESIGN.md +179 -0
@@ -0,0 +1,8 @@
1
+ import { synthesizePhaseArtifact } from '../common.js';
2
+
3
+ export async function phaseRunDispatch(input = {}, context = {}) {
4
+ return synthesizePhaseArtifact(input, context, {
5
+ phase: input.phase || context.phase?.id || 'execute',
6
+ summary: 'Phase-run dispatch completed and execution evidence was recorded.',
7
+ });
8
+ }
@@ -0,0 +1,380 @@
1
+ import { readFileSync, existsSync } from 'node:fs';
2
+
3
+ import { readJsonFile, resolveChangePath, writeJsonFile, writeTextFile } from '../../core/change-artifacts.js';
4
+ import { synthesizePhaseArtifact } from '../common.js';
5
+
6
+ function readText(path, fallback = '') {
7
+ if (!existsSync(path)) return fallback;
8
+ return readFileSync(path, 'utf8');
9
+ }
10
+
11
+ function loadCorpsInput(projectRoot, changeId) {
12
+ return readJsonFile(resolveChangePath(projectRoot, changeId, 'corps-input.json'), {}) || {};
13
+ }
14
+
15
+ function buildDirectionOptions(contract = {}) {
16
+ const modules = Array.isArray(contract.required_modules) ? contract.required_modules : [];
17
+ const surface = contract.primary_reference_surface || 'locked primary surface';
18
+ return [
19
+ {
20
+ id: 'shell-first-reference-copy',
21
+ label: 'Shell-first reference copy',
22
+ strengths: [
23
+ 'Fastest path to visual fidelity on the locked desktop surface.',
24
+ `Keeps ${surface} proportionally stable before density polish.`,
25
+ ],
26
+ risks: [
27
+ 'Can under-specify deeper scheduling and detail-panel states if left unchecked.',
28
+ ],
29
+ },
30
+ {
31
+ id: 'journey-first-ia-lock',
32
+ label: 'Journey-first IA lock',
33
+ strengths: [
34
+ 'Preserves declared business invariants around primary journeys, state changes, and detail editing.',
35
+ 'Keeps module coverage aligned with the named competitor model.',
36
+ ],
37
+ risks: [
38
+ 'Can drift visually if the shell is not re-anchored to the primary reference surface.',
39
+ ],
40
+ },
41
+ {
42
+ id: 'component-token-copy',
43
+ label: 'Component/token copy',
44
+ strengths: [
45
+ 'Improves density, control language, and polish across shared components.',
46
+ `Maps well to required modules: ${modules.join(', ') || 'unspecified modules'}.`,
47
+ ],
48
+ risks: [
49
+ 'Without a strict shell baseline it can still look inspired-by rather than copied.',
50
+ ],
51
+ },
52
+ ];
53
+ }
54
+
55
+ function buildRiskEntries(contract = {}) {
56
+ const invariants = Array.isArray(contract.business_logic_invariants) ? contract.business_logic_invariants : [];
57
+ const primarySurface = contract.primary_reference_surface || 'the locked primary reference surface';
58
+ return [
59
+ {
60
+ id: 'reference-drift',
61
+ severity: 'high',
62
+ mitigation: `Lock ${primarySurface} and reject mixed visual direction before implementation.`,
63
+ },
64
+ {
65
+ id: 'domain-invariant-break',
66
+ severity: 'high',
67
+ mitigation: `Keep declared domain behavior explicit in state and tests. Invariants: ${invariants.join(' | ') || 'none declared'}`,
68
+ },
69
+ {
70
+ id: 'density-gap',
71
+ severity: 'medium',
72
+ mitigation: 'Treat task row height, control sizing, and right-panel density as reference-copied constraints rather than local design choices.',
73
+ },
74
+ ];
75
+ }
76
+
77
+ function buildClarifyPayload(contract = {}) {
78
+ return {
79
+ assumptions: [
80
+ 'This change targets the platform and viewport class declared by the contract.',
81
+ 'Visual success is defined by the selected design mode and reference evidence, not local taste alone.',
82
+ ],
83
+ decisions: [
84
+ `Primary competitor product: ${contract.competitor_product || 'unspecified competitor'}`,
85
+ `Primary reference surface: ${contract.primary_reference_surface || 'unspecified primary surface'}`,
86
+ ],
87
+ open_questions: [],
88
+ resolved_scope: {
89
+ target_surfaces: contract.target_surfaces || [],
90
+ primary_journeys: contract.primary_journeys || [],
91
+ required_modules: contract.required_modules || [],
92
+ },
93
+ };
94
+ }
95
+
96
+ function proposalMarkdown(contract = {}) {
97
+ const requiredModules = (contract.required_modules || []).map((entry) => `- ${entry}`).join('\n') || '- (none)';
98
+ const journeys = (contract.primary_journeys || []).map((entry) => `- ${entry}`).join('\n') || '- (none)';
99
+ const invariants = (contract.business_logic_invariants || []).map((entry) => `- ${entry}`).join('\n') || '- (none)';
100
+ return [
101
+ '# Proposal',
102
+ '',
103
+ '## Objective',
104
+ '',
105
+ `Deliver a direct reconstruction of ${contract.competitor_product || 'the named competitor'} on the locked primary surface \`${contract.primary_reference_surface || 'unspecified'}\`.`,
106
+ '',
107
+ '## Required Modules',
108
+ '',
109
+ requiredModules,
110
+ '',
111
+ '## Primary Journeys',
112
+ '',
113
+ journeys,
114
+ '',
115
+ '## Business Logic Invariants',
116
+ '',
117
+ invariants,
118
+ '',
119
+ '## Acceptance Intent',
120
+ '',
121
+ '- The result should read as a competitor copy, not an inspiration piece.',
122
+ '- The shell, density, repeated-component rhythm, primary work surface, and detail editing must stay aligned with the frozen reference.',
123
+ '',
124
+ ].join('\n');
125
+ }
126
+
127
+ function buildUxDesignBrief(contract = {}) {
128
+ const states = Array.isArray(contract.required_states) && contract.required_states.length > 0
129
+ ? contract.required_states
130
+ : Array.isArray(contract.primary_journeys) && contract.primary_journeys.length > 0
131
+ ? contract.primary_journeys.map((journey) => `${journey}_state`)
132
+ : ['primary_ready', 'focused_item', 'empty_or_loading', 'error_or_recovery'];
133
+ return {
134
+ layout: {
135
+ shell: contract.primary_reference_surface || 'desktop workbench',
136
+ primary_reference_surface: contract.primary_reference_surface || null,
137
+ required_modules: contract.required_modules || [],
138
+ },
139
+ flows: contract.primary_journeys || [],
140
+ states,
141
+ interaction_constraints: [
142
+ 'Primary domain objects remain visible until the user explicitly changes their state.',
143
+ 'The main work surface stays visible during key decisions instead of hiding context behind generic forms.',
144
+ 'Secondary panels must feel like part of the same product surface, not pasted-on admin UI.',
145
+ ],
146
+ visual_constraints: {
147
+ competitor_product: contract.competitor_product || null,
148
+ reference_mode: contract.competitor_product ? 'reference_matched' : 'high_aesthetic_native',
149
+ density_target: contract.density_target || 'reference_matched_product_density',
150
+ },
151
+ };
152
+ }
153
+
154
+ function buildPlanJson(contract = {}, changeId = 'change') {
155
+ const workspace = contract.workspace || `specs/changes/${changeId}/prototype`;
156
+ return {
157
+ execution_plan: [
158
+ {
159
+ step: 'Build the product shell to match the locked reference surface proportions and hierarchy.',
160
+ owner: 'workflow',
161
+ outputs: [`${workspace}/index.html`, `${workspace}/styles.css`],
162
+ },
163
+ {
164
+ step: 'Tighten repeated component density, state affordances, and interaction rhythm to match the reference or design standard.',
165
+ owner: 'workflow',
166
+ outputs: [`${workspace}/app.js`],
167
+ },
168
+ {
169
+ step: 'Recheck benchmark evidence against the primary reference surface and repair any remaining drift.',
170
+ owner: 'workflow',
171
+ outputs: ['specs/changes/${change_id}/visual_benchmark.json'],
172
+ },
173
+ ],
174
+ verification: {
175
+ red: contract.tdd_red_command || null,
176
+ green: contract.tdd_green_command || null,
177
+ },
178
+ };
179
+ }
180
+
181
+ function writeStructuredPlanMarkdown(projectRoot, changeId, contract = {}) {
182
+ const plan = buildPlanJson(contract, changeId);
183
+ const workspace = contract.workspace || `specs/changes/${changeId}/prototype`;
184
+ const verificationCommands = [
185
+ contract.tdd_green_command || 'npm test',
186
+ ];
187
+ const modules = (contract.required_modules || []).map((entry) => `- ${entry}`).join('\n') || '- Product shell';
188
+ const journeys = (contract.primary_journeys || []).map((entry) => `- ${entry}`).join('\n') || '- Primary journey remains visible and testable';
189
+ const invariants = (contract.business_logic_invariants || []).map((entry) => `- ${entry}`).join('\n') || '- Preserve the accepted product contract';
190
+ const executionSteps = plan.execution_plan.map((step, index) => [
191
+ `${index + 1}. ${step.step}`,
192
+ ` - Owner: ${step.owner}`,
193
+ ` - Outputs: ${step.outputs.join(', ')}`,
194
+ ].join('\n')).join('\n');
195
+ writeTextFile(resolveChangePath(projectRoot, changeId, 'plan.md'), [
196
+ '# Plan',
197
+ '',
198
+ '## Target Outcome',
199
+ '',
200
+ `Build the accepted product surface in \`${workspace}\` and keep it aligned with the frozen corps contract.`,
201
+ '',
202
+ '## Product Contract',
203
+ '',
204
+ '### Required Modules',
205
+ '',
206
+ modules,
207
+ '',
208
+ '### Primary Journeys',
209
+ '',
210
+ journeys,
211
+ '',
212
+ '### Business Logic Invariants',
213
+ '',
214
+ invariants,
215
+ '',
216
+ '## Execution Plan',
217
+ '',
218
+ executionSteps,
219
+ '',
220
+ '## Verification',
221
+ '',
222
+ '```bash',
223
+ ...verificationCommands,
224
+ '```',
225
+ '',
226
+ '## Main Risk',
227
+ '',
228
+ '- Execution drift: the implementation must preserve the product contract, not merely write phase artifacts.',
229
+ '',
230
+ ].join('\n'));
231
+ }
232
+
233
+ function buildStructuredPhaseSpec(phase, contract = {}, projectRoot, changeId) {
234
+ const planText = readText(resolveChangePath(projectRoot, changeId, 'plan.md'));
235
+ const findingsText = readText(resolveChangePath(projectRoot, changeId, 'findings.md'));
236
+ const proposalText = readText(resolveChangePath(projectRoot, changeId, 'proposal.md'));
237
+ const referenceScenarios = Array.isArray(contract.reference_scenarios_json) ? contract.reference_scenarios_json : [];
238
+
239
+ switch (phase) {
240
+ case 'explore':
241
+ return {
242
+ summary: 'Explore locked the same-category evidence, product invariants, and primary reference surface before decomposition.',
243
+ findings: [
244
+ { summary: `Primary reference surface stays locked to ${contract.primary_reference_surface || 'the declared desktop surface'}.` },
245
+ { summary: `Required modules remain constrained to ${Array.isArray(contract.required_modules) && contract.required_modules.length > 0 ? contract.required_modules.join(', ') : 'the declared module set'}.` },
246
+ { summary: 'Inbox-first capture and explicit scheduling stay frozen as non-negotiable business logic.' },
247
+ ],
248
+ payload: {
249
+ competitor_product: contract.competitor_product || null,
250
+ primary_reference_surface: contract.primary_reference_surface || null,
251
+ target_surfaces: contract.target_surfaces || [],
252
+ required_modules: contract.required_modules || [],
253
+ primary_journeys: contract.primary_journeys || [],
254
+ business_logic_invariants: contract.business_logic_invariants || [],
255
+ },
256
+ };
257
+ case 'brainstorm':
258
+ return {
259
+ summary: 'Brainstorm converged on a shell-first product reconstruction direction with explicit downstream tradeoffs.',
260
+ findings: [
261
+ { summary: 'A shell-first copy path is the safest way to reduce competitor drift early.' },
262
+ { summary: 'Journey-first logic is still necessary to preserve the declared business invariants.' },
263
+ ],
264
+ payload: {
265
+ directions: buildDirectionOptions(contract),
266
+ recommended_direction: 'shell-first-reference-copy',
267
+ recommendation_rationale: 'Pixel-level fidelity depends on freezing the three-column desk shell before component polish or secondary states.',
268
+ reference_context: referenceScenarios.map((scenario) => scenario.id),
269
+ sources_used: {
270
+ plan_present: Boolean(planText.trim()),
271
+ findings_present: Boolean(findingsText.trim()),
272
+ },
273
+ },
274
+ };
275
+ case 'risk_review':
276
+ return {
277
+ summary: 'Risk review captured the main failure modes for competitor-led UI reconstruction before implementation.',
278
+ findings: buildRiskEntries(contract).map((entry) => ({ summary: `${entry.id}: ${entry.mitigation}` })),
279
+ payload: {
280
+ risks: buildRiskEntries(contract),
281
+ },
282
+ };
283
+ case 'clarify':
284
+ return {
285
+ summary: 'Clarify locked the product mode as competitor-led desktop reconstruction with no open blocker questions.',
286
+ findings: [
287
+ { summary: 'The primary surface and product mode are frozen before implementation.' },
288
+ { summary: 'No unresolved scope ambiguity remains for primary state transitions, work surface, or detail-panel behavior.' },
289
+ ],
290
+ payload: buildClarifyPayload(contract),
291
+ };
292
+ case 'proposal':
293
+ return {
294
+ summary: 'Proposal translated the competitor contract into a concrete reconstruction brief.',
295
+ findings: [
296
+ { summary: 'Proposal now names modules, journeys, and invariants as execution constraints.' },
297
+ ],
298
+ payload: {
299
+ objective: `Directly reconstruct ${contract.competitor_product || 'the named competitor'} on ${contract.primary_reference_surface || 'the locked surface'}.`,
300
+ required_modules: contract.required_modules || [],
301
+ primary_journeys: contract.primary_journeys || [],
302
+ business_logic_invariants: contract.business_logic_invariants || [],
303
+ reference_scenarios: referenceScenarios.map((scenario) => scenario.id),
304
+ proposal_markdown_present: Boolean(proposalText.trim()),
305
+ },
306
+ sideEffects() {
307
+ writeTextFile(resolveChangePath(projectRoot, changeId, 'proposal.md'), proposalMarkdown(contract));
308
+ writeJsonFile(resolveChangePath(projectRoot, changeId, 'proposal.json'), {
309
+ version: 1,
310
+ proposal: {
311
+ competitor_product: contract.competitor_product || null,
312
+ primary_reference_surface: contract.primary_reference_surface || null,
313
+ target_surfaces: contract.target_surfaces || [],
314
+ required_modules: contract.required_modules || [],
315
+ primary_journeys: contract.primary_journeys || [],
316
+ business_logic_invariants: contract.business_logic_invariants || [],
317
+ reference_scenarios: referenceScenarios,
318
+ },
319
+ });
320
+ },
321
+ };
322
+ case 'ux_design_brief':
323
+ return {
324
+ summary: 'UX design brief locked the workbench shell, core states, and copy-not-reinterpret visual constraints.',
325
+ findings: [
326
+ { summary: 'UX brief keeps the primary work surface explicit and visible inside the central workbench.' },
327
+ { summary: 'Secondary detail/editing surfaces are constrained to feel like part of the same product system.' },
328
+ ],
329
+ payload: buildUxDesignBrief(contract),
330
+ sideEffects() {
331
+ writeJsonFile(resolveChangePath(projectRoot, changeId, 'ux_design_brief.json'), {
332
+ version: 1,
333
+ ux_design_brief: buildUxDesignBrief(contract),
334
+ });
335
+ },
336
+ };
337
+ case 'plan':
338
+ return {
339
+ summary: 'Plan converted the competitor contract into an implementation-ready execution sequence.',
340
+ findings: [
341
+ { summary: 'Execution is ordered around shell fidelity first, then dense module alignment, then benchmark recheck.' },
342
+ ],
343
+ payload: buildPlanJson(contract, changeId),
344
+ sideEffects() {
345
+ writeJsonFile(resolveChangePath(projectRoot, changeId, 'plan.json'), {
346
+ version: 1,
347
+ ...buildPlanJson(contract, changeId),
348
+ });
349
+ writeStructuredPlanMarkdown(projectRoot, changeId, contract);
350
+ },
351
+ };
352
+ default:
353
+ return {
354
+ summary: `${phase} completed through the heavy structured team-run bridge.`,
355
+ findings: [],
356
+ payload: {},
357
+ };
358
+ }
359
+ }
360
+
361
+ export async function teamRun(input = {}, context = {}) {
362
+ const phase = input.phase || context.phase?.id || null;
363
+ const projectRoot = context.projectRoot || process.cwd();
364
+ const changeId = context.changeId || input.change_id || null;
365
+ const contract = changeId ? loadCorpsInput(projectRoot, changeId) : {};
366
+ const spec = buildStructuredPhaseSpec(phase, contract, projectRoot, changeId);
367
+
368
+ const result = await synthesizePhaseArtifact(input, context, {
369
+ phase,
370
+ summary: spec.summary,
371
+ findings: spec.findings,
372
+ extraEnvelope: spec.payload,
373
+ });
374
+
375
+ if (typeof spec.sideEffects === 'function') {
376
+ spec.sideEffects();
377
+ }
378
+
379
+ return result;
380
+ }
@@ -0,0 +1,231 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+
4
+ import { readJsonFile, resolveChangePath, writeJsonFile } from '../../../core/change-artifacts.js';
5
+
6
+ function nowIso() {
7
+ return new Date().toISOString();
8
+ }
9
+
10
+ function addCheck(checks, id, pass, weight, summary, blocking = true) {
11
+ checks.push({ id, status: pass ? 'pass' : 'fail', weight, summary, blocking });
12
+ }
13
+
14
+ function asArray(value) {
15
+ return Array.isArray(value) ? value : [];
16
+ }
17
+
18
+ function resolveProjectPath(projectRoot, filePath) {
19
+ if (!filePath || typeof filePath !== 'string') return null;
20
+ return filePath.startsWith('/') ? filePath : resolve(projectRoot, filePath);
21
+ }
22
+
23
+ function imageReferenceEvidence(projectRoot, changeId, imageReference = {}) {
24
+ if (imageReference.enabled === false) {
25
+ return {
26
+ required: false,
27
+ status: 'skipped',
28
+ missing_outputs: [],
29
+ missing_files: [],
30
+ references: [],
31
+ };
32
+ }
33
+
34
+ const requiredIds = asArray(imageReference.required_outputs).map((output) => output.id).filter(Boolean);
35
+ const artifact = readJsonFile(resolveChangePath(projectRoot, changeId, 'image_reference_set.json'), null);
36
+ const references = asArray(artifact?.references);
37
+ const readyReferences = references.filter((reference) => reference?.status === 'ready');
38
+ const readyIds = new Set(readyReferences.map((reference) => reference.id).filter(Boolean));
39
+ const missingOutputs = requiredIds.filter((id) => !readyIds.has(id));
40
+ const missingFiles = readyReferences
41
+ .map((reference) => ({
42
+ id: reference.id,
43
+ artifact_path: reference.artifact_path || reference.reference_path || null,
44
+ }))
45
+ .filter((reference) => !reference.artifact_path || !existsSync(resolveProjectPath(projectRoot, reference.artifact_path)))
46
+ .map((reference) => reference.id || reference.artifact_path || 'unknown_reference');
47
+
48
+ return {
49
+ required: true,
50
+ status: artifact?.status || 'missing',
51
+ model_capability: artifact?.model_capability || null,
52
+ missing_outputs: missingOutputs,
53
+ missing_files: missingFiles,
54
+ references: readyReferences.map((reference) => ({
55
+ id: reference.id,
56
+ artifact_path: reference.artifact_path || reference.reference_path || null,
57
+ })),
58
+ ready: artifact?.status === 'ready' && requiredIds.length >= 3 && missingOutputs.length === 0 && missingFiles.length === 0,
59
+ };
60
+ }
61
+
62
+ function designSystemEvidence(projectRoot, changeId) {
63
+ const artifact = readJsonFile(resolveChangePath(projectRoot, changeId, 'design_system_pack.json'), null);
64
+ const practiceSources = asArray(artifact?.practice_sources).map((source) => source?.id).filter(Boolean);
65
+ const requiredSourceIds = ['lovable', 'open_design', 'open_codesign', 'openui'];
66
+ const missingPracticeSources = requiredSourceIds.filter((sourceId) => !practiceSources.includes(sourceId));
67
+ const requiredStates = asArray(artifact?.component_policy?.required_states);
68
+ const requiredStateIds = ['default', 'hover', 'focus', 'selected', 'loading', 'empty', 'error'];
69
+ const missingStates = requiredStateIds.filter((stateId) => !requiredStates.includes(stateId));
70
+ const allowedPrimitives = asArray(artifact?.component_policy?.allowed_primitives);
71
+ const previewArtifacts = asArray(artifact?.preview_loop?.required_artifacts);
72
+ const requiredPreviewArtifacts = ['image_reference_set.json', 'visual_benchmark.json', 'aesthetic_review.json'];
73
+ const missingPreviewArtifacts = requiredPreviewArtifacts.filter((artifactName) => !previewArtifacts.includes(artifactName));
74
+
75
+ return {
76
+ required: true,
77
+ status: artifact?.status || 'missing',
78
+ profile: artifact?.profile || null,
79
+ practice_sources: practiceSources,
80
+ missing_practice_sources: missingPracticeSources,
81
+ missing_required_states: missingStates,
82
+ allowed_primitive_count: allowedPrimitives.length,
83
+ missing_preview_artifacts: missingPreviewArtifacts,
84
+ ready: artifact?.status === 'ready'
85
+ && missingPracticeSources.length === 0
86
+ && missingStates.length === 0
87
+ && allowedPrimitives.length >= 6
88
+ && missingPreviewArtifacts.length === 0,
89
+ };
90
+ }
91
+
92
+ function hasCapturedProductSurfaceEvidence(scenario = {}) {
93
+ if (typeof scenario.capture_url === 'string' && scenario.capture_url.trim()) return true;
94
+ if (scenario.screenshot_evidence_mode === 'captured_page') return true;
95
+ const domRects = scenario.layout_observations?.dom_rects;
96
+ const tokenEvidence = scenario.observed_visual_tokens;
97
+ return Array.isArray(domRects) && domRects.length > 0 && Boolean(tokenEvidence);
98
+ }
99
+
100
+ export async function visualAestheticReview(input = {}, context = {}) {
101
+ const projectRoot = context.projectRoot || process.cwd();
102
+ const changeId = context.changeId || input.change_id || null;
103
+ if (!changeId) {
104
+ throw new Error('change_id is required for H6d.visual.aesthetic_review');
105
+ }
106
+
107
+ const generationArtifact = readJsonFile(resolveChangePath(projectRoot, changeId, 'generation_contract.json'), {}) || {};
108
+ const benchmark = readJsonFile(resolveChangePath(projectRoot, changeId, 'visual_benchmark.json'), {}) || {};
109
+ const aggregate = readJsonFile(resolveChangePath(projectRoot, changeId, 'llm_design_review.json'), {}) || {};
110
+ const contract = generationArtifact.generation_contract || {};
111
+ const visualConstraints = contract.visual_constraints || {};
112
+ const standard = visualConstraints.aesthetic_standard || {};
113
+ const imageReference = visualConstraints.image_reference_generation || {};
114
+ const pencilBoundary = visualConstraints.pencil_role_boundary || {};
115
+ const scenarios = asArray(benchmark.scenarios);
116
+ const capturedSurfaceScenarioCount = scenarios.filter(hasCapturedProductSurfaceEvidence).length;
117
+ const dimensions = asArray(standard.dimensions);
118
+ const imageOutputs = asArray(imageReference.required_outputs);
119
+ const imageEvidence = imageReferenceEvidence(projectRoot, changeId, imageReference);
120
+ const systemEvidence = designSystemEvidence(projectRoot, changeId);
121
+ const checks = [];
122
+
123
+ addCheck(
124
+ checks,
125
+ 'high_aesthetic_standard_declared',
126
+ standard.level === 'high' && Number(standard.min_accept_score || 0) >= 0.85,
127
+ 20,
128
+ 'Generation contract declares a high aesthetic standard with a meaningful acceptance score.',
129
+ );
130
+ addCheck(
131
+ checks,
132
+ 'aesthetic_dimensions_complete',
133
+ ['composition_balance', 'information_hierarchy', 'typography_rhythm', 'color_material_control', 'density_and_spacing', 'interaction_state_polish']
134
+ .every((dimension) => dimensions.includes(dimension)),
135
+ 20,
136
+ 'Aesthetic standard covers composition, hierarchy, type, color/material, density, and interaction-state polish.',
137
+ );
138
+ addCheck(
139
+ checks,
140
+ 'image_reference_generation_ready',
141
+ imageReference.enabled !== false && Boolean(imageReference.model_capability) && imageOutputs.length >= 3,
142
+ 15,
143
+ 'Image-generation reference path is explicit enough to produce high-aesthetic frames and component/state sheets.',
144
+ );
145
+ addCheck(
146
+ checks,
147
+ 'image_reference_outputs_materialized',
148
+ imageReference.enabled === false || imageEvidence.ready === true,
149
+ 15,
150
+ 'Required image-reference outputs are materialized as checked artifacts before Pencil is treated as high-aesthetic.',
151
+ );
152
+ addCheck(
153
+ checks,
154
+ 'pencil_boundary_explicit',
155
+ pencilBoundary.sufficient_alone_for_high_aesthetic === false && Boolean(pencilBoundary.role),
156
+ 10,
157
+ 'Pencil is framed as editable assembly/refinement, not the sole source of taste.',
158
+ );
159
+ addCheck(
160
+ checks,
161
+ 'design_system_practices_absorbed',
162
+ systemEvidence.ready === true,
163
+ 15,
164
+ 'Commercial/open design-system practices are materialized as a generic design_system_pack with component, state, preview, and source coverage.',
165
+ );
166
+ addCheck(
167
+ checks,
168
+ 'benchmark_scenarios_pass',
169
+ scenarios.length > 0 && scenarios.every((scenario) => scenario.status === 'pass'),
170
+ 20,
171
+ 'Reference-backed benchmark scenarios are present and passing.',
172
+ );
173
+ addCheck(
174
+ checks,
175
+ 'final_product_surface_captured',
176
+ capturedSurfaceScenarioCount > 0,
177
+ 25,
178
+ 'At least one benchmark scenario is backed by a captured final product DOM surface, not only static reference images.',
179
+ );
180
+ addCheck(
181
+ checks,
182
+ 'aggregate_has_no_followup',
183
+ (aggregate.benchmark_summary?.needs_follow_up_count || 0) === 0,
184
+ 10,
185
+ 'Aggregated visual review has no unresolved benchmark follow-up.',
186
+ false,
187
+ );
188
+
189
+ const totalWeight = checks.reduce((sum, check) => sum + check.weight, 0);
190
+ const passedWeight = checks.filter((check) => check.status === 'pass').reduce((sum, check) => sum + check.weight, 0);
191
+ const score = totalWeight > 0 ? Number((passedWeight / totalWeight).toFixed(3)) : 0;
192
+ const minAcceptScore = Number(standard.min_accept_score || 0.88);
193
+ const blockers = checks.filter((check) => check.status !== 'pass' && check.blocking).map((check) => check.id);
194
+ const status = blockers.length === 0 && score >= minAcceptScore ? 'accept' : 'needs_refine';
195
+ const payload = {
196
+ version: 1,
197
+ phase: 'aesthetic_review',
198
+ change_id: changeId,
199
+ generated_at: nowIso(),
200
+ status,
201
+ score,
202
+ min_accept_score: minAcceptScore,
203
+ blockers,
204
+ checks,
205
+ capability_positioning: {
206
+ pencil: 'editable assembly and targeted refinement',
207
+ image_generation: imageReference.model_capability || null,
208
+ rule: 'Use image-model output to raise taste targets, then require editable implementation and benchmark proof.',
209
+ },
210
+ image_reference_evidence: imageEvidence,
211
+ design_system_evidence: systemEvidence,
212
+ final_product_surface_evidence: {
213
+ required: true,
214
+ captured_scenario_count: capturedSurfaceScenarioCount,
215
+ ready: capturedSurfaceScenarioCount > 0,
216
+ },
217
+ };
218
+
219
+ const outputPath = resolveChangePath(projectRoot, changeId, 'aesthetic_review.json');
220
+ writeJsonFile(outputPath, payload);
221
+ return {
222
+ ok: status === 'accept',
223
+ status,
224
+ score,
225
+ blockers,
226
+ artifact_file: outputPath,
227
+ payload,
228
+ };
229
+ }
230
+
231
+ export default visualAestheticReview;