bmad-module-ultracode-goal 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +20 -0
- package/.gitattributes +16 -0
- package/.nvmrc +1 -0
- package/LICENSE +27 -0
- package/README.md +200 -0
- package/docs/_internal/RELEASING.md +101 -0
- package/docs/_internal/STABILITY.md +62 -0
- package/docs/architecture.md +77 -0
- package/docs/assets/ucg-logo.svg +73 -0
- package/docs/gate-model.md +109 -0
- package/docs/getting-started.md +51 -0
- package/docs/health-check.md +61 -0
- package/docs/how-it-works.md +73 -0
- package/docs/index.md +25 -0
- package/docs/parallel-mode.md +33 -0
- package/docs/troubleshooting.md +56 -0
- package/docs/why-ultracode-goal.md +34 -0
- package/package.json +100 -0
- package/skills/.gitkeep +0 -0
- package/skills/module.yaml +12 -0
- package/skills/ultracode-goal/SKILL.md +75 -0
- package/skills/ultracode-goal/assets/execute-epic.workflow.js +208 -0
- package/skills/ultracode-goal/customize.toml +47 -0
- package/skills/ultracode-goal/references/define-done.md +41 -0
- package/skills/ultracode-goal/references/execute.md +90 -0
- package/skills/ultracode-goal/references/finalize.md +64 -0
- package/skills/ultracode-goal/references/gate.md +70 -0
- package/skills/ultracode-goal/references/health-check.md +332 -0
- package/skills/ultracode-goal/references/ingest-and-scope.md +46 -0
- package/skills/ultracode-goal/references/preflight.md +100 -0
- package/skills/ultracode-goal/scripts/gate_eval.py +280 -0
- package/skills/ultracode-goal/scripts/health_check_fp.py +196 -0
- package/skills/ultracode-goal/scripts/hooks/budget_stop.py +162 -0
- package/skills/ultracode-goal/scripts/hooks/guard_pretooluse.py +174 -0
- package/skills/ultracode-goal/scripts/preflight_check.py +399 -0
- package/tools/cli/commands/install.js +36 -0
- package/tools/cli/commands/status.js +161 -0
- package/tools/cli/commands/uninstall.js +175 -0
- package/tools/cli/commands/update.js +65 -0
- package/tools/cli/lib/ide-skills.js +218 -0
- package/tools/cli/lib/installer.js +226 -0
- package/tools/cli/lib/manifest.js +100 -0
- package/tools/cli/lib/platform-codes.yaml +223 -0
- package/tools/cli/lib/ui.js +353 -0
- package/tools/cli/lib/version-check.js +86 -0
- package/tools/cli/ucg-cli.js +45 -0
- package/tools/ucg-npx-wrapper.js +36 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Gate Model
|
|
2
|
+
|
|
3
|
+
Completion in UltraCode Goal is decided by a deterministic artifact read, not by judgment. `scripts/gate_eval.py` reads TEA's `gate-decision.json` and returns a routing verdict the skill executes. This page documents the verdict mapping, the production AND, the thresholds, the fail-closed contract, and why the `/goal` evaluator alone is insufficient — all traced to [`../skills/ultracode-goal/scripts/gate_eval.py`](../skills/ultracode-goal/scripts/gate_eval.py).
|
|
4
|
+
|
|
5
|
+
## What the gate reads
|
|
6
|
+
|
|
7
|
+
The script resolves the gate artifact from the trace output directory:
|
|
8
|
+
|
|
9
|
+
1. It looks for a trace-report markdown whose frontmatter records the slim gate file (keys `gateDecisionFile` / `gateDecisionPath` / `gate_decision_path`), defaulting to `<trace-output>/gate-decision.json`.
|
|
10
|
+
2. If that slim file is absent, it falls back to the always-written `e2e-trace-summary.json` and lifts the gate fields from it. **The slim file's absence is normal, not a failure** — TEA only writes it when the run is gate-eligible and the decision is PASS/CONCERNS/FAIL/WAIVED.
|
|
11
|
+
3. If neither file is present, or the run carries no gate fields, `gate_status` is `NOT_EVALUATED`.
|
|
12
|
+
|
|
13
|
+
The script never re-derives TEA's thresholds; it reads `gate_status` as given by the trace workflow.
|
|
14
|
+
|
|
15
|
+
## Verdict mapping
|
|
16
|
+
|
|
17
|
+
The gate status maps to a verdict (`GATE_VERDICT` in the script):
|
|
18
|
+
|
|
19
|
+
| `gate_status` | verdict | the skill does |
|
|
20
|
+
|---------------|---------|----------------|
|
|
21
|
+
| `PASS` | `advance` | story passes; move to the next story |
|
|
22
|
+
| `WAIVED` | `advance` | story passes; move to the next story |
|
|
23
|
+
| `CONCERNS` | `defer` | append non-blocking items to the ledger, then advance anyway |
|
|
24
|
+
| `FAIL` | `reloop` | run `bmad-correct-course`, re-run the story within budget |
|
|
25
|
+
| `NOT_EVALUATED` | `escalate` | stop — the gate could not be read |
|
|
26
|
+
|
|
27
|
+
Any unrecognized status escalates (the script's `GATE_VERDICT.get(gate_status, "escalate")` default), with a `reasons` entry noting it.
|
|
28
|
+
|
|
29
|
+
## The production AND
|
|
30
|
+
|
|
31
|
+
Under `--profile production`, an otherwise-`advance` verdict is additionally ANDed against two TEA signals, and any failure downgrades it to `reloop`. The downgrade floor is `reloop` — a `defer`/`reloop`/`escalate` is unchanged; only an `advance` moves:
|
|
32
|
+
|
|
33
|
+
- **NFR** (`nfr-assessment.md`): the audit's Overall Status must not be `FAIL`.
|
|
34
|
+
- **Test review** (`test-review.md`): the Quality Score must be `>= 80` **and** the Recommendation must not be `Block`.
|
|
35
|
+
|
|
36
|
+
Under `--profile light` none of this applies — the trace gate is the whole decision.
|
|
37
|
+
|
|
38
|
+
## The thresholds
|
|
39
|
+
|
|
40
|
+
The P0/P1/overall percentage thresholds — **P0 = 100%, P1 >= 90%, overall >= 80%** — are decided **upstream by the TEA trace workflow** and written into the gate artifact; `gate_eval.py` reads the resulting `gate_status`, `p0_status`, `p1_status`, and `overall_status` rather than recomputing the percentages. The script's own production AND adds the two coarser signals above (NFR != FAIL, review score >= 80 and recommendation != Block). Do not restate or recompute the TEA percentages elsewhere — they are TEA-owned, and the test-design stage's job is only to assign the P0–P3 priorities honestly so those upstream thresholds key off real priorities.
|
|
41
|
+
|
|
42
|
+
## Fail-closed contract
|
|
43
|
+
|
|
44
|
+
The production AND is deliberately fail-closed (see the `apply_production_and` docstring in the script): a missing `nfr-assessment.md` or `test-review.md`, or a field the scanners cannot parse, is treated as a **failing** signal — not a neutral or absent one. So if TEA's prose format drifts and the Overall Status or Quality Score cannot be read, an otherwise-`advance` degrades to a conservative `reloop` rather than a silent false-advance. The direction is intentional: the module would rather re-loop a green story than advance a story whose evidence it could not actually read. Likewise, a missing or corrupt gate artifact yields `NOT_EVALUATED` → `escalate` — the gate is never assumed green.
|
|
45
|
+
|
|
46
|
+
This is reinforced by the routing invariant in Stage 5: **a P0/critical FAIL never defers.** Only non-gate-blocking work (CONCERNS, non-critical findings, parked decisions) reaches the deferred-work ledger; a FAIL or a P0/critical finding re-loops within budget or escalates.
|
|
47
|
+
|
|
48
|
+
## Output shape
|
|
49
|
+
|
|
50
|
+
The script prints one JSON object (`evaluate()` in the script):
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"verdict": "advance|defer|reloop|escalate",
|
|
55
|
+
"gate_status": "PASS|CONCERNS|FAIL|WAIVED|NOT_EVALUATED",
|
|
56
|
+
"p0_status": "...",
|
|
57
|
+
"p1_status": "...",
|
|
58
|
+
"overall_status": "...",
|
|
59
|
+
"nfr_status": "...",
|
|
60
|
+
"review_score": 0,
|
|
61
|
+
"reasons": ["..."]
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Example — a clean production advance
|
|
66
|
+
|
|
67
|
+
A story whose slim gate file reads `PASS`, with an NFR audit of `PASS` and a test review scoring 92 with an Approve recommendation, produces:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"verdict": "advance",
|
|
72
|
+
"gate_status": "PASS",
|
|
73
|
+
"p0_status": "PASS",
|
|
74
|
+
"p1_status": "PASS",
|
|
75
|
+
"overall_status": "PASS",
|
|
76
|
+
"nfr_status": "PASS",
|
|
77
|
+
"review_score": 92,
|
|
78
|
+
"reasons": [
|
|
79
|
+
"gate read from gate-decision.json",
|
|
80
|
+
"gate_status PASS -> advance"
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Example — a production downgrade
|
|
86
|
+
|
|
87
|
+
The same `PASS` gate, but with a test review scoring 74, downgrades to `reloop` — the gate passed, but a production signal failed:
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"verdict": "reloop",
|
|
92
|
+
"gate_status": "PASS",
|
|
93
|
+
"p0_status": "PASS",
|
|
94
|
+
"p1_status": "PASS",
|
|
95
|
+
"overall_status": "PASS",
|
|
96
|
+
"nfr_status": "PASS",
|
|
97
|
+
"review_score": 74,
|
|
98
|
+
"reasons": [
|
|
99
|
+
"gate read from gate-decision.json",
|
|
100
|
+
"gate_status PASS -> advance",
|
|
101
|
+
"test-review score 74 < 80",
|
|
102
|
+
"production signal failed; advance downgraded to reloop"
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Why the `/goal` evaluator alone is insufficient
|
|
108
|
+
|
|
109
|
+
The `/goal` loop that drives Execute ends with an evaluator confirming the success condition — but that evaluator only sees the transcript. It cannot open `gate-decision.json`. So it can confirm "the tests I was shown printed green" but not "TEA's deterministic gate read PASS against the traceability matrix and the NFR thresholds." Letting it be the completion authority would let the run grade itself from its own notes. `gate_eval.py` reads the file the model cannot author, which is exactly why it — and not the transcript evaluator — decides. See [why](why-ultracode-goal.md) and the routing detail in [`references/gate.md`](../skills/ultracode-goal/references/gate.md).
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
Install UltraCode Goal into a BMAD project, point it at an Epic, and let it run that Epic to a gate-passed Definition-of-Done. This page covers prerequisites, install, the first-run walkthrough, and the run-mode flags.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
UltraCode Goal conducts BMAD and TEA skills and runs deterministic Python under `uv`. You need:
|
|
8
|
+
|
|
9
|
+
| Tool | Required for | Install |
|
|
10
|
+
|------|--------------|---------|
|
|
11
|
+
| Claude Code | **The runtime — non-negotiable.** UCG composes `/goal`, Auto Mode, Auto Memory, and runtime hooks, which only exist in Claude Code; the autonomous run cannot execute anywhere else | <https://www.anthropic.com/claude-code> |
|
|
12
|
+
| Node.js >= 22 | Installation, `npx` commands | <https://nodejs.org> |
|
|
13
|
+
| Python >= 3.10 | The deterministic gate, preflight, and hook scripts (run via `uv`) | <https://www.python.org> |
|
|
14
|
+
| `uv` | Running the module's Python scripts with automatic dependency management | <https://docs.astral.sh/uv/> |
|
|
15
|
+
| `git` | Epic-branch isolation and per-story commits (the real rollback) | <https://git-scm.com> |
|
|
16
|
+
| `gh` (GitHub CLI) | Submitting or queuing [health-check](health-check.md) findings | <https://cli.github.com> |
|
|
17
|
+
| A BMAD project with an Epic | The unit of delivery — a `_bmad/` install, a `sprint-status.yaml`, and at least one Epic with stories | see [bmad-method.org](https://docs.bmad-method.org) |
|
|
18
|
+
|
|
19
|
+
The run also depends on recent Claude Code primitives: `/goal`, dynamic workflows, and Auto Memory. The preflight script version-gates these and reports a mechanical blocker if the installed Claude Code is below the minimum any of them needs (see [troubleshooting](troubleshooting.md)).
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx bmad-module-ultracode-goal install
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The installer is interactive — it prompts for the project name and which IDEs to configure, then copies the skill into place. As an alternative, the module can be installed from the plugin marketplace entry (`.claude-plugin/marketplace.json`) the same way as other BMAD plugins.
|
|
28
|
+
|
|
29
|
+
## First run
|
|
30
|
+
|
|
31
|
+
Invoke the skill with one of its trigger phrases — "run an epic autonomously", "execute this epic", "ultracode goal", or "autonomously deliver the epic" — in a BMAD project.
|
|
32
|
+
|
|
33
|
+
1. **Name the Epic.** Stage 1 opens the floor: name the Epic, or drop any context (a story id, a branch, a paste of the Epic body). The skill fills the gaps from the BMAD artifacts. If `_bmad/` config, `sprint-status.yaml`, and any Epic are *all* absent, this is not a BMAD project — the skill says so and stops, pointing you at `bmad-bmb-setup` and `bmad-sprint-planning`.
|
|
34
|
+
2. **Preflight runs.** Stage 2 is the autonomy gate. It runs a mechanical check (`preflight_check.py`), auto-remediates the fixable ambers (scaffolding the test framework, generating missing acceptance criteria, pre-creating TEA output dirs, and so on), then adds a semantic scan for undecided product or architecture decisions the script cannot see. The run launches **only** when the post-remediation intervention budget is zero and the semantic scan found no red blocker. A single undecided architecture decision stops the run here rather than letting an unattended run guess it.
|
|
35
|
+
3. **The launch briefing.** On an attended run, before the first unattended action the skill prints a one-screen briefing: what is about to run, the worst-case turn envelope, the autonomy line ("from here I will not ask you anything"), the kill switch (Ctrl-C, or delete the Epic branch — `/rewind` will not help), and where to watch (the run's `.decision-log.md` and `run-status.json`). One soft confirm crosses the line.
|
|
36
|
+
|
|
37
|
+
From there the run is autonomous: it defines done with TEA, executes each story to a green commit, gates each one deterministically, and finalizes with a run report and the deferred-work ledger. See [how it works](how-it-works.md) for the full stage-by-stage narration.
|
|
38
|
+
|
|
39
|
+
## Run-mode flags
|
|
40
|
+
|
|
41
|
+
| Flag | Effect |
|
|
42
|
+
|------|--------|
|
|
43
|
+
| `--light` | Trace-only gate. Downscopes from the full TEA chain to `bmad-testarch-trace` plus `gate_eval.py --profile light` — no NFR/test-review AND. |
|
|
44
|
+
| `--parallel` | Experimental worktree fan-out. Each story runs isolated in its own worktree; no mid-run input. The sequential `/goal` spine is the default and recommended path — see [parallel mode](parallel-mode.md). |
|
|
45
|
+
| `--yes` | Skips Stage 1's open-floor invite and the launch confirm. The launch briefing still prints. **Never** skips the hard preflight gate. |
|
|
46
|
+
| `-H` | Headless. Runs non-interactively, never prompts (an unresolvable secret becomes a red blocker, not a question), and emits one JSON object at every exit point. |
|
|
47
|
+
| `--retro` | Runs the close-out retrospective (`bmad-retrospective`). Interactive runs offer it at Epic close anyway; headless runs it only when `--retro` is passed. |
|
|
48
|
+
|
|
49
|
+
## Hook security
|
|
50
|
+
|
|
51
|
+
At preflight the skill auto-merges its **PreToolUse** guard and **Stop** budget hook into `.claude/settings.local.json` — a machine-local, gitignored file, honored after the workspace trust dialog. These hooks are the enforcement layer that blocks a commit on a protected branch and bounds a runaway story; they are not shared into the repo. Because they execute on your machine, review what gets merged: see [SECURITY.md](../SECURITY.md) for the hook-security model and what to check before granting trust.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Health Check
|
|
2
|
+
|
|
3
|
+
Every UltraCode Goal run that reaches Finalize ends with a health check: a brief self-improvement reflection that audits the run for friction, gaps, or bugs in *this module* and, when it finds something, offers to file a structured GitHub issue. The expected outcome is **zero findings** — a clean run exits in a line. This page covers exactly what it sends, the privacy model, how it dedups, and how to turn it off. The deterministic fingerprint and seen-cache plumbing is [`../skills/ultracode-goal/scripts/health_check_fp.py`](../skills/ultracode-goal/scripts/health_check_fp.py).
|
|
4
|
+
|
|
5
|
+
## What it is, and when it runs
|
|
6
|
+
|
|
7
|
+
The health check is a reflection step that runs **after every run that reaches Finalize** — a completed Epic, or one that ended in a story escalation. It does **not** run on an early block: a Stage 1 or Stage 2 stop never executed any of the module's run machinery, so there is nothing to audit. Findings are graded into three severities: `bug`, `friction`, and `gap`.
|
|
8
|
+
|
|
9
|
+
## Exactly what gets sent
|
|
10
|
+
|
|
11
|
+
A finding is a structured issue with a fixed set of fields plus an Environment table. The Environment table carries:
|
|
12
|
+
|
|
13
|
+
- Date
|
|
14
|
+
- OS
|
|
15
|
+
- AI Editor
|
|
16
|
+
- Model
|
|
17
|
+
- Profile (production / light)
|
|
18
|
+
- Run mode (attended / headless)
|
|
19
|
+
- Module Version
|
|
20
|
+
|
|
21
|
+
Explicitly **not** sent: no source code, no Epic content, no secrets. The evidence in a finding is `file:line` citations into **this module's own files** — the skill, its reference stages, and its scripts — never your project's code. A finding is a claim that *the module* could be better, backed by a pointer to the module file that proves it.
|
|
22
|
+
|
|
23
|
+
## Privacy
|
|
24
|
+
|
|
25
|
+
Issues are filed publicly on `armelhbobdad/bmad-module-ultracode-goal`. On an **attended** run nothing is sent silently: the health check always **HALTS at a `[Y]` / `[N]` / `[E]` gate** before submitting — yes to file, no to skip, edit to adjust first. You see the finding and approve it before it leaves your machine.
|
|
26
|
+
|
|
27
|
+
## Unattended behavior
|
|
28
|
+
|
|
29
|
+
Headless runs do not have a human at the gate, so by default they **queue findings locally and never live-submit**. Two config knobs (set in `{project-root}/_bmad/custom/ultracode-goal.toml`) change this:
|
|
30
|
+
|
|
31
|
+
- `health_check_enabled = false` — disables the health check entirely.
|
|
32
|
+
- `health_check_autosubmit = true` — opts **bug-severity** findings into live submission on unattended runs. `friction` and `gap` findings are **never** auto-submitted; they always queue regardless of this setting.
|
|
33
|
+
|
|
34
|
+
The local queue lives at the configured `health_check_queue_path` (by default `{project-root}/_bmad-output/ultracode-goal/improvement-queue/`); each finding is written one file per finding, named `hc-ultracode-goal-{stage}-{YYYYMMDD-HHmmss}.md`.
|
|
35
|
+
|
|
36
|
+
## Deduplication
|
|
37
|
+
|
|
38
|
+
Findings are deduped by a deterministic fingerprint so the same defect does not file twice. The fingerprint (`health_check_fp.py fingerprint`) is:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
fp-XXXXXXX = "fp-" + sha1("{severity}|ultracode-goal/{stage}|"
|
|
42
|
+
"skills/ultracode-goal/references/{stage}.md|{section-slug}")[:7]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The `step_file` component is always the source-repo form `skills/ultracode-goal/references/{stage}.md` regardless of where the skill is installed, so the same defect dedups to the same key across a dev checkout and an installed `_bmad/` tree. `severity` is one of `bug`/`friction`/`gap`; `stage` is one of the six stage names (`ingest-and-scope`, `preflight`, `define-done`, `execute`, `gate`, `finalize`); `section-slug` is validated kebab-case.
|
|
46
|
+
|
|
47
|
+
Dedup runs at three levels:
|
|
48
|
+
|
|
49
|
+
1. **Machine-global seen-cache** at the configured `health_check_seen_cache` (by default `~/.ultracode-goal/health-check-seen.json`) — the `seen` / `record` subcommands check and atomically merge-write this cache (a missing, empty, or corrupt cache is treated as empty, never a crash). Each record carries the issue URL, the action taken (`created` / `reacted` / `commented` / `queued`), and the date.
|
|
50
|
+
2. **Remote search** — before filing, the existing issues are searched so a finding already filed by another machine is not duplicated.
|
|
51
|
+
3. **Server-side** — a repository Action closes duplicates and upvotes the canonical issue, so even a race between two machines converges on one issue.
|
|
52
|
+
|
|
53
|
+
## Submitting a queued finding manually
|
|
54
|
+
|
|
55
|
+
A queued finding sits as a body file under the queue directory. It carries YAML frontmatter (workflow, step_file, severity, fingerprint, date) followed by the same structured body the attended gate would have shown you, including the Environment table and the `file:line` evidence. Submit one when you are ready with:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
gh issue create --repo <health-check-repo> --title "<title>" --body-file <path-to-queued-finding>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Nothing is sent until you run this command. You can also open one through the repository's issue chooser at <https://github.com/armelhbobdad/bmad-module-ultracode-goal/issues/new/choose>.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# How It Works
|
|
2
|
+
|
|
3
|
+
UltraCode Goal runs an Epic through six stages, in order. Each stage routes to the next by testable conditions stated in its reference file under [`../skills/ultracode-goal/references/`](../skills/ultracode-goal/references/). This page narrates the stages faithfully, the conditions that move between them, and the headless contract. For the design behind it, see [architecture](architecture.md); for the gate specifically, see the [gate model](gate-model.md).
|
|
4
|
+
|
|
5
|
+
## The six stages
|
|
6
|
+
|
|
7
|
+
| # | Stage | Routes by |
|
|
8
|
+
|---|-------|-----------|
|
|
9
|
+
| 1 | Ingest & Scope | one resolved Epic id, or stop |
|
|
10
|
+
| 2 | Preflight | post-remediation budget == 0 and no red, or stop |
|
|
11
|
+
| 3 | Define Done | every in-scope story has a red-phase atdd-checklist |
|
|
12
|
+
| 4 | Execute | every story committed at green, or a turn-bound escalation |
|
|
13
|
+
| 5 | Gate | the `gate_eval.py` verdict: advance / defer / reloop / escalate |
|
|
14
|
+
| 6 | Finalize | terminal — report, ledger, memory capture |
|
|
15
|
+
|
|
16
|
+
### Stage 1 — Ingest & Scope
|
|
17
|
+
|
|
18
|
+
Resolve **which** Epic this run delivers and lock the profile. The operator names the Epic (or the skill picks the obvious in-flight one from `sprint-status.yaml`); the skill locates the Epic/story files, the PRD, and the ADR/architecture, and records the paths to the run's `.decision-log.md`. This is the cheap stage that prevents an expensive run from targeting the wrong Epic.
|
|
19
|
+
|
|
20
|
+
The one absence that hard-stops here: if `_bmad/` config **and** `sprint-status.yaml` **and** any Epic are *all* absent, this is not a BMAD project — the skill points at `bmad-bmb-setup` and `bmad-sprint-planning` and stops. A title-only Epic with no stories does **not** stop here (Stage 2 generates the missing stories); an Epic whose stories are all already `done` triggers an "already complete — re-run anyway?" check. If the Epic cannot be resolved to exactly one id, the skill asks rather than guessing. See [`references/ingest-and-scope.md`](../skills/ultracode-goal/references/ingest-and-scope.md).
|
|
21
|
+
|
|
22
|
+
### Stage 2 — Preflight (the autonomy gate)
|
|
23
|
+
|
|
24
|
+
This is the load-bearing gate, because after it the run goes unattended. The posture is **hard gate with auto-remediation**:
|
|
25
|
+
|
|
26
|
+
1. **Mechanical check** — `preflight_check.py` parses tool versions, git state, and file existence and returns a `budget` count of mechanical blockers (test framework absent, dirty tree, on a protected branch, Claude Code below the minimum versions). It does **not** decide semantic intervention.
|
|
27
|
+
2. **Auto-remediation pass** — clear each remediable blocker, then re-run the check so `budget` reflects the fixes: scaffold the test framework (`bmad-testarch-framework`), scaffold the CI quality pipeline (`bmad-testarch-ci`, production only, strictly *after* the framework), generate missing acceptance criteria (`bmad-create-story`), pre-create the TEA output dirs, ensure exactly one `project-context.md`, ensure `sprint-status.yaml` is present, force TEA **Create** mode, and prompt once (interactively) for any secrets.
|
|
28
|
+
3. **Semantic intervention scan** — the part the script cannot do: read the PRD and ADR for undecided product/architecture decisions, contradictions, acceptance criteria that presuppose an unmade decision, or a story whose "done" is undefinable. Any such item is **RED** and cannot be auto-remediated, because the fix is a human decision.
|
|
29
|
+
|
|
30
|
+
The run launches **only** when all hold: post-remediation `budget == 0`, the semantic scan found no RED, and ultracode session effort plus Auto Mode are on. Then the skill arms the environment — creates the Epic branch off `epic_branch_prefix`, merges the PreToolUse and Stop hooks into `.claude/settings.local.json` (asserting they are active, and injecting the resolved config into their env), and pre-populates the allowlist. On an attended run it prints the launch briefing and takes one soft confirm. See [`references/preflight.md`](../skills/ultracode-goal/references/preflight.md).
|
|
31
|
+
|
|
32
|
+
### Stage 3 — Define Done
|
|
33
|
+
|
|
34
|
+
Turn the Epic's acceptance criteria into **executable, red-phase acceptance tests** before any production code is written. Once per Epic, `bmad-testarch-test-design` (Epic-Level Mode) builds the risk-and-priority backbone: a risk matrix with scored, mitigated risks; P0–P3 priorities (the gate keys its thresholds to these); and NFR thresholds (unknowns are marked `UNKNOWN` and deferred, never guessed). Then, per in-scope story in sprint order: `bmad-create-story` sharpens the acceptance criteria, and `bmad-testarch-atdd` generates an `atdd-checklist-{story_key}.md` plus acceptance test files **every test marked `test.skip()`** (TDD red phase). ATDD hard-halts if a story's ACs are vague or the framework is missing — that is the signal to loop back to `bmad-create-story` for that story. Stage 3 is done only when every in-scope story has a story file with clear ACs and a generated atdd-checklist with red-phase tests on disk. See [`references/define-done.md`](../skills/ultracode-goal/references/define-done.md).
|
|
35
|
+
|
|
36
|
+
### Stage 4 — Execute
|
|
37
|
+
|
|
38
|
+
Drive each in-scope story from its red-phase tests to a green, committed state. The default is the **sequential `/goal` spine**; per story, in sprint order: set the current story (so the PreToolUse hook can find its marker) → `bmad-dev-story` implements the feature and un-skips the story's ATDD tests → run tests/lint/build and **print the raw output** as evidence → (production) `bmad-testarch-test-review` then `bmad-code-review` → commit at green (one commit per green story). The loop is wrapped in a single `/goal` whose condition encodes the per-story Definition-of-Done and carries the literal "…or stop after N turns" escape clause. The printed evidence keeps the run judgeable mid-flight, but **passing the `/goal` condition is not completion** — the authoritative verdict is Stage 5. The experimental `--parallel` path fans the same per-story loop out across worktree-isolated agents; see [parallel mode](parallel-mode.md). As the spine advances it overwrites a `run-status.json` heartbeat for pollers. See [`references/execute.md`](../skills/ultracode-goal/references/execute.md).
|
|
39
|
+
|
|
40
|
+
### Stage 5 — Gate
|
|
41
|
+
|
|
42
|
+
Decide whether a story (or, after the last story, the Epic) advances — by a deterministic artifact read. In production, the skill first backfills the evidence in order — `bmad-testarch-automate`, `bmad-testarch-trace` (which writes the gate decision), `bmad-testarch-nfr` — then runs `gate_eval.py`. The script reads TEA's `gate-decision.json` and returns a verdict the skill executes: `advance` (move to the next story), `defer` (append non-blocking items to the ledger and advance anyway), `reloop` (run `bmad-correct-course`, re-run the story within the remaining budget), or `escalate` (stop). The invariant: **a P0/critical FAIL never defers** — it re-loops within budget or escalates. See the [gate model](gate-model.md) and [`references/gate.md`](../skills/ultracode-goal/references/gate.md).
|
|
43
|
+
|
|
44
|
+
### Stage 6 — Finalize
|
|
45
|
+
|
|
46
|
+
Make the run pay off for the next one. Capture learnings deliberately — machine-local quirks to Auto Memory (`remember X`), team standards to the project's CLAUDE.md or `.claude/rules`. Optionally run the retrospective (`--retro`). Audit every `.decision-log.md` entry into the report, the addendum, or explicit process-noise. Produce a `run-report.md` (Epic, profile, per-story outcomes, the Epic-level gate, budget consumed, learnings, a pointer to the ledger), write the terminal `run-status.json`, surface this Epic's deferred-work ledger heading to the user, and fire the `on_epic_complete` hook **only** when the Epic actually advanced. See [`references/finalize.md`](../skills/ultracode-goal/references/finalize.md).
|
|
47
|
+
|
|
48
|
+
## Production vs. `--light`
|
|
49
|
+
|
|
50
|
+
The **production** profile wires the full TEA chain as gates: test-design, atdd, automate, test-review, nfr, trace, ci. **`--light`** downscopes to the trace gate only — Stage 5 skips automate/nfr/test-review backfill and runs only `bmad-testarch-trace`, then `gate_eval.py --profile light`, with no NFR/review AND. The profile is locked in Stage 1 and read (not re-derived) by Stages 3 and 5.
|
|
51
|
+
|
|
52
|
+
## The decision log
|
|
53
|
+
|
|
54
|
+
The run's `.decision-log.md` — held in the skill's run folder — is canonical memory. Compaction can drop everything else; the log recovers full state. It records scope, the preflight verdict, every gate outcome, every deferral, and (in headless) every assumption. **Resume** reads it: on a resumed run, Execute re-enters at the first story whose last logged gate verdict is not `advance`; advanced stories are not re-run, and the Epic branch, hooks, and allowlist are re-asserted (not rebuilt) before continuing.
|
|
55
|
+
|
|
56
|
+
## The run report and deferred-work ledger
|
|
57
|
+
|
|
58
|
+
At the end, two durable outputs sit beside the decision log. The **run report** (`run-report.md`) is the human takeaway. The **deferred-work ledger** (at `deferred_work_path`) holds one heading per Epic with a row per parked item — only non-gate-blocking work lands here (CONCERNS, non-critical findings, parked decisions); a P0/critical FAIL is never deferred. Finalize surfaces this run's Epic heading so nothing parked is invisible at handoff.
|
|
59
|
+
|
|
60
|
+
## Headless contract
|
|
61
|
+
|
|
62
|
+
With `-H`, the run is non-interactive: infer scope, default to production (unless `--light`), never prompt. Every exit point — a complete run at Stage 6, or an early block at Stage 1 (not a BMAD project / Epic unresolved / already complete), Stage 2 (preflight), or a Stage 6 story escalation — emits **one** object with all five keys always present, `null` when an artifact was not produced, and `reason` carrying a one-line cause only when blocked:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{"status": "complete|blocked",
|
|
66
|
+
"skill": "ultracode-goal",
|
|
67
|
+
"decision_log": "<path to this run's .decision-log.md>",
|
|
68
|
+
"report": "<path to run-report.md, or null>",
|
|
69
|
+
"deferred_work": "<path to deferred-work.md, or null>",
|
|
70
|
+
"reason": "<one line, present only when blocked>"}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
An automator parses one schema regardless of where the run stopped; a blocked-before-report exit returns `report` and `deferred_work` as `null` rather than omitting them.
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# UltraCode Goal
|
|
2
|
+
|
|
3
|
+
Run a BMAD Epic autonomously to a machine-checked Definition-of-Done.
|
|
4
|
+
|
|
5
|
+
`bmad-module-ultracode-goal` is a BMAD module **for Claude Code** — it composes `/goal`, Auto Mode, Auto Memory, and runtime hooks, so the autonomous run executes nowhere else. It delivers a single Epic end to end without a human in the loop — but only behind a hard preflight gate and a deterministic completion gate. It does not replace the BMAD epic toolbox or the Test Architect (TEA); it conducts them. The skill preflights the Epic to a remediated green light, turns acceptance criteria into executable red-phase tests with TEA, drives every in-scope story to a green commit on an isolated Epic branch, and advances only when `gate_eval.py` reads TEA's `gate-decision.json` as PASS — never on the model's own say-so, and never on the `/goal` transcript evaluator alone. The output is a delivered, gate-passed Epic, a run report, and a deferred-work ledger of anything safely parked for later.
|
|
6
|
+
|
|
7
|
+
## Documentation
|
|
8
|
+
|
|
9
|
+
### Why
|
|
10
|
+
|
|
11
|
+
- [Why UltraCode Goal](why-ultracode-goal.md) — the problem (autonomous runs that "look done" aren't), the three enforcement layers, and when not to use it.
|
|
12
|
+
|
|
13
|
+
### Try
|
|
14
|
+
|
|
15
|
+
- [Getting Started](getting-started.md) — prerequisites, install, the first-run walkthrough, and the flags table.
|
|
16
|
+
- [How It Works](how-it-works.md) — the six stages narrated, the conditions that route between them, and the headless emit shape.
|
|
17
|
+
- [Parallel Mode](parallel-mode.md) — the experimental `--parallel` worktree fan-out and its known limits.
|
|
18
|
+
|
|
19
|
+
### Reference
|
|
20
|
+
|
|
21
|
+
- [Architecture](architecture.md) — the conductor model, the three enforcement layers in depth, the file layout, and customization resolution.
|
|
22
|
+
- [Gate Model](gate-model.md) — how `gate_eval.py` maps TEA's gate status to a verdict, the thresholds, and the fail-closed contract.
|
|
23
|
+
- [Health Check](health-check.md) — the terminal self-improvement reflection: what it sends, the privacy model, and how to disable it.
|
|
24
|
+
- [Troubleshooting](troubleshooting.md) — real failure modes and their remediations.
|
|
25
|
+
- [Stability](_internal/STABILITY.md) — the 0.x public-contract posture: what is covered by SemVer and what is `@internal`.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Parallel Mode
|
|
2
|
+
|
|
3
|
+
> **Experimental, opt-in.** `--parallel` is an additive execution path. The sequential `/goal` spine ([how it works](how-it-works.md), Stage 4) is the **default and recommended** path. Use `--parallel` only when you understand the known limits below, and expect to fall back to the spine.
|
|
4
|
+
|
|
5
|
+
When the operator passes `--parallel`, Stage 4 fans the Epic out across worktree-isolated per-story agents instead of driving them one at a time on the spine. This page covers what it does, how concurrency is bounded, and — honestly — where it is not yet validated. It is sourced from [`../skills/ultracode-goal/assets/execute-epic.workflow.js`](../skills/ultracode-goal/assets/execute-epic.workflow.js) and [`references/execute.md`](../skills/ultracode-goal/references/execute.md).
|
|
6
|
+
|
|
7
|
+
## What it does
|
|
8
|
+
|
|
9
|
+
`--parallel` invokes the saved dynamic workflow `execute-epic.workflow.js` (registered as `/ultracode-goal-execute`). Each in-scope story runs in its **own git worktree** on its own branch, so concurrent stories never overwrite each other's working tree. Within a worktree the steps are the same as the spine and strictly ordered: `bmad-create-story` (Create mode) → `bmad-dev-story` un-skipping the story's red-phase ATDD tests → run and print tests/lint/build, then write the tests-ran marker → (production) `bmad-testarch-test-review` then `bmad-code-review` → commit at green → per-story `gate_eval.py`. After every story lands, the workflow runs one epic-level trace gate and returns `{ perStory: [{story, verdict, gate_status}], epicGate, deferred: [...] }`, which feeds Stages 5 and 6.
|
|
10
|
+
|
|
11
|
+
Critically, this path **shares the sequential spine's truth sources**: the same `gate_eval.py` reading TEA's `gate-decision.json` (never the model, never the transcript-only `/goal` evaluator), and the same PreToolUse + Stop hooks merged at preflight enforce the invariants. The verdict mapping is owned by `gate_eval.py`; the spawned agents return its stdout fields verbatim and must not recompute TEA thresholds. See the [gate model](gate-model.md).
|
|
12
|
+
|
|
13
|
+
## Concurrency
|
|
14
|
+
|
|
15
|
+
The cap on simultaneous worktree agents is `parallel_max_concurrency` — **default 8** in `customize.toml`, chosen under the platform's 16-concurrent ceiling. Stories are batched: each concurrency-sized batch runs in parallel; batches run sequentially. The literal `4` inside the `.js` is only a fallback when the skill invokes the workflow without supplying `max_concurrency`; the governing value is the passed `parallel_max_concurrency`.
|
|
16
|
+
|
|
17
|
+
## No mid-run input
|
|
18
|
+
|
|
19
|
+
The fan-out takes **no interactive input once launched** — every gate and every blocker must be resolved at preflight or not at all. This is exactly why the [preflight](how-it-works.md) hard gate requires a post-remediation budget of zero before launch: there is no opportunity to answer a question mid-run, so a run that would have needed an answer must refuse to launch instead.
|
|
20
|
+
|
|
21
|
+
## Known limits — be honest
|
|
22
|
+
|
|
23
|
+
This path leans on workflow↔skill interplay the platform docs leave under-specified. Treat its behavior as empirically validated, not guaranteed:
|
|
24
|
+
|
|
25
|
+
- **Shared Auto Memory across worktrees.** All worktrees of one git repo share a single Auto Memory directory — there is no per-worktree isolation of learned facts. Concurrent writers can collide and interleave; expect interleaving rather than clean per-story memory.
|
|
26
|
+
- **Under-documented workflow↔skill interplay.** How args bind, and how the spawned subagents inherit the allowlist and the hooks, is not fully specified by the platform docs. The skill threads the resolved `skill_root` into the workflow args so the spawned agents get an absolute `gate_eval.py` path (the runtime has no `{skill-root}` resolver), but the broader handoff is treated as empirically validated, not guaranteed end to end.
|
|
27
|
+
- **No `run-status.json` heartbeat.** Worktree agents each see their own copy of `implementation_artifacts`, so this path cannot reliably write one shared snapshot — it does not write `run-status.json`. Watch progress via the workflow progress view (`/workflows`) and its run log instead; the launch briefing says so.
|
|
28
|
+
|
|
29
|
+
## Graceful degradation
|
|
30
|
+
|
|
31
|
+
If dynamic workflows are unavailable — wrong Claude Code version, the workflows feature off, or the saved command does not resolve — the skill **falls back to the sequential `/goal` spine** and logs a one-line note in `.decision-log.md` recording why `--parallel` degraded. The Epic still ships; it just ships sequentially. This is the safety net behind the recommendation to treat the spine as the default: choosing `--parallel` never risks the Epic, only the mode.
|
|
32
|
+
|
|
33
|
+
See [troubleshooting](troubleshooting.md) for what to check when `--parallel` does not behave, and [architecture](architecture.md) for how the workflow asset fits the conductor model.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
Real failure modes, sourced from the skill's stage files and scripts, with what the run does about each and what you do. For the design behind these behaviors see [how it works](how-it-works.md), [the gate model](gate-model.md), and [architecture](architecture.md).
|
|
4
|
+
|
|
5
|
+
## Preflight can't reach budget-zero
|
|
6
|
+
|
|
7
|
+
**Symptom.** The run stops at Stage 2 (or, headless, emits `{"status":"blocked", ...}` with a one-line `reason`) and never launches.
|
|
8
|
+
|
|
9
|
+
**What happened.** Preflight is a hard gate: the run launches only when the post-remediation mechanical `budget == 0` **and** the semantic scan found no RED. The auto-remediation pass clears the *fixable* mechanical blockers — it scaffolds the test framework, generates missing acceptance criteria, pre-creates the TEA output dirs, ensures one `project-context.md`, ensures `sprint-status.yaml`, and prompts once (interactively) for secrets — then re-runs the check.
|
|
10
|
+
|
|
11
|
+
**What stays red.** Some things the remediation pass cannot fix, by design:
|
|
12
|
+
|
|
13
|
+
- **Undecided product or architecture decisions.** An open question, a "TBD"/"TODO: decide" on a load-bearing requirement, a PRD↔ADR contradiction, or a story whose "done" is undefinable. The fix is a human decision; an unattended run guessing it produces confidently wrong work. Resolve the decision in the artifacts, then re-run.
|
|
14
|
+
- **Missing secrets in headless.** Headless never prompts, so a secret that cannot be resolved becomes a RED blocker rather than a question. Provide the secret (out of git) before the headless run, or run attended so preflight can prompt once.
|
|
15
|
+
- **Claude Code below the minimum versions.** The primitive-version blocker is marked non-remediable — the script can't upgrade the host. Update Claude Code.
|
|
16
|
+
|
|
17
|
+
The decision log carries the full blocker list with what each needs to clear. Read it, clear the items, re-run.
|
|
18
|
+
|
|
19
|
+
## gate_eval reports blocked / escalate on a missing gate-decision.json
|
|
20
|
+
|
|
21
|
+
**Symptom.** Stage 5 returns `gate_status: NOT_EVALUATED` and verdict `escalate`, with a `reasons` entry like `neither gate-decision.json nor e2e-trace-summary.json present in <dir>`.
|
|
22
|
+
|
|
23
|
+
**What happened.** `gate_eval.py` reads TEA's gate artifact from the trace output directory. `NOT_EVALUATED` means neither the slim `gate-decision.json` nor the fallback `e2e-trace-summary.json` was found there, or the run carried no gate fields. Almost always this is one of:
|
|
24
|
+
|
|
25
|
+
- **The TEA trace gate did not run.** In production, Stage 5 must backfill evidence first (`bmad-testarch-automate` → `bmad-testarch-trace` → `bmad-testarch-nfr`) before the gate; `bmad-testarch-trace` is what writes the gate decision. If it didn't run, there is nothing to read.
|
|
26
|
+
- **Wrong `trace_output_dir`.** The script reads the directory passed as `--trace-output` (resolved from `{workflow.trace_output_dir}`). If TEA wrote elsewhere — or the output dirs were never pre-created at preflight — the artifact is real but in a different place. Confirm `trace_output_dir` matches where TEA actually wrote.
|
|
27
|
+
|
|
28
|
+
Note this is fail-closed on purpose: a missing or unreadable gate artifact escalates rather than being assumed green. The slim file's *absence alone* is not the problem — the script falls back to the summary, and that fallback is explicitly not a failure.
|
|
29
|
+
|
|
30
|
+
## Hooks not firing
|
|
31
|
+
|
|
32
|
+
**Symptom.** A commit lands on a protected branch, or a commit lands before a story's tests ran — the invariants the PreToolUse hook should enforce did not block.
|
|
33
|
+
|
|
34
|
+
**What to check.**
|
|
35
|
+
|
|
36
|
+
- **Older Claude Code.** The hook returns a `deny` decision in the hook JSON and *also* exits 2 with the reason on stderr precisely so older clients that ignore the JSON still block. If neither path fired, the client may not be honoring PreToolUse hooks at all — update Claude Code.
|
|
37
|
+
- **`settings.local.json` not merged.** The hooks are merged into `{project-root}/.claude/settings.local.json` at preflight, and the skill asserts they are active before going unattended. If the file wasn't merged (or the workspace trust dialog wasn't accepted), the hooks aren't loaded. Re-run preflight; verify the two hook entries are present in the resolved settings.
|
|
38
|
+
- **A `customize.toml` override that silently no-ops.** Both hooks read config from env first and fall back to hardcoded defaults (`main`/`master`, `25`, `1_500_000`, `ultracode/epic-`). A `protected_branches` or budget override in `customize.toml` only reaches the hook if preflight injected it into the hook env (`ULTRACODE_PROTECTED_BRANCHES`, etc.). If your custom protected branch isn't being guarded, the override didn't reach the enforcement layer — confirm preflight passed it through.
|
|
39
|
+
|
|
40
|
+
## Budget exhausted mid-story
|
|
41
|
+
|
|
42
|
+
**Symptom.** A story stops re-looping; an escalation marker (`<impl-artifacts>/.escalation-<story>.md`) appears; the run surfaces a budget message.
|
|
43
|
+
|
|
44
|
+
**What happened.** A runaway story is bounded three ways. The real in-loop bound is the literal "…or stop after N turns" clause inside the `/goal` condition. The gate's re-loop budget is deterministic: a `reloop` that would exceed `max_turns_per_story` or `story_token_budget` becomes an `escalate` instead. The **Stop** hook (`budget_stop.py`) is the defensive third layer — it counts turns and tokens and, on overrun, writes the escalation marker and lets the stop proceed.
|
|
45
|
+
|
|
46
|
+
**Its documented limitation.** A Stop hook fires only when Claude is *already* trying to stop — it **cannot interrupt a `/goal` condition mid-turn**. So at this layer the ceiling is advisory; the hard bounds are the in-condition turn clause and the gate re-loop budget. If a story keeps consuming budget, that is the signal to re-scope, split, or hand it off — not to raise the budget and hope.
|
|
47
|
+
|
|
48
|
+
## Resume after an interruption
|
|
49
|
+
|
|
50
|
+
**Symptom.** A run was interrupted (Ctrl-C, a crash, a compaction) and you want to continue rather than restart.
|
|
51
|
+
|
|
52
|
+
**What happens.** The run's `.decision-log.md` is canonical memory and recovers full state regardless of compaction. On resume the skill surfaces the existing log with its last session date and offers to resume. Execute re-enters at the **first story whose last logged gate verdict is not `advance`**; already-advanced stories are not re-run. The Epic branch, hooks, and allowlist are **re-asserted, not rebuilt**, before continuing. You do not need to reconstruct state by hand — point the skill at the same Epic and accept the resume offer.
|
|
53
|
+
|
|
54
|
+
## `--parallel` issues
|
|
55
|
+
|
|
56
|
+
`--parallel` is experimental and opt-in; the sequential spine is the default. If dynamic workflows are unavailable (wrong Claude Code version, the feature off, or the saved command doesn't resolve), the skill **automatically falls back to the spine** and logs why in `.decision-log.md` — the Epic still ships. For the known limits (shared Auto Memory across worktrees, the under-documented workflow↔skill interplay, no `run-status.json` heartbeat), see [parallel mode](parallel-mode.md).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Why UltraCode Goal
|
|
2
|
+
|
|
3
|
+
Autonomous coding agents have one failure mode that dwarfs the rest: a run that **looks** done and isn't. The agent reports green, the transcript reads clean, and the Epic ships with a P0 acceptance criterion silently unmet. UltraCode Goal exists to make that specific failure impossible — by deciding completion from a deterministic artifact a script reads, not from anything the model itself produces. This page explains the problem, the three enforcement layers that answer it, and when the module is the wrong tool.
|
|
4
|
+
|
|
5
|
+
## The problem
|
|
6
|
+
|
|
7
|
+
Three intuitive shortcuts all push an unattended Epic toward false completion, and each one is wrong for a documented mechanical reason.
|
|
8
|
+
|
|
9
|
+
**The evaluator only sees the transcript.** Claude Code's `/goal` mode drives a loop until a success condition is met, then asks an evaluator to confirm. But that evaluator reads the transcript — it cannot open a file on disk. It cannot read TEA's `gate-decision.json`. So if you let the `/goal` condition be the final word on "is this story done," you have asked the model to grade its own homework from its own notes. The model cannot be the judge of its own completion: the thing that decides "done" has to be something it cannot author.
|
|
10
|
+
|
|
11
|
+
**`/rewind` checkpoints miss Bash changes.** The obvious undo for a runaway agent is `/rewind`. But its checkpoints do not capture changes made through Bash — and an autonomous Epic run is mostly Bash: test commands, lint, build, and the git commits themselves. Rolling back to a checkpoint leaves the working tree's Bash-driven mutations in place. The real undo for this kind of run is git: an Epic branch off a protected branch, one commit per green story, worktree isolation. See the [gate model](gate-model.md) and [architecture](architecture.md) for how this is wired.
|
|
12
|
+
|
|
13
|
+
**Memory is context, not enforcement.** It is tempting to encode the run's invariants ("never commit on `main`", "never commit before tests pass") into Auto Memory or a CLAUDE.md rule and trust the model to honor them. But memory is context the model may or may not weigh — it does not *block* a `git commit`. An invariant that lives only in memory is a suggestion. An invariant that must hold has to live somewhere the model cannot talk its way past.
|
|
14
|
+
|
|
15
|
+
## The thesis: three enforcement layers
|
|
16
|
+
|
|
17
|
+
UltraCode Goal answers each shortcut with a layer the model cannot override.
|
|
18
|
+
|
|
19
|
+
**1. Deterministic gate truth.** Completion is decided by `scripts/gate_eval.py`, which reads TEA's `gate-decision.json` and maps its gate status to a routing verdict — `PASS`/`WAIVED` advance, `CONCERNS` defers, `FAIL` re-loops, `NOT_EVALUATED` escalates. The script never re-derives TEA's thresholds and never consults the transcript; it reads the artifact as given. The model produces evidence; the script reads the verdict. They are different things on purpose. See the [gate model](gate-model.md).
|
|
20
|
+
|
|
21
|
+
**2. Hooks as invariants.** The two invariants that must hold — no commit on a protected branch, no commit before a story's tests have actually run green — live in a **PreToolUse** hook, not in memory. The hook inspects each `git commit`/`git push` and denies it when the branch is protected or the story's tests-ran marker is absent. It is merged into `.claude/settings.local.json` at preflight and asserted active before the run goes unattended. A denied commit is enforcement; a remembered rule is not. See [architecture](architecture.md).
|
|
22
|
+
|
|
23
|
+
**3. Budget enforcement.** A runaway story is bounded two ways. The `/goal` condition carries a literal "…or stop after N turns" escape clause (the real in-loop bound, because a Stop hook cannot interrupt a `/goal` condition mid-turn), and a **Stop** hook tracks turns and tokens against `max_turns_per_story` / `story_token_budget`, writing an escalation marker when either is breached. The gate's re-loop budget is the third, deterministic bound: a `reloop` that would exceed the turn or token budget becomes an `escalate` instead. See [how it works](how-it-works.md).
|
|
24
|
+
|
|
25
|
+
These three are the module's non-negotiables. They exist because the documented mechanics make the intuitive shortcut wrong, so the skill does not optimize them away.
|
|
26
|
+
|
|
27
|
+
## When not to use it
|
|
28
|
+
|
|
29
|
+
UltraCode Goal is narrow on purpose. It is the wrong tool when:
|
|
30
|
+
|
|
31
|
+
- **There is no BMAD project.** It needs `_bmad/` config, a `sprint-status.yaml`, or an Epic to target. With none of the three present, Stage 1 hard-stops and points you at `bmad-bmb-setup` and `bmad-sprint-planning`. See [getting started](getting-started.md).
|
|
32
|
+
- **There are no Epics or stories.** The unit of delivery is one Epic with acceptance-bearing stories. There is nothing for the gate to read against a loose task list.
|
|
33
|
+
- **The work is exploratory.** If the Definition-of-Done is genuinely undecided — open product or architecture questions, "TBD" on a load-bearing requirement — preflight refuses to launch rather than let an unattended run guess. That refusal is correct; resolve the decisions first, then run.
|
|
34
|
+
- **You want interactive pairing.** The whole point is that the human leaves the loop after preflight. If you want to review each step, drive the underlying BMAD skills (`bmad-dev-story`, `bmad-code-review`, the TEA workflows) directly instead.
|
package/package.json
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/package.json",
|
|
3
|
+
"name": "bmad-module-ultracode-goal",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "BMAD module — Run a BMAD Epic autonomously to a machine-checked, TEA-gated Definition-of-Done.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"bmad",
|
|
8
|
+
"bmad-method",
|
|
9
|
+
"bmad-module",
|
|
10
|
+
"agent-skills",
|
|
11
|
+
"agentskills",
|
|
12
|
+
"agents",
|
|
13
|
+
"skills",
|
|
14
|
+
"autonomous",
|
|
15
|
+
"epic",
|
|
16
|
+
"goal-mode",
|
|
17
|
+
"auto-mode",
|
|
18
|
+
"auto-memory",
|
|
19
|
+
"quality-gates",
|
|
20
|
+
"tea",
|
|
21
|
+
"test-architect",
|
|
22
|
+
"definition-of-done"
|
|
23
|
+
],
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/armelhbobdad/bmad-module-ultracode-goal.git"
|
|
27
|
+
},
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"author": "armelhbobdad",
|
|
30
|
+
"main": "tools/cli/ucg-cli.js",
|
|
31
|
+
"bin": {
|
|
32
|
+
"bmad-module-ultracode-goal": "tools/ucg-npx-wrapper.js"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"format:check": "prettier --check \"**/*.{js,cjs,mjs,json,yaml}\"",
|
|
36
|
+
"format:fix": "prettier --write \"**/*.{js,cjs,mjs,json,yaml}\"",
|
|
37
|
+
"lint": "eslint . --ext .js,.cjs,.mjs,.yaml --max-warnings=0",
|
|
38
|
+
"lint:fix": "eslint . --ext .js,.cjs,.mjs,.yaml --fix",
|
|
39
|
+
"lint:md": "markdownlint-cli2 \"**/*.md\"",
|
|
40
|
+
"prepare": "husky || exit 0",
|
|
41
|
+
"quality": "npm run format:check && npm run lint && npm run lint:md && npm run test:install && npm run test:cli && npm run test:python && npm run validate:skills && npm run validate:refs",
|
|
42
|
+
"test": "npm run test:install && npm run test:cli && npm run test:python && npm run validate:skills && npm run validate:refs && npm run lint && npm run lint:md && npm run format:check",
|
|
43
|
+
"test:cli": "node test/test-cli-integration.js",
|
|
44
|
+
"test:install": "node test/test-installation-components.js",
|
|
45
|
+
"test:python": "uv run --with pytest pytest skills/ultracode-goal/scripts/tests/ -v",
|
|
46
|
+
"ucg:install": "node tools/cli/ucg-cli.js install",
|
|
47
|
+
"ucg:status": "node tools/cli/ucg-cli.js status",
|
|
48
|
+
"ucg:uninstall": "node tools/cli/ucg-cli.js uninstall",
|
|
49
|
+
"ucg:update": "node tools/cli/ucg-cli.js update",
|
|
50
|
+
"validate:refs": "node tools/validate-file-refs.js --strict",
|
|
51
|
+
"validate:skills": "node tools/validate-skills.js --strict"
|
|
52
|
+
},
|
|
53
|
+
"lint-staged": {
|
|
54
|
+
"*.{js,cjs,mjs}": [
|
|
55
|
+
"npm run lint:fix",
|
|
56
|
+
"npm run format:fix"
|
|
57
|
+
],
|
|
58
|
+
"*.yaml": [
|
|
59
|
+
"eslint --fix",
|
|
60
|
+
"npm run format:fix"
|
|
61
|
+
],
|
|
62
|
+
"*.json": [
|
|
63
|
+
"npm run format:fix"
|
|
64
|
+
],
|
|
65
|
+
"*.md": [
|
|
66
|
+
"markdownlint-cli2"
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
"dependencies": {
|
|
70
|
+
"@clack/prompts": "^1.1.0",
|
|
71
|
+
"chalk": "^4.1.2",
|
|
72
|
+
"commander": "^14.0.0",
|
|
73
|
+
"figlet": "^1.8.0",
|
|
74
|
+
"fs-extra": "^11.3.0",
|
|
75
|
+
"js-yaml": "^4.1.0"
|
|
76
|
+
},
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"@eslint/js": "^9.33.0",
|
|
79
|
+
"conventional-changelog-cli": "^5.0.0",
|
|
80
|
+
"csv-parse": "^6.1.0",
|
|
81
|
+
"eslint": "^9.33.0",
|
|
82
|
+
"eslint-config-prettier": "^10.1.8",
|
|
83
|
+
"eslint-plugin-n": "^17.21.3",
|
|
84
|
+
"eslint-plugin-unicorn": "^60.0.0",
|
|
85
|
+
"eslint-plugin-yml": "^1.18.0",
|
|
86
|
+
"husky": "^9.1.7",
|
|
87
|
+
"lint-staged": "^16.1.1",
|
|
88
|
+
"markdownlint-cli2": "^0.19.1",
|
|
89
|
+
"prettier": "^3.7.4",
|
|
90
|
+
"prettier-plugin-packagejson": "^2.5.19",
|
|
91
|
+
"yaml": "^2.7.0",
|
|
92
|
+
"yaml-eslint-parser": "^1.2.3"
|
|
93
|
+
},
|
|
94
|
+
"engines": {
|
|
95
|
+
"node": ">=22.0.0"
|
|
96
|
+
},
|
|
97
|
+
"publishConfig": {
|
|
98
|
+
"access": "public"
|
|
99
|
+
}
|
|
100
|
+
}
|
package/skills/.gitkeep
ADDED
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
code: ultracode-goal
|
|
2
|
+
name: "UCG: UltraCode Goal — Autonomous Epic Execution"
|
|
3
|
+
header: "Run a BMAD Epic autonomously to a deterministic, TEA-gated Definition-of-Done"
|
|
4
|
+
subheader: "Preflight to a remediated green light, define done as executable tests, advance only on a machine-checked gate verdict"
|
|
5
|
+
description: "Orchestrates the BMAD epic toolbox + TEA quality gates + Claude Code primitives (/goal, Auto Mode, Auto Memory, hooks, worktree isolation) to deliver an Epic unattended. Completion is decided by a deterministic gate_eval.py reading TEA's gate-decision.json — never the model's own judgment."
|
|
6
|
+
default_selected: false
|
|
7
|
+
|
|
8
|
+
# Repository that receives workflow health-check findings (fingerprint-deduped).
|
|
9
|
+
# Override per project in _bmad/custom/ultracode-goal.toml if you fork the module.
|
|
10
|
+
health_check_repo:
|
|
11
|
+
prompt: false
|
|
12
|
+
default: "armelhbobdad/bmad-module-ultracode-goal"
|