feat(core)!: give scheduled jobs their own permission posture - #244
Merged
Conversation
BREAKING CHANGE: an unattended run no longer inherits a permissive permissions.defaultMode. bypassPermissions and acceptEdits are clamped to default unless the job's profile.mode says otherwise. Jobs that relied on the inherited bypass must set profile.mode explicitly. A cron job reads the same settings.json used interactively, so a bypassPermissions chosen for REPL convenience silently became the posture of every job firing at 3am with nobody watching. Those are different decisions and should not share one switch. PR #237 warned about this; the clamp waited until there was an opt-in to point at. TriggerProfile carries mode, permissions and sandbox per job: - mode is honoured as written, including a permissive value. That opt-in is what makes the clamp safe rather than merely restrictive. - permissions only tighten: denies and asks union, allows intersect. Same one-way property as the file contract, for the same reason — a mechanism that can only tighten cannot reduce existing safety whatever the user writes. - sandbox applies only when stricter than ambient. An unrecognised value in hand-edited cron.json is ignored rather than trusted. The clamp is announced in the job log. A silent clamp is as surprising as a silent grant, just in the other direction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
9 tasks
oratis
added a commit
that referenced
this pull request
Aug 8, 2026
Records the threat-model delta, marks the adoption plan implemented with an honest deviation log, and bumps to 0.3.0 (minor, not patch: the unattended permission clamp in #244 is breaking on 0.x). security-model.md gains three threats (#8 in-project secret reads, #9 unattended permissive inheritance, #10 no audit or undo) and a residual-risk section stating plainly what the file contract is not. It constrains dispatcher tool calls, not Bash; path normalization is string math, not realpath. Describing it as secret protection would be the false confidence the plan named as this feature's main risk. The adoption plan now carries a deviation table rather than a claim of clean execution. Six entries, including two where the plan was simply wrong: PR 0's premise that unattended runs might silently allow (the ask path was already fail-closed), and the four-client consistency test (only two hosts resolve policy independently; VS Code and LSP consume the server's answer, and the tests say so instead of implying four verified paths). Two P2 items remain undone and are listed as such. Found while bumping: the repo's own version-consistency test caught Cargo.lock still pinning 0.2.0, a sixth location RELEASING.md's four-item list never mentioned. Both fixed. Co-authored-by: oratis <happyllammar@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR 7 of
docs/FLOATBOAT_ADOPTION_PLAN.md§2.E. Completes the security fix #237 deliberately left half-done.Why break it
A cron job reads the same
settings.jsonyou use interactively. AbypassPermissionschosen for REPL convenience silently became the posture of every job firing at 3am with nobody watching.Those are different decisions. Choosing "never ask me" while sitting there watching is not the same as choosing it for a run nobody sees. They shouldn't share one switch.
#237 shipped a warning for this and explicitly deferred the clamp, because clamping without an opt-in is just breakage. The opt-in now exists, so the clamp lands with it.
The opt-in
{ "schedule": "0 3 * * *", "prompt": "...", "profile": { "mode": "bypassPermissions" } }Or via
CronCreate's newmode/sandboxarguments. An explicitprofile.modeis honoured as written, including a permissive one — that's what makes the clamp a correction rather than a restriction.Everything else only tightens
modepermissionssandboxSame one-way property as the file contract, for the same reason: a mechanism that can only tighten cannot reduce existing safety, whatever a profile author writes. A profile listing
allow: ["Read", "Bash"]against ambientallow: ["Read"]yields["Read"]— there's a test for exactly that.An unrecognised
sandboxvalue in a hand-editedcron.jsonis ignored rather than trusted.The clamp announces itself
A silent clamp is as surprising as a silent grant, just in the other direction.
Migration
Jobs that relied on the inherited bypass need
profile.modeset explicitly. The job log names the exact fix on the first run after upgrading, so this surfaces itself rather than failing mysteriously.Test plan
pnpm test— 1401 passed, 16 skipped (+16 core)pnpm typecheck·pnpm lint·pnpm format:check·pnpm build·node scripts/check-docs.mjsprofile.modehonoured; a stricter profile mode also winsDocumentation
docs/quickstart.md— rewrote the scheduled-jobs caveat into "The permission posture of a scheduled job": the clamp, why the two decisions differ, the opt-in table, and the tighten-only ruleRelease notes label
release-notes:breaking— unattended runs stop inheriting permissive modesRelated
Plan §2.E (PR 7). Completes the deferred half of #237. Research:
docs/research/floatboat.md§3.1(a) — mechanism borrowed, design entirely our own.🤖 Generated with Claude Code