@bx-h/meta-flow 0.1.0

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 (87) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +172 -0
  4. package/bin/meta-flow.js +7 -0
  5. package/examples/sample-task/adjudication-report.json +14 -0
  6. package/examples/sample-task/final-report.md +26 -0
  7. package/examples/sample-task/goal-contract.json +44 -0
  8. package/examples/sample-task/milestone-plan.json +31 -0
  9. package/examples/sample-task/milestones/M1/direction-evaluation.json +16 -0
  10. package/examples/sample-task/milestones/M1/task-list.json +41 -0
  11. package/examples/sample-task/milestones/M1/tasks/T1/execution-report.json +25 -0
  12. package/examples/sample-task/milestones/M1/tasks/T1/task-spec.json +37 -0
  13. package/examples/sample-task/milestones/M1/tasks/T1/verification-report.json +23 -0
  14. package/examples/sample-task/proposal-summary.md +21 -0
  15. package/examples/sample-task/proposal.md +29 -0
  16. package/examples/sample-task/questioning-report.json +31 -0
  17. package/examples/sample-task/raw-request.md +1 -0
  18. package/examples/sample-task/review-aggregate.json +34 -0
  19. package/examples/sample-task/reviews/product-reviewer.json +16 -0
  20. package/examples/sample-task/reviews/risk-reviewer.json +16 -0
  21. package/examples/sample-task/reviews/technical-reviewer.json +16 -0
  22. package/examples/sample-task/reviews/verification-reviewer.json +15 -0
  23. package/examples/sample-task/state.json +35 -0
  24. package/marketplace/marketplace.json +20 -0
  25. package/package.json +50 -0
  26. package/plugin/.codex-plugin/plugin.json +36 -0
  27. package/plugin/agent-templates/adjudicator.toml +26 -0
  28. package/plugin/agent-templates/direction-evaluator.toml +18 -0
  29. package/plugin/agent-templates/executor.toml +21 -0
  30. package/plugin/agent-templates/final-summarizer.toml +17 -0
  31. package/plugin/agent-templates/planner.toml +18 -0
  32. package/plugin/agent-templates/product-reviewer.toml +15 -0
  33. package/plugin/agent-templates/proposal-summarizer.toml +17 -0
  34. package/plugin/agent-templates/questioner.toml +22 -0
  35. package/plugin/agent-templates/researcher-proposer.toml +18 -0
  36. package/plugin/agent-templates/result-verifier.toml +20 -0
  37. package/plugin/agent-templates/risk-reviewer.toml +14 -0
  38. package/plugin/agent-templates/task-decomposer.toml +19 -0
  39. package/plugin/agent-templates/technical-reviewer.toml +14 -0
  40. package/plugin/agent-templates/verification-reviewer.toml +15 -0
  41. package/plugin/assets/icon.svg +10 -0
  42. package/plugin/assets/screenshot-placeholder.md +3 -0
  43. package/plugin/scripts/_common.py +95 -0
  44. package/plugin/scripts/aggregate_reviews.py +99 -0
  45. package/plugin/scripts/new_task.py +78 -0
  46. package/plugin/scripts/status.py +95 -0
  47. package/plugin/scripts/validate_adjudication.py +47 -0
  48. package/plugin/scripts/validate_goal_contract.py +56 -0
  49. package/plugin/scripts/validate_milestone_plan.py +49 -0
  50. package/plugin/scripts/validate_task_list.py +59 -0
  51. package/plugin/scripts/validate_task_verification.py +49 -0
  52. package/plugin/skills/meta-flow/SKILL.md +159 -0
  53. package/plugin/skills/meta-flow/references/adjudication-policy.md +30 -0
  54. package/plugin/skills/meta-flow/references/direction-evaluation-policy.md +24 -0
  55. package/plugin/skills/meta-flow/references/execution-policy.md +35 -0
  56. package/plugin/skills/meta-flow/references/review-rubric.md +28 -0
  57. package/plugin/skills/meta-flow/references/role-contracts.md +29 -0
  58. package/plugin/templates/adjudication-report.json +11 -0
  59. package/plugin/templates/direction-evaluation.json +16 -0
  60. package/plugin/templates/final-report.md +11 -0
  61. package/plugin/templates/goal-contract.json +20 -0
  62. package/plugin/templates/milestone-plan.json +15 -0
  63. package/plugin/templates/proposal-summary.md +11 -0
  64. package/plugin/templates/proposal.md +17 -0
  65. package/plugin/templates/questioning-report.json +15 -0
  66. package/plugin/templates/raw-request.md +3 -0
  67. package/plugin/templates/review-aggregate.json +14 -0
  68. package/plugin/templates/reviewer-report.json +10 -0
  69. package/plugin/templates/state.json +23 -0
  70. package/plugin/templates/task-execution-report.json +13 -0
  71. package/plugin/templates/task-list.json +20 -0
  72. package/plugin/templates/task-spec.json +16 -0
  73. package/plugin/templates/task-verification-report.json +13 -0
  74. package/src/cli/commands/doctor.js +171 -0
  75. package/src/cli/commands/install.js +120 -0
  76. package/src/cli/commands/print_paths.js +20 -0
  77. package/src/cli/commands/uninstall.js +56 -0
  78. package/src/cli/commands/verify.js +197 -0
  79. package/src/cli/index.js +53 -0
  80. package/src/cli/lib/agents.js +89 -0
  81. package/src/cli/lib/args.js +49 -0
  82. package/src/cli/lib/codex_config.js +106 -0
  83. package/src/cli/lib/fs_safe.js +142 -0
  84. package/src/cli/lib/logger.js +23 -0
  85. package/src/cli/lib/marketplace.js +72 -0
  86. package/src/cli/lib/paths.js +37 -0
  87. package/src/cli/lib/plugin.js +57 -0
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import os
6
+ import re
7
+ from datetime import datetime, timezone
8
+ from pathlib import Path
9
+
10
+ from _common import write_json
11
+
12
+
13
+ ROOT = Path(os.environ.get("META_FLOW_ROOT", Path.cwd() / ".meta-flow"))
14
+ DEFAULT_TASKS_ROOT = ROOT / "tasks"
15
+
16
+
17
+ def slugify(text: str) -> str:
18
+ slug = re.sub(r"[^A-Za-z0-9]+", "-", text.strip().lower()).strip("-")
19
+ return slug[:48] or "task"
20
+
21
+
22
+ def parse_args() -> argparse.Namespace:
23
+ parser = argparse.ArgumentParser(description="Create a new meta-flow task directory.")
24
+ parser.add_argument("raw_request", nargs="?", help="Raw user request text.")
25
+ parser.add_argument("--raw-request-file", type=Path, help="File containing raw request text.")
26
+ parser.add_argument("--task-id", help="Override generated task id.")
27
+ parser.add_argument("--tasks-root", type=Path, default=DEFAULT_TASKS_ROOT, help="Task root directory.")
28
+ return parser.parse_args()
29
+
30
+
31
+ def main() -> int:
32
+ args = parse_args()
33
+ if args.raw_request and args.raw_request_file:
34
+ raise SystemExit("Use either raw_request or --raw-request-file, not both.")
35
+ if args.raw_request_file:
36
+ raw_request = args.raw_request_file.read_text(encoding="utf-8")
37
+ elif args.raw_request:
38
+ raw_request = args.raw_request
39
+ else:
40
+ raise SystemExit("A raw request is required.")
41
+
42
+ now = datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
43
+ task_id = args.task_id or f"{datetime.now(timezone.utc).strftime('%Y%m%d-%H%M%S')}-{slugify(raw_request)}"
44
+ task_dir = args.tasks_root / task_id
45
+ task_dir.mkdir(parents=True, exist_ok=False)
46
+
47
+ state = {
48
+ "task_id": task_id,
49
+ "phase": "INTAKE",
50
+ "created_at": now,
51
+ "updated_at": now,
52
+ "proposal_review_round": 0,
53
+ "direction_adjustment_round": 0,
54
+ "current_milestone_id": None,
55
+ "current_task_id": None,
56
+ "max_proposal_rework_rounds": 3,
57
+ "max_task_repair_rounds": 2,
58
+ "max_direction_adjustment_rounds": 2,
59
+ "status": "active",
60
+ "last_route_decision": "Task initialized from raw request.",
61
+ "history": [
62
+ {
63
+ "at": now,
64
+ "from_phase": "NONE",
65
+ "to_phase": "INTAKE",
66
+ "reason": "Raw request captured.",
67
+ }
68
+ ],
69
+ }
70
+
71
+ write_json(task_dir / "state.json", state)
72
+ (task_dir / "raw-request.md").write_text(raw_request.rstrip() + "\n", encoding="utf-8")
73
+ print(task_dir)
74
+ return 0
75
+
76
+
77
+ if __name__ == "__main__":
78
+ raise SystemExit(main())
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import os
6
+ from pathlib import Path
7
+
8
+ from _common import load_json
9
+
10
+
11
+ ROOT = Path(os.environ.get("META_FLOW_ROOT", Path.cwd() / ".meta-flow"))
12
+
13
+
14
+ NEXT_ACTION_BY_PHASE = {
15
+ "INTAKE": "Run questioner and create questioning-report.json.",
16
+ "QUESTIONING": "Resolve blocking questions or record assumptions.",
17
+ "GOAL_CONTRACT_DRAFTED": "Validate goal-contract.json, then run researcher_proposer.",
18
+ "RESEARCH_AND_PROPOSAL": "Produce proposal.md and send it to reviewers.",
19
+ "PROPOSAL_REVIEW": "Run aggregate_reviews.py, then invoke adjudicator.",
20
+ "ADJUDICATION": "Route according to adjudication-report.json.",
21
+ "PROPOSAL_REWORK": "Rework proposal, respecting proposal_review_round limit.",
22
+ "PROPOSAL_SUMMARY": "Summarize proposal for user confirmation.",
23
+ "USER_PROPOSAL_CONFIRMATION": "Ask user to accept or reject the proposal.",
24
+ "PROPOSAL_ACCEPTED": "Run planner.",
25
+ "PLANNING": "Create milestone-plan.json.",
26
+ "USER_PLAN_CONFIRMATION": "Ask user to confirm milestone-plan.json.",
27
+ "MILESTONE_SELECTED": "Run task_decomposer for current milestone.",
28
+ "TASK_DECOMPOSITION": "Validate task-list.json and select the next concrete task.",
29
+ "TASK_EXECUTION": "Run executor for current concrete task.",
30
+ "TASK_VERIFICATION": "Run result_verifier for current concrete task.",
31
+ "TASK_REPAIR": "Return to executor with minimal repair instructions.",
32
+ "MILESTONE_COMPLETED": "Run direction_evaluator.",
33
+ "DIRECTION_EVALUATION": "Route based on direction-evaluation.json.",
34
+ "CONTINUE_NEXT_MILESTONE": "Select the next milestone.",
35
+ "REPLAN": "Return to planner or task_decomposer.",
36
+ "GOAL_ADJUSTMENT_REQUIRED": "Ask user to confirm contract patch before returning to proposal phase.",
37
+ "FINAL_SUMMARY": "Run final_summarizer.",
38
+ "USER_FINAL_CONFIRMATION": "Ask user to confirm final result.",
39
+ "DONE": "No next action.",
40
+ "BLOCKED": "Ask user for a decision or unblock input.",
41
+ }
42
+
43
+
44
+ def parse_args() -> argparse.Namespace:
45
+ parser = argparse.ArgumentParser(description="Print current meta-flow task status.")
46
+ parser.add_argument("task", type=Path, help="Task directory path, or task id under .meta-flow/tasks.")
47
+ return parser.parse_args()
48
+
49
+
50
+ def resolve_task_dir(value: Path) -> Path:
51
+ if value.exists():
52
+ return value
53
+ candidate = ROOT / "tasks" / str(value)
54
+ if candidate.exists():
55
+ return candidate
56
+ raise SystemExit(f"Task directory not found: {value}")
57
+
58
+
59
+ def collect_blocked_issues(task_dir: Path) -> list[str]:
60
+ issues: list[str] = []
61
+ for path in task_dir.rglob("*.json"):
62
+ data = load_json(path)
63
+ if isinstance(data, dict):
64
+ if data.get("status") == "blocked" and isinstance(data.get("summary"), str):
65
+ issues.append(f"{path.name}: {data['summary']}")
66
+ if data.get("decision") == "block":
67
+ reason = data.get("minimal_repair_instructions") or data.get("block_reason") or data.get("rationale")
68
+ if reason:
69
+ issues.append(f"{path.name}: {reason}")
70
+ return issues
71
+
72
+
73
+ def main() -> int:
74
+ task_dir = resolve_task_dir(parse_args().task)
75
+ state = load_json(task_dir / "state.json")
76
+ phase = state.get("phase", "UNKNOWN")
77
+ print(f"task: {state.get('task_id', task_dir.name)}")
78
+ print(f"status: {state.get('status', 'unknown')}")
79
+ print(f"current phase: {phase}")
80
+ print(f"current milestone: {state.get('current_milestone_id')}")
81
+ print(f"current task: {state.get('current_task_id')}")
82
+ print(f"last route decision: {state.get('last_route_decision', '')}")
83
+ blocked = collect_blocked_issues(task_dir)
84
+ print("blocked issues:")
85
+ if blocked:
86
+ for issue in blocked:
87
+ print(f"- {issue}")
88
+ else:
89
+ print("- none")
90
+ print(f"next recommended action: {NEXT_ACTION_BY_PHASE.get(phase, 'Inspect state.json and route manually.')}")
91
+ return 0
92
+
93
+
94
+ if __name__ == "__main__":
95
+ raise SystemExit(main())
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ from pathlib import Path
6
+
7
+ from _common import fail, load_json, ok, require_dict, require_non_empty_string
8
+
9
+
10
+ ALLOWED_NEXT_PHASES = {
11
+ "accept": {"PROPOSAL_SUMMARY", "USER_PROPOSAL_CONFIRMATION", "PROPOSAL_ACCEPTED"},
12
+ "revise_proposal": {"PROPOSAL_REWORK", "RESEARCH_AND_PROPOSAL", "PROPOSAL_REVIEW"},
13
+ "ask_user": {"QUESTIONING", "USER_PROPOSAL_CONFIRMATION", "USER_PLAN_CONFIRMATION", "USER_FINAL_CONFIRMATION", "GOAL_ADJUSTMENT_REQUIRED"},
14
+ "adjust_goal": {"GOAL_ADJUSTMENT_REQUIRED", "QUESTIONING", "RESEARCH_AND_PROPOSAL"},
15
+ "replan": {"REPLAN", "PLANNING", "TASK_DECOMPOSITION"},
16
+ "block": {"BLOCKED", "ADJUDICATION"},
17
+ }
18
+
19
+
20
+ def parse_args() -> argparse.Namespace:
21
+ parser = argparse.ArgumentParser(description="Validate a meta-flow adjudication-report.json file.")
22
+ parser.add_argument("path", type=Path, nargs="?", default=Path("adjudication-report.json"))
23
+ return parser.parse_args()
24
+
25
+
26
+ def main() -> int:
27
+ data = require_dict(load_json(parse_args().path), "adjudication report", errors := [])
28
+ require_non_empty_string(data, "task_id", errors)
29
+ require_non_empty_string(data, "source", errors)
30
+ require_non_empty_string(data, "rationale", errors)
31
+ require_non_empty_string(data, "instructions_for_next_agent", errors)
32
+ decision = data.get("decision")
33
+ next_phase = data.get("next_phase")
34
+ if decision not in ALLOWED_NEXT_PHASES:
35
+ errors.append(f"decision must be one of {sorted(ALLOWED_NEXT_PHASES)}")
36
+ elif next_phase not in ALLOWED_NEXT_PHASES[decision]:
37
+ errors.append(f"next_phase {next_phase!r} is not consistent with decision {decision!r}")
38
+ if not isinstance(data.get("requires_user_confirmation"), bool):
39
+ errors.append("requires_user_confirmation must be a boolean")
40
+ if errors:
41
+ fail(errors)
42
+ ok(f"OK: adjudication valid ({decision} -> {next_phase}).")
43
+ return 0
44
+
45
+
46
+ if __name__ == "__main__":
47
+ raise SystemExit(main())
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ from pathlib import Path
6
+
7
+ from _common import fail, load_json, ok, require_dict, require_list, require_non_empty_string
8
+
9
+
10
+ VALID_METHODS = {"manual", "test", "lint", "typecheck", "script", "review", "user_confirmation"}
11
+ VALID_RISKS = {"low", "medium", "high"}
12
+
13
+
14
+ def parse_args() -> argparse.Namespace:
15
+ parser = argparse.ArgumentParser(description="Validate a meta-flow goal-contract.json file.")
16
+ parser.add_argument("path", type=Path, nargs="?", default=Path("goal-contract.json"))
17
+ return parser.parse_args()
18
+
19
+
20
+ def main() -> int:
21
+ data = require_dict(load_json(parse_args().path), "goal contract", errors := [])
22
+ require_non_empty_string(data, "refined_goal", errors)
23
+ require_non_empty_string(data, "problem_boundary", errors)
24
+ require_non_empty_string(data, "success_definition", errors)
25
+
26
+ non_goals = require_list(data.get("non_goals"), "non_goals", errors)
27
+ constraints = require_list(data.get("constraints"), "constraints", errors)
28
+ criteria = require_list(data.get("acceptance_criteria"), "acceptance_criteria", errors)
29
+ if not criteria:
30
+ errors.append("acceptance_criteria must not be empty")
31
+ for idx, item in enumerate(criteria, start=1):
32
+ item_obj = require_dict(item, f"acceptance_criteria[{idx}]", errors)
33
+ require_non_empty_string(item_obj, "id", errors)
34
+ require_non_empty_string(item_obj, "criterion", errors)
35
+ require_non_empty_string(item_obj, "evidence_required", errors)
36
+ method = item_obj.get("verification_method")
37
+ if method not in VALID_METHODS:
38
+ errors.append(f"acceptance_criteria[{idx}].verification_method must be one of {sorted(VALID_METHODS)}")
39
+
40
+ if data.get("risk_level") not in VALID_RISKS:
41
+ errors.append(f"risk_level must be one of {sorted(VALID_RISKS)}")
42
+ if "non_goals" not in data:
43
+ errors.append("non_goals key must exist")
44
+ if "constraints" not in data:
45
+ errors.append("constraints key must exist")
46
+ if not isinstance(data.get("requires_user_confirmation"), bool):
47
+ errors.append("requires_user_confirmation must be a boolean")
48
+
49
+ if errors:
50
+ fail(errors)
51
+ ok(f"OK: goal contract valid ({len(non_goals)} non-goals, {len(constraints)} constraints, {len(criteria)} criteria).")
52
+ return 0
53
+
54
+
55
+ if __name__ == "__main__":
56
+ raise SystemExit(main())
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ from pathlib import Path
6
+
7
+ from _common import fail, load_json, ok, require_dict, require_list, require_non_empty_string, warn
8
+
9
+
10
+ VALID_RISKS = {"low", "medium", "high"}
11
+ VALID_STATUSES = {"pending", "in_progress", "done", "blocked"}
12
+
13
+
14
+ def parse_args() -> argparse.Namespace:
15
+ parser = argparse.ArgumentParser(description="Validate a meta-flow milestone-plan.json file.")
16
+ parser.add_argument("path", type=Path, nargs="?", default=Path("milestone-plan.json"))
17
+ return parser.parse_args()
18
+
19
+
20
+ def main() -> int:
21
+ data = require_dict(load_json(parse_args().path), "milestone plan", errors := [])
22
+ milestones = require_list(data.get("milestones"), "milestones", errors)
23
+ if not milestones:
24
+ errors.append("milestones must not be empty")
25
+ warnings: list[str] = []
26
+ for idx, milestone in enumerate(milestones, start=1):
27
+ item = require_dict(milestone, f"milestones[{idx}]", errors)
28
+ for key in ("id", "objective"):
29
+ require_non_empty_string(item, key, errors)
30
+ for key in ("scope", "acceptance_checks"):
31
+ value = require_list(item.get(key), f"milestones[{idx}].{key}", errors)
32
+ if key == "acceptance_checks" and not value:
33
+ errors.append(f"milestones[{idx}].acceptance_checks must not be empty")
34
+ if key == "scope" and len(value) > 7:
35
+ warnings.append(f"milestone {item.get('id', idx)} has scope larger than 7 items")
36
+ if item.get("risk") not in VALID_RISKS:
37
+ errors.append(f"milestones[{idx}].risk must be one of {sorted(VALID_RISKS)}")
38
+ if item.get("status") not in VALID_STATUSES:
39
+ errors.append(f"milestones[{idx}].status must be one of {sorted(VALID_STATUSES)}")
40
+ if warnings:
41
+ warn(warnings)
42
+ if errors:
43
+ fail(errors)
44
+ ok(f"OK: milestone plan valid ({len(milestones)} milestones).")
45
+ return 0
46
+
47
+
48
+ if __name__ == "__main__":
49
+ raise SystemExit(main())
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ from pathlib import Path
6
+
7
+ from _common import fail, load_json, ok, require_dict, require_list, require_non_empty_string, warn
8
+
9
+
10
+ VALID_RISKS = {"low", "medium", "high"}
11
+ VALID_STATUSES = {"pending", "in_progress", "passed", "failed", "blocked"}
12
+
13
+
14
+ def parse_args() -> argparse.Namespace:
15
+ parser = argparse.ArgumentParser(description="Validate a meta-flow task-list.json file.")
16
+ parser.add_argument("path", type=Path, nargs="?", default=Path("task-list.json"))
17
+ return parser.parse_args()
18
+
19
+
20
+ def main() -> int:
21
+ data = require_dict(load_json(parse_args().path), "task list", errors := [])
22
+ require_non_empty_string(data, "task_id", errors)
23
+ require_non_empty_string(data, "milestone_id", errors)
24
+ tasks = require_list(data.get("tasks"), "tasks", errors)
25
+ if not tasks:
26
+ errors.append("tasks must not be empty")
27
+ warnings: list[str] = []
28
+ for idx, task in enumerate(tasks, start=1):
29
+ item = require_dict(task, f"tasks[{idx}]", errors)
30
+ for key in ("id", "objective"):
31
+ require_non_empty_string(item, key, errors)
32
+ for key in ("expected_outputs", "acceptance_checks", "dependencies"):
33
+ value = require_list(item.get(key), f"tasks[{idx}].{key}", errors)
34
+ if key in {"expected_outputs", "acceptance_checks"} and not value:
35
+ errors.append(f"tasks[{idx}].{key} must not be empty")
36
+ allowed_files = require_list(item.get("allowed_files"), f"tasks[{idx}].allowed_files", errors)
37
+ require_list(item.get("forbidden_files"), f"tasks[{idx}].forbidden_files", errors)
38
+ if not allowed_files:
39
+ warnings.append(f"task {item.get('id', idx)} has empty allowed_files")
40
+ if len(allowed_files) > 10:
41
+ warnings.append(f"task {item.get('id', idx)} may be too broad: more than 10 allowed files")
42
+ if len(item.get("expected_outputs", [])) > 6:
43
+ warnings.append(f"task {item.get('id', idx)} may be too broad: more than 6 expected outputs")
44
+ if item.get("risk") not in VALID_RISKS:
45
+ errors.append(f"tasks[{idx}].risk must be one of {sorted(VALID_RISKS)}")
46
+ if item.get("status") not in VALID_STATUSES:
47
+ errors.append(f"tasks[{idx}].status must be one of {sorted(VALID_STATUSES)}")
48
+ if not isinstance(item.get("repair_attempts", 0), int):
49
+ errors.append(f"tasks[{idx}].repair_attempts must be an integer")
50
+ if warnings:
51
+ warn(warnings)
52
+ if errors:
53
+ fail(errors)
54
+ ok(f"OK: task list valid ({len(tasks)} concrete tasks).")
55
+ return 0
56
+
57
+
58
+ if __name__ == "__main__":
59
+ raise SystemExit(main())
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ from pathlib import Path
6
+
7
+ from _common import fail, load_json, ok, require_dict, require_list, require_non_empty_string
8
+
9
+
10
+ VALID_DECISIONS = {"pass", "revise", "block"}
11
+
12
+
13
+ def parse_args() -> argparse.Namespace:
14
+ parser = argparse.ArgumentParser(description="Validate a meta-flow task-verification-report.json file.")
15
+ parser.add_argument("path", type=Path, nargs="?", default=Path("task-verification-report.json"))
16
+ return parser.parse_args()
17
+
18
+
19
+ def main() -> int:
20
+ data = require_dict(load_json(parse_args().path), "task verification report", errors := [])
21
+ for key in ("task_id", "milestone_id", "concrete_task_id"):
22
+ require_non_empty_string(data, key, errors)
23
+ decision = data.get("decision")
24
+ if decision not in VALID_DECISIONS:
25
+ errors.append(f"decision must be one of {sorted(VALID_DECISIONS)}")
26
+ failed = require_list(data.get("failed_checks"), "failed_checks", errors)
27
+ new_findings = require_list(data.get("new_findings"), "new_findings", errors)
28
+ require_list(data.get("checks_run"), "checks_run", errors)
29
+ require_list(data.get("passed_checks"), "passed_checks", errors)
30
+ require_list(data.get("evidence_refs"), "evidence_refs", errors)
31
+
32
+ repair = data.get("minimal_repair_instructions", "")
33
+ block_reason = data.get("block_reason", "")
34
+ if decision == "pass" and failed:
35
+ errors.append("failed_checks must be empty when decision is pass")
36
+ if decision in {"revise", "block"} and not str(repair).strip() and not str(block_reason).strip():
37
+ errors.append("revise/block decisions require minimal_repair_instructions or block_reason")
38
+ if data.get("should_trigger_direction_evaluation") is True and not new_findings:
39
+ errors.append("new_findings must not be empty when should_trigger_direction_evaluation is true")
40
+ if not isinstance(data.get("should_trigger_direction_evaluation"), bool):
41
+ errors.append("should_trigger_direction_evaluation must be a boolean")
42
+ if errors:
43
+ fail(errors)
44
+ ok(f"OK: task verification valid ({decision}).")
45
+ return 0
46
+
47
+
48
+ if __name__ == "__main__":
49
+ raise SystemExit(main())
@@ -0,0 +1,159 @@
1
+ ---
2
+ name: meta-flow
3
+ description: Use this workflow for vague, multi-step, high-reliability tasks that need questioning, proposal, review, adjudication, planning, task-level execution, verification, direction evaluation, and user confirmation before completion.
4
+ ---
5
+
6
+ # Meta-Flow
7
+
8
+ Meta-flow turns a vague request into a controlled Codex workflow with proposal review, user gates, milestone planning, concrete-task execution, verification, and direction checks.
9
+
10
+ Use it when reliability matters more than speed. Do not use it for small direct edits, one-off answers, simple code explanations, or tasks without observable acceptance criteria.
11
+
12
+ ## Trigger Conditions
13
+
14
+ - The request is vague or has unclear boundaries.
15
+ - The task crosses files, modules, stages, or external systems.
16
+ - The user asks for a proposal before implementation.
17
+ - The task needs high-reliability execution and review.
18
+ - New findings during execution may change the goal.
19
+ - The work needs milestones, concrete tasks, and verification loops.
20
+
21
+ ## Non-Triggers
22
+
23
+ - The user wants a direct answer.
24
+ - The user wants code explained.
25
+ - The user specified a small, low-risk edit.
26
+ - The task has no executable result.
27
+ - The task has no observable verification path.
28
+
29
+ ## State Machine
30
+
31
+ Proposal phase:
32
+
33
+ ```text
34
+ INTAKE
35
+ -> QUESTIONING
36
+ -> GOAL_CONTRACT_DRAFTED
37
+ -> RESEARCH_AND_PROPOSAL
38
+ -> PROPOSAL_REVIEW
39
+ -> ADJUDICATION
40
+ -> PROPOSAL_REWORK
41
+ -> PROPOSAL_SUMMARY
42
+ -> USER_PROPOSAL_CONFIRMATION
43
+ -> PROPOSAL_ACCEPTED
44
+ ```
45
+
46
+ Execution phase:
47
+
48
+ ```text
49
+ PLANNING
50
+ -> USER_PLAN_CONFIRMATION
51
+ -> MILESTONE_SELECTED
52
+ -> TASK_DECOMPOSITION
53
+ -> TASK_EXECUTION
54
+ -> TASK_VERIFICATION
55
+ -> TASK_REPAIR
56
+ -> MILESTONE_COMPLETED
57
+ -> DIRECTION_EVALUATION
58
+ -> CONTINUE_NEXT_MILESTONE
59
+ -> REPLAN
60
+ -> GOAL_ADJUSTMENT_REQUIRED
61
+ -> FINAL_SUMMARY
62
+ -> USER_FINAL_CONFIRMATION
63
+ -> DONE
64
+ ```
65
+
66
+ Allowed loops:
67
+
68
+ - `PROPOSAL_REVIEW -> ADJUDICATION -> PROPOSAL_REWORK -> PROPOSAL_REVIEW`
69
+ - `USER_PROPOSAL_CONFIRMATION -> ADJUDICATION -> QUESTIONING | RESEARCH_AND_PROPOSAL`
70
+ - `TASK_VERIFICATION -> TASK_REPAIR -> TASK_EXECUTION -> TASK_VERIFICATION`
71
+ - `DIRECTION_EVALUATION -> GOAL_ADJUSTMENT_REQUIRED -> QUESTIONING | RESEARCH_AND_PROPOSAL`
72
+ - `DIRECTION_EVALUATION -> REPLAN -> PLANNING | TASK_DECOMPOSITION`
73
+
74
+ Stop conditions:
75
+
76
+ - Proposal review/rework: max 3 rounds.
77
+ - Concrete task repair: max 2 rounds per task.
78
+ - Direction adjustment: max 2 rounds.
79
+ - When a limit is exceeded, generate a blocked report and ask the user for a decision.
80
+
81
+ ## Proposal Phase Procedure
82
+
83
+ 1. Create a task directory:
84
+
85
+ ```bash
86
+ python3 .meta-flow/scripts/new_task.py "<raw request>"
87
+ ```
88
+
89
+ 2. Save the raw request in `raw-request.md`.
90
+ 3. Invoke `questioner`.
91
+ 4. If blocking questions exist, ask the user before continuing.
92
+ 5. Generate `goal-contract.json`.
93
+ 6. Validate it:
94
+
95
+ ```bash
96
+ python3 .meta-flow/scripts/validate_goal_contract.py <task-dir>/goal-contract.json
97
+ ```
98
+
99
+ 7. Invoke `researcher_proposer` to create `proposal.md`.
100
+ 8. Invoke `product_reviewer`, `technical_reviewer`, `risk_reviewer`, and `verification_reviewer`, preferably in parallel.
101
+ 9. Run the mechanical aggregator:
102
+
103
+ ```bash
104
+ python3 .meta-flow/scripts/aggregate_reviews.py --reviews-dir <task-dir>/reviews --output <task-dir>/review-aggregate.json
105
+ ```
106
+
107
+ 10. Invoke `adjudicator`.
108
+ 11. Validate `adjudication-report.json`.
109
+ 12. If the decision is `revise_proposal`, return to `researcher_proposer`.
110
+ 13. If the decision is `ask_user`, ask the user.
111
+ 14. If the decision is `accept`, invoke `proposal_summarizer`.
112
+ 15. Show `proposal-summary.md` to the user and require confirmation.
113
+
114
+ ## Execution Phase Procedure
115
+
116
+ 1. Invoke `planner` to create `milestone-plan.json`.
117
+ 2. Validate it and ask the user to confirm the plan.
118
+ 3. Select one current milestone.
119
+ 4. Invoke `task_decomposer` to create `task-list.json`.
120
+ 5. Validate the task list.
121
+ 6. Process concrete tasks by dependency order.
122
+ 7. For each concrete task:
123
+ - Invoke `executor` for exactly one concrete task.
124
+ - Invoke `result_verifier` for exactly that concrete task.
125
+ - On `revise`, return to executor for repair, max 2 attempts.
126
+ - On `block`, stop the milestone and route to adjudicator or user.
127
+ 8. After all concrete tasks in a milestone pass, invoke `direction_evaluator`.
128
+ 9. Route from `direction-evaluation.json`:
129
+ - `continue`: select next milestone.
130
+ - `replan`: return to `planner` or `task_decomposer`.
131
+ - `adjust_goal`: ask user to confirm a contract patch, then return to proposal phase.
132
+ - `ask_user`: ask user before continuing.
133
+ - `abort`: generate blocked/final report as appropriate.
134
+ 10. After all milestones complete, invoke `final_summarizer`.
135
+ 11. Show `final-report.md` and require final user confirmation.
136
+ 12. Mark `state.json.status = done` and `phase = DONE`.
137
+
138
+ ## Role Boundaries
139
+
140
+ - User confirmation gates are not agents.
141
+ - Reviewers review; they do not decide the route.
142
+ - Adjudicator decides the route; it does not write code or edit proposals.
143
+ - Planner creates milestones; it does not create concrete task specs.
144
+ - Task decomposer creates concrete tasks; it does not execute or verify.
145
+ - Executor changes only files allowed by the current task spec.
146
+ - Result verifier verifies one concrete task and does not fix code.
147
+ - Direction evaluator checks whether the goal, proposal, or plan is still valid.
148
+
149
+ ## Reference Files
150
+
151
+ - `references/role-contracts.md`
152
+ - `references/review-rubric.md`
153
+ - `references/adjudication-policy.md`
154
+ - `references/execution-policy.md`
155
+ - `references/direction-evaluation-policy.md`
156
+
157
+ ## Required Artifacts
158
+
159
+ Task directories should use the templates in `.meta-flow/templates/` and keep state in `state.json`. Scripts in `.meta-flow/scripts/` provide initialization, validation, aggregation, and status reporting.
@@ -0,0 +1,30 @@
1
+ # Adjudication Policy
2
+
3
+ Adjudication is not mechanical aggregation. `aggregate_reviews.py` only computes a mechanical result. The adjudicator decides the route.
4
+
5
+ ## Inputs
6
+
7
+ - reviewer reports
8
+ - review aggregate
9
+ - user feedback
10
+ - direction evaluator output
11
+ - current state and round counters
12
+
13
+ ## Decisions
14
+
15
+ - `accept`: proposal can move to user-facing summary.
16
+ - `revise_proposal`: proposal needs specific rework.
17
+ - `ask_user`: user input is required.
18
+ - `adjust_goal`: goal contract needs a user-confirmed patch.
19
+ - `replan`: execution plan or task decomposition needs changes.
20
+ - `block`: the workflow cannot continue safely or meaningfully.
21
+
22
+ ## Conflict Handling
23
+
24
+ A reviewer `block` does not automatically stop the workflow. The adjudicator must decide whether the block is valid, whether it can be mitigated, and whether the next route is user input, goal adjustment, proposal rework, or blocked state.
25
+
26
+ ## Round Limits
27
+
28
+ - Proposal rework over 3 rounds: block and ask user.
29
+ - Direction adjustment over 2 rounds: block and ask user.
30
+ - Task repair over 2 rounds: block the concrete task and escalate.
@@ -0,0 +1,24 @@
1
+ # Direction Evaluation Policy
2
+
3
+ Direction evaluation runs after every completed milestone and whenever a verifier reports a major new finding.
4
+
5
+ ## Questions
6
+
7
+ - Does the original goal still hold?
8
+ - Did execution disprove a proposal assumption?
9
+ - Did the milestone expose a new boundary?
10
+ - Are the acceptance criteria still valid?
11
+ - Is user input now required?
12
+ - Would continuing create wrong results or wasted effort?
13
+
14
+ ## Decisions
15
+
16
+ - `continue`: proceed to the next milestone.
17
+ - `replan`: plan or task decomposition needs changes, but the goal is still valid.
18
+ - `adjust_goal`: goal contract needs a user-confirmed patch.
19
+ - `ask_user`: user decision is required before routing.
20
+ - `abort`: continuing is unsafe or meaningless.
21
+
22
+ ## Contract Patch Rule
23
+
24
+ The direction evaluator may propose a contract patch. It must not modify `goal-contract.json` directly. User confirmation is required before goal changes take effect.