Skip to content
This repository was archived by the owner on Jul 30, 2026. It is now read-only.

ci: make every sub-project buildable from its lockfile - #66

Merged
rubenhensen merged 7 commits into
mainfrom
ci/build-matrix-and-sdk-canary
Jul 27, 2026
Merged

ci: make every sub-project buildable from its lockfile#66
rubenhensen merged 7 commits into
mainfrom
ci/build-matrix-and-sdk-canary

Conversation

@dobby-coder

@dobby-coder dobby-coder Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Part of #65

The repo had no build CI, so every PR said "reviewer must verify manually". The two jobs the issue asks for are written and tested, but they live in a PR comment rather than in this diff, because dobby-coder[bot] has no workflows permission and the remote rejects any push touching .github/workflows/:

! [remote rejected] ci/build-matrix-and-sdk-canary -> ci/build-matrix-and-sdk-canary
  (refusing to allow a GitHub App to create or update workflow
   `.github/workflows/_permtest.yml` without `workflows` permission)

So this PR is the part I can land: the lockfile plumbing and the per-sub-project checks both jobs depend on. A maintainer has to paste the two YAML files from the comment below for #65 to be done, hence Part of rather than Closes.

Committed here

  • pg-dotnet/packages.lock.json + RestorePackagesWithLockFile: pg-dotnet was the only sub-project without a lockfile, so "build every sub-project from its lockfile" had nothing to lock against there. RestorePackagesWithLockFile keeps the lockfile in step on a normal restore; CI adds --locked-mode.
  • an npm run check script for pg-node: it has no bundler, so its CI job would otherwise only prove that npm ci succeeds. The check imports src/encryption.mjs, which resolves src/'s named imports against the installed @e4a/pg-js.
  • --fail-on-warnings on pg-manual's build, and a new npm run check: neither SDK breakage this repo has already hit would have failed CI without them. See below.
  • CLAUDE.md: the per-sub-project install-and-build commands, the packages.lock.json regeneration rule, what each check does and does not cover, and the workflows-permission block so the next agent does not rediscover it.

Why pg-manual needed both

pg-manual can break in two ways, and until this PR neither one failed a build.

A static import going stale is the shape of both breakages already recorded in CLAUDE.md's pg-manual notes: web-streams-polyfill v3 renaming PolyfilledWritableStream, and yivi 0.2 to 1.x moving to named exports. Webpack reports these as a warning and exits 0, so npm run build compiled a broken example green.

A dynamic import going stale is the pg-wasm case. Both examples reach the SDK through import('@e4a/pg-wasm') and destructure at runtime, and webpack analyses no exports across that boundary, so it does not even warn.

One flag and one small build cover one half each. Measured on this branch:

Tree npm run build (before) npm run build (now, --fail-on-warnings) npm run check
clean exit 0, zero warnings exit 0, zero warnings exit 0
file.js:2 static rename re-applied exit 0, with warning exit 1 exit 0
seal dropped from the installed SDK exit 0, zero warnings exit 0, zero warnings exit 1

The bottom-right of that table is why the flag alone is not enough. check/sdk-exports.js imports the four names the examples destructure (seal, sealStream, Unsealer, StreamUnsealer) statically, which puts them back under webpack's analysis; check/webpack.config.js keeps that build out of dist/. Both workflows already run npm run build and npm run check --if-present, so the YAML needs no change for either half.

The repo has no test suite (it is example code), so the check script and the flag are themselves the regression guard rather than a test file. Both negative cases above were produced by editing the tree, then reverted.

Verification

Everything below was run in this container against the branch, not reasoned about.

Check Result
npm ci + build/check/lint --if-present in pg-manual, pg-node, pg-sveltekit all exit 0
dotnet restore --locked-mode + dotnet build --no-restore exit 0, net8.0 and net10.0
Bump E4A.PostGuard to 0.5.0 without regenerating the lockfile --locked-mode fails, NU1004
Re-apply the web-streams-polyfill rename in examples/file.js npm run build fails, npm run check still passes
Drop seal from the installed @e4a/pg-wasm npm run check fails, npm run build still passes
Add a name to sdk-exports.js's import but not its object literal check stays at exit 0, the import is elided
Rename the PostGuard import in pg-node's src/encryption.mjs npm run check fails, "does not provide an export named"
Canary leg for pg-manual: npm install @e4a/pg-wasm@latest + build + check exit 0 against 0.6.1, with the new flag on
actionlint 1.7.7 on both workflow files clean
Canary report script, stubbed gh, both branches creates on first failure, comments on the existing issue after

The only check that runs on this PR is Conventional Commit, which passes. There is no build CI yet, which is what #65 is about, so the table above is the stand-in: it runs the proposed workflows' own commands locally.

One thing the canary will not tell you

I ran the canary steps by hand against what is published today. Every example still builds against @e4a/pg-js 2.3.3, @e4a/pg-wasm 0.6.1 and E4A.PostGuard 0.5.0, so the canary is green as of this PR.

The issue's framing points the other way, so it is worth being precise about the gap. pg-node and pg-sveltekit pin ^1.10.0 while 2.3.3 is current, and pg-dotnet pins 0.4.1 while 0.5.0 is current. Being pinned behind is not a build failure. The canary catches an SDK release that breaks the examples; it does not catch the examples drifting behind a current SDK, which is what happened here. I am flagging that rather than also building a version-drift check, since the issue asked for two specific jobs.

Notes for whoever applies the YAML

  • sdk-canary.yml needs issues: write, scoped to the report job only.
  • GitHub disables scheduled workflows on a repo with no commits for 60 days. This repo is active enough, but if the canary goes quiet that is the first thing to check.
  • The canary reports on workflow_dispatch failures too, not only scheduled ones. Manually dispatching a run that fails will open or update the issue.

dobby-coder Bot added 3 commits July 27, 2026 07:58
pg-dotnet was the only sub-project without a lockfile, so a CI job that
builds every sub-project from its lockfile had nothing to lock against.

RestorePackagesWithLockFile keeps packages.lock.json in step with the
csproj on a normal restore; CI adds --locked-mode, which fails with
NU1004 when a PackageReference moves without the lockfile being
regenerated.
pg-node has no bundler, so a build job for it would only prove that
npm ci succeeds. The check runs node --check on the CLI entry point and
imports src/encryption.mjs, which resolves the named imports against the
installed @e4a/pg-js and so fails when an export is renamed or removed.
Adds the per-sub-project install-and-build commands, the packages.lock.json
regeneration rule, and a note that the dobby-coder App cannot push files
under .github/workflows/ so workflow changes arrive as a patch to apply.
@dobby-coder

dobby-coder Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

Workflow YAML for a maintainer to apply

These are the two jobs #65 asks for. I cannot commit them: dobby-coder[bot] has no workflows permission, and the remote rejects any push touching .github/workflows/. Save them at the paths below and they work against this branch as-is.

Both files pass actionlint 1.7.7 clean, and every command in them was run locally against this branch first.

.github/workflows/ci.yml — PR gate, builds every sub-project from its lockfile
name: CI

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

jobs:
  node:
    name: Build ${{ matrix.project }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        project: [pg-manual, pg-node, pg-sveltekit]
    defaults:
      run:
        working-directory: ${{ matrix.project }}
    steps:
      - uses: actions/checkout@v7

      - uses: actions/setup-node@v7
        with:
          node-version: 22
          cache: npm
          cache-dependency-path: ${{ matrix.project }}/package-lock.json

      - name: Install from lockfile
        run: npm ci

      - name: Build
        run: npm run build --if-present

      - name: Check
        run: npm run check --if-present

      - name: Lint
        run: npm run lint --if-present

  dotnet:
    name: Build pg-dotnet
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: pg-dotnet
    steps:
      - uses: actions/checkout@v7

      - uses: actions/setup-dotnet@v6
        with:
          dotnet-version: |
            8.0.x
            10.0.x

      - name: Restore from lockfile
        run: dotnet restore --locked-mode

      - name: Build
        run: dotnet build --no-restore
.github/workflows/sdk-canary.yml — weekly build against the latest published SDKs
name: SDK canary

# Builds every example against the LATEST published PostGuard SDKs instead of
# the pinned lockfile versions, so we hear about a breaking SDK release before
# a reader of docs.postguard.eu does.

on:
  schedule:
    - cron: '0 6 * * 1'
  workflow_dispatch:

permissions:
  contents: read

jobs:
  node:
    name: Canary ${{ matrix.project }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - project: pg-manual
            package: '@e4a/pg-wasm'
          - project: pg-node
            package: '@e4a/pg-js'
          - project: pg-sveltekit
            package: '@e4a/pg-js'
    defaults:
      run:
        working-directory: ${{ matrix.project }}
    steps:
      - uses: actions/checkout@v7

      - uses: actions/setup-node@v7
        with:
          node-version: 22

      - name: Install latest ${{ matrix.package }}
        run: npm install "${{ matrix.package }}@latest"

      - name: Report resolved version
        run: node -p "require('./node_modules/${{ matrix.package }}/package.json').version"

      - name: Build
        run: npm run build --if-present

      - name: Check
        run: npm run check --if-present

      - name: Lint
        run: npm run lint --if-present

  dotnet:
    name: Canary pg-dotnet
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: pg-dotnet
    steps:
      - uses: actions/checkout@v7

      - uses: actions/setup-dotnet@v6
        with:
          dotnet-version: |
            8.0.x
            10.0.x

      - name: Install latest E4A.PostGuard
        run: dotnet add package E4A.PostGuard

      - name: Build
        run: dotnet build

  report:
    name: Report failure
    runs-on: ubuntu-latest
    needs: [node, dotnet]
    if: failure()
    permissions:
      contents: read
      issues: write
    steps:
      - name: Open or update the canary issue
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GH_REPO: ${{ github.repository }}
          RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
        run: |
          set -euo pipefail

          gh label create sdk-canary \
            --description 'Opened by the weekly SDK canary' \
            --color D93F0B --force

          title='SDK canary failed against the latest published SDKs'
          body=$(cat <<EOF
          The weekly canary built every example against the latest published \`@e4a/pg-js\`, \`@e4a/pg-wasm\` and \`E4A.PostGuard\`, and at least one build failed. Per-project results are in the run log:

          $RUN_URL

          This does not affect the pinned lockfile builds on \`main\`. It means an SDK release has moved ahead of what the examples use. Update the affected example, or pin it deliberately and record why.
          EOF
          )

          existing=$(gh issue list --state open --label sdk-canary --json number --jq '.[0].number // empty')
          if [ -n "$existing" ]; then
            gh issue comment "$existing" --body "$body"
          else
            gh issue create --title "$title" --body "$body" --label sdk-canary
          fi

How the two differ

ci.yml installs with npm ci and dotnet restore --locked-mode, so it fails when a lockfile is missing, corrupt, or out of step with the manifest. sdk-canary.yml deliberately ignores the lockfiles: it runs npm install <pkg>@latest and dotnet add package E4A.PostGuard, so it tracks whatever is published on the registry that Monday.

Design choices worth a second opinion

  • The canary opens one issue and comments on it on later failures, keyed off an sdk-canary label the job creates with gh label create --force. Without the dedup you get a fresh issue every Monday until someone fixes the SDK. Say the word if you would rather have one issue per failure.
  • The report job asks for issues: write; the rest of the workflow stays on contents: read.
  • The canary reports on workflow_dispatch failures too, not only scheduled ones. Dispatching a run that fails will open or update the issue.
  • Both node jobs use npm run <script> --if-present, so a sub-project opts into a build, check or lint step by having the script. No workflow edit needed when pg-manual eventually grows a lint step.
  • GitHub disables scheduled workflows on a repo with no commits for 60 days. Worth knowing if the canary ever goes quiet.

What covers pg-manual

ci.yml's npm run build step and sdk-canary.yml's both gained teeth on 7963a9b without the YAML changing, because the flag went on the npm script instead:

  • pg-manual's build now runs webpack --fail-on-warnings, so a stale static import fails the step instead of warning and exiting 0. That is the shape of the two breakages recorded at CLAUDE.md:49 and :50.
  • pg-manual's check (a second, tiny webpack build of check/sdk-exports.js) covers the dynamic import('@e4a/pg-wasm'), which the flag on build cannot see. Dropping seal from the SDK's export list leaves npm run build at exit 0 with zero warnings and fails npm run check.

An earlier version of this comment said webpack --fail-on-warnings would close the pg-wasm hole on its own. It does not, and the second bullet is the measurement. Both jobs above already run npm run build and npm run check --if-present, so the YAML is unchanged from the previous version of this comment apart from the action version bumps.

@dobby-coder
dobby-coder Bot requested a review from rubenhensen July 27, 2026 08:00

@dobby-coder dobby-coder Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: request-changes

Rules + review gate, cycle 1. I re-ran the two load-bearing claims in this container against 6d10682 instead of reasoning about them.

Blocker: the canary's pg-manual leg cannot fail

pg-manual is the only consumer of @e4a/pg-wasm in the matrix, and both examples load the SDK through a dynamic import('@e4a/pg-wasm') and destructure at runtime (examples/string.js:16, examples/file.js:17). Webpack does no static export analysis across that boundary. I renamed the destructured seal binding to a name the SDK does not export, then ran the exact commands the workflows run:

  • npm run build -> compiled successfully, exit 0, zero warnings
  • npx webpack --fail-on-warnings -> compiled successfully, exit 0

A breaking @e4a/pg-wasm release therefore leaves both ci.yml and sdk-canary.yml green, so @e4a/pg-wasm has no coverage at all. That is the failure mode the canary exists for.

It also makes the comment's "Not covered" paragraph wrong where it says webpack --fail-on-warnings "would close that hole". It does not, for this case. It only catches statically imported deps, like the web-streams-polyfill rename recorded at CLAUDE.md:44.

The fix has the same shape as the one already applied to pg-node: give pg-manual a check script that imports the bundler entry and asserts the named exports the examples use (seal, sealStream, plus whatever utils.js pulls). Both workflows already run npm run check --if-present, so no YAML change is needed. Correct the "Not covered" paragraph in the same pass.

Non-blocking

Two smaller points are inline on pg-node/package.json and CLAUDE.md.

Cheap while a maintainer is pasting the YAML anyway: actions/checkout@v5 and actions/setup-node@v5 are two majors behind (v7.0.1 and v7.0.0 are current) and actions/setup-dotnet@v5 is one behind (v6.0.0). Nothing breaks on v5 today. Same pins appear in both files.

Verified green

dotnet restore --locked-mode + dotnet build --no-restore on net8.0 and net10.0, npm ci + build/check/lint across the three node sub-projects, and the NU1004 negative case. The lockfile plumbing itself is sound.

Comment thread pg-node/package.json
Comment thread CLAUDE.md Outdated
The examples load the SDK through a dynamic import('@e4a/pg-wasm') and
destructure at runtime, so webpack never analyses those bindings. Renaming
the destructured `seal` binding leaves `npm run build` at exit 0 with zero
warnings, and `webpack --fail-on-warnings` at exit 0 too, so both the PR
gate and the weekly canary would stay green through a breaking pg-wasm
release. pg-wasm is only used here, so it had no coverage at all.

`npm run check` builds check/sdk-exports.js, which imports the same names
statically, with --fail-on-warnings. Dropping `seal` from the SDK now exits
1 while `npm run build` still succeeds.

Both workflows already run `npm run check --if-present`, so the proposed
YAML is unchanged.

Also correct CLAUDE.md's claim about what pg-node's check covers: it never
links index.mjs's own imports, only src/.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dobby-coder

dobby-coder Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

Cycle 1 addressed in fcde281.

Blocker, the canary's pg-manual leg cannot fail: confirmed and fixed. pg-manual now has an npm run check: a second, tiny webpack build of check/sdk-exports.js, which imports seal, sealStream, Unsealer and StreamUnsealer statically, run with --fail-on-warnings. Static imports are the only thing webpack's export analysis sees, so this puts the four names the examples destructure back under it.

Measured by dropping seal from the installed SDK's export list:

Command Before fcde281 After
npm run build exit 0, zero warnings exit 0, zero warnings
npx webpack --fail-on-warnings exit 0 exit 0
npm run check did not exist exit 1, export 'seal' ... was not found

Both workflows already run npm run check --if-present, so the YAML is unchanged for this.

The "Not covered" paragraph: you were right that it was wrong, and the middle row above is why. That section of the workflow comment is rewritten as "What covers @e4a/pg-wasm", says plainly that the earlier claim was wrong, and records the measurement.

Action versions: bumped in the comment, actions/checkout@v7, actions/setup-node@v7, actions/setup-dotnet@v6. Both files still pass actionlint 1.7.7 clean, and I re-ran the report job's shell against a stubbed gh on both branches.

Two inline threads: both replied to and resolved. CLAUDE.md's workflows paragraph is split into the standing permission rule and a paragraph marked pending that links the YAML to this PR; the pg-node line now says what the check actually reaches, and names the src/config.mjs gap you found.

Re-ran locally after the change: npm ci plus build, check and lint across the three node sub-projects, dotnet restore --locked-mode plus dotnet build --no-restore on net8.0 and net10.0, and the canary leg for pg-manual against @e4a/pg-wasm@latest. All exit 0.

@dobby-coder dobby-coder Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: request-changes

Rules + review gate, cycle 2 on fcde281. Cycle 1's blocker is fixed and I re-verified it: with seal dropped from the installed SDK, npm run check in pg-manual exits 1 while npm run build still exits 0, so @e4a/pg-wasm now has real coverage. The lockfile plumbing stays green.

One blocker left, and it is the mirror image of cycle 1's.

Blocker: the static-import half of the same hole is still open

pg-manual's build script is still bare webpack, so a broken static import compiles with a warning and exit 0. Measured on this branch by re-applying the web-streams-polyfill rename from CLAUDE.md:49: npm run build exit 0 (with the export ... was not found warning), npm run check exit 0, npx webpack --fail-on-warnings exit 1. Both ci.yml steps pass on a broken example.

That is not hypothetical — it is two of the three breakages this repo has already hit (CLAUDE.md:49 and :50), and CLAUDE.md:53, a line this PR edits, asks CI to enforce it. On a clean tree --fail-on-warnings exits 0, so the one-flag fix is safe to turn on today. Details and both measurements are inline on pg-manual/package.json.

Non-blocking

Two inline notes: the sdk-exports.js comment tells a maintainer to update one place where coverage needs two (verified — an unreferenced import keeps the check at exit 0), and the CLAUDE.md sentence about --fail-on-warnings should split the dynamic case from the static one.

Rule sweep

Clean. Checked the CI/canary YAML in the comment against the Actions default-shell pipefail trap (the report job sets set -euo pipefail and has no unguarded pipeline) and the empty-tag-resolves-to-latest trap (no version is read from a file); checked PR hygiene against the conventional-commit title check (pr-title.yml is present, ci: is valid), reviewer assignment (rubenhensen requested), the closing-keyword rule (Part of #65 is the correct deliberate form here, since a human must apply the YAML), and the draft/no-CI rules (draft is correct mid-cycle). Prose and scope sweep found nothing: the new webpack config stays on mode: 'development', so it does not stray into production-build territory.

Verified green on fcde281

npm ci from the lockfile in pg-manual, then plain webpack and webpack --fail-on-warnings both exit 0 with zero warnings on the untouched tree. The negative cases above were produced by editing the tree, then reverted — the branch is unmodified.

Comment thread pg-manual/package.json Outdated
Comment thread pg-manual/check/sdk-exports.js Outdated
Comment thread CLAUDE.md Outdated
`build` ran bare `webpack`, which reports a missing static import as a
warning and still exits 0. Both breakages recorded in CLAUDE.md's
pg-manual notes (web-streams-polyfill v3->v4, yivi 0.2->1.x) are that
shape, so the CI gate would have gone green on either.

Measured in pg-manual: re-applying the web-streams-polyfill rename to
examples/file.js leaves `npm run build` at exit 0 without the flag and
exits 1 with it, while the untouched tree compiles with zero warnings
either way.

The flag on `build` only reaches static imports, so `check` still
carries the dynamic `@e4a/pg-wasm` half. CLAUDE.md now splits the two
claims instead of implying the flag is not useful here, and the
sdk-exports.js comment names both edits a new probe name needs: an
import-only addition is elided and keeps the check at exit 0.
@dobby-coder

dobby-coder Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

Cycle 2 addressed in 7963a9b.

Blocker, the static-import half: confirmed and fixed. pg-manual's build is now webpack --fail-on-warnings. I re-ran your measurement before applying it and got the same result: re-applying the web-streams-polyfill rename at examples/file.js:2 takes npm run build from exit 0 to exit 1, and the untouched tree compiles with zero warnings either way.

The one thing worth adding to your table is the row that shows why check stays:

Tree npm run build (with flag) npm run check
clean 0 0
file.js:2 static rename 1 0
seal dropped from the installed SDK 0 1

One script per half, no overlap. ci.yml already runs both steps, so the YAML is unchanged again.

The two nits: both applied. sdk-exports.js now says a new name goes in the import and the object literal, since an import-only addition is elided (verified with sealNewThing: import only, exit 0 and the name never appears in the output; both places, exit 1). CLAUDE.md:33 is split into one bullet per script, so the dynamic-import caveat no longer reads as an argument against the flag.

All three inline threads are replied to and resolved.

Re-ran after the change: npm ci plus build, check and lint across the three node sub-projects, dotnet restore --locked-mode plus dotnet build --no-restore on net8.0 and net10.0, and the pg-manual canary leg against @e4a/pg-wasm@latest with the new flag on. All exit 0, so the flag does not make the canary fail against 0.6.1. Merged origin/main first, already up to date.

The workflow-YAML comment's closing section is rewritten to match, since it carried the same framing the CLAUDE.md nit was about. The YAML itself is untouched.

The bullet said the flag turns "both breakages recorded under pg-manual
build notes" into a failed build. Only one of the two is a webpack
warning. Measured on this branch by reintroducing each breakage and
running npm run build:

- web-streams-polyfill rename, `import { PolyfilledWritableStream }`:
  exit 1, "export 'PolyfilledWritableStream' ... was not found". Caught.
- yivi, `import * as YiviCore from '@privacybydesign/yivi-core'`:
  exit 0, zero warnings, "compiled successfully". npm run check also
  exit 0. Not caught.

`import * as` is valid ESM regardless of what the module exports, so
webpack emits nothing; yivi-core 1.0.0 is type: module, and the
namespace object throws TypeError: not a constructor at
`new YiviCore(...)` in examples/utils.js:63. That is a runtime failure
no build gate sees, and check/sdk-exports.js cannot close it either
because the mistake is in this repo's import form rather than in the
SDK's export list.

Scopes the sentence to what was measured. No code change.

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

@dobby-coder dobby-coder Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: approve

Rule sweep is clean. The one finding from the review pass was real, and I fixed it in b2f9cff rather than sending the PR back, since it was a one-paragraph docs correction and this is the third cycle.

The finding, confirmed then fixed

CLAUDE.md:33 said --fail-on-warnings turns "both breakages recorded under pg-manual build notes" into a failed build. I reintroduced each breakage on the branch and ran the gates. Only one of the two is a webpack warning:

Reintroduced breakage npm run build npm run check
web-streams-polyfill rename, import { PolyfilledWritableStream } exit 1, export 'PolyfilledWritableStream' ... was not found exit 0
yivi, import * as YiviCore from '@privacybydesign/yivi-core' exit 0, zero warnings, "compiled successfully" exit 0

import * as is valid ESM whatever the module exports, so webpack has nothing to warn about. @privacybydesign/yivi-core 1.0.0 is type: module, and its namespace object is not callable:

typeof NS = object
keys = StateMachineImpl,YiviCore,transitions
new NS() threw: TypeError: NS is not a constructor

That is exactly what examples/utils.js:63 does with new YiviCore({...}). So the yivi class of breakage fails at runtime, and no build gate in this repo covers it. check/sdk-exports.js can't close it either, because the mistake is in this repo's own import form rather than in the SDK's export list. Only running the example would catch it, which is out of scope here.

b2f9cff scopes the sentence to the class the flag does catch and says plainly that the yivi class is uncovered. No code changed, and build plus check still exit 0 in pg-manual and pg-node after the edit.

This mattered because CLAUDE.md is what the next agent trusts. As written it claimed CI covers a breakage class it does not.

Rule sweep

Checked the rules that apply to a CI/lockfile diff on an example repo: PR-title conventional commits, closing-keyword usage, reviewer assignment, cross-repo link format, draft policy, the workflows-permission constraint, the no-build-CI repo caveat, Actions default-shell pipefail, the gh empty-tag trap, example-apps-no-production-build, the writing rules, and no-justification-paragraphs. No violations.

Two worth spelling out, because both look like violations at a glance and are not:

  • Part of #65 instead of Closes #65 is correct here. The issue is not finished until a maintainer applies the YAML, and the PR body says so.
  • Shipping the workflow YAML in a comment rather than the diff is the required pattern, not missing work. dobby-coder[bot] has no workflows permission.

I also spot-checked CLAUDE.md's new install-and-build table against the actual package.json files rather than trusting it: pg-manual has build and check, pg-node has check, pg-sveltekit has build, check and lint, and pg-dotnet carries RestorePackagesWithLockFile. The table is accurate.

The workflow YAML in the comment

GitHub cannot anchor an inline comment to a path outside the diff, so the YAML review goes here. It is also the half a diff review would normally miss, since it lands via a maintainer's commit.

Both files read clean. The report job is the only run: block with real shell in it, and it opens with set -euo pipefail, so the Actions default-shell gap (bash -e without pipefail) does not apply. It also contains no pipe, so there was nothing for that gap to swallow.

I ran that job's shell body against a stubbed gh to check the parts static reading cannot settle. The heredoc de-indents correctly out of the YAML block scalar, the escaped backticks survive as literal backticks, $RUN_URL expands, the no-existing-issue branch reaches gh issue create, and the block exits 0. No version-from-file feeds into gh, so the empty-tag-resolves-to-latest trap does not apply either.

One note for whoever applies it, not a defect: permissions: issues: write is scoped to the report job only, which is right.

State

Flipping out of draft. The repo has no build CI yet, which is the point of #65, so the verification in the PR body is local and a maintainer still needs to paste the two YAML files.

@rubenhensen
rubenhensen merged commit 7f22cda into main Jul 27, 2026
5 checks passed
@dobby-coder
dobby-coder Bot deleted the ci/build-matrix-and-sdk-canary branch July 27, 2026 20:12
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant