Skip to content

epic #284: Planning Carries Only Asked-For Scope - #428

Draft
sameera wants to merge 6 commits into
mainfrom
epic/284-scope-razor
Draft

epic #284: Planning Carries Only Asked-For Scope#428
sameera wants to merge 6 commits into
mainfrom
epic/284-scope-razor

Conversation

@sameera

@sameera sameera commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Implements the story sub-issues of #284, one commit per story in blocked_by order.

Each commit body carries its own Closes #<story> line, so merging this PR
into main closes the stories it implements. The epic itself closes through
/nxs.close, not by merge.

Draft opened by utils/implement-epic.sh; /nxs.analyze runs against it next.

Adds the razor's provenance rule and its one normative home. The new
nxs-razor skill states the two-valued label vocabulary, the rule that a
run's source text is materialized once beside the draft before anything
is labelled, and the normalized-containment comparison with a four-word
floor that decides whether a citation holds.

The epic drafting stage loads the skill rather than restating it. Phase
4 writes the run's source text to the draft folder — the typed
capability description, the stub body, or the discovery document and its
resolved tickets — so the gate compares against the same bytes the
drafting used rather than a live source that may have moved since.

Filing is done from a derived body, not the labelled draft. The stage
strips the labels and then asserts that none survived, so the promise
that no planning bookkeeping reaches a durable reader is a checked
condition rather than an instruction the model has to remember. That
assertion is `nexus razor-check --assert-clean` over a new
@nexus/scope-razor library, which is where the razor's remaining
mechanical checks will join it.

Closes #285
Adds the counted limits, the content rules and the necessity question to
the razor, and restates each limit beside the heading it bounds in the
epic template.

The limits are a ceiling and nothing else. Three to five acceptance
criteria is guidance; above five a story carries one stated reason, per
story rather than per criterion, because the overage is a property of the
story. There is no minimum-count rule anywhere — a blocking floor would
hold a story with two sufficient criteria until a third was manufactured,
which is the padding the razor exists to remove. Assumptions and Out of
Scope take at most five items each with no escape, and either may be
empty.

The content rules ban a personas table where the canonical personas
apply, and ban an acceptance criterion that names a mechanism. The first
is a structural test; the second is a judgment, so it is prevented at
drafting time rather than enforced.

The necessity answer is one line under its own heading and reaches the
filed issue. It is scope reasoning a later reader consumes rather than
planning bookkeeping, and it is what orders the digest's cut list.

The restatements are pointers with a number in them. They sit beside the
heading because the observed failure was a heading's presence beating a
rule stated far from it; where a restatement and the skill disagree, the
skill governs.

Closes #286
Builds the razor's one mechanical enforcer and wires the epic gate to it.
`nexus razor-check --draft <path> --source <path>` counts the acceptance
criteria on each story and the items under Assumptions and Out of Scope,
resolves every asked-citation against the run's materialized source text,
and tests for a table under the personas heading. A blocking finding
names the story or section it belongs to and exits non-zero, so a draft
that breaks a counted limit never reaches the approval digest.

The checker exists because four stages apply these rules and one of them
has no gate agent at all. Counting inside a gate's prompt would mean
either a second implementation or an unchecked rule elsewhere — and
asking the drafting model to count its own list items reproduces, one
layer down, the failure this epic exists to fix.

Everything in the checker is a count, a presence test, or normalized
substring containment. The one razor rule that needs a judgment —
whether an acceptance criterion names a mechanism — stays with the gate
agent, which reports each suspicion as a non-blocking observation the
digest carries to the reviewer. The gate still edits no draft and
creates no issue: it reports, and the caller fixes.

Closes #287
Adds the cut list to the approval digest, so removing scope is one choice
instead of a revise, a hand edit and a re-run. Without it the provenance
labels are decoration: approving as drafted stays the cheaper action, and
the model's additions ship.

The list holds every inferred item, every wholly inferred story, and each
fully asked-for story the necessity answer leaves outside the smallest
usable version. An excluded asked-for story sorts first and is rendered
as asked-for — cutting it removes something the lead requested, and they
have to be able to see that.

It is numbered prose grouped by story, selected by typing the numbers,
because five stories easily yield twenty cuttable items and paginating
them into batches that fit a click surface turns one action into several
rounds. An empty selection is plain approval, with no re-derivation and
no second confirmation.

Cuts apply before anything is filed. A cut naming already-filed content
is refused with the reason rather than silently skipped, an all-stories
cut is a revise, a cut story's dependents are re-parented onto its own
blockers with the cascade stated before the lead confirms, and the
complexity rollup, the design-warrant label and any risk banner quoting
the pre-cut assessment are derived again from the reduced set.

The convention the list follows is stated once in the razor skill, which
the record's second cut gate will follow by shape rather than by sharing
an implementation.

Closes #288
Loads the razor into the decision-record and discovery stages, so scope
the epic stage refuses cannot enter through a neighbouring one. Neither
stage adds a rule of its own and neither implements a check of its own:
both invoke the checker the epic gate invokes, against their own drafts.

The record labels its invariants and risks, materializes the epic and any
imported design doc as its source text, and derives the body it files by
stripping the labels and asserting that none survived. That body is the
artifact the record hash is taken over, so a surviving label would report
a design that did not change as changed. Decisions and refuted
alternatives are not labelled — a refuted alternative is the model's own
by construction, so the label would discriminate nothing.

Discovery labels the discovery document's not-yet-specified and
out-of-scope entries and each ticket's question. Resolutions are not
labelled: a resolution is a decision made in session and is inferred by
construction, so the label would be uniform noise.

Neither stage gains a gate agent. Discovery's standing constraint is
narrowed to what it was actually about — every ticket type routing to
existing machinery — so loading a shared guidance skill is no longer
something an implementer has to read as forbidden or quietly violate.

Closes #289
Removes the decision template's standing refuted-alternative line. A
fixed slot with permission-to-omit in an adjacent comment is the same
shape as a personas heading with a deviations-only rule beside it, and it
produced the same outcome: the slot got filled. Removing it removes the
generation pressure at its source rather than asking a model to resist
it. A decision with no viable alternative now carries nothing there, and
there is no placeholder to replace.

The rule that governs it — write one only where a competent engineer
might genuinely have chosen it, and name the trade-off it lost on — is
stated in the razor and restated where the architect authors. The
provenance rule deliberately does not reach it: a refuted alternative is
the model's own by construction, so asked-versus-inferred discriminates
nothing. Viability is what discriminates, and viability is a judgment, so
it takes the treatment already fixed for mechanism-naming.

That judgment is made by the stage that formats the record, not by the
architect that wrote the alternatives — a model asked to judge its own
additions answers in its own favour. It is rendered only at the gate,
never written into the draft, so there is no marker to strip.

The decision-record stage gains the pre-filing checkpoint the command
already referred to and no phase defined. It runs before every path that
creates or updates the record sub-issue, lists every refuted alternative
numbered and grouped by decision, and removes the ones the reviewer names
before anything is filed. Naming nothing is plain approval, and the
proceed-without-a-record exit stays available.

Closes #424
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant