From 641963e89973e489e79e4e55e4aac2ca1d45eb4d Mon Sep 17 00:00:00 2001 From: Erik LaBianca Date: Tue, 22 Sep 2026 15:21:02 -0400 Subject: [PATCH 1/3] chore(innsigle): reseal the two stale curated pages The Pages deploy has failed since #50 merged: its seal-and-verify step fails the build on any claim that is not VALID, and `use/anti-slop.md` was edited in #50 without resealing. The v0.14.0 release added a second stale page, `use/claude-code-recipe.md`, whose sample `claude plugin list` output carries the version. Reseal both with the house key so the site can publish the release. Note the asymmetry this exposes: tests/validate-innsigle.sh only warns on STALE (so PR checks stay green without 1Password), while pages.yml still fails on it. A stale seal therefore passes review and breaks the deploy. Co-Authored-By: Claude Opus 5 --- .../public/claims/use-anti-slop-md.attestation.json | 10 +++++----- .../claims/use-claude-code-recipe-md.attestation.json | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.innsigle/public/claims/use-anti-slop-md.attestation.json b/.innsigle/public/claims/use-anti-slop-md.attestation.json index 3f799a593..12e140e75 100644 --- a/.innsigle/public/claims/use-anti-slop-md.attestation.json +++ b/.innsigle/public/claims/use-anti-slop-md.attestation.json @@ -2,7 +2,7 @@ "payload": { "innsigle": "1", "type": "https://innsigle.dev/claim/colophon/v1", - "issued_at": "2026-09-16T02:47:37Z", + "issued_at": "2026-09-22T19:20:30Z", "issuer": { "id": "helix", "name": "HELIX", @@ -11,10 +11,10 @@ }, "subjects": [ { - "uri": "https://documentdrivendx.github.io/helix/use/anti-slop.md", + "uri": "https://documentdrivendx.github.io/helix/use/anti-slop/", "digest": { "alg": "sha256", - "value": "ef0e0753f238cdba13908582d68ab30092a34a9e346ee7d54a5142827141931e" + "value": "6edad2e6c896feab312c0dced2435f91f75df94eeacdb9a49014830fbad95a02" } } ], @@ -46,8 +46,8 @@ { "key_id": "ed25519:d5a0b2d95482db5ff68fc8318a1ad4de", "alg": "ed25519", - "sig": "8YJyhhYW7tedR-FWSgx1_fO1Fpn-9P4zxmIcDgPsZmGOWIG3wRQPo_rx96FGb14WuK4zMPptJP7I2MxakB3sAA", - "signed_at": "2026-09-16T02:47:37Z" + "sig": "OLVv4twZXN2U-WUJ-CL9y_r91PSLN33gnmMlcGAnYQyYakmQdZmHZAfwCbXIL5paGyw2THzwF3movxhQvByGBg", + "signed_at": "2026-09-22T19:20:30Z" } ] } diff --git a/.innsigle/public/claims/use-claude-code-recipe-md.attestation.json b/.innsigle/public/claims/use-claude-code-recipe-md.attestation.json index 4e8bab874..c065ede8e 100644 --- a/.innsigle/public/claims/use-claude-code-recipe-md.attestation.json +++ b/.innsigle/public/claims/use-claude-code-recipe-md.attestation.json @@ -2,7 +2,7 @@ "payload": { "innsigle": "1", "type": "https://innsigle.dev/claim/colophon/v1", - "issued_at": "2026-09-19T04:11:18Z", + "issued_at": "2026-09-22T19:20:30Z", "issuer": { "id": "helix", "name": "HELIX", @@ -14,7 +14,7 @@ "uri": "https://documentdrivendx.github.io/helix/use/claude-code-recipe/", "digest": { "alg": "sha256", - "value": "86516a8e29ae19430fce905c0e736718b2097f57f9728329281a5b20df8cff64" + "value": "501d8cb353c60e37f27625e20b0a11c47f4ca3e3c972526e45ea7da278f42db0" } } ], @@ -46,8 +46,8 @@ { "key_id": "ed25519:d5a0b2d95482db5ff68fc8318a1ad4de", "alg": "ed25519", - "sig": "OhYQp2sXCPw9iWM5UOKv_ppCIs2dTfBqsYY9dRrLEes_K2FnB341MBSnP71HQAcljtssyhnsuEC4qCeu6fr3Cw", - "signed_at": "2026-09-19T04:11:18Z" + "sig": "NESjDwpiA8FXD1SFwzkrucb0OApMjN9x5jS60wf5EL7xTqrmbf_I1prB5rvN_aZtAxPQTFLdDS3x5LEJGSizAQ", + "signed_at": "2026-09-22T19:20:30Z" } ] } From 478f038a7fa0aa8abb318f031ab2670afc0d1f36 Mon Sep 17 00:00:00 2001 From: Erik LaBianca Date: Tue, 22 Sep 2026 15:21:02 -0400 Subject: [PATCH 2/3] fix(ci): pass the PR title to release-tag.yml through the environment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The auto-tag workflow has never tagged a release. It interpolated the PR title into the shell as `TITLE=${{ toJSON(...) }}`, so bash consumed the JSON quotes and `json.loads` got a bare string: json.decoder.JSONDecodeError: Expecting value: line 1 column 1 Both runs to date failed this way — v0.13.3 (which is why that version was never tagged) and v0.14.0, whose tag was pushed by hand and verified by release-version-guard.yml instead. Pass the title as an environment variable and read it directly. This also keeps a title with shell metacharacters out of the script text. Co-Authored-By: Claude Opus 5 --- .github/workflows/release-tag.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index a9888de74..7cc609a73 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -35,12 +35,13 @@ jobs: - name: Determine tag from PR title and manifests id: version + env: + PR_TITLE: ${{ github.event.pull_request.title }} run: | - TITLE=${{ toJSON(github.event.pull_request.title) }} - python3 - "$TITLE" <<'PY' + python3 - "$PR_TITLE" <<'PY' import json, re, sys, os - title = json.loads(sys.argv[1]) + title = sys.argv[1] m = re.match(r'^chore\(release\): (v\d+\.\d+\.\d+)\b', title) if not m: print(f"::error::PR title does not start with 'chore(release): vX.Y.Z': {title!r}") From b60697ea4d540bebfa9f21fd74714ed52991fc43 Mon Sep 17 00:00:00 2001 From: Erik LaBianca Date: Tue, 22 Sep 2026 15:31:19 -0400 Subject: [PATCH 3/3] fix(ci): let the Pages deploy use the shared seal policy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The deploy called `innsigle verify --all` raw, which fails on STALE. tests/validate-innsigle.sh — the gate the website workflow runs, and the one contributors see on a pull request — treats an unsigned or stale page as a warning and fails only on a broken or wrong-key signature, because sealing a curated page needs the human key from 1Password and CI does not have it. So a page could pass review and then break the deploy. It did twice: #50 edited use/anti-slop.md without resealing, and v0.14.0 bumped the version string in the recipe page. Neither is a signature failure; a stale seal simply does not render. Run the gate here instead of a second, stricter policy nobody reviews. Co-Authored-By: Claude Opus 5 --- .github/workflows/pages.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 2092f6b90..c11bb7797 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -69,15 +69,23 @@ jobs: python3 scripts/demos/render_session.py "$s" --output "website/static/demos/$slug.cast" done - - name: Seal generated pages (build key) and verify every seal + - name: Seal generated pages (build key) and check seals # Curated pages carry committed human-key claims; generated pages are sealed here with the # CI build key so the deployed site quotes a seal on every page (Innsigle ADR-004). + # + # The check is tests/validate-innsigle.sh, the same gate the website workflow runs, so one + # file defines the policy: an unsigned or stale page warns, and only evidence of a broken or + # wrong signature fails. This step used to call `innsigle verify --all` raw, which also fails + # on STALE. Sealing a curated page needs the human key from 1Password, which CI does not + # have, so a page that passed review blocked the deploy instead — twice, at #50 and again at + # v0.14.0. A stale seal costs that page its rendered seal; it never renders a wrong one. env: INNSIGLE_BUILD_KEY: ${{ secrets.INNSIGLE_BUILD_KEY }} + INNSIGLE_REQUIRED: '1' run: | [ -n "$INNSIGLE_BUILD_KEY" ] || { echo "::error::INNSIGLE_BUILD_KEY secret is not set; run just innsigle-build-key and store the PEM as a repository secret"; exit 1; } bash scripts/innsigle-seal.sh --role build - bash scripts/innsigle-cli.sh verify --all + bash tests/validate-innsigle.sh - name: Setup Pages id: pages