
Issue Driven Workflow
- 1 installs
- 130 repo stars
- Updated July 11, 2026
- appautomaton/agent-designer
issue-driven-workflow is a Claude skill that turns a complex task into a plan and trackable Issue CSV, then executes the rows autonomously.
About
This skill breaks a complex task into a structured plan and a trackable Issue CSV, then executes the rows autonomously. A developer uses it when a task has multiple steps, needs research first, or benefits from status tracking before execution. It runs a plan -> issues -> implement -> test -> review loop, ordering CSV rows by dependency and updating each row's status as work completes.
- Breaks complex tasks into a plan and trackable Issue CSV
- Runs a plan -> issues -> implement -> test -> review loop
- Executes CSV rows autonomously in dependency order with status tracking
Issue Driven Workflow by the numbers
- 1 all-time installs (skills.sh)
- Ranked #2,479 of 3,282 Productivity & Planning skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
issue-driven-workflow capabilities & compatibility
- Capabilities
- planning · project management · orchestration
- Use cases
- planning · project management · orchestration
What issue-driven-workflow says it does
Break down complex tasks into a structured plan and trackable Issue CSV, then execute autonomously.
The CSV is your execution state. Read it to know where you are, update it as you work, keep driving forward.
npx skills add https://github.com/appautomaton/agent-designer --skill issue-driven-workflowAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 130 |
| Last updated | July 11, 2026 |
| Repository | appautomaton/agent-designer ↗ |
What it does
Turn a multi-step task into a dependency-ordered Issue CSV and execute the rows autonomously with status tracking.
Who is it for?
Multi-step tasks that need research, status tracking, and a structured breakdown before execution.
Skip if: Trivial single-step tasks that do not need a plan or CSV tracking.
When should I use this skill?
A task has multiple steps or benefits from a structured, trackable breakdown before execution.
What you get
A validated Issue CSV drives autonomous, dependency-ordered execution with per-row status.
- Plan markdown file
- Trackable Issue CSV with per-row status
By the numbers
- Three complexity tiers: simple, medium, complex
- Three planning scripts: create_plan, list_plans, validate_issues_csv
Files
Issue-Driven Workflow
Philosophy
The plan and Issue CSV are a work amplifier. Front-load the thinking so the agent has a full plate of actionable work to execute autonomously — more rows means more useful work per run.
1. Planning (interactive) — search the web, read docs, ask questions, gather context. A thorough plan means more work the agent can do without stopping. 2. Execution (autonomous) — be proactive, not passive. Work through the CSV end-to-end. Maximize useful work per run — don't wait for permission on routine decisions.
The quality bar: every CSV row should be completable, testable, and markable DONE without further clarification.
E2E loop
plan → issues → implement → test → review
Planning (interactive)
1. Restate the task and assumptions. 2. Gather context — search the web, read project files, inspect dependencies. Make every plan section concrete, not aspirational. 3. Ask up to 2 clarification questions if unclear, then proceed with stated assumptions. 4. Draft the plan in chat using assets/_template.md. Choose complexity (simple|medium|complex). 5. Ask: "Reply CONFIRM to write the plan file." 6. On confirmation:
python3 .codex/skills/issue-driven-workflow/scripts/create_plan.py \
--task "<title>" --complexity <simple|medium|complex>7. Do not edit code while planning.
Creating the CSV (interactive)
1. Generate after the plan is approved. 2. Break the plan into granular, independently actionable rows. 3. Fill all required columns — see references/issue-csv-spec.md. 4. Order by dependency chain. Set Dependencies so execution order is unambiguous. 5. Validate:
python3 .codex/skills/issue-driven-workflow/scripts/validate_issues_csv.py <issues.csv>Executing the CSV (autonomous)
The CSV is your execution state. Read it to know where you are, update it as you work, keep driving forward.
1. Read the CSV. Find the next TODO row in dependency order. 2. Set Dev_Status = DOING. Start working. 3. Complete the row — search, read, write, test, whatever it requires. 4. When Acceptance is met and Test_Method passes, set Dev_Status = DONE. 5. Self-review. Mark Review1_Status = DONE. 6. Immediately move to the next row. Read the CSV again, pick the next TODO, keep going. 7. After all rows are DONE, regression check. Mark Regression_Status = DONE per row. 8. Report progress briefly as you complete rows. 9. Only stop for genuinely blocking unknowns that affect correctness, safety, or irreversible actions.
If a row is too large, split it. If a row fails, fix it or flag it. If in a git repo, commit at natural boundaries. Edit the CSV directly — re-validate after edits.
Scripts
| Script | Purpose |
|---|---|
create_plan.py | Create a plan file with YAML frontmatter under plan/ |
list_plans.py | List existing plans (supports --query, --json) |
validate_issues_csv.py | Validate Issue CSV schema and status values |
Run with --help first. Scripts live in scripts/ relative to this skill.
Naming
Plans: plan/YYYY-MM-DD_HH-mm-ss-<slug>.md — Issue CSVs: issues/YYYY-MM-DD_HH-mm-ss-<slug>.csv — same timestamp/slug.
References
- Issue CSV spec — read when creating or validating CSVs
- Testing policy — read when filling `Test_Method`
- Plan template — structure with complexity tiers
- CSV template — example rows with plan-to-CSV mapping
ID,Title,Description,Acceptance,Test_Method,Tools,Dev_Status,Review1_Status,Regression_Status,Files,Dependencies,Notes
A1,Backend token validation,"Handle invalid/expired tokens in /auth/login","Returns 401 with structured error code","pytest tests/test_auth.py -k test_invalid_token",none,TODO,TODO,TODO,"src/auth/login.ts | src/auth/token.ts",none,"Phase 1"
A2,SSO provider integration,"Integrate SSO provider OAuth flow","User redirected to SSO and returned with valid session","pytest tests/test_auth.py -k test_sso_flow",none,TODO,TODO,TODO,"src/auth/sso.ts | src/auth/session.ts",A1,"Phase 1"
A3,Dashboard renders after SSO,"Dashboard loads within 3s after SSO login","Page renders with user profile and data","manual: login via SSO then open /dashboard",playwright:browser_navigate,TODO,TODO,TODO,"src/pages/dashboard.tsx",A2,"Phase 2"
<!-- Complexity guide: simple — bug fix, small feature, config change (7 sections) medium — multi-file feature, refactor, migration (9 sections) complex — cross-system change, new subsystem, risky migration (12 sections) -->
Plan: <short title>
Goal
<!-- One clear sentence: what does "done" look like? -->
- Example: Users can log in with SSO and see their dashboard within 3 seconds.
Scope
- In: <!-- what this plan covers -->
- Out: <!-- what is explicitly excluded -->
Assumptions / Dependencies
<!-- External requirements, team decisions, or things that must be true -->
- Example: Auth service v2 API is deployed and stable.
- Example: Database migration for
userstable is already applied.
Phases
<!-- Ordered steps. Each phase should be independently testable. --> 1. Phase 1 — <description> 2. Phase 2 — <description>
Tests & Verification
<!-- Map each requirement to its test method. Use the narrowest reliable method. -->
- Login with valid token ->
pytest tests/test_auth.py -k test_valid_login - Login with expired token ->
pytest tests/test_auth.py -k test_expired_token - UI renders dashboard -> manual: open /dashboard, verify layout
Issue CSV
- Path: issues/<YYYY-MM-DD_HH-mm-ss>-<slug>.csv
- Must share the same timestamp/slug as this plan.
- Column spec:
references/issue-csv-spec.md
<!-- How the plan maps to CSV rows: Plan section → CSV column ───────────────────────────────── Phases → rows (each phase = one or more issue rows, e.g., Phase 1 → A1, A2) Scope: In → Description Tests & Verification→ Test_Method Tools / MCP → Tools (server:tool format) Acceptance Checklist→ Acceptance Assumptions / Deps → Dependencies References → Notes (or file paths in Files column) -->
Acceptance Checklist
<!-- Concrete, verifiable items. Each should be pass/fail. -->
- [ ] All tests pass
- [ ] No regressions in existing auth flow
- [ ] PR reviewed and approved
<!-- medium and complex plans: include the sections below -->
Risks / Blockers
<!-- What could go wrong? What would delay this? -->
- Example: If auth service v2 is unstable, SSO login will fail intermittently.
- Example: No staging environment available for E2E testing.
References
<!-- File paths with line numbers, docs, or external links -->
- src/auth/login.ts:42 — current token validation logic
- https://docs.example.com/auth-v2 — API spec
<!-- complex plans only: include the sections below -->
Tools / MCP
<!-- MCP tools needed. Use server:tool format from your available tools. -->
- playwright:browser_navigate — E2E login flow testing
- context7:get-library-docs — check auth library API
Rollback / Recovery
<!-- How to undo if something goes wrong -->
- Revert migration:
python manage.py migrate auth 0042 - Feature flag: disable
SSO_ENABLEDin config
Checkpoints
<!-- When to commit / create a reviewable unit -->
- Commit after: Phase 1 (backend auth changes)
- Commit after: Phase 2 (frontend integration)
- Tag:
v1.2.0-rc1after all issues pass regression
Issue CSV Specification
This repo uses Issue CSV as the execution contract for each plan.
Required columns
All columns are required and must be populated:
| Column | Description |
|---|---|
| ID | Unique issue ID (A1, A2, ...) |
| Title | Short title |
| Description | Scope/boundary |
| Acceptance | Done criteria |
| Test_Method | How to verify (tool, command, or manual) |
| Tools | MCP/tool to use (server:tool format) or "manual"/"none" |
| Dev_Status | TODO \ |
| Review1_Status | TODO \ |
| Regression_Status | TODO \ |
| Files | Paths or scope (use a sentinel if none) |
| Dependencies | Other IDs or external deps (use "none" if none) |
| Notes | Extra context (use "none" if none) |
Status fields
- Dev_Status: implementation progress.
- Review1_Status: verification after the issue is implemented.
- Regression_Status: verification after all issues are complete (full pass/smoke).
Values are always TODO | DOING | DONE — never percentages, never null.
Only mark Review1/Regression as DONE after the declared Test_Method runs and passes, or if manual/not feasible is explicitly recorded with risk noted.
Sentinel values
Use these when a field is required but not applicable:
| Field | Allowed sentinels |
|---|---|
| Files | N/A · external · TBD · module:<name> · <glob> |
| Dependencies | none |
| Notes | none |
| Tools | manual · none |
Test_Method guidance
Every issue must specify how it will be verified. Use the narrowest reliable method:
- Unit / Integration: prefer if a test harness exists and the change is logic-heavy.
- API / Contract: for backend or service changes (e.g., curl, Postman, AUTOCURL).
- UI / E2E: for frontend flows (e.g., Playwright or Chrome DevTools MCP).
- Manual: only if automation is impractical; include the exact steps.
CSV formatting
- If a field contains commas, wrap the field in double quotes.
- Use
|inside a field to list multiple values.
Example rows
ID,Title,Description,Acceptance,Test_Method,Tools,Dev_Status,Review1_Status,Regression_Status,Files,Dependencies,Notes
A1,Backend token validation,"Handle invalid/expired tokens in /auth/login","Returns 401 with structured error code","pytest tests/test_auth.py -k test_invalid_token",none,TODO,TODO,TODO,"src/auth/login.ts | src/auth/token.ts",none,"Phase 1"
A2,SSO provider integration,"Integrate SSO provider OAuth flow","User redirected to SSO and returned with valid session","pytest tests/test_auth.py -k test_sso_flow",none,TODO,TODO,TODO,"src/auth/sso.ts | src/auth/session.ts",A1,"Phase 1"
A3,Dashboard renders after SSO,"Dashboard loads within 3s after SSO login","Page renders with user profile and data","manual: login via SSO then open /dashboard",playwright:browser_navigate,TODO,TODO,TODO,"src/pages/dashboard.tsx",A2,"Phase 2"Testing Policy
Consistent verification across tasks while allowing domain-specific methods.
Required per issue
- Set
Test_Methodin the Issue CSV (command, tool, or "manual"). - Set
Tools(or "manual"/"none") in the Issue CSV. - If
Test_Methodis manual, add a short checklist inNotesorAcceptance.
Default test layers
| Layer | Purpose |
|---|---|
| Unit | Fast checks on individual components |
| Integration | Real dependencies or realistic stubs |
| E2E / Acceptance | Critical user flows or end-to-end verification |
| Regression | Full suite or critical subset after batch completion |
Minimum expectations by task type
| Task type | Required testing |
|---|---|
| Backend logic | Unit + Integration |
| API changes | Integration + contract verification |
| Frontend UI | UI/E2E (or manual checklist if no automation) |
| Data/schema | Migration test + rollback check |
| Performance-sensitive | Targeted perf check or benchmark |
| Research / analysis | Manual review of outputs against acceptance criteria |
| Content / documentation | Manual review or automated lint/link check |
| Infrastructure / config | Smoke test or dry-run verification |
When automation is missing
- Use
Test_Method = manual. - Include a repeatable checklist (steps + expected outcome).
- Add a risk note in the plan if coverage is incomplete.
Regression policy
- After all issues in a batch are DONE, run a regression pass.
- Failures must be fixed before marking
Regression_Status = DONE.
Command format (examples)
pytest -q · npm test · pnpm test:e2e · go test ./... · manual: verify output matches spec
#!/usr/bin/env python3
"""Create a repo-local plan markdown file under ./plan."""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
from plan_utils import (
build_plan_filename,
format_yaml_value,
get_assets_dir,
get_plans_dir,
now_iso,
now_timestamp,
replace_placeholders,
slugify,
validate_slug,
)
ALLOWED_COMPLEXITY = {"simple", "medium", "complex"}
def read_body(args: argparse.Namespace) -> str | None:
if args.template:
template_path = get_assets_dir() / "_template.md"
return template_path.read_text(encoding="utf-8")
if args.body_file:
return Path(args.body_file).read_text(encoding="utf-8")
if not sys.stdin.isatty():
return sys.stdin.read()
return None
def main() -> int:
parser = argparse.ArgumentParser(description="Create a plan file under ./plan.")
parser.add_argument("--task", required=True, help="Short task/title for the plan.")
parser.add_argument(
"--complexity",
default="medium",
choices=sorted(ALLOWED_COMPLEXITY),
help="Plan complexity: simple|medium|complex.",
)
parser.add_argument(
"--slug",
help="Optional slug override for the plan filename (lower-case, hyphen-delimited).",
)
parser.add_argument(
"--timestamp",
help="Override timestamp for the filename (YYYY-MM-DD_HH-mm-ss).",
)
parser.add_argument(
"--created-at",
dest="created_at",
help="Override created_at frontmatter (ISO8601).",
)
parser.add_argument(
"--body-file",
help="Path to markdown body (without frontmatter). If omitted, read from stdin.",
)
parser.add_argument(
"--template",
action="store_true",
help="Use the skill's plan template instead of stdin or --body-file.",
)
parser.add_argument(
"--overwrite",
action="store_true",
help="Overwrite the plan file if it already exists.",
)
args = parser.parse_args()
task = args.task.strip()
if not task or "\n" in task:
raise SystemExit("Task must be a single line.")
slug = args.slug.strip() if args.slug else slugify(task)
validate_slug(slug)
timestamp = args.timestamp.strip() if args.timestamp else now_timestamp()
filename = build_plan_filename(timestamp, slug)
created_at = args.created_at.strip() if args.created_at else now_iso()
body = read_body(args)
if body is None:
raise SystemExit("Provide --body-file, stdin, or --template to supply plan content.")
body = body.strip()
if not body:
raise SystemExit("Plan body cannot be empty.")
if body.lstrip().startswith("---"):
raise SystemExit("Plan body should not include frontmatter.")
body = replace_placeholders(body, timestamp, slug)
plans_dir = get_plans_dir()
plans_dir.mkdir(parents=True, exist_ok=True)
plan_path = plans_dir / filename
if plan_path.exists() and not args.overwrite:
raise SystemExit(f"Plan already exists: {plan_path}. Use --overwrite to replace.")
frontmatter = (
"---\n"
f"mode: {format_yaml_value('plan')}\n"
f"task: {format_yaml_value(task)}\n"
f"created_at: {format_yaml_value(created_at)}\n"
f"complexity: {format_yaml_value(args.complexity)}\n"
"---\n\n"
)
plan_path.write_text(frontmatter + body + "\n", encoding="utf-8")
print(str(plan_path))
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""List repo-local plan summaries by reading frontmatter only."""
from __future__ import annotations
import argparse
import json
from plan_utils import get_plans_dir, parse_frontmatter
REQUIRED_KEYS = {"task", "created_at", "complexity"}
def main() -> int:
parser = argparse.ArgumentParser(description="List plan summaries from ./plan.")
parser.add_argument("--query", help="Case-insensitive substring to filter task/path.")
parser.add_argument("--json", action="store_true", help="Emit JSON output.")
args = parser.parse_args()
plans_dir = get_plans_dir()
if not plans_dir.exists():
raise SystemExit(f"Plans directory not found: {plans_dir}")
query = args.query.lower() if args.query else None
items = []
for path in sorted(plans_dir.glob("*.md")):
try:
data = parse_frontmatter(path)
except ValueError:
continue
if not REQUIRED_KEYS.issubset(data.keys()):
continue
task = data.get("task", "")
created_at = data.get("created_at", "")
complexity = data.get("complexity", "")
if query:
haystack = f"{task} {path}".lower()
if query not in haystack:
continue
items.append(
{
"task": task,
"created_at": created_at,
"complexity": complexity,
"path": str(path),
}
)
if args.json:
print(json.dumps(items))
else:
for item in items:
print(
f"{item['task']}\t{item['created_at']}\t{item['complexity']}\t{item['path']}"
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Shared helpers for repo-local plan scripts."""
from __future__ import annotations
import re
from pathlib import Path
from datetime import datetime
_NAME_RE = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$")
_FILENAME_RE = re.compile(
r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}-[a-z0-9]+(?:-[a-z0-9]+)*\.md$"
)
_TIMESTAMP_RE = re.compile(r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}$")
def get_repo_root(start: Path | None = None) -> Path:
"""Find the repo root by walking up to .git or AGENTS.md."""
path = (start or Path.cwd()).resolve()
for candidate in [path, *path.parents]:
if (candidate / ".git").exists() or (candidate / "AGENTS.md").exists():
return candidate
return path
def get_plans_dir() -> Path:
return get_repo_root() / "plan"
def get_issues_dir() -> Path:
return get_repo_root() / "issues"
def get_assets_dir() -> Path:
return Path(__file__).resolve().parents[1] / "assets"
def slugify(text: str) -> str:
slug = re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-")
slug = re.sub(r"-{2,}", "-", slug)
return slug or "plan"
def validate_slug(slug: str) -> None:
if not slug or not _NAME_RE.match(slug):
raise ValueError(
"Invalid slug. Use short, lower-case, hyphen-delimited names "
"(e.g., update-login-flow)."
)
def validate_timestamp(timestamp: str) -> None:
if not _TIMESTAMP_RE.match(timestamp):
raise ValueError("Timestamp must be in YYYY-MM-DD_HH-mm-ss format.")
def build_plan_filename(timestamp: str, slug: str) -> str:
validate_timestamp(timestamp)
validate_slug(slug)
return f"{timestamp}-{slug}.md"
def format_yaml_value(value: str) -> str:
if value is None:
return ""
needs_quotes = (
not value
or value.strip() != value
or "\n" in value
or any(ch in value for ch in (":", "#", "{", "}", "[", "]", ","))
)
if needs_quotes:
escaped = value.replace('"', "\\\"")
return f'"{escaped}"'
return value
def replace_placeholders(body: str, timestamp: str, slug: str) -> str:
body = body.replace("issues/<YYYY-MM-DD_HH-mm-ss>-<slug>.csv", f"issues/{timestamp}-{slug}.csv")
body = body.replace("<YYYY-MM-DD_HH-mm-ss>", timestamp)
body = body.replace("<slug>", slug)
return body
def validate_plan_filename(filename: str) -> None:
if not _FILENAME_RE.match(filename):
raise ValueError(
"Invalid plan filename. Expected YYYY-MM-DD_HH-mm-ss-<slug>.md."
)
def parse_frontmatter(path: Path) -> dict:
"""Parse YAML frontmatter from a markdown file without reading the body."""
with path.open("r", encoding="utf-8") as handle:
first = handle.readline()
if first.strip() != "---":
raise ValueError("Frontmatter must start with '---'.")
data: dict[str, str] = {}
for line in handle:
stripped = line.strip()
if stripped == "---":
return data
if not stripped or stripped.startswith("#"):
continue
if ":" not in line:
raise ValueError(f"Invalid frontmatter line: {line.rstrip()}")
key, value = line.split(":", 1)
key = key.strip()
value = value.strip()
if value and len(value) >= 2 and value[0] == value[-1] and value[0] in ("\"", "'"):
value = value[1:-1]
data[key] = value
raise ValueError("Frontmatter must end with '---'.")
def now_timestamp() -> str:
return datetime.now().astimezone().strftime("%Y-%m-%d_%H-%M-%S")
def now_iso() -> str:
return datetime.now().astimezone().isoformat(timespec="seconds")
#!/usr/bin/env python3
"""Read plan frontmatter without loading the full markdown body."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from plan_utils import parse_frontmatter
REQUIRED_KEYS = {"task", "created_at", "complexity", "mode"}
def main() -> int:
parser = argparse.ArgumentParser(description="Read frontmatter from a plan markdown file.")
parser.add_argument("plan_path", help="Path to the plan markdown file.")
parser.add_argument("--json", action="store_true", help="Emit JSON output.")
args = parser.parse_args()
path = Path(args.plan_path).expanduser()
if not path.exists():
raise SystemExit(f"Plan not found: {path}")
data = parse_frontmatter(path)
if not REQUIRED_KEYS.issubset(data.keys()):
missing = sorted(REQUIRED_KEYS - set(data.keys()))
raise SystemExit(f"Frontmatter missing required fields: {', '.join(missing)}")
payload = {
"mode": data.get("mode"),
"task": data.get("task"),
"created_at": data.get("created_at"),
"complexity": data.get("complexity"),
"path": str(path),
}
if args.json:
print(json.dumps(payload))
else:
for key in ("mode", "task", "created_at", "complexity", "path"):
print(f"{key}: {payload[key]}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Validate issues CSV schema and required fields."""
from __future__ import annotations
import csv
import sys
from pathlib import Path
REQUIRED_COLUMNS = [
"ID",
"Title",
"Description",
"Acceptance",
"Test_Method",
"Tools",
"Dev_Status",
"Review1_Status",
"Regression_Status",
"Files",
"Dependencies",
"Notes",
]
STATUS_FIELDS = {"Dev_Status", "Review1_Status", "Regression_Status"}
ALLOWED_STATUS = {"TODO", "DOING", "DONE"}
def fail(message: str) -> int:
print(f"error: {message}", file=sys.stderr)
return 1
def main() -> int:
if len(sys.argv) != 2:
return fail("usage: validate_issues_csv.py <issues.csv>")
path = Path(sys.argv[1])
if not path.exists():
return fail(f"file not found: {path}")
rows = []
with path.open(newline="", encoding="utf-8") as handle:
reader = csv.reader(handle)
for row in reader:
if any(cell.strip() for cell in row):
rows.append(row)
if not rows:
return fail("csv is empty")
header = rows[0]
if header != REQUIRED_COLUMNS:
return fail(
"invalid header. expected: "
+ ",".join(REQUIRED_COLUMNS)
+ " | got: "
+ ",".join(header)
)
seen_ids: set[str] = set()
for idx, row in enumerate(rows[1:], start=2):
if len(row) != len(REQUIRED_COLUMNS):
return fail(f"row {idx}: expected {len(REQUIRED_COLUMNS)} columns, got {len(row)}")
row_data = dict(zip(REQUIRED_COLUMNS, row))
for col, value in row_data.items():
if not value.strip():
return fail(f"row {idx}: '{col}' is empty")
if col in STATUS_FIELDS and value.strip() not in ALLOWED_STATUS:
return fail(
f"row {idx}: '{col}' must be one of {sorted(ALLOWED_STATUS)}, got '{value}'"
)
issue_id = row_data["ID"].strip()
if issue_id in seen_ids:
return fail(f"row {idx}: duplicate ID '{issue_id}'")
seen_ids.add(issue_id)
return 0
if __name__ == "__main__":
raise SystemExit(main())
Related skills
FAQ
What is the execution loop?
plan -> issues -> implement -> test -> review, with the Issue CSV as the execution state.
When should you use it?
When a task has multiple steps, needs research before starting, or benefits from status tracking.