gtmesh plan
Computes the deterministic, read-only diff between your desired state and what’s committed, prints it, and writes the planfile.
gtmesh plan # print the plan (like `terraform plan`); also writes the planfile
gtmesh plan --json | jq . # machine output
gtmesh plan > plan.md # keep the rendered planWhat it does
plan computes the same registry-to-registry diff that apply will enact — but changes nothing, so you can run it as often as you like. The printed plan opens with a one-line summary (Plan: 105 to recompute, 15 to propose-retire), then the catalogue/create breakdown by page type, then Changes grouped by action kind — the groups needing human review first, each line naming the page by slug — and finally the Cannibalization and Unresolved lists (pages that matched no sections_map/page_types rule — including fresh demand the topic gate parked in backlog awaiting a topic, since that’s work rather than an out-of-scope park).
Retirement needs a positive observation, never silence. A pull is scoped to one root topic
(gtmesh pull demand --topic <value>), so most pulls never ask about most keywords. Only pulls that
covered a root the keyword has been seen under count toward retirement.dwell_pulls — a keyword whose
root simply has not been re-pulled is stale, not faded, and plan says so and tells you to re-pull it
rather than proposing to retire the page.
A BUILT page is never proposed for retirement. A page that has been written, sealed and published is
work, and an orphaned built page has almost always been reclassified, not faded — the bag carries every
keyword it has ever seen forward, so a page keeps its demand group until something regroups it (an
aliases edit under entity_aware, a changed question_page_min_volume, a question fold). plan reports
it as “its demand group is now empty” and leaves it alone: the node stays, the page keeps rendering, and
gtmesh page retire remains the deliberate
way to remove one. Unbuilt catalogued pages still propose-retire as before.
Advisories — folded question demand, frozen slugs whose head keyword moved on, ambiguous identities — go to stderr, grouped by class so each explanation is stated once with its occurrences listed under it. That keeps stdout pipeable: gtmesh plan > plan.md captures the plan alone.
The engine never guesses: if a keyword is unresolved, fix the config rule (or add a seed/override) and re-plan until the unresolved list is what you expect. Re-planning is free.
plan is optional — apply computes the same diff itself. Run plan whenever you want a read-only preview; you never need it between a change and an apply.
Arguments & flags
| Option | Description | Default |
|---|---|---|
--out <file> | Override the plan.json output path | .gtmesh/plan.json |
The global --json switches stdout to machine-readable output.
Reads & writes
- Reads: the config, reference tables, seeds, and the frozen data bag.
- Writes: the human view to stdout, and a derived
plan.json(git-ignored). - It is read-only with respect to your committed state — it never touches the registry or page bundles.
Examples
gtmesh plan # review the diff before applying
gtmesh plan --out /tmp/p.json
gtmesh plan --json | jq '.actions'Related
- Platform: Plan & apply
- Guide: Walkthrough, Lifecycle & reconcile
- CLI:
gtmesh apply