paranoid_
Checks

watch

watch recursively watches a repository and runs a static-only check on every relevant change, live, instead of one shot after the agent stops.

What it does

Run paranoid watch [path] while an agent (or you) is editing a repository. It resolves a base ref once, at startup, then watches the tree for changes to test files, test configuration, CI workflow files, and dependency manifests. On a debounced batch of changes it runs test integrity plus DEP005 against that fixed base and the current worktree, and prints the result.

$ paranoid watch

watching /home/you/project from base 7db5cfbeaacf (Ctrl-C to stop)
14:32:07  write  internal/calc/calc_test.go
14:32:08  1 finding  score 90  verdict pass  (84ms)

paranoid dev
repo: /home/you/project
base: 7db5cfbeaacf
head: worktree (uncommitted changes included)

score: 90/100  verdict: pass

test-integrity (1 finding)

  TI004  high  internal/calc/calc_test.go:6
      A skip was added to an existing test.
      evidence: TestAdd now calls a skip method
      fix: Remove the skip or explain why the test cannot run.

checks run: test-integrity/config, test-integrity/go, test-integrity/javascript, test-integrity/python, dependencies/consistency
...

Every line of output starts with a clock time. A relevant filesystem event gets one compact line as it happens (write, create, remove, or rename, then the path relative to the watched root). Once a debounced run finishes, one status line always prints (score, verdict, finding count, run time); when the run actually found something, the full report follows it, the same text format verify prints.

What counts as a relevant change

watch reuses the exact same file classification verify already uses, from internal/analyze/testintegrity/common and internal/analyze/deps, so "what is a test file" or "what is a manifest" is never a second, drifting definition:

Anything else, a source file, documentation, an asset, does not trigger a run. The watch is recursive: a newly created directory is added to it automatically, and a removed directory is dropped from it. .git/, node_modules/, and common build directories (dist, build, vendor, __pycache__, and similar) are never descended into.

The static-only scope

Every debounced run is test integrity plus DEP005 (dependency consistency) only. It never contacts a dependency registry (the same skip verify --offline gives DEP001 to DEP004) and never runs the clean-room sandbox. Claims, error-handling, api-surface, and safety analysis do not run either. The report is always honest about this: every stage that does not run is listed in checks_skipped with the reason, never silently missing.

This is a deliberate scope, not a shortcut applied silently: a live watch needs an answer in well under a second, and a container run or a network round trip cannot deliver that. Run paranoid verify for the full pipeline, including the sandbox and claims checking, once you are done.

Base and head

The base ref is resolved exactly once, when watch starts: --base if given, otherwise the same default verify uses (merge-base with the default branch, else HEAD~1). Every run for the life of that one watch process compares against that same base, even if you commit or switch branches while it is running. There is no --head flag: the head is always the current worktree, since that is the entire point of a live watch.

Notifications

--notify sends a desktop notification whenever a run finds something: osascript on darwin, notify-send on linux, when either is on PATH. On any other platform, or when the binary is not available, notification is silently skipped; it never fails the run.

Performance target

From a settled change (the debounce window closing) to a printed report, under 1 second on a 100-file diff. This matches the shape of verify's own target (under 2 seconds without the sandbox on a 100-file diff): watch mode's pipeline is a strict subset of that same work, so it stays well inside it.

Lifecycle

Stop with Ctrl-C (SIGINT) or SIGTERM. Shutdown is always clean: the filesystem watcher is closed, and an in-flight check either finishes on its own or is canceled through its context, never left running after the process exits. A single worker runs one check at a time; a change that arrives while a check is still running is coalesced into exactly one more run once it finishes, never a growing queue of pending runs.

Flags

FlagDefaultMeaning
--base stringmerge-base with the default branch, else HEAD~1State to compare the worktree against, resolved once at startup
--notifyoffSend a desktop notification when a run finds something