Skip to content

docs: prepare the v0.1.1 release - #10

Merged
wonkwonlee merged 1 commit into
mainfrom
claude/release-v0.1.1
Jul 27, 2026
Merged

docs: prepare the v0.1.1 release#10
wonkwonlee merged 1 commit into
mainfrom
claude/release-v0.1.1

Conversation

@wonkwonlee

Copy link
Copy Markdown
Owner

What and why

The repository has no tags. git tag -l is empty and the GitHub tags/releases APIs both return [] — while the README, the example workflow, and v0.1.0's own release notes all say:

uses: wonkwonlee/ChangeSafe@v0.1.0

So anyone following the documented integration path gets unable to find version v0.1.0 from Actions. The flagship adoption path is currently documentation rather than a feature, and two of the roadmap's success metrics ("a stranger goes from README to a gated plan in under 10 minutes", "the Action running in ≥10 public repos") are blocked behind one missing tag.

The same gap means the security fixes merged in #5#9 cannot reach anyone using the Action, because an Action is consumed by tag.

This PR prepares the release. It does not create the tag — that comes after merge, so the tag can point at a commit that already contains the notes.

What's in it

  • docs/RELEASE_NOTES_v0.1.1.md — leads with Action required, because the heredoc injection was in a workflow people were told to copy, and bumping a pin does not fix a copy already sitting in their repository. It gives the before/after snippet for that step, then covers the rest of the fixes, then states plainly that no verdict changed.
  • Pins updated to @v0.1.1 in the README and the example workflow, with @v0 offered for automatic patches and an exact tag or SHA recommended for the stricter supply-chain posture. For a tool whose job is gate integrity, I did not want the docs to recommend a moving tag by default.
  • CONTRIBUTING.md gains a Releases section — immutable tag per release, exactly one moving tag (v0), notes that say what to do, and a rule that a release never modifies verification/, since rebuilding a published snapshot to agree with newer code destroys the only thing it proves.

One decision worth your review: version strings do not move

package.json still reads 0.1.0 and CLI receipts still record appVersion: "changesafe-cli-0.1.0". That is deliberate, and the notes explain it in a dedicated section.

npm run verify:v0.1.0 re-runs the current CLI against the published bundle and requires the replayed receipt to be canonically identical to the signed one (scripts/lib/v0.1.0-release.mjs, the receipt replay check). appVersion is inside that receipt, so moving the string means the v0.1.0 snapshot no longer reproduces from this repository — a release gate failing as a side effect of a version bump.

The honest resolution is that the snapshot should reproduce from the code at its own tag rather than from the working tree, which becomes possible once tags exist. That touches the release verifier, which is precisely the tool a release should not be quietly modifying, so I have scoped it to v0.2.0 rather than doing it here. If you would rather bump the versions now and re-point the verifier in the same change, say so and I will — it is a bigger PR and a different risk profile.

Since nothing is published to npm, no package version is implied by this release; the git tag names it.

Safety review

  • Invariants preserved: documentation and release-process only. No code changed — no policy, gate, receipt, server, or app behavior is touched by this PR.
  • Test that proves it: the existing suite is unchanged and green (440 passing, 2 skipped), and npm run verify:v0.1.0 still reproduces the published snapshot — which is the specific thing the version-string decision above protects.
  • Does this change any policy's verdict for existing scenarios? No.

Checklist

  • npm run lint && npm run typecheck && npm test && npm run build pass
  • npm run test:e2e unaffected — no app code changed
  • No execution path to real infrastructure added
  • No secrets, real data, or third-party branding added
  • Tests added or updated for the behavior change — n/a, docs only

After merge

With your go-ahead I will create the tags on the merge commit and publish the GitHub release:

git tag -a v0.1.1 -m "ChangeSafe v0.1.1" && git push origin v0.1.1
git tag -f v0 v0.1.1 && git push -f origin v0

Creating tags and a public release is outward-facing, so I will not do it without you saying go.


Generated by Claude Code

