@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,70 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ A1.issue.create_or_patch — GitHub issue create or patch.
4
+ Delegates to open_issue_flow.py. Accepts same CLI args.
5
+ """
6
+ import sys, os, json, subprocess
7
+ from pathlib import Path
8
+
9
+ def cli_value(flag):
10
+ argv = sys.argv[1:]
11
+ for index, arg in enumerate(argv):
12
+ if arg == flag and index + 1 < len(argv):
13
+ return argv[index + 1]
14
+ return None
15
+
16
+ def load_body_from_cli():
17
+ body_file = cli_value("--body-file")
18
+ if body_file:
19
+ return Path(body_file).expanduser().read_text(encoding="utf-8")
20
+ return cli_value("--body")
21
+
22
+ def ensure_issue_doc(project_root, change_id):
23
+ body = load_body_from_cli()
24
+ if not body:
25
+ return False
26
+ change_root = Path(project_root).expanduser().resolve() / "specs" / "changes" / change_id
27
+ change_root.mkdir(parents=True, exist_ok=True)
28
+ title = cli_value("--title") or change_id
29
+ issue_path = change_root / "issue.md"
30
+ issue_path.write_text(f"# {title}\n\n{body.strip()}\n", encoding="utf-8")
31
+ return True
32
+
33
+ def main():
34
+ project_root = "."
35
+ change_id = None
36
+ for index, arg in enumerate(sys.argv[1:]):
37
+ if arg == "--project-root" and index + 2 <= len(sys.argv[1:]):
38
+ project_root = sys.argv[index + 2]
39
+ elif arg == "--change-id" and index + 2 <= len(sys.argv[1:]):
40
+ change_id = sys.argv[index + 2]
41
+
42
+ if change_id:
43
+ status_path = Path(project_root).expanduser().resolve() / "specs" / "changes" / change_id / "status.json"
44
+ issue_path = status_path.parent / "issue.md"
45
+ if status_path.exists():
46
+ try:
47
+ status = json.loads(status_path.read_text())
48
+ if status.get("issue_number") and status.get("branch_name") and status.get("checkout_path"):
49
+ if not issue_path.exists():
50
+ ensure_issue_doc(project_root, change_id)
51
+ if not issue_path.exists():
52
+ raise RuntimeError("issue.md is still missing for prepared issue")
53
+ print(json.dumps({
54
+ "ok": True,
55
+ "skipped": True,
56
+ "reason": "issue_already_prepared",
57
+ "issue_number": status.get("issue_number"),
58
+ "branch_name": status.get("branch_name"),
59
+ "checkout_path": status.get("checkout_path"),
60
+ }))
61
+ return
62
+ except Exception:
63
+ pass
64
+
65
+ script = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'openissue', 'scripts', 'open_issue_flow.py'))
66
+ result = subprocess.run([sys.executable, script] + sys.argv[1:])
67
+ sys.exit(result.returncode)
68
+
69
+ if __name__ == '__main__':
70
+ main()
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ A2.branch.create_and_link — Create branch and link to issue.
4
+ Delegates to open_issue_flow.py with --branch-only flag (if supported)
5
+ or as part of the full issue flow.
6
+ """
7
+ import sys, os, json, subprocess
8
+ from pathlib import Path
9
+
10
+ def main():
11
+ project_root = "."
12
+ change_id = None
13
+ for index, arg in enumerate(sys.argv[1:]):
14
+ if arg == "--project-root" and index + 2 <= len(sys.argv[1:]):
15
+ project_root = sys.argv[index + 2]
16
+ elif arg == "--change-id" and index + 2 <= len(sys.argv[1:]):
17
+ change_id = sys.argv[index + 2]
18
+
19
+ if change_id:
20
+ status_path = Path(project_root).expanduser().resolve() / "specs" / "changes" / change_id / "status.json"
21
+ if status_path.exists():
22
+ try:
23
+ status = json.loads(status_path.read_text())
24
+ if status.get("issue_number") and status.get("branch_name") and status.get("checkout_path"):
25
+ print(json.dumps({
26
+ "ok": True,
27
+ "skipped": True,
28
+ "reason": "branch_already_prepared",
29
+ "issue_number": status.get("issue_number"),
30
+ "branch_name": status.get("branch_name"),
31
+ "checkout_path": status.get("checkout_path"),
32
+ }))
33
+ return
34
+ except Exception:
35
+ pass
36
+
37
+ script = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'openissue', 'scripts', 'open_issue_flow.py'))
38
+ result = subprocess.run([sys.executable, script] + sys.argv[1:])
39
+ sys.exit(result.returncode)
40
+
41
+ if __name__ == '__main__':
42
+ main()
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env python3
2
+ """A3.repo.doctor — Consistency check. Delegates to doctor.py."""
3
+ import sys, os, subprocess
4
+
5
+ def main():
6
+ script = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'scripts', 'doctor.py'))
7
+ result = subprocess.run([sys.executable, script] + sys.argv[1:])
8
+ sys.exit(result.returncode)
9
+
10
+ if __name__ == '__main__':
11
+ main()
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env python3
2
+ """A4.project.set_status — Move GitHub project item to target status. Delegates to set_issue_in_progress.py."""
3
+ import sys, os, subprocess
4
+
5
+ def main():
6
+ script = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'exec', 'scripts', 'set_issue_in_progress.py'))
7
+ result = subprocess.run([sys.executable, script] + sys.argv[1:])
8
+ sys.exit(result.returncode)
9
+
10
+ if __name__ == '__main__':
11
+ main()
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env python3
2
+ """A5.archive.commit_push_close — Commit, push branch, close issue, mark project Done. Delegates to archive_change.py."""
3
+ import sys, os, subprocess
4
+
5
+ def main():
6
+ script = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'archive', 'scripts', 'archive_change.py'))
7
+ result = subprocess.run([sys.executable, script] + sys.argv[1:])
8
+ sys.exit(result.returncode)
9
+
10
+ if __name__ == '__main__':
11
+ main()
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ A6.pr.create — Open a PR from issue branch against base.
4
+ Uses gh pr create. Reads change context from status.json.
5
+ """
6
+ import sys, os, json, argparse, subprocess
7
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'scripts'))
8
+ from common import load_status
9
+
10
+ def main():
11
+ p = argparse.ArgumentParser()
12
+ p.add_argument('--project-root', default='.')
13
+ p.add_argument('--change-id', required=True)
14
+ p.add_argument('--base-branch', default='main')
15
+ p.add_argument('--draft', action='store_true')
16
+ args = p.parse_args()
17
+
18
+ _, status = load_status(args.project_root, args.change_id)
19
+ repo = status.get('code_repo', '')
20
+ branch = status.get('branch_name', '')
21
+ issue_num = status.get('issue_number')
22
+ issue_repo = status.get('issue_repo') or status.get('target_repo') or repo
23
+ issue_url = status.get('issue_url')
24
+ title = status.get('title', args.change_id)
25
+ checkout_path = status.get('checkout_path') or args.project_root
26
+
27
+ if not repo or not branch:
28
+ print(json.dumps({'ok': False, 'error': 'Missing code_repo or branch_name in status.json'}))
29
+ sys.exit(1)
30
+
31
+ if issue_url:
32
+ body = f'Tracks {issue_url}'
33
+ elif issue_num and issue_repo and issue_repo != repo:
34
+ body = f'Tracks {issue_repo}#{issue_num}'
35
+ else:
36
+ body = f'Closes #{issue_num}' if issue_num else ''
37
+ cmd = ['gh', 'pr', 'create',
38
+ '--repo', repo,
39
+ '--head', branch,
40
+ '--base', args.base_branch,
41
+ '--title', title,
42
+ '--body', body]
43
+ if args.draft:
44
+ cmd.append('--draft')
45
+
46
+ result = subprocess.run(cmd, cwd=checkout_path, capture_output=True, text=True)
47
+ if result.returncode != 0:
48
+ # PR may already exist
49
+ stderr = result.stderr or ''
50
+ if 'already exists' in stderr:
51
+ view = subprocess.run(
52
+ ['gh', 'pr', 'view', '--repo', repo, '--json', 'url', '--head', branch],
53
+ cwd=checkout_path,
54
+ capture_output=True,
55
+ text=True,
56
+ )
57
+ pr_url = None
58
+ if view.returncode == 0 and view.stdout.strip():
59
+ try:
60
+ pr_url = json.loads(view.stdout).get('url')
61
+ except Exception:
62
+ pr_url = None
63
+ print(json.dumps({'ok': True, 'message': 'PR already exists', 'pr_url': pr_url}))
64
+ sys.exit(0)
65
+ print(json.dumps({'ok': False, 'error': stderr}))
66
+ sys.exit(1)
67
+
68
+ pr_url = result.stdout.strip()
69
+ print(json.dumps({'ok': True, 'pr_url': pr_url}))
70
+
71
+ if __name__ == '__main__':
72
+ main()
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env python3
2
+ """B1.change.scaffold — Initialize specs/changes/<change_id>/. Delegates to scaffold_specs_change.py."""
3
+ import sys, os, subprocess
4
+
5
+ def main():
6
+ script = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'yolo', 'scripts', 'scaffold_specs_change.py'))
7
+ result = subprocess.run([sys.executable, script] + sys.argv[1:])
8
+ sys.exit(result.returncode)
9
+
10
+ if __name__ == '__main__':
11
+ main()
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env python3
2
+ """B2.status.read — Read and return status.json for a change."""
3
+ import sys, os, json, argparse
4
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'scripts'))
5
+ from common import load_status
6
+
7
+ def main():
8
+ p = argparse.ArgumentParser()
9
+ p.add_argument('--project-root', default='.')
10
+ p.add_argument('--change-id', required=True)
11
+ args = p.parse_args()
12
+ status = load_status(args.project_root, args.change_id)
13
+ print(json.dumps(status, indent=2))
14
+
15
+ if __name__ == '__main__':
16
+ main()
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env python3
2
+ """B2b.status.write — Merge fields into status.json."""
3
+ import sys, os, json, argparse
4
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'scripts'))
5
+ from common import load_status, save_status
6
+
7
+ def main():
8
+ p = argparse.ArgumentParser()
9
+ p.add_argument('--project-root', default='.')
10
+ p.add_argument('--change-id', required=True)
11
+ p.add_argument('--fields', required=True, help='JSON object of fields to merge')
12
+ args = p.parse_args()
13
+ fields = json.loads(args.fields)
14
+ path, status = load_status(args.project_root, args.change_id)
15
+ status.update(fields)
16
+ save_status(path, status)
17
+ print(json.dumps({'ok': True, 'updated': list(fields.keys())}))
18
+
19
+ if __name__ == '__main__':
20
+ main()
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env python3
2
+ """B3.status.transition — Assert and record a stage/status transition."""
3
+ import sys, os, json, argparse
4
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'scripts'))
5
+ from common import load_status, save_status, record_stage_transition
6
+
7
+ def main():
8
+ p = argparse.ArgumentParser()
9
+ p.add_argument('--project-root', default='.')
10
+ p.add_argument('--change-id', required=True)
11
+ p.add_argument('--to-stage')
12
+ p.add_argument('--to-status')
13
+ p.add_argument('--reason', default='workflow-advance')
14
+ args = p.parse_args()
15
+
16
+ path, status = load_status(args.project_root, args.change_id)
17
+
18
+ if args.to_stage:
19
+ record_stage_transition(status, args.to_stage, reason=args.reason)
20
+ if args.to_status:
21
+ status['status'] = args.to_status
22
+
23
+ save_status(path, status)
24
+ print(json.dumps({'ok': True, 'stage': status.get('current_stage'), 'status': status.get('status')}))
25
+
26
+ if __name__ == '__main__':
27
+ main()
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env python3
2
+ """B4.handoff.scaffold_or_refresh — Create/refresh HANDOFF.md. Delegates to scaffold_handoff.py."""
3
+ import sys, os, subprocess
4
+
5
+ def main():
6
+ script = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'handoff', 'scripts', 'scaffold_handoff.py'))
7
+ result = subprocess.run([sys.executable, script] + sys.argv[1:])
8
+ sys.exit(result.returncode)
9
+
10
+ if __name__ == '__main__':
11
+ main()
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env python3
2
+ """B5.aha.append — Append AHA entry. Delegates to append_aha.py."""
3
+ import sys, os, subprocess
4
+
5
+ def main():
6
+ script = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'aha', 'scripts', 'append_aha.py'))
7
+ result = subprocess.run([sys.executable, script] + sys.argv[1:])
8
+ sys.exit(result.returncode)
9
+
10
+ if __name__ == '__main__':
11
+ main()
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env python3
2
+ """B6.takein.digest — Session rehydration digest. Delegates to takein_digest.py."""
3
+ import sys, os, subprocess
4
+
5
+ def main():
6
+ script = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'takein', 'scripts', 'takein_digest.py'))
7
+ result = subprocess.run([sys.executable, script] + sys.argv[1:])
8
+ sys.exit(result.returncode)
9
+
10
+ if __name__ == '__main__':
11
+ main()
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env python3
2
+ """B6b.lookback.digest — Lookback sources list. Delegates to lookback_digest.py."""
3
+ import sys, os, subprocess
4
+
5
+ def main():
6
+ script = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'lookback', 'scripts', 'lookback_digest.py'))
7
+ result = subprocess.run([sys.executable, script] + sys.argv[1:])
8
+ sys.exit(result.returncode)
9
+
10
+ if __name__ == '__main__':
11
+ main()
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ B7.change_mission.bind — The ONLY place that writes the change_id <-> mission_id coupling.
4
+ Writes to .as-xflow/bindings.json (append-only, keyed by change_id).
5
+ Does NOT touch state.sqlite or status.json schemas.
6
+ """
7
+ import sys, os, json, argparse
8
+ from datetime import datetime, timezone
9
+
10
+ def main():
11
+ p = argparse.ArgumentParser()
12
+ p.add_argument('--project-root', default='.')
13
+ p.add_argument('--change-id', required=True)
14
+ p.add_argument('--mission-id', default=None)
15
+ p.add_argument('--direction', default='change_to_mission')
16
+ args = p.parse_args()
17
+
18
+ bindings_dir = os.path.join(args.project_root, '.as-xflow')
19
+ os.makedirs(bindings_dir, exist_ok=True)
20
+ bindings_file = os.path.join(bindings_dir, 'bindings.json')
21
+
22
+ bindings = {}
23
+ if os.path.exists(bindings_file):
24
+ try:
25
+ bindings = json.loads(open(bindings_file).read())
26
+ except Exception:
27
+ bindings = {}
28
+
29
+ entry = {
30
+ 'change_id': args.change_id,
31
+ 'mission_id': args.mission_id,
32
+ 'direction': args.direction,
33
+ 'bound_at': datetime.now(timezone.utc).isoformat(),
34
+ }
35
+ bindings[args.change_id] = entry
36
+
37
+ with open(bindings_file, 'w') as f:
38
+ json.dump(bindings, f, indent=2)
39
+
40
+ print(json.dumps({'ok': True, 'binding': entry}))
41
+
42
+ if __name__ == '__main__':
43
+ main()
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ C1.mem — Opaque personal/global memory atom.
4
+ Passes all arguments through to mem.sh verbatim.
5
+ cli-use-memory directory structure is NOT modified by this atom.
6
+ """
7
+ import json
8
+ import os
9
+ import subprocess
10
+ import sys
11
+
12
+
13
+ def candidate_mem_scripts():
14
+ env_script = os.environ.get('XMEM_MEM_SH')
15
+ if env_script:
16
+ yield env_script
17
+
18
+ here = os.path.dirname(__file__)
19
+ yield os.path.abspath(os.path.join(here, '..', '..', '..', 'as-xmem', 'mem.sh'))
20
+
21
+ home = os.path.expanduser('~')
22
+ for target in (
23
+ '.agents/skills/xmem/mem.sh',
24
+ '.codex/skills/xmem/mem.sh',
25
+ '.claude/skills/xmem/mem.sh',
26
+ '.hermes/skills/xmem/mem.sh',
27
+ '.openclaw/skills/xmem/mem.sh',
28
+ '.config/opencode/skills/xmem/mem.sh',
29
+ ):
30
+ yield os.path.join(home, target)
31
+
32
+
33
+ def find_mem_script():
34
+ for script in candidate_mem_scripts():
35
+ if script and os.path.isfile(script):
36
+ return script
37
+ return None
38
+
39
+ def main():
40
+ script = find_mem_script()
41
+ if not script:
42
+ print(json.dumps({
43
+ 'ok': True,
44
+ 'atom': 'C1.mem',
45
+ 'skipped': True,
46
+ 'reason': 'xmem mem.sh not found; install xmem to enable shared-memory persistence',
47
+ }))
48
+ return 0
49
+
50
+ result = subprocess.run(['bash', script] + sys.argv[1:])
51
+ return result.returncode
52
+
53
+ if __name__ == '__main__':
54
+ sys.exit(main())
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ E6.gate.local_precheck — Atom entry point.
4
+ Single source of truth for all deterministic phase gate checks.
5
+ Wraps yolo_gate.py verbatim. Both yolo driver and agentos heavy workflow
6
+ route through this script for structural checks.
7
+
8
+ E6 ≡ E1+E2 invariant: agentos's local-workflow.js must NOT re-implement
9
+ these checks; it must shell out here.
10
+ """
11
+ import sys
12
+ import os
13
+ import subprocess
14
+
15
+ def main():
16
+ # Forward all args to the real implementation
17
+ script = os.path.join(os.path.dirname(__file__), '..', 'yolo', 'scripts', 'yolo_gate.py')
18
+ script = os.path.abspath(script)
19
+ result = subprocess.run([sys.executable, script] + sys.argv[1:])
20
+ sys.exit(result.returncode)
21
+
22
+ if __name__ == '__main__':
23
+ main()
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ H1.design.lite_gate — Deterministic DESIGN.md sanity check.
4
+ Checks for required section headings: Reference Anchors, Beauty Contract, Layout Premise.
5
+ Does NOT invoke any LLM. Returns JSON verdict.
6
+ """
7
+ import sys, os, json, argparse
8
+
9
+ REQUIRED_SECTIONS = [
10
+ 'Reference Anchors',
11
+ 'Anti-References',
12
+ 'Layout Premise',
13
+ 'Beauty Contract',
14
+ 'Failure Modes',
15
+ ]
16
+
17
+ MINIMAL_SECTIONS = [
18
+ 'Visual Tone',
19
+ 'Color System',
20
+ 'Typography',
21
+ ]
22
+
23
+ def main():
24
+ p = argparse.ArgumentParser()
25
+ p.add_argument('--project-root', default='.')
26
+ p.add_argument('--change-id')
27
+ p.add_argument('--require-full-contract', action='store_true')
28
+ args = p.parse_args()
29
+
30
+ # Check project-level DESIGN.md first, then change-scoped design.md
31
+ design_md = os.path.join(args.project_root, 'DESIGN.md')
32
+ change_design_md = None
33
+ if args.change_id:
34
+ change_design_md = os.path.join(args.project_root, 'specs', 'changes', args.change_id, 'design.md')
35
+
36
+ target = change_design_md if (change_design_md and os.path.exists(change_design_md)) else design_md
37
+
38
+ if not os.path.exists(target):
39
+ result = {
40
+ 'ok': False,
41
+ 'gate': 'H1.design.lite_gate',
42
+ 'verdict': 'missing',
43
+ 'message': f'No DESIGN.md or design.md found at {target}',
44
+ 'path': target,
45
+ }
46
+ print(json.dumps(result))
47
+ sys.exit(1)
48
+
49
+ content = open(target).read()
50
+ missing = []
51
+
52
+ if args.require_full_contract:
53
+ sections = REQUIRED_SECTIONS
54
+ else:
55
+ sections = MINIMAL_SECTIONS
56
+
57
+ for sec in sections:
58
+ if sec not in content:
59
+ missing.append(sec)
60
+
61
+ ok = len(missing) == 0
62
+ result = {
63
+ 'ok': ok,
64
+ 'gate': 'H1.design.lite_gate',
65
+ 'verdict': 'pass' if ok else 'fail',
66
+ 'path': target,
67
+ 'missing_sections': missing,
68
+ 'checked_sections': sections,
69
+ }
70
+ print(json.dumps(result))
71
+ sys.exit(0 if ok else 1)
72
+
73
+ if __name__ == '__main__':
74
+ main()
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ I6a.tdd.run — Deterministic test runner harness.
4
+ Runs a test command, captures exit code + truncated stdout,
5
+ writes proof to specs/changes/<id>/tdd/<phase>-<n>.json.
6
+
7
+ The harness is deterministic and records whether the raw test result met the
8
+ phase expectation. Red expects a failing test; green/refactor expect passing tests.
9
+ The proof also binds a snapshot of the tree so I6b can recompute the digest
10
+ and re-run the command.
11
+ """
12
+ import sys, os, json, argparse, subprocess, time
13
+ from datetime import datetime, timezone
14
+ from pathlib import Path
15
+
16
+ import tdd_lib
17
+
18
+
19
+ def default_expectation(phase: str) -> str:
20
+ return 'fail' if phase == 'red' else 'pass'
21
+
22
+ def expectation_met(expect: str, passed: bool) -> bool:
23
+ if expect == 'fail':
24
+ return not passed
25
+ return passed
26
+
27
+ def main():
28
+ p = argparse.ArgumentParser()
29
+ p.add_argument('--project-root', default='.')
30
+ p.add_argument('--change-id', required=True)
31
+ p.add_argument('--test-command', required=True, help='Test command to run')
32
+ p.add_argument('--phase', default='run', help='red | green | refactor | run')
33
+ p.add_argument('--attempt', type=int, default=0)
34
+ p.add_argument('--expect', choices=['pass', 'fail'], help='Expected raw test result')
35
+ args = p.parse_args()
36
+ expect = args.expect or default_expectation(args.phase)
37
+ project = Path(args.project_root).resolve()
38
+
39
+ proof_dir = os.path.join(args.project_root, 'specs', 'changes', args.change_id, 'tdd')
40
+ os.makedirs(proof_dir, exist_ok=True)
41
+
42
+ start = time.time()
43
+ timed_out = False
44
+ try:
45
+ result = subprocess.run(
46
+ args.test_command,
47
+ shell=True,
48
+ capture_output=True,
49
+ text=True,
50
+ cwd=args.project_root,
51
+ timeout=300,
52
+ env=tdd_lib.test_env(),
53
+ )
54
+ elapsed = time.time() - start
55
+ stdout = result.stdout or ''
56
+ stderr = result.stderr or ''
57
+ exit_code = result.returncode
58
+ except subprocess.TimeoutExpired:
59
+ timed_out = True
60
+ elapsed = 300
61
+ stdout = ''
62
+ stderr = 'TIMEOUT after 300s'
63
+ exit_code = -1
64
+
65
+ passed = exit_code == 0
66
+ combined = f"{stdout}\n{stderr}"
67
+ file_hashes = tdd_lib.write_snapshot(project, args.change_id, args.phase, args.attempt)
68
+ proof = {
69
+ 'phase': args.phase,
70
+ 'attempt': args.attempt,
71
+ 'command': args.test_command,
72
+ 'exit_code': exit_code,
73
+ 'passed': passed,
74
+ 'expected': expect,
75
+ 'expectation_met': expectation_met(expect, False if timed_out else passed),
76
+ 'stdout_tail': stdout[-2000:] if stdout else '',
77
+ 'stderr_tail': stderr[-1000:] if stderr else '',
78
+ 'elapsed_s': round(elapsed, 2),
79
+ 'output_sha256': tdd_lib.output_digest(stdout, stderr),
80
+ 'git_head': tdd_lib.git_head(project),
81
+ 'file_hashes': file_hashes,
82
+ 'failure_kind': 'timeout' if timed_out else tdd_lib.classify_output(combined, passed),
83
+ 'recorded_at': datetime.now(timezone.utc).isoformat(),
84
+ }
85
+ proof['binding'] = tdd_lib.compute_binding(proof)
86
+
87
+ proof_file = os.path.join(proof_dir, f'{args.phase}-{args.attempt}.json')
88
+ with open(proof_file, 'w') as f:
89
+ json.dump(proof, f, indent=2)
90
+ f.write('\n')
91
+
92
+ print(json.dumps({
93
+ 'ok': proof['expectation_met'],
94
+ 'proof_file': proof_file,
95
+ 'exit_code': proof['exit_code'],
96
+ 'expected': expect,
97
+ 'passed': proof['passed'],
98
+ 'failure_kind': proof['failure_kind'],
99
+ 'binding': proof['binding'],
100
+ }))
101
+ sys.exit(0 if proof['expectation_met'] else 1)
102
+
103
+ if __name__ == '__main__':
104
+ main()