Subchapter 28.13
references/phases/discover/discover.mdMarkdown9 KBView on GitHub
Inventory what exists on Heroku into a single flat heroku-resource-inventory.json
in $MIGRATION_DIR/. This phase is composed of FRAGMENTS (independent discoverers)
plus one ASSEMBLER, declared in the frontmatter _fragments/_assemble — the
interpreter runs each fragment whose _trigger is true (loading its _file only
then), then the assembler. Read each unit file for its own contract; this phase
owns only lifecycle + the cross-cutting _postconditions.
Two facts the contract can’t express: Procfile/app.json parsing is integrated into
the terraform fragment (there is no standalone Procfile fragment) — when present
alongside Terraform, they supplement resource data with commands, buildpacks, and
declared add-ons. Billing data, when present, is embedded in
heroku-resource-inventory.json (not a separate file); all user communication
is via output messages only (no report/log files).
Live discovery reads the user’s Heroku account through their authenticated Heroku
CLI — read-only, consent-gated, key-names-only for config vars. It is split in two
because the dispatched rw worker has no shell and cannot converse with the user:
discover-live-capture.md) — runs in the MAIN window, after
_init and before the phase’s work is dispatched. It asks for consent, preflights
the CLI (heroku auth:whoami), runs an exact-command allowlist of list/info
commands, and writes raw output to $MIGRATION_DIR/live-capture/ plus a
manifest.json index. It writes NO inventory entries.discover-live.md, the live fragment) — runs in the worker with the
other fragments. Its _trigger is the manifest’s existence; it maps captures to
inventory entries with source: "live".Explicit ordering (cold start): run _init state setup FIRST (create
$MIGRATION_DIR, write .phase-status.json), THEN evaluate the source
_precondition — offering and running capture as part of that evaluation — then
dispatch the phase’s work. Capture writes into $MIGRATION_DIR/live-capture/, so
it cannot run before _init has created the run directory.
When to offer capture: while evaluating the source _precondition, scan the
workspace first (free). If NO heroku_* Terraform is found, offer live capture as
the primary source — load discover-live-capture.md — instead of failing the check.
If Terraform IS found, still offer capture once as an optional live cross-check
(“catch resources managed outside Terraform”); a decline is fine and is not
re-asked. Never run capture without explicit consent.
Source-of-truth rule (for the assembler): when both Terraform and live entries
exist, live is authoritative for current state (config values, plans, quantities);
Terraform supplements structure and provenance. Disagreements are surfaced as drift,
never silently resolved — see discover-assemble.md § Merge & Drift Rules.
After the interpreter emits HANDOFF_OK | phase=discover, build the user-facing
completion message from the inventory contents:
Format: “Discover phase complete. [artifact summaries] Next required step: Phase 2 — Clarify. Load references/phases/clarify/clarify.md now. Do not load Design, Estimate, or Generate until Clarify completes and .phase-status.json marks phases.clarify as completed.”
Non-fatal discovery errors and their handling (fatal source/gate failures are handled by _preconditions/_postconditions + INTERPRETER.md § _on_error):
| Error Category | Behavior |
|---|---|
| Terraform parse error (malformed HCL) | Log warning, skip malformed blocks, continue |
| Procfile/app.json parse error | Record warning per-app, continue |
| Generation detection unresolvable (no stack attr) | Set heroku_generation to unknown, continue |
| Pipeline detection from Terraform incomplete | Record with available data, continue |
| Live capture partially failed (some apps 403 etc.) | Parse the ok captures, mark failed apps discovery_failed, confidence reduced, continue |
| Live capture declined or CLI unavailable | Skip the live fragment (no manifest → trigger never fires), continue with file-based sources |
This phase covers Heroku Discovery ONLY.
FORBIDDEN — Do NOT include ANY of:
Your ONLY job: Inventory what exists on Heroku. Nothing else.