@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,304 @@
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import json
4
+ import re
5
+ import subprocess
6
+ from datetime import date, datetime, timezone
7
+ from pathlib import Path
8
+
9
+
10
+ CURRENT_START = "<!-- xflow:handoff-current:start -->"
11
+ CURRENT_END = "<!-- xflow:handoff-current:end -->"
12
+ VERIFIED_START = "<!-- xflow:handoff-verified:start -->"
13
+ VERIFIED_END = "<!-- xflow:handoff-verified:end -->"
14
+
15
+ TEMPLATE = """# {title} Handoff
16
+
17
+ Date: {today}
18
+
19
+ ## Current State
20
+
21
+ - Fill in the latest high-signal status.
22
+
23
+ ## Context Pack
24
+
25
+ ### TL;DR
26
+
27
+ - Objective:
28
+ - Current status:
29
+ - Next action:
30
+ - Key risks:
31
+
32
+ ### Key Facts
33
+
34
+ - (Anchor to code/paths/commands/numbers/dates)
35
+
36
+ ### Decisions
37
+
38
+ - (Decision + brief rationale)
39
+
40
+ ### Commands
41
+
42
+ - (High-value commands, include caveats)
43
+
44
+ ### Pointers
45
+
46
+ - (Key files/dirs + 1-line description)
47
+
48
+ ### Gotchas
49
+
50
+ - (Operational sharp edges)
51
+
52
+ ## Project Background
53
+
54
+ - What this project is and why it exists.
55
+
56
+ ## Architecture
57
+
58
+ - Core system shape.
59
+ - Non-negotiable invariants.
60
+
61
+ ## Current Objective
62
+
63
+ - The specific task or delivery target the next session is inheriting.
64
+
65
+ ## Progress
66
+
67
+ - What has been completed and what was verified.
68
+
69
+ ## Open Problems
70
+
71
+ - Current blockers, risks, unknowns, and failure modes.
72
+
73
+ ## Lessons Learned
74
+
75
+ - Operational gotchas, failed approaches, and patterns worth preserving.
76
+
77
+ ## Recommendations
78
+
79
+ 1. First recommended next action.
80
+ 2. Second recommended next action.
81
+
82
+ ## Key Files
83
+
84
+ - `HANDOFF.md`
85
+ """
86
+
87
+
88
+ def read_json(path: Path) -> dict:
89
+ if not path.exists():
90
+ return {}
91
+ try:
92
+ return json.loads(path.read_text(encoding="utf-8"))
93
+ except json.JSONDecodeError:
94
+ return {}
95
+
96
+
97
+ def git_value(project_root: Path, args: list[str], fallback: str = "unknown") -> str:
98
+ try:
99
+ result = subprocess.run(
100
+ ["git", *args],
101
+ cwd=str(project_root),
102
+ text=True,
103
+ capture_output=True,
104
+ check=True,
105
+ )
106
+ except Exception:
107
+ return fallback
108
+ return result.stdout.strip() or fallback
109
+
110
+
111
+ def format_issue(status: dict) -> str:
112
+ if status.get("issue_url"):
113
+ return str(status["issue_url"])
114
+ repo = status.get("issue_repo") or status.get("target_repo")
115
+ issue_number = status.get("issue_number")
116
+ if repo and issue_number:
117
+ return f"{repo}#{issue_number}"
118
+ return "none"
119
+
120
+
121
+ def build_current_state_block(project_root: Path, change_id: str | None) -> str:
122
+ status = read_json(project_root / "specs" / "changes" / str(change_id or "") / "status.json")
123
+ branch = git_value(project_root, ["branch", "--show-current"], status.get("branch_name") or "unknown")
124
+ resolved_change_id = status.get("change_id") or change_id or "none"
125
+ updated_at = datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
126
+ lines = [
127
+ CURRENT_START,
128
+ f"- Updated: {updated_at}",
129
+ f"- Git branch: `{branch}`",
130
+ f"- Change: `{resolved_change_id}`",
131
+ f"- Title: {status.get('title') or 'none'}",
132
+ f"- Stage / status: `{status.get('current_stage') or 'unknown'}` / `{status.get('status') or 'unknown'}`",
133
+ f"- Verification: `{status.get('verification_status') or 'unknown'}`",
134
+ f"- Archive: `{status.get('archival_status') or 'unknown'}`",
135
+ f"- Issue: {format_issue(status)}",
136
+ f"- Next action: {status.get('next_action') or 'none'}",
137
+ CURRENT_END,
138
+ ]
139
+ return "\n".join(lines)
140
+
141
+
142
+ def code_span(value: object) -> str:
143
+ text = str(value)
144
+ if "`" not in text:
145
+ return f"`{text}`"
146
+ return f"`` {text} ``"
147
+
148
+
149
+ def build_verified_commands_block(project_root: Path, change_id: str | None) -> str:
150
+ status = read_json(project_root / "specs" / "changes" / str(change_id or "") / "status.json")
151
+ proof = read_json(project_root / "specs" / "changes" / str(change_id or "") / "verify_proof.json")
152
+ results = proof.get("results") if isinstance(proof.get("results"), list) else []
153
+ resolved_change_id = status.get("change_id") or change_id or "none"
154
+ updated_at = datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
155
+ passed_count = sum(1 for result in results if result.get("passed"))
156
+ total_count = len(results)
157
+ summary = f"{passed_count}/{total_count} passed" if total_count else "no verify_proof results"
158
+
159
+ lines = [
160
+ VERIFIED_START,
161
+ f"- Updated: {updated_at}",
162
+ f"- Change: `{resolved_change_id}`",
163
+ f"- Verification: `{status.get('verification_status') or 'unknown'}`",
164
+ f"- Proof recorded: `{proof.get('recorded_at') or 'unknown'}`",
165
+ f"- Summary: `{summary}`",
166
+ ]
167
+ if total_count:
168
+ lines.append("- Commands:")
169
+ for result in results:
170
+ verdict = "PASS" if result.get("passed") else "FAIL"
171
+ command = code_span(result.get("command") or "unknown")
172
+ exit_code = code_span(result.get("exit_code", "unknown"))
173
+ lines.append(f" - {verdict} {command} (exit {exit_code})")
174
+ else:
175
+ lines.append("- Commands: none recorded")
176
+ lines.append(VERIFIED_END)
177
+ return "\n".join(lines)
178
+
179
+
180
+ def upsert_current_state(content: str, block: str) -> str:
181
+ managed_pattern = re.compile(
182
+ rf"{re.escape(CURRENT_START)}.*?{re.escape(CURRENT_END)}",
183
+ re.DOTALL,
184
+ )
185
+ if managed_pattern.search(content):
186
+ return managed_pattern.sub(block, content, count=1)
187
+
188
+ heading_pattern = re.compile(r"(^## Current State\s*\n)", re.MULTILINE)
189
+ heading_match = heading_pattern.search(content)
190
+ if heading_match:
191
+ section_start = heading_match.start()
192
+ body_start = heading_match.end()
193
+ next_heading = re.search(r"^## ", content[body_start:], re.MULTILINE)
194
+ section_end = body_start + next_heading.start() if next_heading else len(content)
195
+ return (
196
+ content[:section_start]
197
+ + "## Current State\n\n"
198
+ + block
199
+ + "\n\n"
200
+ + content[section_end:].lstrip("\n")
201
+ )
202
+
203
+ first_heading = re.match(r"^# .*\n", content)
204
+ if first_heading:
205
+ insert_at = first_heading.end()
206
+ return content[:insert_at] + "\n## Current State\n\n" + block + "\n" + content[insert_at:]
207
+ return "## Current State\n\n" + block + "\n\n" + content
208
+
209
+
210
+ def upsert_verified_commands(content: str, block: str) -> str:
211
+ managed_pattern = re.compile(
212
+ rf"{re.escape(VERIFIED_START)}.*?{re.escape(VERIFIED_END)}",
213
+ re.DOTALL,
214
+ )
215
+ if managed_pattern.search(content):
216
+ return managed_pattern.sub(block, content, count=1)
217
+
218
+ heading_pattern = re.compile(r"(^## Latest Verified Commands(?: and Results)?\s*\n)", re.MULTILINE)
219
+ heading_match = heading_pattern.search(content)
220
+ if heading_match:
221
+ section_start = heading_match.start()
222
+ body_start = heading_match.end()
223
+ next_heading = re.search(r"^## ", content[body_start:], re.MULTILINE)
224
+ section_end = body_start + next_heading.start() if next_heading else len(content)
225
+ return (
226
+ content[:section_start]
227
+ + "## Latest Verified Commands\n\n"
228
+ + block
229
+ + "\n\n"
230
+ + content[section_end:].lstrip("\n")
231
+ )
232
+
233
+ current_section = re.search(r"^## Current State\s*\n", content, re.MULTILINE)
234
+ if current_section:
235
+ body_start = current_section.end()
236
+ next_heading = re.search(r"^## ", content[body_start:], re.MULTILINE)
237
+ insert_at = body_start + next_heading.start() if next_heading else len(content)
238
+ return (
239
+ content[:insert_at].rstrip()
240
+ + "\n\n## Latest Verified Commands\n\n"
241
+ + block
242
+ + "\n\n"
243
+ + content[insert_at:].lstrip("\n")
244
+ )
245
+
246
+ first_heading = re.match(r"^# .*\n", content)
247
+ if first_heading:
248
+ insert_at = first_heading.end()
249
+ return content[:insert_at] + "\n## Latest Verified Commands\n\n" + block + "\n" + content[insert_at:]
250
+ return "## Latest Verified Commands\n\n" + block + "\n\n" + content
251
+
252
+
253
+ def parse_args() -> argparse.Namespace:
254
+ parser = argparse.ArgumentParser(
255
+ description="Create or overwrite the canonical root HANDOFF.md scaffold.",
256
+ )
257
+ parser.add_argument(
258
+ "--project-root",
259
+ default=".",
260
+ help="Project root where HANDOFF.md should be written.",
261
+ )
262
+ parser.add_argument(
263
+ "--title",
264
+ default=None,
265
+ help="Project title for the handoff heading. Defaults to the project root directory name.",
266
+ )
267
+ parser.add_argument(
268
+ "--refresh",
269
+ action="store_true",
270
+ help="Keep an existing HANDOFF.md intact. Create a scaffold only when missing.",
271
+ )
272
+ parser.add_argument(
273
+ "--change-id",
274
+ default=None,
275
+ help="Accepted for atom-run compatibility; not used by this scaffold.",
276
+ )
277
+ return parser.parse_args()
278
+
279
+
280
+ def main() -> int:
281
+ args = parse_args()
282
+ project_root = Path(args.project_root).expanduser().resolve()
283
+ title = args.title or project_root.name
284
+ handoff_path = project_root / "HANDOFF.md"
285
+ project_root.mkdir(parents=True, exist_ok=True)
286
+ if args.refresh and handoff_path.exists():
287
+ if args.change_id:
288
+ content = handoff_path.read_text(encoding="utf-8")
289
+ content = upsert_current_state(content, build_current_state_block(project_root, args.change_id))
290
+ content = upsert_verified_commands(content, build_verified_commands_block(project_root, args.change_id))
291
+ handoff_path.write_text(content, encoding="utf-8")
292
+ print(str(handoff_path))
293
+ return 0
294
+ content = TEMPLATE.format(title=title, today=date.today().isoformat())
295
+ if args.change_id:
296
+ content = upsert_current_state(content, build_current_state_block(project_root, args.change_id))
297
+ content = upsert_verified_commands(content, build_verified_commands_block(project_root, args.change_id))
298
+ handoff_path.write_text(content, encoding="utf-8")
299
+ print(str(handoff_path))
300
+ return 0
301
+
302
+
303
+ if __name__ == "__main__":
304
+ raise SystemExit(main())
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: xflow:lookback
3
+ description: 通过读取项目根目录的 `AHA.md`、`HANDOFF.md` 和核心设计文档,重新加载最相关的持久上下文,用于识别当前工作与既有决策之间是否发生偏移。适用于验证新方向是否仍符合过去的关键认知、设计意图或 handoff 事实时。
4
+ ---
5
+
6
+ # Superflow Lookback
7
+
8
+ Use this skill to check whether current design or implementation has drifted from recorded insight.
9
+
10
+ Primary sources:
11
+
12
+ - project root `AHA.md`
13
+ - project root `HANDOFF.md`
14
+ - project root `DESIGN.md` (if frontend project)
15
+ - core design docs under `docs/plans/`
16
+
17
+ ## Goal
18
+
19
+ Answer one question:
20
+
21
+ `Is the current direction still aligned with the strongest prior reasoning?`
22
+
23
+ ## Workflow
24
+
25
+ 1. Read `AHA.md` if present.
26
+ 2. Read `HANDOFF.md`.
27
+ 3. Read `DESIGN.md` if present (check design decisions).
28
+ 4. Read only the directly relevant design docs in `docs/plans/`.
29
+ 5. Compare current design / code / proposal against:
30
+ - active aha constraints
31
+ - frozen design intent
32
+ - design decisions log (if UI)
33
+ - current handoff truth
34
+ 6. Produce a concise drift report:
35
+ - aligned
36
+ - partially drifted
37
+ - materially drifted
38
+ 7. For any drift, name the exact file or design surface that drifted.
39
+
40
+ ## Output requirements
41
+
42
+ Every lookback should include:
43
+
44
+ 1. current target
45
+ 2. prior anchors consulted
46
+ 3. alignment verdict
47
+ 4. concrete drift points, if any
48
+ 5. recommended correction
49
+
50
+ ## Guardrails
51
+
52
+ - Do not read the whole repo by default.
53
+ - Prefer high-signal docs over broad exploration.
54
+ - Treat `AHA.md` as durable reasoning, not optional commentary.
55
+ - If `AHA.md` and `HANDOFF.md` conflict, surface the conflict explicitly.
56
+
57
+ ## Script
58
+
59
+ The agent autonomously calls this helper for source listing:
60
+
61
+ ```bash
62
+ python3 ~/.codex/skills/xflow/lookback/scripts/lookback_digest.py
63
+ ```
64
+
65
+ Agent uses output to prioritize which docs to read.
66
+ User invokes skill, agent handles execution.
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ from pathlib import Path
4
+
5
+
6
+ def parse_args() -> argparse.Namespace:
7
+ parser = argparse.ArgumentParser(
8
+ description="List the canonical lookback sources for a project.",
9
+ )
10
+ parser.add_argument(
11
+ "--project-root",
12
+ default=".",
13
+ help="Project root containing HANDOFF.md, optional AHA.md, and docs/plans.",
14
+ )
15
+ return parser.parse_args()
16
+
17
+
18
+ def top_design_docs(plans_dir: Path, limit: int = 8) -> list[Path]:
19
+ if not plans_dir.exists():
20
+ return []
21
+ docs = sorted(
22
+ [path for path in plans_dir.glob("*.md") if path.is_file()],
23
+ key=lambda path: path.stat().st_mtime,
24
+ reverse=True,
25
+ )
26
+ return docs[:limit]
27
+
28
+
29
+ def main() -> int:
30
+ args = parse_args()
31
+ root = Path(args.project_root).expanduser().resolve()
32
+ handoff = root / "HANDOFF.md"
33
+ aha = root / "AHA.md"
34
+ plans = root / "docs" / "plans"
35
+
36
+ print(f"PROJECT_ROOT: {root}")
37
+ print(f"HANDOFF: {'present' if handoff.exists() else 'missing'} {handoff}")
38
+ print(f"AHA: {'present' if aha.exists() else 'missing'} {aha}")
39
+ print("DESIGN_DOCS:")
40
+ for doc in top_design_docs(plans):
41
+ print(f"- {doc}")
42
+ return 0
43
+
44
+
45
+ if __name__ == "__main__":
46
+ raise SystemExit(main())