Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions boatstack/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
---
name: boatstack
description: Turn a product request into a question-led, specification-first implementation with test, review, and ship gates, then learn from the evidence without silently changing project rules. Use when planning or building a feature, creating an implementation PR, reviewing work against product intent, diagnosing repeated coding-agent failures, updating Boatstack itself, or exporting the same engineering loop to Cursor, Claude Code, Codex, and GitHub.
description: Use when the user explicitly asks for Boatstack, asks what is next in Boatstack, or works on an active or current-branch published Boatstack delivery. Do not activate from installation, attachment, saved or approved drafts, or repository presence alone.
---

# Boatstack

Build the smallest complete product slice that can be independently verified. Implementation methods remain open: project facts, approval, and gate evidence are canonical; host-specific prompts are adapters. You are free in how you build. Only claims of completion require evidence.

Boatstack starts only from an explicit user request or a managed delivery bound to the current worktree and branch. A saved, approved, policy-ready, ambiguous, stale, or invalid draft is repository evidence, not ambient workflow authority. Before explicit use, only the irreversible-operation safety floor and Boatstack-owned state boundaries apply.

## Start by selecting the operation

Map the request to one operation:
Expand All @@ -30,15 +32,15 @@ For the full state machine, read [workflow.md](references/workflow.md). For arti

## Report what is next

Run the project-local helper's read-only `next-status --repo . --json` inspection. Repository artifacts, managed delivery state, gate receipts, and the recorded PR identity are evidence; conversation, terminal, worktree, and process observations are context only. Never run the returned operation automatically. `NOT_STARTED` points to `auto-plan` (run it with the plan path via `--plan`); `PUBLISHED` means a PR exists but is not a verified merge; only `FEATURE_COMPLETE` requires no action. If state is ambiguous, stale, or invalid, name the blocker instead of choosing by recency or clearing artifacts. When an `AMBIGUOUS` block names only past deliveries the user no longer cares about, name the ignorable delivery slug(s) and offer to exclude them from ambiguity resolution; only after explicit user confirmation, add each slug with `.product-loop/boatstack ignore-delivery --repo . --feature <slug>` (a bounded, provenance-safe write to `workflow.ignored_deliveries` — never hand-edit config or delivery state). Any new, unlisted ambiguous delivery still pauses the workflow.
Run the project-local helper's read-only `next-status --repo . --json` inspection. Once an explicit request identifies a feature slug, pass `--feature <slug>` on every status call so unrelated saved drafts cannot redirect the run. Repository artifacts, managed delivery state, gate receipts, and the recorded PR identity are evidence; conversation, terminal, worktree, and process observations are context only. Never run the returned operation automatically. `NOT_STARTED` points to `auto-plan` (run it with the plan path via `--plan`); `PUBLISHED` means a PR exists but is not a verified merge; only `FEATURE_COMPLETE` requires no action. If unscoped state is ambiguous, stale, or invalid, name the blocker instead of choosing by recency or clearing artifacts. When an `AMBIGUOUS` block names only past deliveries the user no longer cares about, name the ignorable delivery slug(s) and offer to exclude them from ambiguity resolution; only after explicit user confirmation, add each slug with `.product-loop/boatstack ignore-delivery --repo . --feature <slug>` (a bounded, provenance-safe write to `workflow.ignored_deliveries` — never hand-edit config or delivery state). Any new, unlisted ambiguous delivery still pauses the unscoped workflow.

To see every feature at once, run the read-only `.product-loop/boatstack flow frontier --repo .`. It lists each delivery, its observed position, and who owes the next step. To wait for a published PR to move (checks finish, a review lands, a merge happens), run the read-only `.product-loop/boatstack flow watch --repo .`. The watch observes on an interval and exits when the frontier changes, when nothing can move, or at its timeout. It never acts on what it sees. When it exits, run `next-status` again and continue from the fresh state.

## Run to an explicit goal

