Files
summercms/.planning/tmp/plan-pre-assumption-delta.md

5.3 KiB

Assumption-Delta Architecture Checkpoint

Advisory, non-blocking. Fires only when the phase scope shows a singular→plural / required→optional / derived→chosen transition. When it fires, it surfaces ONE identity-model question before the plan is finalized. Most phases will not fire it — that is the point.

Why this exists

Most quietly-imported architectural debt does not come from a missing upfront design phase. It comes at the seam: a later phase introduces a second case (a second platform, auth method, tenant, region, source of truth) and nobody re-asks whether the original abstraction still names the right thing. The phase that adds the second case is exactly the 20-minute conversation that prevents an afternoon of later cleanup.

Run the detector

The detector is a deterministic scan over the phase scope text. It strips fenced code blocks first, so a trigger word that appears only inside a code snippet does not fire. It returns a typed result: { detected, signals[], terms }. Resolve it through the assumption-delta scan query (same phase-section resolver as roadmap.get-phase):

ASSUMPTION_DELTA_JSON=$(gsd_run query assumption-delta scan "${PHASE}" --json 2>/dev/null) || true
[ -n "$ASSUMPTION_DELTA_JSON" ] || ASSUMPTION_DELTA_JSON='{"skipped":true,"reason":"probe_unavailable"}'

If the phase section cannot be resolved (no ROADMAP.md / unknown phase, or a section with no body), the query emits { "skipped": true, "reason": "phase_unresolved" } — not detected:false. A probe that never had input does not get to assert that this phase changes no core assumption. The checkpoint does not fire either way; the difference is that a skip is now distinguishable from a real negative. Do not block on it.

Optional tuning — pass --terms <comma-list> to replace the curated pluralization cues for this project (the optional/chosen cues keep their defaults): gsd_run query assumption-delta scan "${PHASE}" --json --terms second,alternative,fallback.

Decision branch

Read ASSUMPTION_DELTA_JSON. Act on detected only — do not pattern-match the human prose.

If skipped is true: the detector never examined a phase section — it could not resolve one (phase_unresolved) or could not run at all (probe_unavailable). Skip the checkpoint for this run rather than asserting a verdict about input that was never examined; do not raise it with the user. Check for skipped before reading detected — a skipped payload carries no detected key, and treating its absence as false re-creates the fabrication this branch exists to prevent.

If detected is false: this phase does not change a core assumption. Skip the checkpoint entirely and continue planning. Do not raise it with the user.

If detected is true: a core assumption may have lost its monopoly. The signals[] array tells you which family fired:

kind What changed The question to answer
pluralization A second X was introduced where there was one (second platform / auth method / tenant / region / source of truth) Does the current primary key / identity model still name the right noun?
optional A required / only field became optional Is the field still the right anchor, or has the anchor moved?
chosen A derived value became chosen, or a constant became a parameter Has a configuration decision become a modeling decision?

Before finalizing the plan, answer this for the user and record the decision explicitly:

Promote vs. add-alongside. The usual correct move when a generalization occurs is to promote the new general representation to the primary and demote the old specific one to a detail of one variant — not to add the new one alongside the still-required old one. Adding alongside silently contradicts the generalized intent (a later variant that does not fit the old primary can be stored but never confirmed as a default).

Record the outcome in the PLAN.md front matter / a <assumption_delta_decision> block:

  • The noun that is now primary (the generalized identity).
  • The decision: promote | add-alongside | no-change, with a one-line rationale.
  • If add-alongside: call it out as accepted debt and note what would force a later promote.

Optional companion: an invariant test

When detected is true, suggest (do not require) a contract/invariant test that encodes the now-generalized intent — e.g. "every confirmed default round-trips through the primary use-path, for every supported variant." That test goes red the instant a future phase reintroduces the singular assumption, so the regression cannot land silently. If the user accepts, add the test as a task in the plan.

Tuning the vocabulary (optional)

The trigger vocabulary is a curated, additive-only set in gsd-core/bin/lib/assumption-delta.cjs (DEFAULT_ASSUMPTION_DELTA_TERMS). Bare "or" is intentionally excluded — it is too common in prose and would make the gate fire constantly. To widen or narrow the cues for a project, override at the call site with --terms <comma-list> (replaces the pluralization cues; optional/chosen keep defaults). The whole checkpoint is toggleable via workflow.assumption_delta in .planning/config.json.

This checkpoint is advisory: it informs and records; it never blocks the phase.