rcf-lite 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -1
- package/bin/rcf.js +3 -1
- package/bin/view-supervisor-child.mjs +0 -0
- package/blueprints/application-api-rest/docs/topics.md +1 -1
- package/blueprints/application-spa/README.md +3 -3
- package/blueprints/application-spa/contributions/adrs/adr-202-application-spa-theming.json +1 -1
- package/blueprints/application-spa/contributions/adrs/adr-206-application-spa-iconography.json +1 -1
- package/blueprints/application-spa/contributions/tacs/tac-207-application-spa-token-adherence-probe.json +7 -7
- package/blueprints/application-spa/contributions/tacs/tac-208-application-spa-icon-adherence-probe.json +7 -7
- package/blueprints/application-spa/contributions/tacs/tac-209-application-spa-csp-styled-adherence-probe.json +8 -8
- package/blueprints/application-spa/contributions/tacs/tac-210-application-spa-external-dependency-provisioning-probe.json +8 -8
- package/blueprints/application-spa/contributions/tacs/tac-211-application-spa-core-flow-e2e-probe.json +8 -8
- package/blueprints/application-spa/contributions/user-stories/application-spa-us-1129.json +2 -2
- package/blueprints/application-spa/contributions/user-stories/application-spa-us-1130.json +2 -2
- package/blueprints/application-spa/contributions/user-stories/application-spa-us-1131.json +2 -2
- package/blueprints/application-spa/contributions/user-stories/application-spa-us-1132.json +2 -2
- package/blueprints/application-spa/contributions/user-stories/application-spa-us-1133.json +3 -3
- package/blueprints/application-spa/docs/topics.md +1 -1
- package/blueprints/delivery-ci-workflows/CHANGELOG.md +39 -0
- package/blueprints/delivery-ci-workflows/README.md +61 -0
- package/blueprints/delivery-ci-workflows/assets/bootstrap/README.md +26 -0
- package/blueprints/delivery-ci-workflows/assets/bootstrap/adr-bootstrap-coverage-supersession.template.json +28 -0
- package/blueprints/delivery-ci-workflows/assets/ci-provider-examples/github-actions/default-branch-checks.yml +61 -0
- package/blueprints/delivery-ci-workflows/assets/ci-provider-examples/github-actions/pull-request-checks.yml +74 -0
- package/blueprints/delivery-ci-workflows/assets/ci-provider-examples/github-actions/release.yml +75 -0
- package/blueprints/delivery-ci-workflows/assets/ci-provider-examples/github-actions/scheduled-audit.yml +65 -0
- package/blueprints/delivery-ci-workflows/assets/ci-provider-examples/notes.md +61 -0
- package/blueprints/{ci-pipeline → delivery-ci-workflows}/assets/report-samples/per-gate.json +3 -2
- package/blueprints/delivery-ci-workflows/blueprint.json +87 -0
- package/blueprints/delivery-ci-workflows/contributions/adrs/adr-701-delivery-ci-workflows-ci-gates.json +30 -0
- package/blueprints/{ci-pipeline/contributions/adrs/adr-702-ci-pipeline-strict-coverage-gate.json → delivery-ci-workflows/contributions/adrs/adr-702-delivery-ci-workflows-strict-coverage-gate.json} +4 -4
- package/blueprints/{ci-pipeline/contributions/adrs/adr-703-ci-pipeline-node-only-runner.json → delivery-ci-workflows/contributions/adrs/adr-703-delivery-ci-workflows-node-only-runner.json} +1 -1
- package/blueprints/{ci-pipeline/contributions/adrs/adr-704-ci-pipeline-report-shape.json → delivery-ci-workflows/contributions/adrs/adr-704-delivery-ci-workflows-report-shape.json} +3 -3
- package/blueprints/delivery-ci-workflows/contributions/adrs/adr-705-delivery-ci-workflows-elicitation-surface.json +25 -0
- package/blueprints/delivery-ci-workflows/contributions/adrs/adr-706-delivery-ci-workflows-branch-model-defaults.json +25 -0
- package/blueprints/delivery-ci-workflows/contributions/adrs/adr-707-delivery-ci-workflows-release-workflow-shape.json +25 -0
- package/blueprints/delivery-ci-workflows/contributions/adrs/adr-708-delivery-ci-workflows-provider-hint-shape.json +25 -0
- package/blueprints/delivery-ci-workflows/contributions/adrs/adr-709-delivery-ci-workflows-release-artefacts.json +25 -0
- package/blueprints/delivery-ci-workflows/contributions/adrs/adr-710-delivery-ci-workflows-scheduled-audit.json +25 -0
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-001.json +18 -0
- package/blueprints/{ci-pipeline/contributions/requirements/ci-pipeline-req-002.json → delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-002.json} +2 -2
- package/blueprints/{ci-pipeline/contributions/requirements/ci-pipeline-req-003.json → delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-003.json} +2 -2
- package/blueprints/{ci-pipeline/contributions/requirements/ci-pipeline-req-004.json → delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-004.json} +2 -2
- package/blueprints/{ci-pipeline/contributions/requirements/ci-pipeline-req-005.json → delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-005.json} +4 -4
- package/blueprints/{ci-pipeline/contributions/requirements/ci-pipeline-req-006.json → delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-006.json} +2 -2
- package/blueprints/{ci-pipeline/contributions/requirements/ci-pipeline-req-007.json → delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-007.json} +2 -2
- package/blueprints/{ci-pipeline/contributions/requirements/ci-pipeline-req-008.json → delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-008.json} +2 -2
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-009.json +18 -0
- package/blueprints/{ci-pipeline/contributions/requirements/ci-pipeline-req-010.json → delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-010.json} +2 -2
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-011.json +18 -0
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-012.json +18 -0
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-013.json +18 -0
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-014.json +18 -0
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-015.json +18 -0
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-016.json +18 -0
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-017.json +18 -0
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-018.json +18 -0
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-019.json +18 -0
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-020.json +18 -0
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-021.json +18 -0
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-022.json +18 -0
- package/blueprints/delivery-ci-workflows/contributions/requirements/delivery-ci-workflows-req-023.json +18 -0
- package/blueprints/{ci-pipeline/contributions/tacs/tac-701-ci-pipeline-gate-runner.json → delivery-ci-workflows/contributions/tacs/tac-701-delivery-ci-workflows-gate-runner.json} +5 -5
- package/blueprints/{ci-pipeline/contributions/tacs/tac-702-ci-pipeline-gate-report.json → delivery-ci-workflows/contributions/tacs/tac-702-delivery-ci-workflows-gate-report.json} +1 -1
- package/blueprints/{ci-pipeline/contributions/tacs/tac-703-ci-pipeline-aggregate-report.json → delivery-ci-workflows/contributions/tacs/tac-703-delivery-ci-workflows-aggregate-report.json} +2 -2
- package/blueprints/delivery-ci-workflows/contributions/tacs/tac-704-delivery-ci-workflows-workflow-materialiser.json +67 -0
- package/blueprints/delivery-ci-workflows/contributions/tacs/tac-705-delivery-ci-workflows-release-workflow.json +51 -0
- package/blueprints/delivery-ci-workflows/contributions/tacs/tac-706-delivery-ci-workflows-scheduled-audit.json +38 -0
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6101.json +37 -0
- package/blueprints/{ci-pipeline/contributions/user-stories/ci-pipeline-us-6102.json → delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6102.json} +3 -3
- package/blueprints/{ci-pipeline/contributions/user-stories/ci-pipeline-us-6103.json → delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6103.json} +4 -4
- package/blueprints/{ci-pipeline/contributions/user-stories/ci-pipeline-us-6104.json → delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6104.json} +4 -4
- package/blueprints/{ci-pipeline/contributions/user-stories/ci-pipeline-us-6105.json → delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6105.json} +6 -6
- package/blueprints/{ci-pipeline/contributions/user-stories/ci-pipeline-us-6106.json → delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6106.json} +3 -3
- package/blueprints/{ci-pipeline/contributions/user-stories/ci-pipeline-us-6107.json → delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6107.json} +5 -5
- package/blueprints/{ci-pipeline/contributions/user-stories/ci-pipeline-us-6108.json → delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6108.json} +4 -4
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6109.json +36 -0
- package/blueprints/{ci-pipeline/contributions/user-stories/ci-pipeline-us-6110.json → delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6110.json} +3 -3
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6111.json +36 -0
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6112.json +36 -0
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6113.json +36 -0
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6114.json +46 -0
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6115.json +37 -0
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6116.json +28 -0
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6117.json +28 -0
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6118.json +37 -0
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6119.json +28 -0
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6120.json +28 -0
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6121.json +46 -0
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6122.json +46 -0
- package/blueprints/delivery-ci-workflows/contributions/user-stories/delivery-ci-workflows-us-6123.json +37 -0
- package/blueprints/delivery-ci-workflows/docs/topics.md +61 -0
- package/blueprints/delivery-ci-workflows/guide/delivery-ci-workflows.md +174 -0
- package/blueprints/deploy-cloudflare-workers/docs/topics.md +3 -3
- package/blueprints/email-smtp-resend/docs/topics.md +1 -1
- package/blueprints/observability-essentials/README.md +2 -2
- package/blueprints/observability-essentials/docs/topics.md +5 -5
- package/blueprints/observability-probe-endpoints/docs/topics.md +2 -2
- package/blueprints/persistence-data-d1/README.md +2 -2
- package/blueprints/persistence-data-d1/assets/facade-shape/facade-module-shape.md +1 -1
- package/blueprints/persistence-data-d1/contributions/tacs/tac-1403-persistence-data-d1-deploy-gate.json +1 -1
- package/blueprints/persistence-data-d1/docs/topics.md +2 -2
- package/blueprints/persistence-data-d1/guide/persistence-data-d1.md +1 -1
- package/blueprints/persistence-data-sqlite/README.md +1 -1
- package/blueprints/persistence-data-sqlite/docs/topics.md +1 -1
- package/blueprints/security-auth-clerk/README.md +5 -3
- package/blueprints/security-auth-clerk/assets/middleware/workers-fetch-shape.md +123 -0
- package/blueprints/security-auth-clerk/assets/wiring/workers-wrangler-toml-shape.md +51 -0
- package/blueprints/security-auth-clerk/blueprint.json +1 -1
- package/blueprints/security-auth-clerk/docs/topics.md +2 -2
- package/blueprints/security-auth-clerk/guide/security-auth-clerk.md +9 -0
- package/blueprints/security-auth-keycloak/docs/topics.md +2 -2
- package/blueprints/security-auth-magic-link/README.md +1 -1
- package/blueprints/security-auth-magic-link/docs/topics.md +1 -1
- package/blueprints/security-auth-oauth2/README.md +1 -1
- package/blueprints/security-auth-oauth2/docs/topics.md +2 -2
- package/blueprints/security-secrets-management/README.md +1 -1
- package/blueprints/security-secrets-management/docs/topics.md +3 -3
- package/guidance/build-cycle-playbook.md +2 -2
- package/guidance/document-model.md +1 -1
- package/guidance/harness-template.md +13 -0
- package/guidance/managed/agent-instructions-block.hash +1 -1
- package/guidance/managed/agent-instructions-block.md +13 -0
- package/package.json +15 -14
- package/rcf/adrs/adr-001.json +1 -1
- package/rcf/adrs/adr-009.json +1 -1
- package/rcf/build-sequence.json +1 -1
- package/rcf/manifest.json +2 -2
- package/rcf/prd.json +2 -2
- package/releases/releases.yaml +126 -0
- package/src/blueprint/apply.js +60 -13
- package/src/blueprint/index.js +12 -0
- package/src/blueprint/library-loader.js +292 -0
- package/src/blueprint/library-registry.js +341 -0
- package/src/blueprint/list.js +38 -4
- package/src/blueprint/shelf-resolver.js +144 -31
- package/src/blueprint/supersede.js +56 -13
- package/src/cli/blueprint-library.js +447 -0
- package/src/cli/blueprint.js +50 -9
- package/src/cli/guidance.js +1 -1
- package/src/cli/help.js +27 -1
- package/src/cli/version.js +673 -0
- package/src/cli/view.js +282 -1
- package/src/server/index.js +3 -0
- package/src/server/routes.js +15 -1
- package/src/server/scope-endpoint.js +105 -0
- package/src/view/live-client.js +253 -6
- package/src/view/scope.js +231 -0
- package/src/view/style.css +42 -0
- package/blueprints/ci-pipeline/README.md +0 -49
- package/blueprints/ci-pipeline/assets/ci-provider-examples/github-actions.yml +0 -61
- package/blueprints/ci-pipeline/assets/ci-provider-examples/notes.md +0 -50
- package/blueprints/ci-pipeline/blueprint.json +0 -46
- package/blueprints/ci-pipeline/contributions/adrs/adr-701-ci-pipeline-ci-gates.json +0 -25
- package/blueprints/ci-pipeline/contributions/requirements/ci-pipeline-req-001.json +0 -18
- package/blueprints/ci-pipeline/contributions/requirements/ci-pipeline-req-009.json +0 -18
- package/blueprints/ci-pipeline/contributions/user-stories/ci-pipeline-us-6101.json +0 -37
- package/blueprints/ci-pipeline/contributions/user-stories/ci-pipeline-us-6109.json +0 -36
- package/blueprints/ci-pipeline/docs/topics.md +0 -49
- package/blueprints/ci-pipeline/guide/ci-pipeline.md +0 -79
- package/rcf/.identity/profile.md +0 -37
- package/rcf/knowledge/INDEX.md +0 -12
- package/rcf/knowledge/README.md +0 -41
- package/rcf/knowledge/docs/.gitkeep +0 -0
- package/rcf/knowledge/notes/.gitkeep +0 -0
- /package/blueprints/{ci-pipeline → delivery-ci-workflows}/assets/report-samples/pipeline.json +0 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# delivery-ci-workflows blueprint guide
|
|
2
|
+
|
|
3
|
+
## What it is
|
|
4
|
+
|
|
5
|
+
The default CI floor for rcf-lite projects, expressed as a workflow SET the operator declares once and the workflow-materialiser produces from that declaration. The blueprint contributes the WHAT of the workflow set: which workflows exist per branch model, which required check set every check-set workflow runs, what the release workflow does per release mode, what the scheduled-audit workflow does when enabled, and what report files land on disk after every run. Each workflow is a single Node entry point the CI job invokes with one line; the elicitation surface is the four required plus two optional fields of `workflowShape` in `.rcf/config/delivery-ci-workflows.json` on the project tree.
|
|
6
|
+
|
|
7
|
+
Concretely, the blueprint ships 23 requirements, 23 user stories (roughly 50 acceptance criteria), 6 architecture components, and 10 architecture decision records. Three ADRs are `scope: global` on the topics `ciGates` (the required check set every commit-triggered workflow runs), `strictCoverageGate` (the per-AC strict coverage posture), and `releaseArtefacts` (the four-mode release workflow shape); the other seven ADRs are scope-local.
|
|
8
|
+
|
|
9
|
+
## What it is not
|
|
10
|
+
|
|
11
|
+
Not a matrix of provider-specific configuration files. v2.0.0 ships one illustrative GitHub Actions workflow file per workflow the matrix materialises under `assets/ci-provider-examples/github-actions/` (`pull-request-checks.yml`, `default-branch-checks.yml`, `release.yml`, `scheduled-audit.yml`) as copy-paste starting points. Alternate providers (GitLab CI, CircleCI, Buildkite, Jenkins) wire the same Node entry points per the four-point mapping named below applied to each workflow; the blueprint does not carry per-provider configuration files for those runners at v2.0.0.
|
|
12
|
+
|
|
13
|
+
Not a source-code contribution. The blueprint contributes the workflow-materialiser contract, the gate-runner contract, the release-workflow contract, the scheduled-audit contract, the per-gate report writer contract, the aggregate report writer contract, and the discipline that binds them. The project realises the Node entry points against those contracts in its own build cycle; the blueprint does not ship the entry-point source.
|
|
14
|
+
|
|
15
|
+
Not a choice of linter, formatter, typechecker, test runner, or security scanner. The elicited check catalogue names the check kinds; the project picks the tools that satisfy the runner-contract interfaces. A code-formatting style guide, a security-scan severity threshold, and the linter rule set are all project decisions the blueprint does not opine on.
|
|
16
|
+
|
|
17
|
+
Not a release-mechanism. The release workflow reacts to a tag or a `workflow_dispatch`; the project creates the tag through its own release mechanism (a `standard-version` invocation, a manual git tag, a release-please bot). The blueprint does not ship a tag-creation workflow, a changelog generator, or a version-bump tool.
|
|
18
|
+
|
|
19
|
+
Not a merge-queue integration or a coverage-trend dashboard. A project that wants either reads the aggregate reports from the queue tool or the dashboard of its choice; the blueprint owns the aggregate report shapes and stability, not the reader wiring.
|
|
20
|
+
|
|
21
|
+
Not a branch-protection or merge-policy configuration. AC-6101-2, AC-6114-3, AC-6115-1, and AC-6115-2 state the property the project owes on the platform's own configuration surface per branch model; the blueprint does not ship the configuration file.
|
|
22
|
+
|
|
23
|
+
## When to reach for it
|
|
24
|
+
|
|
25
|
+
Reach for the `delivery-ci-workflows` blueprint when:
|
|
26
|
+
|
|
27
|
+
- The project is an rcf-lite deployment with an RCF chain and a default or trunk branch.
|
|
28
|
+
- The project's CI provider can run Node 24 or later on its runner (every mainstream provider can).
|
|
29
|
+
- The project wants the required check set (RCF chain plus enabled elicited checks) to be a merge-blocking property, not an author-time habit.
|
|
30
|
+
- The project wants a stable per-run report artefact any downstream reader (dashboard, audit tool, merge queue) can pick up without scraping the CI provider's log surface.
|
|
31
|
+
- The project wants a release workflow that scales with what the project has already declared (from `none` for internal packages up to `deployHandoff:<slug>` for paired services).
|
|
32
|
+
|
|
33
|
+
## When it does not fit
|
|
34
|
+
|
|
35
|
+
Do not reach for the `delivery-ci-workflows` blueprint when:
|
|
36
|
+
|
|
37
|
+
- The project does not use RCF (no `rcf/` tree, no `rcf define validate` invocation makes sense).
|
|
38
|
+
- The project runs on a CI provider whose runners cannot execute Node 24 or later.
|
|
39
|
+
- The project wants a coverage-mode grace window on newly introduced ACs (supersede ADR-702 with a project-level ADR stating the grace window).
|
|
40
|
+
- The project wants required checks that are not in the catalogue and not naturally cross-blueprint (a mutation-testing gate, an accessibility scan). Supersede ADR-701 with a project-level ADR listing the extended catalogue and register the check under `custom:<name>`.
|
|
41
|
+
|
|
42
|
+
## What a good outcome looks like
|
|
43
|
+
|
|
44
|
+
A project applies the `delivery-ci-workflows` blueprint on a fresh tree, populates `.rcf/config/delivery-ci-workflows.json` with its `workflowShape` (four required fields plus any optional ones), realises the six TACs in project-authored FBSes, and lands on a deployed workflow set where:
|
|
45
|
+
|
|
46
|
+
- Every commit-triggered event fires the appropriate workflow (per branch model). The workflow's aggregate report at `.rcf/reports/ci/pipeline.json` records the trigger, the timing, and the ordered gate outcomes including the `checkKind` per gate.
|
|
47
|
+
- A pull request whose head commit fails any required check sees the failed check in the aggregate, sees the specific issue in the per-gate report, and cannot be merged through the platform's standard merge path.
|
|
48
|
+
- A pull request whose head commit passes every required check sees `verdict: passed` in the aggregate, sees green on the platform's required-check surface, and can be merged.
|
|
49
|
+
- A release trigger fires the release workflow per `releaseMode`: `none` means no workflow at all; `tagOnly` creates a release entity; `tagPlusArtefact` also publishes an artefact; `deployHandoff:<slug>` also invokes the named deploy blueprint's promote workflow with the `versionId` input.
|
|
50
|
+
- When `scheduledAudit: true`, the scheduled-audit workflow fires on a cron cadence and writes to `.rcf/reports/ci/scheduled-audit.json`, distinct from commit-triggered `pipeline.json`.
|
|
51
|
+
- A developer reproducing a CI failure on their machine runs the same one-line invocation the CI job's step recorded and sees the same per-gate outcomes at the same report paths.
|
|
52
|
+
|
|
53
|
+
## The workflowShape declaration (four dimensions plus two optional)
|
|
54
|
+
|
|
55
|
+
The project ships `.rcf/config/delivery-ci-workflows.json` at the project root. The declaration has three required fields, one optional field with defined default semantics, and two optional dimensions.
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"workflowShape": {
|
|
60
|
+
"branchModel": "feature",
|
|
61
|
+
"checkSet": {
|
|
62
|
+
"linter": true,
|
|
63
|
+
"formatter": true,
|
|
64
|
+
"typecheck": true,
|
|
65
|
+
"unitTest": true,
|
|
66
|
+
"securityScan": true
|
|
67
|
+
},
|
|
68
|
+
"releaseMode": "tagPlusArtefact",
|
|
69
|
+
"providerHint": "githubActions",
|
|
70
|
+
"scheduledAudit": false,
|
|
71
|
+
"defaultBranch": "main",
|
|
72
|
+
"packageManager": "pnpm"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- `branchModel` (required): `feature` or `trunk`.
|
|
78
|
+
- `checkSet` (required): object of booleans keyed by elicited-check name.
|
|
79
|
+
- `providerHint` (required): `githubActions`, `gitlabCi`, `circleCi`, `buildkite`, or `jenkins`.
|
|
80
|
+
- `releaseMode` (OPTIONAL, Q6-B ratification): `none`, `tagOnly`, `tagPlusArtefact`, or `deployHandoff:<slug>`. An absent field is treated the same as `none` (no release workflow ships). Not having a release path yet is a common scenario when starting a project; the ratification chose ergonomics over an explicit `none`.
|
|
81
|
+
- `scheduledAudit` (optional): boolean; default `false`.
|
|
82
|
+
- `trunkPullRequests` (optional; qualifies `branchModel: trunk`): `never` or `sometimes`; default `never`.
|
|
83
|
+
- `defaultBranch` (optional; qualifies `branchModel: feature`): the branch name the commit-triggered workflows fire against; default `main`. The materialiser substitutes this value into the trigger's branch list; a project on `develop`, `master`, or any other convention names it here rather than hand-editing the workflow files.
|
|
84
|
+
- `trunkBranch` (optional; qualifies `branchModel: trunk`): the trunk branch name the commit-triggered workflows fire against; default `main`. Same substitution semantics as `defaultBranch`.
|
|
85
|
+
- `packageManager` (optional): `pnpm`, `npm`, `yarn`, or `bun`; default `pnpm`. Selects the within-provider package-manager substitution the materialiser applies to every produced workflow (see the substitution table below).
|
|
86
|
+
|
|
87
|
+
### Profile aliases
|
|
88
|
+
|
|
89
|
+
Named profile aliases (`smallLibrary`, `smallService`, `internalPackage`, `trunkLibrary`) compose the four-field form. The materialiser expands the alias to the four-field form at boot; every AC binds to the four-field form. Adding an alias is a minor bump; renaming or removing an alias is a minor bump.
|
|
90
|
+
|
|
91
|
+
## Bootstrap posture (the guaranteed-red first run)
|
|
92
|
+
|
|
93
|
+
A fresh apply of this blueprint alongside its usual companions (an `application-*` blueprint plus a `persistence-*` blueprint, for example) contributes tens to hundreds of REQs and ACs and zero test cases. The mandatory tier's `coverage-strict` gate runs `rcf audit coverage --strict` which refuses when any AC lacks a resolving TC (ADR-702). That means the very first commit-triggered workflow the materialiser produces is guaranteed to refuse until either every AC has a TC OR a project-level ADR demotes the gate. This is a cost the operator either accepts up-front or defers behind a stated exit criterion; either way, name the posture at apply time rather than discovering it when the first PR is refused.
|
|
94
|
+
|
|
95
|
+
Two ratified bootstrap postures:
|
|
96
|
+
|
|
97
|
+
- **Author every TC before turning CI green.** Every AC the blueprint set contributed gets a TC before the first push; the coverage-strict gate stays merge-blocking from day one. Right when the AC count is small or the project's engineering standards refuse any advisory-only CI window.
|
|
98
|
+
- **Demote coverage-strict to advisory-only for a bounded window.** Ship a project-level ADR that supersedes ADR-702 on the `strictCoverageGate` topic for the project only, demoting the gate to advisory (its per-gate report still lands; its `failed` outcome no longer flips the aggregate). Bind the ADR to a stated exit criterion (recommended: `N` consecutive `passed` outcomes on the default branch, e.g. `N = 5`, or an audit confirming every AC has a resolving TC, whichever comes first). Retire the ADR by flipping its status to `superseded` when the criterion is met; the coverage-strict gate reverts to merge-blocking without further action.
|
|
99
|
+
|
|
100
|
+
The blueprint ships a starting-point ADR for the demotion posture at `assets/bootstrap/adr-bootstrap-coverage-supersession.template.json` (see the sibling `README.md` for the copy-adapt-register steps). Copy it, rename the id, fill the timestamps, register it as `scope: global` on topic `strictCoverageGate`, and the CI's first run turns green on every non-coverage gate on day one. The debt is real; the exit criterion keeps it visible.
|
|
101
|
+
|
|
102
|
+
## The check catalogue
|
|
103
|
+
|
|
104
|
+
Two tiers:
|
|
105
|
+
|
|
106
|
+
- **Mandatory tier** (preserved from v1): `validate` (running `rcf define validate`) then `coverage-strict` (running `rcf audit coverage --strict`), in that order.
|
|
107
|
+
- **Elicited tier** (v2 additions): `linter`, `formatter`, `typecheck`, `unitTest`, `securityScan`. Default: every catalogued check on. Per-check auto-detection may adjust the default (typecheck defaults on when the project tree contains a `tsconfig.json` or equivalent tool marker).
|
|
108
|
+
|
|
109
|
+
Each elicited check ships an AC contract stating what its runner must produce: a non-zero exit code on any blocking finding, a per-gate report at the check's stable path, and a `checkKind` field naming the check kind. The blueprint does NOT ship the linter, formatter, typechecker, test runner, or scanner; the project picks the tools.
|
|
110
|
+
|
|
111
|
+
Custom checks (`custom:<name>`) supersede this ADR at the project level and register alongside the catalogued checks in the aggregate.
|
|
112
|
+
|
|
113
|
+
## The four release modes
|
|
114
|
+
|
|
115
|
+
`releaseMode` scales what the release workflow does:
|
|
116
|
+
|
|
117
|
+
- `none` (or absent, per Q6-B ratification): no release workflow ships.
|
|
118
|
+
- `tagOnly`: on a release trigger, creates a release entity on the provider's release surface (a GitHub Release, a GitLab release).
|
|
119
|
+
- `tagPlusArtefact`: on a release trigger, creates the release entity and runs the project-realised artefact-publish step, uploading the artefact to the release entity.
|
|
120
|
+
- `deployHandoff:<slug>`: on a release trigger, runs the tagPlusArtefact flow and then dispatches the named deploy blueprint's `promote` workflow via `workflow_dispatch` with the just-published version identifier as the `versionId` input.
|
|
121
|
+
|
|
122
|
+
The release workflow does NOT invoke the required check set: that already ran on the `default-branch-checks` workflow at the commit the release points at.
|
|
123
|
+
|
|
124
|
+
The `deployHandoff:<slug>` mode has a manifest boot-check: the materialiser refuses when the named deploy blueprint is absent from `manifest.blueprints[]`.
|
|
125
|
+
|
|
126
|
+
## Wiring alternate CI providers (four-point mapping applied per workflow)
|
|
127
|
+
|
|
128
|
+
Every mainstream CI provider ships the same four ingredients under a different runner language. The blueprint's Node entry points are the fourth ingredient (one per workflow); the other three are provider-specific. The illustrative GitHub Actions workflow files show all four applied per workflow; alternate providers translate the first three and keep the fourth unchanged per workflow:
|
|
129
|
+
|
|
130
|
+
1. Job trigger. The trigger set per workflow (pull_request into the target branch for `pull-request-checks`; push to the target branch for `default-branch-checks`; release event or workflow_dispatch for `release`; cron schedule for `scheduled-audit`).
|
|
131
|
+
2. Node setup.
|
|
132
|
+
3. Package-manager setup and install.
|
|
133
|
+
4. Node entry-point invocation and artefact upload per workflow.
|
|
134
|
+
|
|
135
|
+
See `assets/ci-provider-examples/notes.md` for the per-provider translation.
|
|
136
|
+
|
|
137
|
+
## Within-provider package-manager substitution
|
|
138
|
+
|
|
139
|
+
The four-point mapping covers translation across providers; a project stays on one provider and picks its package manager. The illustrative GHA assets carry paired substitution markers fencing the setup step(s) and the install step; the materialiser replaces every line between the two fence markers (exclusive) with the per-manager block. The markers are YAML comments so the illustrative file is valid YAML on its own and reviewable as a diff after materialisation:
|
|
140
|
+
|
|
141
|
+
- `# @@RCF-SUB-PKG-MGR-SETUP-BEGIN@@` and `# @@RCF-SUB-PKG-MGR-SETUP-END@@` fence the manager's setup step(s).
|
|
142
|
+
- `# @@RCF-SUB-PKG-MGR-INSTALL-BEGIN@@` and `# @@RCF-SUB-PKG-MGR-INSTALL-END@@` fence the manager's install step.
|
|
143
|
+
|
|
144
|
+
The four recognised managers map to these blocks (GHA reference; alternate providers use the equivalent action or command in the provider's own runner language):
|
|
145
|
+
|
|
146
|
+
| `packageManager` | setup step | install line |
|
|
147
|
+
|---|---|---|
|
|
148
|
+
| `pnpm` (default) | `- uses: pnpm/action-setup@v4` followed by `with: { version: 9 }`; then `- uses: actions/setup-node@v4` with `node-version: 24` and `cache: pnpm` | `pnpm install --frozen-lockfile` |
|
|
149
|
+
| `npm` | `- uses: actions/setup-node@v4` with `node-version: 24` and `cache: npm` | `npm ci` |
|
|
150
|
+
| `yarn` | `- uses: actions/setup-node@v4` with `node-version: 24` and `cache: yarn` (yarn v1 or `enable-corepack` for berry) | `yarn install --frozen-lockfile` (v1) or `yarn install --immutable` (berry) |
|
|
151
|
+
| `bun` | `- uses: oven-sh/setup-bun@v2` with `bun-version: latest` | `bun install --frozen-lockfile` |
|
|
152
|
+
|
|
153
|
+
An unrecognised `packageManager` value refuses at the same boot-check exit path the enumerated fields use (AC-6112-2 shape).
|
|
154
|
+
|
|
155
|
+
## Trigger branch-name substitution
|
|
156
|
+
|
|
157
|
+
The commit-triggered assets (`pull-request-checks.yml`, `default-branch-checks.yml`) carry `# @@RCF-SUB-BRANCH-NAME@@` on the line immediately above the `branches:` list. The materialiser rewrites that one `branches:` line from `workflowShape.defaultBranch` (feature model) or `workflowShape.trunkBranch` (trunk model) so the branch-model AC (AC-6114/6115) is observable in the materialised output rather than being masked by a hard-coded `main`. Both fields default to `main`, so a project on the default keeps the current behaviour without touching the fields.
|
|
158
|
+
|
|
159
|
+
## Operator decisions that remain open after apply
|
|
160
|
+
|
|
161
|
+
- The workflow shape declaration (`.rcf/config/delivery-ci-workflows.json` populated).
|
|
162
|
+
- Report directory path (default `.rcf/reports/ci/`).
|
|
163
|
+
- Tool choices for elicited checks (linter, formatter, typechecker, unit-test runner, security scanner).
|
|
164
|
+
- Security-scan severity threshold.
|
|
165
|
+
- Coverage-mode posture (supersede ADR-702 with a project-level ADR if a different posture is wanted).
|
|
166
|
+
- CI provider choice and the trigger/setup/install steps per workflow.
|
|
167
|
+
- Branch-protection or push-protection configuration on the default or trunk branch per branch model.
|
|
168
|
+
- Artefact-upload retention and destination.
|
|
169
|
+
- Merge-queue integration and coverage-trend dashboard wiring.
|
|
170
|
+
- Release-mechanism (how tags get created).
|
|
171
|
+
|
|
172
|
+
## Cost-honesty paragraph
|
|
173
|
+
|
|
174
|
+
Shipping this doc set costs the project the following. Every commit-triggered event runs the required check set every time; the elicited tier lengthens wall time in proportion to the number of enabled checks. The strict-coverage posture makes every new AC a two-file edit (author the AC and author the TC that resolves it). Adding a check kind outside the catalogue is a project-level ADR plus a runner realisation, not a knob-flip. The illustrative GHA assets are a starting point; a project on another provider owes the translation of the first three of the four mapping points per workflow (extending v1's per-workflow cost to a per-workflow-set cost). Branch-protection configuration is not shipped by the blueprint; a project that forgets to configure it satisfies every AC on the doc set and still ships a workflow set that does not block merges. The report directory grows one per-gate file per gate per run plus one aggregate per workflow per run; a project that runs many workflows against short-lived branches accumulates reports until the CI provider's artefact retention rolls them off.
|
|
@@ -6,11 +6,11 @@ This file is the deploy-cloudflare-workers half of the cross-blueprint contract.
|
|
|
6
6
|
|
|
7
7
|
| Topic string | deploy-cloudflare-workers contribution | Origin | Composition note |
|
|
8
8
|
|---|---|---|---|
|
|
9
|
-
| `deploymentTarget` | ADR-1301-deploy-cloudflare-workers-deployment-target | Minted here; pre-cleared as unclaimed against application-spa (`clientRouting`, `theming`, `clientState`, `errorEnvelope`, `authModel`), application-api-rest (`errorEnvelope`, `authModel`, `apiVersioning`, `logging`), security-auth-magic-link (`authModel`), persistence-data-sqlite (`persistenceStore`, `migrationDiscipline`), ci-
|
|
9
|
+
| `deploymentTarget` | ADR-1301-deploy-cloudflare-workers-deployment-target | Minted here; pre-cleared as unclaimed against application-spa (`clientRouting`, `theming`, `clientState`, `errorEnvelope`, `authModel`), application-api-rest (`errorEnvelope`, `authModel`, `apiVersioning`, `logging`), security-auth-magic-link (`authModel`), persistence-data-sqlite (`persistenceStore`, `migrationDiscipline`), delivery-ci-workflows (`ciGates`, `strictCoverageGate`), observability-essentials (`healthProbes`, `readinessSemantics`, `statusPageContract`), security-secrets-management (`secretsSource`), and the hello-panel walkthrough exemplar (`operatorPanel`) | The one project-wide model for how bits reach a running target: an artefact-upload + explicit-promote model, versions addressable at stable preview URLs, one adapter as the sole vendor caller, one served-surface verifier on every promote. A composing blueprint that holds a different opinion on the deployment shape (a container-image-per-commit + rolling-deploy blueprint, a coupled-merge-to-production blueprint, a serverless-function-per-endpoint blueprint) contributes its own scope:global ADR on this exact string and lets composition surface the pairing. Expected resolution: one project-level ADR that fixes the deployment shape and the vendor selection |
|
|
10
10
|
|
|
11
11
|
The deploy-cloudflare-workers blueprint claims one global topic. Every other contribution is scope-local (ADR-1302 through ADR-1305 name the default vendor, the preview-vs-production URL model, the rollback-is-promote posture, and the dev-mode drift posture without contributing global topics; a composing blueprint that holds a different opinion on any of them authors its own project-level ADR if it wants to override).
|
|
12
12
|
|
|
13
|
-
Rules for new topics (inherited from the application-spa, application-api-rest, security-auth-magic-link, persistence-data-sqlite, ci-
|
|
13
|
+
Rules for new topics (inherited from the application-spa, application-api-rest, security-auth-magic-link, persistence-data-sqlite, delivery-ci-workflows, observability-essentials, and security-secrets-management vocabularies, restated as law): lower camel case, one concept per topic, no version suffixes. A topic names the decision area, not the chosen answer. Do not mint variants of existing strings (`deploy`, `deployVendor`, `deployPipeline`, `shipTarget` are all wrong when `deploymentTarget` already exists).
|
|
14
14
|
|
|
15
15
|
## Id number bands (registry bootstrap)
|
|
16
16
|
|
|
@@ -26,7 +26,7 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
26
26
|
| email-smtp-resend | 4101-4899 | 4xx | shipped v1.0.0 | none |
|
|
27
27
|
| hello-panel (walkthrough exemplar) | 4101-4899 | 4xx | doc-reserved; teaching exemplar in `packages/rcf-lite/docs/blueprint-authoring-walkthrough.md`, not shipped as a blueprint directory | `operatorPanel` |
|
|
28
28
|
| persistence-data-sqlite | 5101-5899 | 6xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
29
|
-
| ci-
|
|
29
|
+
| delivery-ci-workflows | 6101-6899 | 7xx | shipped v2.0.0 (renamed from ci-pipeline) | `ciGates`, `strictCoverageGate`, `releaseArtefacts` |
|
|
30
30
|
| observability-essentials | 7101-7899 | 8xx | shipped v1.0.0 | `healthProbes`, `readinessSemantics`, `statusPageContract` |
|
|
31
31
|
| security-secrets-management | 8101-8899 | 9xx | shipped v1.0.0 | `secretsSource` |
|
|
32
32
|
| security-auth-clerk | 9101-9899 | 10xx | shipped v1.0.0 | `authModel` |
|
|
@@ -22,7 +22,7 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
22
22
|
| email-smtp-resend | 4101-4899 | 4xx | shipped v1.0.0 | none |
|
|
23
23
|
| hello-panel (walkthrough exemplar) | 4101-4899 | 4xx | doc-reserved; teaching exemplar in `packages/rcf-lite/docs/blueprint-authoring-walkthrough.md`, not shipped as a blueprint directory | `operatorPanel` |
|
|
24
24
|
| persistence-data-sqlite | 5101-5899 | 6xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
25
|
-
| ci-
|
|
25
|
+
| delivery-ci-workflows | 6101-6899 | 7xx | shipped v2.0.0 (renamed from ci-pipeline) | `ciGates`, `strictCoverageGate`, `releaseArtefacts` |
|
|
26
26
|
| observability-essentials | 7101-7899 | 8xx | shipped v1.0.0 | `healthProbes`, `readinessSemantics`, `statusPageContract` |
|
|
27
27
|
| security-secrets-management | 8101-8899 | 9xx | shipped v1.0.0 | `secretsSource` |
|
|
28
28
|
| security-auth-clerk | 9101-9899 | 10xx | shipped v1.0.0 | `authModel` |
|
|
@@ -21,7 +21,7 @@ Phase 1 resolves local path sources only; registry and git-ref resolution is a m
|
|
|
21
21
|
| Status page sample | `assets/status-page-samples/status-page-with-notice.html` | Illustrative rendered HTML with two components and one active incident notice, showing the machine-readable data attributes |
|
|
22
22
|
| Status page empty sample | `assets/status-page-samples/status-page-clean.html` | Illustrative rendered HTML with no active notice, showing the emptyState marker |
|
|
23
23
|
| Guide | `guide/observability-essentials.md` | Operator-facing: when to use it, when not, what stays your call, and the promotion signal for the future metrics and tracing variants |
|
|
24
|
-
| Coordination vocabulary | `docs/topics.md` | The three global-topic strings this blueprint contributes, the shared id band registry (application-spa, application-api-rest, security-auth-magic-link, hello-panel, persistence-data-sqlite, ci-
|
|
24
|
+
| Coordination vocabulary | `docs/topics.md` | The three global-topic strings this blueprint contributes, the shared id band registry (application-spa, application-api-rest, security-auth-magic-link, hello-panel, persistence-data-sqlite, delivery-ci-workflows, observability-essentials) |
|
|
25
25
|
|
|
26
26
|
The doc set is contributions (copied into the project tree by `rcf define blueprint add`); the guide, assets, and docs are package-resident references. Guide rendering into `rcf/knowledge/docs/blueprint-guides/` and asset ingestion are mechanism follow-ups; until they land, the working agent reads them from the applied blueprint's source path recorded in `manifest.blueprints[].source`.
|
|
27
27
|
|
|
@@ -50,4 +50,4 @@ Two HTTP health probes on the request-traffic listener with stable paths and a s
|
|
|
50
50
|
## Known mechanism-reach gaps
|
|
51
51
|
|
|
52
52
|
- **Historical uptime chart on the status page.** Not shipped at v1.0.0. The status page contract (ADR-803) commits to declared components with current state plus incident notices; a rolling window of per-component uptime numbers would require a metrics store the blueprint does not own and a retention window the blueprint should not decide unilaterally. Turning it into a runtime-observable AC ('the chart renders the last 30 days at 1-day granularity') either becomes document-observable ('a chart element is present') or depends on an unowned subsystem. Recorded here rather than smuggled in as a v1 requirement. Promotion signal: a shipped `metrics-store` blueprint (or the persistence-data-sqlite blueprint's event log fed forward) that a metrics-backed `statusPageContract` v1.1 can build on. Project-side workaround until then: a project that wants a history chart authors it on top of the declared component vocabulary using a project-owned metrics substrate.
|
|
53
|
-
- **Notification outcome grep gate.** ADR-805 requires every notification-attempting code path to invoke `recordOutcome` exactly once before returning. The blueprint states the invariant as an AC (AC-7106-1); the mechanism does not compel a project's source tree to only call the transport through the sink. The practical enforcement is a project-side grep gate ('every call to the notification transport is preceded by recordOutcome for that notificationId'), which the blueprint does not ship. A project that skips the gate produces the silent-notification defect the blueprint exists to prevent, and no build-cycle gate refuses the FBS. Promotion signal: a lint-rule or CI-gate contribution in a future rcf-lite blueprint or in the ci-
|
|
53
|
+
- **Notification outcome grep gate.** ADR-805 requires every notification-attempting code path to invoke `recordOutcome` exactly once before returning. The blueprint states the invariant as an AC (AC-7106-1); the mechanism does not compel a project's source tree to only call the transport through the sink. The practical enforcement is a project-side grep gate ('every call to the notification transport is preceded by recordOutcome for that notificationId'), which the blueprint does not ship. A project that skips the gate produces the silent-notification defect the blueprint exists to prevent, and no build-cycle gate refuses the FBS. Promotion signal: a lint-rule or CI-gate contribution in a future rcf-lite blueprint or in the delivery-ci-workflows blueprint's shipped ruleset. Project-side workaround: author the grep gate as a project-authored TC bound to AC-7106-1.
|
|
@@ -6,9 +6,9 @@ This file is the observability-essentials half of the cross-blueprint contract.
|
|
|
6
6
|
|
|
7
7
|
| Topic string | observability-essentials contribution | Origin | Composition note |
|
|
8
8
|
|---|---|---|---|
|
|
9
|
-
| `healthProbes` | ADR-801-observability-essentials-health-probes | Minted here; pre-cleared as unclaimed against application-spa (`clientRouting`, `theming`, `clientState`, `errorEnvelope`, `authModel`), application-api-rest (`errorEnvelope`, `authModel`, `apiVersioning`, `logging`), security-auth-magic-link (`authModel`), persistence-data-sqlite (`persistenceStore`, `migrationDiscipline`), ci-
|
|
10
|
-
| `readinessSemantics` | ADR-802-observability-essentials-readiness-semantics | Minted here; pre-cleared as unclaimed against application-spa, application-api-rest, security-auth-magic-link, persistence-data-sqlite, ci-
|
|
11
|
-
| `statusPageContract` | ADR-803-observability-essentials-status-page-contract | Minted here; pre-cleared as unclaimed against application-spa, application-api-rest, security-auth-magic-link, persistence-data-sqlite, ci-
|
|
9
|
+
| `healthProbes` | ADR-801-observability-essentials-health-probes | Minted here; pre-cleared as unclaimed against application-spa (`clientRouting`, `theming`, `clientState`, `errorEnvelope`, `authModel`), application-api-rest (`errorEnvelope`, `authModel`, `apiVersioning`, `logging`), security-auth-magic-link (`authModel`), persistence-data-sqlite (`persistenceStore`, `migrationDiscipline`), delivery-ci-workflows (`ciGates`, `strictCoverageGate`), and the hello-panel walkthrough exemplar (`operatorPanel`) | The one HTTP health probe contract for the project: two endpoints (liveness /healthz, readiness /readyz), shared JSON body shape, served on the request-traffic listener. A composing blueprint that holds a different endpoint contract (a single /health endpoint, gRPC health protocol, probes on a separate admin port) contributes its own scope:global ADR on this exact string and lets composition surface the pairing. Expected resolution: one project-level ADR that fixes the endpoint contract |
|
|
10
|
+
| `readinessSemantics` | ADR-802-observability-essentials-readiness-semantics | Minted here; pre-cleared as unclaimed against application-spa, application-api-rest, security-auth-magic-link, persistence-data-sqlite, delivery-ci-workflows, and hello-panel | The one readiness aggregation and declaration scope for the project: strict-any-fail over an explicit boot-time-declared dependency set, evaluated against per-dep cached state. A composing blueprint that holds a different opinion (quorum aggregation, write-path-only readiness, graceful-degradation model) conflicts here by design. Expected resolution: one project-level ADR fixing the semantics |
|
|
11
|
+
| `statusPageContract` | ADR-803-observability-essentials-status-page-contract | Minted here; pre-cleared as unclaimed against application-spa, application-api-rest, security-auth-magic-link, persistence-data-sqlite, delivery-ci-workflows, and hello-panel | The one public status page contract for the project: declared component list plus fixed state enum plus stable-fielded incident notices. A composing blueprint that wants a different public contract (JSON endpoint at /status.json, historical uptime cells as v1 requirement, webhook-posted notices) conflicts here by design. Expected resolution: one project-level ADR fixing the public contract |
|
|
12
12
|
|
|
13
13
|
The observability-essentials blueprint claims three global topics. Every other contribution is scope-local (ADR-804 probe secrecy and ADR-805 notification outcome model do not contribute global topics; a composing blueprint that holds an opinion on probe auth policy or notification outcome shape authors its own project-level ADR if it wants to override).
|
|
14
14
|
|
|
@@ -34,7 +34,7 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
34
34
|
| email-smtp-resend | 4101-4899 | 4xx | shipped v1.0.0 | none |
|
|
35
35
|
| hello-panel (walkthrough exemplar) | 4101-4899 | 4xx | doc-reserved; teaching exemplar in `packages/rcf-lite/docs/blueprint-authoring-walkthrough.md`, not shipped as a blueprint directory | `operatorPanel` |
|
|
36
36
|
| persistence-data-sqlite | 5101-5899 | 6xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
37
|
-
| ci-
|
|
37
|
+
| delivery-ci-workflows | 6101-6899 | 7xx | shipped v2.0.0 (renamed from ci-pipeline) | `ciGates`, `strictCoverageGate`, `releaseArtefacts` |
|
|
38
38
|
| observability-essentials | 7101-7899 | 8xx | shipped v1.0.0 | `healthProbes`, `readinessSemantics`, `statusPageContract` |
|
|
39
39
|
| security-secrets-management | 8101-8899 | 9xx | shipped v1.0.0 | `secretsSource` |
|
|
40
40
|
| security-auth-clerk | 9101-9899 | 10xx | shipped v1.0.0 | `authModel` |
|
|
@@ -46,7 +46,7 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
46
46
|
|
|
47
47
|
US 7101-7110 sit at the LOW end of the 7101-7899 band on purpose. A project-side story that mechanically derives from an observability-essentials REQs id into the number `7110` would collide against observability-essentials-US-7110 in this package; the band leaves headroom at the HIGH end (US 7181-7899) so a project's own stories anchored to observability-essentials REQs can allocate without conflict. The watchpost run4 lesson applies here too.
|
|
48
48
|
|
|
49
|
-
Row-status caveat: the ci-
|
|
49
|
+
Row-status caveat: the delivery-ci-workflows seat shipped first (PR #95 merged at 595cab9c on main). This branch was rebased onto that main; the delivery-ci-workflows row reflects the shipped state, and the observability row flips to `shipped v1.0.0` at this branch's merge (Dave coordinates that final flip in the merge commit or an immediate follow-up).
|
|
50
50
|
|
|
51
51
|
## Shared expectations for future composing blueprints
|
|
52
52
|
|
|
@@ -15,7 +15,7 @@ Note on the deliberate conflict with observability-essentials: this blueprint is
|
|
|
15
15
|
|
|
16
16
|
Note on the delineation from observability-essentials's `statusPageContract` topic: this blueprint does not contribute an opinion on the public status page. A project that wants the probe surface from this blueprint AND the public status page and notification outcome sink from observability-essentials resolves the two `healthProbes` and `readinessSemantics` conflicts with project-level ADRs and keeps `statusPageContract` on the essentials-supplied side; the blueprints then compose without further conflict.
|
|
17
17
|
|
|
18
|
-
Rules for new topics (inherited from the application-spa, application-api-rest, security-auth-magic-link, persistence-data-sqlite, ci-
|
|
18
|
+
Rules for new topics (inherited from the application-spa, application-api-rest, security-auth-magic-link, persistence-data-sqlite, delivery-ci-workflows, observability-essentials, security-secrets-management, security-auth-clerk, email-smtp-resend, and deploy-cloudflare-workers vocabularies, restated as law): lower camel case, one concept per topic, no version suffixes. A topic names the decision area, not the chosen answer. Do not mint variants of existing strings (`probeInterface`, `probeSurface`, `probes`, `livenessReadiness`, `probeTransport` are all wrong when `healthProbes` already exists; `readinessAggregation`, `readyPolicy`, `readinessRule` are all wrong when `readinessSemantics` already exists).
|
|
19
19
|
|
|
20
20
|
## Id number bands (registry bootstrap)
|
|
21
21
|
|
|
@@ -31,7 +31,7 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
31
31
|
| email-smtp-resend | 4101-4899 | 4xx | shipped v1.0.0 | none |
|
|
32
32
|
| hello-panel (walkthrough exemplar) | 4101-4899 | 4xx | doc-reserved; teaching exemplar in `packages/rcf-lite/docs/blueprint-authoring-walkthrough.md`, not shipped as a blueprint directory | `operatorPanel` |
|
|
33
33
|
| persistence-data-sqlite | 5101-5899 | 6xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
34
|
-
| ci-
|
|
34
|
+
| delivery-ci-workflows | 6101-6899 | 7xx | shipped v2.0.0 (renamed from ci-pipeline) | `ciGates`, `strictCoverageGate`, `releaseArtefacts` |
|
|
35
35
|
| observability-essentials | 7101-7899 | 8xx | shipped v1.0.0 | `healthProbes`, `readinessSemantics`, `statusPageContract` |
|
|
36
36
|
| security-secrets-management | 8101-8899 | 9xx | shipped v1.0.0 | `secretsSource` |
|
|
37
37
|
| security-auth-clerk | 9101-9899 | 10xx | shipped v1.0.0 | `authModel` |
|
|
@@ -21,7 +21,7 @@ Phase 1 resolves local path sources only; registry and git-ref resolution is a m
|
|
|
21
21
|
| Facade module shape sample | `assets/facade-shape/facade-module-shape.md` | The exact shape of the D1 store facade module (`createStore({ env, bindingName, onEvent })` factory, named verbs, prepared-statement discipline, batch atomicity) with a worked domain example |
|
|
22
22
|
| Batch usage sample | `assets/batch-usage/batch-atomicity-example.md` | The exact shape of a multi-statement atomic write through `db.batch([...])`, with the mid-batch-failure rollback semantics called out |
|
|
23
23
|
| Guide | `guide/persistence-data-d1.md` | Operator-facing: when to use it, when not, what stays your call, and the promotion signals for the sibling single-file SQLite blueprint and future Postgres or replication blueprints |
|
|
24
|
-
| Coordination vocabulary | `docs/topics.md` | The two global-topic strings this blueprint contributes and the shared id band registry (application-spa, application-api-rest, security-auth-magic-link, email-smtp-resend, persistence-data-sqlite, ci-
|
|
24
|
+
| Coordination vocabulary | `docs/topics.md` | The two global-topic strings this blueprint contributes and the shared id band registry (application-spa, application-api-rest, security-auth-magic-link, email-smtp-resend, persistence-data-sqlite, delivery-ci-workflows, observability-essentials, security-secrets-management, security-auth-clerk, persistence-data-d1) |
|
|
25
25
|
|
|
26
26
|
The doc set is contributions (copied into the project tree by `rcf define blueprint add`); the guide, assets, and docs are package-resident references. Guide rendering into `rcf/knowledge/docs/blueprint-guides/` and asset ingestion are mechanism follow-ups; until they land, the working agent reads them from the applied blueprint's source path recorded in `manifest.blueprints[].source`.
|
|
27
27
|
|
|
@@ -31,7 +31,7 @@ Contributed kinds: REQ, US (with inline ACs), TAC, ADR. Adherence is expressed a
|
|
|
31
31
|
|
|
32
32
|
No FBS contributions, as a matter of principle (ratified policy 2026-08-19): FBSs are the work of the implementing agent, not the blueprint; project constraints have to be applied at the time of creation. The blueprint contributes the WHAT (the store facade contract, the wrangler-owned migration discipline, the deploy-pipeline gate contract, the prepared-statement and batch atomicity boundaries, the two-path recovery model, the metadata-only event log discipline); the implementing agent derives the HOW-tasks (FBS) in the host project, where the ACs contributed here get picked up by the project's own build sequencing.
|
|
33
33
|
|
|
34
|
-
Deliberately not contributed: the domain schema (the tables the project's own entities live in are project-authored, on top of the migration directory the project maintains; the blueprint governs the facade contract and the migration discipline, not the domain shape); an ORM or query builder above the D1 prepared-statement surface (the facade is a project-authored surface; how it internally implements its verbs above the vendor binding is not blueprint-owned, but the boundary discipline refuses raw-SQL passthrough on the public surface); a Worker deploy blueprint (a companion `deploy-cloudflare-workers` blueprint is the natural pair for the Worker deploy step the deploy-gate TAC assumes; until it ships, projects wire their own deploy step); a secrets management surface for the CI credential that runs `wrangler d1 migrations apply` and `wrangler deploy` (the credential storage and rotation posture are the concern of the `security-secrets-management` blueprint, not this one); a CI pipeline template (CI shape is the concern of the `ci-
|
|
34
|
+
Deliberately not contributed: the domain schema (the tables the project's own entities live in are project-authored, on top of the migration directory the project maintains; the blueprint governs the facade contract and the migration discipline, not the domain shape); an ORM or query builder above the D1 prepared-statement surface (the facade is a project-authored surface; how it internally implements its verbs above the vendor binding is not blueprint-owned, but the boundary discipline refuses raw-SQL passthrough on the public surface); a Worker deploy blueprint (a companion `deploy-cloudflare-workers` blueprint is the natural pair for the Worker deploy step the deploy-gate TAC assumes; until it ships, projects wire their own deploy step); a secrets management surface for the CI credential that runs `wrangler d1 migrations apply` and `wrangler deploy` (the credential storage and rotation posture are the concern of the `security-secrets-management` blueprint, not this one); a CI pipeline template (CI shape is the concern of the `delivery-ci-workflows` blueprint, not this one; this blueprint contributes the requirement that the migrations-apply step orders strictly before the deploy step, not the file that implements it); a replication or failover surface (D1's read-replica surface is a read-scaling primitive, not a failover primitive; a project needing failover supersedes ADR-1401 with an alternate engine); a logical schema-agnostic dump beyond the vendor's `wrangler d1 export` (out of scope; see ADR-1405's alternatives).
|
|
35
35
|
|
|
36
36
|
## The two global decisions
|
|
37
37
|
|
|
@@ -112,4 +112,4 @@ Not a runnable facade. The snippets above are the SHAPE; a project's own domain
|
|
|
112
112
|
|
|
113
113
|
Not an ORM. Projects that reach for Drizzle or Kysely wire it inside the verb bodies and preserve the outward verb surface. The prepared-statement discipline (static SQL literals inside the verb, values only through `bind`) survives an ORM: most ORMs emit static SQL from typed queries at build time, and the AC-13104-1 grep still lands on static literals.
|
|
114
114
|
|
|
115
|
-
Not the ci-
|
|
115
|
+
Not the delivery-ci-workflows gate. TAC-1403's deploy-gate is a CI file, not a facade responsibility; the two surfaces meet only through the shared event-record shape.
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"description": "The directory the apply step reads; the file set at HEAD determines what the apply attempts."
|
|
33
33
|
}
|
|
34
34
|
],
|
|
35
|
-
"tradeoffs": "Ordering the apply strictly before the deploy costs a small amount of CI latency per environment (one wrangler invocation, one round trip to the D1 API) in exchange for making the schema-behind-the-code state impossible in production. Projects that need faster CI can parallelise across environments, but never inside one environment's apply-then-deploy sequence. The gate is a project-authored CI file; the blueprint does not ship a CI template, because CI shape is a `ci-
|
|
35
|
+
"tradeoffs": "Ordering the apply strictly before the deploy costs a small amount of CI latency per environment (one wrangler invocation, one round trip to the D1 API) in exchange for making the schema-behind-the-code state impossible in production. Projects that need faster CI can parallelise across environments, but never inside one environment's apply-then-deploy sequence. The gate is a project-authored CI file; the blueprint does not ship a CI template, because CI shape is a `delivery-ci-workflows`-adjacent concern the project owns.",
|
|
36
36
|
"createdAt": "2026-08-30T00:00:00Z",
|
|
37
37
|
"updatedAt": "2026-08-30T00:00:00Z"
|
|
38
38
|
}
|
|
@@ -13,7 +13,7 @@ The persistence-data-d1 blueprint claims two global topics. Every other contribu
|
|
|
13
13
|
|
|
14
14
|
Note on the delineation from the application-api-rest blueprint's `logging` topic: `logging` (owned by application-api-rest ADR-304) governs the wire-log shape of the HTTP tier. This blueprint's ADR-1403 governs the STORE-EVENT log shape (facadeReady, migrationsApplied, backupExported, timeTravelRestored, queryFailed). The two log surfaces may share a shipper but do not share a topic. A blueprint that contributes a unified log discipline across all tiers would author its own scope:global ADR on `logging` and expect to conflict with the REST blueprint there, not here.
|
|
15
15
|
|
|
16
|
-
Rules for new topics (inherited from the application-spa, application-api-rest, security-auth-magic-link, persistence-data-sqlite, ci-
|
|
16
|
+
Rules for new topics (inherited from the application-spa, application-api-rest, security-auth-magic-link, persistence-data-sqlite, delivery-ci-workflows, observability-essentials, security-secrets-management, security-auth-clerk, and email-smtp-resend vocabularies, restated as law): lower camel case, one concept per topic, no version suffixes. A topic names the decision area, not the chosen answer. Do not mint variants of existing strings (`store`, `dataStore`, `db`, `dbEngine`, `d1Store`, `edgeStore` are all wrong when `persistenceStore` already exists; `schemaMigrations`, `dbMigrations`, `wranglerMigrations`, `migrations` are all wrong when `migrationDiscipline` already exists).
|
|
17
17
|
|
|
18
18
|
## Id number bands (registry bootstrap)
|
|
19
19
|
|
|
@@ -29,7 +29,7 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
29
29
|
| email-smtp-resend | 4101-4899 | 4xx | shipped v1.0.0 | none |
|
|
30
30
|
| hello-panel (walkthrough exemplar) | 4101-4899 | 4xx | doc-reserved; teaching exemplar in `packages/rcf-lite/docs/blueprint-authoring-walkthrough.md`, not shipped as a blueprint directory | `operatorPanel` |
|
|
31
31
|
| persistence-data-sqlite | 5101-5899 | 6xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
32
|
-
| ci-
|
|
32
|
+
| delivery-ci-workflows | 6101-6899 | 7xx | shipped v2.0.0 (renamed from ci-pipeline) | `ciGates`, `strictCoverageGate`, `releaseArtefacts` |
|
|
33
33
|
| observability-essentials | 7101-7899 | 8xx | shipped v1.0.0 | `healthProbes`, `readinessSemantics`, `statusPageContract` |
|
|
34
34
|
| security-secrets-management | 8101-8899 | 9xx | shipped v1.0.0 | `secretsSource` |
|
|
35
35
|
| security-auth-clerk | 9101-9899 | 10xx | shipped v1.0.0 | `authModel` |
|
|
@@ -16,7 +16,7 @@ Not a Worker deploy blueprint. The deploy-gate TAC assumes a `wrangler deploy` s
|
|
|
16
16
|
|
|
17
17
|
Not a secrets-management blueprint. The CI credential that runs `wrangler d1 migrations apply` and `wrangler deploy` (the `CLOUDFLARE_API_TOKEN`, or the OAuth-scoped credential the vendor issues) is a secret whose storage and rotation posture the `security-secrets-management` blueprint governs. This blueprint references the credential by role, not by name or storage path.
|
|
18
18
|
|
|
19
|
-
Not a CI-pipeline blueprint. The deploy-gate TAC contributes the requirement that `wrangler d1 migrations apply` orders strictly before `wrangler deploy` and the deploy job refuses on apply failure; the CI file that implements that ordering is the concern of the `ci-
|
|
19
|
+
Not a CI-pipeline blueprint. The deploy-gate TAC contributes the requirement that `wrangler d1 migrations apply` orders strictly before `wrangler deploy` and the deploy job refuses on apply failure; the CI file that implements that ordering is the concern of the `delivery-ci-workflows` blueprint or the project's own CI-authoring practice.
|
|
20
20
|
|
|
21
21
|
Not a logical-dump story beyond the vendor's export. Backups are `wrangler d1 export` artifacts; cross-engine migration and portable dumps beyond D1's own `.sql` export are out of scope. Projects that need a schema-agnostic dump layer a project-authored dump runner beside the file-level runner.
|
|
22
22
|
|
|
@@ -21,7 +21,7 @@ Phase 1 resolves local path sources only; registry and git-ref resolution is a m
|
|
|
21
21
|
| SQLite backup procedure | `assets/backup-procedures/sqlite-file-copy.md` | The downtime-free file-copy procedure under WAL, with the online-backup and checkpoint-then-copy paths |
|
|
22
22
|
| WAL checkpoint note | `assets/backup-procedures/hot-checkpoint-note.md` | Why a checkpoint before a bare cp works, and when the online-backup API is the safer path |
|
|
23
23
|
| Guide | `guide/persistence-data-sqlite.md` | Operator-facing: when to use it, when not, what stays your call, and the promotion signal for the future Postgres variant |
|
|
24
|
-
| Coordination vocabulary | `docs/topics.md` | The two global-topic strings this blueprint contributes, the shared id band registry (application-spa, application-api-rest, security-auth-magic-link, hello-panel, persistence-data-sqlite, ci-
|
|
24
|
+
| Coordination vocabulary | `docs/topics.md` | The two global-topic strings this blueprint contributes, the shared id band registry (application-spa, application-api-rest, security-auth-magic-link, hello-panel, persistence-data-sqlite, delivery-ci-workflows, observability-essentials) |
|
|
25
25
|
|
|
26
26
|
The doc set is contributions (copied into the project tree by `rcf define blueprint add`); the guide, assets, and docs are package-resident references. Guide rendering into `rcf/knowledge/docs/blueprint-guides/` and asset ingestion are mechanism follow-ups; until they land, the working agent reads them from the applied blueprint's source path recorded in `manifest.blueprints[].source`.
|
|
27
27
|
|
|
@@ -29,7 +29,7 @@ This table is maintained shelf-wide across every blueprint's `docs/topics.md`. R
|
|
|
29
29
|
| email-smtp-resend | 4101-4899 | 4xx | shipped v1.0.0 | none |
|
|
30
30
|
| hello-panel (walkthrough exemplar) | 4101-4899 | 4xx | doc-reserved; teaching exemplar in `packages/rcf-lite/docs/blueprint-authoring-walkthrough.md`, not shipped as a blueprint directory | `operatorPanel` |
|
|
31
31
|
| persistence-data-sqlite | 5101-5899 | 6xx | shipped v1.0.0 | `persistenceStore`, `migrationDiscipline` |
|
|
32
|
-
| ci-
|
|
32
|
+
| delivery-ci-workflows | 6101-6899 | 7xx | shipped v2.0.0 (renamed from ci-pipeline) | `ciGates`, `strictCoverageGate`, `releaseArtefacts` |
|
|
33
33
|
| observability-essentials | 7101-7899 | 8xx | shipped v1.0.0 | `healthProbes`, `readinessSemantics`, `statusPageContract` |
|
|
34
34
|
| security-secrets-management | 8101-8899 | 9xx | shipped v1.0.0 | `secretsSource` |
|
|
35
35
|
| security-auth-clerk | 9101-9899 | 10xx | shipped v1.0.0 | `authModel` |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Security auth Clerk blueprint (v1.
|
|
1
|
+
# Security auth Clerk blueprint (v1.1.0)
|
|
2
2
|
|
|
3
3
|
The tenth content blueprint on the rcf-build-lite blueprint mechanism, category `security`. A Clerk-committed sibling to `security-auth-magic-link` on the `authModel` global topic: Clerk hosts the identity surface (users, sessions, sign-in UX, credential storage, MFA, account recovery); the project owns a framework-agnostic middleware boundary, a session verifier confined to one module, a Clerk-claim-to-project-verb authorisation adapter, and a reduced principal shape the rest of the codebase reasons against. Targeted at small greenfield rcf-lite projects that want hosted identity without building the user-and-session surface themselves; larger deployments supersede the vendor by superseding ADR-1001 with a project-level ADR and swapping the middleware and verifier adapters.
|
|
4
4
|
|
|
@@ -20,10 +20,12 @@ Composing with `security-auth-magic-link` (or any other blueprint contributing `
|
|
|
20
20
|
| Doc set | `contributions/` | 9 REQs, 11 USs (25 ACs), 4 TACs, 5 ADRs, all schema-valid and namespaced (`security-auth-clerk-REQ-001` prefix family; `ADR-1001-security-auth-clerk-auth-model` suffix family) |
|
|
21
21
|
| React-family sample | `assets/wiring/clerk-provider-react.md` | The shape of a `ClerkProvider` wiring in a React-family client tier: where the provider mounts, what the SPA blueprint's session-and-redirect posture composes with |
|
|
22
22
|
| Vue-family sample | `assets/wiring/clerk-provider-vue.md` | The same shape rendered for a Vue-family client tier so the operator can pattern-match without a framework translation step |
|
|
23
|
-
| Middleware sample | `assets/middleware/node-middleware-shape.md` | The framework-agnostic middleware boundary contract (`verify(request)`), with adapter samples for Express and Fastify |
|
|
23
|
+
| Middleware sample (Node) | `assets/middleware/node-middleware-shape.md` | The framework-agnostic middleware boundary contract (`verify(request)`), with adapter samples for Express and Fastify |
|
|
24
|
+
| Middleware sample (Workers) | `assets/middleware/workers-fetch-shape.md` | The Cloudflare Workers fetch-handler adapter around the same `verify(request)` contract; a project on Workers picks up this file, a project on Node picks up the Node sample |
|
|
25
|
+
| Workers wrangler integration | `assets/wiring/workers-wrangler-toml-shape.md` | The wrangler.toml overlay a Workers deployer applies: `nodejs_compat`, `CLERK_SIGN_IN_URL`, the Clerk secret names, and the `run_worker_first` posture the auth-gate bypass finding required |
|
|
24
26
|
| Role-claim mapping | `assets/authorisation/role-claim-mapping-sample.md` | A worked verb-to-role mapping-table example, with the reduction from Clerk's `org:role` claim shape into the project's `roles` array |
|
|
25
27
|
| Guide | `guide/security-auth-clerk.md` | Operator-facing: when to use it, when not, the promotion signals for the OAuth2 and Keycloak siblings, the operator decisions that remain open, the cost-honesty paragraph |
|
|
26
|
-
| Coordination vocabulary | `docs/topics.md` | The one global-topic string this blueprint contributes and the shared id band registry (application-spa, application-api-rest, security-auth-magic-link, email-smtp-resend, persistence-data-sqlite, ci-
|
|
28
|
+
| Coordination vocabulary | `docs/topics.md` | The one global-topic string this blueprint contributes and the shared id band registry (application-spa, application-api-rest, security-auth-magic-link, email-smtp-resend, persistence-data-sqlite, delivery-ci-workflows, observability-essentials, security-secrets-management, security-auth-clerk) |
|
|
27
29
|
|
|
28
30
|
The doc set is contributions (copied into the project tree by `rcf define blueprint add`); the guide, assets, and docs are package-resident references. Guide rendering into `rcf/knowledge/docs/blueprint-guides/` and asset ingestion are mechanism follow-ups; until they land, the working agent reads them from the applied blueprint's source path recorded in `manifest.blueprints[].source`.
|
|
29
31
|
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Workers fetch-handler shape
|
|
2
|
+
|
|
3
|
+
The Cloudflare Workers adapter for the middleware boundary (TAC-1001). Chooser: a project on Node picks up `node-middleware-shape.md` (Express or Fastify adapter around `verify(request)`); a project on Workers picks up this file. Both adapters wrap the same `verify` contract; the reduced `Principal` shape the project reasons against downstream is identical.
|
|
4
|
+
|
|
5
|
+
## Why Workers gets its own sample
|
|
6
|
+
|
|
7
|
+
The framework-agnostic `verify(request)` contract (TAC-1001 responsibility 1) already carries onto Workers. What differs is mechanical:
|
|
8
|
+
|
|
9
|
+
- **Request object.** The handler receives a Fetch API `Request`; header reads go through `request.headers.get('cookie')`, not `request.headers.cookie`. There is no `req.accepts()`; content-negotiation is a header inspection on `accept`.
|
|
10
|
+
- **Response shape.** Handlers return a Fetch API `Response` synchronously; there is no `res.status().json()` chain. The refusal paths return `new Response(...)` bodies.
|
|
11
|
+
- **Secret transport.** The Clerk secret arrives on `env.CLERK_SECRET_KEY` (and `env.CLERK_PUBLISHABLE_KEY`) at request time, not from `process.env` at module load. The `createVerify` factory therefore runs inside the fetch handler, not at module top-level; a per-request construction is cheap because Clerk's `authenticateRequest` is the network cost, not the client construction.
|
|
12
|
+
- **Session-cookie parse.** Clerk's `@clerk/backend` `authenticateRequest(request, ...)` reads the `__session` cookie off the Fetch `Request` itself; no manual cookie header parsing is needed at the middleware seam. The pre-check below (a substring test for `__session=`) is a fast-path refusal so unauthenticated hits do not incur an SDK round trip.
|
|
13
|
+
|
|
14
|
+
## The Workers fetch adapter
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
// src/auth/adapters/workers.mjs
|
|
18
|
+
import { createSessionVerifier } from '../session-verifier.mjs';
|
|
19
|
+
import { reduceClaims } from '../claims-mapper.mjs';
|
|
20
|
+
|
|
21
|
+
function jsonResponse(body, status) {
|
|
22
|
+
return new Response(JSON.stringify(body), {
|
|
23
|
+
status,
|
|
24
|
+
headers: { 'content-type': 'application/json' }
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function createVerify({ clerkSecretKey, clerkPublishableKey, auditSink, clock }) {
|
|
29
|
+
const verifier = createSessionVerifier({
|
|
30
|
+
clerkSecretKey,
|
|
31
|
+
clerkPublishableKey,
|
|
32
|
+
auditSink,
|
|
33
|
+
clock
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return async function verify(request) {
|
|
37
|
+
// Fast-path refusal without an SDK round trip.
|
|
38
|
+
const cookieHeader = request.headers.get('cookie') ?? '';
|
|
39
|
+
if (!/(?:^|;\s*)__session=/.test(cookieHeader)) {
|
|
40
|
+
return { authenticated: false, reason: 'no-session-cookie' };
|
|
41
|
+
}
|
|
42
|
+
// Clerk's authenticateRequest reads the __session cookie off the Request
|
|
43
|
+
// and does the vendor-verified session verification server-side.
|
|
44
|
+
const result = await verifier.verifySessionCookie(null, request);
|
|
45
|
+
if (!result.valid) {
|
|
46
|
+
return { authenticated: false, reason: result.reason ?? 'invalid-session' };
|
|
47
|
+
}
|
|
48
|
+
let principal;
|
|
49
|
+
try {
|
|
50
|
+
principal = reduceClaims(result.claims);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
return { authenticated: false, reason: 'claims-map-failed' };
|
|
53
|
+
}
|
|
54
|
+
return { authenticated: true, principal };
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// One-file adapter that a route dispatcher inside `fetch()` can call.
|
|
59
|
+
// The blueprint does not mandate any particular router; the shape below is
|
|
60
|
+
// the seam a project's router calls into. Route dispatch stays a project
|
|
61
|
+
// concern.
|
|
62
|
+
export function workersRequireAuth({ verify }) {
|
|
63
|
+
return async function requireAuth(request) {
|
|
64
|
+
const result = await verify(request);
|
|
65
|
+
if (result.authenticated) {
|
|
66
|
+
return { ok: true, principal: result.principal };
|
|
67
|
+
}
|
|
68
|
+
const wantsHtml = (request.headers.get('accept') ?? '').includes('text/html');
|
|
69
|
+
const refusal = wantsHtml
|
|
70
|
+
? Response.redirect(new URL('/sign-in', request.url).toString(), 302)
|
|
71
|
+
: jsonResponse({ error: 'unauthenticated', reason: result.reason }, 401);
|
|
72
|
+
return { ok: false, refusal };
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Wiring it inside a fetch handler
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
// src/worker.mjs
|
|
81
|
+
import { createVerify, workersRequireAuth } from './auth/adapters/workers.mjs';
|
|
82
|
+
|
|
83
|
+
export default {
|
|
84
|
+
async fetch(request, env, ctx) {
|
|
85
|
+
const url = new URL(request.url);
|
|
86
|
+
|
|
87
|
+
// Public probes and the sign-in redirect run before the auth gate.
|
|
88
|
+
if (url.pathname === '/health' && request.method === 'GET') {
|
|
89
|
+
return new Response(null, { status: 200 });
|
|
90
|
+
}
|
|
91
|
+
if (url.pathname === '/sign-in') {
|
|
92
|
+
return Response.redirect(env.CLERK_SIGN_IN_URL, 302);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Auth-gated routes go through the middleware boundary.
|
|
96
|
+
const verify = createVerify({
|
|
97
|
+
clerkSecretKey: env.CLERK_SECRET_KEY,
|
|
98
|
+
clerkPublishableKey: env.CLERK_PUBLISHABLE_KEY,
|
|
99
|
+
auditSink: (evt) => console.log(JSON.stringify({ ...evt, kind: 'audit' })),
|
|
100
|
+
clock: () => new Date()
|
|
101
|
+
});
|
|
102
|
+
const requireAuth = workersRequireAuth({ verify });
|
|
103
|
+
|
|
104
|
+
if (url.pathname.startsWith('/api/')) {
|
|
105
|
+
const authResult = await requireAuth(request);
|
|
106
|
+
if (!authResult.ok) return authResult.refusal;
|
|
107
|
+
// request.auth is a Fetch API surface convention the project owns:
|
|
108
|
+
// attach the principal onto a locals object the route handler reads.
|
|
109
|
+
return dispatchApi(url, request, env, ctx, authResult.principal);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return env.ASSETS.fetch(request);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Notes
|
|
118
|
+
|
|
119
|
+
- **Attachment convention.** The Fetch `Request` is immutable, so the middleware cannot literally set `request.auth`. The Workers adapter's shape returns `{ ok, principal, refusal }` and the route handler carries the `principal` forward as a function argument. AC-9101-2's runtime observation binds against the reduced `Principal` shape on the successful path either way; the mechanism of attachment is framework-shaped, not contract-shaped.
|
|
120
|
+
- **SDK boundary discipline.** Only `session-verifier.mjs` and this adapter's `createVerify` factory import from `@clerk/backend`. AC-9102-2's source-tree scan still holds on Workers; the two-module allowance covers the Workers case unchanged.
|
|
121
|
+
- **Refusal shape.** The 401 JSON body for API-shaped routes and the 302 redirect for HTML-shaped routes match the Node adapters. AC-9103-2 and AC-9103-3 read the same shapes.
|
|
122
|
+
- **Static-asset ordering.** The wrangler `[assets]` binding serves files before the fetch handler runs by default, which silently bypasses this middleware on any URL that also resolves to a static file. The `run_worker_first` posture on any auth-gated route is a wrangler-configuration concern; the workers wrangler.toml sample carries the fix.
|
|
123
|
+
- **Runtime.** The verifier calls into `@clerk/backend` which needs Node built-ins; wrangler's `compatibility_flags = ["nodejs_compat"]` is the enabling flag on the wrangler side. This is a runtime-config concern, not a middleware-shape concern.
|