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
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ boatstack-config-field:workflow.independent_review_for_high_risk
boatstack-config-field:workflow.allow_pass_with_gaps
boatstack-config-field:workflow.maintain_changelog
boatstack-config-field:workflow.boundary_analysis
boatstack-config-field:workflow.pr_visual_evidence
boatstack-config-field:workspace
boatstack-config-field:workspace.enabled
boatstack-config-field:workspace.mode
Expand All @@ -40,6 +41,7 @@ Boatstack keeps delivery policy in `.boatstack-project.json` so the same project
| Allow a gate to pass with recorded gaps | `workflow.allow_pass_with_gaps` | A gate may report a pass with visible, retained gaps instead of requiring a gap-free result. |
| Keep reader-facing release history | `workflow.maintain_changelog` | Every managed delivery slice and Boatstack-prepared ad-hoc PR must update `CHANGELOG.md`. |
| Look for a missing systemic boundary | `workflow.boundary_analysis` | Planning checks whether the request is a local symptom and asks before expanding it into boundary work. |
| Attach fresh screenshots to frontend PRs | `workflow.pr_visual_evidence` | Boatstack structures visual review evidence without adding media or frontend tooling to Git. |
| Start features in fresh Git workspaces | `workspace` | Boatstack can create a branch or linked worktree and manage local cleanup under the selected policy. |
| Limit generated host adapters | `adapters` | Only the named Cursor, Claude Code, Codex, Gemini CLI, or GitHub surfaces are exported. |
| Add supported specialist workflows | `integrations` | The installer records whether gstack or Spec Kit was requested and its installed state. |
Expand Down Expand Up @@ -79,7 +81,8 @@ JSON does not support comments, so the explanations follow the example.
"independent_review_for_high_risk": true,
"allow_pass_with_gaps": true,
"maintain_changelog": false,
"boundary_analysis": false
"boundary_analysis": false,
"pr_visual_evidence": "off"
},
"workspace": {
"enabled": true,
Expand Down Expand Up @@ -141,6 +144,7 @@ The defaults below describe an omitted JSON field. A fresh installer-generated c
| `allow_pass_with_gaps` | `false` | When `true`, verification may pass with explicitly recorded outstanding gaps. It does not hide or discard them. |
| `maintain_changelog` | `false` | When `true`, requires a reader-visible `CHANGELOG.md` entry for every managed delivery slice and Boatstack-prepared ad-hoc PR. |
| `boundary_analysis` | `false` | When `true`, planning checks whether a request indicates a missing systemic boundary. Scope expansion remains a material human decision; choosing programmatic enforcement produces a boundary slice followed by the feature slice. |
| `pr_visual_evidence` | `off` | `suggest` records missing relevant screenshots as a visible PR gap; `require` blocks completed PR publication until current screenshot evidence is available. Media remains machine-local until PR attachment. |

### `workspace`

Expand Down Expand Up @@ -196,6 +200,18 @@ Add a categorized entry under `CHANGELOG.md`'s current `Unreleased` heading. See

This adds a product decision when repository evidence suggests that a local request is a symptom of a broader missing boundary. It does not silently turn every feature into a refactor.

### Add screenshots to relevant pull requests

```json
{
"workflow": {
"pr_visual_evidence": "suggest"
}
}
```

`suggest` keeps delivery nonblocking when capture or attachment is unavailable and exposes the missing evidence as a PR gap. Use `require` only when every relevant frontend PR must publish current screenshots. Boatstack stores PNG bytes in Git-common machine state, never in the product tree.

### Require independent review for high-risk paths

```json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ When Boatstack improves a branch that did not use the full workflow, it stores t

The preview's frontmatter is publication metadata; the remaining Markdown is the exact GitHub body. The preview is excluded from its own product-diff fingerprint, but any other diff or evidence change makes it stale.

PR schema v3 includes structural visual-evidence policy, status, count, and fingerprint fields. Screenshot binaries are never generated repository files: they live in Git-common Boatstack state until one PR evidence comment is published or manual attachment is required. Each imported PNG carries a `clean` or `human-reviewed` privacy receipt; upload observation is recorded separately so a failed comment can resume against the same PR.

## Worktrees, fresh clones, and updates

One verified runtime is cached under the clone's Git common directory and keyed by Boatstack version, source commit, operating system, and architecture. Linked worktrees share that cache. Their first guarded command atomically restores the ignored local helper and install lock, then evaluates the original command. Hydration uses no network and produces no tracked diff.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ Boatstack found an installed generated file that no longer matches its previous

A new commit, changed evidence, changed approval artifact, or base-branch update invalidated the preview. Ask Boatstack to regenerate it. Do not copy the old body forward.

## Visual evidence is unavailable or stale

Confirm the development launch instruction and retry the bounded health probe. Boatstack reuses a machine capability receipt only while its Boatstack version, lockfile, launch command, browser version, framework configuration, and health state still match. Under `suggest`, keep the missing screenshot visible as a PR gap or attach the displayed local PNG manually. Under `require`, recapture and publish to the same PR; do not open a duplicate PR. If the PR already opened before upload failed, preserve it and fix forward from `visual_pending`.

## A phased plan cannot push or open its next PR

Plan approval is not publication authority. Run `delivery-status` through the active
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Attach fresh visual evidence to frontend pull requests

Repositories can opt into `workflow.pr_visual_evidence` with `suggest` or `require`. Boatstack now makes visual relevance and up to three PNG scenarios structural in managed plans and PR schema v3, binds screenshot hashes to the current PR context, keeps media in machine-local Git-common state, and supports one browser-published evidence comment with manual and fix-forward fallbacks. Existing repositories remain unchanged while the field is omitted or `off`.
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ Before starting `/auto-plan` for a new feature, check `next-status --repo . --js
3. Separate facts, decisions, unknowns, and safely deferrable gaps.
4. Before proposing implementation tasks, inspect the repository and verify any assumptions about API routes, data access, UI components, authentication, server actions, streams, jobs, and external services. Do not guess application architecture.
5. If `workflow.boundary_analysis` is `true` in `project.json`: Evaluate if the requested change is a symptom of a missing systemic boundary (e.g., deficient data normalization, leaky validation, missing authorization edge). If it is, perform a rapid codebase scan for other vulnerabilities sharing this failure mode. Present this as a material product decision, showing concrete codebase evidence of the blast radius. Offer tiered implementation paths: [1a] Symptom Patch (fix only the requested route), or [1b] Programmatic Enforcement (refactor the edge and install a programmatic boundary to mathematically prevent this). If the user chooses programmatic enforcement, explicitly structure the plan into two delivery slices: Slice 1 establishes the programmatic boundary (hook, trigger, or strict test), and Slice 2 implements the feature using that boundary.
- When `workflow.pr_visual_evidence` is `suggest` or `require`, also record a structural `pr_visual_evidence` decision. Use `relevant` with one to three scenarios naming entry, state, viewport, and expected visible outcomes, or `not_relevant` with a reason. Discover repository-owned visual tooling but do not add or require framework-specific tooling.
6. Express verified architectural information as typed `architecture_facts`. Each architecture fact must reference evidence IDs produced by Boatstack repository inspection. Do not create or invent evidence IDs. Reading one arbitrary repository file does not ground an unrelated architectural claim.
7. When an architectural question cannot be verified, record it in `architecture_unknowns`. Do not create an implementation task that depends on an unresolved architecture unknown. Create a bounded discovery task instead.
8. Every architecture-sensitive task must reference the facts it depends on through `requires_facts`.
Expand Down Expand Up @@ -176,6 +177,7 @@ A published delivery is immutable. Record the observation against it, then plan
- After build completes, the source Plan-mode file is no longer a runtime prerequisite. Test, review, and ship use the approved lock, actual diff, and accumulated evidence; provenance remains recorded in the lock.
- Derive tests from acceptance criteria and affected contracts, not only from the implementation.
- Run existing relevant tests plus targeted new tests, linters, type checks, builds, and runtime checks.
- For relevant PR visual scenarios, use the repository runner first, then a host browser against the existing development server, one supplied launch instruction, or an explicitly approved machine-local runtime. Do not modify repository dependencies or configuration for capture. Review each exact PNG for secrets and private data, then import the temporary manifest with `record-pr-visual-evidence`; keep the images outside the repository.
- Treat model-authored tests and same-model self-review as evidence, not ground truth.
- Validate that tests load and exercise the intended interface. For high-risk code, add an independent oracle such as contract fixtures, mutation testing, differential checks, staging verification, or human acceptance.
- A failing check blocks the gate. A skipped check must include a reason and risk owner.
Expand All @@ -196,6 +198,7 @@ A published delivery is immutable. Record the observation against it, then plan
- Treat the actual committed diff as what changed, approved artifacts as why it changed, and evidence as the only support for completion claims.
- In the visible Evidence table, link each managed claim to the current repository-relative evidence ledger using a readable link label; do not expose hashes or absolute paths.
- Always include why, what changed, review order, evidence, gaps/risks, rollout/rollback, and collapsed provenance. Add UI evidence, security/privacy, migration, or operations sections only when relevant.
- When PR visual evidence is relevant or unresolved, show the exact fingerprinted PNGs and public-repository warning, include the structural Visual evidence table, and treat `o` or `u` as approval of the PR body plus one evidence comment. Prefer a signed-in host browser; after observing the upload, record its PR and comment URLs with `record-pr-visual-publication`. Otherwise surface the exact machine-local paths for manual attachment. `suggest` retains a visible gap, while `require` blocks completed publication. Preserve an opened PR and fix forward from `visual_pending` rather than opening a duplicate.
- Internally generate the normalized context and preview skeleton with `pr-context --repo . --feature <feature>`, write `pr.md`, and validate it with `check-pr --repo . --preview <pr.md>`. Keep these helper names and their fingerprints out of the primary response.
- Inspect the projected changed files, diff stat, high-risk matches, and actual diff before composing the brief. Commit messages are navigation aids, not proof of what changed.
- Show the exact title and rendered body before any GitHub mutation. If no PR exists, render the one next action as: Reply `o` to open PR. If one exists, render: Reply `u` to update PR.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@
"blocks": ["T-1"]
}
],
"pr_visual_evidence": {
"relevance": "not_relevant",
"reason": "<why screenshots do not help review this change>",
"scenarios": []
},
"tasks": [
{
"id": "T-1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,52 @@ func recordDeliveryGateCommand(arguments []string) int {
return 0
}

func recordPRVisualEvidenceCommand(arguments []string) int {
flags := flag.NewFlagSet("record-pr-visual-evidence", flag.ContinueOnError)
repo := flags.String("repo", ".", "repository whose Git-common state owns the evidence")
manifest := flags.String("manifest", "", "JSON manifest containing local PNG paths")
if err := flags.Parse(arguments); err != nil {
return 2
}
if *manifest == "" {
return fail(fmt.Errorf("record-pr-visual-evidence requires --manifest"))
}
recorded, err := boatstack.ImportPRVisualEvidence(*repo, *manifest)
if err != nil {
return fail(err)
}
value, err := boatstack.MarshalJSON(recorded)
if err != nil {
return fail(err)
}
fmt.Print(string(value))
return 0
}

func recordPRVisualPublicationCommand(arguments []string) int {
flags := flag.NewFlagSet("record-pr-visual-publication", flag.ContinueOnError)
repo := flags.String("repo", ".", "repository whose Git-common state owns the evidence")
key := flags.String("key", "", "managed feature or ad-hoc branch evidence key")
prURL := flags.String("pr-url", "", "published pull request URL")
commentURL := flags.String("comment-url", "", "observable Boatstack evidence comment URL")
if err := flags.Parse(arguments); err != nil {
return 2
}
if *key == "" || *prURL == "" || *commentURL == "" {
return fail(fmt.Errorf("record-pr-visual-publication requires --key, --pr-url, and --comment-url"))
}
recorded, err := boatstack.RecordPRVisualPublication(*repo, *key, *prURL, *commentURL)
if err != nil {
return fail(err)
}
value, err := boatstack.MarshalJSON(recorded)
if err != nil {
return fail(err)
}
fmt.Print(string(value))
return 0
}

func deliveryStatusCommand(arguments []string) int {
flags := flag.NewFlagSet("delivery-status", flag.ContinueOnError)
repo := flags.String("repo", ".", "repository containing the managed delivery")
Expand Down Expand Up @@ -656,7 +702,7 @@ func publishPRCommand(arguments []string) int {
verb = "updated"
}
fmt.Printf("PASS: PR %s without merge authorization\nPR_URL=%s\n", verb, url)

feature := ""
if preview, err := boatstack.ParsePRPreview(*previewPath); err == nil {
feature = preview.Feature
Expand Down Expand Up @@ -735,7 +781,7 @@ func workspaceStatusCommand(arguments []string) int {

func run() int {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "usage: boatstack-helper <init|update|check-update|release-classify|next-patch|export|check-source-plan|planning-write|check-plan|record-approval|activate-plan|delivery-status|next-status|run-preflight|record-change|record-delivery-gate|check-safety|migrate-config|safety-hook|diagnose-hook|pr-context|check-pr|publish-pr|workspace-cut|workspace-cleanup|workspace-status|doctor|version>")
fmt.Fprintln(os.Stderr, "usage: boatstack-helper <init|update|check-update|release-classify|next-patch|export|check-source-plan|planning-write|check-plan|record-approval|activate-plan|delivery-status|next-status|run-preflight|record-change|record-delivery-gate|record-pr-visual-evidence|record-pr-visual-publication|check-safety|migrate-config|safety-hook|diagnose-hook|pr-context|check-pr|publish-pr|workspace-cut|workspace-cleanup|workspace-status|doctor|version>")
return 2
}
switch os.Args[1] {
Expand Down Expand Up @@ -771,6 +817,10 @@ func run() int {
return recordChangeCommand(os.Args[2:])
case "record-delivery-gate":
return recordDeliveryGateCommand(os.Args[2:])
case "record-pr-visual-evidence":
return recordPRVisualEvidenceCommand(os.Args[2:])
case "record-pr-visual-publication":
return recordPRVisualPublicationCommand(os.Args[2:])
case "pr-context":
return prContextCommand(os.Args[2:])
case "check-pr":
Expand Down
Loading