epic #284: Planning Carries Only Asked-For Scope - #428
Draft
sameera wants to merge 6 commits into
Draft
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 PRinto
maincloses the stories it implements. The epic itself closes through/nxs.close, not by merge.Draft opened by
utils/implement-epic.sh;/nxs.analyzeruns against it next.