Skip to content

feat(init): carry Taskless trust into Claude Code's auto mode, one Taskless rule per permission file - #522

Merged
theCodeDrift merged 12 commits into
mainfrom
init-global-agent-trust
Oct 16, 2026
Merged

theCodeDrift merged 12 commits into
mainfrom
init-global-agent-trust

Conversation

@theCodeDrift

@theCodeDrift theCodeDrift commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

Claude Code's auto mode refused npx @taskless/cli-nightly@<version> info --json during onboarding as "Code from External", even though .claude/settings.json allowed that exact invocation. The classifier overrules project allow rules for downloaded code, and it reads its own exceptions (autoMode) only from user settings, never from a checked-in project file, so nothing init writes into the repo can clear it. The user already made that trust decision when they ran the installer; this carries it into the session from the one place the classifier reads.

What changes

  • One Taskless rule per project permission file. init removes every Taskless allow rule (@taskless/cli or @taskless/cli-nightly, any version, recorded or not, including the Bash(<invocation>:*) form the skill used to propose) and writes the installed version's. Each removal shows as REMOVE:. Deny rules and other packages' rules are untouched. This replaces the spec's "a rule you wrote is never touched": a pinned rule for a version that is no longer installed allows nothing anyone wants.
  • Global trust entry, Claude Code only for now. The wizard (npx @taskless/cli with no arguments) lists every settings edit together, as aligned ALLOW (project): / REMOVE (project): / ALLOW (global): statements wrapped to 76 columns, under one question, "Make these changes to your settings?", and on yes adds the entry to top-level autoMode.allow in ~/.claude/settings.json (honouring CLAUDE_CONFIG_DIR). It names no version and states that a call is covered only when it names an exact version. It is seeded after "$defaults" when init creates the list, found later by its Taskless CLI: label, never rewritten, offered again on every wizard run that does not find it, and never recorded in the repo manifest. Behind a closed per-harness list, so Codex can join once its guardian review is verified.
  • Batch init never writes outside the project. It reads the entry, warns when it is missing (file, key, and text), and reports it under globalTrust in --json.
  • Skill fallback. On a classifier refusal the installed SKILL.md tells the agent to stop without retrying and hand the user the entry inline, which a test keeps identical to the code's copy. It cannot point at an agent <topic> recipe, since that is fetched through the same refused npx.

Size. About 1,300 hand-written lines (the remaining ~400 are the archive's copy of the specs). It ships as one PR because the warning, the wizard offer, and the skill fallback all hand over the same entry, and splitting them would ship a warning that points at an offer that does not exist yet.

Checks. pnpm typecheck, pnpm lint, and the CLI suite (2,370 tests) pass. openspec validate --strict passes; an archive dry run lost only the intentionally reversed scenario "A user's own rule survives an upgrade". The old cli-init requirement is a REMOVED plus an ADDED, because OpenSpec 1.12 refuses a MODIFIED that drops a scenario, and its title would have been false afterwards.

Follow-up (not a merge gate): in a scratch repo under Claude Code auto mode, with the entry in ~/.claude/settings.json, run onboarding and record whether info --json is still refused. The entry is guidance to a model, not a switch, so this is the only evidence that it lands.

@theCodeDrift

Copy link
Copy Markdown
Member Author

@claude /review

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @theCodeDrift's task in 6m 59s —— View job


Review complete

Review mode: incremental — read 0 prior review thread(s) before reviewing. .prior-review.json held no threads, reviews, or comments, so this is the first review of this PR; nothing to carry forward as already-addressed.

  • Read .prior-review.json (empty — no prior findings)
  • Gather PR diff and metadata (gh pr diff, gh pr view)
  • Review core logic: global-trust.ts, permissions.ts, wizard/steps/summary.ts, wizard/index.ts
  • Review init.ts, init.md, skill/spec changes, archive-vs-main spec consistency
  • Review tests for adequacy
  • Post findings

Summary: This is a well-scoped, carefully-tested change. I traced the core logic change in syncPermissions (computing remove from harness.found(text) — the actual file content — instead of from the manifest's recorded rules) through every harness (Claude, Cursor editor/CLI, OpenCode object/array form, Codex), and it's internally consistent: the regexes correctly exclude near-misses (@taskless/cli-foo, npx -y @taskless/cli) while catching the colon form, unversioned rules, and unrecorded stale rules, and the test suite (permissions.test.ts) exercises exactly these edge cases per harness. The wizard/non-interactive split for syncGlobalTrust correctly keeps writes out of the batch path, re-reads fresh state rather than trusting a stale preview before writing, and the single-confirmation summary layout matches its own tests. I also checked the two openspec spec files this PR modifies (cli-init, skill-taskless) against their delta content in the archived change — per this repo's own documented archive hazard (a MODIFIED block replaces a requirement wholesale), both were applied correctly with no scenario silently dropped.

Three [New] findings, all non-blocking:

  1. openspec/changes/.../design.md:49 — The archived design doc says the wizard asks two confirmations (project, then a separate global-trust one). The actual implementation (summary.ts) and the added spec (agent-global-trust/spec.md) both specify one confirmation covering both, which is also what the wizard-integration tests assert. The design record contradicts the shipped behavior from day one (not just drifted later) — worth a quick fix so the historical record doesn't permanently misdescribe the decision.
  2. packages/cli/src/install/permissions.ts:668-671 (and the matching check at line 690) — Codex file recognition (found()/edit()) now keys entirely off text.startsWith(CODEX_HEADER), an exact literal match. This fixes the "lost manifest record" case this PR targets, but if CODEX_HEADER's wording is ever edited in a later release, every previously-written .codex/rules/taskless.rules stops being recognized as Taskless's own and is permanently skipped as "someone else's file" — a narrower version of the staleness bug this PR exists to fix, just moved to a different trigger. Suggest a comment pinning the header text as immutable, the same way GLOBAL_TRUST_ENTRY is called out.
  3. packages/cli/src/install/permissions.ts:659 — Minor formatting nit: one comment line runs ~120 columns, well past the ~75-80 column wrap used everywhere else in that block.

Nothing else stood out across correctness, security, or test coverage — the global-trust write path correctly avoids home-directory I/O in non-interactive/batch mode, re-validates before writing rather than trusting a stale preview, and the skill body's embedded entry text is pinned byte-for-byte to GLOBAL_TRUST_ENTRY by a dedicated test.

Comment thread packages/cli/src/install/permissions.ts Outdated
Comment thread packages/cli/src/install/permissions.ts Outdated
Comment thread openspec/changes/archive/2026-10-15-init-global-agent-trust/design.md Outdated
@theCodeDrift

Copy link
Copy Markdown
Member Author

Re: @github-actions — "Claude finished @theCodeDrift's task in 6m 59s"…
#522 (comment)

All three findings were addressed in 938adea: Codex files are recognised by a fixed # Written by Taskless mark, the archived design now describes the single grouped confirmation, and the long comment line is rewrapped. Each inline thread has a reply and is resolved.

— AI Coding Agent

@theCodeDrift
theCodeDrift merged commit 39568f3 into main Oct 16, 2026
7 checks passed
@theCodeDrift
theCodeDrift deleted the init-global-agent-trust branch October 16, 2026 06:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant