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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Copy this into Cursor, Codex, Claude Code, or Gemini CLI while the repository is
Install Boatstack in this repository from https://github.com/operatorstack/boatstack. Detect whether you are running in Cursor, Codex, Claude Code, or Gemini CLI; create or use a chore/install-boatstack branch; run the official installer for this operating system; default to core unless I request gstack or Spec Kit; keep all portable host adapters; run Boatstack doctor; show me the generated files and installation diff; and prepare the installation PR without merging it or starting product work.
```

Install Boatstack in its own infrastructure PR. Merge that PR before you start a feature. Install once per Git clone. Linked worktrees reuse the verified runtime and restore their ignored local helper automatically.
Install Boatstack in its own infrastructure PR. Merge that PR before you start a feature. After that one repository adoption, fresh clones and linked worktrees inherit tracked launchers that activate the exact verified runtime automatically.

## Start with two moves

Expand Down Expand Up @@ -165,7 +165,7 @@ These behaviors come from coding failures observed in benchmark and product work
| <!-- boatstack-claim:irreversible-operations -->A failed write led to an invented reset path | Denies high-confidence destructive recovery | Hook behavior verified; outcome benefit still being evaluated |
| <!-- boatstack-claim:reviewer-ready-pr -->A PR lost decisions and accepted gaps | Builds a review brief from scope, diff, and evidence | Projection and stale-preview tests |
| <!-- boatstack-claim:phase-scoped-delivery -->A phased plan opened PRs during build | Gates and publishes one delivery slice at a time | Slice-state and bypass tests |
| <!-- boatstack-claim:git-worktree-activation -->A worktree had the hook but not its ignored helper | Restores the verified local runtime before judging the command | Linked-worktree and tamper tests |
| <!-- boatstack-claim:git-worktree-activation -->A worktree inherited no ignored helper | Uses its tracked pinned launcher to verify and activate the exact runtime before command dispatch | Linked-worktree, identity, and tamper tests |

[Read what happened, what is tested, and what remains open](docs/why-these-steps.md). The [claim record](docs/public-claims.json) keeps every material statement tied to its sources.

Expand Down
24 changes: 12 additions & 12 deletions boatstack/SKILL.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion boatstack/cmd/boatstack-helper/command_trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ var commandTracePolicies = map[string]commandTracePolicy{
"prepare-update-pr": {Category: "update-publication"}, "publish-update-pr": {Category: "update-publication"},
"release-classify": {Category: "release"}, "next-patch": {Category: "release"},
"export": {Category: "installation"}, "migrate-config": {Category: "installation"},
"hydrate-runtime": {Category: "installation"}, "doctor": {Category: "readiness"},
"hydrate-runtime": {Category: "installation"}, "activate-worktree-runtime": {Category: "installation"}, "doctor": {Category: "readiness"},
"check-source-plan": {Category: "planning"}, "check-plan": {Category: "planning"},
"planning-write": {Category: "planning"}, "record-approval": {Category: "planning"},
"record-autonomy": {Category: "planning"}, "activate-plan": {Category: "delivery"},
Expand Down
23 changes: 12 additions & 11 deletions boatstack/cmd/boatstack-helper/coverage_conformance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,18 @@ import (
// dispatch verbs that are not transitions of the delivery state machine.
var nonDeliveryVerbs = map[string]bool{
// Update / release / distribution lifecycle (not the per-feature delivery machine).
"init": true,
"update": true,
"check-update": true,
"prepare-update-pr": true,
"publish-update-pr": true,
"release-classify": true,
"next-patch": true,
"export": true,
"migrate-config": true,
"hydrate-runtime": true,
"version": true,
"init": true,
"update": true,
"check-update": true,
"prepare-update-pr": true,
"publish-update-pr": true,
"release-classify": true,
"next-patch": true,
"export": true,
"migrate-config": true,
"hydrate-runtime": true,
"activate-worktree-runtime": true,
"version": true,
// Planning phase, before a plan is activated into a delivery.
"check-source-plan": true,
"check-plan": true,
Expand Down
19 changes: 18 additions & 1 deletion boatstack/cmd/boatstack-helper/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -1298,6 +1298,21 @@ func hydrateRuntimeCommand(arguments []string) int {
return 0
}

// activateWorktreeRuntimeCommand installs or repairs only this worktree's
// ignored helper from the already verified exact shared runtime. Unlike the
// safety-hook bootstrap it reads no hook payload and makes no policy decision.
func activateWorktreeRuntimeCommand(arguments []string) int {
flags := flag.NewFlagSet("activate-worktree-runtime", flag.ContinueOnError)
repo := flags.String("repo", ".", "worktree whose local runtime should be activated")
if err := flags.Parse(arguments); err != nil {
return 2
}
if err := boatstack.HydrateWorktree(*repo); err != nil {
return fail(fmt.Errorf("worktree runtime activation failed: %w", err))
}
return 0
}

func checkSafetyCommand(arguments []string) int {
flags := flag.NewFlagSet("check-safety", flag.ContinueOnError)
repo := flags.String("repo", ".", "repository whose operational diff should be checked")
Expand Down Expand Up @@ -1595,7 +1610,7 @@ func workspaceSyncCommand(arguments []string) int {

func run() (result int) {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "usage: boatstack-helper <attach|detach|detached-status|context|activate|deactivate|init|update|check-update|repair-status|operation-status|prepare-update-pr|publish-update-pr|release-classify|next-patch|export|check-source-plan|planning-write|check-plan|record-approval|record-autonomy|activate-plan|delivery-status|next-status|recovery-status|repair-state|mutation-status|undo|run-preflight|authority-context|record-change|record-journey-results|ignore-delivery|record-delivery-gate|record-pr-visual-evidence|review-pr-visual-evidence|capture-evidence|provision-capability|capability-register|record-pr-visual-publication|attach-evidence|check-safety|migrate-config|safety-hook|ambient-safety-hook|diagnose-hook|render-denial|pr-context|check-pr|publish-pr|workspace-cut|workspace-cleanup|workspace-reap|workspace-status|workspace-sync|flow|retro|insight|doctor|version>")
fmt.Fprintln(os.Stderr, "usage: boatstack-helper <attach|detach|detached-status|context|activate|deactivate|init|update|check-update|repair-status|operation-status|prepare-update-pr|publish-update-pr|release-classify|next-patch|export|check-source-plan|planning-write|check-plan|record-approval|record-autonomy|activate-plan|delivery-status|next-status|recovery-status|repair-state|mutation-status|undo|run-preflight|authority-context|record-change|record-journey-results|ignore-delivery|record-delivery-gate|record-pr-visual-evidence|review-pr-visual-evidence|capture-evidence|provision-capability|capability-register|record-pr-visual-publication|attach-evidence|check-safety|migrate-config|safety-hook|ambient-safety-hook|bootstrap-safety-hook|hydrate-runtime|activate-worktree-runtime|diagnose-hook|render-denial|pr-context|check-pr|publish-pr|workspace-cut|workspace-cleanup|workspace-reap|workspace-status|workspace-sync|flow|retro|insight|doctor|version>")
return 2
}
if complete := commandTraceCompletion(os.Args[1], os.Args[2:]); complete != nil {
Expand Down Expand Up @@ -1706,6 +1721,8 @@ func run() (result int) {
return bootstrapSafetyHookCommand(os.Args[2:])
case "hydrate-runtime":
return hydrateRuntimeCommand(os.Args[2:])
case "activate-worktree-runtime":
return activateWorktreeRuntimeCommand(os.Args[2:])
case "check-safety":
return checkSafetyCommand(os.Args[2:])
case "workspace-cut":
Expand Down
10 changes: 5 additions & 5 deletions boatstack/denial.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func (d Denial) escalationLine() string {
if !d.Escalated {
return ""
}
return fmt.Sprintf("This denial repeated %d times. Run: boatstack-helper doctor --repo .", d.RepeatCount)
return fmt.Sprintf("This denial repeated %d times. Run: .product-loop/boatstack doctor --repo .", d.RepeatCount)
}

// --- ANSI palette (truecolor; matches the approved mockup) -------------------
Expand Down Expand Up @@ -162,7 +162,7 @@ func (d Denial) optionLines(limit int) []string {
}
hidden := len(d.Options) - len(shown)
if d.OptionsTruncated || hidden > 0 {
lines = append(lines, " (more legal moves: run boatstack-helper next-status)")
lines = append(lines, " (more legal moves: run .product-loop/boatstack next-status)")
}
return lines
}
Expand Down Expand Up @@ -467,9 +467,9 @@ func denialFor(host string, finding SafetyFinding) Denial {
d.Detail = "Boatstack could not inspect the " + name + " hook event (HOST_PAYLOAD_MALFORMED:" + finding.Reason +
"). No unsafe operation was detected; execution is denied because the intended command or tool call is unavailable. Retry once with an explicit non-empty command. If this repeats, stop shell and tool retries and preserve current edits."
if strings.EqualFold(host, "cursor") {
d.Detail += " Start a new Cursor task and run `.product-loop/bin/boatstack-helper diagnose-hook --host cursor --repo .` from an external terminal. Do not reinstall Boatstack unless it separately reports a missing, drifted, unsafe, or checksum-invalid runtime."
d.Detail += " Start a new Cursor task and run `.product-loop/boatstack diagnose-hook --host cursor --repo .` from an external terminal. Do not reinstall Boatstack unless it separately reports a missing, drifted, unsafe, or checksum-invalid runtime."
} else {
d.Detail += " Run `.product-loop/bin/boatstack-helper diagnose-hook --host " + strings.ToLower(host) + " --repo .` from an external terminal before changing the installation."
d.Detail += " Run `.product-loop/boatstack diagnose-hook --host " + strings.ToLower(host) + " --repo .` from an external terminal before changing the installation."
}
return d

Expand Down Expand Up @@ -518,7 +518,7 @@ func denialFor(host string, finding SafetyFinding) Denial {
if finding.BlockingFeature != "" {
slug = finding.BlockingFeature
}
d.Detail += fmt.Sprintf(" Planning Markdown is authored through the owned channel: one complete literal `.product-loop/bin/boatstack-helper planning-write --repo . --feature %s --artifact <name>` envelope from `.product-loop/workflow.md` — never a raw host write into `.product-loop/features/` or a manual shell paste.", slug)
d.Detail += fmt.Sprintf(" Planning Markdown is authored through the owned channel: one complete literal `.product-loop/boatstack planning-write --repo . --feature %s --artifact <name>` envelope from `.product-loop/workflow.md` — never a raw host write into `.product-loop/features/` or a manual shell paste.", slug)
}
d.Reassurance = reassureUntouched
return d
Expand Down
2 changes: 1 addition & 1 deletion boatstack/denial_escalation_conformance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func TestThirdIdenticalDenialEscalates(t *testing.T) {
if !strings.Contains(string(output), fmt.Sprintf("repeated %d times", denialEscalationThreshold)) {
t.Fatalf("escalation must carry the repeat count:\n%s", output)
}
if !strings.Contains(string(output), "boatstack-helper doctor") {
if !strings.Contains(string(output), ".product-loop/boatstack doctor") {
t.Fatalf("escalation must prescribe the fresh diagnostic:\n%s", output)
}
}
Expand Down
Loading