From 52245de4a055a97a06d25ae6f2c376ff5c5f711d Mon Sep 17 00:00:00 2001 From: Elliot Braem Date: Fri, 9 Oct 2026 16:25:13 -0500 Subject: [PATCH 1/3] =?UTF-8?q?ci:=20relevance-gate=20workflows=20?= =?UTF-8?q?=E2=80=94=20skip=20deploys,=20release,=20and=20CI=20on=20non-re?= =?UTF-8?q?levant=20merges?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI's pull_request trigger now carries the same paths-ignore the push trigger already had — docs/changeset-only PRs skip the heavy lint/ typecheck job entirely. The docs link check that made that tradeoff lossy moved out of CI into its own zero-dependency workflow (no pnpm install): Docs Check runs on every PR — so it also catches links broken by renaming/deleting a file the markdown points at — and on docs-path pushes to main, which CI's path-ignore previously left completely unchecked. Deploy gains a relevance gate in the existing gate job: the merged diff (first-parent, exact for PR merges and single-commit pushes) must touch a runtime-affecting path — host/api/ui/plugins/packages/scripts, Dockerfile, railway.toml, authored config, root manifests, or the ci/deploy/release workflows themselves — or Deploy Production skips with a notice. Tests-only, .github-only, and markdown-only merges stop spending the full build/upload/publish/image/Railway train; version-PR merges still deploy (they bump packages/*). Uncertainty errs toward deploying, and workflow_dispatch bypasses the gate. Release paths-ignores markdown/docs merges (never .changeset — that is its input), the child-repo CI wrapper gains the same PR paths-ignore as the parent, and Check Skills gets a cancel-in-progress concurrency group so rapid pushes to one PR stop stacking intent validations. Gate regex was simulation-tested against real main history: version PRs, config-only bumps, and merge-tangle empty diffs deploy; the remaining skip surface is md/docs, tests/, and non-deploy .github paths. --- .github/templates/workflows/ci.yml | 6 +++++ .github/workflows/check-skills.yml | 4 +++ .github/workflows/ci.yml | 10 ++++--- .github/workflows/deploy.yml | 34 ++++++++++++++++++++++- .github/workflows/docs-check.yml | 43 ++++++++++++++++++++++++++++++ .github/workflows/release.yml | 6 +++++ 6 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/docs-check.yml diff --git a/.github/templates/workflows/ci.yml b/.github/templates/workflows/ci.yml index 5cad1d5fd..59d7ffadb 100644 --- a/.github/templates/workflows/ci.yml +++ b/.github/templates/workflows/ci.yml @@ -12,6 +12,12 @@ on: - "docs/**" - ".changeset/**" pull_request: + # Docs-only PRs cannot affect lint/typecheck/tests; changeset-only PRs + # have nothing to validate here. + paths-ignore: + - "**.md" + - "docs/**" + - ".changeset/**" workflow_dispatch: permissions: diff --git a/.github/workflows/check-skills.yml b/.github/workflows/check-skills.yml index e3952edf9..8010df2d1 100644 --- a/.github/workflows/check-skills.yml +++ b/.github/workflows/check-skills.yml @@ -28,6 +28,10 @@ on: types: [published] workflow_dispatch: {} +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: validate: name: Validate intent skills diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 45c2ef454..f0aff253a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,13 @@ on: - "docs/**" - ".changeset/**" pull_request: + # Mirrors the push filter. Docs-only PRs run the dedicated Docs Check + # workflow (which also validates links against renamed/deleted targets); + # changeset-only PRs need no validation at all. + paths-ignore: + - "**.md" + - "docs/**" + - ".changeset/**" workflow_dispatch: concurrency: @@ -189,9 +196,6 @@ jobs: - name: Check agent skills lock run: pnpm run skills:check - - name: Check docs links - run: pnpm run docs:check - - name: Run typecheck run: pnpm run typecheck diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d1fd71822..4791828f3 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -33,12 +33,13 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 outputs: - deploy: ${{ steps.tip.outputs.current }} + deploy: ${{ steps.tip.outputs.current == 'true' && steps.relevance.outputs.relevant == 'true' }} steps: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 (Node 24) with: ref: ${{ github.event.workflow_run.head_sha || github.sha }} + fetch-depth: 2 - name: Verify the CI-validated commit is still the main tip id: tip @@ -53,6 +54,37 @@ jobs: echo "::notice::Skipping deploy — main advanced past the CI-validated commit ($SHA → $TIP). The queued run for the new tip deploys instead." fi + # Relevance gate: skip the full deploy train when the merged diff touches + # no runtime-affecting paths (docs/tests/workflows/changesets only). + # Merge commits (PR merges) diff exactly the PR's files; single-commit + # direct pushes too. Any uncertainty errs toward deploying, and manual + # dispatches always deploy. + - name: Check the diff for runtime-relevant paths + id: relevance + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "relevant=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + if ! git rev-parse HEAD^ >/dev/null 2>&1; then + echo "::notice::No parent commit — assuming runtime-relevant" + echo "relevant=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + FILES="$(git diff --name-only HEAD^ HEAD)" + if [ -z "$FILES" ]; then + echo "::notice::Empty first-parent diff (merge-tangle history) — assuming runtime-relevant" + echo "relevant=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + if echo "$FILES" | grep -qE '^Dockerfile$|^railway\.toml$|^scripts/|^host/|^api/|^ui/|^plugins/|^packages/|^bos\.app\.ts$|^bos\.dev\.ts$|^bos\.config\.json$|^biome\.json$|^\.env\.test$|^(package\.json|pnpm-lock\.yaml|pnpm-workspace\.yaml|tsconfig\.json|tsconfig\.base\.json)$|^\.github/workflows/(ci|deploy|release)\.yml$|^\.github/templates/'; then + echo "relevant=true" >> "$GITHUB_OUTPUT" + else + echo "relevant=false" >> "$GITHUB_OUTPUT" + echo "::notice::Skipping deploy — the merged diff touches no runtime-relevant paths:" + while IFS= read -r f; do echo " $f"; done <<< "$FILES" + fi + deploy: name: Deploy Production needs: [gate] diff --git a/.github/workflows/docs-check.yml b/.github/workflows/docs-check.yml new file mode 100644 index 000000000..581eaebe3 --- /dev/null +++ b/.github/workflows/docs-check.yml @@ -0,0 +1,43 @@ +name: Docs Check + +# The docs link check lives here — outside CI's lint-and-typecheck — so CI can +# path-ignore markdown entirely. The script is zero-dependency (node: builtins +# plus git check-ignore), so the whole workflow is checkout + node, no install. +# +# Runs on every PR (not just docs paths): broken references are also caused by +# renaming/deleting a file the markdown points at — docs-surface paths alone +# miss those. +on: + pull_request: + push: + branches: [main] + # Post-merge runs duplicate the PR check for code merges; docs paths still + # get validated on direct md pushes. + paths: + - "**.md" + - "docs/**" + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + docs-link-check: + name: Docs link check + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout repository + uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 (Node 24) + + - name: Setup Node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: "24" + + - name: Check docs links + run: node scripts/docs-link-check.mjs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2d1e81c5f..d83f7a021 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,12 @@ on: push: branches: - main + # Docs-only merges cannot change changesets — skip the changesets pass. + # Deliberately NOT ignoring .changeset/** — that is the input this + # workflow consumes. + paths-ignore: + - "**.md" + - "docs/**" workflow_dispatch: concurrency: ${{ github.workflow }}-${{ github.ref }} From 4917b2dd0458257a6af89a47f37686d25d0f57f1 Mon Sep 17 00:00:00 2001 From: Elliot Braem Date: Fri, 9 Oct 2026 16:32:30 -0500 Subject: [PATCH 2/3] ci: fix npm OIDC trusted publishing in release workflow --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d83f7a021..117c0d48b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,12 +36,13 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + # No registry-url: it injects an empty `_authToken` into the job's + # .npmrc, which blocks npm's OIDC trusted-publishing exchange + # (publish fails with ENEEDAUTH instead of using the OIDC token). - name: Setup Node uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: "24" - npm-version: "11" - registry-url: "https://registry.npmjs.org" - name: Install dependencies run: pnpm install --frozen-lockfile --ignore-scripts @@ -60,4 +61,3 @@ jobs: createGithubReleases: false env: GITHUB_TOKEN: ${{ github.token }} - NODE_AUTH_TOKEN: "" From 6676390cf1f265644fd770df3e3c87390c8fc87d Mon Sep 17 00:00:00 2001 From: Elliot Braem Date: Fri, 9 Oct 2026 16:33:26 -0500 Subject: [PATCH 3/3] chore: purge bos.config.json from CI gates and scaffolded docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bos.app.ts is the canonical authored config form — the scaffold generates it (convertChildConfigToAppForm runs in both init branches, and init.levels.test asserts no bos.config.json remains), so: - drop the dead ^bos\.config\.json$ term from the deploy relevance gate and all four ci.yml detect-changes buckets (bos.app.ts was already covered in each) - sweep the scaffolded README/AGENTS/skill templates that still told child users the config lives in bos.config.json Legacy JSON configs in existing child repos still load (sync handles both forms); test fixtures intentionally keep exercising that path. --- .changeset/scaffold-docs-bos-app-ts.md | 5 +++++ .github/workflows/ci.yml | 8 ++++---- .github/workflows/deploy.yml | 2 +- packages/everything-dev/src/cli/init.ts | 18 +++++++++--------- 4 files changed, 19 insertions(+), 14 deletions(-) create mode 100644 .changeset/scaffold-docs-bos-app-ts.md diff --git a/.changeset/scaffold-docs-bos-app-ts.md b/.changeset/scaffold-docs-bos-app-ts.md new file mode 100644 index 000000000..32eb8bf0e --- /dev/null +++ b/.changeset/scaffold-docs-bos-app-ts.md @@ -0,0 +1,5 @@ +--- +"everything-dev": patch +--- + +Scaffolded docs reference `bos.app.ts`, not `bos.config.json`. The init-scaffolded README/AGENTS/skill templates told users the runtime configuration lives in a root `bos.config.json` — but the scaffold generates (and tests assert) the authored `bos.app.ts` descriptor. Cosmetic doc sweep; legacy JSON configs in existing child repos still load. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0aff253a..4a590854b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -97,7 +97,7 @@ jobs: else echo "workflows=false" >> "$GITHUB_OUTPUT" fi - if echo "$DIFF" | grep -qE '^Dockerfile$|^scripts/|^host/|^api/|^ui/|^plugins/|^packages/|^tests/|^bos\.config\.json$|^\.env\.test$|^(package\.json|pnpm-lock\.yaml|pnpm-workspace\.yaml|tsconfig\.json|tsconfig\.base\.json|bos\.app\.ts)$|^\.github/workflows/ci\.yml$'; then + if echo "$DIFF" | grep -qE '^Dockerfile$|^scripts/|^host/|^api/|^ui/|^plugins/|^packages/|^tests/|^\.env\.test$|^(package\.json|pnpm-lock\.yaml|pnpm-workspace\.yaml|tsconfig\.json|tsconfig\.base\.json|bos\.app\.ts)$|^\.github/workflows/ci\.yml$'; then echo "image-smoke=true" >> "$GITHUB_OUTPUT" else echo "image-smoke=false" >> "$GITHUB_OUTPUT" @@ -112,12 +112,12 @@ jobs: else echo "every-plugin=false" >> "$GITHUB_OUTPUT" fi - if echo "$DIFF" | grep -qE '^host/|^ui/|^packages/|^bos\.config\.json$|^\.env\.test$|^(package\.json|pnpm-lock\.yaml|pnpm-workspace\.yaml|tsconfig\.json|tsconfig\.base\.json|bos\.app\.ts|bos\.dev\.ts)$|^\.github/workflows/ci\.yml$'; then + if echo "$DIFF" | grep -qE '^host/|^ui/|^packages/|^\.env\.test$|^(package\.json|pnpm-lock\.yaml|pnpm-workspace\.yaml|tsconfig\.json|tsconfig\.base\.json|bos\.app\.ts|bos\.dev\.ts)$|^\.github/workflows/ci\.yml$'; then echo "host=true" >> "$GITHUB_OUTPUT" else echo "host=false" >> "$GITHUB_OUTPUT" fi - if echo "$DIFF" | grep -qE '^tests/regression/(http|lib)/|^host/|^api/|^plugins/|^packages/|^scripts/|^Dockerfile$|^bos\.config\.json$|^\.env\.test$|^(package.json|pnpm-lock.yaml|pnpm-workspace.yaml|tsconfig.json|tsconfig.base.json|bos.app.ts|bos.dev.ts)$|^\.github/workflows/ci\.yml$'; then + if echo "$DIFF" | grep -qE '^tests/regression/(http|lib)/|^host/|^api/|^plugins/|^packages/|^scripts/|^Dockerfile$|^\.env\.test$|^(package.json|pnpm-lock.yaml|pnpm-workspace.yaml|tsconfig.json|tsconfig.base.json|bos.app.ts|bos.dev.ts)$|^\.github/workflows/ci\.yml$'; then echo "regression-http=true" >> "$GITHUB_OUTPUT" else echo "regression-http=false" >> "$GITHUB_OUTPUT" @@ -127,7 +127,7 @@ jobs: else echo "regression-framework=false" >> "$GITHUB_OUTPUT" fi - if echo "$DIFF" | grep -qE '^tests/regression/(browser|lib)/|^host/|^ui/|^api/|^plugins/|^packages/|^scripts/|^Dockerfile$|^bos\.config\.json$|^\.env\.test$|^(package.json|pnpm-lock.yaml|pnpm-workspace.yaml|tsconfig.json|tsconfig.base.json|bos.app.ts|bos.dev.ts)$|^\.github/workflows/ci\.yml$'; then + if echo "$DIFF" | grep -qE '^tests/regression/(browser|lib)/|^host/|^ui/|^api/|^plugins/|^packages/|^scripts/|^Dockerfile$|^\.env\.test$|^(package.json|pnpm-lock.yaml|pnpm-workspace.yaml|tsconfig.json|tsconfig.base.json|bos.app.ts|bos.dev.ts)$|^\.github/workflows/ci\.yml$'; then echo "regression-browser=true" >> "$GITHUB_OUTPUT" else echo "regression-browser=false" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4791828f3..91e86f01c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -77,7 +77,7 @@ jobs: echo "relevant=true" >> "$GITHUB_OUTPUT" exit 0 fi - if echo "$FILES" | grep -qE '^Dockerfile$|^railway\.toml$|^scripts/|^host/|^api/|^ui/|^plugins/|^packages/|^bos\.app\.ts$|^bos\.dev\.ts$|^bos\.config\.json$|^biome\.json$|^\.env\.test$|^(package\.json|pnpm-lock\.yaml|pnpm-workspace\.yaml|tsconfig\.json|tsconfig\.base\.json)$|^\.github/workflows/(ci|deploy|release)\.yml$|^\.github/templates/'; then + if echo "$FILES" | grep -qE '^Dockerfile$|^railway\.toml$|^scripts/|^host/|^api/|^ui/|^plugins/|^packages/|^bos\.app\.ts$|^bos\.dev\.ts$|^biome\.json$|^\.env\.test$|^(package\.json|pnpm-lock\.yaml|pnpm-workspace\.yaml|tsconfig\.json|tsconfig\.base\.json)$|^\.github/workflows/(ci|deploy|release)\.yml$|^\.github/templates/'; then echo "relevant=true" >> "$GITHUB_OUTPUT" else echo "relevant=false" >> "$GITHUB_OUTPUT" diff --git a/packages/everything-dev/src/cli/init.ts b/packages/everything-dev/src/cli/init.ts index c2e436d2e..e731666de 100644 --- a/packages/everything-dev/src/cli/init.ts +++ b/packages/everything-dev/src/cli/init.ts @@ -1697,8 +1697,8 @@ You don't need to wait for a PR to merge and run through CI/CD. Publish your own # Output: NEAR_PRIVATE_KEY=ed25519:... \`\`\` Add the key to your account via near-cli-rs, then set \`NEAR_PRIVATE_KEY\` in \`.env\` or CI secrets. -4. **Update \`bos.config.json\`** — set \`account\` to your NEAR account, add \`extends\` to inherit the base platform, keep \`domain\` as the gateway: - \`\`\`json +4. **Update \`bos.app.ts\`** — set \`account\` to your NEAR account, add \`extends\` to inherit the base platform, keep \`domain\` as the gateway: + \`\`\`ts { "extends": "bos:///", "account": ".near", "domain": "" } \`\`\` 5. **Publish and deploy:** @@ -1795,9 +1795,9 @@ The API plugin receives typed client factories for all other plugins via \`creat ### Generated Types -\`api/src/lib/plugins-types.gen.ts\`, \`api/src/lib/auth-types.gen.ts\`, \`ui/src/lib/api-types.gen.ts\`, and \`ui/src/lib/auth-types.gen.ts\` are generated by \`bos types gen\` from \`bos.config.json\`. These files are gitignored and auto-regenerated on \`pnpm install\`, \`typecheck\`, \`bos dev\`, \`bos build\`, and bos plugin management commands. +\`api/src/lib/plugins-types.gen.ts\`, \`api/src/lib/auth-types.gen.ts\`, \`ui/src/lib/api-types.gen.ts\`, and \`ui/src/lib/auth-types.gen.ts\` are generated by \`bos types gen\` from \`bos.app.ts\`. These files are gitignored and auto-regenerated on \`pnpm install\`, \`typecheck\`, \`bos dev\`, \`bos build\`, and bos plugin management commands. -If you hand-edit \`bos.config.json\`, run \`bos types gen\` or restart \`bos dev\` to regenerate.`); +If you hand-edit \`bos.app.ts\`, run \`bos types gen\` or restart \`bos dev\` to regenerate.`); } const testCommands: string[] = []; @@ -1876,7 +1876,7 @@ The host exposes several surfaces for programmatic agent access: ### Architecture note: remotes are code bundles -Remotes in \`bos.config.json\` are **not hosted APIs** — they are code bundles loaded via Module Federation at runtime. The UI, API, auth, and plugins all run in the same host process. There is no remote server to call; everything is loaded in-process through Module Federation and \`every-plugin\`.`); +Remotes in \`bos.app.ts\` are **not hosted APIs** — they are code bundles loaded via Module Federation at runtime. The UI, API, auth, and plugins all run in the same host process. There is no remote server to call; everything is loaded in-process through Module Federation and \`every-plugin\`.`); parts.push(`## Troubleshooting @@ -1888,7 +1888,7 @@ pnpm run dev # Restart \`\`\` **Module Federation errors:** -- Check \`bos.config.json\` URLs are accessible +- Check \`bos.app.ts\` URLs are accessible - Verify shared dependency versions match in package.json - Clear browser cache @@ -1902,7 +1902,7 @@ pnpm run db:studio # Open Drizzle Studio **Required files:** - \`.env\` — Secrets (see \`.env.example\`) -- \`bos.config.json\` — Runtime configuration (committed)`); +- \`bos.app.ts\` — Runtime configuration (committed)`); return `${parts.join("\n\n")}\n`; } @@ -2000,7 +2000,7 @@ export function buildChildSkillMd(title: string, repository: string): string { return `# ${title} skill -Use this when you want an agent to run, edit, and publish **${title}** — an everything.dev app composed at runtime from \`bos.config.json\`. +Use this when you want an agent to run, edit, and publish **${title}** — an everything.dev app composed at runtime from \`bos.app.ts\`. There are two ways to work with this app: @@ -2050,7 +2050,7 @@ The repo ships agent workflow skills in \`.agents/skills/\` — start with \`eve ### Architecture note -Remotes in \`bos.config.json\` are **not hosted APIs** — they are code bundles loaded via Module Federation at runtime. Everything runs in the same host process. +Remotes in \`bos.app.ts\` are **not hosted APIs** — they are code bundles loaded via Module Federation at runtime. Everything runs in the same host process. ### Run locally