Skip to content

feat: ErrImageNotInRegistry sentinel + wfctl render-side actionable hint#579

Merged
intel352 merged 3 commits into
mainfrom
feat/iac-image-not-in-registry-sentinel
May 8, 2026
Merged

feat: ErrImageNotInRegistry sentinel + wfctl render-side actionable hint#579
intel352 merged 3 commits into
mainfrom
feat/iac-image-not-in-registry-sentinel

Conversation

@intel352

@intel352 intel352 commented May 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds interfaces.ErrImageNotInRegistry sentinel used by IaC drivers when an
    AppSpec image SHA is absent from the registry (e.g. DOCR GC pruned it).
  • Adds wfctl-side actionable hint (Plan + Apply paths): when the driver error
    is the sentinel (or its message string survives a gRPC plugin boundary),
    wfctl emits ::error::image not found in registry — Re-run the build/deploy workflow ... to stderr.
  • Additive only; no new resource type; no canonical-schema changes.

This is Wave 1 — PR 1 of the DOCR retention + image-presence design. Wave 2 (declarative retention reconciler in DO plugin) is deferred per ADR 0003 in core-dump.

The exact sentinel message string "iac: image tag or digest not found in registry" is load-bearing: the wfctl render layer matches it verbatim as the gRPC-boundary fallback (since structpb doesn't preserve sentinel identity). A unit test asserts the string is stable.

Test plan

  • go test ./interfaces/ -run \"TestSentinels|TestErrImageNotInRegistry\" — sentinel round-trip + message-string stability
  • go test ./cmd/wfctl/ -run TestImageNotInRegistryHint — typed-error path, gRPC string-match fallback, unrelated-error no-op, nil-input no-op
  • go test ./... — full repo, no regressions
  • go vet ./... clean
  • gofmt -l . clean

Sequenced PRs

  1. PR 1 (this) — workflow ErrImageNotInRegistry sentinel + wfctl hint → tag v0.24.0
  2. workflow-plugin-digitalocean AppPlatformDriver pre-flight → tag v0.12.0
  3. core-dump infra.yaml retention bump + workflow pin updates + tc2-cutover walk-back
  4. core-dump cleanup PR (after TC2 closes)

Rollback

Ship v0.24.1 that no-ops emitImageNotInRegistryHint. Do NOT retag v0.24.0 (Go module proxy is immutable; consumers' go.sum locks).

intel352 and others added 2 commits May 8, 2026 01:27
For IaC drivers that pre-flight image presence in the registry before
mutating an AppSpec (e.g. DOCR, ECR). The exact message string is
load-bearing: wfctl render-side actionable hint matches it verbatim as a
fallback for the gRPC plugin boundary, where structpb does not preserve
sentinel identity. Tests assert the message string is stable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…egistry

When wfctl infra apply/plan surfaces an error wrapping
interfaces.ErrImageNotInRegistry — either via errors.Is (in-process driver)
or via verbatim message-string match (gRPC plugin boundary, where structpb
does not preserve sentinel identity) — emit a single actionable hint to
stderr suggesting a re-run of the build/deploy workflow or an --image-sha
override.

Hooks:
- cmd/wfctl/infra_apply.go: aggregated error from result.Errors[]
  (both v1 + v2 apply paths)
- cmd/wfctl/infra.go: compute-plan error wrap site

4 unit tests cover typed-error path, gRPC string-match fallback,
unrelated-error no-op, and nil-input no-op.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 8, 2026 05:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an IaC-layer sentinel error (interfaces.ErrImageNotInRegistry) to represent “referenced image digest/tag is missing from the registry”, and wires wfctl infra plan/apply to emit a GitHub Actions–style actionable stderr hint when that condition is detected (including a string-match fallback for gRPC plugin boundaries).

Changes:

  • Introduces interfaces.ErrImageNotInRegistry with documentation describing typed (errors.Is) and cross-process string matching expectations.
  • Adds unit tests in interfaces/ to enforce sentinel wrapping semantics and to pin the exact error message string.
  • Adds cmd/wfctl hint emission helper + tests, and calls it from infra plan/apply failure paths.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
interfaces/iac_resource_driver.go Adds the ErrImageNotInRegistry sentinel and documents cross-process matching requirements.
interfaces/iac_resource_driver_test.go Extends sentinel tests and pins the error message string for gRPC string-match fallback.
cmd/wfctl/infra.go Emits an actionable hint on infra plan failures when image-missing sentinel is detected.
cmd/wfctl/infra_image_presence_hint.go Implements hint detection/emission (typed sentinel or fallback string match).
cmd/wfctl/infra_image_presence_hint_test.go Tests typed detection, gRPC string-fallback detection, and no-op behavior.
cmd/wfctl/infra_apply.go Emits an actionable hint when apply completes with per-resource errors (aggregated).

Comment thread cmd/wfctl/infra_image_presence_hint.go Outdated
Comment thread cmd/wfctl/infra_image_presence_hint.go Outdated
Comment thread cmd/wfctl/infra_apply.go
Comment thread cmd/wfctl/infra_apply.go
@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown

⏱ Benchmark Results

No significant performance regressions detected.

benchstat comparison (baseline → PR)
## benchstat: baseline → PR
baseline-bench.txt:260: parsing iteration count: invalid syntax
baseline-bench.txt:331710: parsing iteration count: invalid syntax
baseline-bench.txt:670471: parsing iteration count: invalid syntax
baseline-bench.txt:982253: parsing iteration count: invalid syntax
baseline-bench.txt:1318524: parsing iteration count: invalid syntax
baseline-bench.txt:1647574: parsing iteration count: invalid syntax
benchmark-results.txt:260: parsing iteration count: invalid syntax
benchmark-results.txt:315417: parsing iteration count: invalid syntax
benchmark-results.txt:610078: parsing iteration count: invalid syntax
benchmark-results.txt:920888: parsing iteration count: invalid syntax
benchmark-results.txt:1234737: parsing iteration count: invalid syntax
benchmark-results.txt:1532365: parsing iteration count: invalid syntax
goos: linux
goarch: amd64
pkg: github.com/GoCodeAlone/workflow/dynamic
cpu: AMD EPYC 7763 64-Core Processor                
                            │ benchmark-results.txt │
                            │        sec/op         │
InterpreterCreation-4                  7.131m ± 56%
ComponentLoad-4                        3.589m ± 12%
ComponentExecute-4                     1.963µ ±  1%
PoolContention/workers-1-4             1.099µ ±  1%
PoolContention/workers-2-4             1.084µ ±  2%
PoolContention/workers-4-4             1.093µ ±  2%
PoolContention/workers-8-4             1.085µ ±  1%
PoolContention/workers-16-4            1.101µ ±  1%
ComponentLifecycle-4                   3.581m ±  0%
SourceValidation-4                     2.329µ ±  1%
RegistryConcurrent-4                   807.4n ±  3%
LoaderLoadFromString-4                 3.603m ±  1%
geomean                                18.76µ

                            │ benchmark-results.txt │
                            │         B/op          │
InterpreterCreation-4                  2.027Mi ± 0%
ComponentLoad-4                        2.180Mi ± 0%
ComponentExecute-4                     1.203Ki ± 0%
PoolContention/workers-1-4             1.203Ki ± 0%
PoolContention/workers-2-4             1.203Ki ± 0%
PoolContention/workers-4-4             1.203Ki ± 0%
PoolContention/workers-8-4             1.203Ki ± 0%
PoolContention/workers-16-4            1.203Ki ± 0%
ComponentLifecycle-4                   2.183Mi ± 0%
SourceValidation-4                     1.984Ki ± 0%
RegistryConcurrent-4                   1.133Ki ± 0%
LoaderLoadFromString-4                 2.182Mi ± 0%
geomean                                15.25Ki

                            │ benchmark-results.txt │
                            │       allocs/op       │
InterpreterCreation-4                   15.68k ± 0%
ComponentLoad-4                         18.02k ± 0%
ComponentExecute-4                       25.00 ± 0%
PoolContention/workers-1-4               25.00 ± 0%
PoolContention/workers-2-4               25.00 ± 0%
PoolContention/workers-4-4               25.00 ± 0%
PoolContention/workers-8-4               25.00 ± 0%
PoolContention/workers-16-4              25.00 ± 0%
ComponentLifecycle-4                    18.07k ± 0%
SourceValidation-4                       32.00 ± 0%
RegistryConcurrent-4                     2.000 ± 0%
LoaderLoadFromString-4                  18.06k ± 0%
geomean                                  183.3

cpu: AMD EPYC 9V74 80-Core Processor                
                            │ baseline-bench.txt │
                            │       sec/op       │
InterpreterCreation-4              3.103m ± 213%
ComponentLoad-4                    3.512m ±   6%
ComponentExecute-4                 1.815µ ±   1%
PoolContention/workers-1-4         1.038µ ±   2%
PoolContention/workers-2-4         1.025µ ±   2%
PoolContention/workers-4-4         1.024µ ±   2%
PoolContention/workers-8-4         1.026µ ±   1%
PoolContention/workers-16-4        1.027µ ±   1%
ComponentLifecycle-4               3.536m ±   2%
SourceValidation-4                 2.082µ ±   1%
RegistryConcurrent-4               757.0n ±   3%
LoaderLoadFromString-4             3.565m ±   0%
geomean                            16.64µ

                            │ baseline-bench.txt │
                            │        B/op        │
InterpreterCreation-4               2.027Mi ± 0%
ComponentLoad-4                     2.180Mi ± 0%
ComponentExecute-4                  1.203Ki ± 0%
PoolContention/workers-1-4          1.203Ki ± 0%
PoolContention/workers-2-4          1.203Ki ± 0%
PoolContention/workers-4-4          1.203Ki ± 0%
PoolContention/workers-8-4          1.203Ki ± 0%
PoolContention/workers-16-4         1.203Ki ± 0%
ComponentLifecycle-4                2.183Mi ± 0%
SourceValidation-4                  1.984Ki ± 0%
RegistryConcurrent-4                1.133Ki ± 0%
LoaderLoadFromString-4              2.182Mi ± 0%
geomean                             15.25Ki

                            │ baseline-bench.txt │
                            │     allocs/op      │
InterpreterCreation-4                15.68k ± 0%
ComponentLoad-4                      18.02k ± 0%
ComponentExecute-4                    25.00 ± 0%
PoolContention/workers-1-4            25.00 ± 0%
PoolContention/workers-2-4            25.00 ± 0%
PoolContention/workers-4-4            25.00 ± 0%
PoolContention/workers-8-4            25.00 ± 0%
PoolContention/workers-16-4           25.00 ± 0%
ComponentLifecycle-4                 18.07k ± 0%
SourceValidation-4                    32.00 ± 0%
RegistryConcurrent-4                  2.000 ± 0%
LoaderLoadFromString-4               18.06k ± 0%
geomean                               183.3

pkg: github.com/GoCodeAlone/workflow/middleware
cpu: AMD EPYC 7763 64-Core Processor                
                                  │ benchmark-results.txt │
                                  │        sec/op         │
CircuitBreakerDetection-4                     285.6n ± 5%
CircuitBreakerExecution_Success-4             21.50n ± 1%
CircuitBreakerExecution_Failure-4             66.25n ± 1%
geomean                                       74.09n

                                  │ benchmark-results.txt │
                                  │         B/op          │
CircuitBreakerDetection-4                    144.0 ± 0%
CircuitBreakerExecution_Success-4            0.000 ± 0%
CircuitBreakerExecution_Failure-4            0.000 ± 0%
geomean                                                 ¹
¹ summaries must be >0 to compute geomean

                                  │ benchmark-results.txt │
                                  │       allocs/op       │
CircuitBreakerDetection-4                    1.000 ± 0%
CircuitBreakerExecution_Success-4            0.000 ± 0%
CircuitBreakerExecution_Failure-4            0.000 ± 0%
geomean                                                 ¹
¹ summaries must be >0 to compute geomean

cpu: AMD EPYC 9V74 80-Core Processor                
                                  │ baseline-bench.txt │
                                  │       sec/op       │
CircuitBreakerDetection-4                 303.6n ± 15%
CircuitBreakerExecution_Success-4         22.68n ±  0%
CircuitBreakerExecution_Failure-4         71.16n ±  1%
geomean                                   78.84n

                                  │ baseline-bench.txt │
                                  │        B/op        │
CircuitBreakerDetection-4                 144.0 ± 0%
CircuitBreakerExecution_Success-4         0.000 ± 0%
CircuitBreakerExecution_Failure-4         0.000 ± 0%
geomean                                              ¹
¹ summaries must be >0 to compute geomean

                                  │ baseline-bench.txt │
                                  │     allocs/op      │
CircuitBreakerDetection-4                 1.000 ± 0%
CircuitBreakerExecution_Success-4         0.000 ± 0%
CircuitBreakerExecution_Failure-4         0.000 ± 0%
geomean                                              ¹
¹ summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/module
cpu: AMD EPYC 7763 64-Core Processor                
                                 │ benchmark-results.txt │
                                 │        sec/op         │
JQTransform_Simple-4                        881.3n ± 31%
JQTransform_ObjectConstruction-4            1.453µ ± 24%
JQTransform_ArraySelect-4                   3.362µ ±  2%
JQTransform_Complex-4                       39.15µ ±  1%
JQTransform_Throughput-4                    1.777µ ±  1%
SSEPublishDelivery-4                        62.85n ±  0%
geomean                                     1.631µ

                                 │ benchmark-results.txt │
                                 │         B/op          │
JQTransform_Simple-4                      1.273Ki ± 0%
JQTransform_ObjectConstruction-4          1.773Ki ± 0%
JQTransform_ArraySelect-4                 2.625Ki ± 0%
JQTransform_Complex-4                     16.22Ki ± 0%
JQTransform_Throughput-4                  1.984Ki ± 0%
SSEPublishDelivery-4                        0.000 ± 0%
geomean                                                ¹
¹ summaries must be >0 to compute geomean

                                 │ benchmark-results.txt │
                                 │       allocs/op       │
JQTransform_Simple-4                        10.00 ± 0%
JQTransform_ObjectConstruction-4            15.00 ± 0%
JQTransform_ArraySelect-4                   30.00 ± 0%
JQTransform_Complex-4                       324.0 ± 0%
JQTransform_Throughput-4                    17.00 ± 0%
SSEPublishDelivery-4                        0.000 ± 0%
geomean                                                ¹
¹ summaries must be >0 to compute geomean

cpu: AMD EPYC 9V74 80-Core Processor                
                                 │ baseline-bench.txt │
                                 │       sec/op       │
JQTransform_Simple-4                     853.8n ± 31%
JQTransform_ObjectConstruction-4         1.437µ ±  5%
JQTransform_ArraySelect-4                3.592µ ±  2%
JQTransform_Complex-4                    41.89µ ±  2%
JQTransform_Throughput-4                 1.766µ ±  2%
SSEPublishDelivery-4                     63.69n ±  2%
geomean                                  1.658µ

                                 │ baseline-bench.txt │
                                 │        B/op        │
JQTransform_Simple-4                   1.273Ki ± 0%
JQTransform_ObjectConstruction-4       1.773Ki ± 0%
JQTransform_ArraySelect-4              2.625Ki ± 0%
JQTransform_Complex-4                  16.22Ki ± 0%
JQTransform_Throughput-4               1.984Ki ± 0%
SSEPublishDelivery-4                     0.000 ± 0%
geomean                                             ¹
¹ summaries must be >0 to compute geomean

                                 │ baseline-bench.txt │
                                 │     allocs/op      │
JQTransform_Simple-4                     10.00 ± 0%
JQTransform_ObjectConstruction-4         15.00 ± 0%
JQTransform_ArraySelect-4                30.00 ± 0%
JQTransform_Complex-4                    324.0 ± 0%
JQTransform_Throughput-4                 17.00 ± 0%
SSEPublishDelivery-4                     0.000 ± 0%
geomean                                             ¹
¹ summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/schema
cpu: AMD EPYC 7763 64-Core Processor                
                                    │ benchmark-results.txt │
                                    │        sec/op         │
SchemaValidation_Simple-4                       1.112µ ± 6%
SchemaValidation_AllFields-4                    1.691µ ± 3%
SchemaValidation_FormatValidation-4             1.600µ ± 2%
SchemaValidation_ManySchemas-4                  1.817µ ± 1%
geomean                                         1.529µ

                                    │ benchmark-results.txt │
                                    │         B/op          │
SchemaValidation_Simple-4                      0.000 ± 0%
SchemaValidation_AllFields-4                   0.000 ± 0%
SchemaValidation_FormatValidation-4            0.000 ± 0%
SchemaValidation_ManySchemas-4                 0.000 ± 0%
geomean                                                   ¹
¹ summaries must be >0 to compute geomean

                                    │ benchmark-results.txt │
                                    │       allocs/op       │
SchemaValidation_Simple-4                      0.000 ± 0%
SchemaValidation_AllFields-4                   0.000 ± 0%
SchemaValidation_FormatValidation-4            0.000 ± 0%
SchemaValidation_ManySchemas-4                 0.000 ± 0%
geomean                                                   ¹
¹ summaries must be >0 to compute geomean

cpu: AMD EPYC 9V74 80-Core Processor                
                                    │ baseline-bench.txt │
                                    │       sec/op       │
SchemaValidation_Simple-4                   1.129µ ± 15%
SchemaValidation_AllFields-4                1.660µ ±  2%
SchemaValidation_FormatValidation-4         1.603µ ±  2%
SchemaValidation_ManySchemas-4              1.632µ ±  2%
geomean                                     1.488µ

                                    │ baseline-bench.txt │
                                    │        B/op        │
SchemaValidation_Simple-4                   0.000 ± 0%
SchemaValidation_AllFields-4                0.000 ± 0%
SchemaValidation_FormatValidation-4         0.000 ± 0%
SchemaValidation_ManySchemas-4              0.000 ± 0%
geomean                                                ¹
¹ summaries must be >0 to compute geomean

                                    │ baseline-bench.txt │
                                    │     allocs/op      │
SchemaValidation_Simple-4                   0.000 ± 0%
SchemaValidation_AllFields-4                0.000 ± 0%
SchemaValidation_FormatValidation-4         0.000 ± 0%
SchemaValidation_ManySchemas-4              0.000 ± 0%
geomean                                                ¹
¹ summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/store
cpu: AMD EPYC 7763 64-Core Processor                
                                   │ benchmark-results.txt │
                                   │        sec/op         │
EventStoreAppend_InMemory-4                   1.192µ ± 19%
EventStoreAppend_SQLite-4                     1.358m ±  3%
GetTimeline_InMemory/events-10-4              14.22µ ±  2%
GetTimeline_InMemory/events-50-4              78.43µ ±  4%
GetTimeline_InMemory/events-100-4             124.0µ ±  1%
GetTimeline_InMemory/events-500-4             636.4µ ±  0%
GetTimeline_InMemory/events-1000-4            1.299m ±  1%
GetTimeline_SQLite/events-10-4                108.9µ ±  1%
GetTimeline_SQLite/events-50-4                252.7µ ±  1%
GetTimeline_SQLite/events-100-4               423.0µ ±  0%
GetTimeline_SQLite/events-500-4               1.804m ±  1%
GetTimeline_SQLite/events-1000-4              3.530m ±  1%
geomean                                       221.2µ

                                   │ benchmark-results.txt │
                                   │         B/op          │
EventStoreAppend_InMemory-4                     756.5 ± 6%
EventStoreAppend_SQLite-4                     1.984Ki ± 2%
GetTimeline_InMemory/events-10-4              7.953Ki ± 0%
GetTimeline_InMemory/events-50-4              46.62Ki ± 0%
GetTimeline_InMemory/events-100-4             94.48Ki ± 0%
GetTimeline_InMemory/events-500-4             472.8Ki ± 0%
GetTimeline_InMemory/events-1000-4            944.3Ki ± 0%
GetTimeline_SQLite/events-10-4                16.74Ki ± 0%
GetTimeline_SQLite/events-50-4                87.14Ki ± 0%
GetTimeline_SQLite/events-100-4               175.4Ki ± 0%
GetTimeline_SQLite/events-500-4               846.1Ki ± 0%
GetTimeline_SQLite/events-1000-4              1.639Mi ± 0%
geomean                                       67.10Ki

                                   │ benchmark-results.txt │
                                   │       allocs/op       │
EventStoreAppend_InMemory-4                     7.000 ± 0%
EventStoreAppend_SQLite-4                       53.00 ± 0%
GetTimeline_InMemory/events-10-4                125.0 ± 0%
GetTimeline_InMemory/events-50-4                653.0 ± 0%
GetTimeline_InMemory/events-100-4              1.306k ± 0%
GetTimeline_InMemory/events-500-4              6.514k ± 0%
GetTimeline_InMemory/events-1000-4             13.02k ± 0%
GetTimeline_SQLite/events-10-4                  382.0 ± 0%
GetTimeline_SQLite/events-50-4                 1.852k ± 0%
GetTimeline_SQLite/events-100-4                3.681k ± 0%
GetTimeline_SQLite/events-500-4                18.54k ± 0%
GetTimeline_SQLite/events-1000-4               37.29k ± 0%
geomean                                        1.162k

cpu: AMD EPYC 9V74 80-Core Processor                
                                   │ baseline-bench.txt │
                                   │       sec/op       │
EventStoreAppend_InMemory-4                1.048µ ± 13%
EventStoreAppend_SQLite-4                  1.097m ± 10%
GetTimeline_InMemory/events-10-4           12.58µ ±  1%
GetTimeline_InMemory/events-50-4           70.82µ ±  3%
GetTimeline_InMemory/events-100-4          112.5µ ± 24%
GetTimeline_InMemory/events-500-4          576.0µ ±  1%
GetTimeline_InMemory/events-1000-4         1.184m ±  1%
GetTimeline_SQLite/events-10-4             86.39µ ±  2%
GetTimeline_SQLite/events-50-4             225.3µ ±  3%
GetTimeline_SQLite/events-100-4            396.5µ ±  0%
GetTimeline_SQLite/events-500-4            1.738m ±  1%
GetTimeline_SQLite/events-1000-4           3.378m ±  3%
geomean                                    197.7µ

                                   │ baseline-bench.txt │
                                   │        B/op        │
EventStoreAppend_InMemory-4                 753.0 ± 12%
EventStoreAppend_SQLite-4                 1.985Ki ±  2%
GetTimeline_InMemory/events-10-4          7.953Ki ±  0%
GetTimeline_InMemory/events-50-4          46.62Ki ±  0%
GetTimeline_InMemory/events-100-4         94.48Ki ±  0%
GetTimeline_InMemory/events-500-4         472.8Ki ±  0%
GetTimeline_InMemory/events-1000-4        944.3Ki ±  0%
GetTimeline_SQLite/events-10-4            16.74Ki ±  0%
GetTimeline_SQLite/events-50-4            87.14Ki ±  0%
GetTimeline_SQLite/events-100-4           175.4Ki ±  0%
GetTimeline_SQLite/events-500-4           846.1Ki ±  0%
GetTimeline_SQLite/events-1000-4          1.639Mi ±  0%
geomean                                   67.08Ki

                                   │ baseline-bench.txt │
                                   │     allocs/op      │
EventStoreAppend_InMemory-4                  7.000 ± 0%
EventStoreAppend_SQLite-4                    53.00 ± 0%
GetTimeline_InMemory/events-10-4             125.0 ± 0%
GetTimeline_InMemory/events-50-4             653.0 ± 0%
GetTimeline_InMemory/events-100-4           1.306k ± 0%
GetTimeline_InMemory/events-500-4           6.514k ± 0%
GetTimeline_InMemory/events-1000-4          13.02k ± 0%
GetTimeline_SQLite/events-10-4               382.0 ± 0%
GetTimeline_SQLite/events-50-4              1.852k ± 0%
GetTimeline_SQLite/events-100-4             3.681k ± 0%
GetTimeline_SQLite/events-500-4             18.54k ± 0%
GetTimeline_SQLite/events-1000-4            37.29k ± 0%
geomean                                     1.162k

Benchmarks run with go test -bench=. -benchmem -count=6.
Regressions ≥ 20% are flagged. Results compared via benchstat.

4 findings from copilot-pull-request-reviewer:

1. Hint text referenced a non-existent --image-sha flag. Replaced with
   accurate "Re-run the build/deploy workflow ... then re-dispatch this
   operation" — operator-driven recovery via the workflow that pushes
   images, plus whatever re-dispatch path the caller supports
   (e.g. tc2-cutover.yml's workflow_dispatch.inputs.image_sha).

2. errImageNotInRegistryMessage const duplicated the sentinel's message
   string (drift risk). Replaced with direct interfaces.ErrImageNotInRegistry.Error()
   call. The interfaces-side stability test remains the guardrail.

3 + 4. emitImageNotInRegistryHint was only emitted from the aggregated
   result.Errors[] path. provider.Apply can surface a top-level err
   (especially the gRPC plugin path), which bypassed the hint. Added
   hint emission immediately before each "return fmt.Errorf(\"apply:
   %w\", err)" site (both v1 and v2 dispatch paths in infra_apply.go).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@intel352 intel352 merged commit 6db38ed into main May 8, 2026
23 checks passed
@intel352 intel352 deleted the feat/iac-image-not-in-registry-sentinel branch May 8, 2026 06:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants