Decision Record: Backlog Stubs Become GitHub Issues
Summary
Backlog stubs stop being markdown blocks in per-feature committed files and become epic issues
born unplanned — carrying the epic's own classification from birth plus a single backlog label
meaning "not yet planned". Both writers — oversized-scope decomposition and deferred scope at close —
file them through the existing batch filing path, and promotion populates that same issue in place
rather than creating a second one, so a piece of deferred scope keeps one issue number from the
moment it is deferred through planning, delivery and close. The twenty-two live stubs migrate in one
batch before the committed files are deleted last.
Chosen Approach
A stub is not a new species of issue. It is an epic that has been identified but not yet planned, so
it is filed as an epic and marked with one label denoting that unplanned state; everything else about
it — feature, estimate, candidate stories, originating intent, date — lives in the issue body's meta
block. Both writers reuse the one existing batch filing path with transient work-items rather than
growing a second filing component, which preserves the retry, resume-ledger and idempotency
behaviour that makes irreversible bulk creation safe. Promotion removes the label and fills in the
body; nothing is closed and nothing is created. The deferred-scope writer moves from the local
pre-checkpoint half of close into the post-checkpoint publishing phase, as its first step, because
issue creation is not reversible and because the close record must be able to name the resulting
numbers.
The identity consequence is the load-bearing one. Because the number survives promotion, every
reference written when scope was deferred — a dependency edge between sibling stubs, a mention from
the epic that spawned one, a line in a sequencing table — stays valid for the life of the work
instead of being orphaned the moment the stub is promoted.
Key Decisions
A stub is an epic issue born unplanned, not a separate kind of issue
- Decision: Filing a stub creates an epic issue carrying the epic classification the repository
declares, plus one label meaning "not yet planned". Promotion clears that label. No other kind of
issue is introduced.
- Why: 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 forces every consumer to learn a
third species, and forces the classification question to be answered twice: once for the label-mode
marker and again for the issue-type-mode marker, where no suitable type exists and none can be
minted without organisation-level administration. Filing it as what it already is answers both at
once and costs nothing.
- Refuted alternative: A distinct backlog kind, marked by its own label in label mode and its own
issue type in type mode. It reads cleanly in a triage list and keeps unplanned work out of the epic
query by construction. It loses on three counts: the issue type cannot be created in a repository
whose organisation has not defined one, so the type mode degrades to filing stubs unmarked; it
forces a second identity when the stub is promoted, orphaning every reference to the first; and it
asserts a distinction that dissolves the moment anyone asks what a promoted stub became.
Exactly one label denotes the unplanned state; the feature lives in the body
- Decision: One label, with no per-feature and no per-status variants, denotes that an epic has
not yet been planned; feature, estimate, candidate stories and provenance are body content.
- Why: One label makes the cross-feature backlog one query and makes exclusion one negated
filter — the two outcomes this epic is measured on.
- Refuted alternative: A per-feature label family, which gives free per-feature filtering and
renders directly in the issue list. It loses because the label set then grows with the feature
count, every new feature needs an upsert before its first stub, and exclusion costs one negation
per feature — precisely the noise this epic exists to remove.
The batch filing path takes its classification from the caller
- Decision: The shared batch filing path stops treating everything it creates as a story and
takes the canonical classification — label in label mode, issue type in type mode — from its
caller, defaulting to today's story value so existing behaviour is unchanged. Stub filing passes
the epic classification.
- Why: The path currently stamps the story marker unconditionally, so stubs filed through it
would be classified as stories — wrong under any model, and doubly wrong here, where the whole
point is that a stub is already an epic.
- Refuted alternative: File stubs through the single-epic filing path instead, which already
applies the epic classification correctly. It loses because that path creates one issue at a time
with no resume ledger, no retry, and no dependency wiring — exactly the machinery a
twenty-two-issue migration and every multi-goal decomposition depend on.
Promotion populates the issue in place; it creates nothing and closes nothing
- Decision: Promoting a stub fills in the epic body, writes the planning meta block, files the
story sub-issues, and removes the unplanned label — all on the issue that already exists. The
single-epic filing path gains an update-in-place mode to support this.
- Why: This is what makes the issue number durable. Under a create-and-close promotion every
reference to a stub breaks at the moment of promotion, which is precisely why the historical
dependency edges in this repository point at issues that are closed and superseded. One issue with
one number for the whole life of the work removes that failure mode rather than managing it.
- Refuted alternative: Create the epic fresh and close the stub as completed with a comment
naming its successor, which needs no change to the filing path. It loses because it reintroduces an
identity hop on every promotion, leaving a permanent trail of closed placeholders that every
reader and every dependency edge must indirect through.
A stub that proves oversized at promotion is closed as not planned, naming its successors
- Decision: When planning reveals a stub is larger than a single epic, it re-decomposes into new
stub-epics and the original is closed as not planned with a comment naming them.
- Why: The issue cannot become an epic it is too large to be, and it must not be closed as
completed, which would assert delivery that never happened. Not-planned is the platform's existing
vocabulary for scope abandoned before it was built.
- Refuted alternative: Keep the original open as a parent of its successors. It loses because a
stub is never a parent of anything, and an epic that stays open while its scope lives entirely in
other issues has no closing condition.
A stub is never a sub-issue; its link to its originating epic is a body mention
- Decision: No parent relationship is ever created for a stub. The originating epic is named in
the body, which the platform renders as a back-reference.
- Why: Close hard-blocks until every sub-issue of the epic is closed, deliberately with no
exemptions by kind. A stub filed by close as a sub-issue of the epic being closed would deadlock
the very stage that filed it — and under this model that stub outlives the close by design, since
it is the epic the deferred work will eventually be planned as.
- Refuted alternative: A sub-issue link plus a kind exemption on that gate, which would buy the
native rollup view. It loses because the gate's single virtue is having no bypass — the first
exemption makes an unapproved decision record exemptible next.
Deferred-scope filing is the first step of the post-checkpoint phase, ahead of both the close comment and the close-record commit
- Decision: Move filing after the consent gate, run it before the close record's deferred-scope
section is finalised, and disclose it in the checkpoint's irreversible-actions list.
- Why: Issue creation cannot be undone, so it belongs behind consent; but the record must name
the filed numbers, and in pull-request mode the record is committed and pushed before the close
comment is posted. First-step-after-consent is the only position satisfying both.
- Refuted alternative: File after the epic close comment and leave the record describing the
deferred scope in prose. It loses because the record then either ships without numbers or must be
amended after it was already committed — reintroducing the two-copies problem this epic ends.
The issue number is the only identifier a stub ever has
- Decision: A stub is identified solely by its issue number — as its identity, as the form
dependency edges reference, and as the only input that promotes it. The title is a human-readable
goal summary. No slug is recorded and no slug resolves a stub. A batch-local reference token
survives only inside a single filing run's input, where edges between goals must be expressed
before any issue exists; it never reaches an issue body and never reaches the lead.
- Why: The slug existed only because the file format had no identifier — it was the block heading
and the sole way to name a stub. An issue has an identifier, and under in-place promotion that
identifier is stable for the entire life of the work, so a second lookup key would have no source
of truth, no uniqueness enforcement, and no consumer.
- Refuted alternative: Keep slug input as an undocumented convenience resolving by search over
open stub bodies, preserving the habit through the transition. It loses because the habit is worth
less than a single identifier space, and because a key nothing defines and nothing enforces drifts
silently the moment two stubs collide.
The flag selects the operation; the unplanned label decides whether it is legal
- Decision: A bare issue number always means "plan this epic", and is legal only for an epic
still carrying the unplanned label; pulling an already-planned epic is selected only by its
explicit flag. Any other input is a capability description. A number naming an already-planned
epic fails closed with a diagnostic naming the flag.
- Why: Both inputs now name the same kind of object, so the operation cannot be inferred from
what is referenced — it must be stated. The label then answers a separate question: whether the
stated operation applies to an epic in that state.
- Refuted alternative: Infer the operation from whether the label is present, removing the flag
entirely. It loses because the same command would silently do two different things depending on a
label a third party can remove, and because "plan this" and "load this" differ in consequence.
Reuse the one batch filing path with transient work-items; add no stub filer
- Decision: Both writers and the migration author transient work-items and invoke the existing
batch filing path.
- Why: Transient retry with backoff, a resume ledger, idempotent dependency wiring and idempotent
re-runs are exactly what irreversible bulk creation needs; they already exist and are exercised.
- Refuted alternative: A small purpose-built stub filer, simpler to read in isolation. It loses
because it would duplicate the resume and idempotency machinery — the only reason filing
twenty-two issues in one shot is safe at all.
Dependency references accept a literal issue number, as a migration affordance
- Decision: The dependency input accepts a literal issue number alongside a batch-local
reference, resolved directly against the platform.
- Why: The historical backlog contains edges pointing at work promoted under the old
create-and-close model, whose stubs are now closed behind different issue numbers; those edges can
only be expressed as literals. Going forward the need largely disappears — because promotion
preserves the number, an edge written at decomposition stays valid through promotion without
rewriting — so this is a migration affordance and a convenience, not a structural pillar.
- Refuted alternative: Express out-of-batch dependencies as prose in the issue body. It loses
because it silently drops ordering the migration must preserve, and because accepting a literal
number is a small, generally useful extension rather than a special case.
Migration is one batch across all backlogs, and only proposed blocks migrate
- Decision: A single filing run covers all twenty-two proposed stubs; promoted and superseded
blocks are left to git history and never refiled.
- Why: Cross-feature dependencies exist among the proposed set, and batch-local wiring resolves
them only if they are filed together. Refiling closed history would add noise to exactly the query
this epic is built to make useful.
- Refuted alternative: Per-feature migration runs, easier to review and to roll back one feature
at a time. It loses on precisely those cross-feature edges.
The backlog surfaces as one link in one place, and the unplanned filter belongs on every epic query
- Decision: The cross-feature backlog query is linked once from the features index and named in
the stage reports; feature navigation indexes gain no backlog section. Because stubs are epics,
every query that enumerates epics for planned work carries the negated unplanned filter, not only
the default triage view. Where deleting a backlog file empties its feature container entirely, the
container goes with it and is recreated at that feature's first epic filing.
- Why: With one label and the feature held in the body, a per-feature view is a text search over
issue bodies — brittle and misleading. No feature index links a backlog file today, so there is no
existing surface to repoint; one feature has no index at all and its container holds nothing but
its backlog file, and its stubs carry the feature path in their bodies regardless. The
epic-query cost is the accepted price of a durable identity, and naming it here is what stops it
being discovered later as a defect.
- Refuted alternative: Per-feature index entries each linking a filtered search, preserving the
per-feature navigation path. It loses on brittleness, and on being several links that all mean the
same thing.
Constraints & Invariants
- A stub is an epic issue carrying the repository's declared epic classification; no third kind of
issue is introduced by this epic.
- Exactly one label denotes the unplanned state; feature, estimate, candidate stories, originating
intent and date are body content, never labels.
- The label is upserted before it is applied on every filing path, so filing never fails on a
missing label in a repository that has never seen it.
- Promotion never creates a second issue for the same scope and never closes the stub; the issue
number a stub is filed under is the number its epic, its close, and every reference to it use for
the life of the work.
- A stub that cannot become a single epic is closed as not planned with its successors named, and
is never closed as completed.
- No stub is ever a sub-issue of any issue, and no stub ever enters an epic's sub-issue set.
- No filing path creates any issue before the consent gate for that stage has been passed; declining
the gate leaves the platform untouched.
- Filing is resumable and idempotent — a re-run after partial failure completes only the remainder
and never duplicates an issue.
- Filing succeeds with no project configured, adds the stub to no project, and performs no project
discovery in that case.
- Stub resolution reads issue state only and never globs the documentation tree; promotion works in
a repository containing no backlog file at all.
- The issue number is a stub's only identifier; no second lookup key is recorded in the body or
accepted as input.
- A bare issue number always means "plan this epic" and is legal only while the unplanned label is
present; loading an already-planned epic is selected only by its explicit flag; any other input
is a capability description.
- An input that fails to resolve to exactly one unplanned epic reports why and creates nothing.
- Any stage that reconstructs an epic from its issue refuses an unplanned one with a diagnostic
naming that state, rather than failing on the absent planning meta block or empty story set.
- Only proposed blocks are migrated; promoted and superseded blocks are left to git history and are
never refiled.
- A migrated stub preserves its original recorded date and its full original rationale text, not a
one-line reduction of it.
- The file deletion lands only after both writers, the single reader, and every committed reference
have been repointed.
- Every platform write in this epic resolves its label, classification mode, project target and
repository target through the shared publishing resolver; no call site hardcodes any of them.
- The only new permission required is label creation; where the token lacks it, the stage reports
the missing label before attempting any issue creation, so a permission gap can never leave a
half-filed batch.
Risks (BLOCKER / ADDRESS only)
-
ADDRESS — promotion in place requires an update path the epic filing surface does not have.
That surface is create-only, and the planning meta block is written at creation. Promotion must
instead populate an existing issue's body, write the meta block onto it, and file its stories
beneath it. Mitigation: add the update-in-place mode as part of the promotion story and treat "no
second issue exists after promotion" as its acceptance signal. This is the change that most
pressures that story's sizing, and it is the price of the durable identity — worth paying, but it
should be sized deliberately rather than absorbed.
-
ADDRESS — the batch filing path classifies everything it creates as a story. The story marker
is applied unconditionally rather than supplied by the caller, so stubs filed through it would be
classified as stories in both modes. Mitigation: parameterize the classification with today's value
as the default and pass the epic classification when filing stubs. It changes shared code other
stages depend on and needs its own regression check.
-
ADDRESS — stages that reconstruct an epic from its issue will meet epics that were never
planned. An unplanned stub has no planning meta block and no story sub-issues, which those stages
currently assume. Mitigation: guard with an explicit "not yet planned" refusal, and confirm no
enumeration of epics silently treats a stub as a planned one.
-
ADDRESS — migration's dependency graph points outside the batch and contains one stale edge.
Five proposed stubs depend on blocks promoted under the old model, whose issues are closed behind
different numbers, and one depends on an item recorded as abandoned and superseded by the
issue-sourced-planning work. Unresolved references make the batch report incomplete and exit
non-zero. Mitigation: resolve each outward edge to the issue number that work became, and rewrite
the stale edge to the epic issue that superseded it. This is judgement work that must precede the
migration run, and it is the last time it will be needed — once promotion preserves numbers, these
edges stop breaking.
-
ADDRESS — the close record is authored, and in pull-request mode committed and pushed, before
the publishing phase. Today's phase order assumes everything before the checkpoint is local and
reversible. Mitigation: finalise the record's deferred-scope section between filing and the commit,
and treat "the record names the filed numbers" as the acceptance signal.
-
ADDRESS — twenty-two irreversible creations in one run. A wrong body shape or wrong
classification is twenty-two corrections after the fact, not a revert. Mitigation: run the full
batch in dry-run and compare one rendered stub against a real stub filed by the first story before
the live run; the sequencing already places migration last, which makes that comparison possible.
Open Clarifications
None. Five were raised during analysis and resolved at the approval gate; their answers are folded
into the decisions above.
Story Coverage
Every user story in the epic is covered, with the scope edits noted.
-
Decomposition stubs are filed as backlog issues — covered by the stub-is-an-epic decision, the
single-label decision, the caller-supplied-classification decision, the filing-path reuse decision
and the surfacing decision; invariants 1, 2, 3, 7, 8, 9, 18, 19. Scope note: the right-size gate's
option text now discloses an irreversible platform write rather than a local file append, and the
stubs path writes nothing to the working tree — including the feature container, which is not
needed until an epic is planned.
-
Deferred scope at close becomes stub issues, filed after the checkpoint — covered by the
body-mention decision, the phase-position decision and the filing-path reuse decision; invariants
6, 7, 8, 17. Scope note: the close record's deferred-scope section is finalised between filing and
the commit, and the checkpoint's irreversible-actions list gains the filing.
-
A stub is promoted by issue number, and closing it is the promotion — covered by the
populate-in-place decision, the oversized-stub decision, the issue-number-identity decision and the
flag-selects-the-operation decision; invariants 4, 5, 10, 11, 12, 13, 14. Two scope edits. First,
the story's title and its promotion criterion are wrong under this design: closing the stub is not
the promotion — promotion populates the same issue and removes the unplanned label, and the only
close a stub ever receives is a not-planned close when it proves oversized. Second, retiring the
slug removes the three slug-matching criteria and replaces them with one, that any non-numeric
input is a capability description. Net: the criteria go from eight to six, and the story absorbs
the update-in-place work on the epic filing surface.
-
The committed backlogs are migrated and the files retired — covered by the literal-reference
decision and the one-batch/proposed-only decision; invariants 15, 16, 17. Scope edit: the
dependency graph is judgement work alongside the prose-heavy entries — five edges point outside the
batch at work promoted under the old model, and one stale edge is rewritten to the epic that
superseded it. Factual correction: there are thirteen promoted blocks, not twelve; twenty-two
proposed and three superseded are confirmed.
-
The backlog is one cross-feature query, and triage excludes it — covered by the single-label
decision and the surfacing decision; invariants 2, 17. Scope edits: the per-feature navigation
criterion is replaced by a single features-index entry linking the backlog query, since no feature
index links a backlog file today; the feature container holding nothing but its backlog file is
allowed to go with it; and the story gains the negated-filter requirement on every epic-enumerating
query, not only the default triage view. Added to this story: one line at the head of the
sequencing table naming the backlog query as the authoritative inventory, leaving its wave ordering
and rationale untouched.
Decision Record: Backlog Stubs Become GitHub Issues
Summary
Backlog stubs stop being markdown blocks in per-feature committed files and become epic issues
born unplanned — carrying the epic's own classification from birth plus a single backlog label
meaning "not yet planned". Both writers — oversized-scope decomposition and deferred scope at close —
file them through the existing batch filing path, and promotion populates that same issue in place
rather than creating a second one, so a piece of deferred scope keeps one issue number from the
moment it is deferred through planning, delivery and close. The twenty-two live stubs migrate in one
batch before the committed files are deleted last.
Chosen Approach
A stub is not a new species of issue. It is an epic that has been identified but not yet planned, so
it is filed as an epic and marked with one label denoting that unplanned state; everything else about
it — feature, estimate, candidate stories, originating intent, date — lives in the issue body's meta
block. Both writers reuse the one existing batch filing path with transient work-items rather than
growing a second filing component, which preserves the retry, resume-ledger and idempotency
behaviour that makes irreversible bulk creation safe. Promotion removes the label and fills in the
body; nothing is closed and nothing is created. The deferred-scope writer moves from the local
pre-checkpoint half of close into the post-checkpoint publishing phase, as its first step, because
issue creation is not reversible and because the close record must be able to name the resulting
numbers.
The identity consequence is the load-bearing one. Because the number survives promotion, every
reference written when scope was deferred — a dependency edge between sibling stubs, a mention from
the epic that spawned one, a line in a sequencing table — stays valid for the life of the work
instead of being orphaned the moment the stub is promoted.
Key Decisions
A stub is an epic issue born unplanned, not a separate kind of issue
declares, plus one label meaning "not yet planned". Promotion clears that label. No other kind of
issue is introduced.
destined to become exactly one epic. Modelling it as its own kind forces every consumer to learn a
third species, and forces the classification question to be answered twice: once for the label-mode
marker and again for the issue-type-mode marker, where no suitable type exists and none can be
minted without organisation-level administration. Filing it as what it already is answers both at
once and costs nothing.
issue type in type mode. It reads cleanly in a triage list and keeps unplanned work out of the epic
query by construction. It loses on three counts: the issue type cannot be created in a repository
whose organisation has not defined one, so the type mode degrades to filing stubs unmarked; it
forces a second identity when the stub is promoted, orphaning every reference to the first; and it
asserts a distinction that dissolves the moment anyone asks what a promoted stub became.
Exactly one label denotes the unplanned state; the feature lives in the body
not yet been planned; feature, estimate, candidate stories and provenance are body content.
filter — the two outcomes this epic is measured on.
renders directly in the issue list. It loses because the label set then grows with the feature
count, every new feature needs an upsert before its first stub, and exclusion costs one negation
per feature — precisely the noise this epic exists to remove.
The batch filing path takes its classification from the caller
takes the canonical classification — label in label mode, issue type in type mode — from its
caller, defaulting to today's story value so existing behaviour is unchanged. Stub filing passes
the epic classification.
would be classified as stories — wrong under any model, and doubly wrong here, where the whole
point is that a stub is already an epic.
applies the epic classification correctly. It loses because that path creates one issue at a time
with no resume ledger, no retry, and no dependency wiring — exactly the machinery a
twenty-two-issue migration and every multi-goal decomposition depend on.
Promotion populates the issue in place; it creates nothing and closes nothing
story sub-issues, and removes the unplanned label — all on the issue that already exists. The
single-epic filing path gains an update-in-place mode to support this.
reference to a stub breaks at the moment of promotion, which is precisely why the historical
dependency edges in this repository point at issues that are closed and superseded. One issue with
one number for the whole life of the work removes that failure mode rather than managing it.
naming its successor, which needs no change to the filing path. It loses because it reintroduces an
identity hop on every promotion, leaving a permanent trail of closed placeholders that every
reader and every dependency edge must indirect through.
A stub that proves oversized at promotion is closed as not planned, naming its successors
stub-epics and the original is closed as not planned with a comment naming them.
completed, which would assert delivery that never happened. Not-planned is the platform's existing
vocabulary for scope abandoned before it was built.
stub is never a parent of anything, and an epic that stays open while its scope lives entirely in
other issues has no closing condition.
A stub is never a sub-issue; its link to its originating epic is a body mention
the body, which the platform renders as a back-reference.
exemptions by kind. A stub filed by close as a sub-issue of the epic being closed would deadlock
the very stage that filed it — and under this model that stub outlives the close by design, since
it is the epic the deferred work will eventually be planned as.
native rollup view. It loses because the gate's single virtue is having no bypass — the first
exemption makes an unapproved decision record exemptible next.
Deferred-scope filing is the first step of the post-checkpoint phase, ahead of both the close comment and the close-record commit
section is finalised, and disclose it in the checkpoint's irreversible-actions list.
the filed numbers, and in pull-request mode the record is committed and pushed before the close
comment is posted. First-step-after-consent is the only position satisfying both.
deferred scope in prose. It loses because the record then either ships without numbers or must be
amended after it was already committed — reintroducing the two-copies problem this epic ends.
The issue number is the only identifier a stub ever has
dependency edges reference, and as the only input that promotes it. The title is a human-readable
goal summary. No slug is recorded and no slug resolves a stub. A batch-local reference token
survives only inside a single filing run's input, where edges between goals must be expressed
before any issue exists; it never reaches an issue body and never reaches the lead.
and the sole way to name a stub. An issue has an identifier, and under in-place promotion that
identifier is stable for the entire life of the work, so a second lookup key would have no source
of truth, no uniqueness enforcement, and no consumer.
open stub bodies, preserving the habit through the transition. It loses because the habit is worth
less than a single identifier space, and because a key nothing defines and nothing enforces drifts
silently the moment two stubs collide.
The flag selects the operation; the unplanned label decides whether it is legal
still carrying the unplanned label; pulling an already-planned epic is selected only by its
explicit flag. Any other input is a capability description. A number naming an already-planned
epic fails closed with a diagnostic naming the flag.
what is referenced — it must be stated. The label then answers a separate question: whether the
stated operation applies to an epic in that state.
entirely. It loses because the same command would silently do two different things depending on a
label a third party can remove, and because "plan this" and "load this" differ in consequence.
Reuse the one batch filing path with transient work-items; add no stub filer
batch filing path.
re-runs are exactly what irreversible bulk creation needs; they already exist and are exercised.
because it would duplicate the resume and idempotency machinery — the only reason filing
twenty-two issues in one shot is safe at all.
Dependency references accept a literal issue number, as a migration affordance
reference, resolved directly against the platform.
create-and-close model, whose stubs are now closed behind different issue numbers; those edges can
only be expressed as literals. Going forward the need largely disappears — because promotion
preserves the number, an edge written at decomposition stays valid through promotion without
rewriting — so this is a migration affordance and a convenience, not a structural pillar.
because it silently drops ordering the migration must preserve, and because accepting a literal
number is a small, generally useful extension rather than a special case.
Migration is one batch across all backlogs, and only proposed blocks migrate
blocks are left to git history and never refiled.
them only if they are filed together. Refiling closed history would add noise to exactly the query
this epic is built to make useful.
at a time. It loses on precisely those cross-feature edges.
The backlog surfaces as one link in one place, and the unplanned filter belongs on every epic query
the stage reports; feature navigation indexes gain no backlog section. Because stubs are epics,
every query that enumerates epics for planned work carries the negated unplanned filter, not only
the default triage view. Where deleting a backlog file empties its feature container entirely, the
container goes with it and is recreated at that feature's first epic filing.
issue bodies — brittle and misleading. No feature index links a backlog file today, so there is no
existing surface to repoint; one feature has no index at all and its container holds nothing but
its backlog file, and its stubs carry the feature path in their bodies regardless. The
epic-query cost is the accepted price of a durable identity, and naming it here is what stops it
being discovered later as a defect.
per-feature navigation path. It loses on brittleness, and on being several links that all mean the
same thing.
Constraints & Invariants
issue is introduced by this epic.
intent and date are body content, never labels.
missing label in a repository that has never seen it.
number a stub is filed under is the number its epic, its close, and every reference to it use for
the life of the work.
is never closed as completed.
the gate leaves the platform untouched.
and never duplicates an issue.
discovery in that case.
a repository containing no backlog file at all.
accepted as input.
present; loading an already-planned epic is selected only by its explicit flag; any other input
is a capability description.
naming that state, rather than failing on the absent planning meta block or empty story set.
never refiled.
one-line reduction of it.
have been repointed.
repository target through the shared publishing resolver; no call site hardcodes any of them.
the missing label before attempting any issue creation, so a permission gap can never leave a
half-filed batch.
Risks (BLOCKER / ADDRESS only)
ADDRESS — promotion in place requires an update path the epic filing surface does not have.
That surface is create-only, and the planning meta block is written at creation. Promotion must
instead populate an existing issue's body, write the meta block onto it, and file its stories
beneath it. Mitigation: add the update-in-place mode as part of the promotion story and treat "no
second issue exists after promotion" as its acceptance signal. This is the change that most
pressures that story's sizing, and it is the price of the durable identity — worth paying, but it
should be sized deliberately rather than absorbed.
ADDRESS — the batch filing path classifies everything it creates as a story. The story marker
is applied unconditionally rather than supplied by the caller, so stubs filed through it would be
classified as stories in both modes. Mitigation: parameterize the classification with today's value
as the default and pass the epic classification when filing stubs. It changes shared code other
stages depend on and needs its own regression check.
ADDRESS — stages that reconstruct an epic from its issue will meet epics that were never
planned. An unplanned stub has no planning meta block and no story sub-issues, which those stages
currently assume. Mitigation: guard with an explicit "not yet planned" refusal, and confirm no
enumeration of epics silently treats a stub as a planned one.
ADDRESS — migration's dependency graph points outside the batch and contains one stale edge.
Five proposed stubs depend on blocks promoted under the old model, whose issues are closed behind
different numbers, and one depends on an item recorded as abandoned and superseded by the
issue-sourced-planning work. Unresolved references make the batch report incomplete and exit
non-zero. Mitigation: resolve each outward edge to the issue number that work became, and rewrite
the stale edge to the epic issue that superseded it. This is judgement work that must precede the
migration run, and it is the last time it will be needed — once promotion preserves numbers, these
edges stop breaking.
ADDRESS — the close record is authored, and in pull-request mode committed and pushed, before
the publishing phase. Today's phase order assumes everything before the checkpoint is local and
reversible. Mitigation: finalise the record's deferred-scope section between filing and the commit,
and treat "the record names the filed numbers" as the acceptance signal.
ADDRESS — twenty-two irreversible creations in one run. A wrong body shape or wrong
classification is twenty-two corrections after the fact, not a revert. Mitigation: run the full
batch in dry-run and compare one rendered stub against a real stub filed by the first story before
the live run; the sequencing already places migration last, which makes that comparison possible.
Open Clarifications
None. Five were raised during analysis and resolved at the approval gate; their answers are folded
into the decisions above.
Story Coverage
Every user story in the epic is covered, with the scope edits noted.
Decomposition stubs are filed as backlog issues — covered by the stub-is-an-epic decision, the
single-label decision, the caller-supplied-classification decision, the filing-path reuse decision
and the surfacing decision; invariants 1, 2, 3, 7, 8, 9, 18, 19. Scope note: the right-size gate's
option text now discloses an irreversible platform write rather than a local file append, and the
stubs path writes nothing to the working tree — including the feature container, which is not
needed until an epic is planned.
Deferred scope at close becomes stub issues, filed after the checkpoint — covered by the
body-mention decision, the phase-position decision and the filing-path reuse decision; invariants
6, 7, 8, 17. Scope note: the close record's deferred-scope section is finalised between filing and
the commit, and the checkpoint's irreversible-actions list gains the filing.
A stub is promoted by issue number, and closing it is the promotion — covered by the
populate-in-place decision, the oversized-stub decision, the issue-number-identity decision and the
flag-selects-the-operation decision; invariants 4, 5, 10, 11, 12, 13, 14. Two scope edits. First,
the story's title and its promotion criterion are wrong under this design: closing the stub is not
the promotion — promotion populates the same issue and removes the unplanned label, and the only
close a stub ever receives is a not-planned close when it proves oversized. Second, retiring the
slug removes the three slug-matching criteria and replaces them with one, that any non-numeric
input is a capability description. Net: the criteria go from eight to six, and the story absorbs
the update-in-place work on the epic filing surface.
The committed backlogs are migrated and the files retired — covered by the literal-reference
decision and the one-batch/proposed-only decision; invariants 15, 16, 17. Scope edit: the
dependency graph is judgement work alongside the prose-heavy entries — five edges point outside the
batch at work promoted under the old model, and one stale edge is rewritten to the epic that
superseded it. Factual correction: there are thirteen promoted blocks, not twelve; twenty-two
proposed and three superseded are confirmed.
The backlog is one cross-feature query, and triage excludes it — covered by the single-label
decision and the surfacing decision; invariants 2, 17. Scope edits: the per-feature navigation
criterion is replaced by a single features-index entry linking the backlog query, since no feature
index links a backlog file today; the feature container holding nothing but its backlog file is
allowed to go with it; and the story gains the negated-filter requirement on every epic-enumerating
query, not only the default triage view. Added to this story: one line at the head of the
sequencing table naming the backlog query as the authoritative inventory, leaving its wave ordering
and rationale untouched.