diff --git a/tools/spec-loop/.last-sync b/tools/spec-loop/.last-sync index 4fb0724c7..90f78dfde 100644 --- a/tools/spec-loop/.last-sync +++ b/tools/spec-loop/.last-sync @@ -1 +1 @@ -43e4c6eeb6ba99a2ee1796f2a8ee712db1c31738 +ccb53a38843646e1d02f97b1a27253b82aa2675d diff --git a/tools/spec-loop/specs/adapters.md b/tools/spec-loop/specs/adapters.md index b8e35d43c..94a8b3d5f 100644 --- a/tools/spec-loop/specs/adapters.md +++ b/tools/spec-loop/specs/adapters.md @@ -37,7 +37,12 @@ by swapping the adapter, not the skill. ## Where it lives - `tools/gmail/` — private-mail read/draft (drafts to the outbox; never - sends). + sends). Skills draft through the `gmail-plaintext` MCP server's + `check_auth` / `create_draft` tools, which run outside the sandbox, never + by shelling out to `oauth-draft-create` or probing the token file: the + OAuth token under `~/.config/apache-magpie/` is not readable from + sandboxed Bash, and sandbox-lint refuses an `allowRead` grant for it + (#1681). - `tools/maildir/` — private-mail drafting to a local Maildir spool (`maildir-draft-create`, plus the read-only `maildir-draft-find` for the existing-draft check); an offline alternative to Gmail, selected per @@ -70,7 +75,9 @@ by swapping the adapter, not the skill. Reads and writes keep each file's line endings (LF, CRLF or CR, and a missing final newline), so a field write changes no other byte on a Git checkout with `core.autocrlf` or an SVN one with `svn:eol-style`; a file - mixing line endings is refused. A test runs the body-field conformance + mixing line endings is refused. Report files and folders it creates are + `0600` and `0700`, readable by their owner only; it never changes the + mode of a file that already exists (#1700). A test runs the body-field conformance cases through the store on temporary Git and SVN storage and reads them back from a fresh checkout. `detect` reports `field_layout` (`default` or `custom`, with the field @@ -95,7 +102,14 @@ by swapping the adapter, not the skill. confirmed host and refuses any write when the token was withheld. Body fields go through a byte-identical copy of github-body-field's parser, with wiki `h1.`–`h3.` headings outside closed code blocks read - as `### `; `body` exports exactly those fields. + as `### `; `body` exports exactly those fields. The CLI takes + an explicit `--deployment {server,cloud}`, which vetted-ops always + passes, so an inherited `JIRA_DEPLOYMENT` cannot switch a Server tracker + to Cloud endpoints (#1683). +- Both tracker backends reach the `magpie-vetted-ops` plugin as generated + copies under `plugins/magpie-vetted-ops/tools/` (the plugin generator's + `copies` table), not symlinks out of the plugin root, so a + self-contained install (Codex, a plugin cache) carries them (#1683). - `tools/github/` — issues/PRs/labels read + write-back helpers. Sub-adapters: `tools/github-body-field/` (reads GitHub issue/PR body structured field sets on the body-field contract — every column-0 diff --git a/tools/spec-loop/specs/adoption-and-setup.md b/tools/spec-loop/specs/adoption-and-setup.md index faa8d8818..647da5f09 100644 --- a/tools/spec-loop/specs/adoption-and-setup.md +++ b/tools/spec-loop/specs/adoption-and-setup.md @@ -728,6 +728,11 @@ committed version with drift detection. Checker verification during `config` and `upgrade` uses `--no-welcome`; unreadable optional guides do not suppress actual setup findings, and guide names cannot select Markdown files outside the shipped guides. +38. A freshly scaffolded `project.md` carries `tracker.platform: TODO`, so + `setup config` asks for the security-tracker backend (`github`, + `jira` or `local-files`) on every first setup with a `security-*` skill + in scope; the Jira steps map labels to whitespace-free names and + require the backend's `doctor` to pass (#1683). ## Validation diff --git a/tools/spec-loop/specs/agent-isolation-sandbox.md b/tools/spec-loop/specs/agent-isolation-sandbox.md index 3a1ae4b44..7c7fbcacf 100644 --- a/tools/spec-loop/specs/agent-isolation-sandbox.md +++ b/tools/spec-loop/specs/agent-isolation-sandbox.md @@ -118,7 +118,14 @@ existing sandbox grants can widen the baseline. See `docs/adapters/gemini.md`. window exists, so the watcher's close signal only sets the flag `watch_for_stop` polls; Tk's own handler called `Tcl_Exit` inside the signal and could deadlock the window mid-redraw until force-quit, and a - regression test pins the ordering (#1376). The + regression test pins the ordering (#1376). Visibility never depends on + activation, which macOS 14 made cooperative: a watcher started in the + background can be refused it, so every window is ordered front with + `orderFrontRegardless` at a level above other windows and joins all Spaces + (including a full-screen terminal's), and activation is only asked for + with `activate`, falling back to the deprecated + `activateIgnoringOtherApps:`; a refusal costs the keyboard, not the + window (#1697). The git the agent runs reads the same global config, so the wrapper's two files are a `sandbox.filesystem.allowRead` grant of their own (nothing wider under `~/.claude/`), or every sandboxed signed commit @@ -162,6 +169,15 @@ existing sandbox grants can widen the baseline. See `docs/adapters/gemini.md`. `git --no-pager commit`), never by a fixed argv slice, and `GuardContext.git_subcommand()` gives contributed guards the same resolution `gh_subcommand()` gives for `gh` (#1330). + Every guard first unwraps the prefix wrappers that run their argument, + `env` (its GNU / BSD options, clustered short flags and `NAME=value` + assignments, which still count as inline overrides) and `command -p`, + until none is left, so `env git commit …` reaches the same guards as + `git commit …`; `command -v` is a lookup and is left alone. A segment + nested past a generous depth limit, carrying an undocumented `env` + option or another `command` flag, is marked unparsed and refused, and + `env -S` is refused outright, because GNU env splits its string with + escapes `shlex` does not reproduce (#1691, #1694). Hooks invoke the engine as a bare `python3`; when that resolves to a pre-3.11 interpreter, the engine re-runs itself under the newest `python3.N` (3.11+) on `PATH`, and exits 1 with an actionable message @@ -245,7 +261,13 @@ existing sandbox grants can widen the baseline. See `docs/adapters/gemini.md`. the framework checkout's settings (the `gh` read allows, the remaining exclusions, family-specific domains, personal tool paths), reconciled into each contributor's `.claude/settings.local.json`. `sandbox-lint` lints the - two merged and keeps every `deny` / `ask` in the floor. + two merged and keeps every `deny` / `ask` in the floor. Its workflow also + runs when `.claude/settings.local.json` changes and fails while that file + is tracked, since a committed copy would load for everyone and its + scalars override the floor's (#1692). sandbox-lint refuses + `~/.config/apache-magpie` in `allowRead`: the Gmail OAuth token there is + read only by the drafting MCP server outside the sandbox, and the + template no longer grants it or the Google API hosts (#1681). - `.gemini/settings.json` and `.gemini/policies/magpie.toml` — Magpie's Gemini profile: tool-sandboxing and an explicitly loaded User-tier approval policy. Scoped shell reads are allowed; other shell calls, native edits, and MCP calls ask; listed commands and credential paths deny. Credential-path denies name the canonical `grep_search`; the native probe verifies its `search_file_content` alias, canonical policy names, and search/multi-file/web/resource argument schemas against the loaded runtime. diff --git a/tools/spec-loop/specs/vetted-command-surface.md b/tools/spec-loop/specs/vetted-command-surface.md index 1ac1e9188..319670052 100644 --- a/tools/spec-loop/specs/vetted-command-surface.md +++ b/tools/spec-loop/specs/vetted-command-surface.md @@ -167,6 +167,15 @@ Every call spells the base `~/.claude/magpie/` (AGENTS.md golden rule): the agent-guard `vetted-ops-spelling` guard refuses an expanded home path, `$HOME/…` or a relative checkout path for `vetted-op`, `vetted-op-read`, `vetted-op-tracker` and `adversarial-review`, and names the `~` command. +It accepts the `~` only bare and space-separated: a quoted or escaped `~`, +a quote after it, or the `--project=~/…` form leaves it unexpanded, so uv +gets a directory named `~` and the exclusion does not match. Because an +exclusion matches a command line by its start, the right spelling behind +`env` or `command` still runs sandboxed and is refused with the command to +run instead. The eval wrapper gets the same treatment: any spelling of +`magpie-run-evals.sh` other than the bare `~/.claude/scripts/…` path is +refused (override `MAGPIE_ALLOW_SCRIPT_PATH=1`), because it would run in the +sandbox and every eval case would error ungraded (#1691). It also refuses an absolute path inside the working tree, and reads `uv`'s options on either side of `run`. Under Codex it lets a `vetted-op-read` through from the exact root Codex's own hook substitutes for `~` (the