Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/_cf-worker-delivery.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ on:
type: string
default: ''
descriptor:
description: toJSON of THIS kind's target descriptor (versioned, validated by the Delivery plan and re-validated by the engine). Carries the engine target, GitHub Environment, concurrency groups, expected physical D1, migration owner and smoke mode; never a secret.
description: toJSON of THIS kind's target descriptor (versioned, validated by the Plan job and re-validated by the engine). Carries the engine target, GitHub Environment, concurrency groups, expected physical D1, migration owner and smoke mode; never a secret.
type: string
required: true
outputs:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/_cf-worker-lifecycle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ defaults:

jobs:
lifecycle:
name: Preview lifecycle
name: ledger
runs-on: ${{ inputs.runner }}
timeout-minutes: 10
concurrency:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/_cf-worker-persistent-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ defaults:

jobs:
persistent-dev:
name: Persistent Dev (Hybrid)
name: manual
runs-on: ${{ inputs.runner }}
timeout-minutes: 45
environment: ${{ inputs.app_name }}/dev
Expand Down
28 changes: 14 additions & 14 deletions .github/workflows/_cf-worker-v2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ name: CF Worker Pipeline v2
#
# CI (_cf-worker-ci.yml) Lint | Typecheck | Unit | Integration | Build Validation | E2E in parallel -> Validation Gate (fail closed)
# |
# Delivery plan enabled targets, ONE physical D1, the migration owner (pure data; runs only when CI passed)
# Plan enabled targets, ONE physical D1, the migration owner (pure data; runs only when CI passed)
# |
# Database Migration ONE job: read-only safety checks first (target build, effective-config audit, exact D1 binding), then
# Migrate ONE job: read-only safety checks first (target build, effective-config audit, exact D1 binding), then
# | the migration is applied ONCE. No separate Preflight stage.
# Delivery x enabled kinds _cf-worker-delivery.yml, one call per kind: Deploy -> Smoke -> conditional Rollback, independent per kind
# |
Expand Down Expand Up @@ -451,11 +451,11 @@ env:
jobs:
# -------------------------------------------------------------------------
# CI — ONE call of _cf-worker-ci.yml (six parallel checks + the fail-closed Validation Gate). Credential-free; fork PRs run it, same-repo
# PRs are covered by the push run of the same SHA. Its verdict is the only upstream condition of the Delivery plan.
# PRs are covered by the push run of the same SHA. Its verdict is the only upstream condition of the Plan.
# -------------------------------------------------------------------------
# CI — ONE call of _cf-worker-ci.yml: Lint, Typecheck, Unit, Integration, Build Validation and E2E as six independent, parallel jobs,
# then the fail-closed Validation Gate. Credential-free; fork PRs run it, same-repo PRs are covered by the push run of the same SHA.
# Its result is the only upstream condition of the Delivery plan.
# Its result is the only upstream condition of the Plan.
# -------------------------------------------------------------------------
ci:
name: CI
Expand All @@ -482,12 +482,12 @@ jobs:
test_e2e_env: ${{ inputs.test_e2e_env }}

# -------------------------------------------------------------------------
# Delivery plan — pure data, no Cloudflare credentials: the enabled delivery kinds of THIS event, the ONE physical D1 they share (distinct
# Plan — pure data, no Cloudflare credentials: the enabled delivery kinds of THIS event, the ONE physical D1 they share (distinct
# databases fail the plan) and its migration owner, the per-kind `on_<kind>` switches and target `descriptors`, the canonical lock `group`.
# Runs only after a passed CI, so every job below that needs the plan is implicitly gated by CI. A verify call plans from phase_state.
# -------------------------------------------------------------------------
plan:
name: Delivery plan
name: Plan
needs: [ci]
if: always() && (inputs.pipeline_phase == 'verify' || (needs.ci.result == 'success' && needs.ci.outputs.validation == 'passed' && github.ref_type == 'branch' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')))
runs-on: ${{ inputs.runner }}
Expand Down Expand Up @@ -533,7 +533,7 @@ jobs:
# proceeds) without d1_migrations; never on pull_request, delete or a verify call. The D1 schema is never rolled back automatically.
# =========================================================================
migration:
name: Database Migration
name: Migrate
needs: [plan]
if: always() && inputs.pipeline_phase != 'verify' && inputs.d1_migrations && needs.plan.result == 'success' && needs.plan.outputs.has_migrations == 'true'
runs-on: ${{ inputs.runner }}
Expand Down Expand Up @@ -603,7 +603,7 @@ jobs:
# the single Migration job (success, or skipped when nothing is migrated) and is never touched by another kind's Deploy / Smoke / Rollback.
# =========================================================================
delivery_production:
name: Delivery (production)
name: production
needs: [plan, migration]
if: always() && needs.plan.result == 'success' && needs.plan.outputs.on_production == 'true' && (needs.migration.result == 'success' || needs.migration.result == 'skipped')
uses: $/.github/workflows/_cf-worker-delivery.yml
Expand All @@ -630,7 +630,7 @@ jobs:
SMOKE_PASSWORD: ${{ secrets.SMOKE_PASSWORD }}

delivery_native_branch:
name: Delivery (native-branch)
name: native-branch
needs: [plan, migration]
if: always() && needs.plan.result == 'success' && needs.plan.outputs.on_native_branch == 'true' && (needs.migration.result == 'success' || needs.migration.result == 'skipped')
uses: $/.github/workflows/_cf-worker-delivery.yml
Expand All @@ -649,7 +649,7 @@ jobs:
secrets: *delivery_secrets

delivery_native_dev:
name: Delivery (native-dev)
name: native-dev
needs: [plan, migration]
if: always() && needs.plan.result == 'success' && needs.plan.outputs.on_native_dev == 'true' && (needs.migration.result == 'success' || needs.migration.result == 'skipped')
uses: $/.github/workflows/_cf-worker-delivery.yml
Expand All @@ -668,7 +668,7 @@ jobs:
secrets: *delivery_secrets

delivery_persistent_dev:
name: Delivery (persistent-dev)
name: persistent-dev
needs: [plan, migration]
if: always() && needs.plan.result == 'success' && needs.plan.outputs.on_persistent_dev == 'true' && (needs.migration.result == 'success' || needs.migration.result == 'skipped')
uses: $/.github/workflows/_cf-worker-delivery.yml
Expand Down Expand Up @@ -719,7 +719,7 @@ jobs:
# on ANY event so a misconfigured caller is visible. (production-only keeps ignoring preview_on_branch_push=true, for compatibility.)
# -------------------------------------------------------------------------
config-guard:
name: Config guard
name: Guard
if: inputs.pipeline_phase != 'verify' && ((inputs.deployment_strategy == 'production-only' && (inputs.shared_dev_on_branch_push || inputs.shared_dev_native_preview_on_branch_push)) || (inputs.shared_dev_on_branch_push && inputs.shared_dev_native_preview_on_branch_push))
runs-on: ${{ inputs.runner }}
timeout-minutes: 2
Expand All @@ -733,7 +733,7 @@ jobs:
# branch delete -> retire the ledger, the Preview is KEPT) and `persistent-dev` (Hybrid, explicit dispatch on the approved ref only).
# -------------------------------------------------------------------------
lifecycle:
name: Preview lifecycle
name: lifecycle
if: inputs.pipeline_phase != 'verify' && inputs.deployment_strategy != 'production-only' && ((github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || (github.event_name == 'delete' && github.event.ref_type == 'branch'))
uses: $/.github/workflows/_cf-worker-lifecycle.yml
permissions:
Expand All @@ -747,7 +747,7 @@ jobs:
runner: ${{ inputs.runner }}

persistent-dev:
name: Persistent Dev (Hybrid)
name: persistent-dev
if: inputs.pipeline_phase != 'verify' && github.event_name == 'workflow_dispatch' && inputs.dispatch_target == 'persistent-dev' && inputs.deployment_strategy == 'hybrid' && inputs.persistent_dev_ref != '' && github.ref == inputs.persistent_dev_ref
uses: $/.github/workflows/_cf-worker-persistent-dev.yml
permissions:
Expand Down
2 changes: 1 addition & 1 deletion actions/cf-deploy/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ inputs:
description: publish stage only - the Migration job's migration_json output
default: ''
descriptor:
description: toJSON of this kind's target descriptor (the Delivery plan's validated record). When set, the engine verifies it against target / kind / d1_expected_id and takes the smoke mode from it.
description: toJSON of this kind's target descriptor (the Plan job's validated record). When set, the engine verifies it against target / kind / d1_expected_id and takes the smoke mode from it.
default: ''
call_inputs_json:
description: Deferred verification only - toJSON(inputs) of the VERIFY call, compared with the deploy call's identity inputs (inputs_json)
Expand Down
2 changes: 1 addition & 1 deletion actions/cf-preview/delivery.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ export function checkBuildEnvOrigins(buildEnvText, targetUrl) {
}

// ---------------------------------------------------------------------------------------------------------------------
// Target descriptor: ONE versioned, validated record per enabled kind, produced by the Delivery plan job and consumed by the
// Target descriptor: ONE versioned, validated record per enabled kind, produced by the Plan job and consumed by the
// thin per-kind workflow calls (and by every engine stage). It replaces the per-field `with:` copies (engine target, GitHub
// Environment, concurrency groups, expected D1, migration owner, smoke policy). Pure data: never a secret, never a URL
// (URLs are resolved against Cloudflare before each build), validated at generation AND at every consumer.
Expand Down
13 changes: 7 additions & 6 deletions docs/cf-worker-v2-refactor.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ checked read-only (`heaven-monorepo`, `innovation-apps`) this cannot matter —

### Migration (unchanged guarantees)

Exactly one remote-D1 job per pipeline, same order, same Environment protection: `Validation → Plan → Database Migration (resolve D1 · validate bindings · audit effective config · stale-SHA guard · apply once) → Delivery`.
Exactly one remote-D1 job per pipeline, same order, same Environment protection: `Validation → Plan → Migrate (resolve D1 · validate bindings · audit effective config · stale-SHA guard · apply once) → Delivery`.
It stays inline in the public workflow: extracting it would add a workflow-call layer and re-plumb the descriptors, the inputs blob and the credentials without removing a stage or
weakening anything, which the brief rules out. Preview/Dev still can never reach the Production D1 (plan + engine audit); the shared-D1 group still serialises Workers sharing one D1; distinct physical D1s are still rejected by the plan.

Expand Down Expand Up @@ -195,7 +195,7 @@ The scratch Environments are unprotected and hold fake D1 ids, so Environment ap

No call enables two distinct physical D1s in any modeled event, so the single-D1 rule does not reject any supported configuration. Not modeled: the post-build effective-config audit of Astro/adapter-generated configs (blog Shared Dev is off; it runs only in a live Migration/Deploy).

**Required Checks.** Branch protection and rulesets could not be read for the four private repositories: the API answers `403 Upgrade to GitHub Pro or make this repository public` although the token has repository admin rights (organization plan: `free`; organization rulesets: `Upgrade to GitHub Team`). This is a plan limit, not a permissions gap, and it makes enforcement unlikely, but it is not proof — a rule configured under an earlier plan would be invisible here. Treat them as **UNVERIFIED — ADMIN ACCESS REQUIRED** and have an administrator confirm in the repository settings that none of the following contexts is required (`<job>` is the caller's job name: `Pipeline` for blog, innovation-apps and heaven-www; `Heaven ID`, `Heaven Checkin`, `Heaven ID Test Client` for heaven-monorepo; `cf` for anas-mcp). Required-check names as seen on the consumers' recent default-branch runs ("today") and in the `8ad4d40` scratch runs ("after"):
**Required Checks.** Branch protection and rulesets could not be read for the four private repositories: the API answers `403 Upgrade to GitHub Pro or make this repository public` although the token has repository admin rights (organization plan: `free`; organization rulesets: `Upgrade to GitHub Team`). This is a plan limit, not a permissions gap, and it makes enforcement unlikely, but it is not proof — a rule configured under an earlier plan would be invisible here. Treat them as **UNVERIFIED — ADMIN ACCESS REQUIRED** and have an administrator confirm in the repository settings that none of the following contexts is required (`<job>` is the caller's job name: `Pipeline` for blog, innovation-apps and heaven-www (blog and heaven-www move to `CD`); `Heaven ID`, `Heaven Checkin`, `Heaven ID Test Client` for heaven-monorepo; `cf` for anas-mcp). Required-check names as seen on the consumers' recent default-branch runs ("today") and in the `8ad4d40` scratch runs ("after"):

| Today (consumer runs) | After `8ad4d40` |
| --- | --- |
Expand All @@ -206,9 +206,10 @@ No call enables two distinct physical D1s in any modeled event, so the single-D1
| `<job> / Build Validation` | `<job> / CI / Build Validation` |
| `<job> / E2E` and `<job> / E2E (<name>, <args>, <suffix>)` (blog: four variants) | `<job> / CI / E2E` and `<job> / CI / E2E (<name>, <args>, <suffix>)` |
| — | `<job> / CI / Validation Gate` (new aggregate) |
| `<job> / Migration` | `<job> / Database Migration` |
| `<job> / Deploy`, `Smoke Tests`, `Revert on Verify Failure`, `Shared Dev`, `Shared Dev Smoke Tests`, `Shared Dev Revert on Verify Failure` | `<job> / Delivery (production\|native-branch\|native-dev\|persistent-dev) / Deploy\|Smoke Tests\|Revert on Verify Failure` |
| `<job> / Release`, `Config guard`, `Preview lifecycle`, `Persistent Dev (Hybrid)` | unchanged; new `<job> / Delivery plan` |
| `<job> / Migration` | `<job> / Migrate` |
| `<job> / Deploy`, `Smoke Tests`, `Revert on Verify Failure`, `Shared Dev`, `Shared Dev Smoke Tests`, `Shared Dev Revert on Verify Failure` | `<job> / <kind> / Deploy\|Smoke Tests\|Revert on Verify Failure` (`<kind>` = `production\|native-branch\|native-dev\|persistent-dev`) |
| `<job> / Release` | unchanged |
| `<job> / Config guard`, `Preview lifecycle`, `Persistent Dev (Hybrid)` | `<job> / Guard`, `<job> / lifecycle / ledger`, `<job> / persistent-dev / manual`; new `<job> / Plan` |

Skipped jobs (unconfigured checks) report as skipped; a required context that is never emitted blocks merging. Nothing was modified in any repository's protection settings.

Expand All @@ -227,7 +228,7 @@ Skipped jobs (unconfigured checks) report as skipped; a required context that is
### Remaining risks and limitations

* **Runner floor 2.336.0** for every runner passed as `runner` (see above); GHES is unsupported by `$/`. Confirmed `2.338.0` on all ten org runners on 2026-11-03.
* **Check names changed** (`<job> / CI / Lint`, `<job> / Database Migration`, `<job> / Delivery (<kind>) / …`): required status checks in other consumers must be updated when they adopt this ref; they are unreadable on four of the five consumers (Free plan), see the table above.
* **Check names changed** (`<job> / CI / Lint`, `<job> / Migrate`, `<job> / <kind> / …`): required status checks in other consumers must be updated when they adopt this ref; they are unreadable on four of the five consumers (Free plan), see the table above.
* `actionlint` cannot lint `$/` yet (two message formats ignored, guarded by the contract test).
* A verify call queues under the recorded group before the Plan proves it (see "Concurrency and phase_state").
* The public workflow is 769 lines, above the 600–750 target: the remaining bulk is the 60-input / 20-output public interface (≈ 300 lines), which Phase 1 may not change.
Expand Down
Loading
Loading