diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b69ee6c..592d3cf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ # Contributing -Boatstack is a generated content distribution. Propose changes to workflow semantics, templates, evidence rules, or generated presentation in [Intelligence Flow](https://github.com/operatorstack/intelligence-flow/tree/90356bed91109de2eb131fc95872bdb74abac6a7/labs/12-product-engineering-loop). +Boatstack is a generated content distribution. Propose changes to workflow semantics, templates, evidence rules, or generated presentation in [Intelligence Flow](https://github.com/operatorstack/intelligence-flow/tree/d604f832578355bd5bc34eb66e8d54b062645629/labs/12-product-engineering-loop). The Boatstack repository receives product/runtime changes through a generated pull request. Review the PR's `UPSTREAM.json`, tests, adapter diff, and context-size change; do not hand-edit generated output on `main`. `.github/workflows` is the exception: it is Boatstack's executable control plane, excluded from scheduled projection and changed only through a separate manually reviewed Boatstack PR. diff --git a/UPSTREAM.json b/UPSTREAM.json index 1971b95..9f3e1c7 100644 --- a/UPSTREAM.json +++ b/UPSTREAM.json @@ -12,7 +12,7 @@ }, "files": { ".gitignore": "a7079e923a776f14f1bb3a6aa0a11a133a8e1dfb35af020f327623357b7e3957", - "CONTRIBUTING.md": "16338a21fc8e849ca3a3a72f36c0163f4136f717b54df3cfc704ed75468b60ca", + "CONTRIBUTING.md": "5060717c0e6f5337197d3c2b1acb6a047264ffab0ed6cf8af411ee498cd57051", "README.md": "3ce3e95e511089b44e946a44b8d5f4f81d019ece5336db65b2cab1f9dc4d4dad", "assets/boatstack-journey.svg": "e465befc50c8ce30f3e07e8fd97012931beeb053392c8fbf38ad645023b3cc63", "assets/boatstack-mark.svg": "be1f984da1bfa69fa5d1f986d8343d21f7e20921b71db888c928b4d2e54b09b5", @@ -38,8 +38,8 @@ "boatstack/attach.go": "6a855440fac9acc63be857efef9d76210a4619774728684832dfbb08caf42a30", "boatstack/capability.go": "9d9a75086e88bb1d9d4170821436c686002fe3a125e6ee7d23eea5779aac5cfe", "boatstack/capability_test.go": "e8322903a843970d7f0317d2629466532cb05c3ffcb44b9423adf4219faa8021", - "boatstack/capture.go": "6a279bab615a012de0b2c43aeae9b95d122365068dc54c8923c14c6a4c719449", - "boatstack/capture_test.go": "63fa1177738081f1e862364d7a4257f5e259f8e9c36276ba1775b8085b277105", + "boatstack/capture.go": "0e137d8251a658118bd56812040575d01f02fd2f862c80ef761b253db6b35bbe", + "boatstack/capture_test.go": "31a8afa40a54ae716fea1ea44be3b41705ed171789a181d1ef26432855c05c9e", "boatstack/changelog.go": "6b06be7cd9738de29ba6e87aa2569f3b027a2e618b04524f5abd7abaa17945bf", "boatstack/changelog_test.go": "ce792f23a7fe1e09fb3096cd1314130a6ab69321d4877b12a8e994027541baf7", "boatstack/cmd/boatstack-helper/coverage_conformance_test.go": "f5a931d2b5cdb0d32af85acbbd499fa0f509ed36d62e772e6b6072e348200aca", @@ -165,10 +165,10 @@ "boatstack/planning_first_write_conformance_test.go": "873097aa9384b75bf01e74a475f3ec2ac7cca4a28f733e82f2c82959032c6a30", "boatstack/planning_test.go": "06ec7022222d926040c3ae28b84ab50c3d2f804ae6473e61b303804dd992d884", "boatstack/post_publish_prescribe_conformance_test.go": "3c20d359ff84648db7dedb227b4d64e6574d9f41d3cdca0adefec1c60bfbf4ae", - "boatstack/pr.go": "0ddd88b364d141a9ccadd4d6b6751c13c56769feba5879bd896be46b0d5af5fd", + "boatstack/pr.go": "511ae0025aae3d99a5fbb942bb078d68a04feb62190387b331d562f934902c41", "boatstack/pr_phase.go": "59f8cbb75b6b538a5345474acd6a725450979579bf8ecf9591956cbbe1cc4737", "boatstack/pr_phase_conformance_test.go": "bc9c834e9c4ed43b35d81abafd7b1bf2a264ea2a8c4a4ec9758ee18d1d438968", - "boatstack/pr_test.go": "2e7709e2f163489a29ea3e7eb4bc932cfe6829b30d168f1aea9e942acbc3b4e7", + "boatstack/pr_test.go": "dd223d9ddc50af2f34b20fd30246b9aaef7e4d9fd479bcaa3e87bb4de58bd4b3", "boatstack/provenance.go": "d44dcd5421306269326f1202ba1d52df8c252490550270ef9d022e8ec2b65210", "boatstack/provision.go": "eb7333a73331b011adc93f59a2d97415d850c2e588f0e2bbb5116984e2ef927d", "boatstack/provision_test.go": "214e9edb991a66d5bbb696a7c1b63876d2f799f2cab4e3f40785f4e8f1eac57b", @@ -231,10 +231,10 @@ "docs/benchmark-corpus-audit.md": "f2d206fe8579a514f9da82b2c96c19b343ac004be67617e1bd34f0f8e0e5e6c6", "docs/benchmark-submission-audit.md": "9518abdd17690729c6423f87cab20418ed47b0915b5faa44b9ef975e9e9c3b79", "docs/configuration.md": "caa95f1dd484a71a9051951652d6457294c213999fe00fb764d05e8a509cc786", - "docs/evidence-engineered-coding.md": "59b9d1296da97e2f72960082e951065a433cd4b9c27e507c6be0fe4594eb44be", + "docs/evidence-engineered-coding.md": "bb8bd613b350be866929105ab96a3225f30f8e306b0b650a9b6655fb7c26af07", "docs/generated-files.md": "437791765b0a4015032ae21d1a6618563cad92b7402819e4f963bf5ae16284a3", "docs/getting-started.md": "51c2823f21e35140d31e6d5083dc4b89fddd24721ac6acc474154a4da53ee9f8", - "docs/public-claims.json": "823614ba0cc01218915438d443d187826f80bc4bb4a1bcd925026632d954e6c7", + "docs/public-claims.json": "ef823c6a3d27eee37856712bbb0bb4b9b0d266c97dcd2fd4568aa0c98416041a", "docs/public-surface.md": "713f7a050b5f339cf948299103ef3800417dccfecf2cc1a4166397ea6f978907", "docs/research-and-design.md": "8d78678108f0a6c924e1ff9b32c0f81aae9d1f779e0082843b6f99ad993ae2b6", "docs/safety.md": "7b9b5c515d36e683767ec8d3d9d6d119ac93650b2f629d351deadd4c600ed6a6", @@ -248,7 +248,7 @@ "labs/diagram-json/compiled/evidence.md": "1ba1c989ade070a8ef9a508fbd788d100d7292f2dbacbb2bce895468019f619d", "labs/diagram-json/compiled/tasks.json": "88f60851abf79d851e9fccc754ff3040034ae595306bc87d64784c19eb403e71", "labs/diagram-json/compiled/test-matrix.json": "424657ff505768e50fa113801fd8363364a18269d5297480907a993d44063a39", - "labs/diagram-json/plan.lock.json": "5adb15badc39a262755db561b097f7e09668ab9c462c904e55a36b2825a7663b", + "labs/diagram-json/plan.lock.json": "72e44c5239a3ec0bb1f609d25083d152d43b81d8206aeafdef7ce8db04a37512", "labs/diagram-json/plan.md": "3cc4f533b8d69386deff16b3a594a3ba09d4c0c3db636cccd8c4380084ce6a51", "labs/diagram-json/questions.md": "74733b015002c8a6777c558e7e997fa48c94850b9bd39054fe9366c97ecf728d", "labs/diagram-json/request.md": "0808fc41c36779c404f4a3a121167da6e76cac56df526e70f9ed6d3e0d4c02ed", @@ -389,12 +389,13 @@ "release-notes/2026-07-28-protected-native-auto-merge.md": "67dc76a6e7ce51034a0eadc541ba7a8946cfcabe5433cedc25db55321dfb8b62", "release-notes/2026-07-28-retro-derive-proposals.md": "c90797d76fb00816340475620b584ad150fa32d7d10bc14997a614529ef9b558", "release-notes/2026-07-28-retromine-recurrence-detector.md": "27790993a02e73f3a2700dce7340d044aeb0e52f785ded68271ff6673add9e25", - "release-notes/2026-07-29-readiness-and-journey-control.md": "2411db76974990ad0128fcd90e795c4c1c5c70d6d6f15fccd452f7e1a242310b" + "release-notes/2026-07-29-readiness-and-journey-control.md": "2411db76974990ad0128fcd90e795c4c1c5c70d6d6f15fccd452f7e1a242310b", + "release-notes/2026-07-30-visual-evidence-survives-preview-commit.md": "71d19e9fabb40e8cb1e939f26bf288911d227979926f43f337046c6cf6b66551" }, "generator": "operatorstack/intelligence-flow:boatstack-distribution", "schema_version": 1, "source": { - "commit": "90356bed91109de2eb131fc95872bdb74abac6a7", + "commit": "d604f832578355bd5bc34eb66e8d54b062645629", "path": "labs/12-product-engineering-loop", "repository": "operatorstack/intelligence-flow" } diff --git a/boatstack/capture.go b/boatstack/capture.go index 8572036..2c64147 100644 --- a/boatstack/capture.go +++ b/boatstack/capture.go @@ -180,8 +180,9 @@ func CaptureEvidence(options CaptureEvidenceOptions) (PRVisualEvidenceManifest, } // captureProductDiff reproduces the pr-context product-diff fingerprint so a -// captured manifest is trusted (PASS) by resolvePRVisualEvidence: same head -// commit and same product diff. +// captured manifest is trusted (PASS) by resolvePRVisualEvidence: same product +// diff. The head commit is recorded for provenance only — trust is keyed to +// product identity so committing the reviewed pr.md never stales evidence. func captureProductDiff(repo, base, feature, head string) (headCommit, diffHash string, err error) { baseCommit, err := resolveBaseCommit(repo, base) if err != nil { diff --git a/boatstack/capture_test.go b/boatstack/capture_test.go index 17ce972..9b02af4 100644 --- a/boatstack/capture_test.go +++ b/boatstack/capture_test.go @@ -101,10 +101,10 @@ func TestCaptureEvidenceProducesManifestTrustedByPRContext(t *testing.T) { t.Fatalf("capture mutated the product tree: %s", status) } - // The manifest must be trusted by the same resolver pr-context uses: identical - // head commit and product diff → status is the manifest's PASS, not NOT_VERIFIED. + // The manifest must be trusted by the same resolver pr-context uses: an + // identical product diff → status is the manifest's PASS, not NOT_VERIFIED. head := runGit(t, repo, "rev-parse", "--abbrev-ref", "HEAD") - headCommit, diffHash, err := captureProductDiff(repo, "main", "reviewer-ready", head) + _, diffHash, err := captureProductDiff(repo, "main", "reviewer-ready", head) if err != nil { t.Fatal(err) } @@ -112,7 +112,7 @@ func TestCaptureEvidenceProducesManifestTrustedByPRContext(t *testing.T) { if err != nil { t.Fatal(err) } - _, status, count, _, _, _, resolved, err := resolvePRVisualEvidence(repo, config, "managed", "reviewer-ready", head, headCommit, diffHash) + _, status, count, _, _, _, resolved, err := resolvePRVisualEvidence(repo, config, "managed", "reviewer-ready", head, diffHash) if err != nil { t.Fatal(err) } diff --git a/boatstack/pr.go b/boatstack/pr.go index 903caf5..f08b95e 100644 --- a/boatstack/pr.go +++ b/boatstack/pr.go @@ -109,7 +109,7 @@ func planVisualDecision(repo, feature string) (string, string, []PRVisualScenari return relevance, "managed-plan", scenarios, nil } -func resolvePRVisualEvidence(repo string, config ProjectConfig, mode, feature, head, headCommit, diffHash string) (string, string, int, string, string, string, *PRVisualEvidenceManifest, error) { +func resolvePRVisualEvidence(repo string, config ProjectConfig, mode, feature, head, diffHash string) (string, string, int, string, string, string, *PRVisualEvidenceManifest, error) { policy := normalizedPRVisualEvidencePolicy(config.Workflow.PRVisualEvidence) relevance, source := "unresolved", "agent-proposed" var scenarios []PRVisualScenario @@ -131,7 +131,12 @@ func resolvePRVisualEvidence(repo string, config ProjectConfig, mode, feature, h if loadErr == nil { manifest = &loaded relevance, source, scenarios = loaded.Relevance, loaded.RelevanceSource, loaded.Scenarios - if loaded.SourceCommit == headCommit && loaded.ProductDiffSHA256 == diffHash { + // Trust is keyed to product identity only: the preview pr.md is + // excluded from the product diff yet must be committed before + // publication, so a head-commit equality would invalidate every + // PASS manifest on that mandatory commit. SourceCommit stays + // recorded for provenance and the evidence comment. + if loaded.ProductDiffSHA256 == diffHash { status = loaded.Status } else { status = "NOT_VERIFIED" @@ -653,7 +658,7 @@ func PreparePRContext(options PRContextOptions) (PRContext, error) { return PRContext{}, err } visualPolicy, visualStatus, visualCount, visualFingerprint, visualRelevance, visualSource, visualManifest, err := resolvePRVisualEvidence( - repo, config, mode, options.Feature, head, headCommit, SHA256Bytes(diff), + repo, config, mode, options.Feature, head, SHA256Bytes(diff), ) if err != nil { return PRContext{}, err @@ -1240,11 +1245,18 @@ func PRPreviewTemplate(context PRContext) string { "## Rollout and rollback", "", "Describe deployment impact and the smallest safe rollback.", "", } if context.PRVisualEvidenceStatus != "NOT_APPLICABLE" { + // The Commit column names the commit the pixels were captured from; + // evidence stays trusted across preview-only commits, so this can + // legitimately trail HeadCommit. + evidenceCommit := context.HeadCommit + if context.PRVisualEvidence != nil && strings.TrimSpace(context.PRVisualEvidence.SourceCommit) != "" { + evidenceCommit = context.PRVisualEvidence.SourceCommit + } lines = append(lines, "## Visual evidence", "", "Screenshots are human-review evidence, not mechanical proof. Public-repository attachments are publicly accessible.", "", "| Scenario | Viewport | Commit | Result | Publication |", "|---|---|---|---|---|", - "| Describe the approved state | viewport | "+context.HeadCommit+" | `"+context.PRVisualEvidenceStatus+"` | Boatstack evidence comment or manual fallback |", "", + "| Describe the approved state | viewport | "+evidenceCommit+" | `"+context.PRVisualEvidenceStatus+"` | Boatstack evidence comment or manual fallback |", "", ) } if context.TotalSlices > 1 { diff --git a/boatstack/pr_test.go b/boatstack/pr_test.go index 6e5f27b..2d963d4 100644 --- a/boatstack/pr_test.go +++ b/boatstack/pr_test.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" "testing" + "time" ) func runGit(t *testing.T, repo string, arguments ...string) string { @@ -648,6 +649,101 @@ func TestRequiredPRVisualEvidenceBlocksPublicationBeforeMutation(t *testing.T) { } } +// savePassVisualManifest records PASS evidence for the fixture feature's +// approved scenario, bound to the given source commit and product diff. +func savePassVisualManifest(t *testing.T, repo, feature, sourceCommit, diffHash string) { + t.Helper() + pngPath := filepath.Join(t.TempDir(), "warning.png") + writeTestPNG(t, pngPath) + if _, err := SavePRVisualEvidence(repo, PRVisualEvidenceManifest{ + Key: feature, Policy: "suggest", Relevance: "relevant", RelevanceSource: "managed-plan", + Status: "PASS", SourceCommit: sourceCommit, ProductDiffSHA256: diffHash, + Scenarios: []PRVisualScenario{{ID: "warning", Entry: "/onboarding", State: "picker open", Viewport: "1440x900", Expected: []string{"warning visible"}}}, + Items: []PRVisualEvidenceItem{{ + ScenarioID: "warning", Path: pngPath, Viewport: "1440x900", + CapturedAt: time.Now().UTC().Truncate(time.Second).Format(time.RFC3339), + Status: "captured", PrivacyStatus: "human-reviewed", + }}, + Publication: PRVisualPublication{State: "pending"}, + }); err != nil { + t.Fatal(err) + } +} + +func TestCommittingPreviewNeverInvalidatesPassVisualEvidence(t *testing.T) { + repo := prTestRepoConfigured(t, func(config *ProjectConfig) { + config.Workflow.PRVisualEvidence = "suggest" + }) + activateManagedFeature(t, repo, "reviewer-ready") + captureCommit := runGit(t, repo, "rev-parse", "HEAD") + context, err := PreparePRContext(PRContextOptions{Repo: repo, Feature: "reviewer-ready"}) + if err != nil { + t.Fatal(err) + } + savePassVisualManifest(t, repo, "reviewer-ready", captureCommit, context.ProductDiffSHA256) + fresh, err := PreparePRContext(PRContextOptions{Repo: repo, Feature: "reviewer-ready"}) + if err != nil { + t.Fatal(err) + } + if fresh.PRVisualEvidenceStatus != "PASS" || fresh.PRVisualEvidenceCount != 1 { + t.Fatalf("recorded PASS evidence was not trusted: %#v", fresh) + } + previewPath := writePreview(t, repo, fresh, "Keep evidence trusted across the preview commit", visualEvidenceBody(managedPRBody(), fresh.PRVisualEvidenceStatus)) + runGit(t, repo, "add", fresh.PreviewPath) + runGit(t, repo, "commit", "-m", "record exact PR preview") + committed, err := PreparePRContext(PRContextOptions{Repo: repo, Feature: "reviewer-ready"}) + if err != nil { + t.Fatal(err) + } + if committed.PRVisualEvidenceStatus != "PASS" { + t.Fatalf("committing the reviewed pr.md invalidated PASS evidence: %s", committed.PRVisualEvidenceStatus) + } + if committed.PRVisualEvidenceFingerprint != fresh.PRVisualEvidenceFingerprint { + t.Fatalf("preview commit changed the visual evidence fingerprint") + } + if _, _, err := CheckPRPreview(repo, previewPath); err != nil { + t.Fatalf("committed preview no longer checks: %v", err) + } + if committed.PRVisualEvidence == nil || committed.PRVisualEvidence.SourceCommit != captureCommit { + t.Fatalf("evidence provenance lost its capture commit: %#v", committed.PRVisualEvidence) + } + if template := PRPreviewTemplate(committed); !strings.Contains(template, captureCommit) { + t.Fatalf("preview template does not name the capture commit") + } +} + +func TestProductDiffChangeInvalidatesPassVisualEvidence(t *testing.T) { + repo := prTestRepoConfigured(t, func(config *ProjectConfig) { + config.Workflow.PRVisualEvidence = "suggest" + }) + activateManagedFeature(t, repo, "reviewer-ready") + context, err := PreparePRContext(PRContextOptions{Repo: repo, Feature: "reviewer-ready"}) + if err != nil { + t.Fatal(err) + } + savePassVisualManifest(t, repo, "reviewer-ready", runGit(t, repo, "rev-parse", "HEAD"), context.ProductDiffSHA256) + config, _, err := LoadConfig(filepath.Join(repo, ".product-loop", "project.json")) + if err != nil { + t.Fatal(err) + } + changedDiff := strings.Repeat("c", 64) + _, status, _, _, _, _, _, err := resolvePRVisualEvidence(repo, config, "managed", "reviewer-ready", context.HeadBranch, changedDiff) + if err != nil { + t.Fatal(err) + } + if status != "NOT_VERIFIED" { + t.Fatalf("product change did not stale the evidence: %s", status) + } + config.Workflow.PRVisualEvidence = "require" + _, status, _, _, _, _, _, err = resolvePRVisualEvidence(repo, config, "managed", "reviewer-ready", context.HeadBranch, changedDiff) + if err != nil { + t.Fatal(err) + } + if status != "BLOCKED" { + t.Fatalf("require did not coerce stale evidence to BLOCKED: %s", status) + } +} + func TestPublishPRRequiresExactConfirmationAndUsesBodyWithoutFrontmatter(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("fake gh fixture uses a POSIX shell; publication behavior is covered by cross-platform pure-Go checks") diff --git a/docs/evidence-engineered-coding.md b/docs/evidence-engineered-coding.md index 76798ca..a03f380 100644 --- a/docs/evidence-engineered-coding.md +++ b/docs/evidence-engineered-coding.md @@ -146,6 +146,6 @@ Delivery and system improvement also remain separate. A failed task may suggest ## What is evidence-backed -The current moves were derived from the Intelligence Flow benchmark corpus and product-repository studies. The generated source commit is [`90356bed91109de2eb131fc95872bdb74abac6a7`](https://github.com/operatorstack/intelligence-flow/tree/90356bed91109de2eb131fc95872bdb74abac6a7/labs/12-product-engineering-loop). +The current moves were derived from the Intelligence Flow benchmark corpus and product-repository studies. The generated source commit is [`d604f832578355bd5bc34eb66e8d54b062645629`](https://github.com/operatorstack/intelligence-flow/tree/d604f832578355bd5bc34eb66e8d54b062645629/labs/12-product-engineering-loop). The evidence supports specific failure mechanisms and guardrails. It does not establish that Boatstack is optimal, that control-theory notation proves software quality, or that one workflow dominates every team. Those are evaluation questions, so the distribution preserves measurements, provenance, gaps, and negative results. diff --git a/docs/public-claims.json b/docs/public-claims.json index af3d458..cb5ef8f 100644 --- a/docs/public-claims.json +++ b/docs/public-claims.json @@ -1,6 +1,6 @@ { "schema_version": 1, - "source_commit": "90356bed91109de2eb131fc95872bdb74abac6a7", + "source_commit": "d604f832578355bd5bc34eb66e8d54b062645629", "statuses": ["verified", "observed", "still_being_evaluated"], "claims": [ { @@ -12,7 +12,7 @@ "readable_evidence": "why-these-steps.md#portable-workflow-and-state", "implementation": ["../boatstack/export.go", "../boatstack/references/artifacts.md", "../boatstack/references/workflow.md"], "verification": ["../boatstack/export_test.go"], - "last_verified_version": "source:90356bed91109de2eb131fc95872bdb74abac6a7" + "last_verified_version": "source:d604f832578355bd5bc34eb66e8d54b062645629" }, { "id": "human-decisions", @@ -23,7 +23,7 @@ "readable_evidence": "why-these-steps.md#human-decisions", "implementation": ["../boatstack/references/workflow.md", "../boatstack/plan.go"], "verification": ["../boatstack/plan_test.go", "../boatstack/planning_test.go"], - "last_verified_version": "source:90356bed91109de2eb131fc95872bdb74abac6a7" + "last_verified_version": "source:d604f832578355bd5bc34eb66e8d54b062645629" }, { "id": "validation-provenance", @@ -34,7 +34,7 @@ "readable_evidence": "why-these-steps.md#validation-provenance", "implementation": ["validation-and-evidence.md", "../boatstack/plan.go"], "verification": ["../boatstack/plan_test.go"], - "last_verified_version": "source:90356bed91109de2eb131fc95872bdb74abac6a7" + "last_verified_version": "source:d604f832578355bd5bc34eb66e8d54b062645629" }, { "id": "irreversible-operations", @@ -46,7 +46,7 @@ "readable_evidence": "why-these-steps.md#irreversible-operations", "implementation": ["safety.md", "../boatstack/safety.go", "../boatstack/hooks.go"], "verification": ["../boatstack/safety_test.go", "../boatstack/hooks_test.go"], - "last_verified_version": "source:90356bed91109de2eb131fc95872bdb74abac6a7" + "last_verified_version": "source:d604f832578355bd5bc34eb66e8d54b062645629" }, { "id": "reviewer-ready-pr", @@ -57,7 +57,7 @@ "readable_evidence": "why-these-steps.md#reviewer-ready-pr", "implementation": ["../boatstack/pr.go", "getting-started.md"], "verification": ["../boatstack/pr_test.go"], - "last_verified_version": "source:90356bed91109de2eb131fc95872bdb74abac6a7" + "last_verified_version": "source:d604f832578355bd5bc34eb66e8d54b062645629" }, { "id": "phase-scoped-delivery", @@ -68,7 +68,7 @@ "readable_evidence": "why-these-steps.md#phase-scoped-delivery", "implementation": ["../boatstack/delivery.go", "../boatstack/safety.go", "../boatstack/hooks.go", "../boatstack/references/workflow.md"], "verification": ["../boatstack/delivery_test.go", "../boatstack/pr_test.go"], - "last_verified_version": "source:90356bed91109de2eb131fc95872bdb74abac6a7" + "last_verified_version": "source:d604f832578355bd5bc34eb66e8d54b062645629" }, { "id": "model-neutral-contract", @@ -79,7 +79,7 @@ "readable_evidence": "why-these-steps.md#model-choice-and-budget", "implementation": ["research-and-design.md", "../boatstack/references/workflow.md"], "verification": ["../boatstack/export_test.go", "../boatstack/planning_test.go"], - "last_verified_version": "source:90356bed91109de2eb131fc95872bdb74abac6a7" + "last_verified_version": "source:d604f832578355bd5bc34eb66e8d54b062645629" }, { "id": "cross-model-failures", @@ -90,7 +90,7 @@ "readable_evidence": "why-these-steps.md#model-choice-and-budget", "implementation": ["research-and-design.md"], "verification": ["benchmark-corpus-audit.md", "benchmark-submission-audit.md"], - "last_verified_version": "source:90356bed91109de2eb131fc95872bdb74abac6a7" + "last_verified_version": "source:d604f832578355bd5bc34eb66e8d54b062645629" }, { "id": "lower-cost-outcomes", @@ -101,7 +101,7 @@ "readable_evidence": "why-these-steps.md#model-choice-and-budget", "implementation": ["research-and-design.md"], "verification": ["benchmark-corpus-audit.md", "benchmark-submission-audit.md"], - "last_verified_version": "source:90356bed91109de2eb131fc95872bdb74abac6a7" + "last_verified_version": "source:d604f832578355bd5bc34eb66e8d54b062645629" }, { "id": "git-worktree-activation", @@ -112,7 +112,7 @@ "readable_evidence": "why-these-steps.md#git-worktree-activation", "implementation": ["../boatstack/runtime_cache.go", "../boatstack/hooks.go"], "verification": ["../boatstack/runtime_cache_test.go", "../boatstack/hooks_test.go"], - "last_verified_version": "source:90356bed91109de2eb131fc95872bdb74abac6a7" + "last_verified_version": "source:d604f832578355bd5bc34eb66e8d54b062645629" }, { "id": "visible-updates", @@ -123,7 +123,7 @@ "readable_evidence": "why-these-steps.md#visible-updates", "implementation": ["../boatstack/update.go", "../boatstack/init.go"], "verification": ["../boatstack/update_test.go", "../boatstack/init_test.go", "../boatstack/export_test.go"], - "last_verified_version": "source:90356bed91109de2eb131fc95872bdb74abac6a7" + "last_verified_version": "source:d604f832578355bd5bc34eb66e8d54b062645629" } ] } diff --git a/labs/diagram-json/plan.lock.json b/labs/diagram-json/plan.lock.json index 32b57cc..9a9097a 100644 --- a/labs/diagram-json/plan.lock.json +++ b/labs/diagram-json/plan.lock.json @@ -6,7 +6,7 @@ "plan_path": "labs/diagram-json/plan.md", "plan_sha256": "3cc4f533b8d69386deff16b3a594a3ba09d4c0c3db636cccd8c4380084ce6a51", "schema_version": 1, - "source_commit": "90356bed91109de2eb131fc95872bdb74abac6a7", + "source_commit": "d604f832578355bd5bc34eb66e8d54b062645629", "source_plan_path": "labs/diagram-json/source-plan.md", "source_plan_sha256": "e10593ddaa7522ab80cc991d0a09399257139799e37f737794cd49d68a39985b", "spec_path": "labs/diagram-json/spec.md", diff --git a/release-notes/2026-07-30-visual-evidence-survives-preview-commit.md b/release-notes/2026-07-30-visual-evidence-survives-preview-commit.md new file mode 100644 index 0000000..b987cf2 --- /dev/null +++ b/release-notes/2026-07-30-visual-evidence-survives-preview-commit.md @@ -0,0 +1,3 @@ +### Visual evidence stays trusted when you commit the reviewed pr.md + +Boatstack now trusts recorded visual evidence by product identity: the manifest stays `PASS` while the product diff is unchanged. Before this change, the mandatory commit of the reviewed `pr.md` moved the head commit and always degraded `PASS` evidence to `NOT_VERIFIED`, so `require` could not reach publication with current screenshots. Any change to product content still makes the evidence stale immediately. The capture commit stays recorded and the PR preview names it in the Visual evidence table.