@surlymochan/evalid 0.0.0-stage → 0.4.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.
- package/CHANGELOG.md +94 -0
- package/LICENSE +21 -0
- package/README.md +315 -2
- package/RELEASE_NOTES.md +124 -0
- package/atoms/registry.json +758 -0
- package/bin/xflow.js +7261 -0
- package/docs/adoption/README.md +128 -0
- package/docs/adoption/openflow-release-hardening.md +61 -0
- package/docs/benchmark-openflow-vs-gstack-superpowers-openspec-2026-05-25.md +81 -0
- package/docs/compatibility.md +52 -0
- package/docs/competitive-benchmark.md +108 -0
- package/docs/corps-operator-guide.md +75 -0
- package/docs/demo-proof.md +126 -0
- package/docs/examples-gallery.md +115 -0
- package/docs/fixtures/tracker-item.json +7 -0
- package/docs/goal-vs-codex.md +129 -0
- package/docs/gstack-comparison.md +88 -0
- package/docs/install-upgrade.md +133 -0
- package/docs/integrations.md +49 -0
- package/docs/launch-demo.md +123 -0
- package/docs/launch-dossier.md +180 -0
- package/docs/methodology.md +149 -0
- package/docs/npm-publish-handoff.md +96 -0
- package/docs/openspec-migration.md +106 -0
- package/docs/public-benchmark.md +68 -0
- package/docs/public-release.md +145 -0
- package/docs/quality-assessment.md +139 -0
- package/docs/quickstart.md +156 -0
- package/docs/reviewer-guide.md +43 -0
- package/docs/spec-kit-benchmark.md +73 -0
- package/docs/super-assistant-comparison.md +106 -0
- package/docs/superpowers-comparison.md +82 -0
- package/docs/team-adoption.md +123 -0
- package/docs/tooling-matrix.md +91 -0
- package/docs/walkthrough.md +76 -0
- package/docs/workflow-manual.md +1130 -0
- package/package.json +83 -4
- package/prompts/pencil-draft.md +46 -0
- package/prompts/pencil-refine.md +46 -0
- package/prompts/phase-artifact.md +24 -0
- package/prompts/phase-execution.md +32 -0
- package/schemas/competitor-reconstruction-contract.schema.json +91 -0
- package/schemas/llm-design-review-aggregate.schema.json +83 -0
- package/schemas/visual-benchmark.schema.json +978 -0
- package/schemas/workflow.schema.json +100 -0
- package/scripts/run-tests.mjs +23 -0
- package/scripts/takein_digest.py +139 -0
- package/specs/README.md +118 -0
- package/specs/architecture.md +36 -0
- package/specs/product.md +33 -0
- package/specs/workflow.md +349 -0
- package/src/agent_team/atoms/codex-app-bridge.js +8 -0
- package/src/agent_team/atoms/design/competitor-reconstruction-review.js +44 -0
- package/src/agent_team/atoms/design/contract-freeze.js +8 -0
- package/src/agent_team/atoms/design/generation-contract.js +442 -0
- package/src/agent_team/atoms/design/reconstruction-pack.js +246 -0
- package/src/agent_team/atoms/design/reference-surface-lock.js +49 -0
- package/src/agent_team/atoms/patch-challenge.js +19 -0
- package/src/agent_team/atoms/pencil/accept.js +9 -0
- package/src/agent_team/atoms/pencil/draft.js +365 -0
- package/src/agent_team/atoms/pencil/refine.js +229 -0
- package/src/agent_team/atoms/phase-run-dispatch.js +8 -0
- package/src/agent_team/atoms/team-run.js +380 -0
- package/src/agent_team/atoms/visual/aesthetic-review.js +231 -0
- package/src/agent_team/atoms/visual/benchmark-repair-plan.js +291 -0
- package/src/agent_team/atoms/visual/benchmark.js +1652 -0
- package/src/agent_team/atoms/visual/design-selection.js +23 -0
- package/src/agent_team/atoms/visual/direction-synthesis.js +124 -0
- package/src/agent_team/atoms/visual/dom-rect-export.js +129 -0
- package/src/agent_team/atoms/visual/image-diff.js +186 -0
- package/src/agent_team/atoms/visual/layout-competition.js +8 -0
- package/src/agent_team/atoms/visual/page-evidence-capture.js +806 -0
- package/src/agent_team/atoms/visual/playwright-runtime.js +8 -0
- package/src/agent_team/atoms/visual/review-aggregate.js +87 -0
- package/src/agent_team/atoms/visual/review.js +77 -0
- package/src/agent_team/atoms/visual/visual-report.js +197 -0
- package/src/agent_team/atoms/visual/visual-token-export.js +417 -0
- package/src/agent_team/common.js +311 -0
- package/src/core/atoms/agent-invoke.js +684 -0
- package/src/core/atoms/event/ingest.js +30 -0
- package/src/core/atoms/experience/digest.js +55 -0
- package/src/core/atoms/gate/artifact-verify.js +83 -0
- package/src/core/atoms/gate/competitor-contract-validate.js +119 -0
- package/src/core/atoms/gate/evidence-collect.js +64 -0
- package/src/core/atoms/gate/payload-from-phase-run.js +78 -0
- package/src/core/atoms/gate/visual-benchmark-validate.js +183 -0
- package/src/core/atoms/gate/workbench.js +79 -0
- package/src/core/atoms/intake/classify.js +15 -0
- package/src/core/atoms/intake/design-skill-select.js +16 -0
- package/src/core/atoms/intake/preview-or-refine.js +19 -0
- package/src/core/atoms/intake/recommend.js +20 -0
- package/src/core/atoms/intake/research-same-category.js +19 -0
- package/src/core/atoms/intervention/create.js +30 -0
- package/src/core/atoms/memory/manage.js +27 -0
- package/src/core/atoms/memory/mount.js +13 -0
- package/src/core/atoms/mission/create.js +19 -0
- package/src/core/atoms/mission/list.js +14 -0
- package/src/core/atoms/mission/show.js +26 -0
- package/src/core/atoms/notify/checkpoint.js +46 -0
- package/src/core/atoms/phase-run/complete.js +42 -0
- package/src/core/atoms/phase-run/fail.js +40 -0
- package/src/core/atoms/phase-run/persist.js +39 -0
- package/src/core/atoms/phase-run/start.js +30 -0
- package/src/core/atoms/plugin/run.js +76 -0
- package/src/core/atoms/policy/gate.js +90 -0
- package/src/core/atoms/policy/resolve.js +11 -0
- package/src/core/atoms/storage/sync.js +57 -0
- package/src/core/atoms/workflow/advance.js +25 -0
- package/src/core/atoms/workflow/guard.js +127 -0
- package/src/core/atoms/workflow/state-load.js +37 -0
- package/src/core/bindings.js +37 -0
- package/src/core/change-artifacts.js +134 -0
- package/src/core/competitive-score.js +265 -0
- package/src/core/concurrency-policy.js +35 -0
- package/src/core/execution-log.js +108 -0
- package/src/core/intake-recommendation.js +134 -0
- package/src/core/memory-state.js +62 -0
- package/src/core/mission-state.js +780 -0
- package/src/core/pending-gates.js +66 -0
- package/src/core/phase-cost.js +116 -0
- package/src/core/phase-run-read.js +27 -0
- package/src/core/policy-overlay.js +180 -0
- package/src/core/state-store.js +201 -0
- package/src/core/workflow-executor.js +1014 -0
- package/src/core/workflow-integrity.js +111 -0
- package/src/core/workflow-loader.js +163 -0
- package/src/core/workflow-rerun.js +84 -0
- package/src/core/workflow-state.js +119 -0
- package/src/server.js +1307 -0
- package/workflows/corps.yaml +481 -0
- package/workflows/yolo.yaml +241 -0
- package/xflow/README.md +76 -0
- package/xflow/aha/SKILL.md +82 -0
- package/xflow/aha/scripts/append_aha.py +50 -0
- package/xflow/archive/scripts/archive_change.py +338 -0
- package/xflow/archive/templates/merge-design.md +54 -0
- package/xflow/archive/templates/merge-workflow.md +26 -0
- package/xflow/atoms/__init__.py +6 -0
- package/xflow/atoms/a1_issue_create_or_patch.py +70 -0
- package/xflow/atoms/a2_branch_create_and_link.py +42 -0
- package/xflow/atoms/a3_repo_doctor.py +11 -0
- package/xflow/atoms/a4_project_set_status.py +11 -0
- package/xflow/atoms/a5_archive_commit_push_close.py +11 -0
- package/xflow/atoms/a6_pr_create.py +72 -0
- package/xflow/atoms/b1_change_scaffold.py +11 -0
- package/xflow/atoms/b2_status_read.py +16 -0
- package/xflow/atoms/b2b_status_write.py +20 -0
- package/xflow/atoms/b3_status_transition.py +27 -0
- package/xflow/atoms/b4_handoff_scaffold_or_refresh.py +11 -0
- package/xflow/atoms/b5_aha_append.py +11 -0
- package/xflow/atoms/b6_takein_digest.py +11 -0
- package/xflow/atoms/b6b_lookback_digest.py +11 -0
- package/xflow/atoms/b7_change_mission_bind.py +43 -0
- package/xflow/atoms/c1_mem.py +54 -0
- package/xflow/atoms/e6_gate_local_precheck.py +23 -0
- package/xflow/atoms/h1_design_lite_gate.py +74 -0
- package/xflow/atoms/i6a_tdd_run.py +107 -0
- package/xflow/atoms/i6b_tdd_proof_validate.py +184 -0
- package/xflow/atoms/i6c_tdd_quality_review.py +372 -0
- package/xflow/atoms/j1_tests_run.py +129 -0
- package/xflow/atoms/j4a_spec_consistency_structural.py +105 -0
- package/xflow/atoms/j4b_spec_delta_review.py +316 -0
- package/xflow/atoms/j4c_openspec_migration_map.py +121 -0
- package/xflow/atoms/j4d_spec_clarify_gate.py +373 -0
- package/xflow/atoms/j6_cr_review.py +443 -0
- package/xflow/atoms/k1_artifacts_complete_check.py +39 -0
- package/xflow/atoms/k2_merge_snippets_apply.py +11 -0
- package/xflow/atoms/k3_handoff_refresh.py +14 -0
- package/xflow/atoms/k4_mem_lesson_persist.py +65 -0
- package/xflow/atoms/k5_archive_publish.py +21 -0
- package/xflow/atoms/k6_aha_merge.py +42 -0
- package/xflow/atoms/tdd_lib.py +956 -0
- package/xflow/corps/SKILL.md +109 -0
- package/xflow/exec/scripts/set_issue_in_progress.py +159 -0
- package/xflow/goal/SKILL.md +150 -0
- package/xflow/handoff/SKILL.md +95 -0
- package/xflow/handoff/scripts/scaffold_handoff.py +304 -0
- package/xflow/lookback/SKILL.md +66 -0
- package/xflow/lookback/scripts/lookback_digest.py +46 -0
- package/xflow/openissue/scripts/open_issue_flow.py +438 -0
- package/xflow/plan/SKILL.md +47 -0
- package/xflow/ralph/SKILL.md +123 -0
- package/xflow/scripts/check_installed_xflow_skill_sync.sh +44 -0
- package/xflow/scripts/common.py +483 -0
- package/xflow/scripts/doctor.py +278 -0
- package/xflow/scripts/sync_installed_xflow_skill.sh +134 -0
- package/xflow/takein/SKILL.md +81 -0
- package/xflow/takein/scripts/takein_digest.py +88 -0
- package/xflow/yolo/SKILL.md +75 -0
- package/xflow/yolo/scripts/scaffold_specs_change.py +673 -0
- package/xflow/yolo/scripts/yolo_gate.py +312 -0
- 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,107 @@
|
|
|
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, snapshot_scope = tdd_lib.write_snapshot(
|
|
68
|
+
project, args.change_id, args.phase, args.attempt, command=args.test_command
|
|
69
|
+
)
|
|
70
|
+
proof = {
|
|
71
|
+
'phase': args.phase,
|
|
72
|
+
'attempt': args.attempt,
|
|
73
|
+
'command': args.test_command,
|
|
74
|
+
'exit_code': exit_code,
|
|
75
|
+
'passed': passed,
|
|
76
|
+
'expected': expect,
|
|
77
|
+
'expectation_met': expectation_met(expect, False if timed_out else passed),
|
|
78
|
+
'stdout_tail': stdout[-2000:] if stdout else '',
|
|
79
|
+
'stderr_tail': stderr[-1000:] if stderr else '',
|
|
80
|
+
'elapsed_s': round(elapsed, 2),
|
|
81
|
+
'output_sha256': tdd_lib.output_digest(stdout, stderr),
|
|
82
|
+
'git_head': tdd_lib.git_head(project),
|
|
83
|
+
'file_hashes': file_hashes,
|
|
84
|
+
'snapshot_scope': snapshot_scope,
|
|
85
|
+
'failure_kind': 'timeout' if timed_out else tdd_lib.classify_output(combined, passed),
|
|
86
|
+
'recorded_at': datetime.now(timezone.utc).isoformat(),
|
|
87
|
+
}
|
|
88
|
+
proof['binding'] = tdd_lib.compute_binding(proof)
|
|
89
|
+
|
|
90
|
+
proof_file = os.path.join(proof_dir, f'{args.phase}-{args.attempt}.json')
|
|
91
|
+
with open(proof_file, 'w') as f:
|
|
92
|
+
json.dump(proof, f, indent=2)
|
|
93
|
+
f.write('\n')
|
|
94
|
+
|
|
95
|
+
print(json.dumps({
|
|
96
|
+
'ok': proof['expectation_met'],
|
|
97
|
+
'proof_file': proof_file,
|
|
98
|
+
'exit_code': proof['exit_code'],
|
|
99
|
+
'expected': expect,
|
|
100
|
+
'passed': proof['passed'],
|
|
101
|
+
'failure_kind': proof['failure_kind'],
|
|
102
|
+
'binding': proof['binding'],
|
|
103
|
+
}))
|
|
104
|
+
sys.exit(0 if proof['expectation_met'] else 1)
|
|
105
|
+
|
|
106
|
+
if __name__ == '__main__':
|
|
107
|
+
main()
|