Repository navigation
Schedule: the command line epic closes #887
Description
Deliverable. Epic #740 closes: every program in the
suite presents one command-line convention, every invariant §14 names has a test, and the specification says what
the code does. Worked by the session named devlore-cli | develop | pair:claude | Schedule: command line.
The problem
Fifteen of the epic's twenty-three leaves are done, and the work got there opportunistically: features were
picked up as they surfaced rather than in a committed order, and the epic's own plan says status: complete
while carrying an open box. A full audit on 2026-09-11 read every source the epic touches — its issues open and
closed, the specification end to end, every plan naming the convention, the invariant tests, the scenario
assertions, and the help text of all 143 commands across the four programs — and found 43 items, frozen as a
closure ledger. Eight are the
open leaves below; the rest are defects with no issue, placements into other epics, and documents that state
things the code does not do. Nothing has been added to that ledger since it was frozen, and nothing will be:
a new finding appends as row 44 and never renumbers.
The goal
The eighteen lanes below, in order, in four pull requests. The ledger's remaining rows
ride whichever pull request owns them. When the last merges, #740 closes and no re-reading reopens it, because
every source is already read.
Before → after
| What | You have today (2026-09-12) | You will have when this schedule closes |
|---|---|---|
| Exit codes | Three sources disagree: §9 says 0/1/2, the constants are sysexits with five gaps, and every failure on every program exits 1. star bypasses cli.ExitCode entirely |
The sysexits set as Declare-BashScript defines it, one source, and a subprocess test per program pinning a bad flag, an unknown command and a bad --output |
version |
Prints five lines of text on every program; --output yaml prints the same five lines |
A record through cli.Emit, so --output json parses and --output value gives the five values |
| The no-direct-stdout walk | Misses cmd.OutOrStdout(), so four sites reach stdout outside the pipeline and the walk stays green |
cmd.OutOrStdout() is called in cli.Emit and nowhere else, shown red on a fixture |
--silent |
Two ambient-runtime lines survive it, because the banner narrates before argv is parsed | --silent takes effect before the runtime is assembled |
| A report | Has no rendering: --output value prints the markdown source, markup and all |
--output markdown gives the document, --output terminal renders it for reading |
| The rendering set | Nine names in one alphabetical run, yaml sold as a human rendering |
Five groups -- Serialized, Records, Document, Composed, Nothing -- and the group names are the man-page headings |
| Long human renderings | Scroll off the terminal: star docs starlark, the command tree, a 775-line report |
Paged like git diff: only on a TTY, only when long, $DEVLORE_PAGER then $PAGER honored, --no-pager to refuse |
writ repo’s verbs |
add refuses a layer that is already registered, after its clone has completed; remove leaves writ’s own clone behind |
set registers or re-points, narrating the change and removing a writ-owned clone it displaces; unset removes both; add and remove are unknown commands |
A command’s Long |
Runs to 105 columns at COLUMNS=60; only flag usage wraps |
Wrapped, under a stated convention that keeps examples and indented lists verbatim |
star and devlore-test |
Have no generated CLI reference; the published site carries nothing about them | Generated with the rest, and a test asserts every in-scope program has a tree |
| §14's invariants | Six named, four enforced. Invariant 5 has no test at all, and #776's third invariant — every leaf of a root consumes the set — never shipped | Every invariant names a test that exists and asserts what the invariant says |
| The specification | Contradicts itself on adoption in three places, names an unexported symbol six times, and carries a broken godoc link | States what the code does |
Lanes
Leaves, in the order committed. One pull request closes several lanes. No lane is split once this schedule is
committed; if delivering a row above needs something not in these lanes, the deliverable has changed and we
reconvene to replan.
| Lane | Item | Next | Waits on |
|---|---|---|---|
| 1 | devlore-cli#795 — version emits a record |
PR A | — |
| 2 | devlore-cli#794 — the walk learns cmd.OutOrStdout() |
PR A | lane 1: the walk is red on version.go until its text write goes |
| 3 | devlore-cli#828 — --silent before argv is parsed |
PR A | — |
| 4 | devlore-cli#884 — the exit codes are the sysexits set | PR A | — |
| 5 | devlore-cli#826 — a document rendering: markdown, and terminal rendered from it |
PR B | — |
| 6 | devlore-cli#895 — the renderings group, and the group names are the headings | PR B | lane 5: terminal is the second member of the Document group |
| 7 | devlore-cli#830 — long renderings page | PR B | lane 6: the grouping decides what pages — Records and Document, nothing else |
| 8 | devlore-cli#897 — a group refuses a verb it does not have, instead of printing help and exiting 0 | PR C | — |
| 9 | devlore-cli#791 — repo set and unset replace add and remove |
PR C | lane 8: until a group refuses an unknown verb, writ repo add exits 0 with help and the rename is invisible |
| 10 | devlore-cli#792 — a writ-owned clone goes with its registration | PR C | lane 9: the displaced clone is set’s to remove, and #792 rides on whichever spelling lands first |
| 11 | devlore-cli#793 — a clone is named the way git clone names it |
PR C | lane 10: the clone that goes with its registration is the clone whose name changes; pulled forward from #894 on 2026-09-19 |
| 12 | devlore-cli#822 — file.mkdir obeys --conflict, as file.link and file.copy already do |
PR D | — (pulled forward from #894 on 2026-09-20: a real writ deploy stops on it on both virtual machines, and the Windows run carried from PR C needs a deploy that works) |
| 13 | devlore-cli#899 — the conflict policy's vocabulary: three names and no others, and one floor | PR D | lane 12: the same words, the same pull request |
| 14 | devlore-cli#900 — the winget driver reads winget's answer case-insensitively, as winget does | PR D | lane 12: its Windows proof cannot reach file.mkdir until the packages subgraph passes |
| 15 | devlore-cli#759 — a command's Long wraps |
PR D | — |
| 16 | devlore-cli#787 — star and devlore-test get a CLI reference |
PR D | — |
| 17 | devlore-cli#904 — the ledger snapshot compares etags and recomputes only what changed; a discovered directory is never Merkle-walked | PR E | — (found 2026-09-21: writ deploy spends 169 s hashing ~ at the end of every run) |
| 18 | devlore-cli#906 — the catalog and the ledger described in the architecture: what each holds, when a snapshot is taken, the etag/digest ladder | PR F | lane 17: the section records #904's rulings |
| 19 | devlore-cli#907 — file.mkdir claims a directory it found as a discovery, not a product; every deploy parent, home included, leaves the ledger as found |
PR E | lane 17: #904's third box (deploy under five seconds) cannot close without it |
PR A closes feature #832 and ledger rows 2, 4, 6, 22,
30, 31, 32, 33. PR B closes feature #833 and rows 12,
13, 14. PR C closes #897, #791 #792 and
#793, both under feature
#463 in another epic and placed here by the owner on
2026-09-15, so the command line work ends with the verbs this specification names. PR D closes feature
#834, rows 36 through 40,
and the epic: rows 41 and 42 correct the epic's plan and body, and #740 closes with the last commit.
Explicitly out — other deliverables, not deferred lanes
- Placements, ledger rows 19 through 27: work whose fix is entirely in
cmd/internal/clibut which is filed
under another epic — devlore-cli#780,
#763, #673,
#337, #83,
#518, #436.
PR A states where each belongs; moving them is their epics' work. - Ledger row 3, whether
devlore-docs,devlore-indexanddevlore-inventoryjoin the shared root. A
ruling that would add lanes rather than close them, and §2 already marks them "not yet". - Ledger row 16, the
!prefix in an interactive flow. Deferred by design since 2026-09-03. - Ledger row 17, plan-status drift across
docs/plans. Belongs to the process schedule. - Ledger rows 24 and 25, help text that names retired commands or promises unimplemented work, where the
command belongs to another epic — fifteenlorecommands and threestar keyleaves.writ repo add
andremovewere on this list until 2026-09-15; they are lanes 8 and 9. - Rows 44 and 45, found while working Command naming and package layout #835 and not yet placed:
--output tableorders struct columns
alphabetically where §8 says declaration order; and every document in the store verifiesuntrustedbecause
nothing seedsallowed_signers. - Long argument names, ruled 2026-09-12. The specification binds
-ofor--outputby design decision 1 and
CheckNoOwnOutputFlagenforces it. Retiring the short forms is a change to a shipped ruling and is the owner's.
How the report reads this
star gh issues report --by schedule --schedule "command line" finds this issue by the Schedule: title prefix,
reads the lane table, and renders each lane's live state. Edit this body to move a pointer; the edit history is
the schedule's history.
A schedule issue carries no Epic: label and no **Feature:** marker: it is a view of the work, not work
(issue-standards §Schedule).
Amended 2026-09-19 by the owner: lane 8, #897, inserted and the rest renumbered. Found while writing lane 9's retirement test: writ repo add on the renamed tree exits 0 and prints the group's help, because cobra's legacyArgs does unknown-command checking at the root alone. The rename is invisible without it, so it ships in the same pull request.
Amended 2026-09-19 by the owner, second: lane 11, #793, pulled forward from #894 and the rest renumbered. The layer-journey scenario, skipped on develop for want of repo set, ran on #898's first CI and asserted #793 in the same step as #791, on all six legs; the code follows the scenario, in the same pull request.
Amended 2026-09-20 by the owner: lane 12, #822, pulled forward from #894 and the last two renumbered. Ruled after PR C merged: the epic's remaining lanes are help and reference, and a writ deploy that stops at file.mkdir on every virtual machine blocks the end-to-end sequence every pull request runs.
Amended 2026-09-20 by the owner, second: lane 13, #899, inserted; #759 and #787 renumbered. Found planning lane 12: writ deploy's Examples and the guide name policies the flag rejects, and three comments disagree about the floor. Ruled: its own issue, resolved in lane 12's pull request, so #881 stays on the Planner sentence.
Amended 2026-09-21 by the owner: lane 14, #900, inserted; #759 and #787 renumbered. Found running lane 12's Windows sequence: the deploy stopped in the packages subgraph because the winget driver's post-check matched the id case-sensitively against winget's case-insensitive answer, so #822's occupant was never reached. Ruled: its own issue, resolved in lane 12's pull request.
Amended 2026-09-21 by the owner, second: lane 17, #904, appended. The epic closes on lane 16; lane 17 is a ResourceModel bug taken here because it was found running lane 16's own writ deploy and the owner ruled it addressed while fresh.
Amended 2026-09-21 by the owner, third: lane 18, #906, appended: the catalog and the ledger have no description in the architecture, which is how #904 stayed invisible; a documentation task under the resource model epic, taken while fresh.
Amended 2026-09-22 by the owner, fourth: lane 19, #907, inserted into PR E. Found running lane 17's local deploy: with the ledger ladder in place the deploy still took 182 s, because Mkdir stamps a directory it merely found as its product and the trace digests it as one. Ruled a catalog-contents bug whose fix is local to Mkdir; taken in lane 17's pull request because #904's acceptance depends on it.
Activity
Lanes 1 to 4: the shared root conforms
Lanes 1 to 4: the shared root conforms (#890)
David-Noble-at-work commentedon Sep 14, 2026
Lanes 1 to 4 closed by #890:
versionis a result, the no-direct-stdout walk seescmd.OutOrStdout(),--silenttakes effect before cobra parses, and the exit codes are the sysexits set. Feature #832 closes with them. Next: lanes 5 and 6 as PR B, lanes 7 and 8 as PR C.