Skip to content

Commit 986a60e

Browse files
Sync Boatstack from Intelligence Flow Labs @ 7bcc7dcb692a (#158)
Co-authored-by: operator-stack-publisher[bot] <operator-stack-publisher[bot]@users.noreply.github.com>
1 parent e23bb3d commit 986a60e

19 files changed

Lines changed: 287 additions & 58 deletions

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# Contributing
44

5-
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/e5dbf426e4417f4f2f73c9f855d34808f7fa8f9a/labs/12-product-engineering-loop).
5+
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/7bcc7dcb692a3f4b34f6cbd84d46e648e055634e/labs/12-product-engineering-loop).
66

77
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.
88

UPSTREAM.json

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
},
1313
"files": {
1414
".gitignore": "a7079e923a776f14f1bb3a6aa0a11a133a8e1dfb35af020f327623357b7e3957",
15-
"CONTRIBUTING.md": "c6a8df3d94c8dd4228cbd6e1d85223ebd3f64613b31a534ee34f9d12c4ccee1f",
15+
"CONTRIBUTING.md": "d384c4c6422684eddae5de40a837210dd0a40712caecff478a41d17563e77e7e",
1616
"README.md": "3ce3e95e511089b44e946a44b8d5f4f81d019ece5336db65b2cab1f9dc4d4dad",
1717
"assets/boatstack-journey.svg": "e465befc50c8ce30f3e07e8fd97012931beeb053392c8fbf38ad645023b3cc63",
1818
"assets/boatstack-mark.svg": "be1f984da1bfa69fa5d1f986d8343d21f7e20921b71db888c928b4d2e54b09b5",
@@ -36,15 +36,15 @@
3636
"boatstack/atomic_unix.go": "89f2723361591de2bb8bd22ce7e34ec529d3278509f0df78fd5c4a7d4140fbe9",
3737
"boatstack/atomic_windows.go": "cefd775cbe7e7c3bd8a3f5673b11cdd784c6d3ebd6de7dcb8f39406b0bee511f",
3838
"boatstack/attach.go": "6a855440fac9acc63be857efef9d76210a4619774728684832dfbb08caf42a30",
39-
"boatstack/capability.go": "fc6bccb0ce4df579b81252ecffeb23c68d62f02ff041bfec980d844baf0885a3",
40-
"boatstack/capability_test.go": "e8322903a843970d7f0317d2629466532cb05c3ffcb44b9423adf4219faa8021",
41-
"boatstack/capture.go": "98a29fedcd7f6a95fadc9bac8c1bd601fee24c17cd64a6a76892e068f040ef66",
42-
"boatstack/capture_test.go": "7072d0c5f9ab0ea9ef493f9e6dcb01cf0de544514c8201e30b8e4e6a4065505a",
39+
"boatstack/capability.go": "270288270ac9689551232e772d1656acbb4c3b2e730c441beb7064b53dd21836",
40+
"boatstack/capability_test.go": "4537db261b48311f6fd50f4bdf7c88b0795407dcb2ea627273d8fbd167c40892",
41+
"boatstack/capture.go": "dc94692728f72626f2818fcb0f76383a19254592299b0afbec848bfbd7e1c734",
42+
"boatstack/capture_test.go": "84a74efde7ae6f9f0900c764f0cea6d84b00c1f71efb1a34f62e6f81d5104f7f",
4343
"boatstack/changelog.go": "6b06be7cd9738de29ba6e87aa2569f3b027a2e618b04524f5abd7abaa17945bf",
4444
"boatstack/changelog_test.go": "ce792f23a7fe1e09fb3096cd1314130a6ab69321d4877b12a8e994027541baf7",
4545
"boatstack/cmd/boatstack-helper/coverage_conformance_test.go": "5c10219591d5b9998361ed83eac88debb4d4c338c835c7502b709ed81cf151b6",
4646
"boatstack/cmd/boatstack-helper/flow.go": "0d41c7a86b49004e897f59551780220841d5941396202c81de97a4d52f593520",
47-
"boatstack/cmd/boatstack-helper/main.go": "903a233fdbe706e9281b9840abda458e78035b8379a99c9c97dd922832430a64",
47+
"boatstack/cmd/boatstack-helper/main.go": "538f56dc211e2332d6d49b1fe6885b8b1fecc3f6a5a86a6b555f01f096973318",
4848
"boatstack/cmd/boatstack-helper/main_test.go": "b36c52d6d5c9dd2428730de10ff18194b7e32a98722e41341c301c6f7a04cad5",
4949
"boatstack/cmd/boatstack-helper/retro.go": "68b83e33ade5b5fec126c70ec798fdcbed22fda755dc1cad2758143fead8e187",
5050
"boatstack/command.go": "4726ac515dedab4947be7eb48f88c6cb8b53d674124504b69f03e6396b080ee8",
@@ -159,19 +159,19 @@
159159
"boatstack/paths.go": "f9a615f35e0f439d6f11c48e881f11db26c0029e1eb7dd5978941cf51c74e710",
160160
"boatstack/plan.go": "a130c9e587f97a280b6cfa531bcaab46fc6e58772f27921031be03eb98f04439",
161161
"boatstack/plan_test.go": "1b01e7d9d7794eb11c998e19a2f3532d3b8509d984eca934cf5f1662a0a7e573",
162-
"boatstack/plan_validation.go": "99e40806fd579ff72de53f391cd6124acc9ff18707ecf9676c5e507b738d87d0",
163-
"boatstack/plan_validation_test.go": "6cbde4ac719baef6b73aa569515d6a9daadcbf14b33f76fa78159826954e20fa",
162+
"boatstack/plan_validation.go": "48c4fd061257f821ed54a0892b291cc04450e93e3db48f358f8c6599b21f9e41",
163+
"boatstack/plan_validation_test.go": "406c672470e909cb8d54f42175952c581ea872fa21bdcf34675f23187688fe33",
164164
"boatstack/planning.go": "63f2dcfcce85a3c7a39d183b5a557f7085f306ed57b130282e0678209b05c6c8",
165165
"boatstack/planning_first_write_conformance_test.go": "873097aa9384b75bf01e74a475f3ec2ac7cca4a28f733e82f2c82959032c6a30",
166166
"boatstack/planning_test.go": "06ec7022222d926040c3ae28b84ab50c3d2f804ae6473e61b303804dd992d884",
167167
"boatstack/post_publish_prescribe_conformance_test.go": "3c20d359ff84648db7dedb227b4d64e6574d9f41d3cdca0adefec1c60bfbf4ae",
168-
"boatstack/pr.go": "57a9f63fc622dc76bd2382cf2ccb9099b447d37eafadc457e3743217eba7a714",
168+
"boatstack/pr.go": "4c6952a9481d0c5d39f4ef92fef0de026f3a5cf1e4c1cf23525626ede766917f",
169169
"boatstack/pr_phase.go": "59f8cbb75b6b538a5345474acd6a725450979579bf8ecf9591956cbbe1cc4737",
170170
"boatstack/pr_phase_conformance_test.go": "bc9c834e9c4ed43b35d81abafd7b1bf2a264ea2a8c4a4ec9758ee18d1d438968",
171171
"boatstack/pr_test.go": "5c0ff03eb21e383026a4e9fbc5671b2e316040e4e4c55ab6583b930e7e117dda",
172172
"boatstack/provenance.go": "d44dcd5421306269326f1202ba1d52df8c252490550270ef9d022e8ec2b65210",
173-
"boatstack/provision.go": "eb7333a73331b011adc93f59a2d97415d850c2e588f0e2bbb5116984e2ef927d",
174-
"boatstack/provision_test.go": "214e9edb991a66d5bbb696a7c1b63876d2f799f2cab4e3f40785f4e8f1eac57b",
173+
"boatstack/provision.go": "9531f975f09f61ba3eed120ca7518cdc53b1e0df9f45a4da2bd7f1f8d96984fe",
174+
"boatstack/provision_test.go": "70199eac574cc8843ce12f2bad58b7fea0f86a4205a6d3be0a96594abd967b5d",
175175
"boatstack/publication_ignored_repro_test.go": "b6f3aeb8ba22949ff9af7ac5afe8fb828385d9708d5d5893ef41f33a3de873e1",
176176
"boatstack/published_slice_routing_test.go": "ea7e7351018bc13dcd31c4b96f50f8bc230e8a1dbf7806fba32a12ae58923e7e",
177177
"boatstack/readiness.go": "30ddf87d650c92e66f4393c6d18b6728b0b38f0831f564159998be2dbe12b708",
@@ -182,7 +182,7 @@
182182
"boatstack/reexec_unix.go": "ff86157a9aa20c82a56fcd859b70669b7eacf4e0a9f61a4546ef33808437939e",
183183
"boatstack/reexec_windows.go": "f5335c8c28cb4e89048b058b1c4d12f78644f99acb4f6167ff60e622dfb9e742",
184184
"boatstack/references/artifacts.md": "e362b2663c904beb8daa777c997f9fcb2ed0e70442ed2e7bef3b169895d170f1",
185-
"boatstack/references/config-schema.md": "01a3b7a9269a08a5cd2a86e967940c1e57f78e166db0b7289bff7a98732c8691",
185+
"boatstack/references/config-schema.md": "d295a7ecc5545cffad056ada6b9681ab82545b68ed92a4a8270a775b444f5a18",
186186
"boatstack/references/failure-moves.md": "b65ef72035afa6ad0dce589a0b38f84bc40cde3864c9ecf973f08fc687f001c3",
187187
"boatstack/references/host-hook-contracts.md": "2a89d44d0e418a53f2e3b6300fed957cdf878f45ea97ce24b55b66065f0eaa1d",
188188
"boatstack/references/irreversible-operation-boundary.md": "e0076f0fea3bf729b2e9bdf353eaeaaf7cdafabfaf26b8d9b27287e5414c2441",
@@ -218,7 +218,7 @@
218218
"boatstack/update_publication_test.go": "c5f32578db53be65e35452d5e8b4520884354e4e80a370dcec19860ea644d091",
219219
"boatstack/update_test.go": "bf5f19f8499db6dd7356917d867b113b790548ab89bfdea59e2adf4999a82a6a",
220220
"boatstack/visual_attach_conformance_test.go": "a376f032cfc59615fe4d403bdb8d7c7d3972dc7de334c3f6763edf1531ff1f03",
221-
"boatstack/visual_evidence.go": "f9fbde0e89f0ce8d9503939197d9b92cc8da377053007371d28360e29715a84a",
221+
"boatstack/visual_evidence.go": "682debd135dea3835c5d5a2a8eb4bff1d3bb814c30990a9ea71cf00bbdae9adb",
222222
"boatstack/visual_evidence_test.go": "b2e50093080d4c2381c8f441a4e1cfeca714fa3d034e7536835b7077fad0556a",
223223
"boatstack/visual_publisher.go": "ec5e95228b48e4ec20731975606048f5e732c4e09a7fd175770d4b15e447cc88",
224224
"boatstack/visual_publisher_test.go": "979f600edae00c77569ae753b80530e8cbf2e3b342995efcd992d351ff382eaf",
@@ -231,11 +231,11 @@
231231
"docs/account-recovery-walkthrough.md": "676034974594a7d1a559b24dbed31d7ccc429eb81404b203ca07bbdaa19ec3d3",
232232
"docs/benchmark-corpus-audit.md": "f2d206fe8579a514f9da82b2c96c19b343ac004be67617e1bd34f0f8e0e5e6c6",
233233
"docs/benchmark-submission-audit.md": "9518abdd17690729c6423f87cab20418ed47b0915b5faa44b9ef975e9e9c3b79",
234-
"docs/configuration.md": "8c5b0a6a8394333165dd0b2b1a70ca74e9d53f784b478e2ef7f1b12d93bc03b0",
235-
"docs/evidence-engineered-coding.md": "a0bc4ce88390774ea8ad188173264024505dc84e554820b3b219944e11cfe870",
234+
"docs/configuration.md": "675cfe8fc5d97bea8d925d47a3c3b852cf6cebbb050d2b4d0266684d2c531d3b",
235+
"docs/evidence-engineered-coding.md": "059d890ee788558964a5ffc2839674194434590ee8360e1b8352c9e94e5a1a8e",
236236
"docs/generated-files.md": "437791765b0a4015032ae21d1a6618563cad92b7402819e4f963bf5ae16284a3",
237237
"docs/getting-started.md": "51c2823f21e35140d31e6d5083dc4b89fddd24721ac6acc474154a4da53ee9f8",
238-
"docs/public-claims.json": "dd049cd3c8e23362bdd0bfcf2090015432f309e6298ab8b45b67741b780d170a",
238+
"docs/public-claims.json": "6495ef482d4d92fa0f10d84e70f36c1e1c5f57dbd801224c6bc25a76d41138a0",
239239
"docs/public-surface.md": "713f7a050b5f339cf948299103ef3800417dccfecf2cc1a4166397ea6f978907",
240240
"docs/research-and-design.md": "8d78678108f0a6c924e1ff9b32c0f81aae9d1f779e0082843b6f99ad993ae2b6",
241241
"docs/safety.md": "7b9b5c515d36e683767ec8d3d9d6d119ac93650b2f629d351deadd4c600ed6a6",
@@ -249,7 +249,7 @@
249249
"labs/diagram-json/compiled/evidence.md": "1ba1c989ade070a8ef9a508fbd788d100d7292f2dbacbb2bce895468019f619d",
250250
"labs/diagram-json/compiled/tasks.json": "88f60851abf79d851e9fccc754ff3040034ae595306bc87d64784c19eb403e71",
251251
"labs/diagram-json/compiled/test-matrix.json": "424657ff505768e50fa113801fd8363364a18269d5297480907a993d44063a39",
252-
"labs/diagram-json/plan.lock.json": "eebacf19b2b399d8cb6bbd3aad96e9e1705e369b69561f8841aeeb06f5d68e03",
252+
"labs/diagram-json/plan.lock.json": "bdf5a145a1d663b2157e1e8afc19ac4829c6476e10206e312e8d5e32695db37e",
253253
"labs/diagram-json/plan.md": "3cc4f533b8d69386deff16b3a594a3ba09d4c0c3db636cccd8c4380084ce6a51",
254254
"labs/diagram-json/questions.md": "74733b015002c8a6777c558e7e997fa48c94850b9bd39054fe9366c97ecf728d",
255255
"labs/diagram-json/request.md": "0808fc41c36779c404f4a3a121167da6e76cac56df526e70f9ed6d3e0d4c02ed",
@@ -392,14 +392,15 @@
392392
"release-notes/2026-07-28-retromine-recurrence-detector.md": "27790993a02e73f3a2700dce7340d044aeb0e52f785ded68271ff6673add9e25",
393393
"release-notes/2026-07-29-readiness-and-journey-control.md": "2411db76974990ad0128fcd90e795c4c1c5c70d6d6f15fccd452f7e1a242310b",
394394
"release-notes/2026-07-30-auto-capture-on-ship.md": "49aa077ac52005d380ba2cae9e406c7d0a9941157c40aa4ed44b9e59195b3837",
395+
"release-notes/2026-07-30-per-surface-capture-harnesses.md": "6489002d0f95088be0d900b7699800d830ba8a1f8e299251405cefe664c91d5b",
395396
"release-notes/2026-07-30-plan-approved-scenarios-escalate-to-require.md": "21c3f2be51b834fc2eb7662236db2549e80b1a1a0665a0721e773b7736e2c079",
396397
"release-notes/2026-07-30-visual-attach-retry-prescription.md": "38b610685a7a62c0341ba21b273b42c24d82ca803de6d8f694754156eaa606a9",
397398
"release-notes/2026-07-30-visual-evidence-survives-preview-commit.md": "71d19e9fabb40e8cb1e939f26bf288911d227979926f43f337046c6cf6b66551"
398399
},
399400
"generator": "operatorstack/intelligence-flow:boatstack-distribution",
400401
"schema_version": 1,
401402
"source": {
402-
"commit": "e5dbf426e4417f4f2f73c9f855d34808f7fa8f9a",
403+
"commit": "7bcc7dcb692a3f4b34f6cbd84d46e648e055634e",
403404
"path": "labs/12-product-engineering-loop",
404405
"repository": "operatorstack/intelligence-flow"
405406
}

boatstack/capability.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,20 @@ func ResolveCapability(name string, config ProjectConfig) (CapabilityResolution,
6868
}
6969
return CapabilityResolution{Name: capability.Name, Kind: "unavailable"}, nil
7070
}
71+
72+
// ResolveCapabilityForSurface resolves a capability command for one product
73+
// surface: the surface-scoped key ("visual:web") outranks the global alias
74+
// ladder, and an empty or unregistered surface falls back to it exactly — a
75+
// repository with only a global command keeps serving every surface.
76+
func ResolveCapabilityForSurface(name, surface string, config ProjectConfig) (CapabilityResolution, error) {
77+
capability, ok := LookupCapability(name)
78+
if !ok {
79+
return CapabilityResolution{}, fmt.Errorf("unknown evidence capability %q", name)
80+
}
81+
if surface = strings.TrimSpace(surface); surface != "" {
82+
if command := strings.TrimSpace(config.Project.Commands[capability.Name+":"+surface]); command != "" {
83+
return CapabilityResolution{Name: capability.Name, Kind: "repository-command", Command: command}, nil
84+
}
85+
}
86+
return ResolveCapability(name, config)
87+
}

boatstack/capability_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,40 @@ func TestLookupCapabilityExposesRegisteredMetadata(t *testing.T) {
5959
t.Fatalf("visual capability metadata is incomplete: %#v", capability)
6060
}
6161
}
62+
63+
// Invariant: a surface-scoped command outranks the global alias, and its
64+
// absence falls back to the global ladder exactly — a repository with one
65+
// global harness keeps serving every surface (zero-value behavior).
66+
func TestResolveCapabilityForSurfacePrefersSurfaceScopedCommand(t *testing.T) {
67+
config := testConfig()
68+
delete(config.Project.Commands, "visual")
69+
delete(config.Project.Commands, "screenshot")
70+
delete(config.Project.Commands, "e2e")
71+
config.Project.Commands["visual"] = "npm run capture:visual"
72+
config.Project.Commands["visual:web"] = "npm run capture:web"
73+
74+
resolution, err := ResolveCapabilityForSurface("visual", "web", config)
75+
if err != nil {
76+
t.Fatal(err)
77+
}
78+
if resolution.Command != "npm run capture:web" {
79+
t.Fatalf("surface key did not outrank the global alias: %#v", resolution)
80+
}
81+
for _, surface := range []string{"", "ops"} {
82+
resolution, err = ResolveCapabilityForSurface("visual", surface, config)
83+
if err != nil {
84+
t.Fatal(err)
85+
}
86+
if resolution.Command != "npm run capture:visual" {
87+
t.Fatalf("surface %q did not fall back to the global alias: %#v", surface, resolution)
88+
}
89+
}
90+
delete(config.Project.Commands, "visual")
91+
resolution, err = ResolveCapabilityForSurface("visual", "ops", config)
92+
if err != nil {
93+
t.Fatal(err)
94+
}
95+
if resolution.Kind != "unavailable" {
96+
t.Fatalf("unregistered surface with no global command must be unavailable: %#v", resolution)
97+
}
98+
}

boatstack/capture.go

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ func (execCaptureRunner) Run(request CaptureRequest) error {
4747
"BOATSTACK_CAPTURE_ENTRY="+request.Scenario.Entry,
4848
"BOATSTACK_CAPTURE_STATE="+request.Scenario.State,
4949
"BOATSTACK_CAPTURE_VIEWPORT="+request.Scenario.Viewport,
50+
"BOATSTACK_CAPTURE_SURFACE="+request.Scenario.Surface,
5051
"BOATSTACK_CAPTURE_OUTPUT="+request.OutputPath,
5152
)
5253
// The harness's authoritative output is the PNG on disk, not stdout; only
@@ -96,14 +97,6 @@ func CaptureEvidence(options CaptureEvidenceOptions) (PRVisualEvidenceManifest,
9697
if err != nil {
9798
return PRVisualEvidenceManifest{}, fmt.Errorf("capture requires a valid Boatstack project configuration: %w", err)
9899
}
99-
resolution, err := ResolveCapability(name, config)
100-
if err != nil {
101-
return PRVisualEvidenceManifest{}, err
102-
}
103-
if resolution.Kind != "repository-command" {
104-
return PRVisualEvidenceManifest{}, fmt.Errorf("evidence capability %q is unavailable: register a repository command (project.commands) or provision it first", name)
105-
}
106-
107100
relevance, source, scenarios, err := planVisualDecision(repo, feature)
108101
if err != nil {
109102
return PRVisualEvidenceManifest{}, err
@@ -114,6 +107,13 @@ func CaptureEvidence(options CaptureEvidenceOptions) (PRVisualEvidenceManifest,
114107
if len(scenarios) == 0 {
115108
return PRVisualEvidenceManifest{}, fmt.Errorf("no %s scenarios declared in the plan (pr_visual_evidence.scenarios)", name)
116109
}
110+
// Every scenario's command must resolve before any capture runs — a
111+
// surface-scoped key outranks the global alias; a missing surface key
112+
// with no global fallback is named exactly, never captured around.
113+
commands, err := resolveScenarioCaptureCommands(name, scenarios, config)
114+
if err != nil {
115+
return PRVisualEvidenceManifest{}, err
116+
}
117117

118118
head, err := gitCommand(repo, "rev-parse", "--abbrev-ref", "HEAD")
119119
if err != nil {
@@ -147,7 +147,7 @@ func CaptureEvidence(options CaptureEvidenceOptions) (PRVisualEvidenceManifest,
147147
items := make([]PRVisualEvidenceItem, 0, len(scenarios))
148148
for _, scenario := range scenarios {
149149
outputPath := filepath.Join(stagingDir, scenario.ID+".png")
150-
if err := captureScenario(repo, capability, resolution.Command, scenario, outputPath, feature, head, headCommit, diffHash, runner); err != nil {
150+
if err := captureScenario(repo, capability, commands[scenario.ID], scenario, outputPath, feature, head, headCommit, diffHash, runner); err != nil {
151151
return PRVisualEvidenceManifest{}, err
152152
}
153153
items = append(items, PRVisualEvidenceItem{
@@ -182,6 +182,28 @@ func CaptureEvidence(options CaptureEvidenceOptions) (PRVisualEvidenceManifest,
182182
return saved, nil
183183
}
184184

185+
// resolveScenarioCaptureCommands resolves the harness command for every
186+
// scenario up front (surface key first, global alias fallback), so capture
187+
// either runs with a complete command map or fails naming the exact missing
188+
// registration before any harness executes.
189+
func resolveScenarioCaptureCommands(name string, scenarios []PRVisualScenario, config ProjectConfig) (map[string]string, error) {
190+
commands := make(map[string]string, len(scenarios))
191+
for _, scenario := range scenarios {
192+
resolution, err := ResolveCapabilityForSurface(name, scenario.Surface, config)
193+
if err != nil {
194+
return nil, err
195+
}
196+
if resolution.Kind != "repository-command" {
197+
if surface := strings.TrimSpace(scenario.Surface); surface != "" {
198+
return nil, fmt.Errorf("evidence capability %q is unavailable for surface %q: register project.commands[%q] (capability-register --capability %s --surface %s --command <command>) or a global command", name, surface, name+":"+surface, name, surface)
199+
}
200+
return nil, fmt.Errorf("evidence capability %q is unavailable: register a repository command (capability-register --capability %s --command <command>) or provision it first", name, name)
201+
}
202+
commands[scenario.ID] = resolution.Command
203+
}
204+
return commands, nil
205+
}
206+
185207
// captureProductDiff reproduces the pr-context product-diff fingerprint so a
186208
// captured manifest is trusted (PASS) by resolvePRVisualEvidence: same product
187209
// diff. The head commit is recorded for provenance only — trust is keyed to

0 commit comments

Comments
 (0)