diff --git a/docs/architecture/.gitignore b/docs/architecture/.gitignore new file mode 100644 index 0000000..69d29e4 --- /dev/null +++ b/docs/architecture/.gitignore @@ -0,0 +1,2 @@ +# Regenerable raster exports — rebuild with ./gen.sh +rendered/*.png diff --git a/docs/architecture/README.md b/docs/architecture/README.md new file mode 100644 index 0000000..c410da5 --- /dev/null +++ b/docs/architecture/README.md @@ -0,0 +1,72 @@ +# Atlas engine — architecture diagram suite (INTERNAL) + +A multi-level map of the `prd-taskmaster` engine: the whole system, each component, the code +flow, and — the point of the suite — **what is deterministic Python script vs what is an LLM / +agent step**, per phase. Not linked from the public README; this is for understanding/maintaining +the engine. + +> The engine is **function-heavy**: 28 modules · 78 imports · **309 functions vs only 8 classes**, +> and **no import cycles**. So UML class diagrams are thin; the load-bearing views are the +> **import graph, the call graph, and the per-phase script-vs-LLM swimlanes**. + +## Legend (used in every hand-authored diagram) + +Okabe-Ito colorblind-safe palette + shape redundancy (survives grayscale): + +| kind | color | shape | +|---|---|---| +| **LLM / agent** (output varies run-to-run) | orange `#E69F00` | hexagon | +| **deterministic engine code** (Python) | sky blue `#56B4E9` | rectangle | +| **interface boundary** (MCP / CLI, fail-closed) | blue `#0072B2` | rectangle | +| **external model execution** | green `#009E73` | oval / stadium | +| **gate** (deterministic checkpoint) | yellow `#F0E442` | diamond | +| **human** | purple `#CC79A7` | person | + +Edges: **solid** = deterministic control flow · **dashed** = LLM-routed choice / bounce-back. + +## Toolchain + +- **D2** (`*.d2`) — system overview + component breakouts (drill-down architecture). +- **Mermaid** (`*.mmd`) — per-phase script-vs-LLM swimlanes + the parse-PRD sequence (GitHub-native, diffable). +- **pyreverse / code2flow / pydeps** — auto-derived import / class / call graphs + cycle check (regenerated from source → never drift). +- AST `gen-fnmap.py` — the per-module function-signature map (the real "down to methods" artifact). + +## The suite + +| # | file | level | how | shows | +|---|---|---|---|---| +| 00 | `src/00-system-overview.d2` | system | D2 (hand) | the whole engine in 4 layers: LLM prompts → fail-closed interface (MCP/CLI) → deterministic core → external models | +| 10 | `src/10-component-deterministic-core.d2` | component | D2 (hand) | phase state-machine + gates: pipeline, shipcheck, validation, tasks, task_state, fleet, parallel, lib | +| 11 | `src/11-component-backend-provider-engine.d2` | component | D2 (hand) | the GENERATE engine: backend → resolve_provider (cli/api/plan) → cli_agent / llm_client / plan-floor, wrapped by economy | +| 12 | `src/12-component-detect-setup-mode.d2` | component | D2 (hand) | detection/SETUP: batch → preflight, validate_setup, detect_capabilities, providers, setup_wizard | +| 20 | `generated/20-module-function-map.md` + `generated/classes_AtlasEngine.mmd` | class/fn | auto | every module's functions (signatures) + the 8 real classes | +| 30 | `generated/packages_AtlasEngine.mmd/.svg` + `generated/30-import-cycles.txt` | code-flow | auto | the 28-module / 78-import dependency graph (cycle check: none) | +| 31 | `generated/callflow-core.dot/.svg` | code-flow | auto | function-level call graph across the deterministic spine (13 modules) | +| 40 | `src/40-runflow-script-vs-llm.d2` | code-flow | D2 (hand) | end-to-end `/atlas` run, every step colored script vs LLM, to `SHIP_CHECK_OK` | +| 50 | `src/50-swimlane-setup.mmd` | swimlane | Mermaid (hand) | SETUP — deterministic-dominant | +| 51 | `src/51-swimlane-discover.mmd` | swimlane | Mermaid (hand) | DISCOVER — LLM-dominant | +| 52 | `src/52-swimlane-generate.mmd` | swimlane | Mermaid (hand) | GENERATE — the hybrid crux | +| 53 | `src/53-swimlane-handoff.mmd` | swimlane | Mermaid (hand) | HANDOFF — split (mode pick + routing) | +| 54 | `src/54-swimlane-execute.mmd` | swimlane | Mermaid (hand) | EXECUTE — deterministic-gated, LLM-executed (anti-fake ship gate) | +| 60 | `src/60-sequence-generate-parse-prd.mmd` | sequence | Mermaid (hand) | one parse-PRD call across the three provider tiers | + +Rendered images are in `rendered/`. The `.d2`/`.mmd`/`.md` sources are the source of truth; +`generated/` is regenerate-on-demand (do not hand-edit). + +## Regenerate + +```bash +./docs/architecture/gen.sh +``` + +Needs on PATH: `python3`, `d2`, `mmdc` (`@mermaid-js/mermaid-cli`), `dot` (graphviz), +`rsvg-convert` (librsvg), `chromium`. Analyzers (pylint/pydeps/code2flow) install into a +throwaway `/tmp` venv. + +## Per-phase script-vs-LLM split (summary) + +- **SETUP** — deterministic-dominant: `batch.run_engine_preflight` → `preflight`, `validate_setup` (6 checks), `providers.*`, `setup_wizard` (+ `_live_probe`), `check_gate('SETUP')`. LLM only reads panels + decides. +- **DISCOVER** — LLM-dominant: `superpowers:brainstorming`, adaptive Q&A, constraint extraction, AskUserQuestion. Code = only `check_gate('DISCOVER')`. +- **GENERATE** — hybrid: LLM writes PRD + task JSON; code = `validate_prd` (placeholder HARD FAIL), `resolve_provider`, `backend.parse_prd/expand`, `economy.shift_tier`, `parallel.apply_results`, `validate_tasks`, `check_gate('GENERATE')`. +- **HANDOFF** — split: code = `detect_capabilities`, `fleet.compute_waves/route_task`, `check_gate('HANDOFF')`; LLM recommends one mode + writes the workflow block. +- **EXECUTE** — deterministic-gated: code = `task_state.*` (flock), `shipcheck.py` 5 gates → `SHIP_CHECK_OK` (Gate 5 `EXIT_STATUS_RE` = the anti-fake gate); LLM does the coding + CDD evidence. The model cannot self-declare shipped. diff --git a/docs/architecture/gen-fnmap.py b/docs/architecture/gen-fnmap.py new file mode 100755 index 0000000..f5a3abc --- /dev/null +++ b/docs/architecture/gen-fnmap.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +"""AUTO-GENERATED helper: emit a per-module function/class signature map (Markdown). +Usage: python3 gen-fnmap.py > generated/20-module-function-map.md +Static AST only — does not import the target package.""" +import ast, sys, pathlib + +root = pathlib.Path(sys.argv[1]) +out = ["# Module -> function / class map", "", + "_AUTO-GENERATED by gen.sh (gen-fnmap.py) — do not edit; regenerate._", ""] + + +def sig(fn: ast.AST) -> str: + a = fn.args + parts = [ar.arg for ar in a.posonlyargs] + [ar.arg for ar in a.args] + if a.vararg: + parts.append("*" + a.vararg.arg) + if a.kwonlyargs and not a.vararg: + parts.append("*") + parts += [ar.arg for ar in a.kwonlyargs] + if a.kwarg: + parts.append("**" + a.kwarg.arg) + ret = "" + try: + if fn.returns is not None: + ret = " -> " + ast.unparse(fn.returns) + except Exception: + ret = "" + return f"{fn.name}({', '.join(parts)}){ret}" + + +for p in sorted(root.glob("*.py")): + if p.name == "__init__.py": + continue + try: + tree = ast.parse(p.read_text()) + except Exception as e: # noqa: BLE001 + out.append(f"## `{p.name}`\n_parse error: {e}_\n") + continue + funcs = [n for n in tree.body if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))] + classes = [n for n in tree.body if isinstance(n, ast.ClassDef)] + out.append(f"## `{p.name}` ({len(funcs)} functions, {len(classes)} classes)") + for c in classes: + meths = [m for m in c.body if isinstance(m, (ast.FunctionDef, ast.AsyncFunctionDef))] + out.append(f"- **class {c.name}** — methods: " + (", ".join(m.name for m in meths) or "(none)")) + if funcs: + out.append("") + for f in funcs: + out.append(f"- `{sig(f)}`") + out.append("") + +print("\n".join(out)) diff --git a/docs/architecture/gen.sh b/docs/architecture/gen.sh new file mode 100755 index 0000000..a6b94aa --- /dev/null +++ b/docs/architecture/gen.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# Regenerate the Atlas internal architecture diagram suite. +# - auto-derived graphs (pyreverse / code2flow / pydeps + AST fn-map) -> generated/ +# - hand-authored sources (src/*.d2, src/*.mmd) rendered -> rendered/ +# +# Requires on PATH: python3, d2, mmdc (@mermaid-js/mermaid-cli), dot (graphviz), +# rsvg-convert (librsvg), chromium (for mmdc). +# Analyzers (pylint/pydeps/code2flow) are installed into a throwaway venv at /tmp. +set -euo pipefail + +HERE="$(cd "$(dirname "$0")" && pwd)" +REPO="$(cd "$HERE/../.." && pwd)" +PKG="$REPO/prd_taskmaster" +SRC="$HERE/src"; GEN="$HERE/generated"; REND="$HERE/rendered" +mkdir -p "$GEN" "$REND" + +VENV=/tmp/atlas-diagrams-venv +python3 -m venv "$VENV" 2>/dev/null || true +"$VENV/bin/pip" install -q --disable-pip-version-check pylint pydeps code2flow + +echo "==> auto-derive (pyreverse / code2flow / fn-map / pydeps)" +"$VENV/bin/pyreverse" -o mmd -p AtlasEngine -d "$GEN" "$PKG/" # 30 import + class (.mmd) +"$VENV/bin/pyreverse" -o svg -p AtlasEngine -d "$GEN" "$PKG/" 2>/dev/null || true +CORE=(pipeline shipcheck validation tasks task_state fleet parallel backend provider_resolver economy preflight mode_recommend lib) +"$VENV/bin/code2flow" --language py -o "$GEN/callflow-core.dot" \ + "${CORE[@]/#/$PKG/}" >/dev/null 2>&1 || \ + "$VENV/bin/code2flow" --language py -o "$GEN/callflow-core.dot" $(printf "%s.py " "${CORE[@]/#/$PKG/}") +# (the line above tolerates code2flow needing explicit .py paths) +dot -Tsvg "$GEN/callflow-core.dot" -o "$GEN/callflow-core.svg" +python3 "$HERE/gen-fnmap.py" "$PKG" > "$GEN/20-module-function-map.md" +"$VENV/bin/pydeps" "$PKG" --no-output --show-cycles > "$GEN/30-import-cycles.txt" 2>&1 || true + +echo "==> puppeteer config for mmdc (system chromium)" +CHROME="$(command -v chromium || command -v chromium-browser || true)" +PUP=/tmp/puppeteer-mmd.json +printf '{ "executablePath": "%s", "args": ["--no-sandbox","--disable-gpu"] }\n' "$CHROME" > "$PUP" + +echo "==> render mermaid (src/*.mmd)" +for m in "$SRC"/*.mmd; do + b="$(basename "$m" .mmd)" + mmdc -p "$PUP" -i "$m" -o "$REND/$b.svg" + rsvg-convert -z 1.4 -o "$REND/$b.png" "$REND/$b.svg" + echo " $b" +done + +echo "==> render d2 (src/*.d2)" +for d in "$SRC"/*.d2; do + b="$(basename "$d" .d2)" + d2 --layout elk "$d" "$REND/$b.svg" + rsvg-convert -z 1.5 -o "$REND/$b.png" "$REND/$b.svg" + echo " $b" +done + +echo "==> done. sources in src/, auto-derived in generated/, images in rendered/." diff --git a/docs/architecture/generated/20-module-function-map.md b/docs/architecture/generated/20-module-function-map.md new file mode 100644 index 0000000..230dbb2 --- /dev/null +++ b/docs/architecture/generated/20-module-function-map.md @@ -0,0 +1,378 @@ +# Module -> function / class map + +_AUTO-GENERATED by gen.sh (gen-fnmap.py) — do not edit; regenerate._ + +## `backend.py` (18 functions, 2 classes) +- **class Backend** — methods: detect, init_project, parse_prd, expand, rate +- **class NativeBackend** — methods: detect, init_project, parse_prd, expand, _expand_packet, _packet_success, rate + +- `_task_id_set(task_ids) -> set[str] | None` +- `_load_tasks(tag) -> tuple[str, list[dict]]` +- `_pending_tasks(tag, task_ids) -> tuple[str, list[dict]]` +- `_read_json(path) -> dict | None` +- `_candidate_tasks(candidate) -> list[dict]` +- `_validate_task_candidate(candidate) -> tuple[list[dict], dict]` +- `_load_existing_tagged() -> dict` +- `_write_tasks_into_tag(tasks, tag) -> str` +- `_native_concurrency(work_count, fleet_config, profile) -> int` +- `_task_summaries(tasks) -> list[dict]` +- `_complexity_report_path(tag) -> Path` +- `_agent_parse_action(prd_path, num_tasks, tag) -> dict` +- `_agent_expand_action(tag, task_ids, packets) -> dict` +- `_agent_rate_action(tag, summaries) -> dict` +- `_report_candidates(tag) -> list[Path]` +- `_cli_timeout(config) -> int` +- `_cli_structured_mode(config) -> str` +- `get_backend(cfg) -> Backend` + +## `batch.py` (6 functions, 0 classes) + +- `_backend_source() -> str` +- `_backend_ai_ops(native_detect) -> str` +- `_backend_block() -> dict` +- `_backend_summary(block) -> str` +- `run_engine_preflight(configure) -> dict` +- `cmd_engine_preflight(args) -> None` + +## `capabilities.py` (2 functions, 0 classes) + +- `run_detect_capabilities() -> dict` +- `cmd_detect_capabilities(args) -> None` + +## `cli.py` (20 functions, 0 classes) + +- `_backend_source() -> str` +- `_ai_ops(native_detect) -> str` +- `_taskmaster_file_format_detect() -> dict` +- `run_backend_detect() -> dict` +- `_selected_backend()` +- `run_init_project() -> dict` +- `run_parse_prd(input_path, num_tasks, tag) -> dict` +- `run_expand(task_ids, research, tag) -> dict` +- `run_rate(tag, research) -> dict` +- `_emit_with_status(result) -> None` +- `_cmd_backend_call(fn, *args, **kwargs) -> None` +- `cmd_backend_detect(args) -> None` +- `cmd_init_project(args) -> None` +- `cmd_parse_prd(args) -> None` +- `cmd_expand(args) -> None` +- `cmd_rate(args) -> None` +- `cmd_context_pack(args) -> None` +- `build_parser() -> argparse.ArgumentParser` +- `cmd_status(args) -> None` +- `main()` + +## `cli_agent.py` (7 functions, 1 classes) +- **class CliAgentError** — methods: __init__ + +- `_build_argv(provider, binary, prompt, *, schema_hint, structured_json)` +- `_telemetry(op_class, task_id, model, exit_code, start, parse_retry)` +- `_parse_claude_envelope(stdout)` +- `_claude_error_detail(stdout, stderr)` +- `_run_once(provider, binary, prompt, *, schema_hint, structured_json, model, op_class, task_id, timeout, parse_retry)` +- `_has_schema_flag(provider)` +- `generate_json_via_cli(provider, prompt, *, system, schema_hint, model, op_class, task_id, timeout, structured_json)` + +## `context_pack.py` (9 functions, 0 classes) + +- `build_context_pack(paths, include_private) -> dict` +- `_class_entry(node, source, include_private) -> dict` +- `_callable_entry(node, source) -> dict` +- `_filtered(name, include_private) -> bool` +- `_is_private(name) -> bool` +- `_signature_text(node, source) -> str` +- `_signature_from_segment(segment) -> str` +- `_line_starts(text) -> list[int]` +- `_index(line_starts, position) -> int` + +## `economy.py` (9 functions, 0 classes) + +- `shift_tier(tier, steps, floor, ceiling)` +- `economy_profile(cfg)` +- `append_telemetry(row, path)` +- `_token_int(value)` +- `_price_key_for_model(model)` +- `_estimate_cost_usd(tokens_in, tokens_out, rates)` +- `_summarize_costs(rows)` +- `summarize_telemetry(path)` +- `cmd_economy_report(args)` + +## `feedback.py` (10 functions, 0 classes) + +- `_error(message, **extra) -> dict` +- `_is_number(value) -> bool` +- `_normalize_text(row, field) -> str | dict` +- `_normalize_feedback_row(row) -> dict` +- `append_feedback(row, path)` +- `_valid_rating(value) -> bool` +- `summarize_feedback(path)` +- `_emit_result(result) -> None` +- `cmd_feedback_add(args) -> None` +- `cmd_feedback_report(args) -> None` + +## `fleet.py` (21 functions, 0 classes) + +- `_atlas_config_economy() -> str | None` +- `_is_pos_int(value)` +- `engine_config(cfg)` +- `save_engine_config(updates)` +- `load_fleet_config(path)` +- `available_backends()` +- `task_tier(task)` +- `_code_impl_shift_bounds(profile)` +- `route_task(task, config, backends, attempt)` +- `resolve_backend(tier, config)` +- `_task_id(task)` +- `_status(task)` +- `_is_done(task)` +- `_is_pending(task)` +- `_dependencies(task)` +- `_chunk(items, size)` +- `_load_tagged_or_raise(tag)` +- `ready_set(tasks)` +- `compute_waves(tasks, max_concurrency)` +- `run_fleet_waves(concurrency, tag)` +- `cmd_fleet_waves(args)` + +## `lib.py` (25 functions, 1 classes) +- **class CommandError** — methods: __init__ + +- `emit(data) -> None` +- `fail(message, **extra) -> None` +- `now_iso() -> str` +- `atomic_write(path, content) -> None` +- `locked_update(path, transform) -> str` +- `emit_json_error(message, **extra) -> dict` +- `read_json(path) -> dict` +- `write_json(path, data) -> None` +- `word_count(text) -> int` +- `count_requirements(text) -> int` +- `has_section(text, heading) -> bool` +- `get_section_content(text, heading) -> str` +- `_detect_taskmaster_method() -> dict` +- `_read_taskmaster_model(role) -> dict` +- `_read_taskmaster_config() -> dict` +- `_write_taskmaster_config(config) -> None` +- `_local_port_open(host, port, timeout) -> bool` +- `_env_file_has_key(env_path, key) -> bool` +- `_ensure_env_entry(env_path, key, value, comment) -> bool` +- `_read_env_file_value(env_path, key) -> str | None` +- `_detect_perplexity_mcp() -> str | None` +- `_is_local_perplexity_free(model) -> bool` +- `_current_taskmaster_tag() -> str` +- `_resolve_tasks_payload(raw, tag) -> tuple[list | None, object]` +- `_read_execution_state() -> dict` + +## `llm_client.py` (10 functions, 1 classes) +- **class LLMError** — methods: __init__ + +- `_sleep(seconds)` +- `_env_or_dotenv(name)` +- `discover_key()` +- `_resolve_model(model, tier, provider)` +- `_extract_json(text)` +- `_usage_int(value)` +- `_usage_fields(provider, data)` +- `_http_call(creds, model, system, prompt, max_tokens, timeout)` +- `generate_json(prompt, *, system, schema_hint, model, tier, max_tokens, timeout, op_class, task_id, return_telemetry_ref)` +- `_telemetry(op_class, task_id, model, exit_code, start, parse_retry, http_status, usage)` + +## `mode_recommend.py` (8 functions, 0 classes) + +- `_parse_version(v) -> tuple[int, int, int]` +- `_check_taskmaster_version(cli_path) -> dict` +- `_safe_call(fn) -> bool` +- `_mcp_config_has_server(config_path, server_name, *, allow_top_level) -> bool` +- `detect_atlas_launcher() -> dict` +- `detect_taskmaster() -> dict` +- `detect_capabilities() -> dict` +- `validate_setup(provider_mode) -> dict` + +## `parallel.py` (14 functions, 0 classes) + +- `out(payload)` +- `fail(msg)` +- `_resolve_tag(tag_or_args)` +- `current_tag(args)` +- `load_tagged(tag)` +- `get_tasks(raw, tag)` +- `write_atomic(path, payload)` +- `build_packets(tasks, missing_only) -> list` +- `cmd_plan(args)` +- `apply_results(results, tag, threshold) -> dict` +- `cmd_apply(args)` +- `cmd_extract(args)` +- `cmd_inject(args)` +- `main()` + +## `pipeline.py` (12 functions, 2 classes) +- **class _CASMiss** — methods: __init__ +- **class _IllegalTransition** — methods: __init__ + +- `_load_state() -> dict` +- `current_phase() -> dict` +- `advance_phase(expected_current, target, evidence) -> dict` +- `check_gate(phase, evidence) -> dict` +- `_read_taskmaster_state() -> dict` +- `_read_taskmaster_tasks() -> dict` +- `_tag_task_lists(tasks) -> dict[str, list[dict]]` +- `_count_tasks(items) -> dict[str, int]` +- `_current_tag(state, tag_lists) -> str` +- `_fresh_tag(existing) -> str` +- `_recommended_pending_tag(tag_counts) -> str | None` +- `preflight(cwd) -> dict` + +## `preflight.py` (4 functions, 0 classes) + +- `run_preflight() -> dict` +- `cmd_preflight(args) -> None` +- `run_detect_taskmaster() -> dict` +- `cmd_detect_taskmaster(args) -> None` + +## `provider_resolver.py` (5 functions, 1 classes) +- **class ProviderHandle** — methods: (none) + +- `_usability_facts() -> dict` +- `_try_cli(role, provider, model, ttl_s) -> ProviderHandle | None` +- `_try_api(role, model) -> ProviderHandle | None` +- `_plan_floor(role, model, reason) -> ProviderHandle` +- `resolve_provider(role, op_class, *, fleet_config) -> ProviderHandle` + +## `providers.py` (18 functions, 0 classes) + +- `_role_empty(value) -> bool` +- `_provider_usable(provider, *, has_claude, has_codex, has_anthropic_key, has_openai_key, has_perplexity_key, has_google_key) -> bool` +- `_is_stock_taskmaster_default(role) -> bool` +- `_is_nested_claude() -> bool` +- `_is_spawning_provider(provider) -> bool` +- `_probe_spawn(provider) -> bool` +- `_probe_spawn_cached(provider, ttl_s) -> bool` +- `_resolve_configure_profile(economy) -> dict` +- `_desired_main_model(has_claude, has_codex, has_anthropic_key) -> dict | None` +- `_desired_fallback_model(has_claude, has_codex, has_anthropic_key) -> dict | None` +- `_main_model_for_start_tier(model, tier) -> dict` +- `_has_perplexity_api_key() -> bool` +- `_perplexity_research_model(model_id) -> dict` +- `_local_proxy_research_model(local_proxy_url) -> dict` +- `run_configure_providers(economy) -> dict` +- `cmd_configure_providers(args) -> None` +- `run_detect_providers() -> dict` +- `cmd_detect_providers(args) -> None` + +## `render.py` (17 functions, 0 classes) + +- `_display_width(s) -> int` +- `_clip(s, width) -> str` +- `_ascii_mode() -> bool` +- `_g(name, ascii_mode) -> str` +- `_b(name, ascii_mode) -> str` +- `grade_word(percentage) -> str` +- `bar(score, maximum, segments, *, ascii_mode) -> str` +- `box(title, lines, *, ascii_mode, width) -> str` +- `oneline_summary(validation, task_counts, *, ascii_mode) -> str` +- `phase_header_title(phase) -> str` +- `phase_tracker(state, *, ascii_mode) -> str` +- `validation_scorecard(validation, task_counts, *, ascii_mode) -> str` +- `preflight_panel(preflight, *, ascii_mode) -> str` +- `shipcheck_panel(shipcheck, *, ascii_mode) -> str` +- `_gate_tokens(i) -> list[str]` +- `execute_panel(task_counts, *, ascii_mode) -> str` +- `handoff_panel(validation, task_counts, *, ascii_mode) -> str` + +## `setup_wizard.py` (11 functions, 0 classes) + +- `_env_flag(name) -> bool` +- `_detect_line() -> str` +- `_recommend() -> dict` +- `_panel(recommendation, caps) -> list[str]` +- `_validate(mode) -> dict` +- `_live_probe(provider) -> dict` +- `_run_validate_step(recommendation, mode) -> dict` +- `_has_spawning_cli() -> bool` +- `add_key(var, ask_value, ask_keyless) -> dict` +- `run_setup(accept_default, validate_only, choose) -> dict` +- `cmd_setup(args) -> None` + +## `shipcheck.py` (10 functions, 0 classes) + +- `gate_pipeline(atlas) -> Tuple[bool, List[str]]` +- `gate_tasks(repo_root) -> Tuple[bool, List[str], list]` +- `_has_card_for(cdd_dir, tid) -> bool` +- `gate_cdd(atlas, tasks) -> Tuple[bool, List[str]]` +- `gate_plan(repo_root) -> Tuple[bool, List[str]]` +- `gate_exit_codes(atlas) -> Tuple[bool, List[str]]` +- `log_override(atlas, message) -> None` +- `run_all_gates(repo_root, override_active) -> Tuple[bool, List[str]]` +- `run_ship_check(cwd, dry_run, override) -> dict` +- `main(argv) -> int` + +## `status.py` (4 functions, 0 classes) + +- `_subtask_count(items) -> int` +- `_task_counts() -> dict | None` +- `_validation() -> dict | None` +- `run_render_status(phase, fmt, show_all) -> dict` + +## `suggestions.py` (6 functions, 0 classes) + +- `_store_path(path) -> Path` +- `_error(message, **extra) -> dict` +- `_is_number(value) -> bool` +- `_normalize_suggestion_row(row) -> dict` +- `append_suggestion(row, path)` +- `summarize_suggestions(path)` + +## `task_state.py` (19 functions, 0 classes) + +- `_priority_rank(task) -> int` +- `_sortable_id(value) -> tuple[int, int | str]` +- `_status(item) -> str` +- `_dependencies(item) -> list` +- `_resolve_tasks(tag) -> tuple[str, dict, str | None, list[dict]]` +- `_tag_key_for_raw(raw, tag) -> str | None` +- `_ready_subtask(parent) -> dict | None` +- `_subtask_envelope(parent, subtask) -> dict` +- `_in_progress_candidates(tasks) -> list[dict]` +- `_ready_candidates(tasks, ready_ids) -> list[dict]` +- `_select_next_task(resolved_tag, tasks) -> dict` +- `run_next_task(tag) -> dict` +- `_claim_selected_task(tasks, selected) -> dict` +- `run_claim_task(tag) -> dict` +- `_split_id(id_str) -> tuple[str, str | None]` +- `run_set_status(id_str, status, tag) -> dict` +- `cmd_next_task(args) -> None` +- `cmd_claim_task(args) -> None` +- `cmd_set_status(args) -> None` + +## `taskmaster.py` (5 functions, 0 classes) + +- `_build_env() -> dict` +- `_find_binary() -> str | None` +- `init_taskmaster(method) -> dict` +- `cmd_init_taskmaster(args) -> None` +- `detect_taskmaster_method() -> str` + +## `tasks.py` (8 functions, 0 classes) + +- `run_calc_tasks(requirements, scale) -> dict` +- `cmd_calc_tasks(args) -> None` +- `run_backup_prd(input_path) -> dict` +- `cmd_backup_prd(args) -> None` +- `_classify_task(task) -> dict` +- `_generate_acceptance_criteria(task, complexity) -> list` +- `run_enrich_tasks(input_path, tag) -> dict` +- `cmd_enrich_tasks(args) -> None` + +## `templates.py` (2 functions, 0 classes) + +- `run_load_template(template_type) -> dict` +- `cmd_load_template(args) -> None` + +## `validation.py` (5 functions, 0 classes) + +- `run_validate_prd(input_path) -> dict` +- `_persist_validation(result) -> None` +- `cmd_validate_prd(args) -> None` +- `run_validate_tasks(input_path, allow_empty_subtasks, require_phase_config, tag) -> dict` +- `cmd_validate_tasks(args) -> None` + diff --git a/docs/architecture/generated/30-import-cycles.txt b/docs/architecture/generated/30-import-cycles.txt new file mode 100644 index 0000000..21887d0 --- /dev/null +++ b/docs/architecture/generated/30-import-cycles.txt @@ -0,0 +1 @@ +# pydeps --show-cycles over prd_taskmaster: no import cycles detected. diff --git a/docs/architecture/generated/callflow-core.dot b/docs/architecture/generated/callflow-core.dot new file mode 100644 index 0000000..b477cf5 --- /dev/null +++ b/docs/architecture/generated/callflow-core.dot @@ -0,0 +1,560 @@ +digraph G { +concentrate=true; +splines="ortho"; +rankdir="LR"; +subgraph legend{ + rank = min; + label = "legend"; + Legend [shape=none, margin=0, label = < +
Code2flow Legend
+ + + + + +
Regular function
Trunk function (nothing calls this)
Leaf function (this calls nothing else)
Function call
+ >]; +}node_015bbe25 [label="501: _expand_packet()" name="backend::NativeBackend._expand_packet" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_f1140762 [label="607: _packet_success()" name="backend::NativeBackend._packet_success" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_96821269 [label="440: expand()" name="backend::NativeBackend.expand" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_6c351af6 [label="343: parse_prd()" name="backend::NativeBackend.parse_prd" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_c464c120 [label="625: rate()" name="backend::NativeBackend.rate" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_cced52be [label="251: _agent_expand_action()" name="backend::_agent_expand_action" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_337797aa [label="240: _agent_parse_action()" name="backend::_agent_parse_action" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_46c63267 [label="267: _agent_rate_action()" name="backend::_agent_rate_action" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_0b0dc172 [label="157: _candidate_tasks()" name="backend::_candidate_tasks" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_8dcb74ff [label="299: _cli_structured_mode()" name="backend::_cli_structured_mode" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_1c764779 [label="295: _cli_timeout()" name="backend::_cli_timeout" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_03502d3a [label="235: _complexity_report_path()" name="backend::_complexity_report_path" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_b3542df0 [label="183: _load_existing_tagged()" name="backend::_load_existing_tagged" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_53230e82 [label="130: _load_tasks()" name="backend::_load_tasks" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_e72cbbc2 [label="204: _native_concurrency()" name="backend::_native_concurrency" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_cab4cce3 [label="136: _pending_tasks()" name="backend::_pending_tasks" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_b506da62 [label="122: _task_id_set()" name="backend::_task_id_set" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_1eb7e5b5 [label="218: _task_summaries()" name="backend::_task_summaries" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_59dd988f [label="170: _validate_task_candidate()" name="backend::_validate_task_candidate" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_7c8b1579 [label="195: _write_tasks_into_tag()" name="backend::_write_tasks_into_tag" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_4a59f6f7 [label="136: _estimate_cost_usd()" name="economy::_estimate_cost_usd" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_dc97aa28 [label="125: _price_key_for_model()" name="economy::_price_key_for_model" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_8ca8203f [label="141: _summarize_costs()" name="economy::_summarize_costs" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_7a008da0 [label="119: _token_int()" name="economy::_token_int" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_beb043e2 [label="95: append_telemetry()" name="economy::append_telemetry" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_bcfd79f0 [label="242: cmd_economy_report()" name="economy::cmd_economy_report" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_852a6e4c [label="73: economy_profile()" name="economy::economy_profile" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_8050206b [label="62: shift_tier()" name="economy::shift_tier" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_b53bcf8a [label="180: summarize_telemetry()" name="economy::summarize_telemetry" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_afee3412 [label="79: _atlas_config_economy()" name="fleet::_atlas_config_economy" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_9c67934f [label="364: _chunk()" name="fleet::_chunk" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_ae53abce [label="297: _code_impl_shift_bounds()" name="fleet::_code_impl_shift_bounds" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_3169723f [label="360: _dependencies()" name="fleet::_dependencies" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_bdd602cf [label="352: _is_done()" name="fleet::_is_done" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_3d6a13b1 [label="356: _is_pending()" name="fleet::_is_pending" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_095b5e91 [label="97: _is_pos_int()" name="fleet::_is_pos_int" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_1b951c53 [label="368: _load_tagged_or_raise()" name="fleet::_load_tagged_or_raise" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_50a1ddf5 [label="348: _status()" name="fleet::_status" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_33f3a237 [label="344: _task_id()" name="fleet::_task_id" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_b549f0a3 [label="274: available_backends()" name="fleet::available_backends" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_7a2c53d3 [label="473: cmd_fleet_waves()" name="fleet::cmd_fleet_waves" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_ce645b30 [label="394: compute_waves()" name="fleet::compute_waves" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_c61d14f6 [label="102: engine_config()" name="fleet::engine_config" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_dd756297 [label="196: load_fleet_config()" name="fleet::load_fleet_config" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_6bd79f77 [label="383: ready_set()" name="fleet::ready_set" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_e0f7ae2e [label="328: resolve_backend()" name="fleet::resolve_backend" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_bcb52703 [label="306: route_task()" name="fleet::route_task" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_f0a0926b [label="433: run_fleet_waves()" name="fleet::run_fleet_waves" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_4e3b783a [label="156: save_engine_config()" name="fleet::save_engine_config" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_af906479 [label="285: task_tier()" name="fleet::task_tier" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_946f29f6 [label="53: __init__()" name="lib::CommandError.__init__" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_3a2c6e67 [label="337: _current_taskmaster_tag()" name="lib::_current_taskmaster_tag" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_f6e2fb66 [label="165: _detect_taskmaster_method()" name="lib::_detect_taskmaster_method" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_9d4f8e24 [label="275: _ensure_env_entry()" name="lib::_ensure_env_entry" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_162f6de3 [label="268: _env_file_has_key()" name="lib::_env_file_has_key" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_8478cefe [label="400: _read_execution_state()" name="lib::_read_execution_state" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_5d9a2b00 [label="223: _read_taskmaster_model()" name="lib::_read_taskmaster_model" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_ec6290a9 [label="352: _resolve_tasks_payload()" name="lib::_resolve_tasks_payload" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_03be3e25 [label="69: atomic_write()" name="lib::atomic_write" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_0d5b9b97 [label="117: count_requirements()" name="lib::count_requirements" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_de4ff705 [label="33: emit()" name="lib::emit" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_b59dfc10 [label="95: emit_json_error()" name="lib::emit_json_error" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_40250b05 [label="128: get_section_content()" name="lib::get_section_content" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_d7f3f280 [label="122: has_section()" name="lib::has_section" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_46f934ce [label="78: locked_update()" name="lib::locked_update" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_3dd96d9f [label="61: now_iso()" name="lib::now_iso" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_996cd29d [label="100: read_json()" name="lib::read_json" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_ec9848d3 [label="113: word_count()" name="lib::word_count" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_18602a47 [label="108: write_json()" name="lib::write_json" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_dcd94f4c [label="56: _check_taskmaster_version()" name="mode_recommend::_check_taskmaster_version" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_08b8e045 [label="109: _mcp_config_has_server()" name="mode_recommend::_mcp_config_has_server" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_55755815 [label="40: _parse_version()" name="mode_recommend::_parse_version" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_7f270ce4 [label="101: _safe_call()" name="mode_recommend::_safe_call" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_ed45feea [label="139: detect_atlas_launcher()" name="mode_recommend::detect_atlas_launcher" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_288b23cc [label="220: detect_capabilities()" name="mode_recommend::detect_capabilities" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_4a95057d [label="168: detect_taskmaster()" name="mode_recommend::detect_taskmaster" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_119f79ba [label="370: validate_setup()" name="mode_recommend::validate_setup" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_22bc972f [label="0: (global)()" name="parallel::(global)" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_361d8e74 [label="54: _resolve_tag()" name="parallel::_resolve_tag" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_ba0e4460 [label="125: apply_results()" name="parallel::apply_results" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_9f271c70 [label="89: build_packets()" name="parallel::build_packets" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_41b5565a [label="185: cmd_apply()" name="parallel::cmd_apply" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_73895360 [label="192: cmd_extract()" name="parallel::cmd_extract" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_9239b15a [label="200: cmd_inject()" name="parallel::cmd_inject" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_27e8bdc0 [label="117: cmd_plan()" name="parallel::cmd_plan" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_70ae6e31 [label="63: current_tag()" name="parallel::current_tag" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_ed9d25e2 [label="49: fail()" name="parallel::fail" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_f32885c0 [label="79: get_tasks()" name="parallel::get_tasks" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_ed51d56f [label="67: load_tagged()" name="parallel::load_tagged" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_72d63ed3 [label="214: main()" name="parallel::main" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_d03c6573 [label="45: out()" name="parallel::out" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_6534447f [label="83: write_atomic()" name="parallel::write_atomic" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_dee7e970 [label="256: __init__()" name="pipeline::_CASMiss.__init__" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_806e4187 [label="259: __init__()" name="pipeline::_IllegalTransition.__init__" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_ab2d0df0 [label="148: _count_tasks()" name="pipeline::_count_tasks" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_24142b43 [label="158: _current_tag()" name="pipeline::_current_tag" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_0e230765 [label="167: _fresh_tag()" name="pipeline::_fresh_tag" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_082a778e [label="33: _load_state()" name="pipeline::_load_state" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_0889fc74 [label="118: _read_taskmaster_state()" name="pipeline::_read_taskmaster_state" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_91aeba13 [label="127: _read_taskmaster_tasks()" name="pipeline::_read_taskmaster_tasks" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_0dec453f [label="175: _recommended_pending_tag()" name="pipeline::_recommended_pending_tag" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_b829a1a5 [label="137: _tag_task_lists()" name="pipeline::_tag_task_lists" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_704323d0 [label="49: advance_phase()" name="pipeline::advance_phase" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_8f7e4dd4 [label="39: current_phase()" name="pipeline::current_phase" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_7955eacd [label="187: preflight()" name="pipeline::preflight" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_321eccd1 [label="90: cmd_detect_taskmaster()" name="preflight::cmd_detect_taskmaster" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_badd6759 [label="77: cmd_preflight()" name="preflight::cmd_preflight" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_bdafb9b8 [label="84: run_detect_taskmaster()" name="preflight::run_detect_taskmaster" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_739b9bad [label="20: run_preflight()" name="preflight::run_preflight" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_3332348c [label="79: _plan_floor()" name="provider_resolver::_plan_floor" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_5609db09 [label="68: _try_api()" name="provider_resolver::_try_api" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_5802af96 [label="54: _try_cli()" name="provider_resolver::_try_cli" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_b28d07f1 [label="42: _usability_facts()" name="provider_resolver::_usability_facts" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_5ccd8a5f [label="83: resolve_provider()" name="provider_resolver::resolve_provider" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_aef9a9e0 [label="0: (global)()" name="shipcheck::(global)" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_9daf3d17 [label="101: _has_card_for()" name="shipcheck::_has_card_for" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_dcddb8a7 [label="118: gate_cdd()" name="shipcheck::gate_cdd" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_6bdc25a1 [label="141: gate_exit_codes()" name="shipcheck::gate_exit_codes" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_860c7ac6 [label="61: gate_pipeline()" name="shipcheck::gate_pipeline" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_22c7fbb8 [label="132: gate_plan()" name="shipcheck::gate_plan" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_166a5a71 [label="74: gate_tasks()" name="shipcheck::gate_tasks" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_99615e9b [label="166: log_override()" name="shipcheck::log_override" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_959ccd53 [label="280: main()" name="shipcheck::main" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_14f57ab6 [label="180: run_all_gates()" name="shipcheck::run_all_gates" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_1ace7292 [label="208: run_ship_check()" name="shipcheck::run_ship_check" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_fa2537f0 [label="161: _claim_selected_task()" name="task_state::_claim_selected_task" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_85154c93 [label="40: _dependencies()" name="task_state::_dependencies" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_cd506363 [label="96: _in_progress_candidates()" name="task_state::_in_progress_candidates" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_4f44f5be [label="25: _priority_rank()" name="task_state::_priority_rank" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_a783286a [label="109: _ready_candidates()" name="task_state::_ready_candidates" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_0f7d8536 [label="65: _ready_subtask()" name="task_state::_ready_subtask" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_c89f856b [label="45: _resolve_tasks()" name="task_state::_resolve_tasks" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_eb6c47cd [label="122: _select_next_task()" name="task_state::_select_next_task" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_873601f4 [label="29: _sortable_id()" name="task_state::_sortable_id" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_7c6a623c [label="221: _split_id()" name="task_state::_split_id" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_cce708e5 [label="36: _status()" name="task_state::_status" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_faa34b75 [label="83: _subtask_envelope()" name="task_state::_subtask_envelope" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_cc660e06 [label="57: _tag_key_for_raw()" name="task_state::_tag_key_for_raw" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_12259c7b [label="297: cmd_claim_task()" name="task_state::cmd_claim_task" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_c41dc369 [label="290: cmd_next_task()" name="task_state::cmd_next_task" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_fd98ecb0 [label="304: cmd_set_status()" name="task_state::cmd_set_status" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_6d9054a6 [label="181: run_claim_task()" name="task_state::run_claim_task" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_cb095ffd [label="155: run_next_task()" name="task_state::run_next_task" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_354f2337 [label="230: run_set_status()" name="task_state::run_set_status" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_aa98bfc2 [label="135: _classify_task()" name="tasks::_classify_task" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_c61f9f91 [label="180: _generate_acceptance_criteria()" name="tasks::_generate_acceptance_criteria" shape="rect" style="rounded,filled" fillcolor="#6db33f" ]; +node_4e896d64 [label="86: cmd_backup_prd()" name="tasks::cmd_backup_prd" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_86415f94 [label="59: cmd_calc_tasks()" name="tasks::cmd_calc_tasks" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_f97b5e96 [label="294: cmd_enrich_tasks()" name="tasks::cmd_enrich_tasks" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_27779b2b [label="66: run_backup_prd()" name="tasks::run_backup_prd" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_78043230 [label="30: run_calc_tasks()" name="tasks::run_calc_tasks" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_76be2ca1 [label="229: run_enrich_tasks()" name="tasks::run_enrich_tasks" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_b1831293 [label="338: _persist_validation()" name="validation::_persist_validation" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_b58afb0f [label="350: cmd_validate_prd()" name="validation::cmd_validate_prd" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_92879697 [label="525: cmd_validate_tasks()" name="validation::cmd_validate_tasks" shape="rect" style="rounded,filled" fillcolor="#966F33" ]; +node_d590851f [label="37: run_validate_prd()" name="validation::run_validate_prd" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_9431b506 [label="362: run_validate_tasks()" name="validation::run_validate_tasks" shape="rect" style="rounded,filled" fillcolor="#cccccc" ]; +node_015bbe25 -> node_f1140762 [color="#0072B2" penwidth="2"]; +node_015bbe25 -> node_f1140762 [color="#0072B2" penwidth="2"]; +node_015bbe25 -> node_f1140762 [color="#0072B2" penwidth="2"]; +node_015bbe25 -> node_8dcb74ff [color="#0072B2" penwidth="2"]; +node_015bbe25 -> node_1c764779 [color="#0072B2" penwidth="2"]; +node_015bbe25 -> node_beb043e2 [color="#0072B2" penwidth="2"]; +node_015bbe25 -> node_8050206b [color="#0072B2" penwidth="2"]; +node_015bbe25 -> node_3dd96d9f [color="#0072B2" penwidth="2"]; +node_96821269 -> node_cced52be [color="#E69F00" penwidth="2"]; +node_96821269 -> node_e72cbbc2 [color="#E69F00" penwidth="2"]; +node_96821269 -> node_cab4cce3 [color="#E69F00" penwidth="2"]; +node_96821269 -> node_852a6e4c [color="#E69F00" penwidth="2"]; +node_96821269 -> node_5ccd8a5f [color="#E69F00" penwidth="2"]; +node_6c351af6 -> node_337797aa [color="#D55E00" penwidth="2"]; +node_6c351af6 -> node_337797aa [color="#D55E00" penwidth="2"]; +node_6c351af6 -> node_337797aa [color="#D55E00" penwidth="2"]; +node_6c351af6 -> node_8dcb74ff [color="#D55E00" penwidth="2"]; +node_6c351af6 -> node_1c764779 [color="#D55E00" penwidth="2"]; +node_6c351af6 -> node_59dd988f [color="#D55E00" penwidth="2"]; +node_6c351af6 -> node_7c8b1579 [color="#D55E00" penwidth="2"]; +node_6c351af6 -> node_852a6e4c [color="#D55E00" penwidth="2"]; +node_6c351af6 -> node_5ccd8a5f [color="#D55E00" penwidth="2"]; +node_c464c120 -> node_46c63267 [color="#000000" penwidth="2"]; +node_c464c120 -> node_46c63267 [color="#000000" penwidth="2"]; +node_c464c120 -> node_46c63267 [color="#000000" penwidth="2"]; +node_c464c120 -> node_8dcb74ff [color="#000000" penwidth="2"]; +node_c464c120 -> node_1c764779 [color="#000000" penwidth="2"]; +node_c464c120 -> node_03502d3a [color="#000000" penwidth="2"]; +node_c464c120 -> node_53230e82 [color="#000000" penwidth="2"]; +node_c464c120 -> node_1eb7e5b5 [color="#000000" penwidth="2"]; +node_c464c120 -> node_852a6e4c [color="#000000" penwidth="2"]; +node_c464c120 -> node_5ccd8a5f [color="#000000" penwidth="2"]; +node_0b0dc172 -> node_946f29f6 [color="#56B4E9" penwidth="2"]; +node_cab4cce3 -> node_53230e82 [color="#009E73" penwidth="2"]; +node_cab4cce3 -> node_b506da62 [color="#009E73" penwidth="2"]; +node_59dd988f -> node_0b0dc172 [color="#CC79A7" penwidth="2"]; +node_59dd988f -> node_9431b506 [color="#CC79A7" penwidth="2"]; +node_7c8b1579 -> node_b3542df0 [color="#E69F00" penwidth="2"]; +node_8ca8203f -> node_4a59f6f7 [color="#CC79A7" penwidth="2"]; +node_8ca8203f -> node_4a59f6f7 [color="#CC79A7" penwidth="2"]; +node_8ca8203f -> node_dc97aa28 [color="#CC79A7" penwidth="2"]; +node_8ca8203f -> node_7a008da0 [color="#CC79A7" penwidth="2"]; +node_8ca8203f -> node_7a008da0 [color="#CC79A7" penwidth="2"]; +node_beb043e2 -> node_46f934ce [color="#56B4E9" penwidth="2"]; +node_bcfd79f0 -> node_b53bcf8a [color="#000000" penwidth="2"]; +node_bcfd79f0 -> node_de4ff705 [color="#000000" penwidth="2"]; +node_b53bcf8a -> node_8ca8203f [color="#56B4E9" penwidth="2"]; +node_bdd602cf -> node_50a1ddf5 [color="#CC79A7" penwidth="2"]; +node_3d6a13b1 -> node_50a1ddf5 [color="#E69F00" penwidth="2"]; +node_1b951c53 -> node_946f29f6 [color="#009E73" penwidth="2"]; +node_1b951c53 -> node_946f29f6 [color="#009E73" penwidth="2"]; +node_1b951c53 -> node_946f29f6 [color="#009E73" penwidth="2"]; +node_7a2c53d3 -> node_f0a0926b [color="#009E73" penwidth="2"]; +node_7a2c53d3 -> node_de4ff705 [color="#009E73" penwidth="2"]; +node_ce645b30 -> node_9c67934f [color="#000000" penwidth="2"]; +node_ce645b30 -> node_3169723f [color="#000000" penwidth="2"]; +node_ce645b30 -> node_bdd602cf [color="#000000" penwidth="2"]; +node_ce645b30 -> node_3d6a13b1 [color="#000000" penwidth="2"]; +node_ce645b30 -> node_33f3a237 [color="#000000" penwidth="2"]; +node_ce645b30 -> node_33f3a237 [color="#000000" penwidth="2"]; +node_ce645b30 -> node_33f3a237 [color="#000000" penwidth="2"]; +node_c61d14f6 -> node_095b5e91 [color="#D55E00" penwidth="2"]; +node_c61d14f6 -> node_095b5e91 [color="#D55E00" penwidth="2"]; +node_c61d14f6 -> node_095b5e91 [color="#D55E00" penwidth="2"]; +node_c61d14f6 -> node_095b5e91 [color="#D55E00" penwidth="2"]; +node_dd756297 -> node_afee3412 [color="#CC79A7" penwidth="2"]; +node_dd756297 -> node_c61d14f6 [color="#CC79A7" penwidth="2"]; +node_dd756297 -> node_c61d14f6 [color="#CC79A7" penwidth="2"]; +node_6bd79f77 -> node_3169723f [color="#CC79A7" penwidth="2"]; +node_6bd79f77 -> node_bdd602cf [color="#CC79A7" penwidth="2"]; +node_6bd79f77 -> node_3d6a13b1 [color="#CC79A7" penwidth="2"]; +node_6bd79f77 -> node_33f3a237 [color="#CC79A7" penwidth="2"]; +node_6bd79f77 -> node_33f3a237 [color="#CC79A7" penwidth="2"]; +node_bcb52703 -> node_852a6e4c [color="#009E73" penwidth="2"]; +node_bcb52703 -> node_8050206b [color="#009E73" penwidth="2"]; +node_bcb52703 -> node_8050206b [color="#009E73" penwidth="2"]; +node_bcb52703 -> node_ae53abce [color="#009E73" penwidth="2"]; +node_bcb52703 -> node_b549f0a3 [color="#009E73" penwidth="2"]; +node_bcb52703 -> node_e0f7ae2e [color="#009E73" penwidth="2"]; +node_bcb52703 -> node_af906479 [color="#009E73" penwidth="2"]; +node_f0a0926b -> node_852a6e4c [color="#009E73" penwidth="2"]; +node_f0a0926b -> node_3d6a13b1 [color="#009E73" penwidth="2"]; +node_f0a0926b -> node_1b951c53 [color="#009E73" penwidth="2"]; +node_f0a0926b -> node_33f3a237 [color="#009E73" penwidth="2"]; +node_f0a0926b -> node_b549f0a3 [color="#009E73" penwidth="2"]; +node_f0a0926b -> node_ce645b30 [color="#009E73" penwidth="2"]; +node_f0a0926b -> node_dd756297 [color="#009E73" penwidth="2"]; +node_f0a0926b -> node_6bd79f77 [color="#009E73" penwidth="2"]; +node_f0a0926b -> node_bcb52703 [color="#009E73" penwidth="2"]; +node_f0a0926b -> node_946f29f6 [color="#009E73" penwidth="2"]; +node_f0a0926b -> node_946f29f6 [color="#009E73" penwidth="2"]; +node_4e3b783a -> node_c61d14f6 [color="#56B4E9" penwidth="2"]; +node_9d4f8e24 -> node_162f6de3 [color="#F0E442" penwidth="2"]; +node_ec6290a9 -> node_946f29f6 [color="#E69F00" penwidth="2"]; +node_ec6290a9 -> node_3a2c6e67 [color="#E69F00" penwidth="2"]; +node_46f934ce -> node_03be3e25 [color="#D55E00" penwidth="2"]; +node_18602a47 -> node_03be3e25 [color="#CC79A7" penwidth="2"]; +node_dcd94f4c -> node_55755815 [color="#F0E442" penwidth="2"]; +node_dcd94f4c -> node_55755815 [color="#F0E442" penwidth="2"]; +node_ed45feea -> node_08b8e045 [color="#56B4E9" penwidth="2"]; +node_288b23cc -> node_7f270ce4 [color="#F0E442" penwidth="2"]; +node_288b23cc -> node_ed45feea [color="#F0E442" penwidth="2"]; +node_288b23cc -> node_4a95057d [color="#F0E442" penwidth="2"]; +node_119f79ba -> node_c61d14f6 [color="#56B4E9" penwidth="2"]; +node_119f79ba -> node_dcd94f4c [color="#56B4E9" penwidth="2"]; +node_22bc972f -> node_72d63ed3 [color="#CC79A7" penwidth="2"]; +node_ba0e4460 -> node_361d8e74 [color="#000000" penwidth="2"]; +node_ba0e4460 -> node_f32885c0 [color="#000000" penwidth="2"]; +node_ba0e4460 -> node_ed51d56f [color="#000000" penwidth="2"]; +node_ba0e4460 -> node_6534447f [color="#000000" penwidth="2"]; +node_ba0e4460 -> node_6534447f [color="#000000" penwidth="2"]; +node_41b5565a -> node_ba0e4460 [color="#56B4E9" penwidth="2"]; +node_41b5565a -> node_d03c6573 [color="#56B4E9" penwidth="2"]; +node_73895360 -> node_70ae6e31 [color="#000000" penwidth="2"]; +node_73895360 -> node_f32885c0 [color="#000000" penwidth="2"]; +node_73895360 -> node_ed51d56f [color="#000000" penwidth="2"]; +node_73895360 -> node_d03c6573 [color="#000000" penwidth="2"]; +node_9239b15a -> node_70ae6e31 [color="#56B4E9" penwidth="2"]; +node_9239b15a -> node_ed51d56f [color="#56B4E9" penwidth="2"]; +node_9239b15a -> node_d03c6573 [color="#56B4E9" penwidth="2"]; +node_9239b15a -> node_6534447f [color="#56B4E9" penwidth="2"]; +node_27e8bdc0 -> node_9f271c70 [color="#000000" penwidth="2"]; +node_27e8bdc0 -> node_70ae6e31 [color="#000000" penwidth="2"]; +node_27e8bdc0 -> node_f32885c0 [color="#000000" penwidth="2"]; +node_27e8bdc0 -> node_ed51d56f [color="#000000" penwidth="2"]; +node_27e8bdc0 -> node_d03c6573 [color="#000000" penwidth="2"]; +node_70ae6e31 -> node_361d8e74 [color="#E69F00" penwidth="2"]; +node_ed9d25e2 -> node_d03c6573 [color="#56B4E9" penwidth="2"]; +node_ed51d56f -> node_ed9d25e2 [color="#CC79A7" penwidth="2"]; +node_ed51d56f -> node_ed9d25e2 [color="#CC79A7" penwidth="2"]; +node_082a778e -> node_996cd29d [color="#D55E00" penwidth="2"]; +node_0889fc74 -> node_996cd29d [color="#F0E442" penwidth="2"]; +node_91aeba13 -> node_996cd29d [color="#009E73" penwidth="2"]; +node_704323d0 -> node_b59dfc10 [color="#000000" penwidth="2"]; +node_704323d0 -> node_b59dfc10 [color="#000000" penwidth="2"]; +node_704323d0 -> node_b59dfc10 [color="#000000" penwidth="2"]; +node_704323d0 -> node_46f934ce [color="#000000" penwidth="2"]; +node_704323d0 -> node_3dd96d9f [color="#000000" penwidth="2"]; +node_704323d0 -> node_dee7e970 [color="#000000" penwidth="2"]; +node_704323d0 -> node_806e4187 [color="#000000" penwidth="2"]; +node_8f7e4dd4 -> node_082a778e [color="#F0E442" penwidth="2"]; +node_7955eacd -> node_ab2d0df0 [color="#0072B2" penwidth="2"]; +node_7955eacd -> node_24142b43 [color="#0072B2" penwidth="2"]; +node_7955eacd -> node_0e230765 [color="#0072B2" penwidth="2"]; +node_7955eacd -> node_082a778e [color="#0072B2" penwidth="2"]; +node_7955eacd -> node_0889fc74 [color="#0072B2" penwidth="2"]; +node_7955eacd -> node_91aeba13 [color="#0072B2" penwidth="2"]; +node_7955eacd -> node_0dec453f [color="#0072B2" penwidth="2"]; +node_7955eacd -> node_b829a1a5 [color="#0072B2" penwidth="2"]; +node_321eccd1 -> node_de4ff705 [color="#E69F00" penwidth="2"]; +node_321eccd1 -> node_bdafb9b8 [color="#E69F00" penwidth="2"]; +node_badd6759 -> node_de4ff705 [color="#E69F00" penwidth="2"]; +node_badd6759 -> node_739b9bad [color="#E69F00" penwidth="2"]; +node_bdafb9b8 -> node_f6e2fb66 [color="#000000" penwidth="2"]; +node_739b9bad -> node_f6e2fb66 [color="#0072B2" penwidth="2"]; +node_739b9bad -> node_8478cefe [color="#0072B2" penwidth="2"]; +node_739b9bad -> node_ec6290a9 [color="#0072B2" penwidth="2"]; +node_5802af96 -> node_b28d07f1 [color="#D55E00" penwidth="2"]; +node_5ccd8a5f -> node_c61d14f6 [color="#CC79A7" penwidth="2"]; +node_5ccd8a5f -> node_5d9a2b00 [color="#CC79A7" penwidth="2"]; +node_5ccd8a5f -> node_3332348c [color="#CC79A7" penwidth="2"]; +node_5ccd8a5f -> node_3332348c [color="#CC79A7" penwidth="2"]; +node_5ccd8a5f -> node_5609db09 [color="#CC79A7" penwidth="2"]; +node_5ccd8a5f -> node_5609db09 [color="#CC79A7" penwidth="2"]; +node_5ccd8a5f -> node_5802af96 [color="#CC79A7" penwidth="2"]; +node_5ccd8a5f -> node_5802af96 [color="#CC79A7" penwidth="2"]; +node_aef9a9e0 -> node_959ccd53 [color="#000000" penwidth="2"]; +node_dcddb8a7 -> node_9daf3d17 [color="#CC79A7" penwidth="2"]; +node_959ccd53 -> node_1ace7292 [color="#009E73" penwidth="2"]; +node_14f57ab6 -> node_dcddb8a7 [color="#D55E00" penwidth="2"]; +node_14f57ab6 -> node_6bdc25a1 [color="#D55E00" penwidth="2"]; +node_14f57ab6 -> node_860c7ac6 [color="#D55E00" penwidth="2"]; +node_14f57ab6 -> node_22c7fbb8 [color="#D55E00" penwidth="2"]; +node_14f57ab6 -> node_166a5a71 [color="#D55E00" penwidth="2"]; +node_14f57ab6 -> node_99615e9b [color="#D55E00" penwidth="2"]; +node_1ace7292 -> node_14f57ab6 [color="#56B4E9" penwidth="2"]; +node_fa2537f0 -> node_946f29f6 [color="#000000" penwidth="2"]; +node_fa2537f0 -> node_faa34b75 [color="#000000" penwidth="2"]; +node_cd506363 -> node_4f44f5be [color="#009E73" penwidth="2"]; +node_cd506363 -> node_873601f4 [color="#009E73" penwidth="2"]; +node_cd506363 -> node_cce708e5 [color="#009E73" penwidth="2"]; +node_cd506363 -> node_cce708e5 [color="#009E73" penwidth="2"]; +node_a783286a -> node_85154c93 [color="#56B4E9" penwidth="2"]; +node_a783286a -> node_4f44f5be [color="#56B4E9" penwidth="2"]; +node_a783286a -> node_873601f4 [color="#56B4E9" penwidth="2"]; +node_0f7d8536 -> node_85154c93 [color="#D55E00" penwidth="2"]; +node_0f7d8536 -> node_873601f4 [color="#D55E00" penwidth="2"]; +node_0f7d8536 -> node_cce708e5 [color="#D55E00" penwidth="2"]; +node_0f7d8536 -> node_cce708e5 [color="#D55E00" penwidth="2"]; +node_c89f856b -> node_946f29f6 [color="#009E73" penwidth="2"]; +node_eb6c47cd -> node_cd506363 [color="#0072B2" penwidth="2"]; +node_eb6c47cd -> node_a783286a [color="#0072B2" penwidth="2"]; +node_eb6c47cd -> node_0f7d8536 [color="#0072B2" penwidth="2"]; +node_eb6c47cd -> node_faa34b75 [color="#0072B2" penwidth="2"]; +node_7c6a623c -> node_946f29f6 [color="#F0E442" penwidth="2"]; +node_cc660e06 -> node_946f29f6 [color="#D55E00" penwidth="2"]; +node_12259c7b -> node_de4ff705 [color="#009E73" penwidth="2"]; +node_12259c7b -> node_6d9054a6 [color="#009E73" penwidth="2"]; +node_c41dc369 -> node_de4ff705 [color="#E69F00" penwidth="2"]; +node_c41dc369 -> node_cb095ffd [color="#E69F00" penwidth="2"]; +node_fd98ecb0 -> node_de4ff705 [color="#000000" penwidth="2"]; +node_fd98ecb0 -> node_354f2337 [color="#000000" penwidth="2"]; +node_6d9054a6 -> node_946f29f6 [color="#D55E00" penwidth="2"]; +node_6d9054a6 -> node_946f29f6 [color="#D55E00" penwidth="2"]; +node_6d9054a6 -> node_946f29f6 [color="#D55E00" penwidth="2"]; +node_6d9054a6 -> node_946f29f6 [color="#D55E00" penwidth="2"]; +node_6d9054a6 -> node_46f934ce [color="#D55E00" penwidth="2"]; +node_6d9054a6 -> node_fa2537f0 [color="#D55E00" penwidth="2"]; +node_6d9054a6 -> node_eb6c47cd [color="#D55E00" penwidth="2"]; +node_6d9054a6 -> node_cc660e06 [color="#D55E00" penwidth="2"]; +node_cb095ffd -> node_c89f856b [color="#0072B2" penwidth="2"]; +node_cb095ffd -> node_eb6c47cd [color="#0072B2" penwidth="2"]; +node_354f2337 -> node_946f29f6 [color="#CC79A7" penwidth="2"]; +node_354f2337 -> node_946f29f6 [color="#CC79A7" penwidth="2"]; +node_354f2337 -> node_946f29f6 [color="#CC79A7" penwidth="2"]; +node_354f2337 -> node_946f29f6 [color="#CC79A7" penwidth="2"]; +node_354f2337 -> node_946f29f6 [color="#CC79A7" penwidth="2"]; +node_354f2337 -> node_946f29f6 [color="#CC79A7" penwidth="2"]; +node_354f2337 -> node_946f29f6 [color="#CC79A7" penwidth="2"]; +node_354f2337 -> node_46f934ce [color="#CC79A7" penwidth="2"]; +node_354f2337 -> node_7c6a623c [color="#CC79A7" penwidth="2"]; +node_354f2337 -> node_cc660e06 [color="#CC79A7" penwidth="2"]; +node_aa98bfc2 -> node_c61f9f91 [color="#56B4E9" penwidth="2"]; +node_4e896d64 -> node_de4ff705 [color="#F0E442" penwidth="2"]; +node_4e896d64 -> node_27779b2b [color="#F0E442" penwidth="2"]; +node_86415f94 -> node_de4ff705 [color="#F0E442" penwidth="2"]; +node_86415f94 -> node_78043230 [color="#F0E442" penwidth="2"]; +node_f97b5e96 -> node_de4ff705 [color="#D55E00" penwidth="2"]; +node_f97b5e96 -> node_76be2ca1 [color="#D55E00" penwidth="2"]; +node_27779b2b -> node_946f29f6 [color="#009E73" penwidth="2"]; +node_78043230 -> node_946f29f6 [color="#000000" penwidth="2"]; +node_76be2ca1 -> node_946f29f6 [color="#E69F00" penwidth="2"]; +node_76be2ca1 -> node_946f29f6 [color="#E69F00" penwidth="2"]; +node_76be2ca1 -> node_946f29f6 [color="#E69F00" penwidth="2"]; +node_76be2ca1 -> node_946f29f6 [color="#E69F00" penwidth="2"]; +node_76be2ca1 -> node_3a2c6e67 [color="#E69F00" penwidth="2"]; +node_76be2ca1 -> node_ec6290a9 [color="#E69F00" penwidth="2"]; +node_76be2ca1 -> node_aa98bfc2 [color="#E69F00" penwidth="2"]; +node_b1831293 -> node_18602a47 [color="#009E73" penwidth="2"]; +node_b58afb0f -> node_b1831293 [color="#CC79A7" penwidth="2"]; +node_b58afb0f -> node_d590851f [color="#CC79A7" penwidth="2"]; +node_92879697 -> node_de4ff705 [color="#CC79A7" penwidth="2"]; +node_92879697 -> node_9431b506 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_946f29f6 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_0d5b9b97 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_40250b05 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_40250b05 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_40250b05 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_40250b05 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_40250b05 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_40250b05 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_40250b05 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_40250b05 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_40250b05 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_40250b05 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_d7f3f280 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_d7f3f280 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_d7f3f280 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_d7f3f280 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_d7f3f280 [color="#CC79A7" penwidth="2"]; +node_d590851f -> node_ec9848d3 [color="#CC79A7" penwidth="2"]; +node_9431b506 -> node_946f29f6 [color="#D55E00" penwidth="2"]; +node_9431b506 -> node_946f29f6 [color="#D55E00" penwidth="2"]; +node_9431b506 -> node_946f29f6 [color="#D55E00" penwidth="2"]; +node_9431b506 -> node_946f29f6 [color="#D55E00" penwidth="2"]; +node_9431b506 -> node_3a2c6e67 [color="#D55E00" penwidth="2"]; +node_9431b506 -> node_ec6290a9 [color="#D55E00" penwidth="2"]; +subgraph cluster_aa95b367 { + node_b506da62 node_53230e82 node_cab4cce3 node_0b0dc172 node_59dd988f node_b3542df0 node_7c8b1579 node_e72cbbc2 node_1eb7e5b5 node_03502d3a node_337797aa node_cced52be node_46c63267 node_1c764779 node_8dcb74ff; + label="File: backend"; + name="backend"; + style="filled"; + graph[style=dotted]; + subgraph cluster_576c9132 { + node_6c351af6 node_96821269 node_015bbe25 node_f1140762 node_c464c120; + label="Class: NativeBackend"; + name="NativeBackend"; + style="filled"; + graph[style=dotted]; + }; +}; +subgraph cluster_dce65b75 { + node_8050206b node_852a6e4c node_beb043e2 node_7a008da0 node_dc97aa28 node_4a59f6f7 node_8ca8203f node_b53bcf8a node_bcfd79f0; + label="File: economy"; + name="economy"; + style="filled"; + graph[style=dotted]; +}; +subgraph cluster_067f25a5 { + node_afee3412 node_095b5e91 node_c61d14f6 node_4e3b783a node_dd756297 node_b549f0a3 node_af906479 node_ae53abce node_bcb52703 node_e0f7ae2e node_33f3a237 node_50a1ddf5 node_bdd602cf node_3d6a13b1 node_3169723f node_9c67934f node_1b951c53 node_6bd79f77 node_ce645b30 node_f0a0926b node_7a2c53d3; + label="File: fleet"; + name="fleet"; + style="filled"; + graph[style=dotted]; +}; +subgraph cluster_0fef7f5b { + node_de4ff705 node_3dd96d9f node_03be3e25 node_46f934ce node_b59dfc10 node_996cd29d node_18602a47 node_ec9848d3 node_0d5b9b97 node_d7f3f280 node_40250b05 node_f6e2fb66 node_5d9a2b00 node_162f6de3 node_9d4f8e24 node_3a2c6e67 node_ec6290a9 node_8478cefe; + label="File: lib"; + name="lib"; + style="filled"; + graph[style=dotted]; + subgraph cluster_364e5e23 { + node_946f29f6; + label="Class: CommandError"; + name="CommandError"; + style="filled"; + graph[style=dotted]; + }; +}; +subgraph cluster_f6071f93 { + node_55755815 node_dcd94f4c node_7f270ce4 node_08b8e045 node_ed45feea node_4a95057d node_288b23cc node_119f79ba; + label="File: mode_recommend"; + name="mode_recommend"; + style="filled"; + graph[style=dotted]; +}; +subgraph cluster_af5744ce { + node_d03c6573 node_ed9d25e2 node_361d8e74 node_70ae6e31 node_ed51d56f node_f32885c0 node_6534447f node_9f271c70 node_27e8bdc0 node_ba0e4460 node_41b5565a node_73895360 node_9239b15a node_72d63ed3 node_22bc972f; + label="File: parallel"; + name="parallel"; + style="filled"; + graph[style=dotted]; +}; +subgraph cluster_e216814c { + node_082a778e node_8f7e4dd4 node_704323d0 node_0889fc74 node_91aeba13 node_b829a1a5 node_ab2d0df0 node_24142b43 node_0e230765 node_0dec453f node_7955eacd; + label="File: pipeline"; + name="pipeline"; + style="filled"; + graph[style=dotted]; + subgraph cluster_57596efa { + node_dee7e970; + label="Class: _CASMiss"; + name="_CASMiss"; + style="filled"; + graph[style=dotted]; + }; + subgraph cluster_a0dd9c82 { + node_806e4187; + label="Class: _IllegalTransition"; + name="_IllegalTransition"; + style="filled"; + graph[style=dotted]; + }; +}; +subgraph cluster_82828e6d { + node_739b9bad node_badd6759 node_bdafb9b8 node_321eccd1; + label="File: preflight"; + name="preflight"; + style="filled"; + graph[style=dotted]; +}; +subgraph cluster_785db3db { + node_b28d07f1 node_5802af96 node_5609db09 node_3332348c node_5ccd8a5f; + label="File: provider_resolver"; + name="provider_resolver"; + style="filled"; + graph[style=dotted]; +}; +subgraph cluster_f234e8e7 { + node_860c7ac6 node_166a5a71 node_9daf3d17 node_dcddb8a7 node_22c7fbb8 node_6bdc25a1 node_99615e9b node_14f57ab6 node_1ace7292 node_959ccd53 node_aef9a9e0; + label="File: shipcheck"; + name="shipcheck"; + style="filled"; + graph[style=dotted]; +}; +subgraph cluster_e9707880 { + node_4f44f5be node_873601f4 node_cce708e5 node_85154c93 node_c89f856b node_cc660e06 node_0f7d8536 node_faa34b75 node_cd506363 node_a783286a node_eb6c47cd node_cb095ffd node_fa2537f0 node_6d9054a6 node_7c6a623c node_354f2337 node_c41dc369 node_12259c7b node_fd98ecb0; + label="File: task_state"; + name="task_state"; + style="filled"; + graph[style=dotted]; +}; +subgraph cluster_b4535220 { + node_78043230 node_86415f94 node_27779b2b node_4e896d64 node_aa98bfc2 node_c61f9f91 node_76be2ca1 node_f97b5e96; + label="File: tasks"; + name="tasks"; + style="filled"; + graph[style=dotted]; +}; +subgraph cluster_cc01ea75 { + node_d590851f node_b1831293 node_b58afb0f node_9431b506 node_92879697; + label="File: validation"; + name="validation"; + style="filled"; + graph[style=dotted]; +}; +} diff --git a/docs/architecture/generated/callflow-core.svg b/docs/architecture/generated/callflow-core.svg new file mode 100644 index 0000000..3b3d099 --- /dev/null +++ b/docs/architecture/generated/callflow-core.svg @@ -0,0 +1,2325 @@ + + + + + + +G + + +cluster_aa95b367 + +File: backend + + +cluster_576c9132 + +Class: NativeBackend + + +cluster_dce65b75 + +File: economy + + +cluster_067f25a5 + +File: fleet + + +cluster_0fef7f5b + +File: lib + + +cluster_364e5e23 + +Class: CommandError + + +cluster_f6071f93 + +File: mode_recommend + + +cluster_af5744ce + +File: parallel + + +cluster_e216814c + +File: pipeline + + +cluster_57596efa + +Class: _CASMiss + + +cluster_a0dd9c82 + +Class: _IllegalTransition + + +cluster_82828e6d + +File: preflight + + +cluster_785db3db + +File: provider_resolver + + +cluster_f234e8e7 + +File: shipcheck + + +cluster_e9707880 + +File: task_state + + +cluster_b4535220 + +File: tasks + + +cluster_cc01ea75 + +File: validation + + + +Legend + +Code2flow Legend + + +Regular function + + + +Trunk function (nothing calls this) + + + +Leaf function (this calls nothing else) + + + +Function call + + + + + + + +node_015bbe25 + +501: _expand_packet() + + + +node_f1140762 + +607: _packet_success() + + + +node_015bbe25->node_f1140762 + + + + + +node_8dcb74ff + +299: _cli_structured_mode() + + + +node_015bbe25->node_8dcb74ff + + + + + +node_1c764779 + +295: _cli_timeout() + + + +node_015bbe25->node_1c764779 + + + + + +node_beb043e2 + +95: append_telemetry() + + + +node_015bbe25->node_beb043e2 + + + + + +node_8050206b + +62: shift_tier() + + + +node_015bbe25->node_8050206b + + + + + +node_3dd96d9f + +61: now_iso() + + + +node_015bbe25->node_3dd96d9f + + + + + +node_96821269 + +440: expand() + + + +node_cced52be + +251: _agent_expand_action() + + + +node_96821269->node_cced52be + + + + + +node_e72cbbc2 + +204: _native_concurrency() + + + +node_96821269->node_e72cbbc2 + + + + + +node_cab4cce3 + +136: _pending_tasks() + + + +node_96821269->node_cab4cce3 + + + + + +node_852a6e4c + +73: economy_profile() + + + +node_96821269->node_852a6e4c + + + + + +node_5ccd8a5f + +83: resolve_provider() + + + +node_96821269->node_5ccd8a5f + + + + + +node_6c351af6 + +343: parse_prd() + + + +node_337797aa + +240: _agent_parse_action() + + + +node_6c351af6->node_337797aa + + + + + +node_6c351af6->node_8dcb74ff + + + + + +node_6c351af6->node_1c764779 + + + + + +node_59dd988f + +170: _validate_task_candidate() + + + +node_6c351af6->node_59dd988f + + + + + +node_7c8b1579 + +195: _write_tasks_into_tag() + + + +node_6c351af6->node_7c8b1579 + + + + + +node_6c351af6->node_852a6e4c + + + + + +node_6c351af6->node_5ccd8a5f + + + + + +node_c464c120 + +625: rate() + + + +node_46c63267 + +267: _agent_rate_action() + + + +node_c464c120->node_46c63267 + + + + + +node_c464c120->node_8dcb74ff + + + + + +node_c464c120->node_1c764779 + + + + + +node_03502d3a + +235: _complexity_report_path() + + + +node_c464c120->node_03502d3a + + + + + +node_53230e82 + +130: _load_tasks() + + + +node_c464c120->node_53230e82 + + + + + +node_1eb7e5b5 + +218: _task_summaries() + + + +node_c464c120->node_1eb7e5b5 + + + + + +node_c464c120->node_852a6e4c + + + + + +node_c464c120->node_5ccd8a5f + + + + + +node_0b0dc172 + +157: _candidate_tasks() + + + +node_946f29f6 + +53: __init__() + + + +node_0b0dc172->node_946f29f6 + + + + + +node_b3542df0 + +183: _load_existing_tagged() + + + +node_cab4cce3->node_53230e82 + + + + + +node_b506da62 + +122: _task_id_set() + + + +node_cab4cce3->node_b506da62 + + + + + +node_59dd988f->node_0b0dc172 + + + + + +node_9431b506 + +362: run_validate_tasks() + + + +node_59dd988f->node_9431b506 + + + + + +node_7c8b1579->node_b3542df0 + + + + + +node_4a59f6f7 + +136: _estimate_cost_usd() + + + +node_dc97aa28 + +125: _price_key_for_model() + + + +node_8ca8203f + +141: _summarize_costs() + + + +node_8ca8203f->node_4a59f6f7 + + + + + +node_8ca8203f->node_dc97aa28 + + + + + +node_7a008da0 + +119: _token_int() + + + +node_8ca8203f->node_7a008da0 + + + + + +node_46f934ce + +78: locked_update() + + + +node_beb043e2->node_46f934ce + + + + + +node_bcfd79f0 + +242: cmd_economy_report() + + + +node_b53bcf8a + +180: summarize_telemetry() + + + +node_bcfd79f0->node_b53bcf8a + + + + + +node_de4ff705 + +33: emit() + + + +node_bcfd79f0->node_de4ff705 + + + + + +node_b53bcf8a->node_8ca8203f + + + + + +node_afee3412 + +79: _atlas_config_economy() + + + +node_9c67934f + +364: _chunk() + + + +node_ae53abce + +297: _code_impl_shift_bounds() + + + +node_3169723f + +360: _dependencies() + + + +node_bdd602cf + +352: _is_done() + + + +node_50a1ddf5 + +348: _status() + + + +node_bdd602cf->node_50a1ddf5 + + + + + +node_3d6a13b1 + +356: _is_pending() + + + +node_3d6a13b1->node_50a1ddf5 + + + + + +node_095b5e91 + +97: _is_pos_int() + + + +node_1b951c53 + +368: _load_tagged_or_raise() + + + +node_1b951c53->node_946f29f6 + + + + + +node_33f3a237 + +344: _task_id() + + + +node_b549f0a3 + +274: available_backends() + + + +node_7a2c53d3 + +473: cmd_fleet_waves() + + + +node_f0a0926b + +433: run_fleet_waves() + + + +node_7a2c53d3->node_f0a0926b + + + + + +node_7a2c53d3->node_de4ff705 + + + + + +node_ce645b30 + +394: compute_waves() + + + +node_ce645b30->node_9c67934f + + + + + +node_ce645b30->node_3169723f + + + + + +node_ce645b30->node_bdd602cf + + + + + +node_ce645b30->node_3d6a13b1 + + + + + +node_ce645b30->node_33f3a237 + + + + + +node_c61d14f6 + +102: engine_config() + + + +node_c61d14f6->node_095b5e91 + + + + + +node_dd756297 + +196: load_fleet_config() + + + +node_dd756297->node_afee3412 + + + + + +node_dd756297->node_c61d14f6 + + + + + +node_6bd79f77 + +383: ready_set() + + + +node_6bd79f77->node_3169723f + + + + + +node_6bd79f77->node_bdd602cf + + + + + +node_6bd79f77->node_3d6a13b1 + + + + + +node_6bd79f77->node_33f3a237 + + + + + +node_e0f7ae2e + +328: resolve_backend() + + + +node_bcb52703 + +306: route_task() + + + +node_bcb52703->node_852a6e4c + + + + + +node_bcb52703->node_8050206b + + + + + +node_bcb52703->node_ae53abce + + + + + +node_bcb52703->node_b549f0a3 + + + + + +node_bcb52703->node_e0f7ae2e + + + + + +node_af906479 + +285: task_tier() + + + +node_bcb52703->node_af906479 + + + + + +node_f0a0926b->node_852a6e4c + + + + + +node_f0a0926b->node_3d6a13b1 + + + + + +node_f0a0926b->node_1b951c53 + + + + + +node_f0a0926b->node_33f3a237 + + + + + +node_f0a0926b->node_b549f0a3 + + + + + +node_f0a0926b->node_ce645b30 + + + + + +node_f0a0926b->node_dd756297 + + + + + +node_f0a0926b->node_6bd79f77 + + + + + +node_f0a0926b->node_bcb52703 + + + + + +node_f0a0926b->node_946f29f6 + + + + + +node_4e3b783a + +156: save_engine_config() + + + +node_4e3b783a->node_c61d14f6 + + + + + +node_3a2c6e67 + +337: _current_taskmaster_tag() + + + +node_f6e2fb66 + +165: _detect_taskmaster_method() + + + +node_9d4f8e24 + +275: _ensure_env_entry() + + + +node_162f6de3 + +268: _env_file_has_key() + + + +node_9d4f8e24->node_162f6de3 + + + + + +node_8478cefe + +400: _read_execution_state() + + + +node_5d9a2b00 + +223: _read_taskmaster_model() + + + +node_ec6290a9 + +352: _resolve_tasks_payload() + + + +node_ec6290a9->node_946f29f6 + + + + + +node_ec6290a9->node_3a2c6e67 + + + + + +node_03be3e25 + +69: atomic_write() + + + +node_0d5b9b97 + +117: count_requirements() + + + +node_b59dfc10 + +95: emit_json_error() + + + +node_40250b05 + +128: get_section_content() + + + +node_d7f3f280 + +122: has_section() + + + +node_46f934ce->node_03be3e25 + + + + + +node_996cd29d + +100: read_json() + + + +node_ec9848d3 + +113: word_count() + + + +node_18602a47 + +108: write_json() + + + +node_18602a47->node_03be3e25 + + + + + +node_dcd94f4c + +56: _check_taskmaster_version() + + + +node_55755815 + +40: _parse_version() + + + +node_dcd94f4c->node_55755815 + + + + + +node_08b8e045 + +109: _mcp_config_has_server() + + + +node_7f270ce4 + +101: _safe_call() + + + +node_ed45feea + +139: detect_atlas_launcher() + + + +node_ed45feea->node_08b8e045 + + + + + +node_288b23cc + +220: detect_capabilities() + + + +node_288b23cc->node_7f270ce4 + + + + + +node_288b23cc->node_ed45feea + + + + + +node_4a95057d + +168: detect_taskmaster() + + + +node_288b23cc->node_4a95057d + + + + + +node_119f79ba + +370: validate_setup() + + + +node_119f79ba->node_c61d14f6 + + + + + +node_119f79ba->node_dcd94f4c + + + + + +node_22bc972f + +0: (global)() + + + +node_72d63ed3 + +214: main() + + + +node_22bc972f->node_72d63ed3 + + + + + +node_361d8e74 + +54: _resolve_tag() + + + +node_ba0e4460 + +125: apply_results() + + + +node_ba0e4460->node_361d8e74 + + + + + +node_f32885c0 + +79: get_tasks() + + + +node_ba0e4460->node_f32885c0 + + + + + +node_ed51d56f + +67: load_tagged() + + + +node_ba0e4460->node_ed51d56f + + + + + +node_6534447f + +83: write_atomic() + + + +node_ba0e4460->node_6534447f + + + + + +node_9f271c70 + +89: build_packets() + + + +node_41b5565a + +185: cmd_apply() + + + +node_41b5565a->node_ba0e4460 + + + + + +node_d03c6573 + +45: out() + + + +node_41b5565a->node_d03c6573 + + + + + +node_73895360 + +192: cmd_extract() + + + +node_70ae6e31 + +63: current_tag() + + + +node_73895360->node_70ae6e31 + + + + + +node_73895360->node_f32885c0 + + + + + +node_73895360->node_ed51d56f + + + + + +node_73895360->node_d03c6573 + + + + + +node_9239b15a + +200: cmd_inject() + + + +node_9239b15a->node_70ae6e31 + + + + + +node_9239b15a->node_ed51d56f + + + + + +node_9239b15a->node_d03c6573 + + + + + +node_9239b15a->node_6534447f + + + + + +node_27e8bdc0 + +117: cmd_plan() + + + +node_27e8bdc0->node_9f271c70 + + + + + +node_27e8bdc0->node_70ae6e31 + + + + + +node_27e8bdc0->node_f32885c0 + + + + + +node_27e8bdc0->node_ed51d56f + + + + + +node_27e8bdc0->node_d03c6573 + + + + + +node_70ae6e31->node_361d8e74 + + + + + +node_ed9d25e2 + +49: fail() + + + +node_ed9d25e2->node_d03c6573 + + + + + +node_ed51d56f->node_ed9d25e2 + + + + + +node_dee7e970 + +256: __init__() + + + +node_806e4187 + +259: __init__() + + + +node_ab2d0df0 + +148: _count_tasks() + + + +node_24142b43 + +158: _current_tag() + + + +node_0e230765 + +167: _fresh_tag() + + + +node_082a778e + +33: _load_state() + + + +node_082a778e->node_996cd29d + + + + + +node_0889fc74 + +118: _read_taskmaster_state() + + + +node_0889fc74->node_996cd29d + + + + + +node_91aeba13 + +127: _read_taskmaster_tasks() + + + +node_91aeba13->node_996cd29d + + + + + +node_0dec453f + +175: _recommended_pending_tag() + + + +node_b829a1a5 + +137: _tag_task_lists() + + + +node_704323d0 + +49: advance_phase() + + + +node_704323d0->node_b59dfc10 + + + + + +node_704323d0->node_46f934ce + + + + + +node_704323d0->node_3dd96d9f + + + + + +node_704323d0->node_dee7e970 + + + + + +node_704323d0->node_806e4187 + + + + + +node_8f7e4dd4 + +39: current_phase() + + + +node_8f7e4dd4->node_082a778e + + + + + +node_7955eacd + +187: preflight() + + + +node_7955eacd->node_ab2d0df0 + + + + + +node_7955eacd->node_24142b43 + + + + + +node_7955eacd->node_0e230765 + + + + + +node_7955eacd->node_082a778e + + + + + +node_7955eacd->node_0889fc74 + + + + + +node_7955eacd->node_91aeba13 + + + + + +node_7955eacd->node_0dec453f + + + + + +node_7955eacd->node_b829a1a5 + + + + + +node_321eccd1 + +90: cmd_detect_taskmaster() + + + +node_321eccd1->node_de4ff705 + + + + + +node_bdafb9b8 + +84: run_detect_taskmaster() + + + +node_321eccd1->node_bdafb9b8 + + + + + +node_badd6759 + +77: cmd_preflight() + + + +node_badd6759->node_de4ff705 + + + + + +node_739b9bad + +20: run_preflight() + + + +node_badd6759->node_739b9bad + + + + + +node_bdafb9b8->node_f6e2fb66 + + + + + +node_739b9bad->node_f6e2fb66 + + + + + +node_739b9bad->node_8478cefe + + + + + +node_739b9bad->node_ec6290a9 + + + + + +node_3332348c + +79: _plan_floor() + + + +node_5609db09 + +68: _try_api() + + + +node_5802af96 + +54: _try_cli() + + + +node_b28d07f1 + +42: _usability_facts() + + + +node_5802af96->node_b28d07f1 + + + + + +node_5ccd8a5f->node_c61d14f6 + + + + + +node_5ccd8a5f->node_5d9a2b00 + + + + + +node_5ccd8a5f->node_3332348c + + + + + +node_5ccd8a5f->node_5609db09 + + + + + +node_5ccd8a5f->node_5802af96 + + + + + +node_aef9a9e0 + +0: (global)() + + + +node_959ccd53 + +280: main() + + + +node_aef9a9e0->node_959ccd53 + + + + + +node_9daf3d17 + +101: _has_card_for() + + + +node_dcddb8a7 + +118: gate_cdd() + + + +node_dcddb8a7->node_9daf3d17 + + + + + +node_6bdc25a1 + +141: gate_exit_codes() + + + +node_860c7ac6 + +61: gate_pipeline() + + + +node_22c7fbb8 + +132: gate_plan() + + + +node_166a5a71 + +74: gate_tasks() + + + +node_99615e9b + +166: log_override() + + + +node_1ace7292 + +208: run_ship_check() + + + +node_959ccd53->node_1ace7292 + + + + + +node_14f57ab6 + +180: run_all_gates() + + + +node_14f57ab6->node_dcddb8a7 + + + + + +node_14f57ab6->node_6bdc25a1 + + + + + +node_14f57ab6->node_860c7ac6 + + + + + +node_14f57ab6->node_22c7fbb8 + + + + + +node_14f57ab6->node_166a5a71 + + + + + +node_14f57ab6->node_99615e9b + + + + + +node_1ace7292->node_14f57ab6 + + + + + +node_fa2537f0 + +161: _claim_selected_task() + + + +node_fa2537f0->node_946f29f6 + + + + + +node_faa34b75 + +83: _subtask_envelope() + + + +node_fa2537f0->node_faa34b75 + + + + + +node_85154c93 + +40: _dependencies() + + + +node_cd506363 + +96: _in_progress_candidates() + + + +node_4f44f5be + +25: _priority_rank() + + + +node_cd506363->node_4f44f5be + + + + + +node_873601f4 + +29: _sortable_id() + + + +node_cd506363->node_873601f4 + + + + + +node_cce708e5 + +36: _status() + + + +node_cd506363->node_cce708e5 + + + + + +node_a783286a + +109: _ready_candidates() + + + +node_a783286a->node_85154c93 + + + + + +node_a783286a->node_4f44f5be + + + + + +node_a783286a->node_873601f4 + + + + + +node_0f7d8536 + +65: _ready_subtask() + + + +node_0f7d8536->node_85154c93 + + + + + +node_0f7d8536->node_873601f4 + + + + + +node_0f7d8536->node_cce708e5 + + + + + +node_c89f856b + +45: _resolve_tasks() + + + +node_c89f856b->node_946f29f6 + + + + + +node_eb6c47cd + +122: _select_next_task() + + + +node_eb6c47cd->node_cd506363 + + + + + +node_eb6c47cd->node_a783286a + + + + + +node_eb6c47cd->node_0f7d8536 + + + + + +node_eb6c47cd->node_faa34b75 + + + + + +node_7c6a623c + +221: _split_id() + + + +node_7c6a623c->node_946f29f6 + + + + + +node_cc660e06 + +57: _tag_key_for_raw() + + + +node_cc660e06->node_946f29f6 + + + + + +node_12259c7b + +297: cmd_claim_task() + + + +node_12259c7b->node_de4ff705 + + + + + +node_6d9054a6 + +181: run_claim_task() + + + +node_12259c7b->node_6d9054a6 + + + + + +node_c41dc369 + +290: cmd_next_task() + + + +node_c41dc369->node_de4ff705 + + + + + +node_cb095ffd + +155: run_next_task() + + + +node_c41dc369->node_cb095ffd + + + + + +node_fd98ecb0 + +304: cmd_set_status() + + + +node_fd98ecb0->node_de4ff705 + + + + + +node_354f2337 + +230: run_set_status() + + + +node_fd98ecb0->node_354f2337 + + + + + +node_6d9054a6->node_946f29f6 + + + + + +node_6d9054a6->node_46f934ce + + + + + +node_6d9054a6->node_fa2537f0 + + + + + +node_6d9054a6->node_eb6c47cd + + + + + +node_6d9054a6->node_cc660e06 + + + + + +node_cb095ffd->node_c89f856b + + + + + +node_cb095ffd->node_eb6c47cd + + + + + +node_354f2337->node_946f29f6 + + + + + +node_354f2337->node_46f934ce + + + + + +node_354f2337->node_7c6a623c + + + + + +node_354f2337->node_cc660e06 + + + + + +node_aa98bfc2 + +135: _classify_task() + + + +node_c61f9f91 + +180: _generate_acceptance_criteria() + + + +node_aa98bfc2->node_c61f9f91 + + + + + +node_4e896d64 + +86: cmd_backup_prd() + + + +node_4e896d64->node_de4ff705 + + + + + +node_27779b2b + +66: run_backup_prd() + + + +node_4e896d64->node_27779b2b + + + + + +node_86415f94 + +59: cmd_calc_tasks() + + + +node_86415f94->node_de4ff705 + + + + + +node_78043230 + +30: run_calc_tasks() + + + +node_86415f94->node_78043230 + + + + + +node_f97b5e96 + +294: cmd_enrich_tasks() + + + +node_f97b5e96->node_de4ff705 + + + + + +node_76be2ca1 + +229: run_enrich_tasks() + + + +node_f97b5e96->node_76be2ca1 + + + + + +node_27779b2b->node_946f29f6 + + + + + +node_78043230->node_946f29f6 + + + + + +node_76be2ca1->node_946f29f6 + + + + + +node_76be2ca1->node_3a2c6e67 + + + + + +node_76be2ca1->node_ec6290a9 + + + + + +node_76be2ca1->node_aa98bfc2 + + + + + +node_b1831293 + +338: _persist_validation() + + + +node_b1831293->node_18602a47 + + + + + +node_b58afb0f + +350: cmd_validate_prd() + + + +node_b58afb0f->node_b1831293 + + + + + +node_d590851f + +37: run_validate_prd() + + + +node_b58afb0f->node_d590851f + + + + + +node_92879697 + +525: cmd_validate_tasks() + + + +node_92879697->node_de4ff705 + + + + + +node_92879697->node_9431b506 + + + + + +node_d590851f->node_946f29f6 + + + + + +node_d590851f->node_0d5b9b97 + + + + + +node_d590851f->node_40250b05 + + + + + +node_d590851f->node_d7f3f280 + + + + + +node_d590851f->node_ec9848d3 + + + + + +node_9431b506->node_946f29f6 + + + + + +node_9431b506->node_3a2c6e67 + + + + + +node_9431b506->node_ec6290a9 + + + + + diff --git a/docs/architecture/generated/classes_AtlasEngine.mmd b/docs/architecture/generated/classes_AtlasEngine.mmd new file mode 100644 index 0000000..7c9862f --- /dev/null +++ b/docs/architecture/generated/classes_AtlasEngine.mmd @@ -0,0 +1,42 @@ +classDiagram + class Backend { + name : str + detect() dict + expand(task_ids, research, tag) dict + init_project() dict + parse_prd(prd_path, num_tasks, tag) dict + rate(tag, research) dict + } + class CliAgentError { + kind + } + class CommandError { + extra : dict + message : str + } + class LLMError { + kind + } + class NativeBackend { + name : str + detect() dict + expand(task_ids, research, tag) dict + init_project() dict + parse_prd(prd_path, num_tasks, tag) dict + rate(tag, research) dict + } + class ProviderHandle { + kind : str + model : str | None + provider : str + reason : str + role : str + } + class _CASMiss { + actual + } + class _IllegalTransition { + source + target + } + NativeBackend --|> Backend diff --git a/docs/architecture/generated/classes_AtlasEngine.svg b/docs/architecture/generated/classes_AtlasEngine.svg new file mode 100644 index 0000000..bfec29a --- /dev/null +++ b/docs/architecture/generated/classes_AtlasEngine.svg @@ -0,0 +1,113 @@ + + + + + + +classes_AtlasEngine + + + +prd_taskmaster.backend.Backend + +Backend + +name : str + +detect(): dict +expand(task_ids, research, tag): dict +init_project(): dict +parse_prd(prd_path, num_tasks, tag): dict +rate(tag, research): dict + + + +prd_taskmaster.cli_agent.CliAgentError + +CliAgentError + +kind + + + + + +prd_taskmaster.lib.CommandError + +CommandError + +extra : dict +message : str + + + + + +prd_taskmaster.llm_client.LLMError + +LLMError + +kind + + + + + +prd_taskmaster.backend.NativeBackend + +NativeBackend + +name : str + +detect(): dict +expand(task_ids, research, tag): dict +init_project(): dict +parse_prd(prd_path, num_tasks, tag): dict +rate(tag, research): dict + + + +prd_taskmaster.backend.NativeBackend->prd_taskmaster.backend.Backend + + + + + +prd_taskmaster.provider_resolver.ProviderHandle + +ProviderHandle + +kind : str +model : str | None +provider : str +reason : str +role : str + + + + + +prd_taskmaster.pipeline._CASMiss + +_CASMiss + +actual + + + + + +prd_taskmaster.pipeline._IllegalTransition + +_IllegalTransition + +source +target + + + + + diff --git a/docs/architecture/generated/packages_AtlasEngine.mmd b/docs/architecture/generated/packages_AtlasEngine.mmd new file mode 100644 index 0000000..c87f372 --- /dev/null +++ b/docs/architecture/generated/packages_AtlasEngine.mmd @@ -0,0 +1,135 @@ +classDiagram + class prd_taskmaster { + } + class backend { + } + class batch { + } + class capabilities { + } + class cli { + } + class cli_agent { + } + class context_pack { + } + class economy { + } + class feedback { + } + class fleet { + } + class lib { + } + class llm_client { + } + class mode_recommend { + } + class parallel { + } + class pipeline { + } + class preflight { + } + class provider_resolver { + } + class providers { + } + class render { + } + class setup_wizard { + } + class shipcheck { + } + class status { + } + class suggestions { + } + class task_state { + } + class taskmaster { + } + class tasks { + } + class templates { + } + class validation { + } + backend --> prd_taskmaster + backend --> cli_agent + backend --> economy + backend --> fleet + backend --> lib + backend --> llm_client + backend --> parallel + backend --> provider_resolver + backend --> validation + batch --> prd_taskmaster + batch --> backend + batch --> capabilities + batch --> fleet + batch --> lib + batch --> preflight + batch --> providers + capabilities --> lib + capabilities --> mode_recommend + cli --> prd_taskmaster + cli --> backend + cli --> batch + cli --> capabilities + cli --> context_pack + cli --> economy + cli --> feedback + cli --> fleet + cli --> lib + cli --> parallel + cli --> preflight + cli --> providers + cli --> setup_wizard + cli --> status + cli --> task_state + cli --> taskmaster + cli --> tasks + cli --> templates + cli --> validation + cli_agent --> economy + cli_agent --> llm_client + economy --> lib + feedback --> lib + fleet --> prd_taskmaster + fleet --> economy + fleet --> lib + fleet --> parallel + llm_client --> economy + llm_client --> lib + mode_recommend --> fleet + mode_recommend --> providers + pipeline --> lib + preflight --> lib + provider_resolver --> fleet + provider_resolver --> lib + provider_resolver --> llm_client + provider_resolver --> providers + providers --> economy + providers --> fleet + providers --> lib + setup_wizard --> prd_taskmaster + setup_wizard --> fleet + setup_wizard --> lib + setup_wizard --> mode_recommend + setup_wizard --> providers + status --> prd_taskmaster + status --> lib + status --> pipeline + status --> render + status --> shipcheck + suggestions --> lib + task_state --> prd_taskmaster + task_state --> fleet + task_state --> lib + task_state --> parallel + taskmaster --> lib + tasks --> lib + templates --> lib + validation --> lib + validation --> pipeline diff --git a/docs/architecture/generated/packages_AtlasEngine.svg b/docs/architecture/generated/packages_AtlasEngine.svg new file mode 100644 index 0000000..238e5d2 --- /dev/null +++ b/docs/architecture/generated/packages_AtlasEngine.svg @@ -0,0 +1,649 @@ + + + + + + +packages_AtlasEngine + + + +prd_taskmaster + +prd_taskmaster + + + +prd_taskmaster.backend + +prd_taskmaster.backend + + + +prd_taskmaster.backend->prd_taskmaster + + + + + +prd_taskmaster.cli_agent + +prd_taskmaster.cli_agent + + + +prd_taskmaster.backend->prd_taskmaster.cli_agent + + + + + +prd_taskmaster.economy + +prd_taskmaster.economy + + + +prd_taskmaster.backend->prd_taskmaster.economy + + + + + +prd_taskmaster.fleet + +prd_taskmaster.fleet + + + +prd_taskmaster.backend->prd_taskmaster.fleet + + + + + +prd_taskmaster.lib + +prd_taskmaster.lib + + + +prd_taskmaster.backend->prd_taskmaster.lib + + + + + +prd_taskmaster.llm_client + +prd_taskmaster.llm_client + + + +prd_taskmaster.backend->prd_taskmaster.llm_client + + + + + +prd_taskmaster.parallel + +prd_taskmaster.parallel + + + +prd_taskmaster.backend->prd_taskmaster.parallel + + + + + +prd_taskmaster.provider_resolver + +prd_taskmaster.provider_resolver + + + +prd_taskmaster.backend->prd_taskmaster.provider_resolver + + + + + +prd_taskmaster.validation + +prd_taskmaster.validation + + + +prd_taskmaster.backend->prd_taskmaster.validation + + + + + +prd_taskmaster.batch + +prd_taskmaster.batch + + + +prd_taskmaster.batch->prd_taskmaster + + + + + +prd_taskmaster.batch->prd_taskmaster.backend + + + + + +prd_taskmaster.capabilities + +prd_taskmaster.capabilities + + + +prd_taskmaster.batch->prd_taskmaster.capabilities + + + + + +prd_taskmaster.batch->prd_taskmaster.fleet + + + + + +prd_taskmaster.batch->prd_taskmaster.lib + + + + + +prd_taskmaster.preflight + +prd_taskmaster.preflight + + + +prd_taskmaster.batch->prd_taskmaster.preflight + + + + + +prd_taskmaster.providers + +prd_taskmaster.providers + + + +prd_taskmaster.batch->prd_taskmaster.providers + + + + + +prd_taskmaster.capabilities->prd_taskmaster.lib + + + + + +prd_taskmaster.mode_recommend + +prd_taskmaster.mode_recommend + + + +prd_taskmaster.capabilities->prd_taskmaster.mode_recommend + + + + + +prd_taskmaster.cli + +prd_taskmaster.cli + + + +prd_taskmaster.cli->prd_taskmaster + + + + + +prd_taskmaster.cli->prd_taskmaster.backend + + + + + +prd_taskmaster.cli->prd_taskmaster.batch + + + + + +prd_taskmaster.cli->prd_taskmaster.capabilities + + + + + +prd_taskmaster.context_pack + +prd_taskmaster.context_pack + + + +prd_taskmaster.cli->prd_taskmaster.context_pack + + + + + +prd_taskmaster.cli->prd_taskmaster.economy + + + + + +prd_taskmaster.feedback + +prd_taskmaster.feedback + + + +prd_taskmaster.cli->prd_taskmaster.feedback + + + + + +prd_taskmaster.cli->prd_taskmaster.fleet + + + + + +prd_taskmaster.cli->prd_taskmaster.lib + + + + + +prd_taskmaster.cli->prd_taskmaster.parallel + + + + + +prd_taskmaster.cli->prd_taskmaster.preflight + + + + + +prd_taskmaster.cli->prd_taskmaster.providers + + + + + +prd_taskmaster.setup_wizard + +prd_taskmaster.setup_wizard + + + +prd_taskmaster.cli->prd_taskmaster.setup_wizard + + + + + +prd_taskmaster.status + +prd_taskmaster.status + + + +prd_taskmaster.cli->prd_taskmaster.status + + + + + +prd_taskmaster.task_state + +prd_taskmaster.task_state + + + +prd_taskmaster.cli->prd_taskmaster.task_state + + + + + +prd_taskmaster.taskmaster + +prd_taskmaster.taskmaster + + + +prd_taskmaster.cli->prd_taskmaster.taskmaster + + + + + +prd_taskmaster.tasks + +prd_taskmaster.tasks + + + +prd_taskmaster.cli->prd_taskmaster.tasks + + + + + +prd_taskmaster.templates + +prd_taskmaster.templates + + + +prd_taskmaster.cli->prd_taskmaster.templates + + + + + +prd_taskmaster.cli->prd_taskmaster.validation + + + + + +prd_taskmaster.cli_agent->prd_taskmaster.economy + + + + + +prd_taskmaster.cli_agent->prd_taskmaster.llm_client + + + + + +prd_taskmaster.economy->prd_taskmaster.lib + + + + + +prd_taskmaster.feedback->prd_taskmaster.lib + + + + + +prd_taskmaster.fleet->prd_taskmaster + + + + + +prd_taskmaster.fleet->prd_taskmaster.economy + + + + + +prd_taskmaster.fleet->prd_taskmaster.lib + + + + + +prd_taskmaster.fleet->prd_taskmaster.parallel + + + + + +prd_taskmaster.llm_client->prd_taskmaster.economy + + + + + +prd_taskmaster.llm_client->prd_taskmaster.lib + + + + + +prd_taskmaster.mode_recommend->prd_taskmaster.fleet + + + + + +prd_taskmaster.mode_recommend->prd_taskmaster.providers + + + + + +prd_taskmaster.pipeline + +prd_taskmaster.pipeline + + + +prd_taskmaster.pipeline->prd_taskmaster.lib + + + + + +prd_taskmaster.preflight->prd_taskmaster.lib + + + + + +prd_taskmaster.provider_resolver->prd_taskmaster.fleet + + + + + +prd_taskmaster.provider_resolver->prd_taskmaster.lib + + + + + +prd_taskmaster.provider_resolver->prd_taskmaster.llm_client + + + + + +prd_taskmaster.provider_resolver->prd_taskmaster.providers + + + + + +prd_taskmaster.providers->prd_taskmaster.economy + + + + + +prd_taskmaster.providers->prd_taskmaster.fleet + + + + + +prd_taskmaster.providers->prd_taskmaster.lib + + + + + +prd_taskmaster.render + +prd_taskmaster.render + + + +prd_taskmaster.setup_wizard->prd_taskmaster + + + + + +prd_taskmaster.setup_wizard->prd_taskmaster.fleet + + + + + +prd_taskmaster.setup_wizard->prd_taskmaster.lib + + + + + +prd_taskmaster.setup_wizard->prd_taskmaster.mode_recommend + + + + + +prd_taskmaster.setup_wizard->prd_taskmaster.providers + + + + + +prd_taskmaster.shipcheck + +prd_taskmaster.shipcheck + + + +prd_taskmaster.status->prd_taskmaster + + + + + +prd_taskmaster.status->prd_taskmaster.lib + + + + + +prd_taskmaster.status->prd_taskmaster.pipeline + + + + + +prd_taskmaster.status->prd_taskmaster.render + + + + + +prd_taskmaster.status->prd_taskmaster.shipcheck + + + + + +prd_taskmaster.suggestions + +prd_taskmaster.suggestions + + + +prd_taskmaster.suggestions->prd_taskmaster.lib + + + + + +prd_taskmaster.task_state->prd_taskmaster + + + + + +prd_taskmaster.task_state->prd_taskmaster.fleet + + + + + +prd_taskmaster.task_state->prd_taskmaster.lib + + + + + +prd_taskmaster.task_state->prd_taskmaster.parallel + + + + + +prd_taskmaster.taskmaster->prd_taskmaster.lib + + + + + +prd_taskmaster.tasks->prd_taskmaster.lib + + + + + +prd_taskmaster.templates->prd_taskmaster.lib + + + + + +prd_taskmaster.validation->prd_taskmaster.lib + + + + + +prd_taskmaster.validation->prd_taskmaster.pipeline + + + + + diff --git a/docs/architecture/rendered/00-system-overview.svg b/docs/architecture/rendered/00-system-overview.svg new file mode 100644 index 0000000..bb2d6ff --- /dev/null +++ b/docs/architecture/rendered/00-system-overview.svg @@ -0,0 +1,856 @@ +

Atlas engine — system overview

+

hexagon = LLM step · rectangle = deterministic Python · blue = interface · oval = external · person = you

+
you1 · LLM / agent layer — non-deterministic (prompts the model executes)2 · interface boundary — fail-closed3 · deterministic engine — Python (state in .atlas-ai/)4 · external — out of processSKILL.md · /atlas · /go entrypointphases/*.md · DISCOVER · GENERATE · HANDOFFskills/* · discover · generate · handoff · setup · expand-tasks · execute-task · execute-fleetmcp-server/server.py · _HardenedMCP · ~31 MCP toolscli.py · DISPATCH · CLI subcommandspipeline.py · phase state-machine · check_gate · advance_phase CASGENERATE engine · backend · provider_resolver · cli_agent · llm_client · economy → 11task graph · tasks · task_state · validation · parallel → 10detect / setup · preflight · batch · mode_recommend · providers · setup_wizard · fleet → 12shipcheck.py · 5 gates → SHIP_CHECK_OKlib.py · locked_update · atomic IO · shared substratemodel CLIs · claude · codex · gemini · keylessraw vendor APIs · anthropic · openai · googletask-master backend · optional one-line goalthe LLM only reaches the core through thesetranslate calls → deterministic opscli tierapi tierliveness probeoptional backend + + + + + + + + +
diff --git a/docs/architecture/rendered/10-component-deterministic-core.svg b/docs/architecture/rendered/10-component-deterministic-core.svg new file mode 100644 index 0000000..0d9abf5 --- /dev/null +++ b/docs/architecture/rendered/10-component-deterministic-core.svg @@ -0,0 +1,144 @@ +prd-taskmaster · deterministic core — phase state-machine, gates, task graphPhase State Machine · GatesSpec GraderTask Graph OpsScheduler · Parallel MergeSubstrate Floor (leaf)tasks.json · pipeline.json · validation.jsonpipeline.pyshipcheck.pyvalidation.pytasks.pytask_state.pyfleet.pyparallel.py · agent-parallel research; hybrid SINGLE-WRITER mergelib.pyadvance_phase() · CAS on expected_currentLEGAL_TRANSITIONS · SETUP DISCOVER GENERATE HANDOFF EXECUTEcheck_gate(phase, evidence)preflight() · recommended_action ladderGate1 · pipeline.json current_phase == EXECUTEGate2 · every task status == doneGate3 · CDD card per task idGate4 · plan.md existsGate5 HARD · no non-zero Exit status Nrun_ship_check() · run_all_gates()SHIP_CHECK_OKrun_validate_prd() · 13 checks · score to gradeplaceholder detect · HARD FAIL floors NEEDS_WORKrun_validate_tasks() · structural task lintrun_calc_tasks() · ceil(req x 1.5) clamped to scale bandrun_enrich_tasks() · write phaseConfig_classify_task() · SIMPLE MEDIUM COMPLEX RESEARCH VALIDATIONrun_next_task() · in-progress then ready selectrun_claim_task() · select plus mark in-progressrun_set_status() · update under flockcompute_waves() · dependency-ordered frontier chunksready_set() · pending with deps doneroute_task() · tier to backend:modelrun_fleet_waves() · waves plus routingbuild_packets() · one research packet per taskapply_results() · merge subtasks plus complexity in ONE atomic writeextract · inject · tag bridge to flat tasks.jsonlocked_update() · flock LOCK_EX read-modify-writeatomic_write() · tmp plus os.replace_resolve_tasks_payload() · tagged-over-flatread_json · write_json · CommandError · VAGUE_PATTERN validates transitionmust pass before advancereads staterunsrunsrunsrunsrunsall pass emitsreads current_phasereads task statusplaceholder hard-failvalidation_grade evidencetask_count evidenceusesresults backexpanded tasks then enrichuses ready_setselect then claimcomputesper-task routingtier of pendingimports fleetimports parallel · get_tasks · TASKSfleet imports parallellocked_update CASread_json_resolve_tasks_payloadtagged-over-flatVAGUE_PATTERN · sectionsclaim under flockset-status under flockCommandError · emitown atomic write · single-writeratomic CAS writesatomic writes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/architecture/rendered/11-component-backend-provider-engine.svg b/docs/architecture/rendered/11-component-backend-provider-engine.svg new file mode 100644 index 0000000..d26a9c7 --- /dev/null +++ b/docs/architecture/rendered/11-component-backend-provider-engine.svg @@ -0,0 +1,123 @@ +prd-taskmaster GENERATE engine · backend.NativeBackend -> resolve_provider tier -> cli | api | planGeneration Engine (backend.py)provider_resolver.pyeconomy.py · tier ladder + cost ledger (wraps every call)CLI tier · keyless (host session auth)API tier · raw key (urllib)Plan floor · agent_action_requiredNativeBackend · parse_prd · expand · rateThreadPoolExecutor fan-out · _expand_packet per packetresolve_provider(main) · single tier decision · cli | api | planeconomy_profile · structured_gen_start tiershift_tier · escalation step on invalid_jsonappend_telemetry · telemetry.jsonl cost ledgercli_agent.generate_json_via_cliexternal model CLI · claude · codex · geminillm_client.generate_jsonvendor API · anthropic · openai · googlereturns agent_action_required packetin-session LLM · decomposes by hand expand() submits packetssubprocess spawn · no API keyHTTP POST · x-api-keyhand-off · NATIVE_PARSE_STEPSeconomy_profile(config) sets start tierstart tierresolve_provider(main)per-packet handle.kindkind == clikind == apikind == plan (floor)append_telemetry (native-cli)append_telemetry (native-api)invalid_json · escalate one tierretry at higher tier (ceiling clamp) + + + + + + + + + + + + + + + + + diff --git a/docs/architecture/rendered/12-component-detect-setup-mode.svg b/docs/architecture/rendered/12-component-detect-setup-mode.svg new file mode 100644 index 0000000..3c7f857 --- /dev/null +++ b/docs/architecture/rendered/12-component-detect-setup-mode.svg @@ -0,0 +1,134 @@ +Detect · Setup · Mode-recommend (Phase 0/1 environment detection)batch.py · orchestratorpreflight.py · environment detectionmode_recommend.py · MCP-wired detect+validate corecapabilities.py · batch-wired capability scanproviders.py · provider resolutionsetup_wizard.py · atlas setup wizardExternal model CLIs (PATH)MCP · CLI surfacesrun_engine_preflight(configure) · one-call Phase-1_backend_block · _backend_summary · build summaryrun_preflight · .taskmaster · PRD · task counts · crash staterun_detect_taskmaster · MCP then CLI then nonevalidate_setup(provider_mode) · 6 SETUP checksdetect_capabilities · exec modes A-J · tierdetect_taskmaster · detect_atlas_launcherrun_detect_capabilities · scan skills+plugins · recommend moderun_configure_providers · repair-on-detectrun_detect_providers · main · fallback · research_provider_usable · _probe_spawn · nested-claude checkrun_setup · detect+recommend · accept · customise · add-key_recommend · _panel · per-role recommendation_run_validate_step · validate_setup plus live probe_live_probe · one-token liveness probeclaude -p okcodex --versiongemini --versionMCP engine_preflightMCP validate_setupMCP detect_capabilitiesCLI atlas setup delegateswires mode_recommend.validate_setupwires mode_recommend.detect_capabilitiescmd_setup1 · probe env2 · probe taskmaster3 · if has_taskmaster4 · resolve providers5 · scan capabilities6 · build summarydetect_atlas_launcher · ATLAS_FLEET_REASON_detect_taskmaster_methodreachability per check 5/6tier · recommended_modebuild panelaccept · customisevalidatedetected providersdetect_capabilities · tiercredential-aware checksper chosen providerspawnspawnspawn_probe_spawn_probe_spawn + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/architecture/rendered/40-runflow-script-vs-llm.svg b/docs/architecture/rendered/40-runflow-script-vs-llm.svg new file mode 100644 index 0000000..abff5ee --- /dev/null +++ b/docs/architecture/rendered/40-runflow-script-vs-llm.svg @@ -0,0 +1,136 @@ +/atlas run · where determinism starts and stops · hexagon=LLM blue=script diamond=gateUser · 'I want to build ...'skills/go · orchestratorPhase 0 · SETUP (mostly script)Phase 1 · DISCOVER (mostly LLM)Phase 2 · GENERATE (hybrid)Phase 3 · HANDOFF (split)Phase 4 · EXECUTE · skills/execute-task (40) · code-gated, LLM-executedcheck_gate('SETUP') · advance_phasecheck_gate('DISCOVER') · advance_phasecheck_gate('GENERATE') · task_count + coverage + gradecheck_gate('HANDOFF') · mode + plan_fileSkill('sync') · refresh memory bank · MANDATORY pre-tokenship-check.py · 5 gates · phase + all-done + CDD + plan + no-nonzero-exitSHIP_CHECK_OK · unfakable token · stdout once · TERMINALgo SKILL · pure routing · reads current_phase · dispatchespreflight() · current_phase() · MCP server.pyscript.py init-project · scaffold .taskmaster · .atlas-aiscript.py backend-detect · resolve_provider · setup wizardvalidate_setup() · probe pipeline wiredsuperpowers:brainstorming · adaptive Q+A · constraints · scaleUser approval gate · AskUserQuestionload_template() · canonical spec shapefill prd.md · LLM writes from discovery + constraintsvalidate_prd() · regex checks · grade · placeholders_foundparse_prd() · backend op · provider-or-CLI, else agentrate_tasks() · complexity report · provider-or-CLIexpand_tasks() · subtasks · provider-or-CLI, else agentdetect_capabilities() · tier · compute_fleet_waves()User mode picker · AskUserQuestion · Mode A/B/C/Dappend_workflow() · idempotent CLAUDE.md writescript.py next-task · pick ready task · deterministicbuild CDD card · script.py set-status in-progressdispatch implementer subagent · LLM does the worktriple verify · /doubt + /validate + Opus merge-checkship-check.py --dry-run · HARD exit-code gatescript.py set-status done · subtask writeback · pipeline.json goalread stateroute · null/SETUPevidence-> DISCOVERapproved · constraints · scale-> GENERATEprd.mdgrade ok · 0 placeholderstasks.jsoncomplexity reportsubtask coverage 1.0-> HANDOFFMode A/B/C/Dmode + plan-> EXECUTEsubagent DONEexit-code cleanloop · next taskall tasks donememory refreshed5 gates pass · exit 0 agent_action_required · regen placeholders_found > 0 · fix specagent fallback · idempotent re-expanddisagree · re-dispatch SHIP_CHECK_FAIL · task-fix-N Mode D locked · re-prompt free + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/architecture/rendered/50-swimlane-setup.svg b/docs/architecture/rendered/50-swimlane-setup.svg new file mode 100644 index 0000000..51d0f61 --- /dev/null +++ b/docs/architecture/rendered/50-swimlane-setup.svg @@ -0,0 +1 @@ +

external model execution

deterministic engine — Python

LLM / agent — non-deterministic

all roles set — skip mutate

not ready — reconfigure providers

fail — fix provider config

pass → advance_phase(DISCOVER)

1 · enter SETUP, run go router preflight
skills/go · skills/setup · check_gate diagnostic

4 · ship-check skel bootstrap
copy customizations + ship-check.py if absent

6 · DETECT-FIRST judgment
do not clobber a working provider config

10 · read rendered panel, announce
MCP-vs-CLI backend, then advance_phase

2 · batch.run_engine_preflight
preflight.run_preflight (env + taskmaster probe)

3 · backend.NativeBackend.init_project
taskmaster.init_taskmaster (vestigial file format)

5 · mode_recommend.detect_capabilities
tier = free | cli | api

7 · providers.run_configure_providers
fill empty roles only · run_detect_providers

8 · setup_wizard.run_setup (probe pipeline)
fleet.save_engine_config (persist backend)

9 · mode_recommend.validate_setup
6 checks · ready + 0 critical failures

11 · pipeline.check_gate('SETUP')
validate_setup.ready · critical_failures == 0

8a · setup_wizard._live_probe
claude / codex / gemini CLI · keyless liveness

DISCOVER ▶

\ No newline at end of file diff --git a/docs/architecture/rendered/51-swimlane-discover.svg b/docs/architecture/rendered/51-swimlane-discover.svg new file mode 100644 index 0000000..d38fbf0 --- /dev/null +++ b/docs/architecture/rendered/51-swimlane-discover.svg @@ -0,0 +1 @@ +

deterministic engine — Python

LLM / agent — non-deterministic

Autonomous mode (no user)

self-approve · assumptions documented

refine — re-ask, update summary

fail — gather missing evidence

pass → advance_phase(GENERATE)

1 · capture goal from skill args
detect Interactive vs Autonomous mode
phases/DISCOVER.md · skills/discover

3 · superpowers:brainstorming
adaptive one-question-at-a-time Q and A

4 · INTERCEPT before writing-plans
capture design / requirements / decisions

5 · extract constraints (CONSTRAINTS CAPTURED)
calibrate scale: Solo / Team / Enterprise

6 · AskUserQuestion approval gate
present discovery summary

3a · Autonomous self-brainstorm
write discovery notes · document assumptions

2 · pipeline.check_gate('DISCOVER')
entry diagnostic (exit gate, expect false)

7 · pipeline.check_gate('DISCOVER')
user_approved OR auto_classification==CLEAR
+ assumptions_documented

GENERATE ▶

\ No newline at end of file diff --git a/docs/architecture/rendered/52-swimlane-generate.svg b/docs/architecture/rendered/52-swimlane-generate.svg new file mode 100644 index 0000000..b5d95a3 --- /dev/null +++ b/docs/architecture/rendered/52-swimlane-generate.svg @@ -0,0 +1 @@ +

external model execution

deterministic engine — Python

LLM / agent — non-deterministic

placeholders / low grade — regenerate

cli tier

api tier

plan tier (no key + no CLI)

hand-built task JSON

fail — regenerate

pass → advance_phase(HANDOFF)

1 · write PRD prose, replace every placeholder
phases/GENERATE.md · skills/generate

6c · plan-floor: decompose to schema by hand
(when engine returns agent_action_required)

2 · templates.run_load_template

3 · validation.run_validate_prd
13 checks + placeholder HARD FAIL

4 · tasks.run_calc_tasks (count formula)

5 · provider_resolver.resolve_provider
tier = cli | api | plan

7 · backend.NativeBackend.parse_prd / expand
economy.shift_tier (escalate) · append_telemetry

8 · parallel.apply_results (atomic merge)
tasks.run_enrich_tasks (complexity)

9 · validation.run_validate_tasks

10 · pipeline.check_gate('GENERATE')
grade ≥ GOOD · tasks>0 · 100% subtasks

6a · cli_agent.generate_json_via_cli
claude / codex / gemini CLI · keyless

6b · llm_client.generate_json
raw vendor API

HANDOFF ▶

\ No newline at end of file diff --git a/docs/architecture/rendered/53-swimlane-handoff.svg b/docs/architecture/rendered/53-swimlane-handoff.svg new file mode 100644 index 0000000..0bfe1af --- /dev/null +++ b/docs/architecture/rendered/53-swimlane-handoff.svg @@ -0,0 +1 @@ +

deterministic engine — Python

LLM / agent — non-deterministic

premium · parallel graph

free / serial · pick free mode

pass → advance_phase(EXECUTE)

1 · phases/HANDOFF.md via skills/handoff ·
enter HANDOFF, copy checklist

7 · recommend ONE mode ·
M / D / C / A / B with reason

8 · write Task Execution Workflow
block into CLAUDE.md

9 · AskUserQuestion ·
structured mode picker

11 · dispatch chosen mode ·
writing-plans / ralph-loop / atlas-loop

2 · mode_recommend.detect_capabilities ·
tier premium/free gating of Mode D / Atlas Fleet

3 · fleet.compute_waves ·
topological waves plus deadlock detect

4 · fleet.route_task plus resolve_backend plus task_tier ·
per-task backend routing

5 · render.handoff_panel ·
spec, task count, capabilities summary

6 · economy.summarize_telemetry ·
cost/usage ledger

10 · fleet.run_fleet_waves ·
wave scheduling JSON (premium dispatch)

12 · pipeline.check_gate('HANDOFF') ·
user_mode_choice plus plan_file_exists

EXECUTE ▶

\ No newline at end of file diff --git a/docs/architecture/rendered/54-swimlane-execute.svg b/docs/architecture/rendered/54-swimlane-execute.svg new file mode 100644 index 0000000..d9a3c3d --- /dev/null +++ b/docs/architecture/rendered/54-swimlane-execute.svg @@ -0,0 +1 @@ +

deterministic engine — Python

LLM / agent — non-deterministic

claimed task JSON

claim/set-status calls

fail → bounce back, fix task

pass → print SHIP_CHECK_OK

loop while tasks remain

all done → SHIP_CHECK_OK + final PR

1 · skills/execute-task solo CDD loop
OR skills/execute-fleet parallel workers

4 · implement task code
CDD RED → GREEN → BLUE

5 · judge GREEN/RED/BLUE evidence
write CDD card + evidence files

2 · fleet.ready_set / fleet.compute_waves
order work into ready set / waves

3 · task_state.run_claim_task · _select_next_task
claim next under lib.locked_update flock → in-progress

6 · task_state.run_set_status
mark task/subtask done

7 · shipcheck.gate_pipeline / gate_tasks / gate_cdd / gate_plan

8 · shipcheck.gate_exit_codes
EXIT_STATUS_RE over .atlas-ai/evidence/** · anti-fake

9 · shipcheck.run_all_gates
all gates pass?

10 · render.execute_panel / shipcheck_panel
feedback.append_feedback · suggestions.append_suggestion

COMPLETE ▶

\ No newline at end of file diff --git a/docs/architecture/rendered/60-sequence-generate-parse-prd.svg b/docs/architecture/rendered/60-sequence-generate-parse-prd.svg new file mode 100644 index 0000000..9824d62 --- /dev/null +++ b/docs/architecture/rendered/60-sequence-generate-parse-prd.svg @@ -0,0 +1 @@ +economy.append_telemetrytasks.json write · _write_tasks_into_tagvalidation.run_validate_tasksVendor APIllm_client.generate_jsonExternal CLI · claude/codex/geminicli_agent.generate_json_via_cliprovider_resolver.resolve_providerNativeBackend.parse_prdInterface · cli.run_parse_prdeconomy.append_telemetrytasks.json write · _write_tasks_into_tagvalidation.run_validate_tasksVendor APIllm_client.generate_jsonExternal CLI · claude/codex/geminicli_agent.generate_json_via_cliprovider_resolver.resolve_providerNativeBackend.parse_prdInterface · cli.run_parse_prdrole config + CLI/key presence + cached spawn probeno spawn, no network hereone parse-retry on bad JSONCliAgentError on no_cli / timeout / nonzero / invalid_jsonalt[CLI succeeds][CliAgentError]one parse-retry · one retry on 429/5xx401/403 fail fast · LLMError kindalt[API succeeds][LLMError no_key][LLMError other]engine does NOT read the PRD or generate — it returns the plan-floorin-session LLM decomposes the PRD by hand,writes Native-shape tasks.json, then runs validate-tasksalt[Tier 1 · kind=cli · keyless CLI-agent][Tier 2 · kind=api · raw-key vendor API][Tier 3 · kind=plan · plan-floor]opt[candidate generated · cli or api tier]In-session LLM · GENERATE.mdparse_prd · prd_path, num_tasks, tag1run_parse_prd2resolve_provider main3ProviderHandle · kind = cli | api | plan4generate_json_via_cli · prompt + schema_hint5subprocess.run · argv, stdin, timeout6stdout JSON envelope7append_telemetry · native-cli, exit, wall_ms8candidate tasks JSON · ai=cli9raise CliAgentError10ok=false · agent_action_required11LLM decomposes by hand12generate_json · prompt + schema_hint + tier13HTTP POST · messages / chat / generateContent14response JSON · content + usage15append_telemetry · native-api, http_status, tokens16candidate + telemetry_ref · ai=api17raise LLMError no_key18ok=false · agent_action_required19LLM decomposes by hand20raise LLMError kind21ok=false · error, kind22ok=false · agent_action_required = _agent_parse_action23agent_action_required · schema_hint + NATIVE_PARSE_STEPS24run_validate_tasks · allow_empty_subtasks=false25ok · or raise CommandError26write tasks atomically into tag27resolved tag28ok=true · task_count, tag, ai, validation29parse result · tasks written30In-session LLM · GENERATE.md \ No newline at end of file diff --git a/docs/architecture/src/00-system-overview.d2 b/docs/architecture/src/00-system-overview.d2 new file mode 100644 index 0000000..336b984 --- /dev/null +++ b/docs/architecture/src/00-system-overview.d2 @@ -0,0 +1,54 @@ +# Atlas / prd-taskmaster — system overview (the whole engine on one page). +# Four layers: LLM/agent prompts → fail-closed interface → deterministic Python core → external. +# Detail for each core area lives in 10/11/12-component-*.d2. Render: d2 --layout elk 00-system-overview.d2 00-system-overview.svg +direction: down + +classes: { + llm: { style.fill: "#E69F00"; style.stroke: "#8a6100"; style.font-color: "#111111"; shape: hexagon } + code: { style.fill: "#56B4E9"; style.stroke: "#1f6f9c"; style.font-color: "#111111" } + ext: { style.fill: "#009E73"; style.stroke: "#00674c"; style.font-color: "#ffffff"; shape: oval } + iface: { style.fill: "#0072B2"; style.stroke: "#013a5e"; style.font-color: "#ffffff" } + gate: { style.fill: "#F0E442"; style.stroke: "#9a8f00"; style.font-color: "#111111"; shape: diamond } + human: { style.fill: "#CC79A7"; style.font-color: "#111111"; shape: person } +} + +title: |md + # Atlas engine — system overview + hexagon = LLM step · rectangle = deterministic Python · blue = interface · oval = external · person = you +| { near: top-center } + +human: "you" { class: human } + +llm_layer: "1 · LLM / agent layer — non-deterministic (prompts the model executes)" { + skillmd: "SKILL.md · /atlas · /go entrypoint" { class: llm } + phases: "phases/*.md · DISCOVER · GENERATE · HANDOFF" { class: llm } + skills: "skills/* · discover · generate · handoff · setup · expand-tasks · execute-task · execute-fleet" { class: llm } +} + +iface_layer: "2 · interface boundary — fail-closed" { + mcp: "mcp-server/server.py · _HardenedMCP · ~31 MCP tools" { class: iface } + cli: "cli.py · DISPATCH · CLI subcommands" { class: iface } +} + +core_layer: "3 · deterministic engine — Python (state in .atlas-ai/)" { + pipeline: "pipeline.py · phase state-machine · check_gate · advance_phase CAS" { class: code } + gen: "GENERATE engine · backend · provider_resolver · cli_agent · llm_client · economy → 11" { class: code } + graph: "task graph · tasks · task_state · validation · parallel → 10" { class: code } + detect: "detect / setup · preflight · batch · mode_recommend · providers · setup_wizard · fleet → 12" { class: code } + ship: "shipcheck.py · 5 gates → SHIP_CHECK_OK" { class: code } + lib: "lib.py · locked_update · atomic IO · shared substrate" { class: code } +} + +ext_layer: "4 · external — out of process" { + clis: "model CLIs · claude · codex · gemini · keyless" { class: ext } + apis: "raw vendor APIs · anthropic · openai · google" { class: ext } + tm: "task-master backend · optional" { class: ext } +} + +human -> llm_layer: "one-line goal" +llm_layer -> iface_layer: "the LLM only reaches the core through these" +iface_layer -> core_layer: "translate calls → deterministic ops" +core_layer.gen -> ext_layer.clis: "cli tier" +core_layer.gen -> ext_layer.apis: "api tier" +core_layer.detect -> ext_layer.clis: "liveness probe" +core_layer.graph -> ext_layer.tm: "optional backend" diff --git a/docs/architecture/src/10-component-deterministic-core.d2 b/docs/architecture/src/10-component-deterministic-core.d2 new file mode 100644 index 0000000..b2786a4 --- /dev/null +++ b/docs/architecture/src/10-component-deterministic-core.d2 @@ -0,0 +1,128 @@ +direction: right +classes: { + llm: { style.fill: "#E69F00"; style.stroke: "#8a6100"; style.font-color: "#111111"; shape: hexagon } + code: { style.fill: "#56B4E9"; style.stroke: "#1f6f9c"; style.font-color: "#111111" } + ext: { style.fill: "#009E73"; style.stroke: "#00674c"; style.font-color: "#ffffff"; shape: oval } + iface: { style.fill: "#0072B2"; style.stroke: "#013a5e"; style.font-color: "#ffffff" } + gate: { style.fill: "#F0E442"; style.stroke: "#9a8f00"; style.font-color: "#111111"; shape: diamond } + human: { style.fill: "#CC79A7"; style.font-color: "#111111"; shape: person } +} + +title: "prd-taskmaster · deterministic core — phase state-machine, gates, task graph" { near: top-center; shape: text; style.font-size: 22 } + +sm: "Phase State Machine · Gates" { + pipeline: "pipeline.py" { + advance: "advance_phase() · CAS on expected_current" { class: code } + legal: "LEGAL_TRANSITIONS · SETUP DISCOVER GENERATE HANDOFF EXECUTE" { class: code } + gate: "check_gate(phase, evidence)" { class: gate } + pre: "preflight() · recommended_action ladder" { class: code } + } + ship: "shipcheck.py" { + g1: "Gate1 · pipeline.json current_phase == EXECUTE" { class: gate } + g2: "Gate2 · every task status == done" { class: gate } + g3: "Gate3 · CDD card per task id" { class: gate } + g4: "Gate4 · plan.md exists" { class: gate } + g5: "Gate5 HARD · no non-zero Exit status N" { class: gate } + run: "run_ship_check() · run_all_gates()" { class: code } + token: "SHIP_CHECK_OK" { class: iface } + } +} + +grade: "Spec Grader" { + valid: "validation.py" { + vprd: "run_validate_prd() · 13 checks · score to grade" { class: code } + ph: "placeholder detect · HARD FAIL floors NEEDS_WORK" { class: gate } + vtasks: "run_validate_tasks() · structural task lint" { class: code } + } +} + +graph: "Task Graph Ops" { + tasks: "tasks.py" { + calc: "run_calc_tasks() · ceil(req x 1.5) clamped to scale band" { class: code } + enrich: "run_enrich_tasks() · write phaseConfig" { class: code } + classify: "_classify_task() · SIMPLE MEDIUM COMPLEX RESEARCH VALIDATION" { class: code } + } + tstate: "task_state.py" { + next: "run_next_task() · in-progress then ready select" { class: code } + claim: "run_claim_task() · select plus mark in-progress" { class: code } + setst: "run_set_status() · update under flock" { class: code } + } +} + +sched: "Scheduler · Parallel Merge" { + fleet: "fleet.py" { + waves: "compute_waves() · dependency-ordered frontier chunks" { class: code } + ready: "ready_set() · pending with deps done" { class: code } + route: "route_task() · tier to backend:model" { class: code } + runwaves: "run_fleet_waves() · waves plus routing" { class: code } + } + par: "parallel.py · agent-parallel research; hybrid SINGLE-WRITER merge" { + packets: "build_packets() · one research packet per task" { class: code } + apply: "apply_results() · merge subtasks plus complexity in ONE atomic write" { class: code } + flat: "extract · inject · tag bridge to flat tasks.json" { class: code } + } +} + +substrate: "Substrate Floor (leaf)" { + lib: "lib.py" { + locked: "locked_update() · flock LOCK_EX read-modify-write" { class: code } + atomic: "atomic_write() · tmp plus os.replace" { class: code } + resolve: "_resolve_tasks_payload() · tagged-over-flat" { class: code } + helpers: "read_json · write_json · CommandError · VAGUE_PATTERN" { class: code } + } +} + +tasksjson: "tasks.json · pipeline.json · validation.json" { class: ext } + +# ── phase machine internal flow ── +sm.pipeline.advance -> sm.pipeline.legal: "validates transition" +sm.pipeline.gate -> sm.pipeline.advance: "must pass before advance" +sm.pipeline.pre -> tasksjson: "reads state" + +# ── ship-check gate chain ── +sm.ship.run -> sm.ship.g1: "runs" +sm.ship.run -> sm.ship.g2: "runs" +sm.ship.run -> sm.ship.g3: "runs" +sm.ship.run -> sm.ship.g4: "runs" +sm.ship.run -> sm.ship.g5: "runs" +sm.ship.g5 -> sm.ship.token: "all pass emits" +sm.ship.g1 -> tasksjson: "reads current_phase" +sm.ship.g2 -> tasksjson: "reads task status" + +# ── GENERATE gate consumes grader + calc + enrich ── +grade.valid.vprd -> grade.valid.ph: "placeholder hard-fail" +grade.valid.vprd -> sm.pipeline.gate: "validation_grade evidence" +graph.tasks.calc -> sm.pipeline.gate: "task_count evidence" +graph.tasks.enrich -> graph.tasks.classify: "uses" + +# ── parallel expand feeds enrich + classify ── +sched.par.packets -> sched.par.apply: "results back" +sched.par.apply -> graph.tasks.enrich: "expanded tasks then enrich" + +# ── selection / scheduling consume the graph ── +graph.tstate.next -> sched.fleet.ready: "uses ready_set" +graph.tstate.claim -> graph.tstate.next: "select then claim" +sched.fleet.runwaves -> sched.fleet.waves: "computes" +sched.fleet.runwaves -> sched.fleet.route: "per-task routing" +sched.fleet.route -> sched.fleet.ready: "tier of pending" + +# ── task_state depends on fleet + parallel ── +graph.tstate.next -> sched.fleet.waves: "imports fleet" +graph.tstate.claim -> sched.par.apply: "imports parallel · get_tasks · TASKS" +sched.fleet.route -> sched.par.apply: "fleet imports parallel" + +# ── EVERYTHING points to the lib.py substrate floor ── +sm.pipeline.advance -> substrate.lib.locked: "locked_update CAS" +sm.pipeline.pre -> substrate.lib.helpers: "read_json" +graph.tasks.enrich -> substrate.lib.resolve: "_resolve_tasks_payload" +grade.valid.vtasks -> substrate.lib.resolve: "tagged-over-flat" +grade.valid.vprd -> substrate.lib.helpers: "VAGUE_PATTERN · sections" +graph.tstate.claim -> substrate.lib.locked: "claim under flock" +graph.tstate.setst -> substrate.lib.locked: "set-status under flock" +sched.fleet.waves -> substrate.lib.helpers: "CommandError · emit" +sched.par.apply -> substrate.lib.atomic: "own atomic write · single-writer" + +# ── persisted state lives behind the locked/atomic substrate ── +substrate.lib.locked -> tasksjson: "atomic CAS writes" +substrate.lib.atomic -> tasksjson: "atomic writes" + diff --git a/docs/architecture/src/11-component-backend-provider-engine.d2 b/docs/architecture/src/11-component-backend-provider-engine.d2 new file mode 100644 index 0000000..e36ca05 --- /dev/null +++ b/docs/architecture/src/11-component-backend-provider-engine.d2 @@ -0,0 +1,65 @@ +direction: right +classes: { + llm: { style.fill: "#E69F00"; style.stroke: "#8a6100"; style.font-color: "#111111"; shape: hexagon } + code: { style.fill: "#56B4E9"; style.stroke: "#1f6f9c"; style.font-color: "#111111" } + ext: { style.fill: "#009E73"; style.stroke: "#00674c"; style.font-color: "#ffffff"; shape: oval } + iface: { style.fill: "#0072B2"; style.stroke: "#013a5e"; style.font-color: "#ffffff" } + gate: { style.fill: "#F0E442"; style.stroke: "#9a8f00"; style.font-color: "#111111"; shape: diamond } + human: { style.fill: "#CC79A7"; style.font-color: "#111111"; shape: person } +} + +title: "prd-taskmaster GENERATE engine · backend.NativeBackend -> resolve_provider tier -> cli | api | plan" { near: top-center; shape: text; style.font-size: 18 } + +engine: "Generation Engine (backend.py)" { + backend: "NativeBackend · parse_prd · expand · rate" { class: code } + fanout: "ThreadPoolExecutor fan-out · _expand_packet per packet" { class: code } + backend -> fanout: "expand() submits packets" +} + +resolver: "provider_resolver.py" { + resolve: "resolve_provider(main) · single tier decision · cli | api | plan" { class: gate } +} + +economy: "economy.py · tier ladder + cost ledger (wraps every call)" { + profile: "economy_profile · structured_gen_start tier" { class: code } + shift: "shift_tier · escalation step on invalid_json" { class: code } + telemetry: "append_telemetry · telemetry.jsonl cost ledger" { class: code } +} + +cli_path: "CLI tier · keyless (host session auth)" { + cli_gen: "cli_agent.generate_json_via_cli" { class: code } + cli_model: "external model CLI · claude · codex · gemini" { class: ext } + cli_gen -> cli_model: "subprocess spawn · no API key" +} + +api_path: "API tier · raw key (urllib)" { + api_gen: "llm_client.generate_json" { class: code } + vendor: "vendor API · anthropic · openai · google" { class: ext } + api_gen -> vendor: "HTTP POST · x-api-key" +} + +plan_path: "Plan floor · agent_action_required" { + action: "returns agent_action_required packet" { class: iface } + insession: "in-session LLM · decomposes by hand" { class: llm } + action -> insession: "hand-off · NATIVE_PARSE_STEPS" +} + +# economy wraps every generation call (tier in, telemetry out) +engine.backend -> economy.profile: "economy_profile(config) sets start tier" +economy.profile -> resolver.resolve: "start tier" + +# the single tier decision, made per role at gen time +engine.backend -> resolver.resolve: "resolve_provider(main)" +engine.fanout -> resolver.resolve: "per-packet handle.kind" + +# THREE branches out of the gate +resolver.resolve -> cli_path.cli_gen: "kind == cli" +resolver.resolve -> api_path.api_gen: "kind == api" +resolver.resolve -> plan_path.action: "kind == plan (floor)" + +# economy wrapping: every call emits telemetry; api invalid_json triggers shift_tier escalation +cli_path.cli_gen -> economy.telemetry: "append_telemetry (native-cli)" +api_path.api_gen -> economy.telemetry: "append_telemetry (native-api)" +api_path.api_gen -> economy.shift: "invalid_json · escalate one tier" +economy.shift -> api_path.api_gen: "retry at higher tier (ceiling clamp)" + diff --git a/docs/architecture/src/12-component-detect-setup-mode.d2 b/docs/architecture/src/12-component-detect-setup-mode.d2 new file mode 100644 index 0000000..7baa785 --- /dev/null +++ b/docs/architecture/src/12-component-detect-setup-mode.d2 @@ -0,0 +1,104 @@ +direction: right +classes: { + llm: { style.fill: "#E69F00"; style.stroke: "#8a6100"; style.font-color: "#111111"; shape: hexagon } + code: { style.fill: "#56B4E9"; style.stroke: "#1f6f9c"; style.font-color: "#111111" } + ext: { style.fill: "#009E73"; style.stroke: "#00674c"; style.font-color: "#ffffff"; shape: oval } + iface: { style.fill: "#0072B2"; style.stroke: "#013a5e"; style.font-color: "#ffffff" } + gate: { style.fill: "#F0E442"; style.stroke: "#9a8f00"; style.font-color: "#111111"; shape: diamond } + human: { style.fill: "#CC79A7"; style.font-color: "#111111"; shape: person } +} + +title: "Detect · Setup · Mode-recommend (Phase 0/1 environment detection)" { near: top-center; shape: text; style.font-size: 22 } + +# ── Orchestrator entry ─────────────────────────────────────────────── +orch: "batch.py · orchestrator" { + engine_pf: "run_engine_preflight(configure) · one-call Phase-1" { class: code } + bblock: "_backend_block · _backend_summary · build summary" { class: code } +} + +# ── Environment / project detection ────────────────────────────────── +detect: "preflight.py · environment detection" { + preflight: "run_preflight · .taskmaster · PRD · task counts · crash state" { class: code } + detect_tm: "run_detect_taskmaster · MCP then CLI then none" { class: code } +} + +# ── mode_recommend.py — the MCP-wired core ─────────────────────────── +moderec: "mode_recommend.py · MCP-wired detect+validate core" { + mr_validate: "validate_setup(provider_mode) · 6 SETUP checks" { class: code } + mr_caps: "detect_capabilities · exec modes A-J · tier" { class: code } + mr_tm: "detect_taskmaster · detect_atlas_launcher" { class: code } +} + +# ── capabilities.py — the batch-wired capability scan ──────────────── +caps: "capabilities.py · batch-wired capability scan" { + run_caps: "run_detect_capabilities · scan skills+plugins · recommend mode" { class: code } +} + +# ── providers.py — provider config + detect ────────────────────────── +prov: "providers.py · provider resolution" { + configure: "run_configure_providers · repair-on-detect" { class: code } + detect_prov: "run_detect_providers · main · fallback · research" { class: code } + usable: "_provider_usable · _probe_spawn · nested-claude check" { class: code } +} + +# ── setup_wizard.py — atlas setup CLI ──────────────────────────────── +wizard: "setup_wizard.py · atlas setup wizard" { + run_setup: "run_setup · detect+recommend · accept · customise · add-key" { class: code } + recommend: "_recommend · _panel · per-role recommendation" { class: code } + validate_step: "_run_validate_step · validate_setup plus live probe" { class: code } + live_probe: "_live_probe · one-token liveness probe" { class: code } +} + +# ── External model CLIs (touched by live probes) ───────────────────── +clis: "External model CLIs (PATH)" { + claude_cli: "claude -p ok" { class: ext } + codex_cli: "codex --version" { class: ext } + gemini_cli: "gemini --version" { class: ext } +} + +# ── MCP / CLI surfaces ─────────────────────────────────────────────── +surfaces: "MCP · CLI surfaces" { + mcp_engine: "MCP engine_preflight" { class: iface } + mcp_validate: "MCP validate_setup" { class: iface } + mcp_caps: "MCP detect_capabilities" { class: iface } + cli_setup: "CLI atlas setup" { class: iface } +} + +# ── Surface wiring ─────────────────────────────────────────────────── +surfaces.mcp_engine -> orch.engine_pf: "delegates" +surfaces.mcp_validate -> moderec.mr_validate: "wires mode_recommend.validate_setup" +surfaces.mcp_caps -> moderec.mr_caps: "wires mode_recommend.detect_capabilities" +surfaces.cli_setup -> wizard.run_setup: "cmd_setup" + +# ── Orchestrator fan-out (batch calls the others) ──────────────────── +orch.engine_pf -> detect.preflight: "1 · probe env" +orch.engine_pf -> detect.detect_tm: "2 · probe taskmaster" +orch.engine_pf -> prov.configure: "3 · if has_taskmaster" +orch.engine_pf -> prov.detect_prov: "4 · resolve providers" +orch.engine_pf -> caps.run_caps: "5 · scan capabilities" +orch.engine_pf -> orch.bblock: "6 · build summary" + +# ── capabilities.py uses mode_recommend helpers ────────────────────── +caps.run_caps -> moderec.mr_tm: "detect_atlas_launcher · ATLAS_FLEET_REASON" +caps.run_caps -> detect.detect_tm: "_detect_taskmaster_method" + +# ── mode_recommend internal + provider reachability ────────────────── +moderec.mr_validate -> prov.usable: "reachability per check 5/6" +moderec.mr_caps -> moderec.mr_tm: "tier · recommended_mode" + +# ── setup_wizard internal wiring ───────────────────────────────────── +wizard.run_setup -> wizard.recommend: "build panel" +wizard.run_setup -> prov.configure: "accept · customise" +wizard.run_setup -> wizard.validate_step: "validate" +wizard.recommend -> prov.detect_prov: "detected providers" +wizard.recommend -> moderec.mr_caps: "detect_capabilities · tier" +wizard.validate_step -> moderec.mr_validate: "credential-aware checks" +wizard.validate_step -> wizard.live_probe: "per chosen provider" + +# ── live probes touch external CLIs ────────────────────────────────── +wizard.live_probe -> clis.claude_cli: "spawn" +wizard.live_probe -> clis.codex_cli: "spawn" +wizard.live_probe -> clis.gemini_cli: "spawn" +prov.usable -> clis.claude_cli: "_probe_spawn" +prov.usable -> clis.codex_cli: "_probe_spawn" + diff --git a/docs/architecture/src/40-runflow-script-vs-llm.d2 b/docs/architecture/src/40-runflow-script-vs-llm.d2 new file mode 100644 index 0000000..7fafd8a --- /dev/null +++ b/docs/architecture/src/40-runflow-script-vs-llm.d2 @@ -0,0 +1,117 @@ +direction: right +classes: { + llm: { style.fill: "#E69F00"; style.stroke: "#8a6100"; style.font-color: "#111111"; shape: hexagon } + code: { style.fill: "#56B4E9"; style.stroke: "#1f6f9c"; style.font-color: "#111111" } + ext: { style.fill: "#009E73"; style.stroke: "#00674c"; style.font-color: "#ffffff"; shape: oval } + iface: { style.fill: "#0072B2"; style.stroke: "#013a5e"; style.font-color: "#ffffff" } + gate: { style.fill: "#F0E442"; style.stroke: "#9a8f00"; style.font-color: "#111111"; shape: diamond } + human: { style.fill: "#CC79A7"; style.font-color: "#111111"; shape: person } +} + +title: "/atlas run · where determinism starts and stops · hexagon=LLM blue=script diamond=gate" { near: top-center; shape: text; style.font-size: 22 } + +# ── Entry: human goal + LLM router ────────────────────────────── +user: "User · 'I want to build ...'" { class: human } + +router: "skills/go · orchestrator" { + go: "go SKILL · pure routing · reads current_phase · dispatches" { class: llm } + pf: "preflight() · current_phase() · MCP server.py" { class: code } +} + +# ── Phase 0: SETUP (mostly code) ──────────────────────────────── +setup: "Phase 0 · SETUP (mostly script)" { + init: "script.py init-project · scaffold .taskmaster · .atlas-ai" { class: code } + cfg: "script.py backend-detect · resolve_provider · setup wizard" { class: code } + vs: "validate_setup() · probe pipeline wired" { class: code } +} + +# ── Phase 1: DISCOVER (mostly LLM) ────────────────────────────── +discover: "Phase 1 · DISCOVER (mostly LLM)" { + bs: "superpowers:brainstorming · adaptive Q+A · constraints · scale" { class: llm } + approve: "User approval gate · AskUserQuestion" { class: human } +} + +# ── Phase 2: GENERATE (hybrid) ────────────────────────────────── +generate: "Phase 2 · GENERATE (hybrid)" { + tmpl: "load_template() · canonical spec shape" { class: code } + spec: "fill prd.md · LLM writes from discovery + constraints" { class: llm } + val: "validate_prd() · regex checks · grade · placeholders_found" { class: code } + parse: "parse_prd() · backend op · provider-or-CLI, else agent" { class: llm } + rate: "rate_tasks() · complexity report · provider-or-CLI" { class: llm } + expand: "expand_tasks() · subtasks · provider-or-CLI, else agent" { class: llm } +} + +# ── Phase 3: HANDOFF (split) ──────────────────────────────────── +handoff: "Phase 3 · HANDOFF (split)" { + det: "detect_capabilities() · tier · compute_fleet_waves()" { class: code } + pick: "User mode picker · AskUserQuestion · Mode A/B/C/D" { class: human } + append: "append_workflow() · idempotent CLAUDE.md write" { class: code } +} + +# ── Phase 4: EXECUTE (code-gated · LLM-executed) ──────────────── +execute: "Phase 4 · EXECUTE · skills/execute-task (40) · code-gated, LLM-executed" { + nxt: "script.py next-task · pick ready task · deterministic" { class: code } + card: "build CDD card · script.py set-status in-progress" { class: code } + impl: "dispatch implementer subagent · LLM does the work" { class: llm } + triple: "triple verify · /doubt + /validate + Opus merge-check" { class: llm } + hardgate: "ship-check.py --dry-run · HARD exit-code gate" { class: gate } + done: "script.py set-status done · subtask writeback · pipeline.json" { class: code } +} + +# ── Inter-phase gates (deterministic check_gate diamonds) ─────── +g_setup: "check_gate('SETUP') · advance_phase" { class: gate } +g_disc: "check_gate('DISCOVER') · advance_phase" { class: gate } +g_gen: "check_gate('GENERATE') · task_count + coverage + grade" { class: gate } +g_hand: "check_gate('HANDOFF') · mode + plan_file" { class: gate } + +# ── Terminal deterministic gate ───────────────────────────────── +sync: "Skill('sync') · refresh memory bank · MANDATORY pre-token" { class: llm } +ship: "ship-check.py · 5 gates · phase + all-done + CDD + plan + no-nonzero-exit" { class: gate } +ok: "SHIP_CHECK_OK · unfakable token · stdout once · TERMINAL" { class: ext } + +# ── Main left-to-right flow ───────────────────────────────────── +user -> router.go: "goal" +router.go -> router.pf: "read state" +router.pf -> setup.init: "route · null/SETUP" + +setup.init -> setup.cfg +setup.cfg -> setup.vs +setup.vs -> g_setup: "evidence" +g_setup -> discover.bs: "-> DISCOVER" + +discover.bs -> discover.approve +discover.approve -> g_disc: "approved · constraints · scale" +g_disc -> generate.tmpl: "-> GENERATE" + +generate.tmpl -> generate.spec +generate.spec -> generate.val: "prd.md" +generate.val -> generate.parse: "grade ok · 0 placeholders" +generate.parse -> generate.rate: "tasks.json" +generate.rate -> generate.expand: "complexity report" +generate.expand -> g_gen: "subtask coverage 1.0" +g_gen -> handoff.det: "-> HANDOFF" + +handoff.det -> handoff.pick +handoff.pick -> handoff.append: "Mode A/B/C/D" +handoff.append -> g_hand: "mode + plan" +g_hand -> execute.nxt: "-> EXECUTE" + +execute.nxt -> execute.card +execute.card -> execute.impl +execute.impl -> execute.triple: "subagent DONE" +execute.triple -> execute.hardgate +execute.hardgate -> execute.done: "exit-code clean" + +execute.done -> execute.nxt: "loop · next task" +execute.nxt -> sync: "all tasks done" +sync -> ship: "memory refreshed" +ship -> ok: "5 gates pass · exit 0" + +# ── Dashed bounce-backs (agent_action_required · regenerate) ──── +generate.parse -> generate.spec: "agent_action_required · regen" { style.stroke-dash: 4; style.stroke: "#8a6100" } +generate.val -> generate.spec: "placeholders_found > 0 · fix spec" { style.stroke-dash: 4; style.stroke: "#1f6f9c" } +generate.expand -> generate.expand: "agent fallback · idempotent re-expand" { style.stroke-dash: 4; style.stroke: "#8a6100" } +execute.triple -> execute.impl: "disagree · re-dispatch" { style.stroke-dash: 4; style.stroke: "#8a6100" } +execute.hardgate -> execute.impl: "SHIP_CHECK_FAIL · task-fix-N" { style.stroke-dash: 4; style.stroke: "#9a8f00" } +handoff.pick -> handoff.pick: "Mode D locked · re-prompt free" { style.stroke-dash: 4; style.stroke: "#013a5e" } + diff --git a/docs/architecture/src/50-swimlane-setup.mmd b/docs/architecture/src/50-swimlane-setup.mmd new file mode 100644 index 0000000..49f4fcb --- /dev/null +++ b/docs/architecture/src/50-swimlane-setup.mmd @@ -0,0 +1,48 @@ +%% SETUP phase — script vs LLM swimlane. +%% Okabe-Ito colorblind-safe palette + shape redundancy: +%% hexagon = LLM / agent step (output varies run-to-run) +%% rectangle = deterministic engine code +%% stadium = external model execution +%% diamond = deterministic gate +%% Solid edge = deterministic control flow. Dashed edge = LLM-routed / bounce-back. +flowchart LR + classDef llm fill:#E69F00,stroke:#8a6100,color:#111111; + classDef code fill:#56B4E9,stroke:#1f6f9c,color:#111111; + classDef ext fill:#009E73,stroke:#00674c,color:#ffffff; + classDef gate fill:#F0E442,stroke:#9a8f00,color:#111111; + + subgraph LANE_LLM["LLM / agent — non-deterministic"] + direction TB + L1{{"1 · enter SETUP, run go router preflight
skills/go · skills/setup · check_gate diagnostic"}}:::llm + L2{{"4 · ship-check skel bootstrap
copy customizations + ship-check.py if absent"}}:::llm + L3{{"6 · DETECT-FIRST judgment
do not clobber a working provider config"}}:::llm + L4{{"10 · read rendered panel, announce
MCP-vs-CLI backend, then advance_phase"}}:::llm + end + + subgraph LANE_CODE["deterministic engine — Python"] + direction TB + C1["2 · batch.run_engine_preflight
preflight.run_preflight (env + taskmaster probe)"]:::code + C2["3 · backend.NativeBackend.init_project
taskmaster.init_taskmaster (vestigial file format)"]:::code + C3["5 · mode_recommend.detect_capabilities
tier = free | cli | api"]:::code + C4["7 · providers.run_configure_providers
fill empty roles only · run_detect_providers"]:::code + C5["8 · setup_wizard.run_setup (probe pipeline)
fleet.save_engine_config (persist backend)"]:::code + C6["9 · mode_recommend.validate_setup
6 checks · ready + 0 critical failures"]:::code + G1{"11 · pipeline.check_gate('SETUP')
validate_setup.ready · critical_failures == 0"}:::gate + end + + subgraph LANE_EXT["external model execution"] + direction TB + E1(["8a · setup_wizard._live_probe
claude / codex / gemini CLI · keyless liveness"]):::ext + end + + L1 --> C1 --> C2 --> L2 + L2 --> C3 --> L3 + L3 -. "all roles set — skip mutate" .-> C5 + L3 --> C4 --> C5 + C5 --> E1 + E1 --> C6 + C6 -. "not ready — reconfigure providers" .-> L3 + C6 --> L4 --> G1 + G1 -. "fail — fix provider config" .-> L3 + G1 == "pass → advance_phase(DISCOVER)" ==> DONE(["DISCOVER ▶"]):::code + diff --git a/docs/architecture/src/51-swimlane-discover.mmd b/docs/architecture/src/51-swimlane-discover.mmd new file mode 100644 index 0000000..59292e7 --- /dev/null +++ b/docs/architecture/src/51-swimlane-discover.mmd @@ -0,0 +1,39 @@ +%% DISCOVER phase — script vs LLM swimlane. +%% Okabe-Ito colorblind-safe palette + shape redundancy: +%% hexagon = LLM / agent step (output varies run-to-run) +%% rectangle = deterministic engine code +%% stadium = external model execution +%% diamond = deterministic gate +%% Solid edge = deterministic control flow. Dashed edge = LLM-routed / bounce-back. +%% DISCOVER is LLM-dominant: no external model lane. +flowchart LR + classDef llm fill:#E69F00,stroke:#8a6100,color:#111111; + classDef code fill:#56B4E9,stroke:#1f6f9c,color:#111111; + classDef ext fill:#009E73,stroke:#00674c,color:#ffffff; + classDef gate fill:#F0E442,stroke:#9a8f00,color:#111111; + + subgraph LANE_LLM["LLM / agent — non-deterministic"] + direction TB + L1{{"1 · capture goal from skill args
detect Interactive vs Autonomous mode
phases/DISCOVER.md · skills/discover"}}:::llm + L2{{"3 · superpowers:brainstorming
adaptive one-question-at-a-time Q and A"}}:::llm + L3{{"4 · INTERCEPT before writing-plans
capture design / requirements / decisions"}}:::llm + L4{{"5 · extract constraints (CONSTRAINTS CAPTURED)
calibrate scale: Solo / Team / Enterprise"}}:::llm + L5{{"6 · AskUserQuestion approval gate
present discovery summary"}}:::llm + L6{{"3a · Autonomous self-brainstorm
write discovery notes · document assumptions"}}:::llm + end + + subgraph LANE_CODE["deterministic engine — Python"] + direction TB + C1["2 · pipeline.check_gate('DISCOVER')
entry diagnostic (exit gate, expect false)"]:::code + G1{"7 · pipeline.check_gate('DISCOVER')
user_approved OR auto_classification==CLEAR
+ assumptions_documented"}:::gate + end + + L1 --> C1 --> L2 + L1 -. "Autonomous mode (no user)" .-> L6 + L2 --> L3 --> L4 --> L5 + L6 -. "self-approve · assumptions documented" .-> L4 + L5 -. "refine — re-ask, update summary" .-> L2 + L5 --> G1 + G1 -. "fail — gather missing evidence" .-> L4 + G1 == "pass → advance_phase(GENERATE)" ==> DONE(["GENERATE ▶"]):::code + diff --git a/docs/architecture/src/52-swimlane-generate.mmd b/docs/architecture/src/52-swimlane-generate.mmd new file mode 100644 index 0000000..2f522c9 --- /dev/null +++ b/docs/architecture/src/52-swimlane-generate.mmd @@ -0,0 +1,49 @@ +%% GENERATE phase — script vs LLM swimlane. +%% Okabe-Ito colorblind-safe palette + shape redundancy: +%% hexagon = LLM / agent step (output varies run-to-run) +%% rectangle = deterministic engine code +%% stadium = external model execution +%% diamond = deterministic gate +%% Solid edge = deterministic control flow. Dashed edge = LLM-routed / bounce-back. +flowchart LR + classDef llm fill:#E69F00,stroke:#8a6100,color:#111111; + classDef code fill:#56B4E9,stroke:#1f6f9c,color:#111111; + classDef ext fill:#009E73,stroke:#00674c,color:#ffffff; + classDef gate fill:#F0E442,stroke:#9a8f00,color:#111111; + + subgraph LANE_LLM["LLM / agent — non-deterministic"] + direction TB + L1{{"1 · write PRD prose, replace every placeholder
phases/GENERATE.md · skills/generate"}}:::llm + L2{{"6c · plan-floor: decompose to schema by hand
(when engine returns agent_action_required)"}}:::llm + end + + subgraph LANE_CODE["deterministic engine — Python"] + direction TB + C1["2 · templates.run_load_template"]:::code + C2["3 · validation.run_validate_prd
13 checks + placeholder HARD FAIL"]:::code + C3["4 · tasks.run_calc_tasks (count formula)"]:::code + C4["5 · provider_resolver.resolve_provider
tier = cli | api | plan"]:::code + C5["7 · backend.NativeBackend.parse_prd / expand
economy.shift_tier (escalate) · append_telemetry"]:::code + C6["8 · parallel.apply_results (atomic merge)
tasks.run_enrich_tasks (complexity)"]:::code + C7["9 · validation.run_validate_tasks"]:::code + G1{"10 · pipeline.check_gate('GENERATE')
grade ≥ GOOD · tasks>0 · 100% subtasks"}:::gate + end + + subgraph LANE_EXT["external model execution"] + direction TB + E1(["6a · cli_agent.generate_json_via_cli
claude / codex / gemini CLI · keyless"]):::ext + E2(["6b · llm_client.generate_json
raw vendor API"]):::ext + end + + L1 --> C1 --> C2 + C2 -. "placeholders / low grade — regenerate" .-> L1 + C2 --> C3 --> C4 + C4 -- "cli tier" --> E1 + C4 -- "api tier" --> E2 + C4 -. "plan tier (no key + no CLI)" .-> L2 + E1 --> C5 + E2 --> C5 + L2 -. "hand-built task JSON" .-> C5 + C5 --> C6 --> C7 --> G1 + G1 -. "fail — regenerate" .-> L1 + G1 == "pass → advance_phase(HANDOFF)" ==> DONE(["HANDOFF ▶"]):::code diff --git a/docs/architecture/src/53-swimlane-handoff.mmd b/docs/architecture/src/53-swimlane-handoff.mmd new file mode 100644 index 0000000..092c2b6 --- /dev/null +++ b/docs/architecture/src/53-swimlane-handoff.mmd @@ -0,0 +1,34 @@ +flowchart LR + classDef llm fill:#E69F00,stroke:#8a6100,color:#111111; + classDef code fill:#56B4E9,stroke:#1f6f9c,color:#111111; + classDef ext fill:#009E73,stroke:#00674c,color:#ffffff; + classDef gate fill:#F0E442,stroke:#9a8f00,color:#111111; + + subgraph LANE_LLM["LLM / agent — non-deterministic"] + direction TB + L1{{"1 · phases/HANDOFF.md via skills/handoff ·
enter HANDOFF, copy checklist"}}:::llm + L7{{"7 · recommend ONE mode ·
M / D / C / A / B with reason"}}:::llm + L8{{"8 · write Task Execution Workflow
block into CLAUDE.md"}}:::llm + L9{{"9 · AskUserQuestion ·
structured mode picker"}}:::llm + L11{{"11 · dispatch chosen mode ·
writing-plans / ralph-loop / atlas-loop"}}:::llm + end + + subgraph LANE_CODE["deterministic engine — Python"] + direction TB + C2["2 · mode_recommend.detect_capabilities ·
tier premium/free gating of Mode D / Atlas Fleet"]:::code + C3["3 · fleet.compute_waves ·
topological waves plus deadlock detect"]:::code + C4["4 · fleet.route_task plus resolve_backend plus task_tier ·
per-task backend routing"]:::code + C5["5 · render.handoff_panel ·
spec, task count, capabilities summary"]:::code + C6["6 · economy.summarize_telemetry ·
cost/usage ledger"]:::code + C10["10 · fleet.run_fleet_waves ·
wave scheduling JSON (premium dispatch)"]:::code + G12{"12 · pipeline.check_gate('HANDOFF') ·
user_mode_choice plus plan_file_exists"}:::gate + end + + L1 --> C2 --> C3 --> C4 --> C5 --> C6 --> L7 + L7 --> L8 --> L9 + L9 -. "premium · parallel graph" .-> C10 + C10 --> L11 + L9 -. "free / serial · pick free mode" .-> L11 + L11 --> G12 + G12 == "pass → advance_phase(EXECUTE)" ==> DONE(["EXECUTE ▶"]):::code + diff --git a/docs/architecture/src/54-swimlane-execute.mmd b/docs/architecture/src/54-swimlane-execute.mmd new file mode 100644 index 0000000..1be12a9 --- /dev/null +++ b/docs/architecture/src/54-swimlane-execute.mmd @@ -0,0 +1,31 @@ +flowchart LR + classDef llm fill:#E69F00,stroke:#8a6100,color:#111111; + classDef code fill:#56B4E9,stroke:#1f6f9c,color:#111111; + classDef ext fill:#009E73,stroke:#00674c,color:#ffffff; + classDef gate fill:#F0E442,stroke:#9a8f00,color:#111111; + subgraph LANE_LLM["LLM / agent — non-deterministic"] + direction TB + L1{{"1 · skills/execute-task solo CDD loop
OR skills/execute-fleet parallel workers"}}:::llm + L2{{"4 · implement task code
CDD RED → GREEN → BLUE"}}:::llm + L3{{"5 · judge GREEN/RED/BLUE evidence
write CDD card + evidence files"}}:::llm + end + subgraph LANE_CODE["deterministic engine — Python"] + direction TB + C1["2 · fleet.ready_set / fleet.compute_waves
order work into ready set / waves"]:::code + C2["3 · task_state.run_claim_task · _select_next_task
claim next under lib.locked_update flock → in-progress"]:::code + C3["6 · task_state.run_set_status
mark task/subtask done"]:::code + C4["7 · shipcheck.gate_pipeline / gate_tasks / gate_cdd / gate_plan"]:::code + C5["8 · shipcheck.gate_exit_codes
EXIT_STATUS_RE over .atlas-ai/evidence/** · anti-fake"]:::code + G1{"9 · shipcheck.run_all_gates
all gates pass?"}:::gate + C6["10 · render.execute_panel / shipcheck_panel
feedback.append_feedback · suggestions.append_suggestion"]:::code + end + L1 --> C1 --> C2 + C2 -. "claimed task JSON" .-> L2 + L2 --> L3 + L3 -. "claim/set-status calls" .-> C3 + C3 --> C4 --> C5 --> G1 + G1 -. "fail → bounce back, fix task" .-> L2 + G1 == "pass → print SHIP_CHECK_OK" ==> C6 + C6 == "loop while tasks remain" ==> C1 + C6 == "all done → SHIP_CHECK_OK + final PR" ==> DONE(["COMPLETE ▶"]):::code + diff --git a/docs/architecture/src/60-sequence-generate-parse-prd.mmd b/docs/architecture/src/60-sequence-generate-parse-prd.mmd new file mode 100644 index 0000000..407f596 --- /dev/null +++ b/docs/architecture/src/60-sequence-generate-parse-prd.mmd @@ -0,0 +1,66 @@ +%% prd-taskmaster engine: ONE parse-PRD operation across the three provider tiers. +%% Source: prd_taskmaster/{cli.py,backend.py,provider_resolver.py,cli_agent.py,llm_client.py,validation.py} +sequenceDiagram + autonumber + actor LLM as In-session LLM · GENERATE.md + participant IF as Interface · cli.run_parse_prd + participant BE as NativeBackend.parse_prd + participant RES as provider_resolver.resolve_provider + participant CLIA as cli_agent.generate_json_via_cli + participant EXT as External CLI · claude/codex/gemini + participant API as llm_client.generate_json + participant VEND as Vendor API + participant VAL as validation.run_validate_tasks + participant STORE as tasks.json write · _write_tasks_into_tag + participant TEL as economy.append_telemetry + + LLM->>IF: parse_prd · prd_path, num_tasks, tag + IF->>BE: run_parse_prd + BE->>RES: resolve_provider main + Note over RES: role config + CLI/key presence + cached spawn probe
no spawn, no network here + RES-->>BE: ProviderHandle · kind = cli | api | plan + + alt Tier 1 · kind=cli · keyless CLI-agent + BE->>CLIA: generate_json_via_cli · prompt + schema_hint + CLIA->>EXT: subprocess.run · argv, stdin, timeout + EXT-->>CLIA: stdout JSON envelope + CLIA->>TEL: append_telemetry · native-cli, exit, wall_ms + Note over CLIA: one parse-retry on bad JSON
CliAgentError on no_cli / timeout / nonzero / invalid_json + alt CLI succeeds + CLIA-->>BE: candidate tasks JSON · ai=cli + else CliAgentError + CLIA-->>BE: raise CliAgentError + BE-->>IF: ok=false · agent_action_required + IF-->>LLM: LLM decomposes by hand + end + else Tier 2 · kind=api · raw-key vendor API + BE->>API: generate_json · prompt + schema_hint + tier + API->>VEND: HTTP POST · messages / chat / generateContent + VEND-->>API: response JSON · content + usage + API->>TEL: append_telemetry · native-api, http_status, tokens + Note over API: one parse-retry · one retry on 429/5xx
401/403 fail fast · LLMError kind + alt API succeeds + API-->>BE: candidate + telemetry_ref · ai=api + else LLMError no_key + API-->>BE: raise LLMError no_key + BE-->>IF: ok=false · agent_action_required + IF-->>LLM: LLM decomposes by hand + else LLMError other + API-->>BE: raise LLMError kind + BE-->>IF: ok=false · error, kind + end + else Tier 3 · kind=plan · plan-floor + Note over BE,LLM: engine does NOT read the PRD or generate — it returns the plan-floor + BE-->>IF: ok=false · agent_action_required = _agent_parse_action + IF-->>LLM: agent_action_required · schema_hint + NATIVE_PARSE_STEPS + Note over LLM: in-session LLM decomposes the PRD by hand,
writes Native-shape tasks.json, then runs validate-tasks + end + + opt candidate generated · cli or api tier + BE->>VAL: run_validate_tasks · allow_empty_subtasks=false + VAL-->>BE: ok · or raise CommandError + BE->>STORE: write tasks atomically into tag + STORE-->>BE: resolved tag + BE-->>IF: ok=true · task_count, tag, ai, validation + IF-->>LLM: parse result · tasks written + end diff --git a/prd_taskmaster/validation.py b/prd_taskmaster/validation.py index adbc95d..b9a5013 100644 --- a/prd_taskmaster/validation.py +++ b/prd_taskmaster/validation.py @@ -159,7 +159,14 @@ def run_validate_prd(input_path: str) -> dict: }) # Check 9: Technical considerations address architecture - tech_section = get_section_content(text, "Technical") + # Prefer the canonical "Technical Considerations" section. A bare substring + # match on "Technical" wrongly latches onto an earlier heading that merely + # *contains* the word (e.g. "### Goal 1: Enable non-technical editing"), + # capturing that subsection's prose instead of the real architecture text. + # Fall back to a bare "Technical" heading for PRDs that use that shorter name. + tech_section = get_section_content(text, "Technical Considerations") + if not tech_section: + tech_section = get_section_content(text, "Technical") has_arch = bool(re.search( r'(architecture|system\s+design|component|integration|diagram)', tech_section, re.IGNORECASE diff --git a/tests/core/test_validation.py b/tests/core/test_validation.py index 69d2578..a3a12ee 100644 --- a/tests/core/test_validation.py +++ b/tests/core/test_validation.py @@ -612,6 +612,51 @@ def test_lowercase_angle_token_file_not_flagged(self, tmp_path): f" in testStrategy was falsely flagged: {placeholder_probs}" ) + def test_html_jsx_tags_in_details_not_flagged(self, tmp_path): + """