From 1cf7bdc0d314108f3bc8b245a08252dd2e940186 Mon Sep 17 00:00:00 2001 From: kjgbot Date: Sun, 6 Sep 2026 12:27:51 +0200 Subject: [PATCH 1/7] refactor(layout): move sdk/ and surface/ under packages/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1 --- .github/workflows/cloud-runtime-artifact.yml | 6 ++--- .github/workflows/surface-package.yml | 8 +++---- README.md | 4 ++-- docs/SURFACE.md | 2 +- docs/bootstrap-report.md | 10 ++++----- examples/research/shims/agent-cli.ts | 4 ++-- kernel/relayflowd-core/src/schema.rs | 2 +- kernel/relayflowd-core/tests/spec_parity.rs | 2 +- ops/FORBIDDEN_PATHS | 2 +- ops/cargo.sh | 2 +- ops/probes/pr134-repair-0903/harness.mjs | 2 +- .../pr134-repair-0903/verb-output-fields.mjs | 2 +- {sdk => packages/sdk}/package-lock.json | 0 {sdk => packages/sdk}/package.json | 0 .../sdk}/scripts/make-cli-executable.mjs | 0 .../sdk}/scripts/prune-test-build.mjs | 0 {sdk => packages/sdk}/scripts/test.sh | 0 .../sdk}/src/authored-flow-error.ts | 0 .../sdk}/src/authored-flow-executor.ts | 0 .../sdk}/src/authored-flow-lifecycle.ts | 0 .../sdk}/src/authored-flow-loader.ts | 0 .../sdk}/src/authored-flow-operation.ts | 0 {sdk => packages/sdk}/src/authored-flow.ts | 0 .../sdk}/src/authored-promise-graph.ts | 0 {sdk => packages/sdk}/src/backlog-picker.ts | 0 {sdk => packages/sdk}/src/canonical.ts | 0 {sdk => packages/sdk}/src/cli-adapter.ts | 0 {sdk => packages/sdk}/src/cli-executable.ts | 0 {sdk => packages/sdk}/src/cli.ts | 0 {sdk => packages/sdk}/src/cli/check.ts | 0 {sdk => packages/sdk}/src/cli/direct-run.ts | 0 {sdk => packages/sdk}/src/cli/hn-monitor.ts | 0 .../sdk}/src/cli/interruptible-sleep.ts | 0 {sdk => packages/sdk}/src/cli/run.ts | 0 {sdk => packages/sdk}/src/cli/tick-runner.ts | 0 {sdk => packages/sdk}/src/compile.ts | 0 {sdk => packages/sdk}/src/demo-hn-monitor.ts | 0 .../sdk}/src/dir-watcher-poller.ts | 0 {sdk => packages/sdk}/src/direct-input.ts | 0 {sdk => packages/sdk}/src/failure-kinds.ts | 0 {sdk => packages/sdk}/src/gate-contract.ts | 0 {sdk => packages/sdk}/src/hn-poller.ts | 0 {sdk => packages/sdk}/src/index.ts | 0 {sdk => packages/sdk}/src/journal-client.ts | 0 .../sdk}/src/json-schema-bound.ts | 0 {sdk => packages/sdk}/src/json-schema.ts | 0 {sdk => packages/sdk}/src/json-value.ts | 0 {sdk => packages/sdk}/src/model-name.ts | 0 {sdk => packages/sdk}/src/output-schema.ts | 0 {sdk => packages/sdk}/src/preflight.ts | 0 {sdk => packages/sdk}/src/protocol.ts | 0 {sdk => packages/sdk}/src/spec.ts | 0 .../sdk}/src/step-dependencies.ts | 0 {sdk => packages/sdk}/src/step-fields.ts | 0 {sdk => packages/sdk}/src/tick-source.ts | 0 {sdk => packages/sdk}/src/unknown-keys.ts | 0 {sdk => packages/sdk}/src/validate.ts | 0 .../sdk}/src/work-package-consumer.ts | 0 .../sdk}/src/work-package-validator.ts | 0 {sdk => packages/sdk}/src/worker-cli.ts | 0 {sdk => packages/sdk}/src/worker.ts | 0 {sdk => packages/sdk}/src/wrapper-runtime.ts | 0 {sdk => packages/sdk}/src/wrapper-session.ts | 0 .../authored-flow-lifecycle-executor.test.ts | 0 .../tests/authored-flow-operation.test.ts | 0 .../sdk}/tests/authored-flow.test.ts | 0 .../sdk}/tests/backlog-picker-flow.test.ts | 8 +++---- .../sdk}/tests/backlog-picker.test.ts | 4 ++-- {sdk => packages/sdk}/tests/bin.test.ts | 4 ++-- .../sdk}/tests/classify-outcome.test.ts | 0 .../sdk}/tests/cli-adapter.test.ts | 0 .../sdk}/tests/cli-hn-monitor.test.ts | 0 {sdk => packages/sdk}/tests/cli.test.ts | 2 +- .../sdk}/tests/dependency-validation.test.ts | 0 .../sdk}/tests/deterministic-llm.test.ts | 0 .../sdk}/tests/dir-watcher-poller.test.ts | 0 .../sdk}/tests/direct-input.test.ts | 10 ++++----- .../sdk}/tests/fixtures/direct-input.flow.ts | 0 .../fixtures/direct-output-control.flow.ts | 0 .../fixtures/pre-journal-side-effect.flow.ts | 0 .../tests/fixtures/runtime-bridge.flow.ts | 0 .../sdk}/tests/gate-contract.test.ts | 2 +- .../sdk}/tests/hello-deterministic.test.ts | 0 {sdk => packages/sdk}/tests/hn-poller.test.ts | 0 .../sdk}/tests/journal-client-loopback.ts | 0 .../sdk}/tests/journal-client.test.ts | 0 .../sdk}/tests/json-schema-bound.test.ts | 2 +- .../sdk}/tests/live-kernel.test.ts | 4 ++-- .../sdk}/tests/model-selection.test.ts | 0 .../sdk}/tests/parse-json-output.test.ts | 0 {sdk => packages/sdk}/tests/preflight.test.ts | 0 .../sdk}/tests/real-cli-adapters.test.ts | 0 .../sdk}/tests/spec-parity.test.ts | 2 +- .../sdk}/tests/tick-runner.test.ts | 0 .../sdk}/tests/tick-source.test.ts | 2 +- .../sdk}/tests/typed-output.test.ts | 2 +- {sdk => packages/sdk}/tests/validate.test.ts | 0 .../sdk}/tests/verb-field-lint.test.ts | 2 +- .../sdk}/tests/work-package-consumer.test.ts | 2 +- .../sdk}/tests/work-package-validator.test.ts | 0 .../sdk}/tests/worker-cli.test.ts | 0 {sdk => packages/sdk}/tsconfig.json | 0 {sdk => packages/sdk}/tsconfig.tests.json | 0 .../sdk}/tsconfig.type-tests.json | 0 .../sdk}/type-tests/step-fields.ts | 0 {sdk => packages/sdk}/vitest.config.ts | 0 {surface => packages/surface}/README.md | 0 {surface => packages/surface}/bun.lock | 0 {surface => packages/surface}/package.json | 0 {surface => packages/surface}/src/cloud.ts | 0 .../surface}/src/completion.ts | 0 {surface => packages/surface}/src/context.ts | 0 {surface => packages/surface}/src/flow.ts | 0 {surface => packages/surface}/src/index.ts | 0 {surface => packages/surface}/src/runtime.ts | 0 {surface => packages/surface}/src/step.ts | 0 .../surface}/tests/flow.test.ts | 0 {surface => packages/surface}/tsconfig.json | 0 .../surface}/tsconfig.test.json | 0 .../surface}/vitest.config.ts | 0 workflows/bootstrap-gate1.yaml | 4 ++-- workflows/drive-cloud.yaml | 18 +++++++-------- workflows/drive.yaml | 22 +++++++++---------- 123 files changed, 68 insertions(+), 68 deletions(-) rename {sdk => packages/sdk}/package-lock.json (100%) rename {sdk => packages/sdk}/package.json (100%) rename {sdk => packages/sdk}/scripts/make-cli-executable.mjs (100%) rename {sdk => packages/sdk}/scripts/prune-test-build.mjs (100%) rename {sdk => packages/sdk}/scripts/test.sh (100%) rename {sdk => packages/sdk}/src/authored-flow-error.ts (100%) rename {sdk => packages/sdk}/src/authored-flow-executor.ts (100%) rename {sdk => packages/sdk}/src/authored-flow-lifecycle.ts (100%) rename {sdk => packages/sdk}/src/authored-flow-loader.ts (100%) rename {sdk => packages/sdk}/src/authored-flow-operation.ts (100%) rename {sdk => packages/sdk}/src/authored-flow.ts (100%) rename {sdk => packages/sdk}/src/authored-promise-graph.ts (100%) rename {sdk => packages/sdk}/src/backlog-picker.ts (100%) rename {sdk => packages/sdk}/src/canonical.ts (100%) rename {sdk => packages/sdk}/src/cli-adapter.ts (100%) rename {sdk => packages/sdk}/src/cli-executable.ts (100%) rename {sdk => packages/sdk}/src/cli.ts (100%) rename {sdk => packages/sdk}/src/cli/check.ts (100%) rename {sdk => packages/sdk}/src/cli/direct-run.ts (100%) rename {sdk => packages/sdk}/src/cli/hn-monitor.ts (100%) rename {sdk => packages/sdk}/src/cli/interruptible-sleep.ts (100%) rename {sdk => packages/sdk}/src/cli/run.ts (100%) rename {sdk => packages/sdk}/src/cli/tick-runner.ts (100%) rename {sdk => packages/sdk}/src/compile.ts (100%) rename {sdk => packages/sdk}/src/demo-hn-monitor.ts (100%) rename {sdk => packages/sdk}/src/dir-watcher-poller.ts (100%) rename {sdk => packages/sdk}/src/direct-input.ts (100%) rename {sdk => packages/sdk}/src/failure-kinds.ts (100%) rename {sdk => packages/sdk}/src/gate-contract.ts (100%) rename {sdk => packages/sdk}/src/hn-poller.ts (100%) rename {sdk => packages/sdk}/src/index.ts (100%) rename {sdk => packages/sdk}/src/journal-client.ts (100%) rename {sdk => packages/sdk}/src/json-schema-bound.ts (100%) rename {sdk => packages/sdk}/src/json-schema.ts (100%) rename {sdk => packages/sdk}/src/json-value.ts (100%) rename {sdk => packages/sdk}/src/model-name.ts (100%) rename {sdk => packages/sdk}/src/output-schema.ts (100%) rename {sdk => packages/sdk}/src/preflight.ts (100%) rename {sdk => packages/sdk}/src/protocol.ts (100%) rename {sdk => packages/sdk}/src/spec.ts (100%) rename {sdk => packages/sdk}/src/step-dependencies.ts (100%) rename {sdk => packages/sdk}/src/step-fields.ts (100%) rename {sdk => packages/sdk}/src/tick-source.ts (100%) rename {sdk => packages/sdk}/src/unknown-keys.ts (100%) rename {sdk => packages/sdk}/src/validate.ts (100%) rename {sdk => packages/sdk}/src/work-package-consumer.ts (100%) rename {sdk => packages/sdk}/src/work-package-validator.ts (100%) rename {sdk => packages/sdk}/src/worker-cli.ts (100%) rename {sdk => packages/sdk}/src/worker.ts (100%) rename {sdk => packages/sdk}/src/wrapper-runtime.ts (100%) rename {sdk => packages/sdk}/src/wrapper-session.ts (100%) rename {sdk => packages/sdk}/tests/authored-flow-lifecycle-executor.test.ts (100%) rename {sdk => packages/sdk}/tests/authored-flow-operation.test.ts (100%) rename {sdk => packages/sdk}/tests/authored-flow.test.ts (100%) rename {sdk => packages/sdk}/tests/backlog-picker-flow.test.ts (97%) rename {sdk => packages/sdk}/tests/backlog-picker.test.ts (98%) rename {sdk => packages/sdk}/tests/bin.test.ts (98%) rename {sdk => packages/sdk}/tests/classify-outcome.test.ts (100%) rename {sdk => packages/sdk}/tests/cli-adapter.test.ts (100%) rename {sdk => packages/sdk}/tests/cli-hn-monitor.test.ts (100%) rename {sdk => packages/sdk}/tests/cli.test.ts (99%) rename {sdk => packages/sdk}/tests/dependency-validation.test.ts (100%) rename {sdk => packages/sdk}/tests/deterministic-llm.test.ts (100%) rename {sdk => packages/sdk}/tests/dir-watcher-poller.test.ts (100%) rename {sdk => packages/sdk}/tests/direct-input.test.ts (95%) rename {sdk => packages/sdk}/tests/fixtures/direct-input.flow.ts (100%) rename {sdk => packages/sdk}/tests/fixtures/direct-output-control.flow.ts (100%) rename {sdk => packages/sdk}/tests/fixtures/pre-journal-side-effect.flow.ts (100%) rename {sdk => packages/sdk}/tests/fixtures/runtime-bridge.flow.ts (100%) rename {sdk => packages/sdk}/tests/gate-contract.test.ts (99%) rename {sdk => packages/sdk}/tests/hello-deterministic.test.ts (100%) rename {sdk => packages/sdk}/tests/hn-poller.test.ts (100%) rename {sdk => packages/sdk}/tests/journal-client-loopback.ts (100%) rename {sdk => packages/sdk}/tests/journal-client.test.ts (100%) rename {sdk => packages/sdk}/tests/json-schema-bound.test.ts (99%) rename {sdk => packages/sdk}/tests/live-kernel.test.ts (99%) rename {sdk => packages/sdk}/tests/model-selection.test.ts (100%) rename {sdk => packages/sdk}/tests/parse-json-output.test.ts (100%) rename {sdk => packages/sdk}/tests/preflight.test.ts (100%) rename {sdk => packages/sdk}/tests/real-cli-adapters.test.ts (100%) rename {sdk => packages/sdk}/tests/spec-parity.test.ts (99%) rename {sdk => packages/sdk}/tests/tick-runner.test.ts (100%) rename {sdk => packages/sdk}/tests/tick-source.test.ts (99%) rename {sdk => packages/sdk}/tests/typed-output.test.ts (99%) rename {sdk => packages/sdk}/tests/validate.test.ts (100%) rename {sdk => packages/sdk}/tests/verb-field-lint.test.ts (99%) rename {sdk => packages/sdk}/tests/work-package-consumer.test.ts (98%) rename {sdk => packages/sdk}/tests/work-package-validator.test.ts (100%) rename {sdk => packages/sdk}/tests/worker-cli.test.ts (100%) rename {sdk => packages/sdk}/tsconfig.json (100%) rename {sdk => packages/sdk}/tsconfig.tests.json (100%) rename {sdk => packages/sdk}/tsconfig.type-tests.json (100%) rename {sdk => packages/sdk}/type-tests/step-fields.ts (100%) rename {sdk => packages/sdk}/vitest.config.ts (100%) rename {surface => packages/surface}/README.md (100%) rename {surface => packages/surface}/bun.lock (100%) rename {surface => packages/surface}/package.json (100%) rename {surface => packages/surface}/src/cloud.ts (100%) rename {surface => packages/surface}/src/completion.ts (100%) rename {surface => packages/surface}/src/context.ts (100%) rename {surface => packages/surface}/src/flow.ts (100%) rename {surface => packages/surface}/src/index.ts (100%) rename {surface => packages/surface}/src/runtime.ts (100%) rename {surface => packages/surface}/src/step.ts (100%) rename {surface => packages/surface}/tests/flow.test.ts (100%) rename {surface => packages/surface}/tsconfig.json (100%) rename {surface => packages/surface}/tsconfig.test.json (100%) rename {surface => packages/surface}/vitest.config.ts (100%) diff --git a/.github/workflows/cloud-runtime-artifact.yml b/.github/workflows/cloud-runtime-artifact.yml index 925c260ea..8cfab4e15 100644 --- a/.github/workflows/cloud-runtime-artifact.yml +++ b/.github/workflows/cloud-runtime-artifact.yml @@ -20,7 +20,7 @@ on: paths: - ".github/workflows/cloud-runtime-artifact.yml" - "kernel/**" - - "sdk/**" + - "packages/sdk/**" - "scripts/cloud-artifact.mjs" - "scripts/cloud-artifact.test.mjs" - "testdata/**" @@ -128,7 +128,7 @@ jobs: # second, debug copy through a wrapper that cannot run here. # # The build is still required: several test files fail at collection - # without sdk/dist, which is why a bare `vitest run` is not enough. + # without packages/sdk/dist, which is why a bare `vitest run` is not enough. # test:prep's other half, kept: it re-asserts the executable bit on the # preflight CLI fixtures. They are committed 100755 so actions/checkout # already restores them, but the guard is one line and the failure it @@ -144,7 +144,7 @@ jobs: - name: Build standalone flows CLI run: | mkdir -p dist/cloud-artifact-input - bun build sdk/src/cli-executable.ts \ + bun build packages/sdk/src/cli-executable.ts \ --compile \ --target=bun-linux-x64 \ --outfile=dist/cloud-artifact-input/flows diff --git a/.github/workflows/surface-package.yml b/.github/workflows/surface-package.yml index 7aa60c0c5..000f3dbbb 100644 --- a/.github/workflows/surface-package.yml +++ b/.github/workflows/surface-package.yml @@ -6,18 +6,18 @@ on: paths: - ".github/workflows/surface-package.yml" - "scripts/surface-package-gate.sh" - - "surface/**" + - "packages/surface/**" - "regressions/**" - - "sdk/**" + - "packages/sdk/**" push: branches: - main paths: - ".github/workflows/surface-package.yml" - "scripts/surface-package-gate.sh" - - "surface/**" + - "packages/surface/**" - "regressions/**" - - "sdk/**" + - "packages/sdk/**" permissions: contents: read diff --git a/README.md b/README.md index a536c4a08..0ea1ae575 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ Nothing in this repo may contradict it; changing it is a human decision. ``` kernel/ relayflowd — Rust. Journal, scheduler, leases, timers, streams. One binary. -sdk/ TypeScript-first authoring SDK. Compiles specs; speaks the journal protocol. -surface/ @relayflows/surface — the TypeScript flow-authoring contract. +packages/sdk/ TypeScript-first authoring SDK. Compiles specs; speaks the journal protocol. +packages/surface/ @relayflows/surface — the TypeScript flow-authoring contract. workflows/ The gates. Each gate is a relayflow; the build is orchestrated by relayflows. docs/ RFC-0001 and design docs. charter/ The Relayflow Lead. diff --git a/docs/SURFACE.md b/docs/SURFACE.md index a46d8cc06..67aa682d7 100644 --- a/docs/SURFACE.md +++ b/docs/SURFACE.md @@ -385,7 +385,7 @@ cycle` error — before a journal exists and before the step's command runs. Cycles that pass through a child applicator (`properties`, `items`, `prefixItems`, ...) consume one level of the instance per step, so ordinary recursive schemas stay legal. `kernel/relayflowd-core/src/schema.rs` and -`sdk/src/json-schema-bound.ts` implement the same rule and are pinned to the +`packages/sdk/src/json-schema-bound.ts` implement the same rule and are pinned to the shared corpus in `testdata/json-schema-bound-cases.json`, so the kernel and the SDK agree on which schemas are legal by construction. `verify` compiles through the same gate, so a journal written before the bound existed fails its gate with diff --git a/docs/bootstrap-report.md b/docs/bootstrap-report.md index 4684a4dd4..81f3871ef 100644 --- a/docs/bootstrap-report.md +++ b/docs/bootstrap-report.md @@ -5,7 +5,7 @@ Honest state only: what exists, what passed, what is missing. ## What was built -All of the following is **uncommitted** on `main` (untracked `kernel/`, `sdk/`, +All of the following is **uncommitted** on `main` (untracked `kernel/`, `packages/sdk/`, `testdata/`; `workflows/bootstrap-gate1.yaml` modified mid-run to swap the adversary agent's CLI from `grok` to `claude`). A human decides branch/commit/PR. @@ -35,7 +35,7 @@ v0 (12 verbs, JSON over unix socket). - Largest file 397 lines; `cargo clippy -D warnings` and `cargo fmt --check` passed at build time (per kernel-dev's step report). -### sdk/ — @relayflows/sdk, TypeScript (sdk-dev) +### packages/sdk/ — @relayflows/sdk, TypeScript (sdk-dev) - `spec.ts` — spec types mirroring RFC §1's ladder (`deterministic | llm | agent`), verification gates, recovery modes, agent surfaces, budgets; zero-agent flows legal by construction. @@ -50,7 +50,7 @@ v0 (12 verbs, JSON over unix socket). ### testdata/ — shared parity fixture `hello-ladder.flow.yaml` → `hello-ladder.spec.canonical.json` + sha256, pinned -bit-for-bit on **both** sides (`sdk/tests/spec-parity.test.ts`, +bit-for-bit on **both** sides (`packages/sdk/tests/spec-parity.test.ts`, `kernel/relayflowd-core/tests/spec_parity.rs`), so the SDK-compiled spec and the kernel-parsed spec provably hash identically. @@ -112,7 +112,7 @@ test tests::append_is_durable_and_monotonic_after_reopen ... ok test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ``` -### `npm test` (sdk/: `tsc --noEmit && vitest run`) — 41 passed, 0 failed +### `npm test` (packages/sdk/: `tsc --noEmit && vitest run`) — 41 passed, 0 failed ``` ✓ tests/journal-client.test.ts (7 tests) 17ms @@ -143,7 +143,7 @@ Minor observations carried forward from the review (not violations): 2. `next_actions` returns only `ArmTimer` for the first backing-off step in spec order — wall-clock inefficiency for future parallel DAGs, irrelevant to the sequential gate-1 ladder. -3. `sdk/dist/` build artifacts are checked in; fresh today, but they can drift. +3. `packages/sdk/dist/` build artifacts are checked in; fresh today, but they can drift. ## What gate 1 still needs diff --git a/examples/research/shims/agent-cli.ts b/examples/research/shims/agent-cli.ts index 0ef82c9ee..d3455e8c3 100644 --- a/examples/research/shims/agent-cli.ts +++ b/examples/research/shims/agent-cli.ts @@ -1,6 +1,6 @@ // REPLACE-WHEN: gate-1 agent dispatch accepts a run-time instruction and the // kernel starts independent steps concurrently. Then `f.agent` is the SDK's -// AgentWorker (sdk/src/worker.ts) and this file is deleted. +// AgentWorker (packages/sdk/src/worker.ts) and this file is deleted. // // What this shim provides today: the `agent` verb of research.flow.ts's // context, executed by spawning the lane's declared CLI through its headless @@ -159,7 +159,7 @@ export const runAgentWithCli: AgentRunner = async (options) => { } const { argv, stdin } = headlessInvocation(cli, { model, promptFile, cwd: options.cwd, binaries: options.binaries }); const env: NodeJS.ProcessEnv = { ...process.env }; - // Same rule as sdk/src/worker.ts: unset first, then set only when declared, + // Same rule as packages/sdk/src/worker.ts: unset first, then set only when declared, // so a CLI can tell "no model chosen" from an inherited pin. delete env[MODEL_ENV]; if (model !== undefined) env[MODEL_ENV] = model; diff --git a/kernel/relayflowd-core/src/schema.rs b/kernel/relayflowd-core/src/schema.rs index a6b416794..caff2af18 100644 --- a/kernel/relayflowd-core/src/schema.rs +++ b/kernel/relayflowd-core/src/schema.rs @@ -17,7 +17,7 @@ //! recursive schemas: each step consumes one level of the instance, so they //! terminate, and they stay legal. //! -//! `sdk/src/json-schema-bound.ts` implements the same rule, and +//! `packages/sdk/src/json-schema-bound.ts` implements the same rule, and //! `testdata/json-schema-bound-cases.json` is the corpus both sides are pinned //! to, so kernel and SDK agree on which schemas are legal by construction //! rather than by coincidence of two engines' overflow behaviour. diff --git a/kernel/relayflowd-core/tests/spec_parity.rs b/kernel/relayflowd-core/tests/spec_parity.rs index a5a573b29..2bcfb1de0 100644 --- a/kernel/relayflowd-core/tests/spec_parity.rs +++ b/kernel/relayflowd-core/tests/spec_parity.rs @@ -1,7 +1,7 @@ //! The kernel half of the cross-boundary spec-parity gate. //! //! `testdata/hello-ladder.spec.canonical.json` is emitted by the SDK compiler -//! (see `sdk/tests/spec-parity.test.ts`). This test proves the kernel parses +//! (see `packages/sdk/tests/spec-parity.test.ts`). This test proves the kernel parses //! that exact artifact fail-closed, and that re-serializing it — precisely what //! the engine hashes when it stamps `spec_hash` in `run.spawned` — reproduces //! the same canonical bytes and the same sha256 the SDK computed. Together the diff --git a/ops/FORBIDDEN_PATHS b/ops/FORBIDDEN_PATHS index f5d8bd150..cb45faeec 100644 --- a/ops/FORBIDDEN_PATHS +++ b/ops/FORBIDDEN_PATHS @@ -15,7 +15,7 @@ # Review rejected an in-kernel HTTP adapter (PR #16, P1): a durable-execution # kernel must not own provider-specific product logic or network I/O. The -# Hacker News adapter lives at sdk/src/hn-poller.ts, outside the kernel. +# Hacker News adapter lives at packages/sdk/src/hn-poller.ts, outside the kernel. kernel/relayflowd/src/engine/hn_poller.rs kernel/relayflowd/tests/hn_poller.rs diff --git a/ops/cargo.sh b/ops/cargo.sh index 18018e8e7..13a617d32 100755 --- a/ops/cargo.sh +++ b/ops/cargo.sh @@ -40,7 +40,7 @@ export RUSTUP_HOME="$toolchain_home/rustup" # whole of the problem. # # Anything that hardcodes kernel/target/debug must read RELAYFLOWD_BIN instead; -# sdk/tests/live-kernel.test.ts already does. +# packages/sdk/tests/live-kernel.test.ts already does. # Keyed per worktree. Review caught that a single shared target dir would be # used by every worktree under the same HOME (PR #38): cargo locks it, so the # builds are safe, but two different source trees sharing one target thrash diff --git a/ops/probes/pr134-repair-0903/harness.mjs b/ops/probes/pr134-repair-0903/harness.mjs index d8c48ddf3..a0362fdc1 100644 --- a/ops/probes/pr134-repair-0903/harness.mjs +++ b/ops/probes/pr134-repair-0903/harness.mjs @@ -1,7 +1,7 @@ // Shared probe harness for the PR #134 authored-lifecycle repair. // // Drives the real `executeAuthoredFlow` against a loopback journal faithful to -// `sdk/tests/journal-client-loopback.ts`, and reports which journal runs were +// `packages/sdk/tests/journal-client-loopback.ts`, and reports which journal runs were // started — so "did the flow lower its terminal complete-* run" is observed, // not inferred. Run any probe in this directory with plain `node`. import { randomUUID } from 'node:crypto'; diff --git a/ops/probes/pr134-repair-0903/verb-output-fields.mjs b/ops/probes/pr134-repair-0903/verb-output-fields.mjs index 97fab5070..87ca70281 100644 --- a/ops/probes/pr134-repair-0903/verb-output-fields.mjs +++ b/ops/probes/pr134-repair-0903/verb-output-fields.mjs @@ -66,4 +66,4 @@ for (const [name, body] of Object.entries(bodies)) { } console.log(` ${name.padEnd(14)} ${line}`); } -if (outDir) console.log(`--- PATH 3 (supporting) fixtures written to ${outDir} (run: node sdk/dist/cli.js check )`); +if (outDir) console.log(`--- PATH 3 (supporting) fixtures written to ${outDir} (run: node packages/sdk/dist/cli.js check )`); diff --git a/sdk/package-lock.json b/packages/sdk/package-lock.json similarity index 100% rename from sdk/package-lock.json rename to packages/sdk/package-lock.json diff --git a/sdk/package.json b/packages/sdk/package.json similarity index 100% rename from sdk/package.json rename to packages/sdk/package.json diff --git a/sdk/scripts/make-cli-executable.mjs b/packages/sdk/scripts/make-cli-executable.mjs similarity index 100% rename from sdk/scripts/make-cli-executable.mjs rename to packages/sdk/scripts/make-cli-executable.mjs diff --git a/sdk/scripts/prune-test-build.mjs b/packages/sdk/scripts/prune-test-build.mjs similarity index 100% rename from sdk/scripts/prune-test-build.mjs rename to packages/sdk/scripts/prune-test-build.mjs diff --git a/sdk/scripts/test.sh b/packages/sdk/scripts/test.sh similarity index 100% rename from sdk/scripts/test.sh rename to packages/sdk/scripts/test.sh diff --git a/sdk/src/authored-flow-error.ts b/packages/sdk/src/authored-flow-error.ts similarity index 100% rename from sdk/src/authored-flow-error.ts rename to packages/sdk/src/authored-flow-error.ts diff --git a/sdk/src/authored-flow-executor.ts b/packages/sdk/src/authored-flow-executor.ts similarity index 100% rename from sdk/src/authored-flow-executor.ts rename to packages/sdk/src/authored-flow-executor.ts diff --git a/sdk/src/authored-flow-lifecycle.ts b/packages/sdk/src/authored-flow-lifecycle.ts similarity index 100% rename from sdk/src/authored-flow-lifecycle.ts rename to packages/sdk/src/authored-flow-lifecycle.ts diff --git a/sdk/src/authored-flow-loader.ts b/packages/sdk/src/authored-flow-loader.ts similarity index 100% rename from sdk/src/authored-flow-loader.ts rename to packages/sdk/src/authored-flow-loader.ts diff --git a/sdk/src/authored-flow-operation.ts b/packages/sdk/src/authored-flow-operation.ts similarity index 100% rename from sdk/src/authored-flow-operation.ts rename to packages/sdk/src/authored-flow-operation.ts diff --git a/sdk/src/authored-flow.ts b/packages/sdk/src/authored-flow.ts similarity index 100% rename from sdk/src/authored-flow.ts rename to packages/sdk/src/authored-flow.ts diff --git a/sdk/src/authored-promise-graph.ts b/packages/sdk/src/authored-promise-graph.ts similarity index 100% rename from sdk/src/authored-promise-graph.ts rename to packages/sdk/src/authored-promise-graph.ts diff --git a/sdk/src/backlog-picker.ts b/packages/sdk/src/backlog-picker.ts similarity index 100% rename from sdk/src/backlog-picker.ts rename to packages/sdk/src/backlog-picker.ts diff --git a/sdk/src/canonical.ts b/packages/sdk/src/canonical.ts similarity index 100% rename from sdk/src/canonical.ts rename to packages/sdk/src/canonical.ts diff --git a/sdk/src/cli-adapter.ts b/packages/sdk/src/cli-adapter.ts similarity index 100% rename from sdk/src/cli-adapter.ts rename to packages/sdk/src/cli-adapter.ts diff --git a/sdk/src/cli-executable.ts b/packages/sdk/src/cli-executable.ts similarity index 100% rename from sdk/src/cli-executable.ts rename to packages/sdk/src/cli-executable.ts diff --git a/sdk/src/cli.ts b/packages/sdk/src/cli.ts similarity index 100% rename from sdk/src/cli.ts rename to packages/sdk/src/cli.ts diff --git a/sdk/src/cli/check.ts b/packages/sdk/src/cli/check.ts similarity index 100% rename from sdk/src/cli/check.ts rename to packages/sdk/src/cli/check.ts diff --git a/sdk/src/cli/direct-run.ts b/packages/sdk/src/cli/direct-run.ts similarity index 100% rename from sdk/src/cli/direct-run.ts rename to packages/sdk/src/cli/direct-run.ts diff --git a/sdk/src/cli/hn-monitor.ts b/packages/sdk/src/cli/hn-monitor.ts similarity index 100% rename from sdk/src/cli/hn-monitor.ts rename to packages/sdk/src/cli/hn-monitor.ts diff --git a/sdk/src/cli/interruptible-sleep.ts b/packages/sdk/src/cli/interruptible-sleep.ts similarity index 100% rename from sdk/src/cli/interruptible-sleep.ts rename to packages/sdk/src/cli/interruptible-sleep.ts diff --git a/sdk/src/cli/run.ts b/packages/sdk/src/cli/run.ts similarity index 100% rename from sdk/src/cli/run.ts rename to packages/sdk/src/cli/run.ts diff --git a/sdk/src/cli/tick-runner.ts b/packages/sdk/src/cli/tick-runner.ts similarity index 100% rename from sdk/src/cli/tick-runner.ts rename to packages/sdk/src/cli/tick-runner.ts diff --git a/sdk/src/compile.ts b/packages/sdk/src/compile.ts similarity index 100% rename from sdk/src/compile.ts rename to packages/sdk/src/compile.ts diff --git a/sdk/src/demo-hn-monitor.ts b/packages/sdk/src/demo-hn-monitor.ts similarity index 100% rename from sdk/src/demo-hn-monitor.ts rename to packages/sdk/src/demo-hn-monitor.ts diff --git a/sdk/src/dir-watcher-poller.ts b/packages/sdk/src/dir-watcher-poller.ts similarity index 100% rename from sdk/src/dir-watcher-poller.ts rename to packages/sdk/src/dir-watcher-poller.ts diff --git a/sdk/src/direct-input.ts b/packages/sdk/src/direct-input.ts similarity index 100% rename from sdk/src/direct-input.ts rename to packages/sdk/src/direct-input.ts diff --git a/sdk/src/failure-kinds.ts b/packages/sdk/src/failure-kinds.ts similarity index 100% rename from sdk/src/failure-kinds.ts rename to packages/sdk/src/failure-kinds.ts diff --git a/sdk/src/gate-contract.ts b/packages/sdk/src/gate-contract.ts similarity index 100% rename from sdk/src/gate-contract.ts rename to packages/sdk/src/gate-contract.ts diff --git a/sdk/src/hn-poller.ts b/packages/sdk/src/hn-poller.ts similarity index 100% rename from sdk/src/hn-poller.ts rename to packages/sdk/src/hn-poller.ts diff --git a/sdk/src/index.ts b/packages/sdk/src/index.ts similarity index 100% rename from sdk/src/index.ts rename to packages/sdk/src/index.ts diff --git a/sdk/src/journal-client.ts b/packages/sdk/src/journal-client.ts similarity index 100% rename from sdk/src/journal-client.ts rename to packages/sdk/src/journal-client.ts diff --git a/sdk/src/json-schema-bound.ts b/packages/sdk/src/json-schema-bound.ts similarity index 100% rename from sdk/src/json-schema-bound.ts rename to packages/sdk/src/json-schema-bound.ts diff --git a/sdk/src/json-schema.ts b/packages/sdk/src/json-schema.ts similarity index 100% rename from sdk/src/json-schema.ts rename to packages/sdk/src/json-schema.ts diff --git a/sdk/src/json-value.ts b/packages/sdk/src/json-value.ts similarity index 100% rename from sdk/src/json-value.ts rename to packages/sdk/src/json-value.ts diff --git a/sdk/src/model-name.ts b/packages/sdk/src/model-name.ts similarity index 100% rename from sdk/src/model-name.ts rename to packages/sdk/src/model-name.ts diff --git a/sdk/src/output-schema.ts b/packages/sdk/src/output-schema.ts similarity index 100% rename from sdk/src/output-schema.ts rename to packages/sdk/src/output-schema.ts diff --git a/sdk/src/preflight.ts b/packages/sdk/src/preflight.ts similarity index 100% rename from sdk/src/preflight.ts rename to packages/sdk/src/preflight.ts diff --git a/sdk/src/protocol.ts b/packages/sdk/src/protocol.ts similarity index 100% rename from sdk/src/protocol.ts rename to packages/sdk/src/protocol.ts diff --git a/sdk/src/spec.ts b/packages/sdk/src/spec.ts similarity index 100% rename from sdk/src/spec.ts rename to packages/sdk/src/spec.ts diff --git a/sdk/src/step-dependencies.ts b/packages/sdk/src/step-dependencies.ts similarity index 100% rename from sdk/src/step-dependencies.ts rename to packages/sdk/src/step-dependencies.ts diff --git a/sdk/src/step-fields.ts b/packages/sdk/src/step-fields.ts similarity index 100% rename from sdk/src/step-fields.ts rename to packages/sdk/src/step-fields.ts diff --git a/sdk/src/tick-source.ts b/packages/sdk/src/tick-source.ts similarity index 100% rename from sdk/src/tick-source.ts rename to packages/sdk/src/tick-source.ts diff --git a/sdk/src/unknown-keys.ts b/packages/sdk/src/unknown-keys.ts similarity index 100% rename from sdk/src/unknown-keys.ts rename to packages/sdk/src/unknown-keys.ts diff --git a/sdk/src/validate.ts b/packages/sdk/src/validate.ts similarity index 100% rename from sdk/src/validate.ts rename to packages/sdk/src/validate.ts diff --git a/sdk/src/work-package-consumer.ts b/packages/sdk/src/work-package-consumer.ts similarity index 100% rename from sdk/src/work-package-consumer.ts rename to packages/sdk/src/work-package-consumer.ts diff --git a/sdk/src/work-package-validator.ts b/packages/sdk/src/work-package-validator.ts similarity index 100% rename from sdk/src/work-package-validator.ts rename to packages/sdk/src/work-package-validator.ts diff --git a/sdk/src/worker-cli.ts b/packages/sdk/src/worker-cli.ts similarity index 100% rename from sdk/src/worker-cli.ts rename to packages/sdk/src/worker-cli.ts diff --git a/sdk/src/worker.ts b/packages/sdk/src/worker.ts similarity index 100% rename from sdk/src/worker.ts rename to packages/sdk/src/worker.ts diff --git a/sdk/src/wrapper-runtime.ts b/packages/sdk/src/wrapper-runtime.ts similarity index 100% rename from sdk/src/wrapper-runtime.ts rename to packages/sdk/src/wrapper-runtime.ts diff --git a/sdk/src/wrapper-session.ts b/packages/sdk/src/wrapper-session.ts similarity index 100% rename from sdk/src/wrapper-session.ts rename to packages/sdk/src/wrapper-session.ts diff --git a/sdk/tests/authored-flow-lifecycle-executor.test.ts b/packages/sdk/tests/authored-flow-lifecycle-executor.test.ts similarity index 100% rename from sdk/tests/authored-flow-lifecycle-executor.test.ts rename to packages/sdk/tests/authored-flow-lifecycle-executor.test.ts diff --git a/sdk/tests/authored-flow-operation.test.ts b/packages/sdk/tests/authored-flow-operation.test.ts similarity index 100% rename from sdk/tests/authored-flow-operation.test.ts rename to packages/sdk/tests/authored-flow-operation.test.ts diff --git a/sdk/tests/authored-flow.test.ts b/packages/sdk/tests/authored-flow.test.ts similarity index 100% rename from sdk/tests/authored-flow.test.ts rename to packages/sdk/tests/authored-flow.test.ts diff --git a/sdk/tests/backlog-picker-flow.test.ts b/packages/sdk/tests/backlog-picker-flow.test.ts similarity index 97% rename from sdk/tests/backlog-picker-flow.test.ts rename to packages/sdk/tests/backlog-picker-flow.test.ts index 0b938437f..b5bd14934 100644 --- a/sdk/tests/backlog-picker-flow.test.ts +++ b/packages/sdk/tests/backlog-picker-flow.test.ts @@ -18,7 +18,7 @@ import { describe, expect, it } from 'vitest'; * test of a paraphrase would pass while the flow stayed broken. */ function stepCommands(): Record { - const flowPath = join(__dirname, '..', '..', 'testdata', 'backlog-picker.flow.yaml'); + const flowPath = join(__dirname, '..', '..', '..', 'testdata', 'backlog-picker.flow.yaml'); const flow = load(readFileSync(flowPath, 'utf8')) as { steps: Array<{ id: string; command: string }> }; return Object.fromEntries(flow.steps.map((s) => [s.id, s.command])); } @@ -114,7 +114,7 @@ describe('backlog-picker canonical spec', () => { // caught it. A divergence between the two is silent by nature: both files // are valid, the tests over the yaml pass, and the kernel keeps executing // the stale command. - const root = join(__dirname, '..', '..'); + const root = join(__dirname, '..', '..', '..'); const flow = load(readFileSync(join(root, 'testdata', 'backlog-picker.flow.yaml'), 'utf8')) as { steps: Array<{ id: string; command?: string }>; }; @@ -143,7 +143,7 @@ describe('backlog-picker canonical spec', () => { // The rule is shape, not content: whatever fields the kernel-authored // steps carry, every step must carry, and no step may carry an authoring // -surface alias the kernel does not read. - const root = join(__dirname, '..', '..'); + const root = join(__dirname, '..', '..', '..'); const canonical = JSON.parse( readFileSync(join(root, 'testdata', 'backlog-picker.spec.canonical.json'), 'utf8'), ) as { steps: Array> }; @@ -176,7 +176,7 @@ describe('backlog-picker canonical spec', () => { // yaml declares the dependencies; the canonical spec must carry the same // ones under `depends_on`. This cannot go stale as the kernel's schema // grows, because it asserts a relationship rather than a field list. - const root = join(__dirname, '..', '..'); + const root = join(__dirname, '..', '..', '..'); const flow = load(readFileSync(join(root, 'testdata', 'backlog-picker.flow.yaml'), 'utf8')) as { steps: Array<{ id: string; dependsOn?: string[] }>; }; diff --git a/sdk/tests/backlog-picker.test.ts b/packages/sdk/tests/backlog-picker.test.ts similarity index 98% rename from sdk/tests/backlog-picker.test.ts rename to packages/sdk/tests/backlog-picker.test.ts index b7459d35d..3019dabc3 100644 --- a/sdk/tests/backlog-picker.test.ts +++ b/packages/sdk/tests/backlog-picker.test.ts @@ -60,7 +60,7 @@ describe('backlog picker', () => { }); it('does not treat backticked prose containing a slash as a file in scope', () => { - const flowPath = join(__dirname, '..', '..', 'testdata', 'backlog-picker.flow.yaml'); + const flowPath = join(__dirname, '..', '..', '..', 'testdata', 'backlog-picker.flow.yaml'); const flow = load(readFileSync(flowPath, 'utf8')) as { steps: Array<{ id: string; command: string }>; }; @@ -138,7 +138,7 @@ describe('work package validation', () => { }); it('keeps at least twenty real backlog entries actionable', async () => { - const backlog = readFileSync(join(__dirname, '..', '..', 'ops', 'BACKLOG.md'), 'utf8'); + const backlog = readFileSync(join(__dirname, '..', '..', '..', 'ops', 'BACKLOG.md'), 'utf8'); const entries = [...backlog.matchAll(/^- \*\*(.+?)\*\*\s*(.*(?:\n .*)*)/gm)].map( (match) => ({ title: match[1] ?? '', diff --git a/sdk/tests/bin.test.ts b/packages/sdk/tests/bin.test.ts similarity index 98% rename from sdk/tests/bin.test.ts rename to packages/sdk/tests/bin.test.ts index f896bfc92..bb36ba6c3 100644 --- a/sdk/tests/bin.test.ts +++ b/packages/sdk/tests/bin.test.ts @@ -12,8 +12,8 @@ import { spawnSync } from 'node:child_process'; import { fileURLToPath } from 'node:url'; import { afterEach, describe, expect, it } from 'vitest'; -const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..'); -const SDK = join(ROOT, 'sdk'); +const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..'); +const SDK = join(ROOT, 'packages', 'sdk'); const BUILT_CLI = join(SDK, 'dist', 'cli.js'); const PREFLIGHT = join(ROOT, 'testdata', 'preflight'); const temporaryDirectories: string[] = []; diff --git a/sdk/tests/classify-outcome.test.ts b/packages/sdk/tests/classify-outcome.test.ts similarity index 100% rename from sdk/tests/classify-outcome.test.ts rename to packages/sdk/tests/classify-outcome.test.ts diff --git a/sdk/tests/cli-adapter.test.ts b/packages/sdk/tests/cli-adapter.test.ts similarity index 100% rename from sdk/tests/cli-adapter.test.ts rename to packages/sdk/tests/cli-adapter.test.ts diff --git a/sdk/tests/cli-hn-monitor.test.ts b/packages/sdk/tests/cli-hn-monitor.test.ts similarity index 100% rename from sdk/tests/cli-hn-monitor.test.ts rename to packages/sdk/tests/cli-hn-monitor.test.ts diff --git a/sdk/tests/cli.test.ts b/packages/sdk/tests/cli.test.ts similarity index 99% rename from sdk/tests/cli.test.ts rename to packages/sdk/tests/cli.test.ts index e16ddb5d0..b55d6b5c7 100644 --- a/sdk/tests/cli.test.ts +++ b/packages/sdk/tests/cli.test.ts @@ -22,7 +22,7 @@ import { type LoopbackHandlers, } from './journal-client-loopback.js'; -const TESTDATA = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'testdata'); +const TESTDATA = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'testdata'); const DIRECT_INPUT_FLOW = join(dirname(fileURLToPath(import.meta.url)), 'fixtures', 'direct-input.flow.ts'); const PREFLIGHT = join(TESTDATA, 'preflight'); const LADDER = ['hello-deterministic', 'hello-llm', 'hello-agent'] as const; diff --git a/sdk/tests/dependency-validation.test.ts b/packages/sdk/tests/dependency-validation.test.ts similarity index 100% rename from sdk/tests/dependency-validation.test.ts rename to packages/sdk/tests/dependency-validation.test.ts diff --git a/sdk/tests/deterministic-llm.test.ts b/packages/sdk/tests/deterministic-llm.test.ts similarity index 100% rename from sdk/tests/deterministic-llm.test.ts rename to packages/sdk/tests/deterministic-llm.test.ts diff --git a/sdk/tests/dir-watcher-poller.test.ts b/packages/sdk/tests/dir-watcher-poller.test.ts similarity index 100% rename from sdk/tests/dir-watcher-poller.test.ts rename to packages/sdk/tests/dir-watcher-poller.test.ts diff --git a/sdk/tests/direct-input.test.ts b/packages/sdk/tests/direct-input.test.ts similarity index 95% rename from sdk/tests/direct-input.test.ts rename to packages/sdk/tests/direct-input.test.ts index 6e0f55118..90f0b17c9 100644 --- a/sdk/tests/direct-input.test.ts +++ b/packages/sdk/tests/direct-input.test.ts @@ -15,11 +15,11 @@ import { spawn, spawnSync, type ChildProcess } from 'node:child_process'; import { fileURLToPath } from 'node:url'; import { afterEach, beforeAll, describe, expect, it } from 'vitest'; -const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..'); -const BUILT_CLI = join(ROOT, 'sdk', 'dist', 'cli.js'); -const FLOW = join(ROOT, 'sdk', 'tests', 'fixtures', 'direct-input.flow.ts'); -const CONTROL_FLOW = join(ROOT, 'sdk', 'tests', 'fixtures', 'direct-output-control.flow.ts'); -const SIDE_EFFECT_FLOW = join(ROOT, 'sdk', 'tests', 'fixtures', 'pre-journal-side-effect.flow.ts'); +const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..'); +const BUILT_CLI = join(ROOT, 'packages', 'sdk', 'dist', 'cli.js'); +const FLOW = join(ROOT, 'packages', 'sdk', 'tests', 'fixtures', 'direct-input.flow.ts'); +const CONTROL_FLOW = join(ROOT, 'packages', 'sdk', 'tests', 'fixtures', 'direct-output-control.flow.ts'); +const SIDE_EFFECT_FLOW = join(ROOT, 'packages', 'sdk', 'tests', 'fixtures', 'pre-journal-side-effect.flow.ts'); const TOOLCHAIN_TARGET = process.env['CARGO_TARGET_DIR'] ?? join(process.env['RELAYFLOWS_TOOLCHAIN_HOME'] ?? join(homedir(), '.relayflows-toolchain'), 'target'); const RELAYFLOWD = resolve(process.env['RELAYFLOWD_BIN'] ?? locateRelayflowd()); diff --git a/sdk/tests/fixtures/direct-input.flow.ts b/packages/sdk/tests/fixtures/direct-input.flow.ts similarity index 100% rename from sdk/tests/fixtures/direct-input.flow.ts rename to packages/sdk/tests/fixtures/direct-input.flow.ts diff --git a/sdk/tests/fixtures/direct-output-control.flow.ts b/packages/sdk/tests/fixtures/direct-output-control.flow.ts similarity index 100% rename from sdk/tests/fixtures/direct-output-control.flow.ts rename to packages/sdk/tests/fixtures/direct-output-control.flow.ts diff --git a/sdk/tests/fixtures/pre-journal-side-effect.flow.ts b/packages/sdk/tests/fixtures/pre-journal-side-effect.flow.ts similarity index 100% rename from sdk/tests/fixtures/pre-journal-side-effect.flow.ts rename to packages/sdk/tests/fixtures/pre-journal-side-effect.flow.ts diff --git a/sdk/tests/fixtures/runtime-bridge.flow.ts b/packages/sdk/tests/fixtures/runtime-bridge.flow.ts similarity index 100% rename from sdk/tests/fixtures/runtime-bridge.flow.ts rename to packages/sdk/tests/fixtures/runtime-bridge.flow.ts diff --git a/sdk/tests/gate-contract.test.ts b/packages/sdk/tests/gate-contract.test.ts similarity index 99% rename from sdk/tests/gate-contract.test.ts rename to packages/sdk/tests/gate-contract.test.ts index b201ef0f6..0b7636e8a 100644 --- a/sdk/tests/gate-contract.test.ts +++ b/packages/sdk/tests/gate-contract.test.ts @@ -11,7 +11,7 @@ import { preflight } from '../src/index.js'; import type { FlowSpec } from '../src/spec.js'; import { validateSpec } from '../src/validate.js'; -const TESTDATA = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'testdata'); +const TESTDATA = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'testdata'); function schemaFixture(name: 'valid' | 'invalid'): unknown { return JSON.parse(readFileSync(join(TESTDATA, `json-schema-${name}.json`), 'utf8')); diff --git a/sdk/tests/hello-deterministic.test.ts b/packages/sdk/tests/hello-deterministic.test.ts similarity index 100% rename from sdk/tests/hello-deterministic.test.ts rename to packages/sdk/tests/hello-deterministic.test.ts diff --git a/sdk/tests/hn-poller.test.ts b/packages/sdk/tests/hn-poller.test.ts similarity index 100% rename from sdk/tests/hn-poller.test.ts rename to packages/sdk/tests/hn-poller.test.ts diff --git a/sdk/tests/journal-client-loopback.ts b/packages/sdk/tests/journal-client-loopback.ts similarity index 100% rename from sdk/tests/journal-client-loopback.ts rename to packages/sdk/tests/journal-client-loopback.ts diff --git a/sdk/tests/journal-client.test.ts b/packages/sdk/tests/journal-client.test.ts similarity index 100% rename from sdk/tests/journal-client.test.ts rename to packages/sdk/tests/journal-client.test.ts diff --git a/sdk/tests/json-schema-bound.test.ts b/packages/sdk/tests/json-schema-bound.test.ts similarity index 99% rename from sdk/tests/json-schema-bound.test.ts rename to packages/sdk/tests/json-schema-bound.test.ts index 42e3c8914..d71754931 100644 --- a/sdk/tests/json-schema-bound.test.ts +++ b/packages/sdk/tests/json-schema-bound.test.ts @@ -16,7 +16,7 @@ import { jsonSchemaBoundError, UNBOUNDED_REF_CYCLE } from '../src/json-schema-bo import { jsonSchemaError } from '../src/json-schema.js'; import type { FlowSpec } from '../src/spec.js'; -const TESTDATA = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'testdata'); +const TESTDATA = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'testdata'); interface Case { name: string; diff --git a/sdk/tests/live-kernel.test.ts b/packages/sdk/tests/live-kernel.test.ts similarity index 99% rename from sdk/tests/live-kernel.test.ts rename to packages/sdk/tests/live-kernel.test.ts index f4332b7dd..ee0748491 100644 --- a/sdk/tests/live-kernel.test.ts +++ b/packages/sdk/tests/live-kernel.test.ts @@ -24,8 +24,8 @@ import { AgentWorker } from '../src/worker.js'; import { resolveSpecCliPaths } from '../src/cli/hn-monitor.js'; import { emitDueTicks, type TickCursor } from '../src/tick-source.js'; -const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..'); -const SDK = join(ROOT, 'sdk'); +const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..'); +const SDK = join(ROOT, 'packages', 'sdk'); const BUILT_CLI = join(SDK, 'dist', 'cli.js'); const TESTDATA = join(ROOT, 'testdata'); // ops/cargo.sh builds into a target dir OUTSIDE the repo, because diff --git a/sdk/tests/model-selection.test.ts b/packages/sdk/tests/model-selection.test.ts similarity index 100% rename from sdk/tests/model-selection.test.ts rename to packages/sdk/tests/model-selection.test.ts diff --git a/sdk/tests/parse-json-output.test.ts b/packages/sdk/tests/parse-json-output.test.ts similarity index 100% rename from sdk/tests/parse-json-output.test.ts rename to packages/sdk/tests/parse-json-output.test.ts diff --git a/sdk/tests/preflight.test.ts b/packages/sdk/tests/preflight.test.ts similarity index 100% rename from sdk/tests/preflight.test.ts rename to packages/sdk/tests/preflight.test.ts diff --git a/sdk/tests/real-cli-adapters.test.ts b/packages/sdk/tests/real-cli-adapters.test.ts similarity index 100% rename from sdk/tests/real-cli-adapters.test.ts rename to packages/sdk/tests/real-cli-adapters.test.ts diff --git a/sdk/tests/spec-parity.test.ts b/packages/sdk/tests/spec-parity.test.ts similarity index 99% rename from sdk/tests/spec-parity.test.ts rename to packages/sdk/tests/spec-parity.test.ts index 32804f94d..0f07ebd79 100644 --- a/sdk/tests/spec-parity.test.ts +++ b/packages/sdk/tests/spec-parity.test.ts @@ -18,7 +18,7 @@ import { canonicalize, kernelToAuthoring, specHash } from '../src/index.js'; // make "sha256(canonical JSON) == kernel spec_hash" a tested fact, not a // comment. -const TESTDATA = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'testdata'); +const TESTDATA = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'testdata'); function fixture(name: string): string { return readFileSync(join(TESTDATA, name), 'utf8'); diff --git a/sdk/tests/tick-runner.test.ts b/packages/sdk/tests/tick-runner.test.ts similarity index 100% rename from sdk/tests/tick-runner.test.ts rename to packages/sdk/tests/tick-runner.test.ts diff --git a/sdk/tests/tick-source.test.ts b/packages/sdk/tests/tick-source.test.ts similarity index 99% rename from sdk/tests/tick-source.test.ts rename to packages/sdk/tests/tick-source.test.ts index 549c5888f..e02a8226b 100644 --- a/sdk/tests/tick-source.test.ts +++ b/packages/sdk/tests/tick-source.test.ts @@ -17,7 +17,7 @@ import { } from '../src/tick-source.js'; import { compileYaml, toKernelSpec } from '../src/compile.js'; -const TESTDATA = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'testdata'); +const TESTDATA = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'testdata'); /** * Stands in for the kernel's `(flow_key, subscription_id, dedupe_key)` claim. diff --git a/sdk/tests/typed-output.test.ts b/packages/sdk/tests/typed-output.test.ts similarity index 99% rename from sdk/tests/typed-output.test.ts rename to packages/sdk/tests/typed-output.test.ts index 91c73fd0b..06446f0dd 100644 --- a/sdk/tests/typed-output.test.ts +++ b/packages/sdk/tests/typed-output.test.ts @@ -16,7 +16,7 @@ import type { LlmStepSpec, } from '../src/spec.js'; -const TESTDATA = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'testdata'); +const TESTDATA = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'testdata'); const extractionSchema: JsonOutputSchema = { type: 'object', diff --git a/sdk/tests/validate.test.ts b/packages/sdk/tests/validate.test.ts similarity index 100% rename from sdk/tests/validate.test.ts rename to packages/sdk/tests/validate.test.ts diff --git a/sdk/tests/verb-field-lint.test.ts b/packages/sdk/tests/verb-field-lint.test.ts similarity index 99% rename from sdk/tests/verb-field-lint.test.ts rename to packages/sdk/tests/verb-field-lint.test.ts index bb2d3e150..c77f7e14b 100644 --- a/sdk/tests/verb-field-lint.test.ts +++ b/packages/sdk/tests/verb-field-lint.test.ts @@ -37,7 +37,7 @@ interface InvalidFieldCase { } const AUTHENTICATED_CLI = join( - dirname(fileURLToPath(import.meta.url)), '..', '..', 'testdata', 'preflight', 'authenticated-cli', + dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'testdata', 'preflight', 'authenticated-cli', ); const TYPO_STEP_FIELDS = [ diff --git a/sdk/tests/work-package-consumer.test.ts b/packages/sdk/tests/work-package-consumer.test.ts similarity index 98% rename from sdk/tests/work-package-consumer.test.ts rename to packages/sdk/tests/work-package-consumer.test.ts index aae51acfb..28386917c 100644 --- a/sdk/tests/work-package-consumer.test.ts +++ b/packages/sdk/tests/work-package-consumer.test.ts @@ -74,7 +74,7 @@ describe('the Garden join: picker output feeds the consumer', () => { const { join } = require('node:path') as typeof import('node:path'); const { load } = require('js-yaml') as typeof import('js-yaml'); - const flowPath = join(__dirname, '..', '..', 'testdata', 'backlog-picker.flow.yaml'); + const flowPath = join(__dirname, '..', '..', '..', 'testdata', 'backlog-picker.flow.yaml'); const flow = load(readFileSync(flowPath, 'utf8')) as { steps: Array<{ id: string; command: string }> }; const step = (id: string) => flow.steps.find((s) => s.id === id)!.command; // stdio: stderr captured, not echoed -- see backlog-picker-flow.test.ts. diff --git a/sdk/tests/work-package-validator.test.ts b/packages/sdk/tests/work-package-validator.test.ts similarity index 100% rename from sdk/tests/work-package-validator.test.ts rename to packages/sdk/tests/work-package-validator.test.ts diff --git a/sdk/tests/worker-cli.test.ts b/packages/sdk/tests/worker-cli.test.ts similarity index 100% rename from sdk/tests/worker-cli.test.ts rename to packages/sdk/tests/worker-cli.test.ts diff --git a/sdk/tsconfig.json b/packages/sdk/tsconfig.json similarity index 100% rename from sdk/tsconfig.json rename to packages/sdk/tsconfig.json diff --git a/sdk/tsconfig.tests.json b/packages/sdk/tsconfig.tests.json similarity index 100% rename from sdk/tsconfig.tests.json rename to packages/sdk/tsconfig.tests.json diff --git a/sdk/tsconfig.type-tests.json b/packages/sdk/tsconfig.type-tests.json similarity index 100% rename from sdk/tsconfig.type-tests.json rename to packages/sdk/tsconfig.type-tests.json diff --git a/sdk/type-tests/step-fields.ts b/packages/sdk/type-tests/step-fields.ts similarity index 100% rename from sdk/type-tests/step-fields.ts rename to packages/sdk/type-tests/step-fields.ts diff --git a/sdk/vitest.config.ts b/packages/sdk/vitest.config.ts similarity index 100% rename from sdk/vitest.config.ts rename to packages/sdk/vitest.config.ts diff --git a/surface/README.md b/packages/surface/README.md similarity index 100% rename from surface/README.md rename to packages/surface/README.md diff --git a/surface/bun.lock b/packages/surface/bun.lock similarity index 100% rename from surface/bun.lock rename to packages/surface/bun.lock diff --git a/surface/package.json b/packages/surface/package.json similarity index 100% rename from surface/package.json rename to packages/surface/package.json diff --git a/surface/src/cloud.ts b/packages/surface/src/cloud.ts similarity index 100% rename from surface/src/cloud.ts rename to packages/surface/src/cloud.ts diff --git a/surface/src/completion.ts b/packages/surface/src/completion.ts similarity index 100% rename from surface/src/completion.ts rename to packages/surface/src/completion.ts diff --git a/surface/src/context.ts b/packages/surface/src/context.ts similarity index 100% rename from surface/src/context.ts rename to packages/surface/src/context.ts diff --git a/surface/src/flow.ts b/packages/surface/src/flow.ts similarity index 100% rename from surface/src/flow.ts rename to packages/surface/src/flow.ts diff --git a/surface/src/index.ts b/packages/surface/src/index.ts similarity index 100% rename from surface/src/index.ts rename to packages/surface/src/index.ts diff --git a/surface/src/runtime.ts b/packages/surface/src/runtime.ts similarity index 100% rename from surface/src/runtime.ts rename to packages/surface/src/runtime.ts diff --git a/surface/src/step.ts b/packages/surface/src/step.ts similarity index 100% rename from surface/src/step.ts rename to packages/surface/src/step.ts diff --git a/surface/tests/flow.test.ts b/packages/surface/tests/flow.test.ts similarity index 100% rename from surface/tests/flow.test.ts rename to packages/surface/tests/flow.test.ts diff --git a/surface/tsconfig.json b/packages/surface/tsconfig.json similarity index 100% rename from surface/tsconfig.json rename to packages/surface/tsconfig.json diff --git a/surface/tsconfig.test.json b/packages/surface/tsconfig.test.json similarity index 100% rename from surface/tsconfig.test.json rename to packages/surface/tsconfig.test.json diff --git a/surface/vitest.config.ts b/packages/surface/vitest.config.ts similarity index 100% rename from surface/vitest.config.ts rename to packages/surface/vitest.config.ts diff --git a/workflows/bootstrap-gate1.yaml b/workflows/bootstrap-gate1.yaml index da77b1da2..1aa6e3540 100644 --- a/workflows/bootstrap-gate1.yaml +++ b/workflows/bootstrap-gate1.yaml @@ -105,7 +105,7 @@ workflows: maxIterations: 3 task: | Read kernel/DESIGN.md, docs/RFC-0001-everything-is-a-relayflow.md and - AGENTS.md. Implement sdk/ as a TypeScript package (npm, vitest): + AGENTS.md. Implement packages/sdk/ as a TypeScript package (npm, vitest): - Spec types mirroring RFC §1's ladder: deterministic | llm | agent steps, verification gates, flow spec. Zero-agent flows are legal. - A YAML → spec JSON compiler with schema validation. @@ -131,7 +131,7 @@ workflows: dependsOn: [kernel-tests, sdk-tests] task: | Read docs/RFC-0001-everything-is-a-relayflow.md, AGENTS.md, - kernel/DESIGN.md, and the full contents of kernel/ and sdk/. + kernel/DESIGN.md, and the full contents of kernel/ and packages/sdk/. Adversarially review: try to refute that this skeleton satisfies the gate-1 direction — journal append-only and fail-closed? completed steps replay as results, not code? completionReason everywhere? diff --git a/workflows/drive-cloud.yaml b/workflows/drive-cloud.yaml index 6c58c818e..0cdd8ed62 100644 --- a/workflows/drive-cloud.yaml +++ b/workflows/drive-cloud.yaml @@ -27,7 +27,7 @@ agents: - name: builder cli: codex preset: worker - role: Implements the work package. Rust for kernel/, TypeScript for sdk/. + role: Implements the work package. Rust for kernel/, TypeScript for packages/sdk/. - name: adversary cli: claude preset: reviewer @@ -101,7 +101,7 @@ workflows: \ the known\nsandbox faults that are NOT reasons to block. Then read\nops/DIRECTIVES.md \u2014 standing\ \ human directives outrank the backlog;\nif one is unsatisfied, it IS the work package.\nThen read\ \ docs/bootstrap-report.md and ops/DRIVE-LOG.md if they exist,\n`git log --oneline -15`, `gh pr\ - \ list --state open` and open PR review\nstate, kernel/ and sdk/ test status. Then write ops/NEXT.md:\ + \ list --state open` and open PR review\nstate, kernel/ and packages/sdk/ test status. Then write ops/NEXT.md:\ \ the\nSINGLE highest-priority work package toward the current gate\n(gate 1 until its done-when\ \ in RFC-0001 \xA73 holds), with: objective,\nfiles in scope, definition of done (must include passing\ \ commands),\nand what is explicitly OUT of scope for this tick. If an open PR is\nawaiting fixes\ @@ -221,18 +221,18 @@ workflows: \ test --workspace 2>&1); rc=$?\n echo \"$out\" | tail -8; ran=1\n if [ $rc -eq 124 ]; then\n\ \ echo \"VERIFY_SUITE_TIMEOUT: the kernel suite exceeded ${VERIFY_SUITE_TIMEOUT:-900}s and was\ \ killed.\"\n echo \" A hanging test is a defect, not a pass \u2014 recording it as a failure.\"\ - \n fi\n [ $rc -eq 0 ] || ok=1\nfi\nif [ -f sdk/package.json ]; then\n # node_modules is not in\ + \n fi\n [ $rc -eq 0 ] || ok=1\nfi\nif [ -f packages/sdk/package.json ]; then\n # node_modules is not in\ \ `git ls-files`, so a sandbox has none.\n # Install before testing, and treat a failed install\ \ as a failed\n # verify rather than letting `npm test` report a confusing error.\n if [ ! -d\ - \ sdk/node_modules ]; then\n echo \"VERIFY_INSTALL: sdk/node_modules absent \u2014 installing\"\ + \ packages/sdk/node_modules ]; then\n echo \"VERIFY_INSTALL: packages/sdk/node_modules absent \u2014 installing\"\ \n out=$(cd sdk && run_bounded \"npm ci\" npm ci 2>&1); rc=$?\n if [ $rc -ne 0 ]; then\n \ \ echo \"$out\" | tail -12\n echo \"VERIFY_FAIL: npm ci failed \u2014 cannot test what\ \ did not install\"\n exit 1\n fi\n fi\n # Restore exec bits. `npm ci` reported success\ \ (96 packages) and\n # esbuild still failed with EACCES on run 909e18f6, so the mount\n # this\ \ installs onto does not carry the executable bit. That is\n # the same fault that left ops/cargo.sh\ \ non-executable. Which\n # layer drops it is NOT established; this repairs the symptom\n # where\ - \ it is observed and is a no-op where the bit survives.\n chmod -R +x sdk/node_modules/.bin 2>/dev/null\ - \ || true\n find sdk/node_modules -type d -name bin -path \"*esbuild*\" \\\n -exec chmod -R\ + \ it is observed and is a no-op where the bit survives.\n chmod -R +x packages/sdk/node_modules/.bin 2>/dev/null\ + \ || true\n find packages/sdk/node_modules -type d -name bin -path \"*esbuild*\" \\\n -exec chmod -R\ \ +x {} + 2>/dev/null || true\n out=$(cd sdk && run_bounded \"sdk suite\" npm test 2>&1); rc=$?\n\ \ echo \"$out\" | tail -8; ran=1\n if [ $rc -eq 124 ]; then\n echo \"VERIFY_SUITE_TIMEOUT:\ \ the sdk suite exceeded ${VERIFY_SUITE_TIMEOUT:-900}s and was killed.\"\n fi\n [ $rc -eq 0 ]\ @@ -244,7 +244,7 @@ workflows: \ on\n# #35, #40 and #48. Four recurrences after the warning is enough\n# evidence that prose does\ \ not hold and a check does.\n#\n# Same for unevidenced test claims: \"all three tests pass\" with\ \ no\n# transcript. validateNextWorkPackage refuses both shapes.\n#\n# Runs BEFORE the node_modules\ - \ cleanup below, which needs sdk/dist.\nif [ -f ops/NEXT.md ] && [ -f sdk/dist/index.js ]; then\n\ + \ cleanup below, which needs packages/sdk/dist.\nif [ -f ops/NEXT.md ] && [ -f packages/sdk/dist/index.js ]; then\n\ \ nextout=$(node -e '\n const fs = require(\"node:fs\");\n const { validateNextWorkPackage\ \ } = require(\"./sdk/dist/index.js\");\n const verdict = validateNextWorkPackage(\n fs.readFileSync(\"\ ops/NEXT.md\", \"utf8\"),\n (p) => fs.existsSync(p),\n );\n if (!verdict.accepted) {\n\ @@ -268,8 +268,8 @@ workflows: # a PR. A run that delivers needs its flush to work whether or not\n# its gates passed, and node_modules\ \ is reinstallable, so there was\n# never anything to preserve for diagnosis here.\n#\n# Guarded\ \ with || true and placed AFTER the pass/fail decision is\n# computed, so a cleanup problem can\ - \ never change the verdict.\nrm -rf sdk/node_modules sdk/dist surface/dist 2>/dev/null || true\n\ - echo \"VERIFY_TREE_SLIMMED: removed sdk/node_modules and generated dist trees before flush (ok=$ok)\"\ + \ never change the verdict.\nrm -rf packages/sdk/node_modules packages/sdk/dist packages/surface/dist 2>/dev/null || true\n\ + echo \"VERIFY_TREE_SLIMMED: removed packages/sdk/node_modules and generated dist trees before flush (ok=$ok)\"\ \n\n# Measure the tree instead of guessing at it.\n#\n# The relayfile flush keeps failing (http\ \ 413) and three successive\n# hypotheses about WHY were wrong: kernel/target was removed and the\n\ # count barely moved; node_modules was removed and it still failed;\n# HOME turned out to be /home/daytona,\ diff --git a/workflows/drive.yaml b/workflows/drive.yaml index 9bbda344e..46db76219 100644 --- a/workflows/drive.yaml +++ b/workflows/drive.yaml @@ -20,7 +20,7 @@ agents: - name: builder cli: codex preset: worker - role: Implements the work package. Rust for kernel/, TypeScript for sdk/. + role: Implements the work package. Rust for kernel/, TypeScript for packages/sdk/. - name: adversary cli: claude preset: reviewer @@ -153,7 +153,7 @@ workflows: if one is unsatisfied, it IS the work package. Then read docs/bootstrap-report.md and ops/DRIVE-LOG.md if they exist, `git log --oneline -15`, `gh pr list --state open` and open PR review - state, kernel/ and sdk/ test status. Then write ops/NEXT.md: the + state, kernel/ and packages/sdk/ test status. Then write ops/NEXT.md: the SINGLE highest-priority work package toward the current gate (gate 1 until its done-when in RFC-0001 §3 holds), with: objective, files in scope, definition of done (must include passing commands), @@ -346,12 +346,12 @@ workflows: fi [ $rc -eq 0 ] || ok=1 fi - if [ -f sdk/package.json ]; then + if [ -f packages/sdk/package.json ]; then # node_modules is not in `git ls-files`, so a sandbox has none. # Install before testing, and treat a failed install as a failed # verify rather than letting `npm test` report a confusing error. - if [ ! -d sdk/node_modules ]; then - echo "VERIFY_INSTALL: sdk/node_modules absent — installing" + if [ ! -d packages/sdk/node_modules ]; then + echo "VERIFY_INSTALL: packages/sdk/node_modules absent — installing" out=$(cd sdk && run_bounded "npm ci" npm ci 2>&1); rc=$? if [ $rc -ne 0 ]; then echo "$out" | tail -12 @@ -365,8 +365,8 @@ workflows: # the same fault that left ops/cargo.sh non-executable. Which # layer drops it is NOT established; this repairs the symptom # where it is observed and is a no-op where the bit survives. - chmod -R +x sdk/node_modules/.bin 2>/dev/null || true - find sdk/node_modules -type d -name bin -path "*esbuild*" \ + chmod -R +x packages/sdk/node_modules/.bin 2>/dev/null || true + find packages/sdk/node_modules -type d -name bin -path "*esbuild*" \ -exec chmod -R +x {} + 2>/dev/null || true out=$(cd sdk && run_bounded "sdk suite" npm test 2>&1); rc=$? echo "$out" | tail -8; ran=1 @@ -390,8 +390,8 @@ workflows: # Same for unevidenced test claims: "all three tests pass" with no # transcript. validateNextWorkPackage refuses both shapes. # - # Runs BEFORE the node_modules cleanup below, which needs sdk/dist. - if [ -f ops/NEXT.md ] && [ -f sdk/dist/index.js ]; then + # Runs BEFORE the node_modules cleanup below, which needs packages/sdk/dist. + if [ -f ops/NEXT.md ] && [ -f packages/sdk/dist/index.js ]; then nextout=$(node -e ' const fs = require("node:fs"); const { validateNextWorkPackage } = require("./sdk/dist/index.js"); @@ -443,8 +443,8 @@ workflows: # # Guarded with || true and placed AFTER the pass/fail decision is # computed, so a cleanup problem can never change the verdict. - rm -rf sdk/node_modules sdk/dist surface/dist 2>/dev/null || true - echo "VERIFY_TREE_SLIMMED: removed sdk/node_modules and generated dist trees before flush (ok=$ok)" + rm -rf packages/sdk/node_modules packages/sdk/dist packages/surface/dist 2>/dev/null || true + echo "VERIFY_TREE_SLIMMED: removed packages/sdk/node_modules and generated dist trees before flush (ok=$ok)" # Measure the tree instead of guessing at it. # From ff50419a1a9a3bbedf7bd4e980cb966be2a2f5a8 Mon Sep 17 00:00:00 2001 From: kjgbot Date: Sun, 6 Sep 2026 12:57:52 +0200 Subject: [PATCH 2/7] fix(layout): migrate the path references the first sweep could not see MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1 --- examples/research/tsconfig.json | 2 +- ops/probes/pr134-repair-0903/gate-cost.mjs | 4 ++-- ops/probes/pr134-repair-0903/harness.mjs | 6 +++--- ops/probes/pr134-repair-0903/promise-all-semantics.mjs | 2 +- ops/probes/pr134-repair-0903/verb-output-fields.mjs | 4 ++-- regressions/tsconfig.json | 2 +- scripts/surface-package-gate.sh | 10 +++++----- workflows/drive-cloud.yaml | 6 +++--- workflows/drive.yaml | 6 +++--- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/examples/research/tsconfig.json b/examples/research/tsconfig.json index 2d183a36d..cd3fa677a 100644 --- a/examples/research/tsconfig.json +++ b/examples/research/tsconfig.json @@ -11,7 +11,7 @@ "allowImportingTsExtensions": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, - "typeRoots": ["../../sdk/node_modules/@types"], + "typeRoots": ["../../packages/sdk/node_modules/@types"], "types": ["node"] }, "include": ["**/*.ts"] diff --git a/ops/probes/pr134-repair-0903/gate-cost.mjs b/ops/probes/pr134-repair-0903/gate-cost.mjs index 15a4bd61b..30c0ee609 100644 --- a/ops/probes/pr134-repair-0903/gate-cost.mjs +++ b/ops/probes/pr134-repair-0903/gate-cost.mjs @@ -5,8 +5,8 @@ import { fileURLToPath } from 'node:url'; const REPO = resolve(dirname(fileURLToPath(import.meta.url)), '../../..'); const DIST = process.argv[2] ?? 'dist'; const N = Number(process.argv[3] ?? 30000); -const { AuthoredFlowOperation, verifyAuthoredOperations } = await import(`${REPO}/sdk/${DIST}/authored-flow-operation.js`); -const { AuthoredFlowLifecycle } = await import(`${REPO}/sdk/${DIST}/authored-flow-lifecycle.js`); +const { AuthoredFlowOperation, verifyAuthoredOperations } = await import(`${REPO}/packages/sdk/${DIST}/authored-flow-operation.js`); +const { AuthoredFlowLifecycle } = await import(`${REPO}/packages/sdk/${DIST}/authored-flow-lifecycle.js`); const lc = new AuthoredFlowLifecycle(); const ops = []; diff --git a/ops/probes/pr134-repair-0903/harness.mjs b/ops/probes/pr134-repair-0903/harness.mjs index a0362fdc1..a45bf6fa6 100644 --- a/ops/probes/pr134-repair-0903/harness.mjs +++ b/ops/probes/pr134-repair-0903/harness.mjs @@ -12,9 +12,9 @@ import { fileURLToPath } from 'node:url'; import { rmSync } from 'node:fs'; const REPO = resolve(dirname(fileURLToPath(import.meta.url)), '../../..'); -export const { executeAuthoredFlow } = await import(`${REPO}/sdk/dist/authored-flow-executor.js`); -export const { JournalClient } = await import(`${REPO}/sdk/dist/journal-client.js`); -export const { flow } = await import(`${REPO}/surface/dist/index.js`); +export const { executeAuthoredFlow } = await import(`${REPO}/packages/sdk/dist/authored-flow-executor.js`); +export const { JournalClient } = await import(`${REPO}/packages/sdk/dist/journal-client.js`); +export const { flow } = await import(`${REPO}/packages/surface/dist/index.js`); function sockPath() { return join(tmpdir(), `rf-${randomUUID().slice(0, 8)}.sock`); } diff --git a/ops/probes/pr134-repair-0903/promise-all-semantics.mjs b/ops/probes/pr134-repair-0903/promise-all-semantics.mjs index bc710e26a..d7be13837 100644 --- a/ops/probes/pr134-repair-0903/promise-all-semantics.mjs +++ b/ops/probes/pr134-repair-0903/promise-all-semantics.mjs @@ -5,7 +5,7 @@ import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; const REPO = resolve(dirname(fileURLToPath(import.meta.url)), '../../..'); const DIST = process.argv[2] ?? 'dist'; -const { AuthoredFlowLifecycle } = await import(`${REPO}/sdk/${DIST}/authored-flow-lifecycle.js`); +const { AuthoredFlowLifecycle } = await import(`${REPO}/packages/sdk/${DIST}/authored-flow-lifecycle.js`); const nativeAll = Promise.all; const lc = new AuthoredFlowLifecycle(); diff --git a/ops/probes/pr134-repair-0903/verb-output-fields.mjs b/ops/probes/pr134-repair-0903/verb-output-fields.mjs index 87ca70281..c02141776 100644 --- a/ops/probes/pr134-repair-0903/verb-output-fields.mjs +++ b/ops/probes/pr134-repair-0903/verb-output-fields.mjs @@ -5,8 +5,8 @@ import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { mkdirSync, writeFileSync } from 'node:fs'; const REPO = resolve(dirname(fileURLToPath(import.meta.url)), '../../..'); -const { validateSpec } = await import(`${REPO}/sdk/dist/validate.js`); -const { compileYaml, toKernelSpec } = await import(`${REPO}/sdk/dist/compile.js`); +const { validateSpec } = await import(`${REPO}/packages/sdk/dist/validate.js`); +const { compileYaml, toKernelSpec } = await import(`${REPO}/packages/sdk/dist/compile.js`); // Documented shape (docs/SURFACE.md): the JSON Schema sits directly under `output`. const OUTPUT = { type: 'object', properties: { verdict: { type: 'string' } }, required: ['verdict'] }; diff --git a/regressions/tsconfig.json b/regressions/tsconfig.json index 7aaf71e39..1c01dd6b2 100644 --- a/regressions/tsconfig.json +++ b/regressions/tsconfig.json @@ -6,7 +6,7 @@ "moduleResolution": "Bundler", "baseUrl": ".", "paths": { - "@relayflows/surface": ["../surface/src/index.ts"] + "@relayflows/surface": ["../packages/surface/src/index.ts"] }, "lib": ["ES2022"], "strict": true, diff --git a/scripts/surface-package-gate.sh b/scripts/surface-package-gate.sh index dd8636424..9f17c19fc 100755 --- a/scripts/surface-package-gate.sh +++ b/scripts/surface-package-gate.sh @@ -6,15 +6,15 @@ pack_dir="$(mktemp -d /tmp/relayflows-surface-pack.XXXXXX)" consumer_dir="$(mktemp -d /tmp/relayflows-surface-consumer.XXXXXX)" trap 'rm -rf "$pack_dir" "$consumer_dir"' EXIT -cd "$repo_root/surface" +cd "$repo_root/packages/surface" bun install --frozen-lockfile --ignore-scripts bun run build bun run test bun run typecheck:regressions bun pm pack --destination "$pack_dir" -npm ci --prefix "$repo_root/sdk" --ignore-scripts -npm run typecheck --prefix "$repo_root/sdk" +npm ci --prefix "$repo_root/packages/sdk" --ignore-scripts +npm run typecheck --prefix "$repo_root/packages/sdk" tarball="$(find "$pack_dir" -maxdepth 1 -type f -name '*.tgz' -print -quit)" if [[ -z "$tarball" ]]; then @@ -158,8 +158,8 @@ cat > tsconfig.consumer.json <<'JSON' } JSON -"$repo_root/surface/node_modules/.bin/tsc" -p tsconfig.consumer.json +"$repo_root/packages/surface/node_modules/.bin/tsc" -p tsconfig.consumer.json echo "PACKED_TYPESCRIPT_OK" -cd "$repo_root/sdk" +cd "$repo_root/packages/sdk" ./node_modules/.bin/vitest run tests/authored-flow.test.ts diff --git a/workflows/drive-cloud.yaml b/workflows/drive-cloud.yaml index 0cdd8ed62..3fdbb688a 100644 --- a/workflows/drive-cloud.yaml +++ b/workflows/drive-cloud.yaml @@ -225,7 +225,7 @@ workflows: \ `git ls-files`, so a sandbox has none.\n # Install before testing, and treat a failed install\ \ as a failed\n # verify rather than letting `npm test` report a confusing error.\n if [ ! -d\ \ packages/sdk/node_modules ]; then\n echo \"VERIFY_INSTALL: packages/sdk/node_modules absent \u2014 installing\"\ - \n out=$(cd sdk && run_bounded \"npm ci\" npm ci 2>&1); rc=$?\n if [ $rc -ne 0 ]; then\n \ + \n out=$(cd packages/sdk && run_bounded \"npm ci\" npm ci 2>&1); rc=$?\n if [ $rc -ne 0 ]; then\n \ \ echo \"$out\" | tail -12\n echo \"VERIFY_FAIL: npm ci failed \u2014 cannot test what\ \ did not install\"\n exit 1\n fi\n fi\n # Restore exec bits. `npm ci` reported success\ \ (96 packages) and\n # esbuild still failed with EACCES on run 909e18f6, so the mount\n # this\ @@ -233,7 +233,7 @@ workflows: \ non-executable. Which\n # layer drops it is NOT established; this repairs the symptom\n # where\ \ it is observed and is a no-op where the bit survives.\n chmod -R +x packages/sdk/node_modules/.bin 2>/dev/null\ \ || true\n find packages/sdk/node_modules -type d -name bin -path \"*esbuild*\" \\\n -exec chmod -R\ - \ +x {} + 2>/dev/null || true\n out=$(cd sdk && run_bounded \"sdk suite\" npm test 2>&1); rc=$?\n\ + \ +x {} + 2>/dev/null || true\n out=$(cd packages/sdk && run_bounded \"sdk suite\" npm test 2>&1); rc=$?\n\ \ echo \"$out\" | tail -8; ran=1\n if [ $rc -eq 124 ]; then\n echo \"VERIFY_SUITE_TIMEOUT:\ \ the sdk suite exceeded ${VERIFY_SUITE_TIMEOUT:-900}s and was killed.\"\n fi\n [ $rc -eq 0 ]\ \ || ok=1\nfi\nif [ \"$ran\" -eq 0 ]; then\n echo \"VERIFY_FAIL: no suite found \u2014 a verify\ @@ -246,7 +246,7 @@ workflows: \ no\n# transcript. validateNextWorkPackage refuses both shapes.\n#\n# Runs BEFORE the node_modules\ \ cleanup below, which needs packages/sdk/dist.\nif [ -f ops/NEXT.md ] && [ -f packages/sdk/dist/index.js ]; then\n\ \ nextout=$(node -e '\n const fs = require(\"node:fs\");\n const { validateNextWorkPackage\ - \ } = require(\"./sdk/dist/index.js\");\n const verdict = validateNextWorkPackage(\n fs.readFileSync(\"\ + \ } = require(\"./packages/sdk/dist/index.js\");\n const verdict = validateNextWorkPackage(\n fs.readFileSync(\"\ ops/NEXT.md\", \"utf8\"),\n (p) => fs.existsSync(p),\n );\n if (!verdict.accepted) {\n\ \ console.log(\"NEXT_REFUSED \" + verdict.reason);\n process.exit(1);\n }\n console.log(\"\ NEXT_OK\");\n ' 2>&1) || true\n echo \"$nextout\" | tail -2\n case \"$nextout\" in\n *NEXT_REFUSED*)\n\ diff --git a/workflows/drive.yaml b/workflows/drive.yaml index 46db76219..a3482dd8a 100644 --- a/workflows/drive.yaml +++ b/workflows/drive.yaml @@ -352,7 +352,7 @@ workflows: # verify rather than letting `npm test` report a confusing error. if [ ! -d packages/sdk/node_modules ]; then echo "VERIFY_INSTALL: packages/sdk/node_modules absent — installing" - out=$(cd sdk && run_bounded "npm ci" npm ci 2>&1); rc=$? + out=$(cd packages/sdk && run_bounded "npm ci" npm ci 2>&1); rc=$? if [ $rc -ne 0 ]; then echo "$out" | tail -12 echo "VERIFY_FAIL: npm ci failed — cannot test what did not install" @@ -368,7 +368,7 @@ workflows: chmod -R +x packages/sdk/node_modules/.bin 2>/dev/null || true find packages/sdk/node_modules -type d -name bin -path "*esbuild*" \ -exec chmod -R +x {} + 2>/dev/null || true - out=$(cd sdk && run_bounded "sdk suite" npm test 2>&1); rc=$? + out=$(cd packages/sdk && run_bounded "sdk suite" npm test 2>&1); rc=$? echo "$out" | tail -8; ran=1 if [ $rc -eq 124 ]; then echo "VERIFY_SUITE_TIMEOUT: the sdk suite exceeded ${VERIFY_SUITE_TIMEOUT:-900}s and was killed." @@ -394,7 +394,7 @@ workflows: if [ -f ops/NEXT.md ] && [ -f packages/sdk/dist/index.js ]; then nextout=$(node -e ' const fs = require("node:fs"); - const { validateNextWorkPackage } = require("./sdk/dist/index.js"); + const { validateNextWorkPackage } = require("./packages/sdk/dist/index.js"); const verdict = validateNextWorkPackage( fs.readFileSync("ops/NEXT.md", "utf8"), (p) => fs.existsSync(p), From 1c7f85b8d06ed644268a7aaedb434eb64a627b8c Mon Sep 17 00:00:00 2001 From: kjgbot Date: Sun, 6 Sep 2026 13:08:52 +0200 Subject: [PATCH 3/7] 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) Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1 --- packages/sdk/package.json | 2 +- packages/surface/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sdk/package.json b/packages/sdk/package.json index def01f057..65dcb2037 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -24,7 +24,7 @@ "prepare": "npm run build", "typecheck": "tsc --noEmit && tsc -p tsconfig.type-tests.json", "typecheck:tests": "tsc -p tsconfig.tests.json", - "test:prep": "( cd ../kernel && sh ../ops/cargo.sh build ) && ( [ ! -d ../testdata/preflight ] || find ../testdata/preflight -name '*-cli' -type f -exec chmod +x {} + )", + "test:prep": "( cd ../../kernel && sh ../ops/cargo.sh build ) && ( [ ! -d ../../testdata/preflight ] || find ../../testdata/preflight -name '*-cli' -type f -exec chmod +x {} + )", "test": "sh scripts/test.sh", "test:watch": "vitest" }, diff --git a/packages/surface/package.json b/packages/surface/package.json index adb35088d..a9a94f64e 100644 --- a/packages/surface/package.json +++ b/packages/surface/package.json @@ -23,7 +23,7 @@ "build": "tsc", "prepare": "bun run build", "typecheck": "tsc --noEmit", - "typecheck:regressions": "tsc -p ../regressions/tsconfig.json", + "typecheck:regressions": "tsc -p ../../regressions/tsconfig.json", "test": "bun run build && tsc -p tsconfig.test.json && vitest run" }, "license": "Apache-2.0", From 5cdab76b1c80a058da76cd6b21502485c2feaca4 Mon Sep 17 00:00:00 2001 From: kjgbot Date: Sun, 6 Sep 2026 13:22:13 +0200 Subject: [PATCH 4/7] 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) Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1 --- .github/workflows/cloud-runtime-artifact.yml | 6 +++--- testdata/backlog-picker.flow.yaml | 2 +- workflows/bootstrap-gate1.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cloud-runtime-artifact.yml b/.github/workflows/cloud-runtime-artifact.yml index 8cfab4e15..1bc53302b 100644 --- a/.github/workflows/cloud-runtime-artifact.yml +++ b/.github/workflows/cloud-runtime-artifact.yml @@ -81,7 +81,7 @@ jobs: run: cargo test --workspace - name: Build authoring surface - working-directory: surface + working-directory: packages/surface run: | bun install --frozen-lockfile --ignore-scripts bun run build @@ -90,10 +90,10 @@ jobs: # npm runs the file: dependency's prepare before its own devDependencies # exist. The SDK's own build is the next step, so nothing is skipped. - name: Install SDK dependencies - run: npm ci --prefix sdk --ignore-scripts + run: npm ci --prefix packages/sdk --ignore-scripts - name: Test SDK and type-level authoring contracts - working-directory: sdk + working-directory: packages/sdk env: # `live-kernel.test.ts` runs one case against the REAL Claude analyzer # and fails by default when it cannot, deliberately: an unavailable diff --git a/testdata/backlog-picker.flow.yaml b/testdata/backlog-picker.flow.yaml index 41c8209f4..1dea71efe 100644 --- a/testdata/backlog-picker.flow.yaml +++ b/testdata/backlog-picker.flow.yaml @@ -10,7 +10,7 @@ steps: - id: build-sdk type: deterministic dependsOn: [read-backlog] - command: "cd sdk && npm ci --silent --no-audit --no-fund && npm run build --silent" + command: "cd packages/sdk && npm ci --silent --no-audit --no-fund && npm run build --silent" - id: select-entry type: deterministic dependsOn: [read-backlog, build-sdk] diff --git a/workflows/bootstrap-gate1.yaml b/workflows/bootstrap-gate1.yaml index 1aa6e3540..4aa6c8ed9 100644 --- a/workflows/bootstrap-gate1.yaml +++ b/workflows/bootstrap-gate1.yaml @@ -122,7 +122,7 @@ workflows: - name: sdk-tests type: deterministic dependsOn: [sdk-skeleton] - command: cd sdk && npm install --no-audit --no-fund >/dev/null 2>&1 && npm test 2>&1 | tail -15 + command: cd packages/sdk && npm install --no-audit --no-fund >/dev/null 2>&1 && npm test 2>&1 | tail -15 timeoutMs: 900000 - name: adversarial-review From d380008710b2245c4b74d802185791ed0eb888cd Mon Sep 17 00:00:00 2001 From: kjgbot Date: Sun, 6 Sep 2026 13:22:22 +0200 Subject: [PATCH 5/7] feat(release): build and verify ordered OIDC npm releases Session-Id: 01a0766a-1a37-73d1-9f4f-f3f5e435c735 --- .github/workflows/publish.yml | 197 +++++++++++++++++++++++++++++ packages/sdk/package.json | 2 +- packages/surface/package.json | 2 +- scripts/assert-release-package.mjs | 45 +++++++ scripts/version-packages.mjs | 30 +++++ 5 files changed, 274 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/publish.yml create mode 100644 scripts/assert-release-package.mjs create mode 100644 scripts/version-packages.mjs diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 000000000..48db7d45d --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,197 @@ +name: Publish Package + +on: + pull_request: + paths: + - .github/workflows/publish.yml + - scripts/version-packages.mjs + - scripts/assert-release-package.mjs + - packages/** + - kernel/** + workflow_dispatch: + inputs: + package: + description: Package to publish (single-package selection is dry-run only) + required: true + type: choice + options: [all, surface, sdk, runtime-linux-x64] + default: all + version: + description: Version bump type + required: true + type: choice + options: [patch, minor, major, prepatch, preminor, premajor, prerelease] + default: patch + custom_version: + description: Custom version (optional, overrides version type) + required: false + type: string + preid: + description: Prerelease identifier (used with pre* version types) + required: false + type: choice + options: [beta, alpha, rc] + default: beta + dry_run: + description: Dry run (do not actually publish) + required: false + type: boolean + default: true + tag: + description: NPM dist-tag + required: false + type: choice + options: [latest, next, beta, alpha, rc] + default: latest + +concurrency: + group: publish-package-${{ github.event_name == 'pull_request' && github.event.pull_request.number || 'release' }} + cancel-in-progress: false + +permissions: + contents: write + id-token: write + +env: + NPM_CONFIG_FUND: 'false' + +jobs: + publish: + name: Build, version, verify & publish + runs-on: ubuntu-24.04 + timeout-minutes: 30 + env: + PACKAGE: ${{ inputs.package || 'all' }} + # PRs always exercise the real build, pack, and smoke steps without publishing. + DRY_RUN: ${{ github.event_name != 'workflow_dispatch' || inputs.dry_run }} + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '22' + registry-url: https://registry.npmjs.org + + - uses: oven-sh/setup-bun@v2 + with: + bun-version: '1.4.0' + + - uses: dtolnay/rust-toolchain@stable + + # npm >=11.5.1 supports trusted publishing. No npm token is used. + # Each npm package must trust AgentWorkforce/flows / publish.yml. + - name: Update npm for OIDC support + run: npm install -g npm@11 + + # Like relayfile, the anchor and all internal dependencies move together. + # A partial real release would leave dependencies at unpublished versions. + - name: Validate release mode + run: | + if [[ "$PACKAGE" != all && "$DRY_RUN" != true ]]; then + echo 'Real releases require package=all; use dry_run for individual packages.' >&2 + exit 1 + fi + + - name: Version all packages + id: bump + env: + CUSTOM_VERSION: ${{ inputs.custom_version }} + VERSION_TYPE: ${{ inputs.version || 'patch' }} + PREID: ${{ inputs.preid || 'beta' }} + DIST_TAG: ${{ inputs.tag || 'latest' }} + run: node scripts/version-packages.mjs + + - name: Build authoring surface + working-directory: packages/surface + run: | + bun install --frozen-lockfile --ignore-scripts + bun run build + + - name: Pack and assert surface + run: | + mkdir -p dist/npm + npm pack ./packages/surface --ignore-scripts --pack-destination dist/npm + node scripts/assert-release-package.mjs surface dist/npm + + # Install the exact built surface, without a source symlink or saving a + # file: dependency. The new version need not exist on npm, even in dry runs. + - name: Build SDK against packed surface + env: + NEW_VERSION: ${{ steps.bump.outputs.new_version }} + run: | + npm install --prefix packages/sdk --ignore-scripts --no-save --package-lock=false \ + "$PWD/dist/npm/relayflows-surface-${NEW_VERSION}.tgz" + test ! -L packages/sdk/node_modules/@relayflows/surface + npm run build --prefix packages/sdk + + - name: Pack and assert SDK + run: | + npm pack ./packages/sdk --ignore-scripts --pack-destination dist/npm + node scripts/assert-release-package.mjs sdk dist/npm + + - name: Build relayflowd + working-directory: kernel + run: cargo build --locked --release -p relayflowd + + - name: Build and stage runtime binaries + run: | + mkdir -p packages/runtime-linux-x64/bin + cp kernel/target/release/relayflowd packages/runtime-linux-x64/bin/relayflowd + bun build packages/sdk/src/cli-executable.ts --compile --target=bun-linux-x64 \ + --outfile=packages/runtime-linux-x64/bin/flows + chmod +x packages/runtime-linux-x64/bin/relayflowd packages/runtime-linux-x64/bin/flows + + - name: Pack and assert runtime + run: | + npm pack ./packages/runtime-linux-x64 --ignore-scripts --pack-destination dist/npm + node scripts/assert-release-package.mjs runtime-linux-x64 dist/npm + + # Execute the actual packed binaries, also on dry runs. Artifact upload + # would otherwise hide lost executable bits and omitted binary files. + - name: Smoke packed packages + env: + NEW_VERSION: ${{ steps.bump.outputs.new_version }} + run: | + mkdir -p dist/runtime-smoke + tar -xzf "dist/npm/relayflows-runtime-linux-x64-${NEW_VERSION}.tgz" -C dist/runtime-smoke + test -x dist/runtime-smoke/package/bin/relayflowd + test -x dist/runtime-smoke/package/bin/flows + dist/runtime-smoke/package/bin/relayflowd --help + dist/runtime-smoke/package/bin/flows check --json testdata/hello-deterministic.flow.yaml > dist/runtime-smoke/check.json + node -e ' + const report = require("./dist/runtime-smoke/check.json"); + if (report.ok !== true || report.path !== "testdata/hello-deterministic.flow.yaml") { + throw new Error("packed flows smoke failed: " + JSON.stringify(report)); + } + console.log("PACKED_RUNTIME_OK", JSON.stringify(report)); + ' + consumer="$(mktemp -d)" + npm install --prefix "$consumer" --ignore-scripts --no-audit --no-fund \ + "$PWD/dist/npm/relayflows-surface-${NEW_VERSION}.tgz" \ + "$PWD/dist/npm/relayflows-sdk-${NEW_VERSION}.tgz" + node --input-type=module -e "await import('file://$consumer/node_modules/@relayflows/surface/dist/index.js'); await import('file://$consumer/node_modules/@relayflows/sdk/dist/index.js'); console.log('PACKED_IMPORTS_OK')" + node "$consumer/node_modules/@relayflows/sdk/dist/cli.js" check --json testdata/hello-deterministic.flow.yaml + + - name: Upload verified tarballs + uses: actions/upload-artifact@v4 + with: + name: npm-packages-${{ steps.bump.outputs.new_version }} + path: dist/npm/*.tgz + if-no-files-found: error + retention-days: 14 + + # Publish the inspected tarballs, sequentially in dependency order. + - name: Publish to NPM (or dry run) + env: + NEW_VERSION: ${{ steps.bump.outputs.new_version }} + DIST_TAG: ${{ inputs.tag || 'latest' }} + run: | + for package in surface sdk runtime-linux-x64; do + if [[ "$PACKAGE" != all && "$PACKAGE" != "$package" ]]; then continue; fi + tarball="dist/npm/relayflows-${package}-${NEW_VERSION}.tgz" + if [[ "$DRY_RUN" == true ]]; then + npm publish "$tarball" --dry-run --access public --ignore-scripts --tag "$DIST_TAG" + else + npm publish "$tarball" --access public --provenance --ignore-scripts --tag "$DIST_TAG" + fi + done diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 65dcb2037..17300a2cb 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -45,6 +45,6 @@ "repository": { "type": "git", "url": "git+https://github.com/AgentWorkforce/flows.git", - "directory": "sdk" + "directory": "packages/sdk" } } diff --git a/packages/surface/package.json b/packages/surface/package.json index a9a94f64e..a4d1a8094 100644 --- a/packages/surface/package.json +++ b/packages/surface/package.json @@ -34,6 +34,6 @@ "repository": { "type": "git", "url": "git+https://github.com/AgentWorkforce/flows.git", - "directory": "surface" + "directory": "packages/surface" } } diff --git a/scripts/assert-release-package.mjs b/scripts/assert-release-package.mjs new file mode 100644 index 000000000..6b33b874b --- /dev/null +++ b/scripts/assert-release-package.mjs @@ -0,0 +1,45 @@ +import assert from 'node:assert/strict'; +import { execFileSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +const [name, directory] = process.argv.slice(2); +assert(['surface', 'sdk', 'runtime-linux-x64'].includes(name), 'Unknown release package'); +const source = JSON.parse(readFileSync(`packages/${name}/package.json`, 'utf8')); +const archive = join(directory, `relayflows-${name}-${source.version}.tgz`); +const entries = new Set(execFileSync('tar', ['-tzf', archive], { encoding: 'utf8' }).trim().split('\n')); +const pkg = JSON.parse(execFileSync('tar', ['-xOzf', archive, 'package/package.json'], { encoding: 'utf8' })); +assert.equal(pkg.name, `@relayflows/${name}`); +assert.equal(pkg.version, source.version); + +const requireFile = (path) => { + const entry = `package/${path.replace(/^\.\//, '')}`; + assert(entries.has(entry), `Missing ${entry} in ${archive}`); +}; +// Assert every advertised entrypoint, including declaration files and subpaths. +const walkExports = (value) => { + if (typeof value === 'string') requireFile(value); + else for (const child of Object.values(value || {})) walkExports(child); +}; +if (name === 'runtime-linux-x64') { + assert.deepEqual(pkg.os, ['linux']); + assert.deepEqual(pkg.cpu, ['x64']); + requireFile('bin/relayflowd'); + requireFile('bin/flows'); +} else { + assert.equal(pkg.main, './dist/index.js'); + requireFile('dist/index.js'); + requireFile(pkg.types); + walkExports(pkg.exports); +} +for (const path of Object.values(pkg.bin || {})) requireFile(path); +for (const field of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) { + for (const [dependency, version] of Object.entries(pkg[field] || {})) { + assert(!/^(file:|link:|workspace:|\.{1,2}\/|\/)/.test(version), `Local dependency ${dependency}: ${version}`); + if (['@relayflows/surface', '@relayflows/sdk', '@relayflows/runtime-linux-x64'].includes(dependency)) { + assert.equal(version, pkg.version, `Internal dependency ${dependency} must use release version`); + } + } +} +if (name === 'sdk') assert.equal(pkg.dependencies['@relayflows/surface'], pkg.version); +console.log(`PACKED_CONTENTS_OK ${pkg.name}@${pkg.version}`); diff --git a/scripts/version-packages.mjs b/scripts/version-packages.mjs new file mode 100644 index 000000000..1a67de5ba --- /dev/null +++ b/scripts/version-packages.mjs @@ -0,0 +1,30 @@ +import { appendFileSync, readFileSync, writeFileSync } from 'node:fs'; +import { execFileSync } from 'node:child_process'; + +const paths = ['surface', 'sdk', 'runtime-linux-x64'].map((name) => `packages/${name}/package.json`); +const read = (path) => JSON.parse(readFileSync(path, 'utf8')); +const names = new Set(paths.map((path) => read(path).name)); +const bump = process.env.CUSTOM_VERSION || process.env.VERSION_TYPE || 'patch'; +if (bump.startsWith('-')) throw new Error('Version cannot be an npm option'); + +// The SDK is the one release anchor. npm validates semver and performs all +// seven supported bumps; package-lock=false leaves the development lock alone. +execFileSync('npm', ['version', bump, '--no-git-tag-version', '--allow-same-version', + '--ignore-scripts', '--package-lock=false', `--preid=${process.env.PREID || 'beta'}`], +{ cwd: 'packages/sdk', stdio: 'inherit' }); +const version = read('packages/sdk/package.json').version; +if (version.includes('-') && process.env.DIST_TAG === 'latest') { + throw new Error('Prereleases require a non-latest dist-tag'); +} +for (const path of paths) { + const pkg = read(path); + pkg.version = version; + for (const field of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) { + for (const name of Object.keys(pkg[field] || {})) { + if (names.has(name)) pkg[field][name] = version; + } + } + writeFileSync(path, `${JSON.stringify(pkg, null, 2)}\n`); + console.log(`${pkg.name} -> ${version}`); +} +if (process.env.GITHUB_OUTPUT) appendFileSync(process.env.GITHUB_OUTPUT, `new_version=${version}\n`); From d9205673c8be13cd989c027e9ed5516d67013297 Mon Sep 17 00:00:00 2001 From: kjgbot Date: Sun, 6 Sep 2026 13:24:48 +0200 Subject: [PATCH 6/7] Revert "feat(release): build and verify ordered OIDC npm releases" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1 --- .github/workflows/publish.yml | 197 ----------------------------- packages/sdk/package.json | 2 +- packages/surface/package.json | 2 +- scripts/assert-release-package.mjs | 45 ------- scripts/version-packages.mjs | 30 ----- 5 files changed, 2 insertions(+), 274 deletions(-) delete mode 100644 .github/workflows/publish.yml delete mode 100644 scripts/assert-release-package.mjs delete mode 100644 scripts/version-packages.mjs diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 48db7d45d..000000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,197 +0,0 @@ -name: Publish Package - -on: - pull_request: - paths: - - .github/workflows/publish.yml - - scripts/version-packages.mjs - - scripts/assert-release-package.mjs - - packages/** - - kernel/** - workflow_dispatch: - inputs: - package: - description: Package to publish (single-package selection is dry-run only) - required: true - type: choice - options: [all, surface, sdk, runtime-linux-x64] - default: all - version: - description: Version bump type - required: true - type: choice - options: [patch, minor, major, prepatch, preminor, premajor, prerelease] - default: patch - custom_version: - description: Custom version (optional, overrides version type) - required: false - type: string - preid: - description: Prerelease identifier (used with pre* version types) - required: false - type: choice - options: [beta, alpha, rc] - default: beta - dry_run: - description: Dry run (do not actually publish) - required: false - type: boolean - default: true - tag: - description: NPM dist-tag - required: false - type: choice - options: [latest, next, beta, alpha, rc] - default: latest - -concurrency: - group: publish-package-${{ github.event_name == 'pull_request' && github.event.pull_request.number || 'release' }} - cancel-in-progress: false - -permissions: - contents: write - id-token: write - -env: - NPM_CONFIG_FUND: 'false' - -jobs: - publish: - name: Build, version, verify & publish - runs-on: ubuntu-24.04 - timeout-minutes: 30 - env: - PACKAGE: ${{ inputs.package || 'all' }} - # PRs always exercise the real build, pack, and smoke steps without publishing. - DRY_RUN: ${{ github.event_name != 'workflow_dispatch' || inputs.dry_run }} - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version: '22' - registry-url: https://registry.npmjs.org - - - uses: oven-sh/setup-bun@v2 - with: - bun-version: '1.4.0' - - - uses: dtolnay/rust-toolchain@stable - - # npm >=11.5.1 supports trusted publishing. No npm token is used. - # Each npm package must trust AgentWorkforce/flows / publish.yml. - - name: Update npm for OIDC support - run: npm install -g npm@11 - - # Like relayfile, the anchor and all internal dependencies move together. - # A partial real release would leave dependencies at unpublished versions. - - name: Validate release mode - run: | - if [[ "$PACKAGE" != all && "$DRY_RUN" != true ]]; then - echo 'Real releases require package=all; use dry_run for individual packages.' >&2 - exit 1 - fi - - - name: Version all packages - id: bump - env: - CUSTOM_VERSION: ${{ inputs.custom_version }} - VERSION_TYPE: ${{ inputs.version || 'patch' }} - PREID: ${{ inputs.preid || 'beta' }} - DIST_TAG: ${{ inputs.tag || 'latest' }} - run: node scripts/version-packages.mjs - - - name: Build authoring surface - working-directory: packages/surface - run: | - bun install --frozen-lockfile --ignore-scripts - bun run build - - - name: Pack and assert surface - run: | - mkdir -p dist/npm - npm pack ./packages/surface --ignore-scripts --pack-destination dist/npm - node scripts/assert-release-package.mjs surface dist/npm - - # Install the exact built surface, without a source symlink or saving a - # file: dependency. The new version need not exist on npm, even in dry runs. - - name: Build SDK against packed surface - env: - NEW_VERSION: ${{ steps.bump.outputs.new_version }} - run: | - npm install --prefix packages/sdk --ignore-scripts --no-save --package-lock=false \ - "$PWD/dist/npm/relayflows-surface-${NEW_VERSION}.tgz" - test ! -L packages/sdk/node_modules/@relayflows/surface - npm run build --prefix packages/sdk - - - name: Pack and assert SDK - run: | - npm pack ./packages/sdk --ignore-scripts --pack-destination dist/npm - node scripts/assert-release-package.mjs sdk dist/npm - - - name: Build relayflowd - working-directory: kernel - run: cargo build --locked --release -p relayflowd - - - name: Build and stage runtime binaries - run: | - mkdir -p packages/runtime-linux-x64/bin - cp kernel/target/release/relayflowd packages/runtime-linux-x64/bin/relayflowd - bun build packages/sdk/src/cli-executable.ts --compile --target=bun-linux-x64 \ - --outfile=packages/runtime-linux-x64/bin/flows - chmod +x packages/runtime-linux-x64/bin/relayflowd packages/runtime-linux-x64/bin/flows - - - name: Pack and assert runtime - run: | - npm pack ./packages/runtime-linux-x64 --ignore-scripts --pack-destination dist/npm - node scripts/assert-release-package.mjs runtime-linux-x64 dist/npm - - # Execute the actual packed binaries, also on dry runs. Artifact upload - # would otherwise hide lost executable bits and omitted binary files. - - name: Smoke packed packages - env: - NEW_VERSION: ${{ steps.bump.outputs.new_version }} - run: | - mkdir -p dist/runtime-smoke - tar -xzf "dist/npm/relayflows-runtime-linux-x64-${NEW_VERSION}.tgz" -C dist/runtime-smoke - test -x dist/runtime-smoke/package/bin/relayflowd - test -x dist/runtime-smoke/package/bin/flows - dist/runtime-smoke/package/bin/relayflowd --help - dist/runtime-smoke/package/bin/flows check --json testdata/hello-deterministic.flow.yaml > dist/runtime-smoke/check.json - node -e ' - const report = require("./dist/runtime-smoke/check.json"); - if (report.ok !== true || report.path !== "testdata/hello-deterministic.flow.yaml") { - throw new Error("packed flows smoke failed: " + JSON.stringify(report)); - } - console.log("PACKED_RUNTIME_OK", JSON.stringify(report)); - ' - consumer="$(mktemp -d)" - npm install --prefix "$consumer" --ignore-scripts --no-audit --no-fund \ - "$PWD/dist/npm/relayflows-surface-${NEW_VERSION}.tgz" \ - "$PWD/dist/npm/relayflows-sdk-${NEW_VERSION}.tgz" - node --input-type=module -e "await import('file://$consumer/node_modules/@relayflows/surface/dist/index.js'); await import('file://$consumer/node_modules/@relayflows/sdk/dist/index.js'); console.log('PACKED_IMPORTS_OK')" - node "$consumer/node_modules/@relayflows/sdk/dist/cli.js" check --json testdata/hello-deterministic.flow.yaml - - - name: Upload verified tarballs - uses: actions/upload-artifact@v4 - with: - name: npm-packages-${{ steps.bump.outputs.new_version }} - path: dist/npm/*.tgz - if-no-files-found: error - retention-days: 14 - - # Publish the inspected tarballs, sequentially in dependency order. - - name: Publish to NPM (or dry run) - env: - NEW_VERSION: ${{ steps.bump.outputs.new_version }} - DIST_TAG: ${{ inputs.tag || 'latest' }} - run: | - for package in surface sdk runtime-linux-x64; do - if [[ "$PACKAGE" != all && "$PACKAGE" != "$package" ]]; then continue; fi - tarball="dist/npm/relayflows-${package}-${NEW_VERSION}.tgz" - if [[ "$DRY_RUN" == true ]]; then - npm publish "$tarball" --dry-run --access public --ignore-scripts --tag "$DIST_TAG" - else - npm publish "$tarball" --access public --provenance --ignore-scripts --tag "$DIST_TAG" - fi - done diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 17300a2cb..65dcb2037 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -45,6 +45,6 @@ "repository": { "type": "git", "url": "git+https://github.com/AgentWorkforce/flows.git", - "directory": "packages/sdk" + "directory": "sdk" } } diff --git a/packages/surface/package.json b/packages/surface/package.json index a4d1a8094..a9a94f64e 100644 --- a/packages/surface/package.json +++ b/packages/surface/package.json @@ -34,6 +34,6 @@ "repository": { "type": "git", "url": "git+https://github.com/AgentWorkforce/flows.git", - "directory": "packages/surface" + "directory": "surface" } } diff --git a/scripts/assert-release-package.mjs b/scripts/assert-release-package.mjs deleted file mode 100644 index 6b33b874b..000000000 --- a/scripts/assert-release-package.mjs +++ /dev/null @@ -1,45 +0,0 @@ -import assert from 'node:assert/strict'; -import { execFileSync } from 'node:child_process'; -import { readFileSync } from 'node:fs'; -import { join } from 'node:path'; - -const [name, directory] = process.argv.slice(2); -assert(['surface', 'sdk', 'runtime-linux-x64'].includes(name), 'Unknown release package'); -const source = JSON.parse(readFileSync(`packages/${name}/package.json`, 'utf8')); -const archive = join(directory, `relayflows-${name}-${source.version}.tgz`); -const entries = new Set(execFileSync('tar', ['-tzf', archive], { encoding: 'utf8' }).trim().split('\n')); -const pkg = JSON.parse(execFileSync('tar', ['-xOzf', archive, 'package/package.json'], { encoding: 'utf8' })); -assert.equal(pkg.name, `@relayflows/${name}`); -assert.equal(pkg.version, source.version); - -const requireFile = (path) => { - const entry = `package/${path.replace(/^\.\//, '')}`; - assert(entries.has(entry), `Missing ${entry} in ${archive}`); -}; -// Assert every advertised entrypoint, including declaration files and subpaths. -const walkExports = (value) => { - if (typeof value === 'string') requireFile(value); - else for (const child of Object.values(value || {})) walkExports(child); -}; -if (name === 'runtime-linux-x64') { - assert.deepEqual(pkg.os, ['linux']); - assert.deepEqual(pkg.cpu, ['x64']); - requireFile('bin/relayflowd'); - requireFile('bin/flows'); -} else { - assert.equal(pkg.main, './dist/index.js'); - requireFile('dist/index.js'); - requireFile(pkg.types); - walkExports(pkg.exports); -} -for (const path of Object.values(pkg.bin || {})) requireFile(path); -for (const field of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) { - for (const [dependency, version] of Object.entries(pkg[field] || {})) { - assert(!/^(file:|link:|workspace:|\.{1,2}\/|\/)/.test(version), `Local dependency ${dependency}: ${version}`); - if (['@relayflows/surface', '@relayflows/sdk', '@relayflows/runtime-linux-x64'].includes(dependency)) { - assert.equal(version, pkg.version, `Internal dependency ${dependency} must use release version`); - } - } -} -if (name === 'sdk') assert.equal(pkg.dependencies['@relayflows/surface'], pkg.version); -console.log(`PACKED_CONTENTS_OK ${pkg.name}@${pkg.version}`); diff --git a/scripts/version-packages.mjs b/scripts/version-packages.mjs deleted file mode 100644 index 1a67de5ba..000000000 --- a/scripts/version-packages.mjs +++ /dev/null @@ -1,30 +0,0 @@ -import { appendFileSync, readFileSync, writeFileSync } from 'node:fs'; -import { execFileSync } from 'node:child_process'; - -const paths = ['surface', 'sdk', 'runtime-linux-x64'].map((name) => `packages/${name}/package.json`); -const read = (path) => JSON.parse(readFileSync(path, 'utf8')); -const names = new Set(paths.map((path) => read(path).name)); -const bump = process.env.CUSTOM_VERSION || process.env.VERSION_TYPE || 'patch'; -if (bump.startsWith('-')) throw new Error('Version cannot be an npm option'); - -// The SDK is the one release anchor. npm validates semver and performs all -// seven supported bumps; package-lock=false leaves the development lock alone. -execFileSync('npm', ['version', bump, '--no-git-tag-version', '--allow-same-version', - '--ignore-scripts', '--package-lock=false', `--preid=${process.env.PREID || 'beta'}`], -{ cwd: 'packages/sdk', stdio: 'inherit' }); -const version = read('packages/sdk/package.json').version; -if (version.includes('-') && process.env.DIST_TAG === 'latest') { - throw new Error('Prereleases require a non-latest dist-tag'); -} -for (const path of paths) { - const pkg = read(path); - pkg.version = version; - for (const field of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies']) { - for (const name of Object.keys(pkg[field] || {})) { - if (names.has(name)) pkg[field][name] = version; - } - } - writeFileSync(path, `${JSON.stringify(pkg, null, 2)}\n`); - console.log(`${pkg.name} -> ${version}`); -} -if (process.env.GITHUB_OUTPUT) appendFileSync(process.env.GITHUB_OUTPUT, `new_version=${version}\n`); From 235b948d01613c0ba54881e7ce5e3921c125aa16 Mon Sep 17 00:00:00 2001 From: kjgbot Date: Sun, 6 Sep 2026 13:43:38 +0200 Subject: [PATCH 7/7] fix(layout): update backlog-picker's canonical spec alongside its flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1 --- testdata/backlog-picker.spec.canonical.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testdata/backlog-picker.spec.canonical.json b/testdata/backlog-picker.spec.canonical.json index ff98fc9a0..a131257d6 100644 --- a/testdata/backlog-picker.spec.canonical.json +++ b/testdata/backlog-picker.spec.canonical.json @@ -1 +1 @@ -{"description":"Read ops/BACKLOG.md and deterministically emit its first work package.","name":"backlog-picker","steps":[{"command":"mkdir -p .relayflow && cat ops/BACKLOG.md > .relayflow/backlog-picker-source.md","depends_on":[],"id":"read-backlog","max_iterations":1,"retry":{"initial_backoff_ms":100,"jitter_percent":20,"max_backoff_ms":60000,"multiplier":2},"type":"deterministic","verification":{}},{"command":"cd sdk && npm ci --silent --no-audit --no-fund && npm run build --silent","depends_on":["read-backlog"],"id":"build-sdk","max_iterations":1,"retry":{"initial_backoff_ms":100,"jitter_percent":20,"max_backoff_ms":60000,"multiplier":2},"type":"deterministic","verification":{}},{"command":"node -e 'const fs=require(\"node:fs\");const P=require(\"node:path\"),F=require(\"node:fs\");const SDK=(()=>{if(process.env.RELAYFLOWS_SDK_DIST)return P.resolve(process.env.RELAYFLOWS_SDK_DIST,\"backlog-picker.js\");let c=process.cwd();for(;;){const f=P.join(c,\"sdk\",\"dist\",\"backlog-picker.js\");if(F.existsSync(f))return f;const up=P.dirname(c);if(up===c)throw new Error(\"SDK_DIST_NOT_FOUND: build the sdk or set RELAYFLOWS_SDK_DIST\");c=up}})();const {validateWorkPackage,packageFromEntry:pack}=require(SDK);fs.rmSync(\".relayflow/backlog-picker-entry.json\",{force:true});const text=fs.readFileSync(\".relayflow/backlog-picker-source.md\",\"utf8\");const entries=[...text.matchAll(/^- \\*\\*(.+?)\\*\\*\\s*(.*(?:\\n .*)*)/gm)].map(m=>({title:m[1],body:m[2].replace(/\\s+/g,\" \").trim()}));const skipped=[];for(const entry of entries){const verdict=validateWorkPackage(pack(entry));if(verdict.accepted){process.stderr.write(\"SKIPPED_UNACTIONABLE=\"+skipped.length+(skipped.length?\" \"+skipped.join(\"; \"):\"\")+\"\\n\");fs.writeFileSync(\".relayflow/backlog-picker-entry.json\",JSON.stringify(entry));process.stdout.write(JSON.stringify(entry));process.exit(0)}skipped.push(entry.title.slice(0,40)+\"[\"+verdict.reason+\"]\")}process.stderr.write(\"NO_ACTIONABLE_BACKLOG_ENTRY scanned=\"+entries.length+\" \"+skipped.join(\"; \")+\"\\n\");process.exit(1)'","depends_on":["read-backlog","build-sdk"],"id":"select-entry","max_iterations":1,"retry":{"initial_backoff_ms":100,"jitter_percent":20,"max_backoff_ms":60000,"multiplier":2},"type":"deterministic","verification":{}},{"command":"node -e 'const fs=require(\"node:fs\");const P=require(\"node:path\"),F=require(\"node:fs\");const SDK=(()=>{if(process.env.RELAYFLOWS_SDK_DIST)return P.resolve(process.env.RELAYFLOWS_SDK_DIST,\"backlog-picker.js\");let c=process.cwd();for(;;){const f=P.join(c,\"sdk\",\"dist\",\"backlog-picker.js\");if(F.existsSync(f))return f;const up=P.dirname(c);if(up===c)throw new Error(\"SDK_DIST_NOT_FOUND: build the sdk or set RELAYFLOWS_SDK_DIST\");c=up}})();const sdk=require(SDK);const entry=JSON.parse(fs.readFileSync(\".relayflow/backlog-picker-entry.json\",\"utf8\"));const pkg=sdk.packageFromEntry(entry);const verdict=sdk.validateWorkPackage(pkg);if(!verdict.accepted){process.stderr.write(\"REFUSED_MALFORMED_BACKLOG_ENTRY reason=\"+verdict.reason+\"\\n\");process.exit(1)}process.stdout.write(JSON.stringify(pkg))'","depends_on":["select-entry"],"id":"emit-package","max_iterations":1,"retry":{"initial_backoff_ms":100,"jitter_percent":20,"max_backoff_ms":60000,"multiplier":2},"type":"deterministic","verification":{}}],"version":"0.1.0"} +{"description":"Read ops/BACKLOG.md and deterministically emit its first work package.","name":"backlog-picker","steps":[{"command":"mkdir -p .relayflow && cat ops/BACKLOG.md > .relayflow/backlog-picker-source.md","depends_on":[],"id":"read-backlog","max_iterations":1,"retry":{"initial_backoff_ms":100,"jitter_percent":20,"max_backoff_ms":60000,"multiplier":2},"type":"deterministic","verification":{}},{"command":"cd packages/sdk && npm ci --silent --no-audit --no-fund && npm run build --silent","depends_on":["read-backlog"],"id":"build-sdk","max_iterations":1,"retry":{"initial_backoff_ms":100,"jitter_percent":20,"max_backoff_ms":60000,"multiplier":2},"type":"deterministic","verification":{}},{"command":"node -e 'const fs=require(\"node:fs\");const P=require(\"node:path\"),F=require(\"node:fs\");const SDK=(()=>{if(process.env.RELAYFLOWS_SDK_DIST)return P.resolve(process.env.RELAYFLOWS_SDK_DIST,\"backlog-picker.js\");let c=process.cwd();for(;;){const f=P.join(c,\"sdk\",\"dist\",\"backlog-picker.js\");if(F.existsSync(f))return f;const up=P.dirname(c);if(up===c)throw new Error(\"SDK_DIST_NOT_FOUND: build the sdk or set RELAYFLOWS_SDK_DIST\");c=up}})();const {validateWorkPackage,packageFromEntry:pack}=require(SDK);fs.rmSync(\".relayflow/backlog-picker-entry.json\",{force:true});const text=fs.readFileSync(\".relayflow/backlog-picker-source.md\",\"utf8\");const entries=[...text.matchAll(/^- \\*\\*(.+?)\\*\\*\\s*(.*(?:\\n .*)*)/gm)].map(m=>({title:m[1],body:m[2].replace(/\\s+/g,\" \").trim()}));const skipped=[];for(const entry of entries){const verdict=validateWorkPackage(pack(entry));if(verdict.accepted){process.stderr.write(\"SKIPPED_UNACTIONABLE=\"+skipped.length+(skipped.length?\" \"+skipped.join(\"; \"):\"\")+\"\\n\");fs.writeFileSync(\".relayflow/backlog-picker-entry.json\",JSON.stringify(entry));process.stdout.write(JSON.stringify(entry));process.exit(0)}skipped.push(entry.title.slice(0,40)+\"[\"+verdict.reason+\"]\")}process.stderr.write(\"NO_ACTIONABLE_BACKLOG_ENTRY scanned=\"+entries.length+\" \"+skipped.join(\"; \")+\"\\n\");process.exit(1)'","depends_on":["read-backlog","build-sdk"],"id":"select-entry","max_iterations":1,"retry":{"initial_backoff_ms":100,"jitter_percent":20,"max_backoff_ms":60000,"multiplier":2},"type":"deterministic","verification":{}},{"command":"node -e 'const fs=require(\"node:fs\");const P=require(\"node:path\"),F=require(\"node:fs\");const SDK=(()=>{if(process.env.RELAYFLOWS_SDK_DIST)return P.resolve(process.env.RELAYFLOWS_SDK_DIST,\"backlog-picker.js\");let c=process.cwd();for(;;){const f=P.join(c,\"sdk\",\"dist\",\"backlog-picker.js\");if(F.existsSync(f))return f;const up=P.dirname(c);if(up===c)throw new Error(\"SDK_DIST_NOT_FOUND: build the sdk or set RELAYFLOWS_SDK_DIST\");c=up}})();const sdk=require(SDK);const entry=JSON.parse(fs.readFileSync(\".relayflow/backlog-picker-entry.json\",\"utf8\"));const pkg=sdk.packageFromEntry(entry);const verdict=sdk.validateWorkPackage(pkg);if(!verdict.accepted){process.stderr.write(\"REFUSED_MALFORMED_BACKLOG_ENTRY reason=\"+verdict.reason+\"\\n\");process.exit(1)}process.stdout.write(JSON.stringify(pkg))'","depends_on":["select-entry"],"id":"emit-package","max_iterations":1,"retry":{"initial_backoff_ms":100,"jitter_percent":20,"max_backoff_ms":60000,"multiplier":2},"type":"deterministic","verification":{}}],"version":"0.1.0"}