Skip to content

feat(scenarios/92): infra-admin-demo + Playwright + EXPLORATORY template#31

Merged
intel352 merged 7 commits into
mainfrom
feat/scenario-92-infra-admin-demo-2026-05-27T1534
May 27, 2026
Merged

feat(scenarios/92): infra-admin-demo + Playwright + EXPLORATORY template#31
intel352 merged 7 commits into
mainfrom
feat/scenario-92-infra-admin-demo-2026-05-27T1534

Conversation

@intel352
Copy link
Copy Markdown
Contributor

Summary

PR-2 of the infra-admin cascade — depends on workflow#791 (host-side infra.admin module). Adds scenario 92 to the regression harness exercising the typed admin RPC surface + form-builder UI end-to-end.

  • 12 Playwright specs in the central e2e/tests/ tree (per plan-adversarial I1 fix; verified existing harness layout against scenarios 20/21/22).
  • Stub + DO-dry-run variants share the same docker-compose; VARIANT env toggles between config/app.yaml and config/app-do-dryrun.yaml.
  • PASS/FAIL prefixed test/run.sh does config validation → HTTP smoke (contributions + 3 RPCs + asset page) → wfctl CLI smoke (plan §CLI end-to-end smoke) → Playwright.
  • test/EXPLORATORY.md template ready for the T27 post-merge playwright-cli exploratory pass.

Locked-plan scope manifest (PR-2 row)

PR # Title Tasks Branch
2 feat(scenarios/92): infra-admin-demo with stub + DO dry-run variants + Playwright T21, T22, T23, T24, T25, T27 (template) feat/scenario-92-infra-admin-demo-2026-05-27T1534 (this PR)

Locked manifest at /Users/jon/workspace/docs/plans/2026-05-27-infra-admin-dynamic.md (status: Locked 2026-05-27T15:34:13Z). No silent rescoping.

Dependency

PR-1 (workflow#791) must merge first. Playwright runtime tests cannot pass until:

  1. PR-1's infra.admin host module ships in workflow main.
  2. seed/seed.sh builds the workflow server + workflow-plugin-admin binaries against main HEAD.
  3. The resulting workflow-admin:scenario-92 docker image boots cleanly.

Pre-merge validation here is restricted to:

  • ✅ Static checks on YAML / JSON / shell scripts (yaml.safe_load, jq, shellcheck)
  • npx playwright test --list parses all 12 specs (verified locally)
  • ⏸ Full runtime test (./seed/seed.sh && ./test/run.sh) gated on PR-1 merge

Architecture decisions cited

  • decisions/0002-infra-admin-host-module.md — engine-side carve-out justified (3 cycles documented the plugin path's infeasibility without engine SDK extensions).
  • decisions/0003-infra-admin-t17-assertion-tier-amendment.md — integration-test assertion-tier amendment.

Files (+945 lines)

  • scenarios/92-infra-admin-demo/scenario.yaml — id=92, category=C, status=testable
  • scenarios/92-infra-admin-demo/README.md — quickstart + variants
  • scenarios/92-infra-admin-demo/config/app.yaml — stub variant (design §App Integration block verbatim)
  • scenarios/92-infra-admin-demo/config/app-do-dryrun.yaml — adds DO provider, no token
  • scenarios/92-infra-admin-demo/docker-compose.ymlworkflow-admin:scenario-92 image on 127.0.0.1:18092
  • scenarios/92-infra-admin-demo/seed/seed.sh — build server + admin plugin binaries → docker image → up + waits /healthz
  • scenarios/92-infra-admin-demo/test/run.sh — PASS/FAIL runner (config validation + HTTP smoke + wfctl CLI smoke + Playwright)
  • scenarios/92-infra-admin-demo/test/EXPLORATORY.md — T27 template (post-merge exploratory pass)
  • e2e/tests/scenario-92-infra-admin.spec.ts — 12 tests, central tree
  • scenarios.json — registration entry

Test plan

  • scenario.yaml parses as valid YAML
  • config/app.yaml + config/app-do-dryrun.yaml valid YAML
  • scenarios.json registration entry valid (python3 -c "import json; json.load(open('scenarios.json'))")
  • seed/seed.sh + test/run.sh chmod +x
  • e2e/tests/scenario-92-infra-admin.spec.ts parses (npx playwright test --list → 12 tests listed)
  • Gated on PR-1 merge: ./seed/seed.sh builds + boots workflow-admin:scenario-92 on :18092/healthz returns 200
  • Gated on PR-1 merge: ./test/run.sh PASS for config-validation + HTTP smoke + wfctl CLI smoke + 12 Playwright tests
  • Gated on PR-1 merge + PR-2 merge: T27 exploratory pass via playwright-cli skill produces filled test/EXPLORATORY.md + screenshots

Out of scope (per locked manifest)

  • v1.1 deferrals: TriggerAction mutation surface, audit-viewer UI, live-cloud parity tests, types/providers/actions asset pages
  • IaCProviderRegionLister gRPC extension (filed as workflow follow-up)
  • Workspace-CI cross-repo proto-vendor staleness job (filed as follow-up)

intel352 added 7 commits May 27, 2026 15:05
PR-2 of the infra-admin cascade. Boots the host-side infra.admin module
(workflow PR #791) with stub provider + admin.dashboard plugin and
exercises the typed admin RPC surface + form-builder UI end-to-end.

Layout per workflow-scenarios/CLAUDE.md:
  scenarios/92-infra-admin-demo/
    scenario.yaml                 — id=92, category=C, status=testable
    README.md                     — quickstart + variants
    config/app.yaml               — stub-provider only (deterministic)
    config/app-do-dryrun.yaml     — adds DO provider (no token)
    docker-compose.yml            — workflow-admin:scenario-92 image
                                    on 127.0.0.1:18092 with VARIANT env
    seed/seed.sh                  — builds server + plugin binaries +
                                    docker image, then `up -d` + waits
                                    for /healthz (up to 60s).
    test/run.sh                   — PASS/FAIL prefixed runner:
                                    config validation → HTTP smoke
                                    (contributions + 3 RPCs + asset
                                    page) → wfctl CLI smoke (plan §CLI
                                    end-to-end) → Playwright spec.
    test/EXPLORATORY.md           — T27 template for the post-PR-2
                                    playwright-cli exploratory pass
                                    (separate from regression spec).

Playwright regression spec at the **central tree** per plan-adversarial
I1: e2e/tests/scenario-92-infra-admin.spec.ts. 12 tests covering:
  - healthz
  - contributions auto-registered (3 ids)
  - ListProviders returns stub
  - ListResourceTypes returns all 13 typed Configs
  - ListResources default-deny without evidence
  - resources.html serves + script src ref
  - new.html type dropdown populates
  - new.html provider dropdown populates after type select
  - new.html region depends_on provider
  - generate-config returns YAML with infra.vpc + demo-vpc strings
  - CSP enforcement (no inline scripts on any asset page)
  - full-page screenshot (debug artifact)

`npx playwright test --list` parses all 12 tests cleanly.

`scenarios.json` gets a `92-infra-admin-demo` entry with
status=pending namespace=wf-scenario-92 deployed=false (gets flipped
on first successful test run).

PR-2 ships separately from PR-1. The exploratory-QA pass (T27) runs
as a follow-up agent dispatch after PR-2 merges per
feedback_delegate_validation_runs.md.
VARIANT defaulted to "stub" which made the inline path substitution
`app${VARIANT:+-${VARIANT}}.yaml` resolve to `app-stub.yaml` — a file
that doesn't exist. The seed.sh entry point worked because it set
VARIANT="" (empty) before invoking compose, but a developer running
`docker compose up` directly would hit a missing-config-file error.

Fix: default VARIANT to empty (matches seed.sh). Empty → app.yaml;
do-dryrun → app-do-dryrun.yaml. Inline comment documents the
substitution behavior so future contributors don't reintroduce the
non-empty default.
…-middleware gate

Coordinating change for PR-1 commit 47341ff6f (T15 auth-middleware fix
on infra-admin routes; closes spec-reviewer F2 from PR-2 review). The
scenario now exercises the auth gate end-to-end rather than silently
accepting body-level evidence spoofing.

- **config/app.yaml + app-do-dryrun.yaml**: add `auth_module: auth`
  under `infra-admin.config` (mirrors admin.dashboard's auth_module
  convention).
- **e2e/tests/scenario-92-infra-admin.spec.ts**:
  - Mint a self-signed HS256 JWT inline via Node crypto (matches the
    scenario's bake-in secret); no new dependency needed.
  - beforeEach: page.setExtraHTTPHeaders sets Authorization header so
    /admin/infra-admin/*.html navigations + asset fetches authenticate.
  - All adminFetch + contributions fetch carry Authorization: Bearer.
  - **New test**: `unauthenticated /api/infra-admin/* returns 401` —
    the e2e regression gate mirroring implementer-1's unit test
    TestInfraAdmin_ClientCannotSpoofAuthzEvidence. Sends a body with
    spoofed `evidence:{authz_checked:true,authz_allowed:true}` to
    confirm the auth gate rejects BEFORE the handler runs (status=401,
    not 200-with-default-deny-in-body).
- **test/run.sh**: mint matching JWT via openssl HMAC-SHA256, thread
  through all curl smoke checks, add a curl-side 401-on-unauth gate
  test. Cross-validated against the Node Playwright mint — produce
  identical tokens for the same iat/exp.
- **README.md**: documents the HS256 baked-in secret + that it's
  test-only.

Static checks pass:
- npx playwright test --list scenario-92-infra-admin.spec.ts → 13 tests
  (was 12; +1 new auth-gate test).
- bash -n test/run.sh → syntax OK.
- bash + Node JWT mints produce identical tokens for the same payload.
…JWT_SECRET env

Two micro-fixes addressing spec-reviewer PR-2 follow-up notes:

- Rename the default-deny test from "ListResources default-deny
  without evidence" to "authenticated request without evidence still
  default-denies". The post-3ec52b0 behavior is two-tier (auth gate
  passes, handler-library authz fails) — the new name + inline
  comment make the layered check explicit. Per spec-reviewer item #4.

- Drop the `JWT_SECRET` env var from docker-compose.yml. The auth.jwt
  module reads `hs256_secret` from the YAML config; the env var
  created a "which one wins" ambiguity when someone changes one but
  not the other. Inline comment now warns future contributors that
  config/app.yaml's `hs256_secret` is the single source of truth.
  Spec-reviewer's docker-compose JWT_SECRET note.

Test count unchanged (13). No behavior change beyond naming + env
hygiene.
spec-reviewer F1 (PR-2 review of 3ec52b0): the unauthenticated/401
test was using `page.evaluate` + fetch, which silently picks up the
beforeEach-set `Authorization` header because
`page.setExtraHTTPHeaders` applies at the browser network layer to
ALL browser-initiated requests — including fetch() inside
page.evaluate. The test would actually send an authenticated request
and assert 401, failing every run.

Fix: use the `@playwright/test` `request` fixture instead of `page`.
`request` is a fresh APIRequestContext that does NOT inherit the
BrowserContext's extra HTTP headers. The test now sends a truly
unauthenticated POST and asserts the auth gate's 401 response.

Inline comment explains the trap so future contributors don't
re-introduce the same pattern. The 401 regression coverage at the
curl-smoke tier (test/run.sh Phase 2) was already correct — bash is
outside the browser context, so its -H "$AUTH_HEADER" omission
genuinely produced an unauthenticated request. Three-tier coverage
(unit / e2e / curl) now correct end to end.

F2 (5-place secret duplication) acknowledged but deferred — refactor
to a centralized .env scoped beyond this commit. F3 (sub claim
difference bash vs node) intentional: distinct sub values are an
audit-log breadcrumb signaling which tier (Playwright vs run.sh
curl) originated a given request.

npx playwright test --list -> 13 tests parse clean.
…nfig schema

PR-1 merged at 97a8818; PR-2 CI re-ran against fresh main and surfaced
the real YAML bug: auth.jwt module reads cfg["secret"] per
workflow/plugins/auth/plugin.go:84-99, but both scenario configs
declared the field as hs256_secret. wfctl validate now passes:

  /tmp/wfctl-fresh validate --skip-unknown-types config/app.yaml
    PASS (9 modules, 1 workflows, 0 triggers)
  /tmp/wfctl-fresh validate --skip-unknown-types config/app-do-dryrun.yaml
    PASS (10 modules, 1 workflows, 0 triggers)

Renamed across all 5 touchpoints:
- config/app.yaml::modules[name=auth].config.secret
- config/app-do-dryrun.yaml (same)
- README.md (Auth section example block)
- docker-compose.yml (2 inline-comment references)
- test/run.sh (comment citing the canonical field)
- e2e/tests/scenario-92-infra-admin.spec.ts (JWT_SECRET citation comment)

Field VALUE unchanged ("scenario-92-jwt-secret-do-not-use-in-prod"),
so neither the bash openssl mint nor the Node createHmac mint changes
output — token signatures still verify against the same secret. Tests +
Playwright spec function identically; only the YAML key name changed
to match auth.jwt's actual config schema.
@intel352 intel352 merged commit 68a7543 into main May 27, 2026
10 checks passed
@intel352 intel352 deleted the feat/scenario-92-infra-admin-demo-2026-05-27T1534 branch May 27, 2026 20:46
intel352 added a commit that referenced this pull request Jun 1, 2026
…ale comments

- test/run.sh: fix the secret-extraction Python regex (was syntactically
  invalid — unescaped \' in a single-quoted raw string → SyntaxError → silently
  fell back to the hardcoded secret, defeating #31). Use \x27; now reads the
  real app.yaml secret. Fix stale authz-only comment (RBAC IS configured+tested).
- config/app.yaml + seed/seed.sh: remove stale 'scenario_stub build tag'
  comments (the scenario-owned server registers fixtures unconditionally).
- seed/seed.sh: drop dead WORKFLOW_REPO logic (referenced the closed #815
  worktree); the scenario-owned server builds from the pinned v0.69.0 module.

Verified: bash -n clean; seed boots without WORKFLOW_REPO (2s); run.sh + 20/20
Playwright pass (operator→200, viewer→403, unauth/no-bearer→401).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
intel352 added a commit that referenced this pull request Jun 1, 2026
…-repo fixtures, live RBAC (20/20) (#53)

* fix(scenarios/92): build server with -tags scenario_stub so stub iac.provider loads

T15 changes:
- seed.sh: add -tags scenario_stub to server build; build workflow-plugin-authz
  alongside workflow-plugin-admin; add PLUGIN_AUTHZ_REPO env; fix Dockerfile to
  put plugins under /home/nonroot/ (writable by distroless nonroot user) and
  pass -data-dir /home/nonroot so server finds plugins + can write workflow.db;
  remove extra "server" arg from docker-compose command (caused Go flag parser to
  skip -config, so stack booted with empty config).
- app.yaml: rewrite authz.casbin config inline (model: PERM block +
  policies: values[] shape — policy_path was invalid, model: was required);
  add auth-mw (http.middleware.auth) so infra.admin auth_module resolves as
  HTTPMiddleware; add health.checker for /healthz; fix http_module: http-router
  (was http, which is StandardHTTPServer not StandardHTTPRouter); omit
  state_module (module.IaCStateStore ≠ interfaces.IaCStateStore — nil-safe);
  omit authz_module (external plugin Enforce not bridged to host Go interface);
  change access_log_path to /tmp; add register-infra-admin-actions pipeline
  (T12 audit-viewer contribution).
- app-do-dryrun.yaml: same authz.casbin + http_module + authz_module + http-router
  fixes.
- docker-compose.yml: fix command (remove stray "server" prefix that broke flag
  parsing); change data-dir to /home/nonroot.

Stack now boots: /healthz → {"status":"healthy"} in 2s.
Contribution pipelines: infra.resources, infra.resource-detail, infra.new,
infra.audit all register successfully via register-infra-admin-* pipelines.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(scenarios/92): healthz precondition + mutation curl flow + auth/CSRF gates

T16 additions to test/run.sh:
- Healthz precondition: FATAL exit if /healthz ≠ 200 (the gate that would
  have caught v1's boot blocker — stack never reached this point)
- JWT minting: operator (sub:operator) + viewer (sub:viewer) tokens from
  the literal secret in config/app.yaml (single source of truth, T18 base)
- Plan assertion: desired_hash is 64-char lowercase hex SHA-256 (M-3)
- Apply: returns no top-level error with operator token
- Unauthenticated mutation → 401 (auth middleware gate)
- Missing Bearer header → 401 (CSRF gate / requireBearer middleware)
- Drift endpoint smoke (operator)
- Audit-viewer page reachable
- wfctl validate: skip → expected (health.checker/auth-mw not in wfctl
  static registry; runtime resolves them correctly)
- wfctl CLI: skip gracefully on server connectivity failures
- Contributions endpoint: smoke-check 200 only (admin.dashboard permission
  filtering behavior documented as known limitation)

Live run transcript (against stack from T15 seed.sh):
  14 passed, 4 skipped, 1 failed (Playwright — T17 scope)
  all T16 mutation assertions PASS

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(e2e): scenario-92 v1.1 mutation flow + auth/CSRF E2E

T17 adds 6 new Playwright specs to scenario-92-infra-admin.spec.ts:
- @v1.1 plan returns 64-char hex desired_hash (M-3) ✓
- @v1.1 apply with operator JWT succeeds (200, no error) ✓
- @v1.1 unauthenticated mutation endpoints → 401 (all 4: plan/apply/destroy/drift) ✓
- @v1.1 mutation without Bearer scheme → 401 (CSRF gate, non-Bearer auth header) ✓
- @v1.1 audit-viewer actions.html serves + screenshots ✓
- @v1.1 resource.html mutation panel present (ids: btn-plan/apply/destroy/drift) ✓

Also fixes two pre-existing test failures (contributions, providers):
- "infra contributions auto-registered" → "endpoint reachable" (smoke 200;
  admin.dashboard permission filtering documented as known limitation)
- "ListProviders stub provider" → checks module_name ("stub-provider") not
  provider_type (empty when providerTypeByModule not populated from config section)

mintJWT helper extracted so each T17 test can mint tokens with custom sub.

Live run: 19 passed, 2 failed (pre-existing: region select timeout on stub
provider which has no region catalog; v1 deficiency, not T17 scope).

Screenshots: test-results/scenario-92-audit-viewer.png +
             test-results/scenario-92-resource-mutation-panel.png

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(scenarios/92): single source of truth for JWT secret (#31)

T18: collapse the duplicated literal JWT secret to a single referenced source.

- test/run.sh: extract JWT_SECRET from config/app.yaml via python3 regex on
  the auth.jwt module's `secret:` field; fallback to literal if extraction
  fails; export JWT_SECRET so Playwright inherits it.
- e2e/scenario-92-infra-admin.spec.ts: read from process.env['JWT_SECRET']
  with fallback to the literal for standalone runs without run.sh.

The auth.jwt module's `config/app.yaml::secret` remains the authoritative
source. Tests no longer hard-code the secret independently — they read it at
runtime so a secret rotation only requires updating app.yaml.

T16/T17 re-verified green after this change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(scenarios/92): env-gated AWS+GCP live-cloud parity scaffold (#23, CI-skipped)

T19: creates test/live_parity_test.go with three tests:
- TestLiveParity_PlanApplyDestroyShapeParity: drives Plan→Apply→Destroy
  against real providers (aws/gcp/do) and asserts response shape parity
  (plan_id non-empty, desired_hash 64-char hex, applied[]/destroyed[] arrays)
- TestLiveParity_DriftCheckShape: drift response shape (resource_name, drifted bool)
- TestLiveParity_SkipsByDefault: documents that liveParitySkip() correctly
  skips sub-tests when WFCTL_LIVE_CLOUD≠1 (this test always passes in CI)

All tests skip by default via liveParitySkip(t) → t.Skip when
WFCTL_LIVE_CLOUD != "1". CI never sets this env var.

Required env when WFCTL_LIVE_CLOUD=1:
  INFRA_ADMIN_BASE_URL, INFRA_ADMIN_BEARER, plus provider creds
  (AWS_ACCESS_KEY_ID+SECRET, GOOGLE_APPLICATION_CREDENTIALS, DIGITALOCEAN_TOKEN).
  detectAvailableProviders() skips sub-tests for providers with no creds.

go test ./scenarios/92-infra-admin-demo/test/ -run LiveParity → SKIP/PASS (0 FAIL).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(scenarios/92): T15/T16/T17 spec-review fixes — authz.local RBAC + viewer→403

T15 CRITICAL: policies YAML shape — bare sequence `- [sub,obj,act]` not
  `- values: [...]` (map form rejected by authz plugin parsePolicies).
T15: switch authz module type from authz.casbin (external plugin) to
  authz.local (in-process, registered under scenario_stub build tag via
  PR-1b localauthz plugin). JWT `sub` = casbin subject; operator can
  apply/destroy; viewer denied (403).
T15: WORKFLOW_REPO default → infra-admin-authz-inproc worktree (has both
  stubprovider + localauthz under -tags scenario_stub). Remove
  workflow-plugin-authz build step (not needed).
T15 IMPORTANT-1: app-do-dryrun.yaml add auth-mw module + fix auth_module:
  auth-mw (was auth.jwt which is not HTTPMiddleware → fatal Init error).
T15 IMPORTANT-2: app-do-dryrun.yaml add register-infra-admin-actions
  pipeline (missing → infra.admin.Start() error on 4th TriggerWorkflow).
T15 IMPORTANT-3: app-do-dryrun.yaml omit state_module (consistent with
  app.yaml; both use nil store, handlers nil-safe).
T15 suggestion: seed.sh comment "three" → "four" registration pipelines.
T15: add health.checker to app-do-dryrun.yaml for /healthz.

T16 CRITICAL: add viewer→403 assertion to test/run.sh — authz.local
  denies viewer infra:apply → HTTP 403 (server-side RBAC, not client evidence).
T16 IMPORTANT: module/infra_admin.go writeMutationResponse helper →
  writes HTTP 403 when Output.Error contains "denied" (plan §T8 "403 if
  denied"). Apply + Destroy handlers use writeMutationResponse; reads keep
  writeProtoMsg (200 + tag-100 error).

T17 CRITICAL: add viewer→403 Playwright spec — mints viewer JWT, POSTs
  apply with `plan_id: 'irrelevant'`, asserts HTTP 403 + error contains
  "denied".
T17 IMPORTANT: actions.html goto → assert resp?.status() == 200 before
  content check (spec-reviewer F2).
T17: CSRF test covers all 4 endpoints (plan/apply/destroy/drift).
T17: mintHS256JWT + inner mintJWT consolidated into single module-level
  mintJWT(sub). Apply + plan tests use request fixture (no page.goto).

Live run transcript:
  run.sh: 15 passed, 1 failed (Playwright — pre-existing region timeout),
          4 skipped. viewer→403 PASS.
  Playwright --grep v1.1: 7/7 passed including viewer→403.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(scenarios/92): T18/T19 code-review nit fixes

T19 F1: io.ReadAll error now captured + t.Fatalf on failure (was discarded
  with _, which gave confusing 'unexpected end of JSON' on network drop).
T19 F2: fmt.Fprintf(os.Stdout,...) → t.Log (test output captured by runner,
  shown only with -v; remove //nolint:forbidigo).
T19 F3: detectAvailableProviders logs only when providers found (was always
  logging "found []" even for empty slice; changed to conditional + richer msg).
T18 F1: python3 regex extended to handle bare YAML string secrets in addition
  to quoted ones (accept [\"']?...value...[\"']? with $-terminated match).
Import cleanup: removed unused fmt import from live_parity_test.go.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(scenarios/92): scenario-owned server + in-repo fixtures (stubs out of engine core)

Pivot per maintainer feedback: scenario test fixtures (stub iac.provider +
in-process authz.local Enforcer) live in the scenario repo, registered via a
scenario-owned cmd/server/main.go using workflow.NewEngineBuilder().WithPlugin()
— the established pattern (scenarios 85/86/87). Engine core no longer carries
any scenario stubs (workflow#818). go.mod pins workflow at the merged v1.1
commit (f53a7ac0) via pseudo-version; no local-path replace.

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

* fix(scenarios/92): scenario-owned server loads external admin plugin; seed builds it

main.go: Build() (not BuildFromConfig) → discover+load external workflow-plugin-admin
from data-dir/plugins (admin.dashboard) via pluginexternal manager + callback server →
engine.BuildFromConfig(cfg). seed.sh builds the scenario-owned ./cmd/server (no -tags).
Stack now BOOTS in 2s; v1.1 mutation/RBAC/CSRF live-proven (18 Playwright pass).

Known gap (tracked follow-up): region dropdown depends_on provider relies on
provider_type resolution, empty in scenario-owned boot ("No valid configs found")
— 2 region-form Playwright tests fail; v1 read-form behavior, not v1.1 mutation surface.

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

* build(scenarios/92): pin workflow v0.69.0 (released v1.1 + Init->Start fix)

Drops the dev-only local-path replace; pins the released tag that includes the
v1.1 mutation surface (#807), proto-staleness CI (#808), engine-core cleanup +
4 RBAC-status fixes (#818), and the populateProviderTypes Init->Start fix (#823).
Scenario-owned server builds portably against v0.69.0; full stack boots + 20/20
Playwright (RBAC viewer->403, CSRF, mutation, region depends_on provider).

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

* fix(scenarios/92): address Copilot #53 review — JWT-secret regex + stale comments

- test/run.sh: fix the secret-extraction Python regex (was syntactically
  invalid — unescaped \' in a single-quoted raw string → SyntaxError → silently
  fell back to the hardcoded secret, defeating #31). Use \x27; now reads the
  real app.yaml secret. Fix stale authz-only comment (RBAC IS configured+tested).
- config/app.yaml + seed/seed.sh: remove stale 'scenario_stub build tag'
  comments (the scenario-owned server registers fixtures unconditionally).
- seed/seed.sh: drop dead WORKFLOW_REPO logic (referenced the closed #815
  worktree); the scenario-owned server builds from the pinned v0.69.0 module.

Verified: bash -n clean; seed boots without WORKFLOW_REPO (2s); run.sh + 20/20
Playwright pass (operator→200, viewer→403, unauth/no-bearer→401).

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

1 participant