Skip to content

refactor(iac/admin): remove scenario stubs from engine core + 4 RBAC-status fixes#818

Merged
intel352 merged 4 commits into
mainfrom
refactor/infra-admin-remove-scenario-stubs-from-core-2026-06-01T0155
Jun 1, 2026
Merged

refactor(iac/admin): remove scenario stubs from engine core + 4 RBAC-status fixes#818
intel352 merged 4 commits into
mainfrom
refactor/infra-admin-remove-scenario-stubs-from-core-2026-06-01T0155

Conversation

@intel352
Copy link
Copy Markdown
Contributor

@intel352 intel352 commented Jun 1, 2026

What

Two things, both arising from infra-admin v1.1 review/QA:

Part A — remove scenario test fixtures from the engine repo. PR #807 (merged) added a scenario_stub-tagged stub iac.provider + (#815, closed) an in-process enforcer into workflow core. Maintainer feedback: test fixtures must not live in the production engine repo — even build-tagged. The established pattern (scenarios 85/86/87) is a scenario-owned cmd/server/main.go. This removes: iac/stubprovider, plugins/stubprovider, plugins/all/extras_stub*.go, the scenarioExtras hook in plugins/all/all.go, and the stub-exclusion tests. DefaultPlugins() returns to its plain base set. The mutation integration test + handler tests migrate to local interfaces.IaCProvider test doubles (no production dependency). Scenario 92 will register its stub provider + enforcer from the scenario repo (workflow-scenarios PR).

Part B — 4 correctness bugs the live RBAC scenario surfaced in module/infra_admin.go + handlers:

  1. Contribution permissions payload shape []any{map[string]any{...}} (was []map[string]any).
  2. nil-store guards in ListResources + GetResource (panic when state_module unset).
  3. Typed 403-on-authz-denial: sentinel handler.ErrAuthzDenied → HTTP 403 via writeMutationResponse; provider/operational errors → 500. Replaces fragile strings.Contains(errMsg,"denied") (false-positived on provider "access denied").
  4. handlePlanResource now enforces (PlanResource is infra:apply-gated) + routes through the denial→403 path (was returning 200 for denied viewers).

New test TestInfraAdmin_TypedAuthzDenied_Returns403: viewer→/plan=403, viewer→/apply=403, operator→/apply=200, provider-error-with-"denied"-text→500 (not 403).

Verification

go test -race ./iac/admin/... ./plugins/all/ ./module/ green; go build ./cmd/server (no -tags) OK; golangci-lint --new-from-rev=origin/main 0 issues; grep-clean of stubprovider/localauthz/scenario_stub. Net: engine core is cleaner than before v1.1 (zero fixtures) + 4 robustness/correctness fixes. ADR for the architecture correction lands in the workspace docs.

🤖 Generated with Claude Code

intel352 and others added 2 commits June 1, 2026 11:03
Delete iac/stubprovider, plugins/stubprovider, and the scenario_stub build-tag
mechanism from plugins/all. Scenario test fixtures must not live in workflow
core; callers that needed stub IaCProvider behaviour now use inline types that
embed iac/iactest.NoopProvider or implement the interface directly.

Touched files:
- iac/stubprovider/ — deleted (provider.go + provider_test.go)
- plugins/stubprovider/ — deleted (plugin.go + plugin_test.go)
- plugins/all/extras_stub.go, extras_stub_test.go, extras_base_test.go — deleted
- plugins/all/all.go — remove scenarioExtras var + append; DefaultPlugins returns base only
- iac/admin/handler/*_test.go — replace stubprovider.New() with planningProvider
  (defined in list_resources_test.go; shared across package handler_test)
- module/infra_admin_mutation_integration_test.go — replace stubprovider.New()
  with testMutationProvider (inline type with real Plan+Destroy+ResourceDriver)

Grep clean: zero remaining refs to stubprovider/localauthz/scenario_stub.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bug 1 — contribution payload shape
  infra_admin.go: permissions field was []map[string]any (Go rejects type-assert
  to []any); changed to []any{map[string]any{…}} across all 4 contribution pipelines.

Bug 2 — nil-store guard
  handler/list_resources.go: panic when state_module not configured → explicit
  nil check returns typed error before store.ListResources call.
  handler/get_resource.go: same guard before store.GetResource.

Bug 3 — typed 403-on-authz-denial
  handler/authz.go: introduce handler.ErrAuthzDenied sentinel.
  handler/plan_resource.go, apply_resource.go, destroy_resource.go: evidence
  default-deny + server-side Enforcer denial now return (output, ErrAuthzDenied)
  instead of (output, nil). Provider/backend errors continue to return (output, nil)
  with the error in output.Error — no string matching.
  module/infra_admin.go: writeMutationResponse() maps errors.Is(ErrAuthzDenied)→403
  and non-empty output.Error→500. NOT strings.Contains("denied") — false-positive
  on provider errors whose message happens to contain "denied".

Bug 4 — handlePlanResource denial→403
  Plan is infra:apply-gated (viewers must not probe desired-state hash or action
  list). handlePlanResource now calls m.authz.Enforce before handler.PlanResource
  and returns 403 directly on denial. writeMutationResponse replaces writeProtoMsg
  for apply/destroy routes too.

Unit tests added:
  TestInfraAdmin_TypedAuthzDenied_Returns403 — subtests:
    viewer/plan=403, viewer/apply=403, operator/apply=200,
    provider-error-denied-text/apply=500-not-403

Existing test updates:
  TestInfraAdmin_ViewerCannotApply: 200+body → 403 (typed discriminator)
  TestInfraAdmin_ApplyRejectsStalePlanHash: 200 → 500 (non-authz output.Error)
  handler denial tests: err!=nil fatalf → errors.Is(ErrAuthzDenied) assertion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 1, 2026 15:08
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR cleans up the workflow engine core by removing scenario-only IaC stub fixtures (previously gated by the scenario_stub build tag) and hardens the infra.admin surface based on RBAC scenario findings (typed 403 mapping, nil-store guards, and plan enforcement).

Changes:

  • Removed stub provider plugin/packages and scenario_stub plugin-wiring hooks/tests from the engine repo.
  • Introduced typed authz-denial signaling (handler.ErrAuthzDenied) and a shared writeMutationResponse helper to map authz denials to HTTP 403 and backend/provider errors to HTTP 500.
  • Fixed infra.admin robustness issues (permissions payload shape; handler nil-store guards) and updated/added tests to pin the new status-code behavior.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
plugins/stubprovider/plugin.go Removed scenario-only stub provider EnginePlugin implementation.
plugins/stubprovider/plugin_test.go Removed tests for the deleted stub provider plugin.
plugins/all/extras_stub.go Removed scenario_stub build-tag init hook that appended stub plugins.
plugins/all/extras_stub_test.go Removed build-tagged test asserting stub plugin inclusion.
plugins/all/extras_base_test.go Removed non-tag test asserting stub plugin exclusion.
plugins/all/all.go Simplified DefaultPlugins() to return only the base plugin set (no scenario extras).
iac/stubprovider/provider.go Removed in-process stub IaC provider fixture from engine core.
iac/stubprovider/provider_test.go Removed tests for the deleted stub provider.
module/infra_admin.go Fixed contributions payload shape; added typed mutation response writing; enforced plan RBAC; switched apply/destroy to typed status responses.
module/infra_admin_test.go Updated existing tests and added coverage for typed 403 vs 500 classification.
module/infra_admin_mutation_integration_test.go Replaced deleted stubprovider dependency with an in-test IaCProvider/driver double.
iac/admin/handler/authz.go Added ErrAuthzDenied sentinel for typed authz denials.
iac/admin/handler/plan_resource.go Returned ErrAuthzDenied on authz default-deny.
iac/admin/handler/plan_resource_test.go Replaced stubprovider dependency; asserted ErrAuthzDenied on default-deny.
iac/admin/handler/apply_resource.go Returned ErrAuthzDenied on authz denials.
iac/admin/handler/apply_resource_test.go Replaced stubprovider dependency; asserted ErrAuthzDenied on denials.
iac/admin/handler/destroy_resource.go Returned ErrAuthzDenied on authz denials.
iac/admin/handler/destroy_resource_test.go Replaced stubprovider dependency; asserted ErrAuthzDenied on denials.
iac/admin/handler/drift_check_test.go Replaced stubprovider dependency with local test provider.
iac/admin/handler/list_resources.go Added nil-store guard to prevent panic when state store is unset.
iac/admin/handler/get_resource.go Added nil-store guard to prevent panic when state store is unset.
iac/admin/handler/list_resources_test.go Introduced shared local test provider/driver used across handler tests.

Comment thread module/infra_admin.go
Comment on lines +1145 to +1149
if enforceErr != nil {
http.Error(w, "plan: authz enforce error", http.StatusInternalServerError)
m.auditAccess(r, "plan", in.GetEvidence(), "error")
return
}
Comment thread module/infra_admin.go
Comment on lines +1150 to +1154
if !ok {
http.Error(w, "plan: infra:apply denied for subject "+subject, http.StatusForbidden)
m.auditAccess(r, "plan", in.GetEvidence(), "denied")
return
}
Comment on lines 38 to 40
if msg := authzError(in.GetEvidence()); msg != "" {
return &adminpb.AdminPlanOutput{Error: msg}, nil
return &adminpb.AdminPlanOutput{Error: msg}, ErrAuthzDenied
}
Fix 1 — auditResultFromErr: typed mutation audit classification
  Add auditResultFromErr(err error, outError string) string that uses
  errors.Is(err, handler.ErrAuthzDenied) for "denied" vs plain outError
  non-empty for "error". Replace auditResultFor (strings.Contains path) in
  handlePlanResource/handleApplyResource/handleDestroyResource audit calls.
  auditResultFor is kept for read handlers (ListResources, GetResource, etc.)
  where no typed error is available.

  Key regression closed: provider error whose message contains "denied"
  (e.g. "provider: access denied to cloud API") now logs result:"error",
  NOT "denied". TestInfraAdmin_AuditResultFromErr covers this with 6 table
  cases including the false-positive regression case.

Fix 2 — plan module-layer 403 body shape
  handlePlanResource Enforcer-deny path now routes through
  writeMutationResponse(w, &AdminPlanOutput{Error: msg}, ErrAuthzDenied)
  instead of http.Error — proto-JSON body consistent with apply/destroy 403s.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 1, 2026 15:18
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

⏱ Benchmark Results

No significant performance regressions detected.

benchstat comparison (baseline → PR)
## benchstat: baseline → PR
baseline-bench.txt:308: parsing iteration count: invalid syntax
baseline-bench.txt:300933: parsing iteration count: invalid syntax
baseline-bench.txt:587044: parsing iteration count: invalid syntax
baseline-bench.txt:901679: parsing iteration count: invalid syntax
baseline-bench.txt:1208079: parsing iteration count: invalid syntax
baseline-bench.txt:1504225: parsing iteration count: invalid syntax
benchmark-results.txt:306: parsing iteration count: invalid syntax
benchmark-results.txt:300541: parsing iteration count: invalid syntax
benchmark-results.txt:623963: parsing iteration count: invalid syntax
benchmark-results.txt:945435: parsing iteration count: invalid syntax
benchmark-results.txt:1282723: parsing iteration count: invalid syntax
benchmark-results.txt:1845251: parsing iteration count: invalid syntax
goos: linux
goarch: amd64
pkg: github.com/GoCodeAlone/workflow/dynamic
cpu: AMD EPYC 7763 64-Core Processor                
                            │ baseline-bench.txt │
                            │       sec/op       │
InterpreterCreation-4               10.98m ± 51%
ComponentLoad-4                     3.672m ±  1%
ComponentExecute-4                  1.998µ ±  2%
PoolContention/workers-1-4          1.084µ ±  4%
PoolContention/workers-2-4          1.085µ ±  1%
PoolContention/workers-4-4          1.091µ ±  2%
PoolContention/workers-8-4          1.094µ ±  1%
PoolContention/workers-16-4         1.100µ ±  4%
ComponentLifecycle-4                3.649m ±  8%
SourceValidation-4                  2.333µ ±  1%
RegistryConcurrent-4                819.0n ±  4%
LoaderLoadFromString-4              3.642m ±  0%
geomean                             19.57µ

                            │ 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

cpu: AMD EPYC 9V74 80-Core Processor                
                            │ benchmark-results.txt │
                            │        sec/op         │
InterpreterCreation-4                  10.39m ± 60%
ComponentLoad-4                        3.725m ±  1%
ComponentExecute-4                     1.848µ ±  1%
PoolContention/workers-1-4             1.063µ ±  3%
PoolContention/workers-2-4             1.072µ ±  1%
PoolContention/workers-4-4             1.047µ ±  4%
PoolContention/workers-8-4             1.049µ ±  1%
PoolContention/workers-16-4            1.047µ ±  1%
ComponentLifecycle-4                   3.698m ±  2%
SourceValidation-4                     2.125µ ±  1%
RegistryConcurrent-4                   780.9n ±  4%
LoaderLoadFromString-4                 3.825m ±  1%
geomean                                18.99µ

                            │ 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

pkg: github.com/GoCodeAlone/workflow/middleware
cpu: AMD EPYC 7763 64-Core Processor                
                                  │ baseline-bench.txt │
                                  │       sec/op       │
CircuitBreakerDetection-4                  288.3n ± 4%
CircuitBreakerExecution_Success-4          21.51n ± 1%
CircuitBreakerExecution_Failure-4          65.91n ± 0%
geomean                                    74.21n

                                  │ 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

cpu: AMD EPYC 9V74 80-Core Processor                
                                  │ benchmark-results.txt │
                                  │        sec/op         │
CircuitBreakerDetection-4                     299.2n ± 4%
CircuitBreakerExecution_Success-4             22.68n ± 1%
CircuitBreakerExecution_Failure-4             71.02n ± 1%
geomean                                       78.41n

                                  │ 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

pkg: github.com/GoCodeAlone/workflow/module
cpu: AMD EPYC 7763 64-Core Processor                
                                 │ baseline-bench.txt │
                                 │       sec/op       │
IaCStateBackend_InProcess-4              348.9n ± 16%
IaCStateBackend_GRPC-4                   9.546m ±  4%
JQTransform_Simple-4                     724.6n ± 30%
JQTransform_ObjectConstruction-4         1.496µ ±  1%
JQTransform_ArraySelect-4                3.547µ ±  5%
JQTransform_Complex-4                    39.00µ ±  2%
JQTransform_Throughput-4                 1.792µ ±  1%
SSEPublishDelivery-4                     64.41n ±  2%
geomean                                  3.936µ

                                 │ baseline-bench.txt │
                                 │        B/op        │
IaCStateBackend_InProcess-4              416.0 ± 0%
IaCStateBackend_GRPC-4                 5.890Mi ± 5%
JQTransform_Simple-4                   1.273Ki ± 0%
JQTransform_ObjectConstruction-4       1.773Ki ± 0%
JQTransform_ArraySelect-4              2.625Ki ± 0%
JQTransform_Complex-4                  16.31Ki ± 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      │
IaCStateBackend_InProcess-4              2.000 ± 0%
IaCStateBackend_GRPC-4                  6.847k ± 0%
JQTransform_Simple-4                     10.00 ± 0%
JQTransform_ObjectConstruction-4         15.00 ± 0%
JQTransform_ArraySelect-4                30.00 ± 0%
JQTransform_Complex-4                    328.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                
                                 │ benchmark-results.txt │
                                 │        sec/op         │
IaCStateBackend_InProcess-4                 295.3n ±  1%
IaCStateBackend_GRPC-4                      11.43m ± 10%
JQTransform_Simple-4                        686.6n ± 31%
JQTransform_ObjectConstruction-4            1.508µ ±  3%
JQTransform_ArraySelect-4                   3.660µ ±  4%
JQTransform_Complex-4                       43.62µ ±  1%
JQTransform_Throughput-4                    1.824µ ±  2%
SSEPublishDelivery-4                        63.32n ±  0%
geomean                                     3.991µ

                                 │ benchmark-results.txt │
                                 │         B/op          │
IaCStateBackend_InProcess-4                 416.0 ± 0%
IaCStateBackend_GRPC-4                    5.820Mi ± 9%
JQTransform_Simple-4                      1.273Ki ± 0%
JQTransform_ObjectConstruction-4          1.773Ki ± 0%
JQTransform_ArraySelect-4                 2.625Ki ± 0%
JQTransform_Complex-4                     16.31Ki ± 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       │
IaCStateBackend_InProcess-4                 2.000 ± 0%
IaCStateBackend_GRPC-4                     6.859k ± 0%
JQTransform_Simple-4                        10.00 ± 0%
JQTransform_ObjectConstruction-4            15.00 ± 0%
JQTransform_ArraySelect-4                   30.00 ± 0%
JQTransform_Complex-4                       328.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                
                                    │ baseline-bench.txt │
                                    │       sec/op       │
SchemaValidation_Simple-4                   1.108µ ± 20%
SchemaValidation_AllFields-4                1.646µ ±  2%
SchemaValidation_FormatValidation-4         1.581µ ±  2%
SchemaValidation_ManySchemas-4              1.828µ ±  3%
geomean                                     1.515µ

                                    │ 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

cpu: AMD EPYC 9V74 80-Core Processor                
                                    │ benchmark-results.txt │
                                    │        sec/op         │
SchemaValidation_Simple-4                      1.077µ ± 15%
SchemaValidation_AllFields-4                   1.615µ ±  5%
SchemaValidation_FormatValidation-4            1.552µ ±  2%
SchemaValidation_ManySchemas-4                 1.596µ ±  1%
geomean                                        1.441µ

                                    │ 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

pkg: github.com/GoCodeAlone/workflow/store
cpu: AMD EPYC 7763 64-Core Processor                
                                   │ baseline-bench.txt │
                                   │       sec/op       │
EventStoreAppend_InMemory-4                1.084µ ± 44%
EventStoreAppend_SQLite-4                  1.298m ±  4%
GetTimeline_InMemory/events-10-4           15.04µ ±  2%
GetTimeline_InMemory/events-50-4           86.68µ ±  1%
GetTimeline_InMemory/events-100-4          154.9µ ± 15%
GetTimeline_InMemory/events-500-4          685.6µ ±  2%
GetTimeline_InMemory/events-1000-4         1.367m ±  1%
GetTimeline_SQLite/events-10-4             112.4µ ±  1%
GetTimeline_SQLite/events-50-4             271.0µ ±  2%
GetTimeline_SQLite/events-100-4            447.7µ ±  1%
GetTimeline_SQLite/events-500-4            1.919m ±  1%
GetTimeline_SQLite/events-1000-4           3.775m ±  3%
geomean                                    233.5µ

                                   │ baseline-bench.txt │
                                   │        B/op        │
EventStoreAppend_InMemory-4                 751.0 ± 13%
EventStoreAppend_SQLite-4                 1.985Ki ±  1%
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.06Ki

                                   │ 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

cpu: AMD EPYC 9V74 80-Core Processor                
                                   │ benchmark-results.txt │
                                   │        sec/op         │
EventStoreAppend_InMemory-4                   1.089µ ± 21%
EventStoreAppend_SQLite-4                     1.057m ±  5%
GetTimeline_InMemory/events-10-4              13.39µ ±  6%
GetTimeline_InMemory/events-50-4              74.42µ ±  6%
GetTimeline_InMemory/events-100-4             151.1µ ±  3%
GetTimeline_InMemory/events-500-4             751.1µ ±  2%
GetTimeline_InMemory/events-1000-4            1.255m ± 17%
GetTimeline_SQLite/events-10-4                87.20µ ±  1%
GetTimeline_SQLite/events-50-4                225.9µ ±  0%
GetTimeline_SQLite/events-100-4               395.4µ ±  1%
GetTimeline_SQLite/events-500-4               1.726m ±  1%
GetTimeline_SQLite/events-1000-4              3.383m ±  2%
geomean                                       210.2µ

                                   │ benchmark-results.txt │
                                   │         B/op          │
EventStoreAppend_InMemory-4                    774.0 ± 12%
EventStoreAppend_SQLite-4                    1.985Ki ±  1%
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.23Ki

                                   │ 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

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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated 1 comment.

) (*adminpb.AdminPlanOutput, error) {
if msg := authzError(in.GetEvidence()); msg != "" {
return &adminpb.AdminPlanOutput{Error: msg}, nil
return &adminpb.AdminPlanOutput{Error: msg}, ErrAuthzDenied
Finding 1 — second http.Error site in handlePlanResource
  The authz-enforce-error path (Enforce returns err) also used plaintext
  http.Error → now routes through writeStatusProto(500, AdminPlanOutput{Error:…})
  matching the proto-JSON contract of all other mutation error responses.

Finding 2 — subject reflected in denial body
  module/infra_admin.go: plan module-layer denial was "plan: infra:apply denied
  for subject <subject>" — genericized to "plan: infra:apply denied" (no principal).
  iac/admin/handler/apply_resource.go: "apply: infra:apply denied for subject X"
  → "apply: infra:apply denied".
  iac/admin/handler/destroy_resource.go: "destroy: infra:destroy denied for subject X"
  → "destroy: infra:destroy denied".
  Subject is captured in the audit log separately; it must not leak in HTTP body.

Finding 3 — stale comment in plan_resource.go
  "HTTP stays 200; consumer sniffs tag-100" updated to reflect the current
  contract: evidence denial returns (output, ErrAuthzDenied) → HTTP 403.

Test updates:
  - viewer/plan=403 subtest extended: asserts Content-Type=application/json +
    valid AdminPlanOutput proto body + body does NOT contain "viewer" (no leak).
  - TestInfraAdmin_AuditResultFor3Way, TestInfraAdmin_AuditDistinguishesDeniedFromError,
    TestInfraAdmin_AuditResultFromErr: example strings updated to new generic messages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 1, 2026

Codecov Report

❌ Patch coverage is 80.35714% with 11 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
module/infra_admin.go 84.78% 5 Missing and 2 partials ⚠️
iac/admin/handler/get_resource.go 0.00% 1 Missing and 1 partial ⚠️
iac/admin/handler/list_resources.go 0.00% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@intel352
Copy link
Copy Markdown
Contributor Author

intel352 commented Jun 1, 2026

Copilot's re-review comments (15:23) are stale re-posts — all three findings are addressed in 252f844, verified against source:

  • module/infra_admin.go:1163-1179 plan-403 now uses writeMutationResponse(&AdminPlanOutput{Error:"plan: infra:apply denied"}, ErrAuthzDenied) (proto-JSON, generic message, no subject reflected); enforce-error uses writeStatusProto(500,...). No http.Error remains on any authz/mutation path (the remaining http.Error calls are legitimate HTTP 400 for malformed-body/decode failures).
  • iac/admin/handler/plan_resource.go godoc updated to "maps this to HTTP 403 via writeMutationResponse — NOT 200".
    Test TestInfraAdmin_TypedAuthzDenied_Returns403 asserts the plan-403 body is JSON AdminPlanOutput and does not contain the principal. CI green; merging.

@intel352 intel352 merged commit f53a7ac into main Jun 1, 2026
28 checks passed
@intel352 intel352 deleted the refactor/infra-admin-remove-scenario-stubs-from-core-2026-06-01T0155 branch June 1, 2026 21:19
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