Execution Layers
Gadriel Code runs at four points in the development lifecycle. Each layer is a trigger with its own output shape and blocking behavior. The layers are canonical and named verbatim throughout the product.
A guiding principle underlies all four: quality over latency. Latency is explicitly not a priority. A full CI scan is allowed to take minutes (and, on large repos, longer) if that buys thorough analysis — deeper graph algorithms, full-repo reachability, attack simulation, git-history secret sweeps. The one hard constraint is that the earliest layer (Watch) must never block the developer's editor.
The four layers
L1 — Watch
| Trigger | File save (filesystem notify event) |
| Command | gadriel code watch |
| Posture | Asynchronous, non-blocking |
| Output | Findings streamed to the terminal as they arrive; dashboard refresh |
| Blocks? | Never |
Watch mode observes the working tree and re-scans changed files on save. It is strictly non-blocking: the watcher captures the save event, queues the work, and returns immediately so keystrokes are never delayed. Results surface in the terminal (or the dashboard) whenever they are ready — seconds for fast cases, longer for cross-file flows.
As shipped today, Watch is L1-lite: a 300 ms-debounced watcher that runs
Secrets + SAST per changed file and merges results into
.security/findings.json. It skips .git/, node_modules/, target/, and
.security/. The full async pipeline (all scanners, graph integration, daemon
coordination) is future work. --fail-on is ignored at L1 — Watch never
gates.
L2 — Pre-Commit
| Trigger | git commit |
| Command | gadriel code scan --staged (invoked from the git hook) |
| Posture | Synchronous to the commit; allowed to take minutes |
| Output | Terminal findings |
| Blocks? | Yes — blocks the commit when --fail-on matches |
Installed by gadriel code init / gadriel code install, the pre-commit hook
scans staged changes. Installation is non-destructive: an existing
.git/hooks/pre-commit is not overwritten — Gadriel appends its call between
named markers (# >>> gadriel-code-security >>>), and recognized hook managers
(husky, lefthook, pre-commit-rs) get a manager-native entry instead. Re-runs
are idempotent, and --uninstall removes only the marked section. If any
finding meets the --fail-on threshold (default high at L2), the commit is
blocked.
L3 — Pull Request
| Trigger | PR open or branch push |
| Command | gadriel code scan --diff <base> |
| Posture | Synchronous to the PR check; minutes are fine |
| Output | A structured PR comment |
| Blocks? | Via the CI check (posts a BLOCKING banner; job exits 2) |
At the PR layer Gadriel posts a single structured comment — an overall score, a per-pillar table, finding counts by scan type, and graph statistics, with the top critical findings in a collapsible section. Rules governing the comment:
- One comment per branch, edited in place — pushes replace the comment, so the PR does not accumulate a wall of stale scan results.
- Resolved findings are struck through, not deleted — reviewers can see a finding existed and was fixed.
- Secret values are never included — matches are shown as e.g.
ANTHROPIC_API_KEY, never the value.
L3 runs on a clean CI checkout with full git history available, so the
git-history secret sweep is enabled here. Its default --fail-on threshold is
critical. GitHub is fully supported; GitLab/Bitbucket get a degraded
"new comment per push" experience until their adapters ship.
L4 — CI/CD
| Trigger | Merge to main / full-repo scan |
| Command | gadriel code scan --ci (= --report --sbom --fail-on critical) |
| Posture | Whatever it takes (default budget 60 min, configurable) |
| Output | The full artifact set |
| Blocks? | Sets the CI exit code per the contract below |
L4 is self-contained: CI runners have no graph daemon, so it builds the
codebase graph from scratch, runs all six scan types, sweeps the full git
history for secrets, and produces the complete .security/ artifact set —
findings.json (OCSF), pillar scores, SBOMs (SPDX 2.3 + CycloneDX 1.4), the
branded PDF/Markdown report, and dashboard/metrics data. It also writes a
deduplicated history snapshot for trend analysis. This is the layer where a
scan legitimately takes minutes and where thoroughness matters most.
The --layer selector
The active layer determines defaults (which scanners/algorithms run, whether
git-history is swept, what output is produced, and the default --fail-on
threshold). In practice each layer is entered through its natural command
surface:
- L1 —
gadriel code watch - L2 — the installed pre-commit hook (
gadriel code scan --staged) - L3 —
gadriel code scan --diff <base>in PR CI - L4 —
gadriel code scan --cion merge
gadriel code install writes turnkey templates for L2 (git hooks) and L4
(GitHub Actions, GitLab CI, CircleCI, Jenkins), each tagged with a
GADRIEL_MANAGED sentinel so re-runs are idempotent and --uninstall removes
only Gadriel-authored files.
Exit codes
gadriel code scan communicates its verdict through the process exit code.
Writing the .security/ artifacts always succeeds; the exit code is the gate
signal.
| Code | Meaning | Fires when |
|---|---|---|
| 0 | Clean / gate passed | All findings are below the --fail-on threshold. |
| 1 | Gate tripped (warning-class) | Findings below threshold but at least one warning signal — e.g. a finding above the previous severity tier, drift-degrading components, or dependency-health flags. |
| 2 | Failure / tooling error | A finding meets or exceeds --fail-on, or the scan could not complete (timeout, malformed input, OSV snapshot stale beyond threshold). |
| 7 | License gate | A license-policy violation (SCA license finding) trips the dedicated license exit code. |
Layer behavior:
- L1 always exits
0— it never gates. - L2 exits
0or2only — the commit either proceeds or it doesn't; no warning-class on commit. - L3 and L4 honor the full
0/1/2contract. At L3 the CI job exits2when the PR comment carries theBLOCKINGbanner.
--fail-on <severity>
--fail-on <severity> means "gate the layer if any finding is at or above this
severity." Thresholds follow the score bands info (0–3.99), low (4–5.99),
medium (6–7.99), high (8–8.99), critical (≥9.0). Defaults are high for
L2 and critical for L3/L4. --fail-on never runs the scan but never gates —
useful for soak-testing a new policy bundle. Defaults are written into a
version-controlled .gadriel.toml by gadriel code init, with per-user
overrides in ~/.gadriel/config.toml.
Why quality over latency
Earlier drafts imposed strict per-layer latency budgets (sub-2s at Watch,
sub-15s at Pre-Commit). Those were dropped. A developer waiting a few minutes
for a thorough result is an acceptable trade; a developer blocked in their
editor is not. That is why L1 is asynchronous and non-blocking while L2–L4 are
allowed to run as long as the analysis needs. Operators can extend the L4
budget (default 3600s; 0 disables the cap); an overrun surfaces an
Operational-pillar finding (CODE-W4-OPS-LATENCY-001) rather than silently
failing.
Related documentation
- scan-coverage.md — the six scan types and eight pillars.
- languages.md — per-language SAST/taint depth and SCA ecosystem coverage.
