Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .nexus/anchors/backlog-stub.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
concept: backlog-stub
source_sha: 9d42385a3585858bf599987598a214d4daa37968
generated: 2026-08-02
---

<!-- DERIVED — regenerated by /nxs.distill on every drain touching this concept.
Never hand-edit; stale anchors are rebuilt, not fixed. -->

# Code Anchors: Backlog Stub

- `.claude/skills/nxs-gh-shared/delivery_config.py` — resolves the unplanned label through the shared precedence chain, exposes the cross-feature backlog as one query in its three forms (listing, search fragment, negated filter), and carries the label preflight that upserts every label a filing run will apply and reports the ones it can neither create nor find.
- `.claude/skills/nxs-gh-create-story/scripts/create_gh_issues.py` — the one batch filing path: takes its canonical classification from the caller instead of stamping every issue a story, upserts every declared label before creating anything, refuses a work-item that is both a stub and a sub-issue, and resolves a literal issue-number dependency edge that points outside the batch.
- `.claude/skills/nxs-gh-create-epic/scripts/nxs_gh_create_epic.py` — the promotion path: reads the target's labels, refuses to promote an issue that is not an unplanned epic, and populates that same issue in place while clearing the unplanned label, so no second issue is created and nothing is closed.
- `libs/epic-resolve/src/classify.ts` — resolves the unplanned label across the process seam and answers whether an epic issue still carries it.
- `libs/epic-resolve/src/resolve.ts` — refuses to materialize an unplanned epic by name rather than emitting one with an empty story set.
- `libs/epic-resolve/src/resolve.spec.ts` — pins the refusal and the label resolution against the resolver's fixtures.
- `.claude/commands/nxs.epic.md` — the decomposition writer: files one stub issue per functional goal behind the right-size gate's consent, and promotes by issue number with no slug lookup.
- `.claude/commands/nxs.close.md` — the deferred-scope writer: files stub issues as the first step after the closure checkpoint, before the close record naming their numbers is committed.
- `.claude/skills/nxs-gh-shared/test_backlog_stubs.py` — covers stub filing through the batch path: classification from the caller, the unplanned label, and the no-parent refusal.
- `.claude/skills/nxs-gh-shared/test_backlog_query.py` — covers the three query forms and the quoting of a multi-word label.
- `.claude/skills/nxs-gh-shared/test_stub_promotion.py` — covers promotion legality and populating the issue in place.
- `.claude/skills/nxs-gh-shared/test_deferred_scope_stubs.py` — covers the close-side writer and the record naming the filed numbers.
- `.claude/skills/nxs-gh-shared/test_literal_dependency_refs.py` — covers the literal issue-number dependency edge.
- `.claude/skills/nxs-gh-shared/test_migration_dry_run.py` — covers the one-batch migration of the retired committed backlogs.
- `docs/features/README.md` — the features index carrying the single link to the cross-feature backlog query.
10 changes: 5 additions & 5 deletions .nexus/anchors/epic-approval-gate.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
---
concept: epic-approval-gate
source_sha: 1e0c90b776f0bd12a70388cb4643cd2bd7831766
generated: 2026-07-26
source_sha: 9d42385a3585858bf599987598a214d4daa37968
generated: 2026-08-02
---

<!-- DERIVED — regenerated by /nxs.distill on every drain touching this concept.
Never hand-edit; stale anchors are rebuilt, not fixed. -->

# Code Anchors: Epic Approval Gate

- `.claude/commands/nxs.epic.md` — takes intent directly, right-sizes, presents the decision-grade digest, and on approval files the epic and its story issues together, committing nothing to the queue at planning.
- `.claude/skills/nxs-gh-create-epic/scripts/nxs_gh_create_epic.py` — files the epic GitHub issue from the epic document, embedding the raw planning frontmatter as a hidden machine comment for the resolver, resolving the epic's target repo, classification, and project through the shared resolver, and applying the needs-design label from the complexity rollup (upsert before apply).
- `.claude/skills/nxs-gh-create-story/scripts/create_gh_issues.py` — files one issue per story as children of the epic, resolving the story target repo independently of the epic's.
- `.claude/commands/nxs.epic.md` — takes intent directly, right-sizes, presents the decision-grade digest, and on approval files the epic and its story issues together, committing nothing to the queue at planning; the oversized branch files one backlog stub issue per functional goal instead, and a promotion re-enters by issue number.
- `.claude/skills/nxs-gh-create-epic/scripts/nxs_gh_create_epic.py` — files the epic GitHub issue from the epic document, or populates an unplanned epic's own issue in place when promoting one, embedding the raw planning frontmatter as a hidden machine comment for the resolver, resolving the epic's target repo, classification, and project through the shared resolver, and applying the needs-design label from the complexity rollup (upsert before apply).
- `.claude/skills/nxs-gh-create-story/scripts/create_gh_issues.py` — files one issue per story as children of the epic, resolving the story target repo independently of the epic's, and takes its canonical classification from the caller so the same path can file stubs as epics.
- `.claude/commands/nxs.close.md` — addresses the epic where it was filed, resolving that target once and threading it onto every issue operation.
8 changes: 4 additions & 4 deletions .nexus/anchors/workspace-resolution.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
concept: workspace-resolution
source_sha: 288906382ecb248dc469d5cb52af2868f97bd5fe
generated: 2026-07-24
source_sha: 9d42385a3585858bf599987598a214d4daa37968
generated: 2026-08-02
---

<!-- DERIVED — regenerated by /nxs.distill on every drain touching this concept.
Expand All @@ -20,8 +20,8 @@ generated: 2026-07-24
- `.claude/skills/nxs-workspace-status/SKILL.md` — the workspace status read-out skill; now also documents the companion docs-root read-out and its two vehicles.
- `.nexus/config/templates/workspace-template.yml` — the hub manifest template leads copy into a real hub; documents the optional docs-root override and the optional workspace-wide publishing-defaults block with its per-key inheritance.
- `.nexus/config/templates/hub-template.yml` — the member pointer template.
- `.claude/commands/nxs.epic.md` — resolves the feature container path once under the docs root, records it in the queue entry, and searches the stub-promotion glob across backlogs under the resolved root.
- `.claude/commands/nxs.close.md` — reads the recorded feature path and writes deferred scope and the process lesson under it, never re-resolving the root.
- `.claude/commands/nxs.epic.md` — resolves the feature container path once under the docs root, and records it in the queue entry; stub promotion reads issue state only and globs nothing under the resolved root.
- `.claude/commands/nxs.close.md` — reads the recorded feature path and writes the process lesson under it, never re-resolving the root; deferred scope is filed as stub issues rather than written to the tree.
- `.claude/commands/nxs.setup.md` — scaffolds the product, system, and delivery docs under the resolved docs root.
- `.claude/commands/nxs.decision-record.md` — hands the resolved context-doc locations into the design brief in place of fixed paths.
- `.claude/commands/nxs.council.md` — resolves the docs root once and threads it into the product-manager and architect briefs.
Expand Down
36 changes: 36 additions & 0 deletions .nexus/concepts/backlog-stub.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
title: "Backlog Stub"
aliases: ["backlog stub", "unplanned epic", "stub decomposition", "stub promotion", "unplanned label", "cross-feature backlog", "deferred scope filing"]
touches: ["epic-approval-gate"]
last_updated_by: "#185"
status: active
verification: verified
---

# Backlog Stub

A backlog stub is an epic identified but not yet planned — an epic issue carrying the repository's declared epic classification plus exactly one label denoting that unplanned state. Planning populates that same issue in place, so the number scope is deferred under is the number it ships under. The whole cross-feature backlog is therefore one query, and excluding unplanned work one negated filter.

## How It Works

Two writers create stubs: the epic stage when scope exceeds one epic, and the close stage when it defers scope. Both author transient work-items and file them through the one batch filing path, which takes its canonical classification from the caller instead of stamping everything a story. That path upserts every label it will apply before creating anything, and a label it can neither create nor find stops the run with nothing created. A stub is never a sub-issue — its link to the epic that spawned it is a body mention — because the close gate that blocks on open sub-issues has no exemptions and would deadlock on a stub it just filed. Feature, estimate, candidate stories and provenance live in the body, never in labels. Planning a stub fills in that same issue, files its story children beneath it, and clears the label; nothing is created and nothing is closed. A stub too large to become one epic is closed as not planned, naming its successors.

## Key Invariants

1. A stub is an epic issue carrying the declared epic classification; no third kind of issue exists.
2. Exactly one label denotes the unplanned state; feature, estimate, candidate stories and provenance are body content.
3. The issue number is a stub's only identifier; no second lookup key is recorded or accepted as input.
4. Promotion populates that same issue and clears the label — never a second issue, never a close.
5. A stub too large for one epic is closed as not planned naming its successors, never as completed.
6. No stub is ever a sub-issue, and no stub enters an epic's sub-issue set.
7. Because a stub is an epic, every query enumerating epics for planned work carries the one negated filter, and every stage reconstructing an epic refuses an unplanned one by name.

## Integration Points

- [epic-approval-gate](epic-approval-gate.md) — what oversized scope becomes, and what a promotion re-enters this gate as.

## Decision Log

### 2026-08-02 — #185 — A stub is an epic born unplanned, and its number survives promotion

Deferred and oversized scope stopped being markdown blocks in per-feature committed files and became epic issues marked unplanned, because a stub already is an epic — a functional goal sized at or below the epic ceiling, destined to become exactly one epic. Modelling it as its own kind would force every consumer to learn a third species and would answer the classification question twice, once per classification mode, where no suitable issue type exists and none can be minted without organisation-level administration. The identity consequence is the load-bearing one: because promotion populates the issue in place rather than creating a successor, every reference written when the scope was deferred — a dependency edge between siblings, a mention from the epic that spawned it, a line in a sequencing table — stays valid for the life of the work instead of being orphaned the moment the stub is promoted. Retiring the per-feature files collapses the backlog to one query and its exclusion to one negated filter; carrying that negation on every epic-enumerating query is the accepted price of the durable identity, named here rather than discovered later as a defect. Refuted alternative: a distinct backlog kind with its own label and its own issue type — it reads cleanly in a triage list and keeps unplanned work out of the epic query by construction, but the issue type cannot be created where the organisation has not defined one, so that mode degrades to filing stubs unmarked, and it forces a second identity at promotion, orphaning every reference to the first. Refuted alternative: create the epic fresh and close the stub as completed naming its successor — needs no change to the filing path, but it reintroduces an identity hop on every promotion and leaves a permanent trail of closed placeholders every reader and every dependency edge must indirect through.
15 changes: 10 additions & 5 deletions .nexus/concepts/epic-approval-gate.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
title: "Epic Approval Gate"
aliases: ["approval digest gate", "epic filing gate", "decision-grade digest", "stub decomposition"]
touches: ["nexus-pipeline", "story-as-unit", "issue-sourced-planning", "publishing-config-resolution", "decision-record"]
last_updated_by: "#139"
aliases: ["approval digest gate", "epic filing gate", "decision-grade digest"]
touches: ["nexus-pipeline", "story-as-unit", "issue-sourced-planning", "publishing-config-resolution", "decision-record", "backlog-stub"]
last_updated_by: "#185"
status: active
verification: verified
---
Expand All @@ -13,14 +13,14 @@ The epic stage files the epic and its story issues together, gated by a single d

## How It Works

The epic stage takes a capability description directly, with no separate brief. It produces a right-sized epic and presents a digest: the feature line, the epic prose, the stories as sized one-liners, and the assumptions and out-of-scope boundary. Approval is the single forcing function; open questions must be resolved first. On approval, the stage files the epic issue and one issue per story, sequences them, and writes the feature navigation index linking to the filed issue. Filing also declares the design-warrant: a medium-or-larger complexity rollup gets the needs-design label, upserted before applied; an absent or unrecognized rollup errs toward needing design, and the lead can edit the label. Under issue-sourced planning it commits nothing at planning — the draft stays in session scratch — files issue-first, and a re-run reuses the already-filed epic issue. Scope too large for one epic decomposes into backlog stubs — slug, functional goal, candidate story-group titles, complexity — rather than several fully generated epics. A stub is promoted to a full epic later, on demand.
The epic stage takes a capability description directly, with no separate brief. It produces a right-sized epic and presents a digest: the feature line, the epic prose, the stories as sized one-liners, and the assumptions and out-of-scope boundary. Approval is the single forcing function; open questions must be resolved first. On approval, the stage files the epic issue and one issue per story, sequences them, and writes the feature navigation index linking to the filed issue. Filing also declares the design-warrant: a medium-or-larger complexity rollup gets the needs-design label, upserted before applied; an absent or unrecognized rollup errs toward needing design, and the lead can edit the label. Under issue-sourced planning it commits nothing at planning — the draft stays in session scratch — files issue-first, and a re-run reuses the already-filed epic issue. Scope too large for one epic decomposes into backlog stub issues instead of several fully generated epics; the gate's consent covers that irreversible filing.

## Key Invariants

1. The epic and its story issues are filed together, gated by one approval.
2. The decision-grade digest, not the full epic document, is the read surface at the gate.
3. Open questions block filing; they are the only pre-filing safeguard.
4. Oversized scope becomes backlog stubs, not multiple fully generated epics.
4. Oversized scope becomes backlog stub issues filed on the gate's consent, not fully generated epics.
5. The epic stage takes intent directly; no separate brief is a precondition.
6. Filing commits nothing at planning: the epic issue precedes its story children, and a re-run reuses an already-filed one.
7. The epic and its stories resolve their target repository independently; later stages address the epic where it was filed.
Expand All @@ -32,6 +32,7 @@ The epic stage takes a capability description directly, with no separate brief.
- [issue-sourced-planning](issue-sourced-planning.md) — the model this gate files into: issues, not a committed file.
- [publishing-config-resolution](publishing-config-resolution.md) — decides the repository, classification, and project for every issue this gate files.
- [decision-record](decision-record.md) — filing applies its needs-design label from the complexity rollup.
- [backlog-stub](backlog-stub.md) — what oversized scope becomes, and what a promotion re-enters this gate as.

## Decision Log

Expand All @@ -54,3 +55,7 @@ Where an issue lands became a resolved decision rather than an implicit one, and
### 2026-07-26 — #139 — Filing declares the design-warrant on the epic issue

Whether an epic warrants a decision record is now decided once, at filing, from the epic's own complexity rollup — medium or larger gets the needs-design label — and lives on the issue where the lead can edit it, so every downstream stage answers "should this epic have a record" from the issue graph with no remembered state, and a hand-filed epic without the label is simply an epic without a record. Small and extra-small are both exempt, since the stated threshold is medium-or-larger; an absent or unrecognized rollup errs toward needing design rather than silently skipping the gate. Refuted alternative: derive the need from the epic's embedded machine metadata — absent on hand-filed epics and not editable in the issue interface, exactly where the label form works for free.

### 2026-08-02 — #185 — Oversized scope files stub issues, and a promotion re-enters by issue number

The oversized path stopped writing markdown blocks into a per-feature file and now files one epic issue per functional goal, marked unplanned — so the gate's consent covers an irreversible platform write rather than a local append, and the choice text at the gate says so. Retiring the file also retires the slug those blocks were addressed by: a promotion re-enters this gate by issue number alone, and because a stub is filed as an epic it is the same kind of object this gate produces. That is what forces the operation to be stated rather than inferred — a bare number always means plan this epic and is legal only while the unplanned label is present, loading an already-planned epic is selected by its own flag, and anything else is a capability description. The gate keeps writing no feature navigation index on this path, because a stub writes nothing to the tree at all. Refuted alternative: infer the operation from whether the unplanned label is present and drop the flag entirely — one fewer input to learn, but the same command would then silently do two different things depending on a label a third party can remove, and the two operations differ in consequence.
Loading