Skip to content

feat: multi-candidate pins — ordered candidate list resolved by a scoped picker#16

Merged
vessux merged 12 commits into
mainfrom
feat/15-multi-candidate-pins
Jun 10, 2026
Merged

feat: multi-candidate pins — ordered candidate list resolved by a scoped picker#16
vessux merged 12 commits into
mainfrom
feat/15-multi-candidate-pins

Conversation

@vessux

@vessux vessux commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Summary

Generalises the .umbel-bundle pin from a single value to an ordered list of candidates (one bundle name per line), implementing the design of record in docs/adr/0007-multi-candidate-pins.md (terminology in CONTEXT.md).

  • One candidate → direct launch (a one-line pin is byte-compatible with old pins; common case unchanged). Many candidates → a scoped picker restricted to just those candidates. The first candidate is the default — pre-selected in the scoped picker and resolved automatically in non-interactive shells.
  • File grammar (pure parsePin): trim each line, skip blanks, dedupe preserving first occurrence, full-line and inline # comments stripped. __vanilla__ is an explicit candidate (renders a (vanilla) row only if listed). An empty/all-commented pin ≡ absent (never an error).
  • Precedence unchanged: arg > UMBEL_BUNDLE > pin; arg/env bypass the picker.
  • umbel apply stays single-candidate/vanilla and now refuses (exit 2) to overwrite a hand-authored multi-candidate pin (hint: run umbel unpin first). Candidates build lazily; the scoped picker is ephemeral (a selection resolves the launch only, never rewrites the pin).
  • umbel list marks every candidate in the PINNED column (yes, default yes* + footnote) — which also fixes the column never marking at all.
  • umbel show / build keep the full picker, pre-selecting the default candidate.
  • Docs: README.md and docs/bundles-spec.md updated to describe the list format, comments, scoped vs full picker, and default-candidate semantics.

Closes #15
Closes #5

Test plan

  • npm test — 337 tests pass (37 files)
  • npx tsc --noEmit clean · npx biome check . clean · npm run build OK
  • New unit tests: parsePin grammar (exhaustive), readPin/isMultiCandidatePin, resolveBundleName multiple-kind + arg/env precedence, scopedPickerOptions builder, run non-TTY default resolution + candidate-order semantics + vanilla-default + unknown-default exit 3, apply overwrite guard, list candidate marking
  • Manual smoke on the built dist/cli.js: multi-candidate pin → list shows yes*/yes + footnote; non-TTY run builds the first/default candidate (reorder flips it); apply over a multi-candidate pin refused (exit 2), pin left untouched

Notes

Built TDD, task-by-task, with per-task spec + code-quality review and a final whole-feature review. No production behaviour outside the pin/picker/list/apply paths changed; back-compat for one-line pins is preserved by the per-line parser.

vessux added 12 commits June 9, 2026 14:31
Finish adopting the delivery-superpowers method on this repo. Its
delivery-superpowers-locations hook redirects superpowers' specs/plans to a
gitignored .local/superpowers/ root (the vendored skill default would commit
them under docs/superpowers/). Ignore .local so those artifacts never get
committed; the SessionStart gate (`git check-ignore -q .local`) now passes.
No trailing slash, so the check matches before the directory exists.

Tier (.repo-visibility=public), beads, ADRs, and PR template are already in
place from the discovery/delivery-base setup. Pinning delivery-superpowers
alongside discovery awaits multi-candidate pins (#15); until then the local
pin stays on discovery and delivery runs via UMBEL_BUNDLE=delivery-superpowers.
@vessux vessux merged commit 7988625 into main Jun 10, 2026
4 checks passed
@vessux vessux deleted the feat/15-multi-candidate-pins branch June 10, 2026 11:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Multi-candidate pins: ordered candidate list resolved by a scoped picker umbel list never marks the pinned bundle (PINNED column always '—')

1 participant