Skip to content

Latest commit

 

History

History
466 lines (438 loc) · 39 KB

File metadata and controls

466 lines (438 loc) · 39 KB

SpecKit backlog

Running list of follow-ups, so nothing gets lost between sessions. Open work is ordered by priority (P1 = next build → P6 = gated release); completed work lives in Completed at the bottom. Status: ✅ done · 🔄 in progress · ⬜ todo · 🔒 blocked.


P1 · ✅ Shipped — GitHub Pillar 1 (PR gating)

Spec-honesty is now a non-bypassable required check. Next build is P2.

  • Web-scaffold quality tasks (prerequisite). fmt/fmt:check (Oxfmt) + lint (Oxlint) mise tasks in the web scaffold, scoped to app/; pnpm pinned in [tools]. .oxfmtrc.json + .oxlintrc.json mirror the Vite+ reference (tanstack-react-start-contacts): Oxfmt with tabWidth 4 / printWidth 100 / arrowParens avoid / perfectionist import sort / sortTailwindcss / sortPackageJson / jsonc+.vscode overrides; Oxlint with type-aware rules + jsPlugins (perfectionist, prefer-let) + import/extensions. Deps: oxlint oxfmt oxlint-tsgolint eslint-plugin-perfectionist eslint-plugin-prefer-let. Green-on-arrival verified for real (oxfmt --check + oxlint + tsgo all exit 0 on a freshly rendered scaffold with the full dep set).
  • The gate Action + ci.yml. Composite action gate/action.yml (markmals/speckit/gate@v1) + reusable workflow .github/workflows/gate.yml; the scaffold's github/ subtree drops a thin-caller .github/workflows/ci.yml via the new scaffold.RenderGitHub (skip-existing) wired into target add. Two jobs: quality (mise tasks) + verify (the gate; runs the suite, so no double-run). Gate emits CI annotations via the new specify gate … --format github (mirrors oxlint --format github). Deviation from the design doc, with rationale: the CI sequence is scan → gate firewall → verify → parity --gate; gate generated/gate scope are git hooks, not PR checksverify legitimately rewrites committed locks on green (a generated PR check would false-positive), and scope validates a single commit subject.
  • Branch-protection recipe — documented gh ruleset fallback in docs/ci-gating.md (required contexts quality + verify / verify, PR required, force-push blocked). specify-native provisioning rides on the gh-extension auth (P2).

Needs a live PR to validate (can't run an Actions runner locally): the mise/pnpm toolchain setup + trust in CI, the firewall --against base-SHA diff, and the @v1/go install …@v1 references (dormant until the first release tag, P6).

Fast-follows (richer annotations — captured under P4): line-level firewall annotations (bindingsInContent is regex-only, no line numbers today); and --format github on verify/parity so unjoinable scenarios, dangling bindings, and drifted specs also annotate to file:line — needs the report structs to carry the spec/test file path.

Design: docs/design/github-integration.md.


P2 · ✅ Shipped (mostly) — GitHub-native core + agent memory

The pivot's heart. Architecture: a portable spec-integrity core (engine works offline, never needs GitHub for correctness) + a GitHub-native workflow shell; the engine projects repo truth onto GitHub. Determinism line: specs/locks/parity/agent-memory stay in the repo; defects/work/gating are ephemeral on GitHub. The whole GitHub surface lives in internal/github, imported only by cmd/specify — never by the engine, so the offline guarantee holds structurally.

  • GitHub foundation + gh-auth inheritance. New internal/github package: a lean REST + GraphQL client that inherits gh's token (gh auth token, env fallback) with zero plumbing and contains its own GraphQL queries (the gh-projects approach, our queries — no go-gh dep, no external extension). Repo auto-detected via gh repo view; no config block. gh ≥ 2.94.0 + op pinned in mise.toml (repo + web scaffold). Fully unit-tested (httptest). Deferred to release (P6): the actual gh extension install markmals/specify distribution (running as gh specify) — the binary + commands are ready; only the release-tagged install path is dormant.
  • Pillar 2 — Issues as ephemeral defect intake. specify issues list|create|close (confirmation-gated, --json); the github/ scaffold subtree now drops the full per-target .github/ (PULL_REQUEST_TEMPLATE.md, ISSUE_TEMPLATE/defect.yml stamping type: Bug + label fallback, config.yml, CODEOWNERS for /features /specs). Dependency updates moved off Dependabot to a single repo-global Renovate gate wired into the mise-monorepo root (mise run deps/check, renovate.json + scripts/deps-check.sh; advisory dry-run, never opens PRs — one ecosystem-agnostic gate for all members). No durable issue↔scenario link (GitHub cross-refs). Close-on-green is the discipline.
  • Pillar 3 — Projects board (Beads-informed). specify work ready|claim|move|discover on the inlined Projects v2 GraphQL client (resolve project/fields/options, add item, set single-select, list items, atomic self-claim, discovered-from label + #N backlink). "ready" is a Status column (a --column flag, not a computed field). Column names are flags that default to the confirmed APL-Innovation-Lab/projects/1 set — Backlog → Ready → In Progress → On Hold → Cancelled → Closed (actionable = Ready; "On Hold" is the blocked signal, skipped for free since ready lists only the actionable column). 🔄 Remaining (minor): blocked-by/epics (sub-issue) helpers exist in the client but have no dedicated work subcommand yet.
  • Agent memory (per-agent memory/). MemoryDir() on the adapter → .claude/memory/ · .agents/memory/ · .github/memory/; init seeds MEMORY.md (skip-if-exists, so re-init never clobbers), wires loading (Claude @import; AGENTS.md/copilot directive), ships the managing-memory skill. Engine ignores it. Goldens updated; dogfooded → this repo's .claude/memory/ + a root CLAUDE.md. Design: docs/design/agent-memory.md.
  • Deploy workflows (optional, none required). specify deploy add <kind> [target] renders deploy.yml for cloudflare-workers-ssr / cloudflare-workers-spa / railway / github-pages-spa (templates use [[ ]] delims so ${{ }} survives) and records the per-target manifest. Per-target (decided). (init --deploy sugar still TODO.)
  • Secrets via 1Password (op). Manifest holds only op:// references (validated; raw values rejected at deploy add + scan). specify secrets sync resolves via local op and pipes into gh secret set (CI) + wrangler secret put (cloudflare runtime, via stdin — never argv/log); railway runtime via argv (CLI limitation, noted). --dry-run prints the plan without resolving. Default = gh secret set sync (decided); runtime-load via 1password/load-secrets-action remains a documented upgrade.
  • Branch-protection provisioning (specify protect). Codifies the docs/ci-gating.md ruleset (require quality + verify / verify, require a PR, block force-push) via the GitHub API; re-runnable (updates an existing same-named ruleset in place).

Resolved open decisions (from the design doc): no github config block (auto-detect via gh); GitHub commands un-namespaced; deploy is per-target; default secret mode is the gh secret set sync; memory frontmatter is optional; the Pillar 3 column set is confirmed (Backlog → Ready → In Progress → On Hold → Cancelled → Closed, actionable = Ready) and baked in as specify work's defaults.


P3 · Scaffolding & stack coverage

  • 🔄 Web scaffold — flesh out to the full default. Approach: prototype-first / resolve-by-running (build the real app green, then templatize), grounded in inspecting the reference apps (trove · tangerine-dashboard · contacts main+convex). Engine decision: plain Vite + oxc + Mise (not vite-plus), confirmed with Mark — matches scaffolds/web.md.
    • Slice 1 — default app, green-on-arrival (verified for real). TanStack Start SSR + Router (virtual file routes, app/routes.tsroutes.gen.ts via tsr generate) + Query; React 19 + React Compiler (@rolldown/plugin-babel + reactCompilerPreset()); Tailwind v4; the React-Aria + cva + Motion foundation recipe (app/components/foundation/); Zod; #/* subpath imports; Mise tasks (dev/test/build/fmt:check/lint/typecheck + routes codegen, mise trust on install); the binding harness preserved. Fresh specify target add webverify green + fmt:check/lint/typecheck/build all pass.
    • 🔄 Slice 2 — --data layers. Scaffold-manifest data variants + a --data flag (scaffold.RenderData overwrites shared base files like router.tsx; per-variant deps + codegen scripts phase-ordered after the base install).
      • --data convex (the default) + --data none, both green-on-arrival, verified for real. Convex is green offline via an anonymous local deploymentCONVEX_AGENT_MODE=anonymous pnpm exec convex dev --once generates convex/_generated/ with no account/login; pnpm-workspace.yaml dangerouslyAllowAllBuilds clears pnpm 11's native-build gate. Client/provider wiring from the contacts@convex delta.
      • --data drizzle (D1) — folds into the Cloudflare runtime slice below (D1 is reached via cloudflare:workers, so it needs that runtime).
    • Slice 3 — runtime axis + Drizzle+D1. A --runtime flag + a generalized variant mechanism (scaffold.Variant, RenderVariant, cross-axis requiresRuntime). Cloudflare SSR is the default (per web.md): @cloudflare/vite-plugin + cloudflare() + wrangler.jsonc + wrangler types codegen; --runtime node is the lighter (no-Cloudflare) variant. --data drizzle is runtime-adaptive (drizzle-orm@rc): a Variant.RuntimeFiles overlay ships the D1 db module + d1_databases binding on --runtime cloudflare and the node:sqlite adapter (drizzle-orm/node-sqlite) on --runtime node — shared schema/drizzle.config + drizzle-kit generate. All of {cloudflare,node}×{convex,drizzle,none} verified green-on-arrival. ⬜ Deferred: the --ssr/--server spa/static modes (refs are all SSR).
    • Slice 4 — --with clerk. The --with Feature mechanism now carries deps + scripts (parallel to Variant), and features render LAST. Clerk feature (from tangerine): adds @clerk/tanstack-react-start, app/start.ts (clerkMiddleware), and wraps root.tsx with ClerkProvider. Green-on-arrival without live keys (typecheck + build pass; auth resolves at runtime).
    • Slice 4b — --with tiptap. A purely additive feature (the first non-provider one): adds @tiptap/react/@tiptap/starter-kit/@tiptap/pm (v3) + an app/components/foundation/editor.tsx RichTextEditor (StarterKit, immediatelyRender: false for SSR, cva/Tailwind-styled). Overwrites no shared file, so it composes with clerk and any data/runtime variant — render-tested + green-on-arrival verified for real (fmt:check/lint/typecheck/test/build + specify verify all pass on a fresh target add web --with tiptap).
    • Slice 4c — provider composition seam + --with posthog. Resolves the provider-stacking fork (Mark's call: use TanStack Router's Wrap). A new base app/providers.tsx (a Go template) is the single place client-side providers compose, via an accumulator (tree = <X>{tree}</X>) gated by {{if .Features.<name>}}; both the base and the convex router.tsx delegate their Wrap to <Providers>. So providers stack deterministically without fighting over a shared file — clerk (root.tsx) is orthogonal and composes for free. posthog is the first provider: add: posthog-js
      • a conditional PostHogProvider block (apiKey form, SSR-safe, env via VITE_PUBLIC_POSTHOG_KEY/_HOST); it carries no files (the wiring lives in the base seam). Also fixed: a base pnpm-workspace.yaml (dangerouslyAllowAllBuilds) so node+none can add build-script deps (posthog → core-js) — previously the one combo without it. Verified green-on-arrival for real across 6 combos: node×{none,convex}×{±posthog}, cloudflare+convex (default), and node+none+clerk+posthog (both providers wired at once).
    • Slice 4d — --with email (Resend + React Email). Additive: ships app/emails/welcome.tsx (a React Email template) + app/server/send-email.tsx (a server-only Resend send helper, key via process.env.RESEND_API_KEY), overwriting no shared file. Deps resend + react-email (v6) + @react-email/render — note the standalone @react-email/components is EOL/deprecated; v6 consolidated the components into the react-email umbrella (clean, zero deprecated transitives). Green-on-arrival verified for real (fmt:check/lint/typecheck/test/build + specify verify). ⚠️ resend is a Node SDK; on the Cloudflare Workers runtime a user wiring the helper into a Worker may need node-compat (the helper is additive/unimported, so the scaffold itself stays green).
    • Slice 4e — --with stripe. Additive: ships app/server/stripe.ts (a server-only Stripe client + createCheckoutSession helper, key via process.env.STRIPE_SECRET_KEY) + app/lib/stripe.ts (a client loadStripe via VITE_STRIPE_PUBLISHABLE_KEY), overwriting no shared file. Deps stripe + @stripe/stripe-js (Stripe SDK 22 makes apiVersion optional). Green-on-arrival verified for real (fmt:check/lint/typecheck/test/build + specify verify). Same Workers caveat as email (Node SDK; additive/unimported so the scaffold stays green).
    • ⬜ Remaining --with add-ons: sentry (now unblocked: client provider rides the same Wrap/providers.tsx seam; note it also adds a vite plugin, which touches vite.config.ts — the runtime axis — so handle that overlap), then tanstack-db (intersects the --data axis) / electron (a bigger shell change).
    • Slice 4f — foundation extras (trove parity). Added lucide-react (icons) + tw-animate-css (animation utilities) to the base web foundation — both actually used in trove. tailwind.css imports tw-animate-css; the example route exercises a lucide icon (so neither is a dead dep). Green-on-arrival verified. (Deferred @clerk/themes: would be a dead dep without an opinionated theme choice.)
    • Replaced the hand-rolled foundation with racket-ui via the official shadcn CLI. racket-ui (github.com/markmals/racket-ui) is a shadcn/ui-compatible registry on React Aria + Tailwind v4 + cva + Tabler. Templatized shadcn-native: @/*→member-root via tsconfig paths + Vite 8 resolve.tsconfigPaths; components/ui+lib/cva.ts at root, app/ for routes/entry/globals.css; import/extensions: off; a new components.json with the @racket-ui registries map (per-item GitHub raw); phase-0 shadcn add @racket-ui/base @racket-ui/button; foundation + styles/cva.ts + tailwind.css removed; lucide→Tabler; #/·#db@/·@/db; tiptap editor → app/components/editor.tsx. Precondition (the real blocker, not just the missing registry.json): racket-ui's per-item distribution public/r/*.json was gitignored, so the namespaced @racket-ui/cva dep 404'd — fixed + merged in racket-ui#1. Verified green-on-arrival for real across node+none, cf+convex (default), cf+drizzle, node+drizzle, and node+clerk+posthog+tiptap+email+stripe (fmt:check/lint/typecheck/test/build + specify verify). Recipe + gotchas in .claude/memory/rac-ui-shadcn.md. (Superseded Slice 4f's lucide; tw-animate-css stays — racket-ui's globals imports it.)
    • Slice 5 — .vscode + pack refresh (Varlock deferred). Ships .vscode/{settings,extensions}.json in the base scaffold (adapted from trove): oxc as the formatter + lint/fix-on-save, tsgo as the type checker (useTsgo + the native-preview tsdk), Tailwind IntelliSense for cva/cx, and recommended extensions (oxc · tailwindcss · native-preview · todo-highlight). web-development pack refreshed to rac-ui (the stack table + the "UI components come from rac-ui via shadcn" idiom; dropped the Tailwind Plus reference). Varlock deferred — the reference apps don't wire it; env stays Vite-native .env.local (design doc updated). .vscode is inert to the build (outside app/); render-tested + a full combo stays green.
  • Library / non-app coverage — add a product kind: app | library (relax the "actor is human" rule for libraries; story+domain+error apply, UI kinds don't) and the stacks swift-package, swift-cli, npm-package, vscode-extension. Engine unchanged (joins scenario↔test regardless). Design: library-products.md. Roster is evidence-based (web · website · apple · android · go-cli · node-cli · swift-package · swift-cli · npm-package · vscode-extension; dropped rust-cli/windows/linux/browser-extension). npm-package shipped 2026-06-27 — the node-family single-TS-library stack (tsdown + Vitest junit + scoped bindings), the node twin of swift-package.
  • go-service scaffold (Tier-2 structural, trove's daemon). A new stack: a runnable Go HTTP daemon (net/http ServeMux + graceful shutdown + a scenario-bound httptest) that's green-on-arrival via the Tier-1 gotest format + Go leading-comment bindings + scoped mode (dogfoods Tier 1: verify → 2 passed · 1 locked · drift clean). Introduces stack-aware member placement (the manifest's memberDir): target add … --stack go-service lands in cmd/<name> (web stays apps/), the first piece of incremental member-add (Mark's monorepo call). Also plumbed target.bindings manifest→config.
    • Shared root go.mod (monorepo composition) (shipped via #27). go-service is now sharedModule: members compose into ONE repo-root go.mod (each a cmd/<name> sharing internal/, trove's shape) instead of a self-contained module per member. target add creates the root go.mod if the repo isn't a module yet (module path from the git origin remote → host/owner/repo, else the dir base name) and a second member just joins it; members render no go.mod of their own. Verified: two members compose, go build ./... sees both, each verify green. (scaffold.SharedModule + cmd/specify ensureRootGoMod/deriveModulePath.)
    • --with openapi (contract-first via oapi-codegen) (shipped via #27). troved's defining pattern: the feature overwrites main.go/greeting.go/greeting_test.go with the strict-server wiring, ships openapi.yaml (the contract, with an x-spec trace) + oapi-codegen.yaml, and adds phase-ordered install scripts (go get the runtime + the oapi-codegen tool directive → generate internal/api/api.gen.gogo mod tidy). Members import the generated package by full module path (needs Data.Module = resolveModulePath). A conditional mise run generate task regenerates from the contract. Verified green-on-arrival: target add … --with openapigo build/fmt:check/vet/test
      • specify verify (2 passed · 1 locked), module path from the git remote. (Also fixed: the default base main.go shipped a literal unrendered {{kebab .Name}} in its package comment.)
    • --with sqlite (store + migrations + flag/env config) (shipped via #27). Additive (like the web email/stripe features — overwrites no shared file, so it composes with the base AND --with openapi): ships a member-private internal/store package (pure-Go glebarez SQLite — busy_timeout + single conn; embedded migrations/*.sql applied once via a _migrations table; a settings KV + a roundtrip test) and a config.go flag/env helper (loadConfig with envStr/envInt fallbacks). Feature files are .tmpl (so speckit's go build ./... doesn't compile them — they import glebarez); deps via a go get script, tidied by the base phase-5 go mod tidy. Verified green-on-arrival: base+sqlite AND openapi+sqlite (17 files, both deps) → go build/fmt:check/vet/specify verify (2 passed · 1 locked; the untagged store test runs + passes, out of scenario scope under bindings: scoped).
    • --with client (external-service client + httptest idiom) (shipped via #27). Additive, pure stdlib (no deps/scripts): a member-private internal/services package — trove's pattern — with a bounded http.Client (DefaultTimeout), a GetJSON helper, a typed APIError for non-2xx, SanitizeURLError (strips the *url.Error URL so a key in a query string never logs), an example Client (project only consumed fields), and a fakeServer(t, routes) httptest harness + tests. Plain .go (no template vars), so speckit's own go build/go test compile + run it (free validation). Verified green-on-arrival: base+client AND the full openapi+sqlite+client stack (internal/{api,services,store}) → go build/fmt:check/vet/test/specify verify (2 passed · 1 locked) all green.
    • trove-parity series complete — all four bundles (root go.mod · openapi · sqlite · client) compose into one troved-shaped green-on-arrival service. ⬜ Still later: pnpm-workspace membership + repo-root internal/ sharing land with the broader monorepo slice; the protocol x-spec coverage reader.
  • 🟡 Per-stack scaffold buildsapple in progress (scaffolds/apple.md):
    • Slice 1 — headless Core harnessspecify target add <name> --stack apple renders a self-contained SwiftPM package (apps/<name>/Core, a @Observable view-model + a pure domain type), the SpecTraits.swift binding harness, an example bound story, and the swift-format target wiring (swift test --event-stream-output-path … --event-stream-version 0). Verify target is the headless Core — no Tuist, Xcode project, simulator, or signing — so it's green-on-arrival with only the Xcode Swift toolchain. Verified end-to-end on a Mac: scan clean · verify (2 passed · 1 locked) · swift build · swift format lint --strict all green. The .scenario() trait is the canonical binding (decided 2026-06-14); the engine's swiftBindRe + event-stream parser already join it. Swift files are .tmpl only for substitution (speckit's Go CI never compiles them — the inverse of the go-service rule).
    • Slice 2 — Tuist app surfaceProject.swift (macOS AppKit .app + unit-test target, signing off, bundleId com.example.<name>) + macOS/{Info.plist,Sources/App,Tests} (programmatic @main delegate + a window controller reading the Core's @Observable model), mise generate/build/launch:macos/ test:app (Tuist pinned). Verify still points at Core; the app build is a Mac-only secondary. Proven on macOS: mise run build (BUILD SUCCEEDED) · test:app (passed) · verify green · lint clean. Gotchas: Tuist anchors on the repo .git root; the Core needs a .library product for the app to consume; a silent post-render swift format pass normalizes the name-dependent AppKit/<Name>Core import order.
    • 🟡 Slice 3 — --with features (go-service-shaped): swiftdata — a <Name>Persistence target (SwiftData @Model + a store mapping the domain Todo, strict-memory-safety off) + a bound persistence test (headless temp-file reopen). --with swiftdata → verify 3 passed · 1 locked + app build + lint, base unaffected. Established two patterns: the Package.swift composition seam ({{if .Features.x}} blocks; features ship only additive files) and one test target (swift test --event-stream-output-path clobbers across multiple test targets → feature tests live in CoreTests; the .spec/.scenario traits moved to a shared TestSupport target). openapi--with openapi: a contract-first <Name>API target via the Swift OpenAPI Generator build-tool plugin (codegen at build time) + a public TodoAPIClient facade + a fake-ClientTransport test (offline-verifiable). --with openapi → verify 3 passed; --with swiftdata --with openapi → 4 passed + app build. (Seam gotcha: SPM needs dependencies after products.) dist ✅ as a deploy kindapp-store-connect (specify deploy add app-store-connect): a macOS release workflow (xcodebuild archive/-exportArchive → TestFlight/App Store on a v* tag) + ASC-API-key/cert secrets as op:// refs; release belongs at root .github/, which --with can't reach. push (APNs) deferred — mostly config, thin testable kernel, gourmand (local app) has no server. Slice 3's verifiable + gourmand-relevant features (swiftdata, openapi) + dist done.
    • Slice 4 — the apple stack pack — the apple pack (templates/packs/apple/) gains the AppKit skill suite adapted from mac-dev-skills (Mark's own, MIT): 14 concise SKILL.md (appkit-design flagship + setup/dev-workflow/hig/code-review/ui-testing/packaging/migration/private-apis/app-inspector/modern-input/ launch-continuity/liquid-glass/session-report) beside the existing ios-development + ios-simulator-control (16 total). Projects via target add --stack apple / specify packs. Zero Go changes (directory-driven), zero golden drift (packs aren't in init); TestProjectPacks asserts the suite. Adapted not vendored (point at first-party docs + sdk-api/sdk-search; no HIG dump / tool binaries). No agent + no MCP projection (the agent's grounding mandate folds into appkit-design; Xcode MCP stays per-machine, documented in-skill). ✅ Pack auto-projection fixed (all stacks): specify init --integration <agent> now seeds agent in .speckit/specs.json (via config.SetAgent, called from project.Init), so target add/packs project the pack without hand-editing; AddTarget preserves it. Golden trees gained .speckit/specs.json; TestInitRecordsAgent covers it.
    • Pack DEPTH + agent + offline HIG (2026-06-15) — rescued the stranded enhanced-pack work (whole-directory pack projection + deep references). loadPack/ProjectPacks now project a skill's whole directory (SKILL.md + references/), keyed by relpath; three appkit skills gained hand-written deep references (app-inspector·design·private-apis, 19 files). Reverses two earlier Slice-4 calls (Mark's call): packs may now carry a per-stack agent (agents/appkit-dev.md → the adapter's AgentsDir(), Claude-only; codex/generic/copilot skip it), and apple-hig (was appkit-hig) bundles the complete offline Apple HIG (~172 md, 2.4MB, snapshot 2026-06-10) — embeds into the binary (pack ≈2.9MB). loadPack keeps the packless-stack handling; TestProjectPacks covers references + agent + packless; binary e2e green (specify packs → 208 files, agent + HIG corpus land). The generated AppKit slice is checked against an external mac-dev-skills checkout by scripts/generate-apple-pack.sh --check in CI; do not hand-edit the generated AppKit pack copy. Deferred follow-ups: deepening the other ~11 appkit skills with references/.
    • swift-package / swift-cli (apple-platform-tools' shape) — two sibling library stacks (memberDir: packages, swift event-stream format, scoped bindings) that reuse Slice 1's headless harness (the SpecTraits .spec/.scenario traits + the event-stream test task) with zero engine changes. swift-package is a flat reusable library (module named after the member over a static Sources/Library via path:; example: a pure SemanticVersion). swift-cli is a library Core + a thin swift-argument-parser executable shell (<Name>Core over Sources/Core + a <name> executableTarget over Sources/CLI; the behaviour is proven through the library so swift test verifies it headlessly — no binary run). Both proven green-on-arrival on a Mac: swift build · swift test · swift format lint --strict · specify verify (2 passed · 1 locked), and the swift-cli binary runs (greet-tool Ada --shoutHELLO, ADA!). Render-tested in Go CI (internal/scaffold/swift_{package,cli}_test.go). Also fixed: a packless valid stack no longer breaks specify packsloadPack treats a missing pack dir as "no pack" for a real scaffold (still errors on a genuinely unknown stack). No pack/MCP/features for these (factor later if needed).
    • Then the rest one at a time, each gated on a tooling preview. node-cli already spec'd (scaffolds/node-cli.md).
  • Feature-folder templates (minor) — NARRATIVE/story/model/view-model/error templates under .speckit/templates/feature/ so the commands scaffold faster (today they point at specs/CONVENTIONS.md, which works).

P4 · Engine & workflow enhancements

  • 🔄 Trove adoption — Tier 1 (engine readiness, in review). Makes the engine able to scan + verify a real Workbench-shaped repo (trove, the first conversion). Validated end-to-end against ~/Developer/Projects/trove: scan clean (was 46 findings), verify joins trove's Go bindings (4 scenarios passed, 0 dangling/unbound, 2 honestly unjoinable cross-target). Changes:
    • protocol kind — a contract kind (protocol.<producer>.<op>, dir specs/protocol/, no scenarios) in internal/specmodel; cleared trove's 17 I2s. The x-spec/OpenAPI binding reader for protocol coverage stays deferred (Tier-1 leaves protocol specs scanning clean + drift-tracked).
    • Filename rule accepts both the id-tail stem (engine.scan.md) and the full-id stem (story.engine.scan.md) — cleared trove's 29 I1s; backward-compatible.
    • Scenario headings at any levelparseScenarios now also reads nested ### Scenario N: under ## Acceptance Criteria (trove's form; an audit miss — bindings were dangling until this).
    • Go + generic // [scenario.id] binding reader.go added to sourceExts; a line-oriented leading-comment reader binds the tag to the next func Test… (Go) or it/test(…) title (TS).
    • gotest report format — parses go test -json (top-level funcs; subtests rolled up; skips omitted).
    • bindings: scoped (decision-for-mark) — opt-in per-target relaxation so a partially-bound suite (untagged unit tests) verifies what it does bind; default stays strict (untagged = unbound D12 violation), so the scaffold + SpecKit's own repo are unchanged. ⚠️ This softens the documented "every untagged test is a hard error" philosophy only when opted in — confirm the name/shape. ⬜ Tier-1 follow-ups: protocol x-spec coverage reader (still deferred). ✅ go-service stack/scaffold (shipped, with the trove-parity feature set). ✅ onboarding an existing repospecify target register <name> --stack <s> records an existing member as a target in .speckit/specs.json without scaffolding/installing (seeds the wiring from the stack's scaffold manifest when it has one — web, go-service — else takes --format/--command/--report/--source/--bindings flags; flags override manifest defaults). Smoke-tested on real trove: registered troved (go-service) + a TS package, scan clean. The next step (converting trove) is the target decomposition — which scenarios/tests scope to which target (trove's bound tests span cmd/troved + internal/), reconciling scenario ids, and the per-member wiring — plus the deferred protocol x-spec reader + product-rollup render.
  • Richer CI annotations (P1 fast-follow). --format github now extends past the firewall: verify annotates unjoinable scenarios at their spec line + dangling bindings at the test line; parity annotates each non-conforming cell at its spec line; the firewall points at the exact it(...)/@Test line. bindingsInContent carries file+line, specmodel.Scenario carries its line, and engine.SpecLocations maps scenarios → spec file:line. The gate action runs verify/parity --format github. (Verified: a repro with an unjoinable scenario + a dangling binding emits the expected ::error file=…,line=…::.)
  • 🔄 Scanner — multi-framework bindings. ✅ generic // [scenario.id] leading comment (Go + JS/TS) shipped with Tier 1. ⬜ Still unread: MSTest [TestProperty("scenario", …)], kotlin @Tag("scenario:…"). (Today: Swift traits, Vitest titles, and the leading-comment form.)
  • Product-rollup rendercover/parity grouping + per-product verdict. ProductTargets() exists; lands with the multi-target slice.
  • triaging-defects skill — reframe around GitHub Issues (Pillar 2 supersedes the old DEFECTS.md-ledger blocker): triage an issue → scenario/regression test → close on green.
  • Hooks (claude-pack overlay)format-on-edit, spec-reconcile, stop-lint, notify-long-task, user-prompt-context + codegen hooks (convex/openapi/tuist). gate already mechanizes the enforcement ones (firewall/generated/scope) — decide which hooks remain vs. folded into gate.
  • codex/copilot review subagents — equivalents to the claude-pack reviewers; their delegation models differ from Claude's dispatch (open question).
  • VS Code extension — codelens on // SPEC: (jump scenario ↔ bound test), drift gutter, a parity tree, run the gate, a board view. The developer-native complement to the CLI.

P5 · Docs decisions & minor cleanups

  • Harness & usage guides (docs/). Shipped the onboarding surface, linked from the README's new Guides section. Both axes, fact-checked against the internal/project adapters + the golden init manifests (the source of truth, kept in lockstep):
    • Per harnessdocs/harnesses/{claude,codex,generic,copilot}.md: what init projects for each agent (exact paths for commands/skills/rules/subagents/memory/ + the orientation file and its loading mechanism), how the /speckit.* commands are invoked there, and the per-agent differences (Claude: user-invocable skills + the 5 review subagents + CLAUDE.md native @import; Codex/Generic: the shared .agents/ + AGENTS.md read-at-start projection, byte-identical to each other, no subagents; Copilot: dual .github/agents+.prompts command projection under .github/).
    • With vs. without GitHubdocs/usage/offline.md (the engine alone: scan/verify/lock/drift/cover/parity/gate + git hooks, determinism line as the spine, no gh/network) and docs/usage/github.md (the optional shell: PR gate + protect, Issues/taskstoissues, the work board, deploy/secrets — every command marked optional). Reflects the shipped P2 surface.
  • Decision — historical-doc vocab. FORK.md / FORK-PLAN.md still use engine-key platform (~100× in FORK-PLAN) as dated planning records. Migrate to target, or leave as pinned artifacts? (Untouched for now.)
  • Decision — init --platforms / extension add vs target add --stack. The features/0002-init extension stories spec a surface that overlaps the shipped target add + packs. Reconcile, or relabel as a deferred Phase-4 design.
  • Legacy whole-file templates. plan-template.md / tasks-template.md / checklist-template.md still use the upstream spec.md/plan.md/tasks.md model + unrendered __SPECKIT_COMMAND_*__ tokens — at odds with the feature-folder model.
  • render.go PLATFORM header — the cover/parity/gate state table prints a PLATFORM column; rename to TARGET.
  • Discussions (maybe) — spec RFCs before they're committed. Take it or leave it. Not pursuing: GitHub MCP toolset, GitHub Agentic Workflows.

P6 · Release (outward — needs explicit go-ahead)

  • ⬜ The first release (v0.1.0 tag) activates brew + mise. On tag: goreleaser publishes archives and dispatches specify-release to the tap; the tap bumps + bottles. Checklist in packaging/homebrew/README.md. Do not tag or push to the tap without confirmation.

Completed

  • Engine & config.speckit/specs.json (plain JSON: version/agent/paths/targets, verify wiring inline); the engine keys on target (platformtarget rename through code, specs, and docs); scan validates the config. The full engine (scan/verify/lock/drift/cover/parity/gate) and init are implemented + tested.
  • Commands & skills — all 9 /speckit.* commands reworked to the fork's reality (.speckit/, the specify engine, no scripts) synthesizing Workbench discipline + spec-kit rigor; 7 universal process/authoring skills + 13 platform-pack skills ported and wired to the commands; the feature-folder data model (features/<NNNN>/…, scenario sub-IDs, // SPEC: pointers) resolved as canonical.
  • Rules packcode-quality · commit-discipline · spec-conventions · enforcement-hierarchy ported from Workbench to the fork's reality (target, specify gate/verify, the engine mechanizes the sync invariants) and projected by init into each agent's rules dir (.claude/rules/ · .agents/rules/ · .github/rules/), referenced from the orientation file (@import for Claude; a directive for AGENTS.md / copilot-instructions.md).
  • Subagents (claude-pack)spec-reviewer · test-gap-finder · drift-hunter · handoff-builder · visual-verifier ported and projected into .claude/agents/.
  • Stack scaffolding (machinery + web) — Part A (config.AddTarget/Save, the internal/scaffold text/template renderer, specify target add … --stack); Part B (the web scaffold green end-to-end: target add webpnpm addmise run testverify green + locked); the create-sprinkles resolve-by-running alignment (phased scripts, pnpm add pins versions, no hardcoded "latest"); tsgo fix (@typescript/native-preview + tsgo typecheck task). web + node-cli stacks spec'd.
  • Distribution — Homebrew from-source bottling (specify.rb + tap auto-bump + cross-repo dispatch); deploy gated on the first release (P6).
  • Docs & design — repo-wide prose currency sweep (platformtarget, de-upstreamed the inherited GitHub community files, removed dead upstream .github/ config); spec-driven.md rewritten to the fork's model; design docs landed for GitHub integration, agent memory, stack scaffolding, and library products; mise task naming convention set to colons (fmt:check).