@tiic-tech/openworkflow 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.
- package/LICENSE +21 -0
- package/README.md +37 -0
- package/dist/adapters/codex/src/doctorCodexAdapter.d.ts +6 -0
- package/dist/adapters/codex/src/doctorCodexAdapter.js +124 -0
- package/dist/adapters/codex/src/doctorCodexAdapter.js.map +1 -0
- package/dist/adapters/codex/src/generateAgents.d.ts +2 -0
- package/dist/adapters/codex/src/generateAgents.js +40 -0
- package/dist/adapters/codex/src/generateAgents.js.map +1 -0
- package/dist/adapters/codex/src/generateCodexAdapter.d.ts +9 -0
- package/dist/adapters/codex/src/generateCodexAdapter.js +59 -0
- package/dist/adapters/codex/src/generateCodexAdapter.js.map +1 -0
- package/dist/adapters/codex/src/generateCommands.d.ts +6 -0
- package/dist/adapters/codex/src/generateCommands.js +205 -0
- package/dist/adapters/codex/src/generateCommands.js.map +1 -0
- package/dist/adapters/codex/src/generateSkills.d.ts +7 -0
- package/dist/adapters/codex/src/generateSkills.js +60 -0
- package/dist/adapters/codex/src/generateSkills.js.map +1 -0
- package/dist/adapters/codex/src/generatedFiles.d.ts +4 -0
- package/dist/adapters/codex/src/generatedFiles.js +67 -0
- package/dist/adapters/codex/src/generatedFiles.js.map +1 -0
- package/dist/adapters/codex/src/manifest.d.ts +4 -0
- package/dist/adapters/codex/src/manifest.js +40 -0
- package/dist/adapters/codex/src/manifest.js.map +1 -0
- package/dist/adapters/codex/src/templates.d.ts +7 -0
- package/dist/adapters/codex/src/templates.js +6 -0
- package/dist/adapters/codex/src/templates.js.map +1 -0
- package/dist/cli/src/args.d.ts +8 -0
- package/dist/cli/src/args.js +34 -0
- package/dist/cli/src/args.js.map +1 -0
- package/dist/cli/src/commands/doctor.d.ts +1 -0
- package/dist/cli/src/commands/doctor.js +26 -0
- package/dist/cli/src/commands/doctor.js.map +1 -0
- package/dist/cli/src/commands/init.d.ts +1 -0
- package/dist/cli/src/commands/init.js +52 -0
- package/dist/cli/src/commands/init.js.map +1 -0
- package/dist/cli/src/commands/shared.d.ts +4 -0
- package/dist/cli/src/commands/shared.js +19 -0
- package/dist/cli/src/commands/shared.js.map +1 -0
- package/dist/cli/src/commands/sync.d.ts +1 -0
- package/dist/cli/src/commands/sync.js +27 -0
- package/dist/cli/src/commands/sync.js.map +1 -0
- package/dist/cli/src/commands/validate.d.ts +1 -0
- package/dist/cli/src/commands/validate.js +17 -0
- package/dist/cli/src/commands/validate.js.map +1 -0
- package/dist/cli/src/dev/validateRepositoryContractsCli.d.ts +2 -0
- package/dist/cli/src/dev/validateRepositoryContractsCli.js +37 -0
- package/dist/cli/src/dev/validateRepositoryContractsCli.js.map +1 -0
- package/dist/cli/src/dev/verifyRuntimeSurface.d.ts +2 -0
- package/dist/cli/src/dev/verifyRuntimeSurface.js +344 -0
- package/dist/cli/src/dev/verifyRuntimeSurface.js.map +1 -0
- package/dist/cli/src/dev/verifyWorkflowE2E.d.ts +2 -0
- package/dist/cli/src/dev/verifyWorkflowE2E.js +366 -0
- package/dist/cli/src/dev/verifyWorkflowE2E.js.map +1 -0
- package/dist/cli/src/index.d.ts +2 -0
- package/dist/cli/src/index.js +51 -0
- package/dist/cli/src/index.js.map +1 -0
- package/dist/core/src/artifacts/registry.d.ts +53 -0
- package/dist/core/src/artifacts/registry.js +483 -0
- package/dist/core/src/artifacts/registry.js.map +1 -0
- package/dist/core/src/commands/registry.d.ts +36 -0
- package/dist/core/src/commands/registry.js +539 -0
- package/dist/core/src/commands/registry.js.map +1 -0
- package/dist/core/src/contracts/index.d.ts +23 -0
- package/dist/core/src/contracts/index.js +16 -0
- package/dist/core/src/contracts/index.js.map +1 -0
- package/dist/core/src/contracts/yaml.d.ts +2 -0
- package/dist/core/src/contracts/yaml.js +12 -0
- package/dist/core/src/contracts/yaml.js.map +1 -0
- package/dist/core/src/contracts.d.ts +23 -0
- package/dist/core/src/contracts.js +15 -0
- package/dist/core/src/contracts.js.map +1 -0
- package/dist/core/src/fs/index.d.ts +4 -0
- package/dist/core/src/fs/index.js +28 -0
- package/dist/core/src/fs/index.js.map +1 -0
- package/dist/core/src/fs.d.ts +4 -0
- package/dist/core/src/fs.js +28 -0
- package/dist/core/src/fs.js.map +1 -0
- package/dist/core/src/initOpenWorkflow.d.ts +7 -0
- package/dist/core/src/initOpenWorkflow.js +220 -0
- package/dist/core/src/initOpenWorkflow.js.map +1 -0
- package/dist/core/src/validateOpenWorkflow.d.ts +5 -0
- package/dist/core/src/validateOpenWorkflow.js +145 -0
- package/dist/core/src/validateOpenWorkflow.js.map +1 -0
- package/dist/core/src/validators/validateOpenWorkflow.d.ts +5 -0
- package/dist/core/src/validators/validateOpenWorkflow.js +551 -0
- package/dist/core/src/validators/validateOpenWorkflow.js.map +1 -0
- package/dist/core/src/validators/validateRepositoryContracts.d.ts +2 -0
- package/dist/core/src/validators/validateRepositoryContracts.js +827 -0
- package/dist/core/src/validators/validateRepositoryContracts.js.map +1 -0
- package/dist/core/src/workflow/initOpenWorkflow.d.ts +7 -0
- package/dist/core/src/workflow/initOpenWorkflow.js +182 -0
- package/dist/core/src/workflow/initOpenWorkflow.js.map +1 -0
- package/dist/core/src/yaml.d.ts +2 -0
- package/dist/core/src/yaml.js +12 -0
- package/dist/core/src/yaml.js.map +1 -0
- package/package.json +55 -0
- package/references/artifact-authoring-templates.md +78 -0
- package/references/audit-first-discovery-loop.md +85 -0
- package/references/contract-graph.md +129 -0
- package/references/discovery-artifact-contracts.md +155 -0
- package/references/engineering-skill-reference-research.md +204 -0
- package/references/npm-cli-architecture.md +63 -0
- package/references/runtime-command-surface.md +169 -0
- package/schemas/artifact-contracts.schema.json +130 -0
- package/schemas/change.schema.json +71 -0
- package/schemas/contract-graph.schema.json +80 -0
- package/schemas/decision-record.schema.json +92 -0
- package/schemas/disclosure-levels.schema.json +66 -0
- package/schemas/openworkflow-contract.schema.json +88 -0
- package/schemas/product-design.schema.json +356 -0
- package/schemas/prototype-evidence.schema.json +325 -0
- package/schemas/prototype.schema.json +149 -0
- package/schemas/validation-target.schema.json +127 -0
- package/schemas/validation.schema.json +123 -0
- package/schemas/vision-session.schema.json +78 -0
- package/schemas/work-items.schema.json +87 -0
- package/schemas/workflow-index.schema.json +70 -0
- package/skills/build-prototype/SKILL.md +87 -0
- package/skills/build-prototype/agents/openai.yaml +4 -0
- package/skills/build-prototype/references/prototype-protocol.md +56 -0
- package/skills/build-prototype/scripts/init_prototype.py +260 -0
- package/skills/build-team/SKILL.md +292 -0
- package/skills/build-team/agents/openai.yaml +4 -0
- package/skills/build-team/references/runtime-schema.md +275 -0
- package/skills/build-team/references/team-protocol.md +244 -0
- package/skills/build-team/scripts/init_team_runtime.py +431 -0
- package/skills/build-validation/SKILL.md +81 -0
- package/skills/build-validation/agents/openai.yaml +4 -0
- package/skills/build-validation/references/validation-protocol.md +51 -0
- package/skills/build-validation/scripts/init_validation.py +194 -0
- package/skills/build-workflow/SKILL.md +65 -0
- package/skills/build-workflow/agents/openai.yaml +4 -0
- package/skills/build-workflow/references/workflow-layout.md +57 -0
- package/skills/build-workflow/scripts/init_workflow.py +423 -0
- package/skills/run-team/SKILL.md +93 -0
- package/skills/run-team/agents/openai.yaml +4 -0
- package/skills/run-team/references/delegation-and-agent-lifecycle.md +78 -0
- package/skills/run-team/references/run-loop.md +73 -0
- package/skills/run-team/references/runtime-audit.md +56 -0
- package/skills/run-team/references/scope-selection.md +64 -0
- package/skills/run-team/scripts/audit_team_runtime.py +173 -0
- package/skills/run-team/scripts/init_next_scope.py +304 -0
- package/templates/README.md +5 -0
- package/templates/codex/README.md +4 -0
- package/templates/openworkflow/README.md +4 -0
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Initialize a repo-local Agent Team runtime skeleton.
|
|
3
|
+
|
|
4
|
+
The script is intentionally conservative: it creates missing directories and
|
|
5
|
+
files, but it does not overwrite existing runtime state unless --force is set.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import json
|
|
12
|
+
import re
|
|
13
|
+
import subprocess
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class Milestone:
|
|
20
|
+
milestone_id: str
|
|
21
|
+
title: str
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def q(value: str | None) -> str:
|
|
25
|
+
if value is None:
|
|
26
|
+
return "null"
|
|
27
|
+
return json.dumps(value)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def slugify(value: str) -> str:
|
|
31
|
+
slug = re.sub(r"[^a-zA-Z0-9]+", "-", value.strip().lower()).strip("-")
|
|
32
|
+
return slug or "milestone"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def git_ref(root: Path) -> str | None:
|
|
36
|
+
try:
|
|
37
|
+
result = subprocess.run(
|
|
38
|
+
["git", "rev-parse", "--short", "HEAD"],
|
|
39
|
+
cwd=root,
|
|
40
|
+
check=True,
|
|
41
|
+
text=True,
|
|
42
|
+
stdout=subprocess.PIPE,
|
|
43
|
+
stderr=subprocess.DEVNULL,
|
|
44
|
+
)
|
|
45
|
+
except (OSError, subprocess.CalledProcessError):
|
|
46
|
+
return None
|
|
47
|
+
ref = result.stdout.strip()
|
|
48
|
+
return ref or None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def parse_milestone(raw: str) -> Milestone:
|
|
52
|
+
for separator in (":", "=", "|"):
|
|
53
|
+
if separator in raw:
|
|
54
|
+
left, right = raw.split(separator, 1)
|
|
55
|
+
milestone_id = left.strip()
|
|
56
|
+
title = right.strip()
|
|
57
|
+
if milestone_id and title:
|
|
58
|
+
return Milestone(milestone_id=milestone_id, title=title)
|
|
59
|
+
cleaned = raw.strip()
|
|
60
|
+
if not cleaned:
|
|
61
|
+
raise ValueError("milestone cannot be empty")
|
|
62
|
+
match = re.match(r"^(M\d+)\s+(.+)$", cleaned, flags=re.IGNORECASE)
|
|
63
|
+
if match:
|
|
64
|
+
return Milestone(milestone_id=match.group(1).upper(), title=match.group(2).strip())
|
|
65
|
+
raise ValueError(f"milestone must look like M01:Title, got {raw!r}")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def detect_source_artifacts(root: Path) -> list[str]:
|
|
69
|
+
candidates = [
|
|
70
|
+
"AGENT.md",
|
|
71
|
+
"README.md",
|
|
72
|
+
"README",
|
|
73
|
+
"ROADMAP.md",
|
|
74
|
+
"SPEC.md",
|
|
75
|
+
"LAUNCH_CHECKLIST.md",
|
|
76
|
+
]
|
|
77
|
+
found = [candidate for candidate in candidates if (root / candidate).exists()]
|
|
78
|
+
if (root / "DESIGN_SPEC").is_dir():
|
|
79
|
+
found.append("DESIGN_SPEC/")
|
|
80
|
+
if (root / "docs").is_dir():
|
|
81
|
+
found.append("docs/")
|
|
82
|
+
return found or ["AGENT.md"]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def detect_application_roots(root: Path) -> list[str]:
|
|
86
|
+
roots: list[str] = []
|
|
87
|
+
for candidate in ("frontend", "app", "src", "packages", "backend", "server", "api"):
|
|
88
|
+
if (root / candidate).exists():
|
|
89
|
+
roots.append(f"{candidate}/")
|
|
90
|
+
for manifest in ("package.json", "pyproject.toml", "Cargo.toml", "go.mod"):
|
|
91
|
+
if (root / manifest).exists():
|
|
92
|
+
roots.append(manifest)
|
|
93
|
+
return roots
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def list_yaml(items: list[str], indent: int = 0) -> str:
|
|
97
|
+
prefix = " " * indent
|
|
98
|
+
if not items:
|
|
99
|
+
return f"{prefix}[]\n"
|
|
100
|
+
return "".join(f"{prefix}- {q(item)}\n" for item in items)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def ensure_dir(path: Path, dry_run: bool) -> None:
|
|
104
|
+
if dry_run:
|
|
105
|
+
print(f"DIR {path}")
|
|
106
|
+
return
|
|
107
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def write_file(path: Path, content: str, force: bool, dry_run: bool) -> None:
|
|
111
|
+
if path.exists() and not force:
|
|
112
|
+
print(f"SKIP {path}")
|
|
113
|
+
return
|
|
114
|
+
if dry_run:
|
|
115
|
+
action = "OVERWRITE" if path.exists() else "WRITE"
|
|
116
|
+
print(f"{action} {path}")
|
|
117
|
+
return
|
|
118
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
119
|
+
path.write_text(content, encoding="utf-8")
|
|
120
|
+
print(f"WRITE {path}")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def touch_gitkeep(path: Path, dry_run: bool) -> None:
|
|
124
|
+
gitkeep = path / ".gitkeep"
|
|
125
|
+
if gitkeep.exists():
|
|
126
|
+
return
|
|
127
|
+
if dry_run:
|
|
128
|
+
print(f"WRITE {gitkeep}")
|
|
129
|
+
return
|
|
130
|
+
gitkeep.write_text("", encoding="utf-8")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def runtime_index(scope_id: str, scope_title: str, source: str, ref: str | None) -> str:
|
|
134
|
+
return (
|
|
135
|
+
f"active_scope: {scope_id}\n"
|
|
136
|
+
"scopes:\n"
|
|
137
|
+
f" - scope_id: {scope_id}\n"
|
|
138
|
+
f" title: {q(scope_title)}\n"
|
|
139
|
+
" status: active\n"
|
|
140
|
+
f" source: {q(source)}\n"
|
|
141
|
+
f" path: .codex/runtime/scopes/{scope_id}/\n"
|
|
142
|
+
f" base_git_ref: {q(ref) if ref else 'null'}\n"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def scope_yaml(
|
|
147
|
+
scope_id: str,
|
|
148
|
+
scope_title: str,
|
|
149
|
+
sources: list[str],
|
|
150
|
+
app_roots: list[str],
|
|
151
|
+
ref: str | None,
|
|
152
|
+
) -> str:
|
|
153
|
+
return (
|
|
154
|
+
f"scope_id: {scope_id}\n"
|
|
155
|
+
f"title: {q(scope_title)}\n"
|
|
156
|
+
"status: active\n"
|
|
157
|
+
"source_artifacts:\n"
|
|
158
|
+
f"{list_yaml(sources, 2)}"
|
|
159
|
+
f"base_git_ref: {q(ref) if ref else 'null'}\n"
|
|
160
|
+
"runtime_protocol:\n"
|
|
161
|
+
" agent_team_protocol: .codex/agents/README.md\n"
|
|
162
|
+
" orchestrator_role: .codex/agents/orchestrator.md\n"
|
|
163
|
+
f" agent_roster: .codex/runtime/scopes/{scope_id}/AGENT_ROSTER.yaml\n"
|
|
164
|
+
"boundary:\n"
|
|
165
|
+
" application_roots:\n"
|
|
166
|
+
f"{list_yaml(app_roots, 4)}"
|
|
167
|
+
" protected_roots:\n"
|
|
168
|
+
" - .git/\n"
|
|
169
|
+
" - .codex/runtime/\n"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def milestones_yaml(scope_id: str, sources: list[str], milestones: list[Milestone]) -> str:
|
|
174
|
+
lines = [f"scope_id: {scope_id}", "source_artifacts:"]
|
|
175
|
+
lines.extend(f" - {q(source)}" for source in sources)
|
|
176
|
+
lines.append("milestones:")
|
|
177
|
+
for index, milestone in enumerate(milestones):
|
|
178
|
+
status = "active" if index == 0 else "planned"
|
|
179
|
+
lines.extend(
|
|
180
|
+
[
|
|
181
|
+
f" - milestone_id: {milestone.milestone_id}",
|
|
182
|
+
f" title: {q(milestone.title)}",
|
|
183
|
+
f" status: {status}",
|
|
184
|
+
f" scope: {q('Define and deliver ' + milestone.title + '.')}",
|
|
185
|
+
f" target: {q('Complete the milestone artifacts and checks.')}",
|
|
186
|
+
" dependencies: []",
|
|
187
|
+
" required_specs:",
|
|
188
|
+
]
|
|
189
|
+
)
|
|
190
|
+
lines.extend(f" - {q(source)}" for source in sources)
|
|
191
|
+
lines.extend(
|
|
192
|
+
[
|
|
193
|
+
" expected_artifacts: []",
|
|
194
|
+
" estimated_atom_tasks: 3-8",
|
|
195
|
+
f" task_file: .codex/runtime/scopes/{scope_id}/milestones/{milestone.milestone_id}/IMPLEMENT_TASKS.yaml",
|
|
196
|
+
f" issue_file: .codex/runtime/scopes/{scope_id}/milestones/{milestone.milestone_id}/IMPLEMENT_ISSUES.yaml",
|
|
197
|
+
" qa_gate:",
|
|
198
|
+
" - required checks pass or skipped checks are documented",
|
|
199
|
+
" acceptance:",
|
|
200
|
+
" - runtime state matches implementation reality",
|
|
201
|
+
]
|
|
202
|
+
)
|
|
203
|
+
return "\n".join(lines) + "\n"
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def implement_index(scope_id: str, milestones: list[Milestone]) -> str:
|
|
207
|
+
active = milestones[0].milestone_id if milestones else "M01"
|
|
208
|
+
lines = [f"scope_id: {scope_id}", f"active_milestone: {active}", "milestones:"]
|
|
209
|
+
for index, milestone in enumerate(milestones):
|
|
210
|
+
status = "active" if index == 0 else "planned"
|
|
211
|
+
branch = f"feat/{milestone.milestone_id.lower()}-{slugify(milestone.title)}"
|
|
212
|
+
lines.extend(
|
|
213
|
+
[
|
|
214
|
+
f" - milestone_id: {milestone.milestone_id}",
|
|
215
|
+
f" status: {status}",
|
|
216
|
+
f" title: {q(milestone.title)}",
|
|
217
|
+
f" task_file: .codex/runtime/scopes/{scope_id}/milestones/{milestone.milestone_id}/IMPLEMENT_TASKS.yaml",
|
|
218
|
+
f" issue_file: .codex/runtime/scopes/{scope_id}/milestones/{milestone.milestone_id}/IMPLEMENT_ISSUES.yaml",
|
|
219
|
+
" qa_report: null",
|
|
220
|
+
f" branch: {branch}",
|
|
221
|
+
" last_checkpoint: null",
|
|
222
|
+
]
|
|
223
|
+
)
|
|
224
|
+
return "\n".join(lines) + "\n"
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def agent_roster(scope_id: str, app_roots: list[str]) -> str:
|
|
228
|
+
owns_frontend = ["frontend/src/app/", "frontend/src/components/", "frontend/src/systems/", "frontend/tests/"]
|
|
229
|
+
if not any(root.startswith("frontend") for root in app_roots):
|
|
230
|
+
owns_frontend = ["src/", "tests/"]
|
|
231
|
+
return "\n".join(
|
|
232
|
+
[
|
|
233
|
+
f"scope_id: {scope_id}",
|
|
234
|
+
"roster_version: 1",
|
|
235
|
+
"updated_at: null",
|
|
236
|
+
"lifecycle_status_values:",
|
|
237
|
+
" - available",
|
|
238
|
+
" - active",
|
|
239
|
+
" - idle",
|
|
240
|
+
" - blocked",
|
|
241
|
+
" - closed",
|
|
242
|
+
" - archived",
|
|
243
|
+
" - legacy_untracked",
|
|
244
|
+
"session_policies:",
|
|
245
|
+
" persistent:",
|
|
246
|
+
" purpose: Keep domain agents mounted across related atom tasks and issue-fix loops.",
|
|
247
|
+
" reuse_rule: Resume the existing matching agent_id before spawning a replacement.",
|
|
248
|
+
" event:",
|
|
249
|
+
" purpose: Run async or one-off review, security, QA, and git drafting work.",
|
|
250
|
+
" reuse_rule: Close after handoff unless the Orchestrator records a reason to keep it idle.",
|
|
251
|
+
"persistent_agents:",
|
|
252
|
+
" - agent_name: tech-prompt-agent",
|
|
253
|
+
" agent_id: null",
|
|
254
|
+
" lifecycle_status: available",
|
|
255
|
+
" session_policy: persistent",
|
|
256
|
+
" owns:",
|
|
257
|
+
f" - .codex/runtime/scopes/{scope_id}/milestones/*/prompts/",
|
|
258
|
+
" current_task: null",
|
|
259
|
+
" last_completed_task: null",
|
|
260
|
+
" active_milestone: null",
|
|
261
|
+
" notes: Spawn once for planning work, then resume for related task prompt creation.",
|
|
262
|
+
" - agent_name: frontend-agent",
|
|
263
|
+
" agent_id: null",
|
|
264
|
+
" lifecycle_status: available",
|
|
265
|
+
" session_policy: persistent",
|
|
266
|
+
" owns:",
|
|
267
|
+
*(f" - {path}" for path in owns_frontend),
|
|
268
|
+
" current_task: null",
|
|
269
|
+
" last_completed_task: null",
|
|
270
|
+
" active_milestone: null",
|
|
271
|
+
" notes: Use only when this repo has frontend or browser-facing work; otherwise replace with a domain implementation agent.",
|
|
272
|
+
"event_agents:",
|
|
273
|
+
" - agent_name: code-review-agent",
|
|
274
|
+
" agent_id: null",
|
|
275
|
+
" lifecycle_status: available",
|
|
276
|
+
" session_policy: event",
|
|
277
|
+
" trigger: artifact_ready",
|
|
278
|
+
" closes_after_handoff: true",
|
|
279
|
+
" notes: Writes review artifacts and issue logs, then closes.",
|
|
280
|
+
" - agent_name: security-review-agent",
|
|
281
|
+
" agent_id: null",
|
|
282
|
+
" lifecycle_status: available",
|
|
283
|
+
" session_policy: event",
|
|
284
|
+
" trigger: security_sensitive_change",
|
|
285
|
+
" closes_after_handoff: true",
|
|
286
|
+
" notes: Spawn for auth, secrets, external input, APIs, dependencies, deployment, analytics, or infrastructure.",
|
|
287
|
+
" - agent_name: tdd-qa-agent",
|
|
288
|
+
" agent_id: null",
|
|
289
|
+
" lifecycle_status: available",
|
|
290
|
+
" session_policy: event",
|
|
291
|
+
" trigger: qa_gate",
|
|
292
|
+
" closes_after_handoff: true",
|
|
293
|
+
" notes: Keep event-driven unless it is actively authoring a long test suite across tasks.",
|
|
294
|
+
" - agent_name: git-release-agent",
|
|
295
|
+
" agent_id: null",
|
|
296
|
+
" lifecycle_status: available",
|
|
297
|
+
" session_policy: event",
|
|
298
|
+
" trigger: checkpoint_or_release",
|
|
299
|
+
" closes_after_handoff: true",
|
|
300
|
+
" notes: Drafts branch, commit, PR, and release text only; Orchestrator performs git actions.",
|
|
301
|
+
"legacy_tracking:",
|
|
302
|
+
" task_agent_ids_before_roster: legacy_untracked",
|
|
303
|
+
" note: Do not invent ids for historical null task agent_id values.",
|
|
304
|
+
]
|
|
305
|
+
) + "\n"
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def state_machine_doc(scope_id: str) -> str:
|
|
309
|
+
return f"""# Agent Team Runtime State Machine
|
|
310
|
+
|
|
311
|
+
Active scope: `{scope_id}`
|
|
312
|
+
|
|
313
|
+
Flow:
|
|
314
|
+
|
|
315
|
+
```txt
|
|
316
|
+
repo_scan
|
|
317
|
+
-> user_parameter_question
|
|
318
|
+
-> scope_design
|
|
319
|
+
-> runtime_bootstrap
|
|
320
|
+
-> milestone_plan
|
|
321
|
+
-> atom_task_plan
|
|
322
|
+
-> delegation_boundary_check
|
|
323
|
+
-> persistent_or_event_agent_selection
|
|
324
|
+
-> prompt_preparation
|
|
325
|
+
-> spawn_or_resume_agent
|
|
326
|
+
-> record_agent_id_in_roster_and_task
|
|
327
|
+
-> implementation_or_event_work
|
|
328
|
+
-> artifact_ready
|
|
329
|
+
-> async_review
|
|
330
|
+
-> issue_fix_loop
|
|
331
|
+
-> resume_original_persistent_agent_for_fix
|
|
332
|
+
-> milestone_qa
|
|
333
|
+
-> git_checkpoint_decision
|
|
334
|
+
-> archive_or_freeze
|
|
335
|
+
-> next_milestone
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Task status values:
|
|
339
|
+
|
|
340
|
+
```txt
|
|
341
|
+
planned, prompted, claimed, in_progress, artifact_ready, review_pending,
|
|
342
|
+
reviewed, fix_required, qa_ready, done, blocked, archived
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Runtime maintenance rules:
|
|
346
|
+
|
|
347
|
+
- Update runtime state when task reality changes.
|
|
348
|
+
- Update AGENT_ROSTER.yaml when agent lifecycle changes.
|
|
349
|
+
- New delegated tasks must not leave agent_id null.
|
|
350
|
+
- Orchestrator direct execution requires an orchestrator_exception note.
|
|
351
|
+
- Keep detailed reasoning in prompts and reviews, not YAML indexes.
|
|
352
|
+
- Preserve stale plans and superseded evidence in `archive/`.
|
|
353
|
+
- Do not infer completion from file existence alone.
|
|
354
|
+
"""
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def build_runtime(args: argparse.Namespace) -> None:
|
|
358
|
+
root = Path(args.root).expanduser().resolve()
|
|
359
|
+
scope_id = args.scope_id.upper()
|
|
360
|
+
sources = args.source_artifact or detect_source_artifacts(root)
|
|
361
|
+
app_roots = args.application_root or detect_application_roots(root)
|
|
362
|
+
milestones = [parse_milestone(raw) for raw in args.milestone]
|
|
363
|
+
if not milestones:
|
|
364
|
+
milestones = [Milestone("M01", "Workflow baseline")]
|
|
365
|
+
|
|
366
|
+
runtime_root = root / ".codex" / "runtime"
|
|
367
|
+
scope_root = runtime_root / "scopes" / scope_id
|
|
368
|
+
base_ref = git_ref(root)
|
|
369
|
+
|
|
370
|
+
for directory in [
|
|
371
|
+
runtime_root,
|
|
372
|
+
runtime_root / "archive",
|
|
373
|
+
runtime_root / "scopes",
|
|
374
|
+
scope_root,
|
|
375
|
+
scope_root / "archive",
|
|
376
|
+
scope_root / "milestones",
|
|
377
|
+
]:
|
|
378
|
+
ensure_dir(directory, args.dry_run)
|
|
379
|
+
|
|
380
|
+
for directory in [runtime_root / "archive", scope_root / "archive"]:
|
|
381
|
+
touch_gitkeep(directory, args.dry_run)
|
|
382
|
+
|
|
383
|
+
write_file(
|
|
384
|
+
runtime_root / "RUNTIME_INDEX.yaml",
|
|
385
|
+
runtime_index(scope_id, args.scope_title, sources[0], base_ref),
|
|
386
|
+
args.force,
|
|
387
|
+
args.dry_run,
|
|
388
|
+
)
|
|
389
|
+
write_file(runtime_root / "STATE_MACHINE.md", state_machine_doc(scope_id), args.force, args.dry_run)
|
|
390
|
+
write_file(scope_root / "SCOPE.yaml", scope_yaml(scope_id, args.scope_title, sources, app_roots, base_ref), args.force, args.dry_run)
|
|
391
|
+
write_file(scope_root / "MILESTONES.yaml", milestones_yaml(scope_id, sources, milestones), args.force, args.dry_run)
|
|
392
|
+
write_file(scope_root / "IMPLEMENT_INDEX.yaml", implement_index(scope_id, milestones), args.force, args.dry_run)
|
|
393
|
+
write_file(scope_root / "IMPLEMENT_ISSUE_INDEX.yaml", f"scope_id: {scope_id}\nissues: []\n", args.force, args.dry_run)
|
|
394
|
+
write_file(scope_root / "AGENT_ROSTER.yaml", agent_roster(scope_id, app_roots), args.force, args.dry_run)
|
|
395
|
+
|
|
396
|
+
for milestone in milestones:
|
|
397
|
+
milestone_root = scope_root / "milestones" / milestone.milestone_id
|
|
398
|
+
for directory in [
|
|
399
|
+
milestone_root,
|
|
400
|
+
milestone_root / "prompts",
|
|
401
|
+
milestone_root / "reviews",
|
|
402
|
+
milestone_root / "archive",
|
|
403
|
+
]:
|
|
404
|
+
ensure_dir(directory, args.dry_run)
|
|
405
|
+
for directory in [milestone_root / "prompts", milestone_root / "reviews", milestone_root / "archive"]:
|
|
406
|
+
touch_gitkeep(directory, args.dry_run)
|
|
407
|
+
write_file(milestone_root / "IMPLEMENT_TASKS.yaml", "tasks: []\n", args.force, args.dry_run)
|
|
408
|
+
write_file(milestone_root / "IMPLEMENT_ISSUES.yaml", "issues: []\n", args.force, args.dry_run)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def parser() -> argparse.ArgumentParser:
|
|
412
|
+
p = argparse.ArgumentParser(description="Initialize .codex/runtime for an Agent Team workflow.")
|
|
413
|
+
p.add_argument("--root", default=".", help="Repository root. Defaults to current directory.")
|
|
414
|
+
p.add_argument("--scope-id", default="MVP", help="Scope id, for example MVP or V1.")
|
|
415
|
+
p.add_argument("--scope-title", default="MVP implementation", help="Human-readable scope title.")
|
|
416
|
+
p.add_argument("--source-artifact", action="append", default=[], help="Source-of-truth artifact path. Repeatable.")
|
|
417
|
+
p.add_argument("--application-root", action="append", default=[], help="Application root path. Repeatable.")
|
|
418
|
+
p.add_argument("--milestone", action="append", default=[], help="Milestone as M01:Title. Repeatable.")
|
|
419
|
+
p.add_argument("--force", action="store_true", help="Overwrite existing runtime files.")
|
|
420
|
+
p.add_argument("--dry-run", action="store_true", help="Print planned writes without changing files.")
|
|
421
|
+
return p
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def main() -> int:
|
|
425
|
+
args = parser().parse_args()
|
|
426
|
+
build_runtime(args)
|
|
427
|
+
return 0
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
if __name__ == "__main__":
|
|
431
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: build-validation
|
|
3
|
+
description: Create validation-first prioritization artifacts from a product vision or change idea. Use when the user asks what should be prioritized, what must be proven first, which feature is core versus supporting, or when a broad idea needs a prototype brief before /ow:change, /ow:team, or implementation work.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Build Validation
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
Identify the smallest proof needed to make a vision credible before converting
|
|
11
|
+
it into implementation scope. This skill ranks assumptions, not backlog tasks.
|
|
12
|
+
|
|
13
|
+
It answers:
|
|
14
|
+
|
|
15
|
+
- Which feature is existential to the vision?
|
|
16
|
+
- Which features support the core experience?
|
|
17
|
+
- Which features are later, operational, or out of scope for validation?
|
|
18
|
+
- What is the smallest prototype that can prove or disprove the core assumption?
|
|
19
|
+
- What evidence decides whether to continue, pivot, stop, or gather more data?
|
|
20
|
+
|
|
21
|
+
## Inputs
|
|
22
|
+
|
|
23
|
+
Read only the relevant upstream contracts:
|
|
24
|
+
|
|
25
|
+
- `.codex/workflow/WORKFLOW_INDEX.yaml`
|
|
26
|
+
- `.codex/workflow/CONTRACT_GRAPH.yaml`
|
|
27
|
+
- `.codex/context/CONTEXT_MAP.yaml` when present and relevant
|
|
28
|
+
- `.codex/vision/VISION_CONTRACT.yaml` or the user's product vision
|
|
29
|
+
- `.codex/decisions/DECISION_INDEX.yaml` when decisions constrain the prototype
|
|
30
|
+
- `.codex/spec/SPEC_INDEX.yaml` only for directly relevant binding constraints
|
|
31
|
+
|
|
32
|
+
Avoid loading archives, unrelated specs, reviews, or runtime state unless the
|
|
33
|
+
user asks for historical evidence.
|
|
34
|
+
|
|
35
|
+
## Output
|
|
36
|
+
|
|
37
|
+
Write validation artifacts under:
|
|
38
|
+
|
|
39
|
+
```txt
|
|
40
|
+
.codex/validation/<validation_id>/
|
|
41
|
+
VALIDATION.yaml
|
|
42
|
+
PROTOTYPE_BRIEF.md
|
|
43
|
+
RESULT.md
|
|
44
|
+
archive/
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`RESULT.md` may remain a placeholder until a prototype is tested.
|
|
48
|
+
|
|
49
|
+
## Workflow
|
|
50
|
+
|
|
51
|
+
1. Restate the vision in one sentence.
|
|
52
|
+
2. Build a feature landscape:
|
|
53
|
+
- `existential`: without this, the vision does not hold
|
|
54
|
+
- `supporting`: makes the core feature useful
|
|
55
|
+
- `later`: valuable after the core assumption is proven
|
|
56
|
+
- `out_of_scope`: explicitly excluded from validation
|
|
57
|
+
3. Identify critical assumptions and rank the riskiest first.
|
|
58
|
+
4. Define one minimum prototype scope.
|
|
59
|
+
5. Define acceptance as evidence questions, not implementation completeness.
|
|
60
|
+
6. Initialize artifacts with `scripts/init_validation.py`.
|
|
61
|
+
7. Validate with `npm run validate` when the repository validator exists.
|
|
62
|
+
|
|
63
|
+
## Boundaries
|
|
64
|
+
|
|
65
|
+
- Do not create full implementation tasks.
|
|
66
|
+
- Do not write large product specs.
|
|
67
|
+
- Do not solve authentication, persistence, deployment, billing, or admin work
|
|
68
|
+
unless those are the existential assumption.
|
|
69
|
+
- Do not treat feature count as progress.
|
|
70
|
+
- Prefer a small prototype brief over a broad spec when uncertainty is high.
|
|
71
|
+
|
|
72
|
+
## Handoff
|
|
73
|
+
|
|
74
|
+
If validation is planned, hand off to `/ow:prototype` or an implementation
|
|
75
|
+
agent with the prototype brief only.
|
|
76
|
+
|
|
77
|
+
If validation passes, feed `VALIDATION.yaml` and `RESULT.md` into
|
|
78
|
+
`/ow:decision`, `/ow:spec`, or `/ow:change`.
|
|
79
|
+
|
|
80
|
+
If validation fails, revise the vision or create a new validation contract
|
|
81
|
+
before generating implementation tasks.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Validation-First Protocol
|
|
2
|
+
|
|
3
|
+
Use this reference when deciding what should happen before implementation.
|
|
4
|
+
|
|
5
|
+
## Priority Means Validation Order
|
|
6
|
+
|
|
7
|
+
OpenWorkflow priority is not a sorted backlog. It is the order in which the
|
|
8
|
+
vision's critical assumptions must be proven.
|
|
9
|
+
|
|
10
|
+
Ask:
|
|
11
|
+
|
|
12
|
+
- What feature makes the idea distinct?
|
|
13
|
+
- If this feature fails, does the product collapse into a generic app?
|
|
14
|
+
- What can be faked, mocked, or hardcoded to test that feature?
|
|
15
|
+
- What should be excluded because it does not answer the core question?
|
|
16
|
+
|
|
17
|
+
## Feature Classes
|
|
18
|
+
|
|
19
|
+
`existential`:
|
|
20
|
+
|
|
21
|
+
- The vision fails without it.
|
|
22
|
+
- It should usually be validated first.
|
|
23
|
+
|
|
24
|
+
`supporting`:
|
|
25
|
+
|
|
26
|
+
- It helps the existential feature become useful.
|
|
27
|
+
- It can be mocked if the validation question does not depend on full fidelity.
|
|
28
|
+
|
|
29
|
+
`later`:
|
|
30
|
+
|
|
31
|
+
- Valuable after the core experience works.
|
|
32
|
+
- Usually includes auth, sharing, import/export, admin, analytics, billing, and
|
|
33
|
+
scale work unless the vision is specifically about those capabilities.
|
|
34
|
+
|
|
35
|
+
`out_of_scope`:
|
|
36
|
+
|
|
37
|
+
- Explicitly excluded from this validation loop.
|
|
38
|
+
|
|
39
|
+
## Prototype Brief Rule
|
|
40
|
+
|
|
41
|
+
The prototype should include the minimum surface that lets a user, reviewer, or
|
|
42
|
+
agent answer the core question. It should exclude production hardening unless
|
|
43
|
+
that hardening is the core assumption.
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
|
|
47
|
+
For an AI-native travel memory globe, the first validation is not login,
|
|
48
|
+
database schema, upload flow, or chat orchestration. It is whether an
|
|
49
|
+
interactive globe with flags, hover photo previews, and a detail panel can feel
|
|
50
|
+
like the product's central experience.
|
|
51
|
+
|