Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
159 commits
Select commit Hold shift + click to select a range
89bc69c
feat: add a Support CodeNomad page and entry points for sponsorship (…
pascalandr Oct 29, 2026
8eef8cd
feat(projects): suggest the clone folder from the repository name and…
pascalandr Oct 29, 2026
7c15f2b
fix(transcript): show local-file Markdown images from the transcript …
pascalandr Oct 29, 2026
1f5e04e
fix(missions): keep the depth editor still while it reads (#1183)
pascalandr Oct 30, 2026
d1adbaa
docs: add a code of conduct, governance and security policy (#1197)
pascalandr Oct 30, 2026
396e584
fix(settings): drop the inaccurate permission promise from the Images…
pascalandr Oct 30, 2026
debe10d
fix(settings): drop the inaccurate paid-request sentence from the Ima…
pascalandr Oct 30, 2026
f3246e2
fix(settings): keep Store images once working in projects with their …
pascalandr Oct 30, 2026
08705aa
feat(design): add the Design agent and skills as native OpenCode cont…
pascalandr Oct 30, 2026
7d1b29b
feat(design): register the Design agent and skills at plugin load, dr…
pascalandr Oct 30, 2026
b652996
feat(design): add the Images gallery and conversation RPCs to the ses…
pascalandr Oct 30, 2026
9c1babe
feat(design): open the Images conversation with the CodeNomad model a…
pascalandr Oct 30, 2026
a3651c7
feat(extensions): add panel extension API 4 for the Design image comp…
pascalandr Oct 30, 2026
4124b60
docs(help): add the Design extension help in ten languages (#1206)
pascalandr Oct 30, 2026
f39642b
test(design): qualify the Design companion end to end and the panel s…
pascalandr Oct 30, 2026
77c2460
fix(opencode): require OpenCode 2.2.3 and adopt its image tool contra…
pascalandr Oct 30, 2026
6a280aa
fix(design): keep a retried image provable after a failed attempt und…
pascalandr Oct 30, 2026
c40378d
fix(help): keep whole words in table columns (#1212)
pascalandr Oct 30, 2026
58616fb
fix(usage): show the Claude Agent provider's quota from Claude Code's…
pascalandr Oct 30, 2026
3ff4ddd
feat(missions): describe the selected playbook under its selector (#1…
pascalandr Oct 30, 2026
56823af
docs(help): explain how context flows through a mission (#1215)
pascalandr Oct 30, 2026
6058206
feat(missions): let Pause finish the steps in progress, with Interrup…
pascalandr Oct 30, 2026
33e97f4
fix(missions): list mission models like the conversation selector, wi…
pascalandr Oct 30, 2026
5bc483d
feat(ui): toggle several rows at once by dragging across toggles (#1221)
pascalandr Oct 30, 2026
0081442
fix(missions): scroll the creation body, keep Create outside it (#1213)
pascalandr Oct 31, 2026
14115f8
test(browser): exempt notification-routing from daemon borrowing (#1225)
pascalandr Oct 31, 2026
a2274d6
chore(opencode): recommend OpenCode 2.2.5 (#1222)
pascalandr Oct 31, 2026
b6ebcb1
fix(missions): finish a creation submitted before the panel's first l…
pascalandr Oct 31, 2026
c17752e
feat(missions): route human questions through the coordinator and rec…
pascalandr Oct 31, 2026
b1e1618
feat(missions): explain every playbook in help and a See details read…
pascalandr Oct 31, 2026
acadef7
test(missions): qualify presence loss through the RPC it actually con…
pascalandr Oct 31, 2026
2e585a7
feat(missions): lift the per-mission actor and task caps (#1220)
pascalandr Oct 31, 2026
5bce230
feat(missions): read mission bodies exactly and bound tool results (#…
pascalandr Oct 31, 2026
248a4b3
feat(status): show the session's /goal on one line above Tokens (#1228)
pascalandr Oct 31, 2026
db1bb2c
fix(test): keep the fixture daemon's console programs from opening wi…
pascalandr Oct 31, 2026
edeafe5
feat(missions): read one-time mission bodies through the content rout…
pascalandr Oct 31, 2026
66a85af
feat(plugins): add, remove and show the version of any OpenCode plugi…
pascalandr Oct 31, 2026
da8607d
feat(missions): send compact mission maps to the panel (#1237)
pascalandr Oct 31, 2026
091361d
fix(ui): capture pointer while resizing side panels (#1233)
Ledracarnos Oct 31, 2026
54a93e7
fix(plugins): repair damaged plugin payloads and show recovery notice…
pascalandr Oct 31, 2026
5313fbf
feat(integrations): add the OpenCode integration contract and its tes…
pascalandr Oct 31, 2026
87e4ac7
test(interruptions): follow the single recovery notice introduced by …
pascalandr Oct 31, 2026
794ac20
feat(integrations): recognise OpenCode integrations from the reviewed…
pascalandr Nov 1, 2026
f90cbbc
feat(desktop): connect directly to remote servers from launch argumen…
pascalandr Nov 1, 2026
abb14f8
feat(missions): page a mission's tasks in the panel (#1241)
pascalandr Nov 1, 2026
2b57a67
feat(missions): add byte-bounded panel page reads (#1246)
pascalandr Nov 1, 2026
4e4090b
test(missions): identify expected catalog refresh cancellations (#1247)
pascalandr Nov 1, 2026
fe1506a
feat(missions): add bounded panel activity and attention reads (#1249)
pascalandr Nov 1, 2026
186d8fc
test(git): wait for computed diff geometry before native selection (#…
pascalandr Nov 1, 2026
fb44655
fix(missions): enable General delegation with project depth overrides…
pascalandr Nov 1, 2026
2c7ff9a
fix(electron): bound Linux process capture without per-PID helpers (#…
pascalandr Nov 1, 2026
72942a9
test(history): verify bounded decode and cooperative yields determini…
pascalandr Nov 1, 2026
dc71b88
feat(missions): consume byte-bounded task pages in the panel (#1250)
pascalandr Nov 1, 2026
a9bf2dd
docs: record both maintainers as repository administrators with equal…
pascalandr Nov 1, 2026
5509bf6
docs: name the CodeNomad contributors in the license's copyright noti…
pascalandr Nov 1, 2026
c250db6
test(missions): use owned SSE streams and exact cancellation evidence…
pascalandr Nov 1, 2026
a5efdef
feat(missions): resume recurring Missions after OpenCode restarts wit…
pascalandr Nov 1, 2026
70c0c96
fix(desktop): recover local windows after the last local view closes …
pascalandr Nov 1, 2026
5bb2dec
fix(transcript): keep pasted text inside one code-fence display (#1260)
pascalandr Nov 1, 2026
926e2c7
ci(release): sign Windows artifacts with SignPath (#1257)
pascalandr Nov 1, 2026
6e82569
fix(plugins): resolve missing Windows pathname device identity safely…
pascalandr Nov 1, 2026
7349d73
fix(server): resolve compiled modules before same-named native helper…
pascalandr Nov 1, 2026
8423db5
feat(missions): adopt bounded panel inventory and selected headers (#…
pascalandr Nov 2, 2026
903b286
feat(sessions): control recursive activity expansion from General set…
pascalandr Nov 2, 2026
f9a97d8
feat(integrations): attach native applications and manage plugin sources
pascalandr Nov 2, 2026
fb4e244
fix(missions): stabilize refresh and simplify task continuation (#1271)
pascalandr Nov 2, 2026
6b8baf2
feat(missions): bound current recurring passage display reads (#1269)
pascalandr Nov 2, 2026
a579403
fix(missions): keep Scheduling stable while it revalidates (#1272)
pascalandr Nov 2, 2026
404f160
feat(usage): estimate OpenCode Zen free quota in Status (#1276)
pascalandr Nov 3, 2026
cc5961a
feat(usage): simplify free-model estimate caveat (#1277)
pascalandr Nov 3, 2026
8271801
fix(supervision): run Windows Main and Recovery without consoles (#1278)
pascalandr Nov 3, 2026
f929bc5
fix(transcript): keep settled tool images from reverting to running (…
heunghingwan Nov 3, 2026
252e07b
fix(files): keep the central file reader open when the right panel hi…
pascalandr Nov 3, 2026
493ebe4
test(missions): route panel reads apart from mutations in browser fix…
pascalandr Nov 3, 2026
31521cc
test(ui): prove real blur and measured offscreen rows in browser fixt…
pascalandr Nov 3, 2026
3f72ba4
fix(sessions): clear acknowledged idle and interrupted badges (#1286)
pascalandr Nov 3, 2026
8195da0
fix(missions): disconnect Missions from service supervision so Play a…
pascalandr Nov 3, 2026
805945b
perf(transcript): keep settled action buttons mounted during streamin…
heunghingwan Nov 3, 2026
5092ef1
fix(sessions): clear Interrupted badges only on explicit gesture (#1287)
pascalandr Nov 3, 2026
e4b40d8
feat(integrations): ship native Status sections and catalogue install…
pascalandr Nov 4, 2026
c8fb4f1
feat(opencode): replace service supervision with Start OpenCode at lo…
pascalandr Nov 4, 2026
25b8553
fix(settings): label login start as Start OpenCode at startup (#1290)
pascalandr Nov 4, 2026
990f6ce
feat(missions): join launch criteria with or/if/else connectors and f…
pascalandr Nov 4, 2026
4bac373
fix(sessions): restore the collapsed parent chevron (#1292)
pascalandr Nov 4, 2026
d832728
perf(transcript): coalesce streaming projections to one pass per fram…
heunghingwan Nov 5, 2026
0240f39
fix(transcript): preserve message order when HTTP is ahead of live ev…
pascalandr Nov 5, 2026
fc1b9e7
feat(transcript): optional first/latest controls in the timeline corn…
pascalandr Nov 5, 2026
30527e5
fix(transcript): hold the top after an immediate jump to the first me…
pascalandr Nov 5, 2026
dccb57e
fix(worktrees): keep checkouts lacking the opened nested folder out o…
pascalandr Nov 6, 2026
6aea911
fix(electron): confirm Windows backend shutdown with native process i…
pascalandr Nov 6, 2026
c73f502
fix(server): complete graceful desktop shutdown promptly (#1310)
pascalandr Nov 6, 2026
f3bf6c4
feat(plugins): protect CodeNomad-managed system plugins (#1308)
pascalandr Nov 6, 2026
c29b885
feat(settings): keep the computer awake while CodeNomad is running (#…
pascalandr Nov 6, 2026
78a734c
perf(electron): run the backend with Electron's Node (#1304)
pascalandr Nov 6, 2026
5939d4c
feat(usage): upgrade provider usage readers (#1309)
pascalandr Nov 6, 2026
cb2dcb7
chore(runtime): update Node LTS to 24.22.2 (#1314)
pascalandr Nov 6, 2026
d71c812
feat(usage): show Gemini CLI and Antigravity quotas with an in-memory…
pascalandr Nov 6, 2026
0829c7c
feat(memory): add opt-in persistent agent memory (#1313)
pascalandr Nov 6, 2026
c46894e
chore(pruning): remove the unused TUI companion plugin (#1316)
pascalandr Nov 6, 2026
547398c
build(tauri): update to Tauri 2.12 and keep its crates current (#1320)
pascalandr Nov 6, 2026
0115b46
feat(usage): read OpenCode Console usage and mark free quota exhausti…
pascalandr Nov 6, 2026
fdc5e3c
feat(sessions): keep the chevron's activity when expanded and mark a …
pascalandr Nov 6, 2026
b48dd6f
build(electron): update to Electron 45 and keep the desktop toolchain…
pascalandr Nov 7, 2026
683b847
build(deps-dev): patch PostCSS and its workspace lockfile (#1322)
dependabot[bot] Nov 7, 2026
244892e
ci(deps): separate security fixes from routine dependency maintenance…
pascalandr Nov 7, 2026
71c6bb6
fix(server): bound malformed arrays before Zod validation (#1329)
pascalandr Nov 7, 2026
02c1765
fix(server): patch Undici remote-response denial of service (#1328)
pascalandr Nov 7, 2026
b6bea60
build(runtime): separate bundled npm pinning from Node maintenance (#…
pascalandr Nov 7, 2026
9f8fbe5
test(missions): use one row snapshot in the task-pages scroll loop (#…
pascalandr Nov 7, 2026
db97d19
test(missions): make deadline-admission checks deterministic (#1344)
pascalandr Nov 7, 2026
3614ef0
build(deps-dev): update get-tsconfig to 4.14.3 (#1341)
dependabot[bot] Nov 7, 2026
d3bfc94
build(deps): refresh compatible Cargo dependencies and security fixes…
dependabot[bot] Nov 7, 2026
17929a1
build(deps): update internationalized date to 3.12.4 (#1339)
dependabot[bot] Nov 7, 2026
ffdb4a7
fix(storage): support native split assistant parts across history and…
pascalandr Nov 7, 2026
3d35299
ci(deps): keep unrelated maintenance proposals separate (#1353)
pascalandr Nov 7, 2026
7fdf23f
fix(deps): patch the Solid serialization dependency family (#1354)
pascalandr Nov 7, 2026
5842b9a
fix(deps): patch node-forge certificate handling (#1355)
pascalandr Nov 7, 2026
dc9318a
fix(deps): patch Fastify proxy address handling (#1356)
pascalandr Nov 7, 2026
9a3784d
fix(deps): patch the maintained xmldom packaging dependency (#1357)
pascalandr Nov 7, 2026
b21d360
build(deps): update plist and its compatible parser dependencies (#1366)
dependabot[bot] Nov 7, 2026
af3b1ec
test(opencode): retain isolated runtime reload failure evidence (#1360)
pascalandr Nov 7, 2026
4d6a517
fix(missions): resolve scheduled wakes from native message records (O…
pascalandr Nov 7, 2026
7292e4e
chore(opencode): recommend qualified runtime 2.2.6 (#1379)
pascalandr Nov 7, 2026
1537a18
test(missions): await owned service readiness before fixture connecti…
pascalandr Nov 7, 2026
3aab4e9
ci(deps): limit ordinary Dependabot intake to one PR per job (#1378)
pascalandr Nov 7, 2026
81d1ccc
build(deps-dev): update Babel dotall-regex transform and its regex he…
dependabot[bot] Nov 7, 2026
9684a4a
build(deps-dev): update semver in the Cloudflare worker tooling (#1372)
dependabot[bot] Nov 7, 2026
8551c78
build(deps-dev): update tslib in the Cloudflare worker tooling (#1373)
dependabot[bot] Nov 7, 2026
b12563d
build(deps-dev): update @emnapi/runtime in the Cloudflare worker tool…
dependabot[bot] Nov 7, 2026
dda9e75
fix(runtime): run bundled npm and npx commands with a scoped, general…
pascalandr Nov 7, 2026
c0c336a
chore(runtime): update bundled npm to 12.2.2 (major 11.x → 12.x: main…
github-actions[bot] Nov 7, 2026
494f1b3
feat(missions): show the Missions panel as a tree under a two-line he…
pascalandr Nov 7, 2026
18c3c35
fix(tauri): attach pasted images and files on Linux (#1383)
pascalandr Nov 8, 2026
2bc918a
ci(deps): turn off routine Dependabot version updates (#1382)
pascalandr Nov 8, 2026
5871175
feat(missions): show tasks as child rows and let a task take the head…
pascalandr Nov 8, 2026
175b182
fix(server): move to Fastify 5 and its current plugins (#1386)
pascalandr Nov 8, 2026
47259a1
feat(missions): open questions from red mission and task rows (#1385)
pascalandr Nov 8, 2026
674d729
fix(previews): keep preview targets on the preview origin (#1387)
pascalandr Nov 8, 2026
263eb6c
chore(missions): bordered panel commands and remove dead tree leftove…
pascalandr Nov 8, 2026
0a23cf1
test(missions): attribute the covered-page cancellation to the owned …
pascalandr Nov 8, 2026
c51d55e
fix(missions): keep the panel inside the window and move Save as brie…
pascalandr Nov 8, 2026
ba29cde
build(deps-dev): update electron-vite to 6 with its Vite 6 contract (…
dependabot[bot] Nov 8, 2026
8656d1f
fix(missions): make the subagent depth one menu row and Save as brief…
pascalandr Nov 8, 2026
4d72ad5
test(missions): wait for the inventory rows before measuring panel ge…
pascalandr Nov 8, 2026
1f91a64
ci: serve release downloads from verified dev-seeded caches (#1390)
pascalandr Nov 8, 2026
689f73b
build(deps): update tauri-winres to 0.3.7 (#1333)
dependabot[bot] Nov 8, 2026
414e2d2
chore(release): prepare CodeNomad 1.1.0 (#1394)
pascalandr Nov 8, 2026
638082b
build(cloudflare): update Wrangler to 4.158 and keep deploys without …
pascalandr Nov 8, 2026
55e4972
test(missions): record failure evidence for two intermittent panel te…
pascalandr Nov 8, 2026
6183405
test(browser): isolate stream delta projection outline requests (#1363)
pascalandr Nov 8, 2026
7a849a9
build(deps): refresh vulnerable glob, minimatch, brace-expansion, jws…
pascalandr Nov 8, 2026
296af86
feat(tauri): embed native WebKit previews on macOS and Linux (#1301)
pascalandr Nov 8, 2026
1c99934
fix(client-state): let one slow identity helper use the whole lookup …
pascalandr Nov 8, 2026
339ecf9
feat(help): open localized guide sections from contextual info icons …
pascalandr Nov 8, 2026
d999843
feat(sessions): archive and restore native conversations (#1406)
pascalandr Nov 9, 2026
32f2961
fix(missions): make the tree behave like the session list (#1402)
pascalandr Nov 9, 2026
50f2ef7
feat(usage): enable Console calibration for free estimates (#1411)
pascalandr Nov 9, 2026
b06f3a5
fix(missions): compact configuration and compose once-now launches (#…
pascalandr Nov 9, 2026
16104c2
fix(missions): apply Missions profile preferences to agent preparations
pascalandr Nov 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Vendored-crate patches apply to the published LF crates; keep them byte-exact on every checkout.
packages/tauri-app/vendor/patches/*.patch -text
packages/tauri-app/tests/webkit-capabilities/*.sh text eol=lf
2 changes: 2 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github: [pascalandr]
custom: ["https://neuralnomadsai.github.io/CodeNomad/en/sponsor.html"]
54 changes: 54 additions & 0 deletions .github/actions/download-cache/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Restore download cache
description: >-
Restore one download cache written only by trusted dev runs, then check the
files that Cargo and the Tauri bundler would use unverified. Never saves; a
miss leaves every download to its tool.

inputs:
kind:
description: electron, cargo, tauri or appimage-runtime
required: true
exact:
description: Restore only the exact key (seeding), without an older Cargo cache
required: false
default: "false"

outputs:
key:
description: Cache key; empty when this kind has no cache on this runner
value: ${{ steps.spec.outputs.key }}
path:
description: Cached paths, one per line
value: ${{ steps.spec.outputs.path }}
cache-hit:
description: Whether the exact key was restored
value: ${{ steps.restore.outputs.cache-hit }}

runs:
using: composite
steps:
# Without a key the cache is skipped and every tool downloads as usual.
- name: Resolve ${{ inputs.kind }} cache
id: spec
continue-on-error: true
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: node "$GITHUB_ACTION_PATH/../../scripts/download-cache.cjs" spec "${{ inputs.kind }}" >> "$GITHUB_OUTPUT"

- name: Restore ${{ inputs.kind }} cache
id: restore
if: ${{ steps.spec.outputs.key != '' }}
uses: actions/cache/restore@v6.1.0
with:
key: ${{ steps.spec.outputs.key }}
path: ${{ steps.spec.outputs.path }}
restore-keys: ${{ inputs.exact != 'true' && steps.spec.outputs.restore-keys || '' }}

- name: Check restored ${{ inputs.kind }} files
if: ${{ steps.restore.outputs.cache-matched-key != '' && inputs.kind != 'electron' }}
shell: bash
env:
CACHE_KEY: ${{ steps.spec.outputs.key }}
CACHE_PATH: ${{ steps.spec.outputs.path }}
run: node "$GITHUB_ACTION_PATH/../../scripts/download-cache.cjs" verify "${{ inputs.kind }}"
63 changes: 63 additions & 0 deletions .github/actions/signpath-sign/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: SignPath sign
description: >-
Upload one build output as a GitHub Actions artifact, submit it to the
CodeNomad SignPath project and download the signed files. SignPath verifies
that the artifact was produced by this repository's GitHub-hosted workflow.

inputs:
api-token:
description: SignPath API token of the CI submitter (repository secret SIGNPATH_API_TOKEN)
required: true
signing-policy:
description: SignPath signing policy slug (test-signing or release-signing)
required: true
artifact-configuration:
description: SignPath artifact configuration slug (sources in .signpath/artifact-configurations)
required: true
artifact-name:
description: Name of the unsigned GitHub Actions artifact
required: true
path:
description: File or glob to submit; it becomes the root of the artifact ZIP
required: true
parameters:
description: 'SignPath user-defined parameters, one `name: "json string"` per line'
required: true
output-directory:
description: Directory that receives the signed files
required: true

outputs:
signing-request-url:
description: SignPath signing request page
value: ${{ steps.sign.outputs.signing-request-web-url }}

runs:
using: composite
steps:
- name: Upload unsigned artifact for SignPath
id: upload
uses: actions/upload-artifact@v7.0.2
with:
name: ${{ inputs.artifact-name }}
path: ${{ inputs.path }}
overwrite: true
if-no-files-found: error
retention-days: 1

# release-signing waits for a manual approval in SignPath; test-signing
# completes without one.
- name: Submit SignPath signing request
id: sign
uses: signpath/github-action-submit-signing-request@v3.0
with:
api-token: ${{ inputs.api-token }}
organization-id: 586d8e7e-ab56-407a-9c5b-832a84bb0670
project-slug: CodeNomad
signing-policy-slug: ${{ inputs.signing-policy }}
artifact-configuration-slug: ${{ inputs.artifact-configuration }}
github-artifact-id: ${{ steps.upload.outputs.artifact-id }}
wait-for-completion: true
wait-for-completion-timeout-in-seconds: ${{ inputs.signing-policy == 'release-signing' && '7200' || '900' }}
output-artifact-directory: ${{ inputs.output-directory }}
parameters: ${{ inputs.parameters }}
108 changes: 107 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,114 @@
version: 2
# Routine version updates are off: every job sets open-pull-requests-limit 0.
# Dependabot opens security updates only (GitHub's separate fixed limit). The
# allow rules also filter security updates, so they stay broad; the
# version-update groups apply again if a limit is raised.
updates:
# All Cargo dependencies, including transitives, from the workspace lockfile.
# allow.update-types limits routine updates only; security fixes may cross majors.
# Group related Tauri security fixes; unmatched dependencies stay individual.
# Explicit security rules override repository/organization grouping here.
# Re-qualify local path patches before merging
# (packages/tauri-app/vendor/README.md; dev-docs/RUNTIME_MAINTENANCE.md).
# Use the default branch (dev) so security updates share this configuration.
- package-ecosystem: cargo
directory: /packages/tauri-app
schedule:
interval: weekly
open-pull-requests-limit: 0
allow:
- dependency-type: all
update-types:
- version-update:semver-minor
- version-update:semver-patch
groups:
tauri-security:
applies-to: security-updates
patterns:
- tauri
- tauri-*
- wry
tauri:
applies-to: version-updates
patterns:
- tauri
- tauri-*
- wry
update-types:
- minor
- patch

- package-ecosystem: github-actions
directory: /
target-branch: dev
schedule:
interval: weekly
open-pull-requests-limit: 5
open-pull-requests-limit: 0

# Keep root workspaces and the separately deployed worker in independent jobs.
# Fastify security fixes share a family PR; unmatched dependencies stay individual.
# Routine majors require a maintainer decision, except supported Electron majors.
# The all-dependency rule preserves security coverage, including major fixes.
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 0
allow:
- dependency-type: all
update-types:
- version-update:semver-minor
- version-update:semver-patch
- dependency-name: electron
update-types:
- version-update:semver-major
groups:
fastify-security:
applies-to: security-updates
patterns:
- fastify
- "@fastify/*"
electron-toolchain:
applies-to: version-updates
patterns:
- electron
- electron-builder
- electron-vite
update-types:
- minor
- patch
tauri-js:
applies-to: version-updates
patterns:
- "@tauri-apps/*"
update-types:
- minor
- patch
opencode-sdk:
applies-to: version-updates
patterns:
- "@opencode/client"
- "@opencode/schema"
- "@opencode/plugin"
update-types:
- minor
- patch

- package-ecosystem: npm
directory: /packages/cloudflare
schedule:
interval: weekly
open-pull-requests-limit: 0
allow:
- dependency-type: all
update-types:
- version-update:semver-minor
- version-update:semver-patch
groups:
cloudflare-security:
applies-to: security-updates
patterns:
- wrangler
- miniflare
- workerd
- "@cloudflare/*"
2 changes: 1 addition & 1 deletion .github/scripts/binary-build-order.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ test("PR builds and both release channels share the ordering without bypassing P
assert.equal(workflow("release.yml").jobs.release.uses, "./.github/workflows/reusable-release.yml")
assert.deepEqual(pr.needs, [
"authorize", "tests", "tests-browser", "tests-tauri-windows", "tests-opencode-compatibility",
"tests-session-pruning-native", "tests-runtime-contracts", "tests-tauri-macos",
"tests-session-pruning-native", "tests-runtime-contracts", "tests-tauri-macos", "tests-tauri-webkit",
])
assert.equal(pr.with.upload, false)
assert.equal(pr.with.set_versions, false)
Expand Down
102 changes: 102 additions & 0 deletions .github/scripts/dependabot-policy.test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
const assert = require("node:assert/strict")
const fs = require("node:fs")
const path = require("node:path")
const test = require("node:test")
const yaml = require("yaml")

const source = fs.readFileSync(path.join(__dirname, "../dependabot.yml"), "utf8")
const parse = (text) => yaml.parse(text, { strict: true, uniqueKeys: true })
const minorPatch = ["version-update:semver-minor", "version-update:semver-patch"]
const allDependencies = { "dependency-type": "all", "update-types": minorPatch }
const electronMajor = { "dependency-name": "electron", "update-types": ["version-update:semver-major"] }
const versionGroup = (patterns) => ({
"applies-to": "version-updates", patterns, "update-types": ["minor", "patch"],
})

// Assert the configuration contract, leaving update resolution to Dependabot.
// Upstream Job#collect_permitted_update_types unions matching allow rules;
// Job#ignored_versions_from_allowed_update_types bypasses security updates.
// GroupUpdateAllVersions#perform runs individual updates after security groups;
// DependencyGroupEngine keeps unmatched dependencies ungrouped.
function assertPolicy(config) {
assert.equal(config.version, 2)
assert.deepEqual(config.updates.map((entry) => [entry["package-ecosystem"], entry.directory ?? entry.directories]), [
["cargo", "/packages/tauri-app"], ["github-actions", "/"], ["npm", "/"], ["npm", "/packages/cloudflare"],
])
for (const entry of config.updates) {
assert.equal(entry.schedule.interval, "weekly")
assert.equal(entry["open-pull-requests-limit"], 0, "routine version updates stay off; security updates are separate")
assert.equal(entry.ignore, undefined, "keep security fixes eligible at every version")
assert.equal(entry["exclude-paths"], undefined, "cover every workspace manifest")
assert.equal(entry.vendor, undefined, "local Cargo patches require manual re-qualification")
}
const [cargo, actions, npm, cloudflare] = config.updates
assert.equal(cargo["target-branch"], undefined, "share default-branch configuration with security updates")
assert.equal(npm["target-branch"], undefined, "share default-branch configuration with security updates")
assert.equal(cloudflare["target-branch"], undefined, "keep worker security updates on the default branch")
assert.equal(actions["target-branch"], "dev")
assert.deepEqual(cargo.allow, [allDependencies], "all crates, including transitives; routine minor/patch only")
assert.deepEqual(npm.allow, [allDependencies, electronMajor], "all npm names; only Electron adds routine majors")
assert.deepEqual(cloudflare.allow, [allDependencies], "worker updates keep independent all-package security coverage")
assert.deepEqual(cloudflare.groups, {
"cloudflare-security": { "applies-to": "security-updates", patterns: ["wrangler", "miniflare", "workerd", "@cloudflare/*"] },
}, "only the related worker toolchain shares a security group")
assert.equal(actions.allow, undefined)
assert.equal(actions.groups, undefined)
assert.deepEqual(Object.keys(cargo.groups), ["tauri-security", "tauri"],
"only related Tauri updates share a group")
assert.deepEqual(cargo.groups, {
"tauri-security": { "applies-to": "security-updates", patterns: ["tauri", "tauri-*", "wry"] },
tauri: versionGroup(["tauri", "tauri-*", "wry"]),
})
assert.deepEqual(Object.keys(npm.groups), ["fastify-security", "electron-toolchain", "tauri-js", "opencode-sdk"],
"only related families share a group; Electron majors stay individual")
assert.deepEqual(npm.groups, {
"fastify-security": { "applies-to": "security-updates", patterns: ["fastify", "@fastify/*"] },
"electron-toolchain": versionGroup(["electron", "electron-builder", "electron-vite"]),
"tauri-js": versionGroup(["@tauri-apps/*"]),
"opencode-sdk": versionGroup(["@opencode/client", "@opencode/schema", "@opencode/plugin"]),
})
}

test("Dependabot keeps broad security coverage and bounded routine updates", () => {
assertPolicy(parse(source))
})

test("policy check rejects security restrictions, broad majors and incorrect grouping", () => {
const mutations = [
...[0, 1, 2, 3].map(index => (config) => { config.updates[index]["open-pull-requests-limit"] = 1 }),
(config) => { config.updates[0].allow[0]["dependency-name"] = "tauri*" },
(config) => { config.updates[0].allow[0]["dependency-type"] = "direct" },
(config) => { config.updates[0]["target-branch"] = "other-branch" },
(config) => { config.updates[2].allow[0]["update-types"].push("version-update:semver-major") },
(config) => { delete config.updates[2].allow[0]["update-types"] },
(config) => { config.updates[2].allow.pop() },
(config) => { config.updates[2].ignore = [{ "dependency-name": "*" }] },
(config) => { config.updates[0].groups.catchall = versionGroup(["*"]) },
(config) => { config.updates[0].groups["tauri-security"].patterns.push("*") },
(config) => { config.updates[2].groups["fastify-security"].patterns = ["*"] },
(config) => { config.updates[0].groups["tauri-security"]["update-types"] = ["minor", "patch"] },
(config) => { config.updates[2].groups["fastify-security"]["update-types"] = ["minor", "patch"] },
(config) => { delete config.updates[0].groups["tauri-security"] },
(config) => { delete config.updates[2].groups["fastify-security"] },
(config) => { config.updates.pop() },
(config) => { config.updates[2].directories = ["/", "/packages/cloudflare"]; delete config.updates[2].directory },
(config) => { config.updates[2].groups.catchall = versionGroup(["*"]) },
(config) => { config.updates[2].groups["opencode-sdk"]["update-types"].push("major") },
(config) => { config.updates[3].allow[0]["dependency-name"] = "wrangler" },
(config) => { config.updates[3].allow.push(electronMajor) },
(config) => { config.updates[3]["target-branch"] = "other-branch" },
(config) => { config.updates[3].groups = { catchall: versionGroup(["*"]) } },
(config) => { config.updates[0].groups = Object.fromEntries(Object.entries(config.updates[0].groups).reverse()) },
]
for (const mutate of mutations) {
const config = parse(source)
mutate(config)
assert.throws(() => assertPolicy(config), assert.AssertionError)
}
})

test("policy YAML rejects duplicate keys", () => {
assert.throws(() => parse(`${source}\nversion: 2\n`), { code: "DUPLICATE_KEY" })
})
Loading
Loading