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
@@ -0,0 +1,3 @@
### Boatstack captures visual evidence itself during ship

When a plan declares relevant visual scenarios and the repository registers a capture command (`project.commands.visual`), `pr-context`, `check-pr`, and `publish-pr` now run the capture harness automatically whenever evidence is missing or stale. The agent no longer has to be told to take or attach screenshots — its remaining step is the privacy review of the captured PNGs. A harness failure records a bounded gap in `pr_visual_evidence_capture_detail` and keeps today's `suggest` behavior; under `require`, the publication block is now a calm denial that names the full recovery ladder (`capture-evidence`, `capability-register`, `provision-capability`, `record-pr-visual-evidence`, or a `not_relevant` plan decision). A capture command that modifies the working tree is refused with the exact violating paths. Repositories without a registered command keep the prior prescribed path exactly.
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ var capabilityRegistry = map[string]Capability{
"visual": {
Name: "visual",
CommandAliases: []string{"visual", "screenshot", "e2e"},
AdmittedStages: []string{"BUILD", "TEST_PASSED"},
AdmittedStages: []string{"BUILD", "TEST_PASSED", "REVIEW_PASSED", "PR_PREVIEW"},
RetryClass: "IDEMPOTENT_EXTERNAL",
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,20 @@ func denialFor(host string, finding SafetyFinding) Denial {
d.Reassurance = "No push or pull request was made."
return d

case "workflow-visual-evidence-missing":
target := "this delivery"
if finding.BlockingFeature != "" {
target = fmt.Sprintf("feature %q", finding.BlockingFeature)
}
d.Qualifier = "visual evidence is owed"
d.Detail = "PR publication is blocked until required visual evidence is current for " + target + "."
if reason := strings.TrimSpace(finding.Reason); reason != "" {
d.Detail += " Automatic capture reported: " + reason + "."
}
d.Detail += " Boatstack captures the plan's approved scenarios itself once a repository command is registered; declare pr_visual_evidence not_relevant (with a reason) only for a genuinely nonvisual change."
d.Reassurance = "No pull request was created or updated."
return d

case "operation-in-flight":
d.Severity = SeverityAdvisory
d.Qualifier = "already supervised"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,47 @@ func enumerateDenialSolutions(repo, host string, finding SafetyFinding) Solution
appendObserveOption(&set, repo, "", "delivery.next")
return set

case finding.Category == "workflow-visual-evidence-missing":
// Every rung of the recovery ladder, in preference order: run the
// registered harness, register or provision a harness, record
// externally captured evidence, or amend the plan's relevance —
// the only escape for a genuinely nonvisual change.
captureArgs := repoFlagArgs(repo)
captureOwed := []string{"--feature"}
if feature := strings.TrimSpace(finding.BlockingFeature); feature != "" {
captureArgs = append(captureArgs, "--feature", feature)
captureOwed = nil
}
appendSolution(&set, PrescribedCommand{
Verb: "capture-evidence", Args: captureArgs,
RequiresHumanInput: captureOwed, AutoDerivable: len(captureOwed) == 0,
Transition: denialMarker("capture-evidence"),
})
appendSolution(&set, PrescribedCommand{
Verb: "capability-register", Args: append(repoFlagArgs(repo), "--capability", "visual"),
RequiresHumanInput: []string{"--command"},
Transition: denialMarker("capability-register"),
})
appendSolution(&set, PrescribedCommand{
Verb: "provision-capability", Args: append(repoFlagArgs(repo), "--capability", "visual"),
AutoDerivable: true,
Transition: denialMarker("provision-capability"),
})
appendSolution(&set, PrescribedCommand{
Verb: "record-pr-visual-evidence", Args: repoFlagArgs(repo),
RequiresHumanInput: []string{"--manifest"},
Transition: denialMarker("record-pr-visual-evidence"),
})
if feature := strings.TrimSpace(finding.BlockingFeature); feature != "" {
// The artifact name and its Markdown (stdin) are authored content — owed.
appendSolution(&set, PrescribedCommand{
Verb: "planning-write", Args: append(repoFlagArgs(repo), "--feature", feature),
RequiresHumanInput: []string{"--artifact"},
Transition: denialMarker("planning-write"),
})
}
return set

case strings.HasPrefix(finding.Category, "operation-"):
// Observation-only by design: inspect the durable operation state before
// any retry (the observed-effect discipline).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var denialCategoryInventory = []SafetyFinding{
{Category: "workflow-phase-bypass", Source: "planning-state", WorkflowStage: "DRAFT_PLAN", NextOperation: "plan-gate", BlockingFeature: "demo"},
{Category: "workflow-phase-bypass", Source: "planning-state", WorkflowStage: "NOT_STARTED", NextOperation: "planning-write", AttemptedPath: ".product-loop/features/demo/plan.md"},
{Category: "workflow-publication-bypass", BlockingFeature: "demo", BlockingSlice: "s1", Source: "tool-input"},
{Category: "workflow-visual-evidence-missing", BlockingFeature: "demo", Source: "publication"},
{Category: "operation-in-flight", OperationID: "op_1", OperationState: "RUNNING", Source: "operation-state"},
{Category: "operation-already-succeeded", OperationID: "op_2", OperationState: "SUCCEEDED", Source: "operation-state"},
{Category: "operation-reconciliation-required", OperationID: "op_3", Source: "operation-state"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ func BuildExportBundle(configPath string, config ProjectConfig, rawConfig []byte
"repair": "First run recovery-status --repo . with the user's exact free-form requested change, its observed source stage, bounded evidence when available, and --json. This resolver covers both active and current-branch published deliveries. On repair_active, read delivery-status, the current plan lock and acceptance criteria, the actual diff, and current receipts; classify the request and invoke record-change before any product edit. On draft_corrective_child, invoke record-change on the published parent, preserve its lock, receipts, slices, and publication evidence, and automatically prepare the suggested one-slice child plan with parent_delivery, exact correction, inherited intent, observed failure, returned existing_diff_sha256 and existing_changed_paths, verification requirements, and the resolved PR destination. Lead with The PR needs a corrective delivery. I prepared it for your approval. Then pause at the normal fingerprinted plan approval boundary; never reuse the parent's approval. An open PR reuses its verified head branch and is updated after fresh gates and publication confirmation. A merged or closed PR uses a fresh branch and PR; when a fingerprinted correction diff already exists, leave the original worktree untouched and transfer that exact reviewed diff into the fresh child only after approval. PUBLISHED_UNKNOWN may be drafted but its destination remains blocking at publication. Stop on BLOCKED and ask one targeted feature question using the returned blockers. If no managed target exists, continue ordinary conversation. Never discard pre-existing correction edits, edit runtime state directly, or bypass test, review, and ship gates. Never ask the user to repeat a denied push or PR mutation. If Cursor reports MainThreadShellExec not initialized, make Developer: Reload Window the one recovery action because Boatstack's hook did not start; reserve reinstall guidance for Boatstack runtime integrity errors.",
"test-gate": "Read delivery-status and test only the active delivery slice. Run the internal repository safety check, build a requirement-to-evidence matrix, and treat self-authored tests as evidence rather than the sole oracle. If the active slice contains a systemic_boundary task, the evidence must prove the verification_oracle actively blocked or normalized a violation attempt (negative test). External writes require immutable target identity, transactional or fix-forward failure behavior, and an independent safety oracle. For relevant PR visual scenarios, use repository-owned capture first, then the host browser against the existing development server, one supplied launch instruction, or an approved machine-only runtime. Do not edit repository dependencies or configuration for capture. Review the exact PNGs for secrets and private data and import their temporary manifest with record-pr-visual-evidence. Commit the intentional slice product and evidence diff, then record-delivery-gate for the active feature and slice with --gate test and PASS or PASS_WITH_GAPS. Editing evidence Markdown alone never passes the gate. On pass respond Tests passed and make Run /review-gate the one next action. On failure respond Testing found a problem and make the required non-destructive repair the one next action.",
"review-gate": "Read delivery-status and review the active slice's actual diff against authorized intent, invariants, risks, gaps, and test evidence. Run the internal repository safety check. Executable destructive capability is blocking even when ordinary tests pass. When workflow.maintain_changelog is true, verify the new CHANGELOG.md Unreleased entry accurately describes the actual reader-visible impact. When workflow.independent_review_for_high_risk is true and changed paths match project.high_risk_paths, use a human peer or separate agent and pass --reviewer-identity plus --review-method human_peer or separate_agent. On pass invoke record-delivery-gate for the same feature and slice with --gate review; it must reject changed or untested diffs, disallowed gaps, missing reviewer provenance, and malformed required changelog evidence. Then respond Review passed and make Run /ship-gate the one next action. When blocked respond Changes required and make the highest-priority blocking repair the one next action.",
"ship-gate": "Prepare a reviewer-ready PR only; do not merge or deploy without separate authorization. Require the current managed feature approval, lock, test evidence, review evidence, and a passing repository safety scan, and commit the intentional product/artifact diff before projection. Internally run pr-context --repo . --feature <feature> in json and template formats, project the approved intent, actual committed diff, decisions, evidence, gaps, rollout, rollback, safety outcome, and operator-only recovery boundary into its required pr.md path, then run check-pr --repo . --preview <pr.md>. Generate a clear, product-focused PR title that describes the user value or system outcome rather than listing technical components (do not use sequence prefixes like 'PR 1'). Always include why, what changed, review order, evidence, gaps/risks, rollout/rollback, and collapsed provenance. When PR visual evidence is relevant or unresolved, show the exact fingerprinted local PNGs and public-repository warning, render the structural Visual evidence section, and treat o or u as authorization for the exact PR package plus one Boatstack-owned evidence comment. Use a signed-in host browser to upload or update that comment when available and record the observed PR and comment URLs with record-pr-visual-publication; otherwise expose the local paths for manual attachment. Suggest records a visible gap; require blocks completed publication. Preserve an opened PR and fix forward from visual_pending after attachment failure. Add security/privacy, migration, or operations sections only when relevant. Show the exact title and rendered body before any GitHub mutation. If PR_ACTION is open, respond PR ready and render the one next action as: Reply `o` to open PR. If update, render: Reply `u` to update PR. If manual, preserve the preview and give one manual publication action. Continue accepting the full replies open PR and update PR for compatibility without advertising them. Only after the matching state-scoped shortcut or compatible full reply: commit only the reviewed pr.md, rerun check-pr and require the same preview fingerprint (PREVIEW_FINGERPRINT), then run publish-pr with --action open or update and that fingerprint. The publisher performs a non-force push and rechecks context before GitHub mutation. If the diff or evidence changes, regenerate instead. If a required check fails on the base branch too, record the evidence and recommend a separate repair PR. Never edit unrelated code in this approved feature branch; a policy-approved bypass requires explicit human authorization. After publication respond PR opened with the link and make Review the PR the one next action; never imply merge authorization. If publish-pr returns UPDATE_AVAILABLE, keep Review the PR as the only next action and append a collapsed update notice saying no files changed and /boatstack-update may be run from the clean default branch after this feature PR merges. Do not check for releases before successful publication.",
"ship-gate": "Prepare a reviewer-ready PR only; do not merge or deploy without separate authorization. Require the current managed feature approval, lock, test evidence, review evidence, and a passing repository safety scan, and commit the intentional product/artifact diff before projection. Internally run pr-context --repo . --feature <feature> in json and template formats, project the approved intent, actual committed diff, decisions, evidence, gaps, rollout, rollback, safety outcome, and operator-only recovery boundary into its required pr.md path, then run check-pr --repo . --preview <pr.md>. Generate a clear, product-focused PR title that describes the user value or system outcome rather than listing technical components (do not use sequence prefixes like 'PR 1'). Always include why, what changed, review order, evidence, gaps/risks, rollout/rollback, and collapsed provenance. When PR visual evidence is relevant, pr-context runs the registered repository capture command itself whenever evidence is missing or stale, so capturing is not your step: review the exact fingerprinted local PNGs for secrets and private data, show the public-repository warning, render the structural Visual evidence section, and treat o or u as authorization for the exact PR package plus one Boatstack-owned evidence comment. Fall back to manual capture (host browser, capture-evidence, record-pr-visual-evidence) only when the context reports the capture capability unavailable or names a harness failure in pr_visual_evidence_capture_detail. Use a signed-in host browser to upload or update that comment when available and record the observed PR and comment URLs with record-pr-visual-publication; otherwise expose the local paths for manual attachment. Suggest records a visible gap; require blocks completed publication. Preserve an opened PR and fix forward from visual_pending after attachment failure. Add security/privacy, migration, or operations sections only when relevant. Show the exact title and rendered body before any GitHub mutation. If PR_ACTION is open, respond PR ready and render the one next action as: Reply `o` to open PR. If update, render: Reply `u` to update PR. If manual, preserve the preview and give one manual publication action. Continue accepting the full replies open PR and update PR for compatibility without advertising them. Only after the matching state-scoped shortcut or compatible full reply: commit only the reviewed pr.md, rerun check-pr and require the same preview fingerprint (PREVIEW_FINGERPRINT), then run publish-pr with --action open or update and that fingerprint. The publisher performs a non-force push and rechecks context before GitHub mutation. If the diff or evidence changes, regenerate instead. If a required check fails on the base branch too, record the evidence and recommend a separate repair PR. Never edit unrelated code in this approved feature branch; a policy-approved bypass requires explicit human authorization. After publication respond PR opened with the link and make Review the PR the one next action; never imply merge authorization. If publish-pr returns UPDATE_AVAILABLE, keep Review the PR as the only next action and append a collapsed update notice saying no files changed and /boatstack-update may be run from the clean default branch after this feature PR merges. Do not check for releases before successful publication.",
"boatstack-update": "Prepare a visible Boatstack infrastructure update; never mix it into product work or merge it. Force check-update with the current helper when available, but treat it and doctor as diagnostics rather than repair authority. If the installed helper is unavailable, resolve the latest stable tag from the official GitHub release endpoint and continue with the checksum-verified target installer; never require the broken helper to fix itself. If current, respond Boatstack is current with No action required. Before mutation fetch the default ref, then require the current default branch whose HEAD equals origin/<default> and no product or user-owned edits; otherwise respond Update postponed and give one recovery action. Ensure no update PR or branch already exists and create chore/update-boatstack-v<latest>. Fetch the installer from that exact release tag: it must checksum-verify the target helper before consulting installed state. Run it with BOATSTACK_MODE=update, BOATSTACK_VERSION=<latest>, BOATSTACK_REPO=<repo>, and BOATSTACK_YES=1. Exact installed hook and generated-state migrations are automatic. If the verified target helper reports REPAIR_AVAILABLE, show repair-status, state that the repair remains in this update PR, and ask whether the user wants the exact update rerun with BOATSTACK_REPAIR=1; do not infer that authority from the update request. Never offer repair for user-owned, mixed, malformed, symlinked, product, network, or authentication failures. Downgrades additionally require separately requested BOATSTACK_ALLOW_DOWNGRADE=1. The verified update preserves configuration, adapters, integrations, and unrelated host settings, writes any repair backup to Git-common state, runs doctor, and touches only Boatstack infrastructure. After installation use prepare-update-pr --repo . --version <latest> --json. Show version and repair provenance, release notes and link, integration state, title, body, changed paths, checksums, rollout, and rollback. Respond Boatstack update ready and render the one next action as: Reply `o` to open update PR. Continue accepting the full reply open update PR for compatibility without advertising it. Only the matching state-scoped shortcut or compatible full reply authorizes publish-update-pr --repo . --preview <preview_path> --preview-fingerprint <fingerprint>. The deterministic publisher stages only previewed paths, reuses an existing update commit, pushes normally, reconciles the exact branch and PR after an interrupted response, and opens at most one reviewer-ready PR. Never stage, commit, push, or open the update PR through free-form terminal calls. If GitHub auth is unavailable, preserve the branch and give one manual publication action. If operation-status reports EXECUTING, wait; if it reports RECONCILE_REQUIRED, reconcile instead of repeating publication. After publication respond Update PR opened with the link and make Review the PR the one next action. On one collision or health failure, respond Update needs attention and make addressing that named problem the one next action. Never merge automatically.",
"review": "Alias of review-gate: review the actual diff against approved intent, invariants, risks, gaps, and test evidence. Use Review passed or Changes required and the same single-action routing as review-gate.",
"ship": "Alias of ship-gate: prepare and preview the exact reviewer-ready title and body before any GitHub mutation. Require the state-scoped reply o to open or u to update the PR before publication, recheck the preview against current evidence, and never merge or deploy. Keep pre-existing unrelated failures out of the approved feature branch. Use PR ready before confirmation or PR opened after publication.",
Expand Down
Loading
Loading