Omniscio documentation
Browse all documentation
  1. Getting Started13
  2. Sessions & Agents115
  3. Inbox & Notifications59
  4. Projects & Tasks95
  5. Automation & Scheduling75
  6. Knowledge & Memory26
  7. AI Features60
  8. Integrations100
  9. Plugins & Marketplace33
  10. Cloud & Teams56
  11. Settings & Customization58
  12. Account & Billing28
  13. Troubleshooting84
  14. CLI & API Reference22
  15. Legal & Policies4
  16. Uncategorised22

Git guardrails (part 2)

The guard rules git-guardrails gained after launch, each dated and named, from the first September additions through the 2026-09-30 pair — the running record of what the guard blocks today, kept as a continuation of part 1.

What it is

This is part 2 of the Git guardrails page. It carries the guard rules added after launch, each dated, moved here because a single page is capped at 40,000 characters.

Each addition below is a rule the guard gained after launch, dated when it landed and named after what it refuses. Read them together with the launch rules on part 1 to see the whole set the guard enforces today.

Where to find it

Nothing on this page is a surface — part 1 covers pausing the guard, what it blocks at launch, the escape hatches and the limits. What follows is the running list of rules added afterwards, in the order they landed.

How it behaves

One more, added 2026-09-03

#13 — a shell command too long to reach the shell. On Windows, the shell Claude Code uses silently throws away everything past about 8,186 bytes of a single command. It does not warn and it does not fail — it just runs a shorter command than the one you sent. When the cut lands inside a quote (it almost always does), the shell then reports unexpected EOF while looking for matching ' at a line number where nothing is wrong, so the error blames your quoting for a problem that is really about size. That one misleading message accounted for 124 reports on the friction ledger; agents spent hours rewriting quotes and swapping heredoc markers chasing a bug that was never there.

The guard now catches it first and says what actually happened, with your command's real byte count and the limit. Two things make a command bigger than it looks, and both are in the message: each apostrophe costs 4 bytes rather than 1 (of the way the command is wrapped), and an em-dash or curly quote costs 3 — which is why a page of prose fails sooner than its length suggests. Write file content with the Write tool instead of a cat <<EOF heredoc, or split the command up.

No escape hatch lifts this one — not advisory mode, not an excluded repo, not the approval window. Nothing can make the shell accept more bytes than it accepts, so letting the command through would only restore the silent truncation. It applies to Bash on Windows only.