For `$boatstack run --to plan|verified|pr`, `/boatstack-run`, or a natural-language run request, resolve the target from the request. When it is absent, ask once for `plan`, `verified`, or `pr`. First run the read-only `next-status --repo . --json` and `operation-status --repo . --json`. Wait for an executing operation and reconcile unknown completion before retrying. When the host supplies the plan path, enter `auto-plan` with `--plan <path>`; when no plan path is supplied, stop and ask the user for the plan to build. Return **Feature complete** only for a verified completed feature, and stop on unverified, ambiguous, stale, or invalid state. Schema-v3 `check-plan` runs the Git freshness preflight before it displays the plan fingerprint. When workspace management is enabled, run `workspace-cut` after the plan passes validation and continue every later command from its `destination_repository`. Only then record human approval or the selected autonomy target, so the receipt binds the final feature branch. Target `plan` stops after the valid reviewable plan. Targets `verified` and `pr` activate with `--autonomy <autonomy.md>` and stop if that receipt becomes stale. A failed fetch, missing remote/base, stale base, upstream drift, wrong worktree, constrained branch mismatch, incomplete journey decision, or ineligible policy decision blocks without creating authority or consuming repair budget. Never repair freshness by merging, rebasing, switching or creating a constrained delivery branch, discarding changes, force-pushing, or broadening permissions.
For `$boatstack run --to plan|verified|pr`, `/boatstack-run`, or a natural-language run request, resolve the target and feature slug from the request or supplied plan. When the target is absent, ask once for `plan`, `verified`, or `pr`. First run the read-only `next-status --repo . --feature <slug> --json` and `operation-status --repo . --json`. Wait for an executing operation and reconcile unknown completion before retrying. When the host supplies the plan path, enter `auto-plan` with `--plan <path>`; when no plan path is supplied, stop and ask the user for the plan to build. Carry the same feature slug through every later status and planning call. Return **Feature complete** only for a verified completed feature, and stop on unverified, stale, or invalid selected state. Schema-v3 `check-plan` runs the Git freshness preflight before it displays the plan fingerprint. When workspace management is enabled, run `workspace-cut` after the plan passes validation and continue every later command from its `destination_repository`. Only then record human approval or the selected autonomy target, so the receipt binds the final feature branch. Target `plan` stops after the valid reviewable plan. Targets `verified` and `pr` activate with `--autonomy <autonomy.md>` and stop if that receipt becomes stale. A failed fetch, missing remote/base, stale base, upstream drift, wrong worktree, constrained branch mismatch, incomplete journey decision, or ineligible policy decision blocks without creating authority or consuming repair budget. Never repair freshness by merging, rebasing, switching or creating a constrained delivery branch, discarding changes, force-pushing, or broadening permissions.

After preflight, repeatedly run `next-status --repo . --json`, execute only its verified next operation, verify the resulting repository state, and resolve again. Continue across all declared slices until the selected target is reached. A policy receipt may resolve only a non-material, within-spec, reversible choice with one recommendation, repository evidence, no protected impact, and a runnable oracle. Record it as `RESOLVED_BY_POLICY`, never `ANSWERED`. Any failed or unknown condition pauses for the human. Target `verified` stops after current test and review evidence passes. Target `pr` supplies scoped authority for one normal open or update action recorded in `autonomy.md`; after the exact preview is revalidated, call `publish-pr --autonomy <autonomy.md>` without asking for `o` or `u`. A changed plan, repository, branch, PR action, preview, evidence, or target invalidates that path. Same-intent test/review failures may be repaired for at most three complete cycles per active slice. Stop on amendments, ambiguity, safety failures, stale evidence, unsupported recovery, branch mismatch, or exhausted repairs. Never force-push, merge, deploy, or execute a foreign program.
After preflight, repeatedly run `next-status --repo . --feature <slug> --json`, execute only its verified next operation, verify the resulting repository state, and resolve again. Continue across all declared slices until the selected target is reached. A policy receipt may resolve only a non-material, within-spec, reversible choice with one recommendation, repository evidence, no protected impact, and a runnable oracle. Record it as `RESOLVED_BY_POLICY`, never `ANSWERED`. Any failed or unknown condition pauses for the human. Target `verified` stops after current test and review evidence passes. Target `pr` supplies scoped authority for one normal open or update action recorded in `autonomy.md`; after the exact preview is revalidated, call `publish-pr --autonomy <autonomy.md>` without asking for `o` or `u`. A changed plan, repository, branch, PR action, preview, evidence, or target invalidates that path. Same-intent test/review failures may be repaired for at most three complete cycles per active slice. Stop on amendments, ambiguity, safety failures, stale evidence, unsupported recovery, branch mismatch, or exhausted repairs. Never force-push, merge, deploy, or execute a foreign program.

When `delivery.terminal` is `merged`, follow the post-publish prescriptions exactly. After publication, run `flow next` (or `next-status`). When it prescribes `flow watch`, run the watch and re-resolve when it exits. When checks fail, it prescribes `record-change --source-stage ci`; derive the exact message, classification, evidence, and changed repair mechanism from the failing check logs, never from memory, then repair, re-gate, and republish with `publish-pr --action update`. When the PR is observed merge-eligible, it prescribes the exact `gh pr merge` command; run it only as rendered, under the host's own permissions — Boatstack never merges, and you never merge without the prescription. A required review approval, a changes-requested verdict, a closed PR, or an unverifiable PR position always ends your turn at the operator frontier.

Expand Down
25 changes: 8 additions & 17 deletions boatstack/delivery_boundary_conformance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,29 +123,20 @@ func TestActiveManagedDeliveriesStaysFailClosedOnInvalid(t *testing.T) {
}
}

// Same-Relation-Same-Law + Coreachability at the MUTATION boundary: invalid
// delivery state fails closed even when ignored (no laundering corrupt state), but
// the block is actionable — it prescribes discard-delivery (a reachable verb that
// clears it), not the opaque error or a verb that refuses. Ignoring quiets status;
// discarding unblocks mutation. This is the mutation-path twin of the read-only
// ResolveNext discard-remedy conformance above.
func TestPreActivationBlockOnInvalidDeliveryPrescribesDiscard(t *testing.T) {
// Negative conformance for
// control-law: ambient-plans-never-activate-workflow-control. Invalid delivery
// observations remain actionable through ResolveNext, but they do not acquire
// ambient authority over unrelated product tools.
func TestInvalidUnselectedDeliveryDoesNotBlockOrdinaryMutation(t *testing.T) {
repo := nextTestRepo(t)
writeInvalidDelivery(t, repo, "stale-one")
if _, err := IgnoreDelivery(repo, "stale-one"); err != nil {
t.Fatal(err)
}
finding, blocked := preActivationFinding(repo, "product.go")
if !blocked {
t.Fatal("invalid delivery state did not block mutation")
if finding, blocked := preActivationFinding(repo, "product.go"); blocked {
t.Fatalf("invalid unselected delivery controlled an ordinary product path: %+v", finding)
}
if finding.NextOperation != "discard-delivery" {
t.Fatalf("block prescribed %q, want the reachable discard-delivery", finding.NextOperation)
}
if !controlledPhaseTransition("boatstack-helper discard-delivery --repo . --feature stale-one", finding.WorkflowStage) {
t.Fatal("prescribed discard-delivery is not admitted for the block it was prescribed for")
}
// The prescribed verb clears the state, and mutation is then unblocked.
// The bounded recovery remains available when Boatstack is explicitly used.
if _, err := DiscardDelivery(repo, "stale-one", true); err != nil {
t.Fatalf("discard-delivery refused the invalid delivery it was prescribed for: %v", err)
}
Expand Down
18 changes: 17 additions & 1 deletion boatstack/delivery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ func TestManagedDeliveryHookDeniesDirectPublicationRoutes(t *testing.T) {
runGit(t, repo, "init", "-b", "main")
if err := saveDeliveryState(repo, DeliveryState{
SchemaVersion: deliveryStateSchemaVersion, Feature: "phased-feature", PlanLockHash: strings.Repeat("a", 64),
ActiveIndex: 0, Slices: []DeliverySlice{{ID: "phase-one", Title: "First", Status: "BUILD"}},
ActiveIndex: 0, Slices: []DeliverySlice{{ID: "phase-one", Title: "First", Status: "BUILD", HeadBranch: "main"}},
}); err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -561,6 +561,22 @@ func TestManagedDeliveryHookDeniesDirectPublicationRoutes(t *testing.T) {
}
}

// Relation conformance for
// control-law: managed-publication-scope-follows-current-branch.
func TestManagedDeliveryPublicationGuardIgnoresUnrelatedBranch(t *testing.T) {
repo := t.TempDir()
runGit(t, repo, "init", "-b", "main")
if err := saveDeliveryState(repo, DeliveryState{
SchemaVersion: deliveryStateSchemaVersion, Feature: "phased-feature", PlanLockHash: strings.Repeat("a", 64),
ActiveIndex: 0, Slices: []DeliverySlice{{ID: "phase-one", Title: "First", Status: "BUILD", HeadBranch: "feature/phased-feature"}},
}); err != nil {
t.Fatal(err)
}
if findings := ClassifyCommand(repo, "git push origin main"); len(findings) != 0 {
t.Fatalf("delivery on another branch controlled publication here: %#v", findings)
}
}

func TestManagedDeliveryStateDoesNotBlockUnrelatedWorktrees(t *testing.T) {
repo := t.TempDir()
runGit(t, repo, "init", "-b", "main")
Expand Down
Loading