Completed 2026-04-25.
- Three packages bumped to
v1.0.0withpublishConfig.provenance = true+access = "public". Atomic three-package release scaffolded via.github/workflows/release.yml. HulumiHardeningPackH3 flipped advisory → mandatory. One-line edit inpackages/policies/src/aws/hulumi-hardening-pack.ts(H3_ENFORCEMENT_LEVEL); test renamedh3_prior_advisory_behavior_removed; pack metadata's enforcement field updated.CHANGELOG.mddocuments the breaking change with three migration paths (tag, suppress, override).docs/deployment/scp.jsonready-to-apply AWS Organizations SCP that protects thehulumi:iac-roletag from non-IaC principals. Template uses__REPLACE_ME__placeholders for account IDs.docs/deployment/scp-guide.mdwalks customize / validate / apply (console + Pulumi paths) / test / revert / SCP-×-H3 interaction matrix.SECURITY.mdrewritten from M1 stub to v1.0.0 full: disclosure channel, SLSA verify steps, Pulumi cooling-off (72h/24h), transitive-supply-chain disclosure table, SCP deployment guidance, supported-versions table..github/workflows/release.yml: tag-triggeredv*.*.*; SLSA Build L3 attestation viaactions/attest-build-provenance@v2; CycloneDX SBOMs per package; npm publish with--provenance(OIDC trusted publishing, noNPM_TOKEN); GitHub Release with tarballs + SBOMs; atomic across three packages..github/workflows/pulumi-cooling-off.yml+scripts/cooling-off-diff.mjs: PR-triggered, diffspnpm-lock.yamlfor@pulumi/*version bumps, hitshttps://registry.npmjs.org/<pkg>for the upstream publish timestamp, fails if < 72h (minor/major) / < 24h (patch). Self-applies to first post-release Pulumi bump..github/workflows/ci.ymlextended: newpulumi-cooling-offjob (every PR) +attestation-dry-runjob (builds + attests onmainpush without publishing)..github/dependabot.ymlrewritten:@pulumi/*grouped together (pulumi-runtimegroup) so cooling-off stays atomic across the three Hulumi packages; the existing toolchain-major-bump ignore list preserved..github/attestations/README.mddocumentsgh attestation verifyandcosignpaths + troubleshooting.docs/launch/{README,csa-outreach,pulumi-discussion,cfp-fwd-cloudsec,cfp-bsides,pulumi-blog-pitch,atlas-contribution-plan}.md— five ready-to-send drafts + index + ATLAS stub. Each has owner, send-by date, and audience-specific copy.- Root
package.jsonscripts:release:dry,release:verify-attestations.
- Atomic three-package release in one workflow, not three separate releases. A v1.0.0 release where one of three packages partially publishes is unrecoverable inside the npm 72h-unpublish window — and across multiple maintainers with different schedules, that window is hard to coordinate. The workflow runs preflight (build + test + lint + format) → tarball + SBOM + attestation → publish all three in sequence. Any failure aborts before the next
npm publish. If@hulumi/baselinesucceeds and@hulumi/policiesfails, the maintainer manually runsnpm unpublish @hulumi/baseline@1.0.0within 72h. CHANGELOG docs the recovery procedure. - SLSA via
slsa-framework/slsa-github-generatoris mentioned in the M5 contract but the actual workflow usesactions/attest-build-provenance@v2directly. The latter is GitHub's first-party path and writes the attestation to GitHub's transparency log + Sigstore. The reusable workflow approach (slsa-github-generator) is an alternative — pin to a specific SHA, opaque to maintainers. We chose first-party for clarity. Functionally equivalent for SLSA Build L3. - Pulumi cooling-off via
scripts/cooling-off-diff.mjsrather than a third-party action. The check is small (~80 LOC) and we control the failure mode (fail-closed on registry error). A third-party action introduces a supply-chain hop we don't need. - No real
npm publishin this PR. v1.0.0 isn't actually tagged in this commit; the release workflow is wired up but it triggers only onv*.*.*tag push. The user pushes the tag when ready (or runsgh release create v1.0.0if they prefer the GitHub UI). The M5 contract's "first green release" closeout happens AFTER this PR merges and AFTER the user tags. - Launch artifacts are drafts, not autoposts. Each file says "ready-to-send" but the maintainer copies + pastes — no automation reaches out to CSA / Pulumi / CFPs / blogs on the maintainer's behalf. Audit safety + reversibility.
- SCP placeholder is
__REPLACE_ME__rather than a sed-friendly regex pattern (AWS_ACCOUNT_ID). The placeholder is unmistakable, won't passaws organizations validate-policy, and the guide's sed snippet handles the substitution. A user who runsaws organizations create-policy --content file://docs/deployment/scp.jsonwithout filling the placeholders gets aMalformedPolicyDocumenterror — which is the correct fail state. - No upstream Pulumi PR or MITRE ATLAS submission in M5. Both are post-release activities tracked in
docs/launch/README.mdsend-by table. The runbook explicitly scoped them out.
- Initial
H3_ENFORCEMENT_LEVELflip broke thePackMetadataenforcement assertion test — the test still expected H3 =advisory. Fix: also flip the test's expectation (expect(h3.enforcement).toBe("mandatory")). The lessons file documented this exact case from M3, so the foreseeing was correct. - Forgot to add
register-urlto release workflow'ssetup-nodestep initially — npm's trusted-publishing OIDC handshake needsregistry-url: https://registry.npmjs.org. Caught at the dry-run review; documented for any maintainer eyeballing the workflow. - Cooling-off script's classifyBump didn't cap version-component parsing —
parseInt("3a", 10)returns 3, so a malformed version string would silently classify. Hardened the parsing with|| 0guards, but a properly-formedpnpm-lock.yamlshouldn't ever hit the malformed path. Documented as a v1.1+ refinement.
- Tests-as-pinned-expectations require dual updates when behavioural fields change. The H3 flip is by design a behavioural change — the test that asserts the old behaviour MUST flip. M5 lessons (this file) documents the pattern: every breaking-change PR should grep tests for the old expectation string.
- Workflow YAML's silent failure modes are easy to miss without a dry-run. The
attestation-dry-runCI job exists for exactly this reason; it builds tarballs + attests on everymainpush so we discover release-workflow regressions on regular development PRs, not at tag time.
- GitHub Actions secret-context warnings:
secrets.PULUMI_ACCESS_TOKENflagged as "context access might be invalid" because the secret isn't set yet. The workflow handles unset gracefully (CONTRACT-ONLY mode) but the IDE warning is loud. Documented indocs/integration-testing.md. Same warnings will appear forsecrets.NPM_TOKENif any maintainer ever adds one — and we explicitly DON'T (OIDC only). - The sheer volume of M5 deliverables — five workflow files, six docs files, six launch artifacts, a SCP, CHANGELOG, SECURITY.md rewrite. Most are doc-only; the load-bearing code is the H3 flip + the cooling-off script. Sequencing the writes so each is committable in isolation took most of the time.
- Verifying
actions/attest-build-provenance@v2semantics without an actual release — the docs are clear but the only way to actually validate the chain is to run a full release. Theattestation-dry-runjob captures the "build tarballs + attest" path; the actualnpm publish + GitHub releaseis exercised when the user tags.
v1.0.0tag pattern: signed git tag (git tag -s v1.0.0 -m "v1.0.0"); release workflow triggers onv*.*.*.- CHANGELOG.md sections: Keep-a-Changelog (Added / Changed / Deprecated / Removed / Fixed / Security). Breaking changes are flagged inline with
**(BREAKING)**. - Launch artifact filenames: lowercase, hyphenated,
<audience>-<purpose>.md(e.g.csa-outreach.md,pulumi-blog-pitch.md). TheREADME.mdindex links each. - SCP placeholder:
__REPLACE_ME__:role/<role-name>— visually unmistakable. pulumi-cooling-off(workflow + script + CI job) rather thancooldown/quarantine/wait-period. Matches the SECURITY.md prose.
- The H3-flip test renamed to
h3_prior_advisory_behavior_removedrather than deleted. The test name itself is a comment for any future reader investigating "wait, why isn't H3 advisory anymore?" — they find a green test that explicitly says "the advisory behaviour was removed in v1.0.0." - Tests still asserting the M2/M3/M4 behaviour (e.g.
cisV5PackMetadata.rulesID list) ran unchanged through H3 flip — the breaking change was deliberately scoped to one field. That's discipline reward. - Pipeline-green-after-H3-flip without any other code change — confirmed before writing the launch docs. Fast feedback that the breaking change is contained.
- Release-workflow integration test — there's no hermetic way to test the actual
npm publishpath without publishing. M5 shipsattestation-dry-run(CI builds tarballs + attests but doesn't publish); the only end-to-end test is the user taggingv1.0.0and observing CI. If the workflow has a typo, we discover it at tag time. Mitigation: tag av1.0.0-rc1first and observe. - Cooling-off self-test — the script handles fresh-bump-fail and stale-bump-pass via
cooling-off-diff.mjs. There's no automated test exercising the script with synthetic lockfile diffs. Thetests/skill-bdd/-style fixture pattern would work; deferred to v1.0.x. - SCP teardown integration test —
scp-guide.mddocuments the revert path but doesn't have a CI test. Real teardown is a manual maintainer action; automating it would require AWS Organizations write access in CI which is over-scope.
- This is the last milestone in the v1 runbook. No "next milestone" applies. v1.1+ scope tracked in GitHub issues opened post-release:
hulumi-driftskill,hulumi-checkskill, standalone CLI, Azure / GCP adapters, CIS v7.0 full pack, MITRE ATLAS submission, Pulumi-upstream provenance PR, BucketV2 → Bucket migration in interfaces.md. - Post-release maintainer actions (in
docs/launch/README.mdsend-by table):- Day-of: send
csa-outreach.mdemail. - +3 days: post
pulumi-discussion.mdGH Discussion. - +7 days: pitch
pulumi-blog-pitch.mdto Pulumi DevRel. - Per CFP deadlines: submit
cfp-fwd-cloudsec.mdandcfp-bsides.md. - Open GitHub issues for v1.1+ items.
- Day-of: send
- The
--provenanceflag onpnpm publishrequires npm trusted publishing to be configured atnpmjs.com/settings/<scope>/access. The runbook template doesn't currently call out this pre-flight as a maintainer action — discoverable only via the npm docs. Future runbooks shipping an npm release should include "Configure npm trusted publishing" as a Pre-Flight checklist item. - Cooling-off threshold as 72h/24h is research-backed (per
synthesis §11), but the runbook should document the source-of-thresholds (typosquat-window analyses, npm tarball CDN propagation latency, etc.) so a future maintainer revisiting the policy has the rationale. - The release workflow's atomic three-package contract assumes all three publishes succeed. The recovery procedure is documented in CHANGELOG, but the runbook template should include a "partial-publish recovery runbook" section that's a copy-pasteable maintainer playbook for the rare-but-real scenario.
- Launch artifact send-by discipline is documented in
docs/launch/README.md. Future runbooks should include this pattern as a standard "post-release outreach" template — it's portable across projects.