It is not alone in that, so do not read it as the single unliftable rule. Three separate lists in the code decide this: long-shell-command, raw-fleet-mutation, destructive-remote-mutation (the git remote rule, #16 below), whole-tree-staging (a git add -A / git add .), empty-branch-config-write (a config write naming an empty branch), the two process-stop rules pattern-selected-stop and app-tree-stop (#17 and #18 below), the two repository-store rules delete-git-store and reinit-existing-repo (#19 and #20 below), auth-artifact-write (a write, move or delete of the permission files themselves — see the override section below) and foreign-worktree-write (a write into a worktree that is someone else's — a warning is a no-op once the folder is not your own working tree) ignore advisory mode (always enforced); long-shell-command, wedged-tree-commit and sparse-phantom-deletion can never be lifted by a grant; and the worktree lockdown and auth-artifact-write sit above the hatch line entirely, so no exclusion, pause or window reaches them either (see the worktree section below) — nor the two repository-store rules, which are never handed a hatch at all. If you are deciding whether an exclusion or an approval window will get you past a refusal, check which of those the rule is in rather than assuming it is only this one.

Two more, added 2026-09-03

#11 — a heavy test run that stayed on your machine. npm run test:agent without --cloud queues behind a slot gate on a box that may already be busy, so the guard asks you to add --cloud. It only ever fires on this project's own test commands, so it can never interrupt you in an unrelated project. Adding --cloud anywhere in the command clears it.

#12 — staging everything at once. git add -A and git add . sweep up every changed file in the folder — including work another agent has in flight but has not finished. The guard asks you to name the files you actually changed. git add --update and any specific path are untouched.

Both were tuned against the same lesson as #8-#10: writing about a command is not running it. A commit message, a note to a teammate, a heredoc, or a search of the docs that happens to mention npm run test:agent or git add -A is text, and none of them trip these rules — that was checked against every wrong alarm the earlier machine-local version of #11 produced.

Neither can be switched off on its own; that is true of every rule in this family. Use the escape hatches below (advisory mode, an excluded repo, or the approval window) instead.

Two more, added 2026-09-04

#14 — a git command hidden inside a script file. The guard used to read only the command an agent typed. So git push --force origin master was caught, but putting that same line in a file and running node cleanup.mjs was not — the command it saw was just node cleanup.mjs, which contains no git at all. Measured against the shipped guard, exactly that shape was allowed on a master checkout, silently, with no warning of any kind.

It now opens the script a command runs — node x.mjs, bash x.sh, powershell -File x.ps1 — and judges what is inside by the same rules. When a block comes from a file, the message says so and names the file, so you are never told your harmless-looking command was a git push without being able to see why.

The half of this that reads code passed inline (bash -c '…', node -e '…') already shipped on 2026-09-03; this is the other half. A script that merely mentions a git command in prose is still fine — the guard only counts it when the script can actually run something.

#15 — a process kill that would take out every agent's test run. Test and check runs don't carry any marker saying which agent they belong to, so "kill the processes matching this script name" hits everyone's, not just yours. On one day in August three such sweeps landed here: one killed up to 67 processes, another ended 12 other sessions' in-flight runs. Those runs die with no result at all, so those agents simply start over — one command, many lost hours.

The rule isn't "never kill". It's "show that you mean your own run", and either proof is enough: name the process id, or name your own worktree in the filter. Every read-only way of listing processes is untouched.

One more, added 2026-09-11

#16 — a git remote change that reaches every worktree. git remote remove, rm, rename, set-url, set-head, set-branches, and prune edit the repo's ONE .git/config (or the tracking refs every checkout reads), and a git worktree shares that file with the main checkout and every sibling worktree. So typing git remote remove origin inside your own feature-branch worktree does not remove your origin — it removes origin for the whole repo. That happened on 2026-09-11: one session believed an earlier git fetch . <refspec> had registered a remote to clean up (it had not — a fetch by path writes a ref, never a remote), removed origin, and every fetch / push / gh pr list across the fleet failed with no git remotes found until it was restored by hand. Removing the promisor remote also drops the partial-clone setting, so even a bare git remote add afterwards is not a full repair.

Rule #1 already refused these verbs — but only while standing on master, which is the one place no agent works. This rule refuses them from any branch, and only when the repo's config is genuinely shared: a linked worktree, or a main checkout that has linked worktrees. A standalone checkout — every throwaway fixture repo the test suites create in a temp folder — is left alone, so git remote add / remove on a scratch repo still works. git remote add of a new remote is allowed everywhere (it is additive, undone by one command, and the first step of the restore), as is git remote update (a fetch) and a prune --dry-run.

The message says what to do instead: a scratch ref belongs under refs/tmp/<name>/ and is dropped with git update-ref -d, never with git remote remove. An excluded repo or an approval window lifts it like the other rules; an agent cannot lift it for itself, because the damage lands on every other agent's worktree. Unlike most rules, it does not follow the enforcement level: it refuses at advisory just as at required (the same always-enforced class as the over-long-command rule), because the incident happened on a box set to advisory, where a warn would only have logged the veto while origin was deleted for everyone. Only the human escape hatches — an excluded repo or an approval window — let it through.

Two more, added 2026-09-25

#17 — a process stop that picks its targets by pattern. On 2026-09-25 an agent stopping its own build ran one command: list every node.exe whose command line contains vite, then stop each one. Omniscio's own launcher is npx electron-vite dev, so the filter matched it too — and the app, with every live session, died with its launcher. A name, a wildcard, a command-line filter or a pipeline cannot tell your process from the app's or another session's, and the guard cannot check what a filter will match, so any stop chosen that way is now refused: Stop-Process -Name, taskkill /IM, pkill, killall, … | Stop-Process, ForEach-Object { Stop-Process -Id $_.ProcessId }, kill $(pgrep …), and a variable filled from a process listing in the same command.

What still works, and what the refusal tells you to do: stop the exact process ids you mean. Stop-Process -Id 1234, taskkill /PID 1234, kill 1234, kill $!, a pid file you wrote when you started the process, the object Start-Process -PassThru gave you, or a loop over a literal list of ids. If you do not know the id, LIST first (read-only), check each row is yours, then stop those exact ids in a second command. Best of all, keep your child's id when you start it.

#18 — stopping the app itself, by its exact id. Listing processes and copying the launcher's id into Stop-Process -Id would take the app down just the same. So the app tells every session it starts which processes are its own — its main process and every process it was launched through, worked out from its live process id, never from names — and a stop of any of them is refused. A cloud session runs on another machine and gets no such list. If you truly asked for a restart, the same one-time override as #9 lifts this rule (and only this one).

Both refuse even in advisory mode. Two fixes rode along. First, the guard used to lose the rest of a powershell -Command "…" after the first escaped quote (\") — which is exactly where that day's stop was hiding. Second, when two rules had something to say about one command, a warning from the first used to end the check before a later rule could refuse.

Two more, added 2026-09-26

#19 — deleting a repository's own .git. On 2026-09-25 a review session whose working folder was the main checkout ran a command meant for a temp folder. A single-quoted path never expanded, so the cd failed and the delete after it ran in the checkout itself, removing its .git — and with it every commit not yet pushed anywhere. Any delete, move or rename that would take a repository's .git with it is now refused: rm -rf .git, Remove-Item -Recurse, rd /s, del /s, mv / Move-Item / Rename-Item / ren of the .git itself (a worktree's .git file included), a glob that empties it (rm -rf .git/*), a folder that holds a repository, and the same inside bash -c, powershell -Command, cmd /c, node -e or python -c. The guard works out where the command really runs: after cd x && … it judges x, and after a cd that may fail followed by ; it judges the folder the shell was already in as well.

#20 — git init inside an existing repository. The same session then ran git init in the checkout and committed into the new, empty store, and three branches were landed into it. git init is now refused whenever its target sits inside a repository, however the target is named: the current folder, a folder argument, -C, --git-dir, --bare, --separate-git-dir, or GIT_DIR=.

Both refuse even in advisory mode, and no exclusion, pause or approval window lifts them — a warning cannot bring a deleted store back. Throwaway repositories are unaffected: anything under your session's scratch folder ($AMC_SESSION_TMP) or the system temp folder can be created, re-initialised and deleted freely, which is also the one-line hint every refusal carries. A path only the shell can work out — "$T/.git", git init "$(mktemp -d)" — is left alone rather than guessed at. A related fix rode along: a warning from a git rule (a write on master, say) used to end the check before these rules ran, so on an advisory box rm -rf .git && git init in the main checkout was only warned about. The warning now waits until every always-enforced rule has had its say.

One more, added 2026-09-30

#21 — a git write the guard cannot place. Added 2026-09-30, after a session reported that a bare git commit in a checkout sitting on master went through, while the very same commit written git -C <that checkout> commit was refused. The guard works out which repository a command touches from the folder the tool call says your session is in. When a call did not say — the detail was missing from what the guard received — it fell back to the folder the guard's own process happened to be running in, and that is a different folder. If it is not a repository, the branch reads as unknown, and an unknown branch quietly switches off every master rule at once, so the write the guard exists to stop was let past with nothing said. A git write the guard cannot place is now refused rather than guessed at, and it says so. Nothing else changed: reads (git status, git log, git diff) are untouched, and a command that names its own folder (git -C <folder> commit, cd <folder> && git commit) is judged on that folder exactly as before — which is also the way out of the refusal. A related fix rode along: a checkout whose branch cannot be read at all (its .git is there, but git will not name the branch — a worktree whose main repository has gone missing) used to be re-checked against the guard process's own folder, which is how that commit was allowed too; it now refuses, the same way the guard Omniscio spawns per session has always refused it.

Related

  • Git guardrails — part 1 of this page, with the surfaces and the user-facing behaviour.

Last verified 2026-10-01