spectastic Examples
Worked example · extended verb · explore

Vibe to learn, spec to keep

The other examples start where you already know what you're building. This one starts where you don't — and the honest move is to build something throwaway to find out. spectastic.explore is the verb for that moment: it quarantines a loose build, keeps the lifecycle from shipping it by accident, and offers exactly two exits — graduate or delete.

Extended verb TypeScript Quarantined Graduated
▷  Try the pathfinder Read the build →
The question

The intent was one line: find the shortest path between two nodes in a small weighted graph. But “shortest” is ambiguous the moment edges carry weight — does it mean the fewest hops or the least total weight? They're the same on an unweighted graph and can diverge sharply on a weighted one, and which one it is decides the algorithm. Rather than argue, this exploration builds both — BFS and Dijkstra — and runs them head-to-head to find out. The answer, grounded in a run, is what graduates into a spec.

The exploration

explore sits upstream of the eight core verbs — the on-ramp before spec. The ceremony is deliberately off (no requirement IDs, no INVEST), and a thin principles floor (P-1, P-2) stays on. You build to learn.

1

Scaffold the quarantine

spectastic explore "…" resolves a NNN-kebab id and writes two files under explorations/<id>/: a rich explore.html ledger (git-ignored — it's throwaway) and a small tracked quarantine.json marker. That marker is the anti-ship flag teammates and CI can see.

2

Build loosely — two approaches

Inside the quarantine, two single-file swings: bfs.ts (my gut — “shortest = fewest steps”, weight-blind) and dijkstra.ts (least weight). A probe.ts runs both on one graph — bare Node, no toolchain, no tests yet. This is the part the lifecycle usually forbids, done on purpose in a walled garden.

3

Discover the answer

The probe settles it. On the sample graph, shortest A→E: BFS returns A→C→E (2 hops, cost 25) while Dijkstra returns A→B→C→D→E (cost 6). They disagree by 19 — so “shortest” means least weight. Verdict: keep Dijkstra. See it below.

4

Graduate or delete — never ship as-is

An exploration has no path to a shippable state. You either graduate it (extract a spec + design from the build, restore the gates) or delete the directory. There is no “abandoned-but-lingering” state, and — until you pick — the guard holds the line.

The anti-ship guard

This is the mechanism that makes explore safe to hand a team. A quarantined exploration turns spectastic validate red — no matter what you point it at — and every core verb refuses to advance the id. You cannot ship a vibe by accident; CI goes red by design.

$ spectastic explore "find the shortest path between two nodes in a small weighted graph"
Wrote explorations/001-find-the-shortest-path-between-two/explore.html (git-ignored ledger) + quarantine.json (tracked marker).
 
$ spectastic validate 'specs/**/*.html'
explorations/001-find-the-shortest-path-between-two/quarantine.json
  1:1  error  Exploration is quarantined — un-graduated work must not ship.  explore-quarantined
1 error, 0 warnings
 
$ spectastic design 001-find-the-shortest-path-between-two
001-… is a quarantined exploration. Core verbs refuse to advance it —
graduate it into a spec, or delete it. (exit 2)

Two legs, both real: validate scans every quarantine.json on every run; the verb state-gate refuses each of the core verbs. The rich ledger stays local; the tiny marker is what teammates and CI actually see.

Graduate or delete

When you know the answer, you classify what the spike was — and that choice decides how it leaves the quarantine. This is the single most under-explained idea in the whole system, and a shortest-path toy is small enough to show both branches.

spikethrow the code away

The prototype taught you something, but the code isn't worth keeping. Graduation extracts a Draft spec; restore then emits a clean-rebuild task list — test-first, from zero — plus an explicit task to delete the archived prototype (never auto-run).

Here, the discarded bfs.ts was the spike-shaped arm — wrong for the job.

tracer-bulletkeep it, harden in place

The prototype was sound. Graduation extracts a Draft spec + design from the build, seeding the design's evidence ledger with the run's verified facts. Restore emits a refactor-to-comply list: move the kept code into place, back-fill tests, restore the gates.

This example took the tracer-bullet path — Dijkstra was kept.

$ spectastic explore --graduate 001-find-the-shortest-path-between-two --classify tracer-bullet
Graduated 001-… (tracer-bullet) → specs/001-…/spec.html
  exploration archived → explorations/archive/001-…/ (frozen)
  Next: review the Draft spec + design, then /spectastic.tasks 001-… --restore
 
$ spectastic validate 'specs/**/*.html' 'principles.html'
no findings # the marker flipped quarantined → graduated; the guard clears

Play · read · trust

The payoff, three ways. Play the discovery in your browser — watch BFS and Dijkstra disagree; read the kept build; trust the tests that graduation demanded.

PlayBFS vs Dijkstra — the ambiguity, live

The same weighted graph the spike used. Pick an algorithm, then click any node to set the destination (start is A). Watch where fewest-hops and least-weight take different routes — and cost you differently.

Result
Total weight 6

A browser re-creation of the spike's probe.ts, for illustration. The real thing is dijkstraPath — proven by the tests below, not by this widget.

ReadThe kept build, and the ledger that grounds it
The finder — least weight, not fewest hops
/** Shortest path by TOTAL WEIGHT. Returns null if goal is unreachable. */ export function dijkstraPath(g: Graph, start: Node, goal: Node): Node[] | null { const dist = new Map([[start, 0]]); const prev = new Map([[start, null]]); const frontier = new Set([start]); while (frontier.size) { // pop the frontier node with the smallest known distance (linear scan) let node = null, best = Infinity; for (const n of frontier) { const d = dist.get(n) ?? Infinity; if (d < best) { best = d; node = n; } } if (node === null || node === goal) break; frontier.delete(node); for (const { to, weight } of g.get(node) ?? []) { const alt = best + weight; if (alt < (dist.get(to) ?? Infinity)) { dist.set(to, alt); prev.set(to, node); frontier.add(to); } } } // …reconstruct the path from prev, or null if goal was never reached }
→ kept from the spike, unchanged; the linear-scan frontier is correct, with a heap deferred. spec 001 · FR-001, design D-001/D-002
The ledger's run block — what graduation promotes to verified
<spec-runblock> <spec-run>node probe.ts</spec-run> <spec-demo>On the weighted sample, shortest A → E: BFS returns A → C → E (2 hops, cost 25) while Dijkstra returns A → B → C → D → E (cost 6). They disagree by 19 — proving "shortest" means least weight.</spec-demo> </spec-runblock>
→ at graduation, this run's proven facts become verified rows in the design's §3 evidence ledger, citing the archived exploration. explore.html §3
TrustThe widget illustrates. This is what graduation demanded.

A spike has one throwaway assertion. The tracer-bullet's restore made it earn a real suite — the probe's fact, pinned to the spec's criteria.

$ node --test  # Node 25, native TypeScript — no build, no deps
   SC-001 · shortest A→E is the least-weight path, not the fewest-hops one
   SC-001 · the returned path is a true minimum over all A→E paths
   SC-002 · an unreachable goal returns null, not a partial path
   FR-003 · start equals goal is the single-node path at cost 0
   NFR-001 · fewest-hops and least-weight genuinely diverge on the sample
  ──────────────────────────────
  tests 5   pass 5   fail 0

The discarded BFS survives only as a test witness — the NFR-001 case computes the hop path and asserts it costs strictly more than Dijkstra's, so the criterion can't quietly pass on a graph where the two happen to agree.

The artifacts

The graduated Draft bundle — a spec · design · tasks triple — plus the frozen exploration record it came from. Every artifact passes spectastic validate at the standard profile, and the archived spike still runs.

001 · least-weight path
The graduated Draft — extracted from the kept build (tracer-bullet)
↳ exploration
The archived ledger — intent, what I built/tried/worked/didn't, and the run block. Read the ledger →
▷ node --testThe TypeScript source — 5 tests, green · view the source on GitHub ↗

These are the real, rendered artifacts — the same files spectastic validate checks; the excerpts above are drawn from them verbatim.

▷  Try the pathfinder View source on GitHub ↗ ← All examples