@h-rig/product-ad-website 0.0.6-alpha.283
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/package.json +52 -0
- package/site/assets/favicon.svg +1 -0
- package/site/assets/og-card.png +0 -0
- package/site/assets/rig.css +199 -0
- package/site/assets/rig.js +66 -0
- package/site/assets/search-index.json +1 -0
- package/site/assets/site.css +46 -0
- package/site/assets/site.js +510 -0
- package/site/cli/index.html +88 -0
- package/site/config/index.html +256 -0
- package/site/docs/agent-tools/index.html +129 -0
- package/site/docs/architecture/index.html +42 -0
- package/site/docs/capabilities/index.html +84 -0
- package/site/docs/entities/index.html +68 -0
- package/site/docs/environment/index.html +62 -0
- package/site/docs/faq/index.html +17 -0
- package/site/docs/fleet-irc/index.html +44 -0
- package/site/docs/gate/index.html +128 -0
- package/site/docs/getting-started/index.html +50 -0
- package/site/docs/glossary/index.html +31 -0
- package/site/docs/index.html +68 -0
- package/site/docs/operator/index.html +49 -0
- package/site/docs/packages/index.html +36 -0
- package/site/docs/pi-extensions/index.html +15 -0
- package/site/docs/recipes/index.html +28 -0
- package/site/docs/runs/index.html +35 -0
- package/site/docs/security/index.html +29 -0
- package/site/docs/server/index.html +27 -0
- package/site/docs/swarm-commander/index.html +41 -0
- package/site/docs/task-sources/index.html +129 -0
- package/site/docs/troubleshooting/index.html +23 -0
- package/site/docs/validators/index.html +177 -0
- package/site/index.html +1653 -0
- package/site/plugins/examples/index.html +419 -0
- package/site/plugins/index.html +193 -0
- package/site/skills/index.html +140 -0
- package/site/variants/deck/index.html +1 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Validators & hooks // RIG</title><meta property="og:image" content="https://how.rig-does.work/assets/og-card.png"><meta name="twitter:card" content="summary_large_image"><meta name="description" content="Deterministic validators gate every run after the agent declares ready; hooks intercept session events. The contract shapes, in-process dispatch, the @rig/core/hook-protocol helpers, and the 30-attempt steer-and-retry loop."><meta property="og:title" content="Validators & hooks // RIG"><meta property="og:description" content="Deterministic validators gate every run after the agent declares ready; hooks intercept session events. The contract shapes, in-process dispatch, the @rig/core/hook-protocol helpers, and the 30-attempt steer-and-retry loop."><link rel="stylesheet" href="../../assets/rig.css?v=v9"><link rel="stylesheet" href="../../assets/site.css?v=v9"><link rel="icon" type="image/svg+xml" href="../../assets/favicon.svg"></head><body data-doc-theme="rails"><canvas id="docfield"></canvas><nav class="nav"><div class="nav-in"><a class="brand" href="../../index.html"><span class="mk"></span><span class="blk">Rig</span></a><button class="burger" onclick="document.getElementById('nl').classList.toggle('open')">menu</button><div class="nav-links" id="nl"><button class="nsearch" type="button" onclick="rigSearchOpen()">search<kbd>⌘K</kbd></button><a href="../../index.html" class="">overview</a><a href="../../docs/index.html" class="active">docs</a><a href="../../plugins/index.html" class="">plugins</a><a href="../../skills/index.html" class="">skills</a><a href="../../cli/index.html" class="">cli</a><a href="../../config/index.html" class="">config</a><a href="https://github.com/humanity-org/rig" class="ext">github</a></div></div></nav><div class="docwrap"><aside class="sidebar"><div class="grp">START</div><a href="../../docs/index.html" class="">docs home</a><a href="../../docs/capabilities/index.html" class="">capabilities</a><a href="../../docs/getting-started/index.html" class="">getting started</a><div class="grp">RUN</div><a href="../../docs/runs/index.html" class="">the run lifecycle</a><a href="../../docs/operator/index.html" class="">operator guide</a><a href="../../docs/swarm-commander/index.html" class="">swarm commander</a><a href="../../docs/fleet-irc/index.html" class="">drone messages</a><a href="../../docs/agent-tools/index.html" class="">agent tools</a><a href="../../docs/gate/index.html" class="">the merge gate</a><a href="../../docs/troubleshooting/index.html" class="">troubleshooting</a><div class="grp">ARCHITECTURE</div><a href="../../docs/architecture/index.html" class="">how rig works</a><a href="../../docs/entities/index.html" class="">entities & data model</a><a href="../../docs/server/index.html" class="">OMP relay/web</a><a href="../../docs/security/index.html" class="">security & trust</a><a href="../../docs/environment/index.html" class="">environment variables</a><div class="grp">EXTEND</div><a href="../../plugins/index.html" class="">plugin authoring</a><a href="../../plugins/examples/index.html" class="">plugin examples</a><a href="../../docs/pi-extensions/index.html" class="">OMP/Pi extensions</a><a href="../../skills/index.html" class="">skills</a><a href="../../docs/task-sources/index.html" class="">task sources</a><a href="../../docs/validators/index.html" class="active">validators & hooks</a><div class="grp">OPERATE</div><a href="../../cli/index.html" class="">cli reference</a><a href="../../config/index.html" class="">rig.config.ts</a><a href="../../docs/recipes/index.html" class="">recipes</a><a href="../../docs/glossary/index.html" class="">glossary</a><a href="../../docs/faq/index.html" class="">faq</a><div class="grp">ENGINE</div><a href="../../docs/packages/index.html" class="">packages</a></aside><main class="doc"><h1>Validators & hooks</h1>
|
|
2
|
+
<p class="lede">The drone says “done.” Validators are the deterministic gate that decides whether it’s telling the truth; hooks are shell commands that watch every move it makes mid-session. Two enforcement channels, both plugin-contributed, neither one the drone can argue with.</p>
|
|
3
|
+
|
|
4
|
+
<h2 id="two-channels">Two channels, two moments <span class="ns">// gate vs intercept</span></h2>
|
|
5
|
+
<p>Rig pins down the work at two moments, with two mechanisms. <b>Validators</b> judge the finished worktree — they run once the agent says “done”, at the <a href="../runs/index.html#stages">Validate stage</a>, and their pass/fail decides whether the work advances toward a PR. <b>Hooks</b> intercept the work <i>as it happens</i> — the harness fires them on session events (a tool call, a prompt, the Stop signal) and a blocking hook can stop a write before it lands. The agent cannot argue with either: a validator only passes when the check is true, and a hook either exits clean or feeds its message straight back into the session.</p>
|
|
6
|
+
<pre class="code"><span class="c">// where each channel sits relative to the run</span>
|
|
7
|
+
agent works in worktree
|
|
8
|
+
<span class="p">│</span> <span class="f">PreToolUse</span> / <span class="f">PostToolUse</span> / <span class="f">UserPromptSubmit</span> hooks fire here <span class="c">← intercept</span>
|
|
9
|
+
<span class="p">↓</span>
|
|
10
|
+
agent declares ready
|
|
11
|
+
<span class="p">│</span> <span class="f">Stop</span> hook fires here
|
|
12
|
+
<span class="p">↓</span>
|
|
13
|
+
<span class="k">VALIDATE</span> stage — server runs the task’s validators <span class="c">← gate</span>
|
|
14
|
+
<span class="p">├─</span> all pass <span class="p">→</span> Commit → Open PR → the merge gate
|
|
15
|
+
<span class="p">└─</span> any fail <span class="p">→</span> steer agent with the summary, retry (up to the attempt cap)</pre>
|
|
16
|
+
<div class="note">Validators gate; hooks intercept. A validator decides “is the finished work acceptable?” A hook decides “is this single action allowed right now?” <b>Use a validator for end-state truth, a hook for in-flight guardrails.</b></div>
|
|
17
|
+
|
|
18
|
+
<h2 id="model">What a validator is</h2>
|
|
19
|
+
<p>A deterministic check the server runs against the drone’s worktree at the <a href="../runs/index.html#stages">Validate stage of the lifecycle</a> — after the drone says “done”, before any commit reaches a PR. The drone cannot talk a validator into passing; it can only make the check true. Every registration carries a category:</p>
|
|
20
|
+
<table class="t"><thead><tr><th>category</th><th>checks</th></tr></thead>
|
|
21
|
+
<tbody>
|
|
22
|
+
<tr><td><code>boundary</code></td><td>Safety and drift — did the change stay where it belongs?</td></tr>
|
|
23
|
+
<tr><td><code>contract</code></td><td>Shape and ownership — do the artifacts have the structure policy demands?</td></tr>
|
|
24
|
+
<tr><td><code>integration</code></td><td>Cross-repo, CI/CD, and operator-workstation wiring.</td></tr>
|
|
25
|
+
<tr><td><code>regression</code></td><td>Things that were true and must stay true.</td></tr>
|
|
26
|
+
<tr><td><code>external</code></td><td>Checks against systems outside the worktree.</td></tr>
|
|
27
|
+
<tr><td><code>custom</code></td><td>Yours.</td></tr>
|
|
28
|
+
</tbody></table>
|
|
29
|
+
|
|
30
|
+
<h2 id="contract">The contract</h2>
|
|
31
|
+
<p>A plugin contributes a validator in two halves. The declarative half is the registration — <code>{ id, category, description? }</code>. The executable half adds a <code>run</code> method:</p>
|
|
32
|
+
<pre class="code"><span class="k">import</span> { definePlugin } <span class="k">from</span> <span class="s">"@rig/core"</span>;
|
|
33
|
+
|
|
34
|
+
<span class="k">export default function</span> <span class="f">myPlugin</span>() {
|
|
35
|
+
<span class="k">return</span> <span class="f">definePlugin</span>(
|
|
36
|
+
{ name: <span class="s">"my-plugin"</span>, version: <span class="s">"0.1.0"</span>,
|
|
37
|
+
contributes: { validators: [{ id: <span class="s">"my:no-empty-changes"</span>, category: <span class="s">"boundary"</span> }] } },
|
|
38
|
+
{ validators: [{ id: <span class="s">"my:no-empty-changes"</span>, category: <span class="s">"boundary"</span>,
|
|
39
|
+
<span class="k">async</span> <span class="f">run</span>(ctx) {
|
|
40
|
+
<span class="c">// ctx.workspaceRoot is the run's worktree; ctx.scope the task's scope: labels</span>
|
|
41
|
+
<span class="k">return</span> { id: <span class="s">"my:no-empty-changes"</span>, passed: <span class="k">true</span>,
|
|
42
|
+
summary: <span class="s">`inspected ${ctx.scope.length} scope entries for ${ctx.taskId}`</span> };
|
|
43
|
+
} }] },
|
|
44
|
+
);
|
|
45
|
+
}</pre>
|
|
46
|
+
<p><code>run(ctx)</code> resolves to a <code>ValidatorResult</code>: <code>{ id, passed, summary, details? }</code>. The <code>summary</code> is what the agent is steered with on failure — make it actionable, because it shows up verbatim in agent output and release gates. The context:</p>
|
|
47
|
+
<table class="t"><thead><tr><th>ValidatorContext field</th><th>meaning</th></tr></thead>
|
|
48
|
+
<tbody>
|
|
49
|
+
<tr><td><code>taskId</code></td><td>The task under validation.</td></tr>
|
|
50
|
+
<tr><td><code>workspaceRoot</code></td><td>The isolated worktree the run produced — check this, not the main tree.</td></tr>
|
|
51
|
+
<tr><td><code>scope</code></td><td>The task’s <code>scope:</code> entries (readonly string array).</td></tr>
|
|
52
|
+
<tr><td><code>monorepoRoot?</code></td><td>The enclosing monorepo root, when distinct.</td></tr>
|
|
53
|
+
<tr><td><code>artifactsDir?</code></td><td>Where to write evidence the timeline records.</td></tr>
|
|
54
|
+
<tr><td><code>taskConfig?</code></td><td>Task-level configuration passthrough.</td></tr>
|
|
55
|
+
</tbody></table>
|
|
56
|
+
|
|
57
|
+
<h2 id="dispatch">How a validator runs <span class="ns">// in-process, or a compiled binary</span></h2>
|
|
58
|
+
<p>Validation is <b>check-ID only</b>. Every entry in a task’s <code>validators</code>/<code>validation</code> list must match <code>category:check-name</code> (for example <code>boundary:no-empty-changes</code>); anything else is rejected as an invalid entry. For each id the server dispatches in two phases:</p>
|
|
59
|
+
<table class="t"><thead><tr><th>phase</th><th>what happens</th></tr></thead>
|
|
60
|
+
<tbody>
|
|
61
|
+
<tr><td>in-process</td><td>If a plugin registered a validator for the id, the server builds one <code>ValidatorContext</code> for the task and calls <code>.run(ctx)</code> directly — no subprocess.</td></tr>
|
|
62
|
+
<tr><td>binary fallback</td><td>If nothing is registered for the id, the server runs a compiled validator binary, reads its <code>ValidatorOutput</code> JSON from stdout, and maps its exit code.</td></tr>
|
|
63
|
+
</tbody></table>
|
|
64
|
+
<p>Both paths return the identical <code>ValidatorOutput</code> shape (<code>{ id, passed, summary, details? }</code>), so the caller sees no difference. A script-style binary just prints that JSON and exits — <code>0</code> pass, <code>1</code> fail, <code>2</code> error — and reads the worktree from <code>RIG_TASK_WORKSPACE</code>:</p>
|
|
65
|
+
<pre class="code"><span class="c">// validators/no-empty-changes — a script-style validator binary</span>
|
|
66
|
+
<span class="k">const</span> root = process.env.<span class="p">RIG_TASK_WORKSPACE</span> ?? process.cwd();
|
|
67
|
+
<span class="k">const</span> changed = <span class="f">gitDiffNames</span>(root); <span class="c">// your own check</span>
|
|
68
|
+
<span class="k">const</span> passed = changed.length > <span class="p">0</span>;
|
|
69
|
+
|
|
70
|
+
<span class="f">console</span>.<span class="f">log</span>(<span class="f">JSON</span>.<span class="f">stringify</span>({
|
|
71
|
+
id: <span class="s">"boundary:no-empty-changes"</span>,
|
|
72
|
+
passed,
|
|
73
|
+
summary: passed ? <span class="s">`${changed.length} files changed`</span> : <span class="s">"no changes on disk"</span>,
|
|
74
|
+
}));
|
|
75
|
+
process.<span class="f">exit</span>(passed ? <span class="p">0</span> : <span class="p">1</span>);</pre>
|
|
76
|
+
<div class="note"><code>@rig/lifecycle-plugin</code> ships one built-in in-process validator, <code>std:typecheck</code> (category <code>custom</code>): it runs the package <code>typecheck</code> script in the worktree and passes when it exits 0. It is contributed like any other validator — the floor’s validator registry starts empty and is populated by plugin contributions.</div>
|
|
77
|
+
|
|
78
|
+
<h2 id="selecting">How a task selects validators, and the retry loop</h2>
|
|
79
|
+
<p>A task picks its validators by id. <code>validator:</code> labels on the task source — a <code>validator:boundary:no-empty-changes</code> label on a GitHub issue or a Linear issue — populate the task’s <code>validators</code>/<code>validation</code> fields. When validation fails, the server doesn’t end the run — it <b>steers the drone with the validator’s actionable output</b> and sends it back in, up to <code>automation.maxValidationAttempts</code> (default 30). Only exhausting the cap fails the run.</p>
|
|
80
|
+
<div class="pi-term" data-title="pi — worker brain (attached)" data-rows="12">
|
|
81
|
+
<script type="application/json">[
|
|
82
|
+
{"t":"u","s":"validation failed and rig steered you with the validator output — what did it say?"},
|
|
83
|
+
{"t":"think","ms":1400},
|
|
84
|
+
{"t":"out","s":"my:no-empty-changes failed: src/parser/lexer.ts is zero bytes. I created the file and never wrote the lexer."},
|
|
85
|
+
{"t":"code","s":"$ bun test packages/parser # 3 fail"},
|
|
86
|
+
{"t":"add","s":"+ export function lex(src: string): Token[] {"},
|
|
87
|
+
{"t":"del","s":"- // TODO: implement"},
|
|
88
|
+
{"t":"out","s":"Implemented, tests green. Declaring ready again — attempt 2 of 30."},
|
|
89
|
+
{"t":"pause","ms":2600}
|
|
90
|
+
]</script>
|
|
91
|
+
<div class="pi-term-status-data" hidden data-path="~/work/rig/.worktrees/18 (rig/18)" data-stats="↑32k ↓1.4k R96k $0.000 (sub) 18.9%/128k (auto)" data-model="worker brain • thinking off"></div>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<h2 id="hooks">Hooks</h2>
|
|
95
|
+
<p>Hooks are the other enforcement channel: plugin registrations fired by the worker harness on session events, declared as <code>{ id, event, matcher, command?, description? }</code>. A plugin implements a hook either as a raw shell <code>command</code> (resolved as-is by the harness’s hook runner) or as a <b>typed function</b> registered via <code>definePlugin(meta, { hooks: { [id]: impl } })</code> that returns <code>{ decision: "allow" | "block", reason?, systemMessage? }</code>. Either way, where validators judge the finished worktree, hooks intercept the work as it happens — a <code>PreToolUse</code> hook can block a write before it lands.</p>
|
|
96
|
+
<table class="t"><thead><tr><th>field</th><th>values</th></tr></thead>
|
|
97
|
+
<tbody>
|
|
98
|
+
<tr><td><code>event</code></td><td><code>PreToolUse</code>, <code>PostToolUse</code>, <code>UserPromptSubmit</code>, <code>Stop</code>, <code>SessionStart</code>, <code>SessionEnd</code>.</td></tr>
|
|
99
|
+
<tr><td><code>matcher</code></td><td><code>{ kind: "all" }</code> · <code>{ kind: "tool", name }</code> · <code>{ kind: "glob", pattern }</code>.</td></tr>
|
|
100
|
+
<tr><td><code>command</code></td><td>A shell command, resolved as-is by the harness’s hook runner. Prefer absolute paths or the project-root environment variable exposed by the active harness.</td></tr>
|
|
101
|
+
</tbody></table>
|
|
102
|
+
<pre class="code">hooks: [{
|
|
103
|
+
id: <span class="s">"my:stop-banner"</span>,
|
|
104
|
+
event: <span class="s">"Stop"</span>,
|
|
105
|
+
matcher: { kind: <span class="s">"all"</span> },
|
|
106
|
+
command: <span class="s">"echo '[my-plugin] task complete'"</span>,
|
|
107
|
+
}]</pre>
|
|
108
|
+
<div class="note"><b>Two hook systems, one word.</b> These registrations are harness hooks. The extension events an OMP/Pi package handles inside the live session are a different layer, reported through the run read model and <code>/drone</code>. <a href="../pi-extensions/index.html">OMP/Pi extensions →</a></div>
|
|
109
|
+
|
|
110
|
+
<h3 id="hook-exit-codes">The exit-code contract</h3>
|
|
111
|
+
<p>A hook command is just a process; what the harness does next is decided by how that process exits. The convention is simple — <b>exit 0 means “allow”</b>, and a <b>non-zero exit means “block”</b>, with the command’s stdout fed back into the session as the reason. Rig’s policy-guard hooks lean on exactly this: <code>@rig/core/hook-protocol</code>’s <code>block()</code> prints a <code>BLOCKED:</code> line and calls <code>process.exit(1)</code>. A hook that finds nothing to object to simply returns and exits 0.</p>
|
|
112
|
+
<table class="t"><thead><tr><th>exit</th><th>meaning</th><th>from</th></tr></thead>
|
|
113
|
+
<tbody>
|
|
114
|
+
<tr><td><code>0</code></td><td>Allow — the action proceeds; any stdout is informational.</td><td>clean return, or no matched rule</td></tr>
|
|
115
|
+
<tr><td><code>1</code></td><td>Block — the stdout message (a <code>BLOCKED:</code> line) is surfaced to the agent and the action is rejected.</td><td><code>block(name, msg, root)</code></td></tr>
|
|
116
|
+
</tbody></table>
|
|
117
|
+
<div class="note">This is the hook channel’s exit contract, and it is distinct from a script-style validator binary, where the process uses <code>0</code>/<code>1</code>/<code>2</code> (pass / fail / error) and the server reads the <code>ValidatorOutput</code> JSON on stdout rather than treating non-zero as “block this action.” <b>Hooks gate one action; validators report on the whole worktree.</b></div>
|
|
118
|
+
|
|
119
|
+
<h3 id="project-root-commands">Project-root commands <span class="ns">// portable script paths</span></h3>
|
|
120
|
+
<p>Hook <code>command</code> strings are resolved <i>as-is</i> by the harness’s hook runner — there is no implicit working directory you can rely on. Point commands at an absolute path or at a project-root variable supplied by the active harness; never use a bare relative path.</p>
|
|
121
|
+
<pre class="code">hooks: [{
|
|
122
|
+
id: <span class="s">"my:stop-check"</span>,
|
|
123
|
+
event: <span class="s">"Stop"</span>,
|
|
124
|
+
matcher: { kind: <span class="s">"all"</span> },
|
|
125
|
+
command: <span class="s">"/repo/scripts/my-stop-check.sh"</span>,
|
|
126
|
+
}]</pre>
|
|
127
|
+
|
|
128
|
+
<h3 id="hook-trips-log">hook_trips.log <span class="ns">// the escalation ledger</span></h3>
|
|
129
|
+
<p>Every call to <code>block()</code> appends a line — <code><hookName> <ISO-timestamp></code> — to <code>hook_trips.log</code> in the run’s state directory (<code>.rig/state/hook_trips.log</code>; the dir is the baked state dir if one was compiled in, else <code>RIG_TASK_WORKSPACE</code>’s <code>.rig/state</code>, else the project root’s). Its purpose is twofold: it is an audit trail of every guardrail the agent hit, and it drives <b>escalation</b> — on the 3rd trip of the <i>same</i> hook, <code>block()</code> appends a sharper message (“REPEATED VIOLATION”, re-read the full project instructions, and record the stuck approach in the run’s failure log). A drone that keeps slamming…
|
|
130
|
+
|
|
131
|
+
<h3 id="scope-guard-example">A worked scope-guard hook</h3>
|
|
132
|
+
<p>The first-party <code>scope-guard</code> is the canonical pattern: a <code>PreToolUse</code> hook that reads the tool call, pulls the file paths out of it, and blocks anything that strays outside the task’s <code>scope:</code> globs — before the edit ever touches disk. Stripped to its spine, using only hook-protocol helpers:</p>
|
|
133
|
+
<pre class="code"><span class="c">// scope-guard hook — PreToolUse, matcher { kind: "all" }</span>
|
|
134
|
+
<span class="k">import</span> {
|
|
135
|
+
readHookInput, block, resolveProjectRoot,
|
|
136
|
+
resolveTaskIdForHook, extractToolFilePaths, resolveTaskScopes,
|
|
137
|
+
} <span class="k">from</span> <span class="s">"@rig/core/hook-protocol"</span>;
|
|
138
|
+
|
|
139
|
+
<span class="k">const</span> projectRoot = <span class="f">resolveProjectRoot</span>();
|
|
140
|
+
<span class="k">const</span> parsed = <span class="k">await</span> <span class="f">readHookInput</span>();
|
|
141
|
+
<span class="k">if</span> (!parsed.valid && parsed.hadPayload)
|
|
142
|
+
<span class="f">block</span>(<span class="s">"scope-guard"</span>, <span class="s">"Failed to parse hook input. Blocking for safety."</span>, projectRoot);
|
|
143
|
+
|
|
144
|
+
<span class="k">const</span> taskId = <span class="f">resolveTaskIdForHook</span>(projectRoot);
|
|
145
|
+
<span class="k">const</span> scopes = <span class="k">await</span> <span class="f">resolveTaskScopes</span>(projectRoot, taskId);
|
|
146
|
+
<span class="k">if</span> (scopes.length === <span class="p">0</span>) process.exit(<span class="p">0</span>); <span class="c">// no scope → nothing to guard</span>
|
|
147
|
+
|
|
148
|
+
<span class="k">const</span> paths = <span class="f">extractToolFilePaths</span>(parsed.input.tool_name ?? <span class="s">""</span>, parsed.input.tool_input ?? {});
|
|
149
|
+
<span class="k">for</span> (<span class="k">const</span> p <span class="k">of</span> paths) {
|
|
150
|
+
<span class="k">if</span> (!<span class="f">inScope</span>(p, scopes))
|
|
151
|
+
<span class="f">block</span>(<span class="s">"scope-guard"</span>, <span class="s">`${p} is outside this task's scope.`</span>, projectRoot);
|
|
152
|
+
}
|
|
153
|
+
<span class="c">// fell through → exit 0, the tool call proceeds</span></pre>
|
|
154
|
+
<p>The real hook delegates the in/out-of-scope decision to the runtime guard and also blocks absolute root-repo paths in isolation mode, but the shape is this: parse, resolve context, extract paths, <code>block()</code> or fall through. Because hooks run on <i>every</i> matched event, keep them to one cheap check — the scope check is a glob match, nothing more.</p>
|
|
155
|
+
<div class="note"><code>@rig/guard-plugin</code> contributes the runtime policy-guard hooks — <b>scope-guard</b>, <b>import-guard</b>, <b>safety-guard</b>, <b>test-integrity-guard</b>, plus <b>audit-trail</b> and <b>post-edit-lint</b> (ids like <code>@rig/guard-plugin:scope-guard</code>) — materialized as compiled binaries under <code>.rig/bin/hooks/<name></code> in each run’s isolated runtime and wired into the active harness settings file. A hook registration with no <code>command</code> is metadata-only and skipped. See <a href="../security/index.html#sandbox">security & trust</a>.</div>
|
|
156
|
+
|
|
157
|
+
<h2 id="hook-kit">@rig/core/hook-protocol</h2>
|
|
158
|
+
<p>A deliberately thin toolbox for writing hook <i>commands</i> as standalone scripts or compiled binaries. What it actually contains:</p>
|
|
159
|
+
<ul>
|
|
160
|
+
<li><code>readHookInput()</code> — parses the JSON hook payload (<code>tool_name</code>, <code>tool_input</code>, <code>command</code>) from stdin or <code>RIG_HOOK_INPUT_FILE</code>, with parse-success flags.</li>
|
|
161
|
+
<li><code>block(hookName, message, projectRoot)</code> — the guard primitive: prints a <code>BLOCKED: <message></code> line to stdout (so the agent sees it immediately), appends a trip record to <code>hook_trips.log</code> in the resolved state dir, escalates the message after 3 trips of the same hook, then exits 1.</li>
|
|
162
|
+
<li>Context resolution — <code>resolveProjectRoot</code>, <code>resolveTaskIdForHook</code>, <code>resolveTaskConfig</code>, <code>resolveTaskScopes</code>, <code>resolvePolicyContent</code>, backed by <code>BAKED_*</code> constants: build-time config injected into compiled hook binaries, with an env-var fallback (<code>RIG_BUILD_CONFIG_JSON</code>) for uncompiled runs. A hook always knows whose project and task it’s guarding.</li>
|
|
163
|
+
<li>Tool-payload utilities — <code>extractToolFilePaths</code> pulls file paths out of a tool call, <code>isTestFilePath</code> classifies them. Together with <code>block</code>, that’s a scope-guard or no-test-tampering hook in a few lines.</li>
|
|
164
|
+
<li><code>resolveBunCli</code> / <code>resolveBunCliInvocation</code> and <code>escapeRegExp</code> — runtime plumbing.</li>
|
|
165
|
+
</ul>
|
|
166
|
+
<p>That is the whole surface. It stays small on purpose: hooks run on every matched event, so they should do one cheap check and exit.</p>
|
|
167
|
+
|
|
168
|
+
<h2 id="see-also">See also</h2>
|
|
169
|
+
<ul>
|
|
170
|
+
<li><a href="../../plugins/index.html">Plugin authoring</a> — how the declarative registration and the executable <code>run</code>/<code>command</code> halves are wired by the plugin host.</li>
|
|
171
|
+
<li><a href="../runs/index.html#stages">The run lifecycle</a> — the Validate stage these validators gate, and where it sits among the lifecycle stages.</li>
|
|
172
|
+
<li><a href="../../config/index.html">rig.config.ts</a> — <code>automation.maxValidationAttempts</code> (the retry cap) and the rest of the run-policy knobs.</li>
|
|
173
|
+
<li><a href="../../skills/index.html">Skills</a> — the other plugin-contributed channel materialized into the OMP/Pi session.</li>
|
|
174
|
+
</ul>
|
|
175
|
+
|
|
176
|
+
<div class="nextnav"><a href="../../docs/task-sources/index.html"><span class="dir">← Back</span><span class="ttl">Task sources</span></a><a href="../../cli/index.html"><span class="dir">Next →</span><span class="ttl">CLI reference</span></a></div>
|
|
177
|
+
</main></div><footer><div class="wrap"><div class="foot"><div class="col"><h5>orchestrate</h5><a href="../../docs/getting-started/index.html">getting started</a><a href="../../docs/runs/index.html">the run lifecycle</a><a href="../../docs/operator/index.html">operator guide</a><a href="../../docs/gate/index.html">the merge gate</a></div><div class="col"><h5>extend</h5><a href="../../plugins/index.html">plugin authoring</a><a href="../../skills/index.html">skills guide</a><a href="../../docs/pi-extensions/index.html">OMP/Pi extensions</a><a href="../../docs/packages/index.html">packages</a></div><div class="col"><h5>live</h5><a href="../../index.html">the home rig</a><a href="../../cli/index.html">cli reference</a><a href="https://where.rig-does.work/install">install script</a></div><div class="sig">Rig is the OMP session extension.<br><span class="blk">You are the operator.</span><br>release: rig --version</div></div></div></footer><script src="../../assets/rig.js"></script><script src="../../assets/site.js?v=v9"></script></body></html>
|