Skip to content

Tmp-First Analyze & Close Artifacts, with GitHub Fallback for Distill #170

Description

@sameera

Epic: Tmp-First Analyze & Close Artifacts, with GitHub Fallback for Distill

⚠️ Utilization risk: assessed L (1–2 weeks). Fills the sprint with no slack for overruns —
watch for scope creep, particularly in the distill fallback story.

Description

Under issue-sourced planning (#114), a local (non---pr) run of /nxs.analyze already resolves
an epic it doesn't find committed by materializing it under the gitignored .nexus/tmp/, and writes
analyze-receipt.md beside it there — nothing to commit, nothing durable, by design. /nxs.close,
however, still talks about writing close-record.md "into the committed queue entry" even when the
entry it inherited is that same .nexus/tmp/ materialization. There is no born-at-close mechanism
for the local path (that only exists for --pr mode, in Phase 0.5), and in single-repo or hub mode a
local close never runs git commit itself — so today, a local close against an issue-sourced epic
produces a close-record.md that the spec calls "committed" but that no step ever actually commits.
(Member mode is the exception: its Phase 7.5 migration does commit the entry, into the hub queue —
Story 5 carries that path.) /nxs.distill then has nothing reliable to drain: it scans only the
committed queue for a close-record.md.

This epic makes the local, same-sitting flow the primary case it should already be: the same
person runs /nxs.analyze then /nxs.close then /nxs.distill back to back, so analyze-receipt.md
and close-record.md are legitimately transient — they exist to hand off from one command to the
very next, not to survive to a different session or machine. Both land in .nexus/tmp/ and
/nxs.distill looks there first, consuming the entry directly without requiring anyone to commit a
throwaway file. --pr mode is materially unaffected — it already commits close-record.md onto the
distill branch (Phase 7.6) because those artifacts have no feature PR to ride, and /nxs.analyze --pr
already publishes its result as a PR review rather than a file. Those two mechanisms stay as they are.

The gap this closes is recovery when .nexus/tmp is gone — a different machine, a cleared tmp
directory, a distill run days after the close. For a --pr-closed epic, /nxs.distill can already
fall back to durable GitHub state: the analyze verdict lives in the PR's published review, and the
close record's Key Decisions + Deviation Rationale prose is already posted verbatim on the epic
issue's close comment (/nxs.close Phase 8.2). This epic wires that fallback into distill's entry
discovery. For a local, non---pr close, there is no PR to fall back to — if .nexus/tmp is gone,
distill has no durable copy of the close record's rationale to recover. This epic requires distill to
report that condition precisely (name the entry, name why it can't be drained) rather than silently
mistaking it for "not yet closed" or fabricating an empty rationale; the exact resolution (a hard
block vs. a degraded close-record-only drain) is a judgment call for /nxs.decision-record to settle
against this epic's invariants, not something decided here.

Success Metrics

  • A local close of an issue-sourced epic (/nxs.analyze/nxs.close, no --pr) in single-repo or
    hub mode produces zero new files in the committed queue — analyze-receipt.md and close-record.md
    both live under .nexus/tmp/ — and nothing requires a manual git add/git commit to hand off to
    /nxs.distill. (Member mode still migrates its entry into the hub queue, as today.)
  • A tmp-resolved entry is drained exactly once: after its distillation-PR merges, a later
    /nxs.distill run does not rediscover it.
  • /nxs.distill run immediately after such a local close drains the entry with no extra flags and no
    prior commit, in the same sitting.
  • /nxs.distill --recover <epic-issue> run against a --pr-closed epic whose .nexus/tmp is empty
    (fresh checkout, cleared tmp) recovers the analyze verdict and the close record's rationale from
    GitHub, with no content loss versus draining it fresh off the worktree. (Amended at implementation
    per record Decision Record: Tmp-First Analyze & Close Artifacts, with GitHub Fallback for Distill #176: recovery is an explicit per-entry path, never a discovery source — an ordinary
    no-argument run does not scan closed epic issues.)
  • /nxs.distill never silently drains an epic with fabricated or missing rationale, and never
    silently treats a genuinely-closed-but-unrecoverable local entry as "not yet closed."

Personas

Per docs/product/context.md. The Secondary — Solo developer persona is the direct motivator for
the local same-sitting flow (Description); the Primary — Engineer on a small team persona is the
one who hits the tmp-cleared / different-machine recovery case this epic's distill fallback covers.

Assumptions

  • --pr mode's existing artifact placement (PR review for analyze, committed-then-git rm'd
    close-record.md on the distill branch for close) is materially correct and stays as-is; this epic
    only adds tmp-first lookup and GitHub-fallback discovery on top of it for /nxs.distill, it does
    not change what --pr mode writes or where.
  • "Same sitting" in local mode means the same machine, uncleared .nexus/tmp, no requirement that it
    be the same terminal session — .nexus/tmp persisting across a wrapper restart is treated as normal,
    not as the "different machine" recovery case.
  • Old-contract entries (a committed epic.md predating Issue-Sourced Planning: Nothing Commits Until Close #114) are out of scope for behavior change —
    every story above preserves their existing committed-artifact path unchanged.
  • Member mode keeps its durable end-state. A member-repo close is always non---pr, and its
    Phase 7.5 migration commits the entry into the hub queue. Moving the local artifacts under a
    gitignored path must not break that migration or leave a second, separately drainable tmp copy
    behind (Story 5). "Ephemeral" describes where a member close writes, never where it ends.

Out of Scope

  • Changing what --pr mode writes or where (Phase 0.5 / Phase 7.6 of /nxs.close, or the PR-review
    publish step of /nxs.analyze) — only local-mode placement and distill's discovery/fallback change.
  • Any change to the decision-record's own storage or its hash-verification contract.
  • Deciding, at the epic level, whether the unrecoverable-local-entry case (Story 4's last AC) is a hard
    block or a degraded drain — that judgment belongs to /nxs.decision-record.

Open Questions

None.

Metadata

Metadata

Assignees

No one assigned

    Labels

    epicEpic (created by nxs-gh-create-epic)in-progressDesign filed; approval is the close of the record sub-issue

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions