Skip to content

ci: publish versioned packages with verified tarballs and OIDC - #206

Merged
kjgbot merged 1 commit into
mainfrom
feat/publish-packages
Sep 6, 2026
Merged

ci: publish versioned packages with verified tarballs and OIDC#206
kjgbot merged 1 commit into
mainfrom
feat/publish-packages

Conversation

@miyaontherelay

Copy link
Copy Markdown
Contributor

Adds a dispatch-only npm release pipeline for the three packages moved by #205. The workflow follows relayfile's publish workflow: build/version once, pass artifacts to publishing, then commit release versions and create a GitHub Release after successful publication.

Stacked on #205 (refactor/packages-layout); this PR contains only the release workflow, its tooling/tests, and package repository-directory corrections.

  • dry_run defaults to true. The six dispatch inputs are package, version, custom_version, preid, dry_run, and tag. As in the reference, real releases require package=all; individual selections support dry runs because every package's version advances together.
  • SDK is the single version anchor. All four internal dependency types are rewritten to the exact new version. Surface is built and packed first, then installed into the SDK with --no-save --package-lock=false; the build asserts that the installed surface is not a symlink. Published manifests contain registry versions.
  • Both jobs run actual npm pack, unpack the tarballs, and assert entrypoints, declarations, versions, internal dependencies, and executable bits. Runtime checks require both nonempty binaries and execute the unpacked daemon plus the compiled CLI against the deterministic fixture, including dry runs. All checks precede the first publish.
  • Runtime uses cargo build --locked --release -p relayflowd and bun build packages/sdk/src/cli-executable.ts --compile --target=bun-linux-x64. Publication is sequential: surface, SDK, runtime.
  • Publishing uses npm 11, contents: write, id-token: write, and npm publish --access public --provenance --ignore-scripts; no npm token. Configure each package's npm trusted publisher for AgentWorkforce/flows, workflow publish.yml before a real release. Release lockfile generation explicitly resolves the newly published surface and rejects a remaining local link.

Validation was run on macOS arm64. The Linux build/smoke steps and hosted OIDC authentication have not been executed here; Docker did not respond. No package was published, release created, or PR merged. The hash-pinned fixtures, historical records, SDK path-depth code, review-swarm workflow, and swarm scripts are unchanged.

Validation commands and captured output excerpts (full suites, no changes to their tests):

cd packages/sdk
PATH=/Users/khaliqgant/.bun/bin:$PATH RELAYFLOWS_ALLOW_ANALYZER_SKIP=1 ./node_modules/.bin/vitest run --maxWorkers=1 --minWorkers=1
 Test Files  32 passed | 1 skipped (33)
      Tests  662 passed | 3 skipped (665)
   Duration  148.33s (transform 1.18s, setup 0ms, collect 5.34s, tests 122.23s, environment 12ms, prepare 3.79s)

The initial default-worker run overlapped the kernel workload and failed two timing bounds. It is not being reported as green:

PATH=/Users/khaliqgant/.bun/bin:$PATH RELAYFLOWS_ALLOW_ANALYZER_SKIP=1 ./node_modules/.bin/vitest run
 Test Files  2 failed | 30 passed | 1 skipped (33)
      Tests  2 failed | 660 passed | 3 skipped (665)
AssertionError: expected 1663 to be less than 1000
"CLI \"/var/folders/_z/f_fpl8j533g_r63706k2xvp00000gn/T/flows-wrapper-FpcXK1/leaky-wrapper\" did not identify as relayflows-agent-cli-v1 within 2000ms."

The rerun changed only worker concurrency. The real Claude analyzer also ran successfully; the three skips are the existing real-CLI-adapter cases. RELAYFLOWS_ALLOW_ANALYZER_SKIP=1 permits unavailable-analyzer diagnostics and does not force that case to skip.

node --test scripts/publish.test.mjs
✔ one SDK anchor rewrites all internal dependency types and preserves external ranges (1658.117875ms)
✔ prerelease bumps use the SDK anchor and output the resolved version (1346.873625ms)
✔ invalid custom versions fail before any package changes (980.328667ms)
✔ actual npm tarballs reject missing dist and local dependencies, then accept built surface (5491.395167ms)
✔ runtime tarball refuses an unstaged binary package (1599.046625ms)
ℹ tests 5
ℹ suites 0
ℹ pass 5
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 11500.398667
node scripts/pack-release.mjs surface
node scripts/pack-release.mjs sdk
PACK_OK @relayflows/surface@2.0.0: package/dist/index.js, package/dist/index.d.ts, package/dist/runtime.js, package/dist/runtime.d.ts
PACK_OK @relayflows/sdk@2.0.0: package/dist/index.js, package/dist/index.d.ts, package/dist/cli.js
npm publish dist/publish/relayflows-surface-2.0.0.tgz --dry-run --access public --provenance --ignore-scripts --tag latest
npm publish dist/publish/relayflows-sdk-2.0.0.tgz --dry-run --access public --provenance --ignore-scripts --tag latest
npm notice Publishing to https://registry.npmjs.org/ with tag latest and public access (dry-run)
+ @relayflows/surface@2.0.0
npm notice Publishing to https://registry.npmjs.org/ with tag latest and public access (dry-run)
+ @relayflows/sdk@2.0.0

actionlint .github/workflows/publish.yml exited 0 with no diagnostics; git diff --check exited 0 with no output.

Final kernel rerun at committed head 076727a06b3d700d0b85bf6473d0a8fbc014bd17: 165 passed, 0 failed across the workspace.

cd kernel
PATH=/Users/khaliqgant/.cargo/bin:$PATH sh ../ops/cargo.sh test --workspace
test result: ok. 35 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.70s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 34 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 38.79s
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.17s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.19s
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 14.77s
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.28s
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s
test result: ok. 53 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.19s
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s
test result: ok. 24 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.08s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

@coderabbitai

coderabbitai Bot commented Sep 6, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: c8cd9339-89de-411c-81d5-a14df164ac45


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

3 issues found across 6 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/pack-release.mjs">

<violation number="1" location="scripts/pack-release.mjs:13">
P1: The `--ignore-scripts` option does not suppress npm's `prepare` hook during `npm pack`. This runs surface's Bun build and SDK's TypeScript build; the release-tooling fixture and publish job lack that build environment, so the workflow fails before validation or publication. Pack the already-built artifacts without lifecycle preparation, or remove the redundant repack.</violation>
</file>

<file name="scripts/version-packages.mjs">

<violation number="1" location="scripts/version-packages.mjs:6">
P2: Custom versions accepted by this regex can be silently normalized by `npm version`, so the workflow publishes and tags a version different from the requested `custom_version`. Reject noncanonical inputs or verify that npm's resolved version exactly matches the requested value before propagating it.</violation>
</file>

<file name=".github/workflows/publish.yml">

<violation number="1" location=".github/workflows/publish.yml:217">
P1: When the selected branch advances after dispatch, this push rejects because the job publishes artifacts built from stale `github.sha`. That failure occurs after npm publish, leaving immutable package versions without the commit, tag, or GitHub Release. Reserve or verify the branch ref before publishing and use a release-specific ref to prevent this partial release.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread scripts/pack-release.mjs
const destination = resolve(output);
mkdirSync(destination, { recursive: true });
const [packed] = JSON.parse(execFileSync('npm', [
'pack', '--ignore-scripts', '--json', '--pack-destination', destination,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: The --ignore-scripts option does not suppress npm's prepare hook during npm pack. This runs surface's Bun build and SDK's TypeScript build; the release-tooling fixture and publish job lack that build environment, so the workflow fails before validation or publication. Pack the already-built artifacts without lifecycle preparation, or remove the redundant repack.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/pack-release.mjs, line 13:

<comment>The `--ignore-scripts` option does not suppress npm's `prepare` hook during `npm pack`. This runs surface's Bun build and SDK's TypeScript build; the release-tooling fixture and publish job lack that build environment, so the workflow fails before validation or publication. Pack the already-built artifacts without lifecycle preparation, or remove the redundant repack.</comment>

<file context>
@@ -0,0 +1,60 @@
+const destination = resolve(output);
+mkdirSync(destination, { recursive: true });
+const [packed] = JSON.parse(execFileSync('npm', [
+  'pack', '--ignore-scripts', '--json', '--pack-destination', destination,
+], { cwd: directory, encoding: 'utf8' }));
+const archive = join(destination, packed.filename);
</file context>

git commit -m "chore(release): v${NEW_VERSION}"
fi
git tag -a "v${NEW_VERSION}" -m "Release v${NEW_VERSION}"
git push --atomic origin "HEAD:refs/heads/${RELEASE_BRANCH}" "refs/tags/v${NEW_VERSION}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: When the selected branch advances after dispatch, this push rejects because the job publishes artifacts built from stale github.sha. That failure occurs after npm publish, leaving immutable package versions without the commit, tag, or GitHub Release. Reserve or verify the branch ref before publishing and use a release-specific ref to prevent this partial release.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/publish.yml, line 217:

<comment>When the selected branch advances after dispatch, this push rejects because the job publishes artifacts built from stale `github.sha`. That failure occurs after npm publish, leaving immutable package versions without the commit, tag, or GitHub Release. Reserve or verify the branch ref before publishing and use a release-specific ref to prevent this partial release.</comment>

<file context>
@@ -0,0 +1,242 @@
+            git commit -m "chore(release): v${NEW_VERSION}"
+          fi
+          git tag -a "v${NEW_VERSION}" -m "Release v${NEW_VERSION}"
+          git push --atomic origin "HEAD:refs/heads/${RELEASE_BRANCH}" "refs/tags/v${NEW_VERSION}"
+      - name: Create GitHub Release
+        if: ${{ !inputs.dry_run }}
</file context>


// The SDK is the version anchor; no package independently computes a bump.
const paths = ['surface', 'sdk', 'runtime-linux-x64'].map((name) => `packages/${name}/package.json`);
if (process.env.CUSTOM_VERSION && !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(process.env.CUSTOM_VERSION)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Custom versions accepted by this regex can be silently normalized by npm version, so the workflow publishes and tags a version different from the requested custom_version. Reject noncanonical inputs or verify that npm's resolved version exactly matches the requested value before propagating it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/version-packages.mjs, line 6:

<comment>Custom versions accepted by this regex can be silently normalized by `npm version`, so the workflow publishes and tags a version different from the requested `custom_version`. Reject noncanonical inputs or verify that npm's resolved version exactly matches the requested value before propagating it.</comment>

<file context>
@@ -0,0 +1,32 @@
+
+// The SDK is the version anchor; no package independently computes a bump.
+const paths = ['surface', 'sdk', 'runtime-linux-x64'].map((name) => `packages/${name}/package.json`);
+if (process.env.CUSTOM_VERSION && !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(process.env.CUSTOM_VERSION)) {
+  throw new Error('custom_version must be a semantic version');
+}
</file context>

kjgbot pushed a commit that referenced this pull request Sep 6, 2026
CI on #206 failed with:

    Build authoring surface
    error: working directory '/home/runner/work/flows/flows/surface': No such file or directory

Same blind spot as `cd sdk`: my rewrite pattern required a trailing slash, so
every bare directory VALUE was invisible to it. Five remained:

  .github/workflows/cloud-runtime-artifact.yml  working-directory: surface
                                                npm ci --prefix sdk
                                                working-directory: sdk
  testdata/backlog-picker.flow.yaml             cd sdk && npm ci ...
  workflows/bootstrap-gate1.yaml                cd sdk && npm install ...

backlog-picker.flow.yaml is safe to edit: unlike tick-heartbeat and hello-ladder
it has no pinned sha256, so changing it breaks no spec-parity assertion.

Three layers of verification missed these and CI did not: the vitest suite never
runs these files, the structure lens read the diff rather than executing it, and
surface-package-gate.sh does not touch cloud-runtime-artifact.yml. The first
thing to actually run this workflow found it immediately, which is the argument
for landing a branch's CI before trusting a local green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
kjgbot pushed a commit that referenced this pull request Sep 6, 2026
… misses

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
kjgbot pushed a commit that referenced this pull request Sep 6, 2026
This reverts d380008 from the layout branch. Not a judgement on the work — it is
a duplicate, and it landed in the wrong place.

I spawned two codex agents on the same task after misreading the first as dead.
Both delivered a release pipeline: the finn-mini run opened #206 as a proper
stacked PR, and the sf-mini run committed a second, independent implementation
directly onto this branch. That left #205 carrying both a layout move and a
release pipeline, which is two changes in one PR and makes the layout
impossible to review on its own.

#205 goes back to being only the move. #206 carries the pipeline and is stacked
on it, which is the shape its own description already claims. The reverted work
remains in this branch's history at d380008 if it turns out to be the better of
the two implementations — that comparison is worth making on the merits, not
settling by which agent pushed last.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
@kjgbot

kjgbot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Reviewed. This is good work, and it solves the problem I had hacked around.

Disclosure: I wrote the brief this was built from, and I wrote the interim workflow it replaces. So I have reviewed it against the requirements rather than against my own earlier attempt, which was worse.

The thing I most wanted to see, and it is here

My discarded version built the surface and symlinked it so the SDK could compile against a sibling whose manifest pointed at the registry. That is a workaround. This does it properly:

  • Build SDK against packed surface — the SDK is compiled against the actual tarball, so what is verified is the artifact that gets published, not a working tree that happens to be adjacent.
  • Regenerate release lockfiles, asserting !surface.link and "release lockfile must resolve the published surface" — that directly guards the failure mode my hack would have papered over. A published SDK that silently resolved a local link would have shipped broken and nobody would have known until a consumer installed it.

version-packages.mjs is the relayfile mechanism done correctly: sdk as the single anchor, semver validation on custom_version, --no-git-tag-version --ignore-scripts --package-lock=false, then a name-set pass that rewrites dependencies/devDependencies/optionalDependencies/peerDependencies to the new version for any sibling package. That is exactly why the manifests must not ship file: deps, and it removes the need for the linking entirely.

Requirements check

required status
six dispatch inputs, dry_run default true present
contents: write + id-token: write, no npm token present, zero token references
provenance publish present
version anchor + internal dep rewrite version-packages.mjs
publish in dependency order Publish to NPM (surface before SDK), for package in surface sdk runtime-linux-x64
pack and assert tarball contents asserted in both build and publish-packages
runtime binaries built AND executed Pack and assert runtime (executes both unpacked binaries)

Beyond the brief: Validate release mode, a Test release tooling step that exercises the release scripts themselves, lockfile regeneration with assertions, tag + GitHub Release, and a summary. I asked for a pipeline and got one with its own tests.

I was wrong in one of my own notes about this PR. I recorded it as "a single job where relayfile uses ordered ones" from a grep of the file. It is two jobs — build then publish-packages — with the ordering inside the publish step. I counted jobs with a pattern instead of parsing the workflow.

What is actually blocking it

Its three red checks are inherited from my bug, not from this work. #206 is stacked on #205, and #205 had stale paths that failed linux-x64-artifact and packed-consumer. Those are fixed now (5cdab76, 235b948) and #205 is green on everything except the repo-wide review gate.

This needs a rebase onto current #205 for its CI to say anything meaningful. I have not rebased it myself — it is your branch and I have already caused one collision today by having two agents on this task.

One question, not a blocker

Commit version bump and create tag pushes to the repo during a release. Worth confirming that lands on the intended branch when the workflow is dispatched from a non-default ref, since dry_run: false is the only guard in front of it.

kjgbot pushed a commit that referenced this pull request Sep 6, 2026
…y version

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
@kjgbot
kjgbot force-pushed the feat/publish-packages branch from 076727a to c97c8b4 Compare September 6, 2026 12:52
kjgbot pushed a commit that referenced this pull request Sep 6, 2026
…eeded on the API-key path

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
kjgbot added a commit that referenced this pull request Sep 6, 2026
* refactor(layout): move sdk/ and surface/ under packages/

Consolidates the layout onto the convention relay and relayfile use, so all
three publishable packages sit together:

    packages/sdk
    packages/surface
    packages/runtime-linux-x64

Scope note: the headline is 138 files and ~1557 references, but only 19 needed
changing. The rest live in ops/reviews transcripts, DRIVE-LOG, briefs and past
run reports — records of what was true when written, deliberately left alone
rather than rewritten to match a layout that did not exist yet.

The subtle half is path arithmetic inside the moved trees. Tests reached the
repo root with two levels of `..`, which now lands at packages/. Every
repo-root climb is now three levels; package-relative single-`..` uses
(RELAYFLOWS_SDK_DIST, dist/cli.js) are unchanged and must stay that way.

testdata/tick-heartbeat.* and hello-ladder.* are reverted rather than updated:
their content is pinned by a sha256 shared across the SDK/kernel spec-parity
boundary, so rewriting a comment inside them breaks the hash. Their prose now
names the old paths; correcting that means regenerating the pinned hash on both
sides, which is a separate change.

Verified in the moved layout:

    kernel:  cargo test --workspace   165 passed, 0 failed
    sdk:     vitest run               662 passed, 3 skipped, 0 failed
             (including the real-analyzer gate-2 case and the daemon-kill
              resume case, both of which exercise repo-root paths)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

* fix(layout): migrate the path references the first sweep could not see

The structure lens failed this branch and was right. My rewrite used a negative
lookbehind excluding `/` and `.`, which skipped every PATH-PREFIXED reference —
`./sdk/dist`, `$repo_root/sdk`, `../surface/src`, `${REPO}/sdk/dist` — and
`cd sdk` has no trailing slash to match at all. Those are exactly the executable
ones.

Five live paths were left pointing at directories that no longer exist:

- workflows/drive.yaml and drive-cloud.yaml: the guards were migrated
  (`[ -f packages/sdk/package.json ]`) but the bodies were not, so `cd sdk`
  short-circuited npm ci and npm test, and `require("./sdk/dist/index.js")`
  threw. The drive verify gate was broken by its own migration.
- scripts/surface-package-gate.sh: `$repo_root/surface` and `$repo_root/sdk`,
  the body of the surface-package gate whose paths filter had already moved.
- regressions/tsconfig.json and examples/research/tsconfig.json.
- ops/probes/pr134-repair-0903/*.mjs, where one file had its println migrated
  and its imports left behind.

`@relayflows/surface` specifiers are package names, not paths, and are untouched.

Worth recording why the suites did not catch this: 662 SDK and 165 kernel tests
pass either way, because none of them runs drive.yaml, the gate script, the
regressions tsconfig, or the probes. A green suite was never going to see it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

* fix(layout): the two depth-dependent paths inside the moved manifests

Running scripts/surface-package-gate.sh found what neither suite could:

    $ tsc -p ../regressions/tsconfig.json
    error TS5058: The specified path does not exist: '../regressions/tsconfig.json'

From packages/surface, `../regressions` is now packages/regressions. Same class
as the test climbs, in a package.json script rather than a test file.

- packages/surface `typecheck:regressions`: ../regressions -> ../../regressions
- packages/sdk `test:prep`: ../kernel -> ../../kernel, ../testdata -> ../../testdata.
  `sh ../ops/cargo.sh` inside it is NOT changed: it runs after `cd` into kernel/,
  which did not move, so it is already repo-root relative.

Why the 662-test run missed it: I invoked vitest directly, so `npm test` never
ran, so `test:prep` never ran. Bypassing the package's own entry point to run its
tests skips the part of the package that knows where things are.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

* fix(layout): the no-trailing-slash path references CI caught

CI on #206 failed with:

    Build authoring surface
    error: working directory '/home/runner/work/flows/flows/surface': No such file or directory

Same blind spot as `cd sdk`: my rewrite pattern required a trailing slash, so
every bare directory VALUE was invisible to it. Five remained:

  .github/workflows/cloud-runtime-artifact.yml  working-directory: surface
                                                npm ci --prefix sdk
                                                working-directory: sdk
  testdata/backlog-picker.flow.yaml             cd sdk && npm ci ...
  workflows/bootstrap-gate1.yaml                cd sdk && npm install ...

backlog-picker.flow.yaml is safe to edit: unlike tick-heartbeat and hello-ladder
it has no pinned sha256, so changing it breaks no spec-parity assertion.

Three layers of verification missed these and CI did not: the vitest suite never
runs these files, the structure lens read the diff rather than executing it, and
surface-package-gate.sh does not touch cloud-runtime-artifact.yml. The first
thing to actually run this workflow found it immediately, which is the argument
for landing a branch's CI before trusting a local green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

* feat(release): build and verify ordered OIDC npm releases

Session-Id: 01a0766a-1a37-73d1-9f4f-f3f5e435c735

* Revert "feat(release): build and verify ordered OIDC npm releases"

This reverts d380008 from the layout branch. Not a judgement on the work — it is
a duplicate, and it landed in the wrong place.

I spawned two codex agents on the same task after misreading the first as dead.
Both delivered a release pipeline: the finn-mini run opened #206 as a proper
stacked PR, and the sf-mini run committed a second, independent implementation
directly onto this branch. That left #205 carrying both a layout move and a
release pipeline, which is two changes in one PR and makes the layout
impossible to review on its own.

#205 goes back to being only the move. #206 carries the pipeline and is stacked
on it, which is the shape its own description already claims. The reverted work
remains in this branch's history at d380008 if it turns out to be the better of
the two implementations — that comparison is worth making on the merits, not
settling by which agent pushed last.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

* fix(layout): update backlog-picker's canonical spec alongside its flow

CI caught this on #205:

    AssertionError: step "build-sdk" diverges from the canonical spec:
    expected 'cd sdk && npm ci --silent …' to be 'cd packages/sdk && npm ci --silent …'

testdata/backlog-picker.flow.yaml and backlog-picker.spec.canonical.json are a
compiled pair, and spec-parity compares them. I changed the flow's `cd sdk` and
left the canonical spec behind, so the two disagreed.

Unlike tick-heartbeat and hello-ladder, this pair carries no pinned sha256 — so
editing both is the correct fix rather than reverting, and nothing downstream
depends on the old bytes.

    vitest tests/spec-parity tests/backlog-picker*  ->  45 passed

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

---------

Co-authored-by: kjgbot <kjgbot@agentrelay.dev>
@kjgbot
kjgbot changed the base branch from refactor/packages-layout to main September 6, 2026 12:54
Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
@kjgbot
kjgbot force-pushed the feat/publish-packages branch from c97c8b4 to 1b981b1 Compare September 6, 2026 13:02
kjgbot pushed a commit that referenced this pull request Sep 6, 2026
… credential

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
@kjgbot
kjgbot merged commit f233c22 into main Sep 6, 2026
4 of 5 checks passed
@kjgbot
kjgbot deleted the feat/publish-packages branch September 6, 2026 13:18
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