The repository has no tags, so `uses: wonkwonlee/ChangeSafe@v0.1.0` — in the
README, the example workflow, and v0.1.0's own notes — resolves to nothing.
Anyone following the documented integration path gets "unable to find version
v0.1.0" from Actions, which makes the flagship path documentation rather than
a feature. The same gap means last week's security fixes cannot reach anyone
using the Action, because the Action is consumed by tag.

Release notes for v0.1.1 lead with what a user must do rather than what
changed: the heredoc injection was in a workflow people were told to copy,
and updating a pin does not fix a copy already in their repository. Pins move
to v0.1.1, with @v0 offered for automatic patches and an exact tag or SHA
recommended for a stricter posture.

CONTRIBUTING gains the release convention this establishes — an immutable tag
per release, one moving major tag, notes that say what to do, and no
modification of published verification snapshots.

Version strings deliberately do not move, and the notes say why:
`verify:v0.1.0` re-runs the current CLI and requires the replayed receipt to
be canonically identical to the signed one, and appVersion is inside that
receipt. Making the snapshot reproduce from its own tag instead of the working
tree is the real fix, and it belongs in v0.2.0 rather than in a patch that
would be quietly editing the release verifier.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Npn2z4Uami1SpJyaCRsy6N
@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
change-safe Ready Ready Preview, Comment Jul 27, 2026 4:02am

@wonkwonlee
wonkwonlee merged commit 3d62475 into main Jul 27, 2026
8 checks passed
@wonkwonlee
wonkwonlee deleted the claude/release-v0.1.1 branch July 27, 2026 08:38
wonkwonlee added a commit that referenced this pull request Aug 4, 2026
PR #53 merged components/hooks/useScenarioDeepLink.ts and
components/CaseStudyBadge.tsx, but neither was ever imported by any
shell, so the case-study-deeplinks e2e suite it also merged has been
failing on main since. Wires the feature up per the merged design doc
(docs/superpowers/specs/2026-07-30-workbench-case-study-deeplinks-design.md):

- `caseStudy` field on `ReviewExampleDescriptorSchema`: an optional
  case-study name, or null. Set for the four scenarios featured in
  docs/CASE_STUDIES.md (three network, one terraform); null everywhere
  else, including all of Kubernetes (no K8s case studies exist yet, so
  only the deep-link hook is wired there, not the badge, per the design
  doc's explicit scope).
- New fifth Terraform fixture, `scenario-p-injected-pr-context`,
  sourced from the scenario's own canonical incident.json rather than
  reusing the thematically similar `terraform-protected-and-injected`
  fixture — citing the wrong one would misattribute Case 3's evidence
  (CLAUDE.md invariant #10).
- All three public shells now read `?scenario=<id>` on mount to
  pre-select an example (falling back to the existing default when the
  id is missing or unrecognized) and keep the URL in sync as the
  visitor picks a different one, via the existing hook's History-API
  approach (deliberately not next/navigation, to avoid forcing the
  route out of static prerendering).

Also fixes the h1-related e2e assertions in workbench.spec.ts,
terraform-workbench.spec.ts, kubernetes-workbench.spec.ts, and
case-study-deeplinks.spec.ts that the earlier h1-is-now-the-scenario
fix broke — the same class of fix already applied to
public-workbench-hardening.spec.ts, just in files this branch hadn't
picked up yet (they arrived via merging origin/main, which had
diverged by two PRs).

Verified: full local Playwright run passes except the pre-existing
Kubernetes ERROR-phase flake (confirmed to reproduce identically on
unmodified commits, unrelated to this change).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
wonkwonlee added a commit that referenced this pull request Aug 4, 2026
…air (#54)

* feat: multi-domain console vNext (workbench, self-hosted review, Kubernetes/Terraform replay) (#48)

* feat: add app review contract schemas

* fix: validate contract version mismatches

* test: pin network regression baseline

* test: probe blocked simulation directly

* test: guard core domain presentation boundaries

* feat(ui): add semantic design tokens

* feat(ui): add static review workbench shell

* test: guard design token regex captures

* fix(ui): ground static shell in replay fixture

* feat(ui): expose public replay workbench route

* test(ui): baseline workbench responsive semantics

* feat(ui): disclose workbench runtime variants

* fix(ui): distinguish queue review-record storage

* test(e2e): baseline public workbench

* test(e2e): harden workbench static boundary

* test(e2e): intercept workbench data requests

* test(e2e): add workbench visual baseline

* test(e2e): make workbench baseline portable

* test(e2e): add platform-specific workbench baselines

* feat: add v1 domain review transport contracts

* fix: enforce review risk and replay fallback contracts

* feat: add closed domain runtime registry

* fix: compose immutable session capabilities

* fix: source durability from transport capability

* feat: add pure review workflow controller

* fix: correlate review controller attempts

* fix: bind review input identity at initialization

* feat: add v1 review analyze API contracts

* feat: add registry-backed review analyze route

* feat: add review controller React hook

* fix: bind hook results to active attempts

* refactor: lazily load domain runtimes

* feat: complete review controller lifecycle

* fix: bind receipts to active review workflow

* fix: bind receipt identity provenance

* feat: expose review hook lifecycle actions

* fix: validate review rebind before abort

* feat: add public replay review transport

* refactor: isolate review API contracts

* feat: add network review examples

* feat: compose network review receipts

* feat: publish network policy version in examples

* fix: bind receipts to session policy version

* fix: version review policy binding contract

* feat: add network workflow compatibility facade

* fix: bind legacy analyses to review session

* fix: bind replay responses to requested fixture

* fix: bind replay provenance to requested fixture

* test: reject authored synthetic fixture substitution

* feat: make network workbench replay interactive

* fix: improve workbench replay accessibility

* fix: mark review canvas busy during replay

* test: prove network review parity

* test: bind parity provenance to fixtures

* test: prove workbench replay provenance

* test: make workbench route assertion order-insensitive

* feat: add Terraform replay examples

* feat: resolve Terraform public replay

* fix: bundle Terraform replay context safely

* feat: add Terraform public replay workbench

* test: prove Terraform replay parity

* fix: keep public replay decision-free

* fix: preserve legacy network review lifecycle

* test: distinguish durable review hook sessions

* feat: add Kubernetes replay examples

* test: prove Kubernetes replay derivation

* fix: isolate Kubernetes YAML parser boundary

* fix: isolate kubernetes offline metadata

* feat: resolve Kubernetes public replays

* fix: load Kubernetes runtime through offline boundary

* feat: add Kubernetes public replay workbench

* fix: match Kubernetes selectors against pod labels

* test: prove Kubernetes public replay parity

* feat: add durable review proof contracts

* fix: bind durable intake and receipt proofs

* fix: verify durable records before persistence

* feat: add durable review record store

* fix: harden durable review store integrity

* fix: block durable review replace conflicts

* fix: guard explicit durable review sequence replacement

* fix: migrate durable review conflict trigger

* feat: split pending durable review resolution

* feat: split durable review pending records

* fix: bind durable resolution to parent at database boundary

* feat: add authenticated durable review endpoints

* fix: scope durable review intake provenance

* fix: isolate durable review owner identities

* fix: migrate ownerless durable review schema

* fix: quarantine ownerless review migrations

* fix: preserve exact durable review migrations

* feat: resolve durable reviews server-side

* fix: serialize durable receipt issuance

* fix: harden durable decision claim lifecycle

* feat: expose durable receipt proof

* fix: verify durable recovery authorship

* fix: expose corrupted receipt proof evidence

* feat: add authenticated self-hosted workbench

* fix: harden self-hosted workbench state

* feat: expose domain policy coverage

* feat: document future domain capability boundary

* fix: validate runtime policy coverage metadata

* feat: add accessible network topology tables

* fix: enforce client telemetry privacy

* fix: close client telemetry bypasses

* feat: harden public workbench accessibility

* fix: meet WCAG contrast for muted text

* fix: harden large workbench evidence

* fix: close public workbench review gaps

* test: close domain route family gaps

* test: enforce public client bundle budgets

* fix: harden public client bundle verification

* fix: scan every emitted client chunk

* fix: guard self-hosted client dependencies

* feat: cut over to vnext network workbench

* docs: align operator guidance with vnext workbench

* chore: remove retired live rate limiter

* docs: correct vnext release references

* refactor: retire legacy local review authority

* build: refresh shipped CLI bundle

* test: keep Action self-test fixtures available

* docs: refresh public bundle baselines

* chore: complete multidomain console ultragoal

* Create 2026-07-29-multidomain-console-vnext-g001.md

* fix: adapt vNext scenario consumers to the reorganized scenarios API

Merging origin/main (#47) renamed ScenarioDefinition.bundle to .input and
scenario.title to .label, and split fixtures out as nullable. Update the
public reviews/analyze route, ReviewWorkbenchShell, and the durable review
store test to the new shape.

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

* fix: restore the Network route's domain bundle boundary after the scenarios reorg

Merging origin/main pulled in scenarios/index.ts's unified registry, which
resolves every domain (network, terraform, kubernetes) through
scenarios/domains.ts at module scope. Any importer — including
app/page.tsx's Network workbench — therefore pulled in
@changesafe/domain-terraform and @changesafe/domain-kubernetes too,
violating the per-route bundle boundary enforced by
tests/unit/workbench-performance-boundaries.test.ts.

Add scenarios/network.ts as a self-contained, network-only scenario
registry (no dependency on scenarios/domains.ts), have scenarios/index.ts
compose SCENARIOS from it instead of duplicating the definitions, and
repoint the Network route's browser-reachable consumers
(ReviewWorkbenchShell, features/domains/network/examples) at it directly.
Update the remaining test files for the renamed ScenarioDefinition fields
(.bundle -> .input, .title -> .label) and the hardcoded pre-reorg fixture
paths in packages/server/tests/reviews.test.ts.

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

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* fix: restore multidomain workbench navigation

* docs: design spec for scenario corpus expansion (Terraform + Kubernetes parity)

Plans 12 new scenarios to bring Terraform and Kubernetes coverage up to
Network's existing risk-level and policy-surface depth.

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

* docs: implementation plan for scenario corpus expansion

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

* feat(scenarios): wire terraform context field through the scenario harness

* feat(scenarios): add scenario-n-stateless-replace (terraform, MEDIUM)

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

* feat(scenarios): add scenario-o-stateful-replace-backed-up (terraform, MEDIUM)

* feat(scenarios): add scenario-p-injected-pr-context (terraform, CRITICAL, adversarial)

* feat(scenarios): add scenario-t-blast-radius-drift (terraform, HIGH)

* feat(scenarios): add scenario-u-unrecorded-prior-state (terraform, CRITICAL, adversarial)

* feat(scenarios): add scenario-q-safe-scale-up (kubernetes, LOW)

* feat(scenarios): add scenario-r-partial-replica-reduction (kubernetes, MEDIUM)

* feat(scenarios): add scenario-s-privileged-injection (kubernetes, CRITICAL, adversarial)

* feat(scenarios): add scenario-v-protected-config-change (kubernetes, CRITICAL)

* feat(scenarios): add scenario-w-mutable-image-tag (kubernetes, HIGH)

* feat(scenarios): add scenario-x-missing-verification (kubernetes, MEDIUM)

* feat(scenarios): add scenario-y-rollback-does-not-restore (kubernetes, CRITICAL, adversarial)

* fix(scenarios): tighten scenario-y teaches narrative to lead with rollback gap

* fix(cli): wire terraform context field through scenario check

checkOne called domain.parseInput with only the raw incident JSON, so the
terraform domain's context (used for UNTRUSTED_INSTRUCTION detection) was
always dropped on the CLI path even though the test-harness registry
already forwarded it, letting scenario-p-injected-pr-context silently pass
CI's corpus-check job with the wrong verdict.

* docs(scenarios): document terraform context field, polish scenario-o narrative

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

* fix: address PR 51 review feedback

* fix: PR review fixes - decision schema and Kubernetes snapshot binding

* refactor(workbench): extract shared WorkbenchNav component

The four workbench shells each hand-duplicated the domain-switch nav bar,
which had already drifted once (fixed in 947ec4c) and still carried two
bugs: Terraform/Kubernetes both linked "Home" and "Network" to the same
href, and self-hosted labeled that same destination "Public replay" while
the others called it "Network" — three labels for one page. A single
WorkbenchNav component now renders the nav for all four shells so the
destination list, active state, and labels can't drift independently
again.

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

* fix(workbench): color-code gate verdicts, show picker selection, fix K8s mobile overflow

Addresses P0/P1 findings from an /impeccable critique of the multi-domain
workbench:

- The design system declares green/amber/red for PASS/WARN/BLOCK, but
  finding badges and riskLevel rendered as identical neutral gray
  regardless of value — CRITICAL and LOW were visually indistinguishable.
  New shared components/StatusTone.tsx (StatusBadge, RiskValue) applies
  the existing tone classes (already used correctly by Terraform's
  ActionBadge) across all three public shells.
- The example/scenario picker had aria-pressed wired but no matching
  visual state, so sighted users couldn't tell which example was loaded.
  Now the selected button gets the same active border/background used
  elsewhere in the design system.
- The Kubernetes workbench overflowed the viewport at 390px. Root cause:
  the section-stacking `grid gap-4` wrapper in both Terraform and
  Kubernetes shells omitted `min-w-0 grid-cols-1`, which the Network
  shell's equivalent container already has — without it the browser's
  default single implicit grid track sizes to content instead of the
  container, so an oversized child can push the whole page wider than
  the viewport without ever triggering its own scrollbar. The nested
  manifest-diff grid in KubernetesWorkbenchShell had the same gap.

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

* fix(workbench): make the outcome h1 the scenario, not the raw phase enum

Addresses the remaining P2 finding from the /impeccable critique: the
page <h1> on all three public shells rendered workflow.phase literally
("APPROVAL_REQUIRED", "BLOCKED"), and focus was moved there on every
phase change — so a screen reader announced a raw enum as the heading,
and a first-time sighted user's first legible content was SCREAMING_SNAKE_CASE.

The h1 is now the scenario identity (e.g. "INC-4977 — Suspected route
leak"), which doesn't change with phase. A new PhasePill component
(components/StatusTone.tsx) renders the phase in human copy and
gate-matching tone, keeps the raw enum alongside in a <code> for
operators, and forwards a ref so focus now moves to the part of the
page that actually changes on a phase transition instead of the static
title.

Updates the e2e hardening spec's phase assertions to target the pill's
stable data-phase attribute instead of relying on the h1 text or
ambiguous nested-text matching, and fixes one assertion in the mobile
ordering test that still hardcoded the *initial* example's label after
the test had already switched to a different (blocked) example.

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

* fix(cli): repair truncated shipped CLI bundle

packages/cli/dist/changesafe.js has been corrupted since PR #50
(b0e277c, 2026-07-30): the committed file was truncated to ~120KB and
literally began with an AI tool's own output-truncation placeholder
text ("Warning: truncated output... bytes omitted...") instead of the
`#!/usr/bin/env node` shebang. The real bundle is ~1.2MB.

This went undetected because tests/integration/verification-bundle.test.ts
always runs `npm run build:cli` before exercising the CLI, so the
committed artifact itself was never tested — only a freshly rebuilt
copy was. Anyone running the shipped dist directly without rebuilding
(a published package install, or the checked-in file as-is) would get
a broken CLI. `main` carries the same corruption and needs the same
fix separately.

Rebuilt via `npm run build:cli` and verified: --help, `scenario check`
across all three domains (25/25 scenarios passing), and
`scenario gallery --check` all pass against the regenerated bundle.

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

* fix(cli): rebuild with a clean install to fix a malformed bundle

The dist file committed in e502bf0 was built against a node_modules
that had drifted from the lockfile during a long local session and
produced a bundle with a duplicate `createRequire2` declaration —
valid enough for `--help` to work, but a SyntaxError under Node's ESM
loader for any command that actually reached the affected code path
(caught by the Action's "gate a destructive plan" CI step).

Rebuilt after `npm ci` for a clean, lockfile-matching install; verified
with `node --check` and by running the exact gate command CI runs
(terraform domain, protected+injected plan, expects exit 1) — passes.

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

* feat(workbench): wire up scenario URL deep links and case-study badges

PR #53 merged components/hooks/useScenarioDeepLink.ts and
components/CaseStudyBadge.tsx, but neither was ever imported by any
shell, so the case-study-deeplinks e2e suite it also merged has been
failing on main since. Wires the feature up per the merged design doc
(docs/superpowers/specs/2026-07-30-workbench-case-study-deeplinks-design.md):

- `caseStudy` field on `ReviewExampleDescriptorSchema`: an optional
  case-study name, or null. Set for the four scenarios featured in
  docs/CASE_STUDIES.md (three network, one terraform); null everywhere
  else, including all of Kubernetes (no K8s case studies exist yet, so
  only the deep-link hook is wired there, not the badge, per the design
  doc's explicit scope).
- New fifth Terraform fixture, `scenario-p-injected-pr-context`,
  sourced from the scenario's own canonical incident.json rather than
  reusing the thematically similar `terraform-protected-and-injected`
  fixture — citing the wrong one would misattribute Case 3's evidence
  (CLAUDE.md invariant #10).
- All three public shells now read `?scenario=<id>` on mount to
  pre-select an example (falling back to the existing default when the
  id is missing or unrecognized) and keep the URL in sync as the
  visitor picks a different one, via the existing hook's History-API
  approach (deliberately not next/navigation, to avoid forcing the
  route out of static prerendering).

Also fixes the h1-related e2e assertions in workbench.spec.ts,
terraform-workbench.spec.ts, kubernetes-workbench.spec.ts, and
case-study-deeplinks.spec.ts that the earlier h1-is-now-the-scenario
fix broke — the same class of fix already applied to
public-workbench-hardening.spec.ts, just in files this branch hadn't
picked up yet (they arrived via merging origin/main, which had
diverged by two PRs).

Verified: full local Playwright run passes except the pre-existing
Kubernetes ERROR-phase flake (confirmed to reproduce identically on
unmodified commits, unrelated to this change).

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

* docs(case-studies): add live deep links now that the feature is wired

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

* fix(kubernetes): stop sending a per-fixture inputId that can never match

Every Kubernetes public-replay fixture evaluates against the one shared
KUBERNETES_PUBLIC_REPLAY_SNAPSHOT, but deriveFixture() was setting each
fixture's inputId to its own sourceId (e.g. "kubernetes-safe-scale")
instead of the snapshot's real id ("snapshot-public-kubernetes-demo").
The server route always echoes the snapshot's real id back regardless
of which fixture was requested (fixed earlier for a different reason
in 4e7dad3, which updated the server side but missed this client-side
counterpart), and the client's expectedInputId check in
receiveTransport() requires exact equality — so every Kubernetes
replay evaluation deterministically failed into the ERROR phase.

This surfaced across this session as what looked like flaky
Playwright failures (5 Kubernetes-touching specs, reproducing
identically with retries on CI); it was not a race, it just needed a
click on "Run replay" to reproduce 100% of the time. Removed the
now-redundant inputId parameter from deriveFixture entirely so a
future fixture can't reintroduce the same mismatch by copy-paste.

Verified: full local Playwright run (39/39) passes with no retries
needed, down from a consistent 5 failures.

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

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants