Skip to content

lore adopts the output convention, and search stops cutting descriptions mid-rune - #779

Merged
David-Noble-at-work merged 1 commit into
developfrom
feature/775-lore-adoption
Sep 2, 2026
Merged

David-Noble-at-work merged 1 commit into
developfrom
feature/775-lore-adoption

Conversation

@David-Noble-at-work

@David-Noble-at-work David-Noble-at-work commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Closes #775
Closes #741

lore registered the common set on inspect alone — a stub — so the convention was learnable on one
command out of fourteen. Every other command invented its own output, and the one real table in the tree
was hand-rolled. Five phases; zero stdout writes remain in cmd/lore.

Acceptance criteria

The canonical copy is the plan's own section —
775-lore-adoption.md § Acceptance criteria.
Every box is checked before merge.

Goals

  • lore's root registers the common set, so every subcommand accepts every flag —
    TestRoot_EverySubcommandAcceptsTheCommonSet walks the tree
  • No lore command carries an output flag of its own — the same test, on each command's local flags
  • The search table is the shared one — searchRows through TableFormatter

Test rows

  • 1 — every subcommand accepts the common set: TestRoot_EverySubcommandAcceptsTheCommonSet (unit)
  • 2 — lore search -o table renders through TableFormatter: TestSearchRows_KeepsMultiByteDescriptions
    (unit)
  • 3 — a multi-byte description is not cut mid-rune: the same test, 47 ASCII bytes then é — lore search truncates descriptions by byte count, cutting multi-byte characters mid-rune #741's
    verification
    , red by construction before the fix
  • 4 — no lore command registers --output, --format or --json: the root test (unit); tree-wide is
    Enforcement: two tests that fail when a command leaves the output convention #776's by design
  • 5 — lore <cmd> -o none writes nothing to stdout: zero fmt.Print/os.Stdout remain non-test; the
    per-package test is Enforcement: two tests that fail when a command leaves the output convention #776's by design
  • 6 — onboard has no --format/--output; the directory is its positional: the two
    ParseLoreOnboardConfig tests (unit)
  • 7 — onboard -o json emits the discovery, -o none leaves only the file:
    TestOnboardResult_EmitsThroughThePipeline pins the shape (unit); the two-line wiring is read by eye,
    since onboard.Run needs an AI provider
  • 8 — the suite passes on all five platforms: darwin-arm64 locally, and on the pull request
    test (darwin-arm64), test (linux-amd64), test (linux-arm64), test (windows-amd64), test (windows-arm64) — all green

Gates, every phase: make build, make vet, make test, gofmt -l empty, Test-GuideFrontmatter.sh.

The five phases

  1. The root registers the set; inspect stops being the one adopter. emitResult and outputOptions
    mirror writ's.
  2. Search is a result. Four fmt.Print become searchRows → the shared table, which aligns by rune and
    truncates nothing. That is where lore search truncates descriptions by byte count, cutting multi-byte characters mid-rune #741's desc[:47] cut goes away.
  3. A destination is a positional operand. bundle @<manifest> <output>; onboard --from <source> [<dir>].
    The ruling is in 10-command-line-interface.md §4. onboard's --format was dead — the parameter it
    fed was _ — and is deleted, not renamed. list's --format goes too.
  4. onboard emits its result. The file write is the side effect; onboard.Result is what -o renders.
    Before this, stdout was silent.
  5. The nine other fmt.Print are narration — progress and a y/N prompt during mutating commands, and
    builder.go's Starlark [print] — all to cli.* on stderr.

Also here

  • The two lore guides that taught --format now say -o
  • The status document ticks lore's box; the conformance table moves lore to "none"
  • The plan was corrected in its own first commit: two findings it did not know, and the destination ruling

Informational

The search table's columns are alphabetical now — confidence, description, installed, name, source, version — because stage-1 normalization sorts map keys, the same as writ reconcile's table. The old
PACKAGE SOURCE CONF … order was the hand-rolled formatter's and went with it.

https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW

…ons mid-rune

Closes #775
Closes #741

lore registered the common set on `inspect` alone -- a stub -- so the convention was learnable on one
command out of fourteen, and `lore search -o json` was an unknown flag. Every other command invented its
own output, and the one real table in the tree was hand-rolled. Five phases; zero stdout writes remain.

THE ROOT REGISTERS THE SET. AddOutputFlags moves from inspect to the root, so every subcommand inherits
--output, --filter, --jq and --store, and a fix in cmd/internal/cli reaches all of them at once.
emitResult and the package-level outputOptions mirror writ's. TestRoot_EverySubcommandAcceptsTheCommonSet
walks the tree and refuses any command that registers --output, --format or --json of its own.

SEARCH IS A RESULT. runSearch's four fmt.Print become searchRows -> emitResult: a row per result with
json-tagged fields, rendered by -o table through the shared TableFormatter, which aligns by rune and
truncates nothing. That is where #741 goes away -- the old loop cut descriptions at desc[:47], through
the middle of any multi-byte character that landed there. TestSearchRows_KeepsMultiByteDescriptions
puts 47 bytes of ASCII and then an e-acute through the row and the table, and requires both intact. It
was red by construction: searchRows did not exist, and the loop it replaced truncated. `installed` is a
column rather than an asterisk glued to the name.

A DESTINATION IS A POSITIONAL OPERAND. bundle becomes `bundle @<manifest> <output>` and onboard
`onboard --from <source> [<dir>]`, the directory defaulting to `.` -- per the ruling recorded in
10-command-line-interface.md §4 two commits ago. Both were stubs or nearly so; the flags that collided
with -o are gone. list's --format is gone with them.

ONBOARD'S --format WAS DEAD, and is deleted rather than renamed: generateManifest took it as `_` and
always wrote packages-manifest.yaml. The Format field is gone from loreOnboardConfig and onboard.Options,
the parameter from generateManifest, and the assertion from the test that carried it.

ONBOARD EMITS ITS RESULT. onboard.Result -- product, vendor, version, complexity, concerns, slots, the
manifest text -- was narrated to stderr and stdout was silent. The file write is the side effect; the
Result now goes to emitResult, so `-o json` yields the discovery and `--jq '.product'` selects it.
TestOnboardResult_EmitsThroughThePipeline pins the shape; the two-line wiring is read by eye, because
onboard.Run needs an AI provider.

THE NINE OTHER fmt.Print ARE NARRATION: "Resolving packages...", the resolution status table, the
LOW-confidence warning, the y/N prompt, "Deploying packages...", and builder.go's Starlark [print] --
progress and interaction during commands whose result is their exit. All to cli.* on stderr.

The two lore guides that taught --format now say -o. The status document ticks lore's box; the
conformance table moves lore to "none". The plan carries the acceptance criteria; one row is CI's.

make build, make vet, make test (104 ok, 0 FAIL), gofmt -l and Test-GuideFrontmatter.sh are green.

Claude-Session: https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW
@David-Noble-at-work David-Noble-at-work self-assigned this Sep 2, 2026
@David-Noble-at-work
David-Noble-at-work merged commit ee754fa into develop Sep 2, 2026
12 checks passed
@David-Noble-at-work
David-Noble-at-work deleted the feature/775-lore-adoption branch September 2, 2026 03:44
David-Noble-at-work pushed a commit that referenced this pull request Sep 2, 2026
…rulings

The CLI design said star bound its own cmd/star/cli.AddOutputFlags; nothing imported the copy, and star's
root bound no output flags at all. Section 15 still analyzed writ reconcile's one-bool bridge as a live
defect two paragraphs after its own table said writ had none; #774 removed the bridge on 2026-08-31, and the
passage is now history with what pins it. Section 7's domain-rendering example, lore list --format manifest,
went with #779. Sections 2, 9 and 12 and decisions 7-9 now carry the 2026-09-02 rulings:
the shared root's commands are one set on the four programs with a program's additions attached beneath;
no usage text follows any error; man is the one route to man pages. star-extensions.md listed a
cmd/star/cli/selfinstall.go that never existed. The status doc and the 740 epic plan record lore's three
phase-4 boxes as landed in #779 and star's deletion as landed in phase 2. The config status doc gains the
command-surface row, the show/sync question, and the two-source discrepancy; the phase-8 implementation plan's
star-unification item records that the command surface was ruled ahead of it and what the fold adds beneath.

Refs #740, #743, #441

Claude-Session: https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW
David-Noble-at-work added a commit that referenced this pull request Sep 3, 2026
…mands as commands (#783)

* Add the plan for #743: star adopts the output convention

The survey changed its shape: cmd/star/cli is dead (main.go imports cmd/internal/cli), renderTable's
promotion already happened in #740 phase 4, and the real work is moving star's hand-built root onto
cli.NewRootCmd, which has no pre-run hook and so is wrapped. Ten stdout sites the issue did not list are
classified; star docs starlark is the epic's json-default question made concrete and is logged, not
decided.

740's thread table and 776's related documents now link the plan file, as they do for 775.

Refs #743

Claude-Session: https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW

* Plan #743 is a draft until it is reviewed

The plan landed with status in-progress because the worktree script was rewritten while it was being run.
A plan that has not been reviewed is a draft; it moves to chartered on review.

Refs #743

Claude-Session: https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW

* Charter the plan for #743: LKG taken, three more collisions, three rulings

Reviewed 2026-09-02. star is the code generator every make target runs through, so the plan carries the
last-known-good process the Makefile documents: build/star.lkg taken before the first edit (done, from
1aaba0f, then `star self install`), retired after the tree builds from source, make regenerate no diff,
make install. Phase 1 is complete.

Checking the install found three collisions with the shared root the survey missed: star registers
version and self itself, and its config group comes from two Starlark extensions. RootConfig gains
post-install and post-uninstall hooks so star's extension install survives the move.

Three rulings, one rule -- the shared route on all four programs, a program's own additions attached
beside it:
  config: the shared set on all four; star's show and sync attach beneath.
  man: the shared `man` is the one route; star's docs man and docs markdown go; docs starlark stays.
  usage text: never on an error, not even a bad flag or an unknown subcommand -- cobra's dump is not
    context-aware; SilenceUsage: true on the shared root, once, for all four programs.

Taking the install also exposed #780, its own worktree.

Refs #743

Claude-Session: https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW

* Config spec and plan: the command surface is one set on four programs

The 2026-09-02 ruling from #743, recorded where configuration is designed: devlore-test, lore, star and
writ carry the same `config` command, the shared root's, and star's show and sync attach beneath it. The
spec gains a "command surface" section stating the ruling, the two sources star's config reads today, and
that `self install` produces each program's default config (#780). The 441 plan gains the status row, the
observation that the surface is uniform before the model beneath it is, and one open question: do show and
sync join the shared set.

Refs #441, #743

Claude-Session: https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW

* Delete cmd/star/cli, the dead copy of cmd/internal/cli

Nothing imported it: main.go imports cmd/internal/cli, and star's root bound no output flags from either
package. The two comments that named it are corrected. The renderTable it carried had already been promoted
to pkg/result.TableFormatter in #740 phase 4.

#743 phase 2; the plan's Phase 2 is complete.

Refs #743

Claude-Session: https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW

* Align the CLI and config design docs with #743 phase 2 and the three rulings

The CLI design said star bound its own cmd/star/cli.AddOutputFlags; nothing imported the copy, and star's
root bound no output flags at all. Section 15 still analyzed writ reconcile's one-bool bridge as a live
defect two paragraphs after its own table said writ had none; #774 removed the bridge on 2026-08-31, and the
passage is now history with what pins it. Section 7's domain-rendering example, lore list --format manifest,
went with #779. Sections 2, 9 and 12 and decisions 7-9 now carry the 2026-09-02 rulings:
the shared root's commands are one set on the four programs with a program's additions attached beneath;
no usage text follows any error; man is the one route to man pages. star-extensions.md listed a
cmd/star/cli/selfinstall.go that never existed. The status doc and the 740 epic plan record lore's three
phase-4 boxes as landed in #779 and star's deletion as landed in phase 2. The config status doc gains the
command-surface row, the show/sync question, and the two-source discrepancy; the phase-8 implementation plan's
star-unification item records that the command surface was ruled ahead of it and what the fold adds beneath.

Refs #740, #743, #441

Claude-Session: https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW

* star moves onto the shared root, and its extension commands become commands

The hand-built root goes: cli.NewRootCmd supplies the common set, --dry-run and --silent, and config, man,
self and version. star wraps the shared pre-run to copy --dry-run into starruntime.DryRun, refresh its flag
map and point the runtime's status at the one narrator. RootConfig gains post-install and post-uninstall
hooks so star's extension install survives the move; the shared root sets SilenceUsage, so no program prints
the usage block after an error; schema.StarDefaultConfig seeds config.d/star.yaml like the other three.

Three Starlark extension commands bound flags the common set owns. The loader now refuses a reserved name.
A script's return value is its result: Command.Run returns it as JSON-shaped Go and the leaf emits it through
the pipeline. generate takes the provider's package as its one operand -- the files land inside it, so
--source, --output, --write and --gen all go, and --dry-run is the preview; the 30 Makefile recipes follow.
validate returns its report and --format goes with its markdown printers. extract writes both artifacts
and --format goes. Extension commands register in name order, so setup no longer appears twice by chance.

The LKG is retired in this commit's gates: build from source, regenerate with no diff, test, install.

#743 phase 3; the plan's Phase 3 is complete.

Refs #743

Claude-Session: https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW

* star's last eight stdout sites: narration narrates, a result emits, a stub fails

The two Starlark print handlers narrate through cli.Note, as lore's does, so --silent gates a script's
print() with everything else. docs starlark returns its guide as a result and emits it through the pipeline;
whether a prose result should default to value is logged on #740. The three key leaves, unimplemented until
ADR-040, fail with an error that says so rather than printing a note and exiting 0 -- the plan had classed
them as narration, and the checklist's rule against stubs that report success overrides that.

No fmt.Print or os.Stdout remains in cmd/star outside tests.

#743 phase 4; the plan's Phase 4 is complete.

Refs #743

Claude-Session: https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW

* Ambient, not non-hermetic

The opposite of hermetic is ambient: a runtime that takes its context from what surrounds it. "Non-hermetic"
named an absence. Eleven lines in six files -- the scripting runtime's banner and its comment, the execution
runtime's note and comment, two test names, the method-classification table, and two plan documents -- now
say ambient, and 2.4-hermeticity-guarantees.md defines the pair once beside hermetic.

#743 phase 5; every phase of the plan is complete.

Refs #743

Claude-Session: https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW

* CI: the dry-run test hands the generator a slash-separated package, and goValue asserts no type

Both Windows test legs failed because the test built the operand with filepath.Join, and the generator
wrote pkg\op\provider\json into the package clause. A Go package path is slash-separated on every
platform; the test says so and reads the files through the platform separator. quality-gate flagged the
one unchecked type assertion in goValue; the list and tuple cases are now separate and assert nothing.

Refs #743

Claude-Session: https://claude.ai/code/session_01SqQVFZz7AdiLZkGoAcUqRW

---------

Co-authored-by: David Noble <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant