Commit 1a0fc0d
committed
Phase 1: job image, SDK runner, baked guardrails
First application code. Headless pi proven in isolation -- no queue, no GitHub, no
panel. This is the phase where the traps found during source-verification either get
caught by a test or ship silently, so it comes before everything else.
image/runner/
Builds the loader exactly as INT-SDK-SESSION-OPTIONS specifies, and the tests import
that same factory rather than constructing their own -- a test that builds its own
loader tests the test.
- noContextFiles: true. The SDK equivalent of -nc, and it is OFF by default: omit the
loader entirely and createAgentSession builds one without it, loading a cloned repo's
AGENTS.md from every ancestor up to `/`. The constraint fails open by omission.
- noSkills/noExtensions + additionalSkillPaths. cwd discovery reads the CHECKED-OUT
branch, which on a PR-triggered job may be a fork. additionalSkillPaths is merged in
both branches and never trust-checked, so the worker's materialised .pi/ is the only
thing that loads. Project trust is never granted.
- Guardrails read explicitly from /opt/pi-dispatch/HARD_RULES.md, not discovered. A
trusted project's .pi/APPEND_SYSTEM.md shadows the global path via an early return in
discoverAppendSystemPromptFile, which would delete the safety floor with no error.
Reading them ourselves removes the class: discovery cannot shadow what it does not
supply.
- await reload(). createAgentSession only reloads a loader it built itself; pass your
own and nothing reloads it, and getAppendSystemPrompt is a plain getter -- so the
persona would be silently empty.
Exit codes come from BOTH a try/catch and stopReason, because the two cover disjoint
failure sets. Preflight throws (pi's own JSDoc documents no-model/no-API-key); the agent
loop never does -- a 429 resolves prompt() cleanly. A catch-only runner exits 0 on every
infra failure; a stopReason-only runner exits Node's default 1 on a missing key, which
this protocol defines as retryable, so the queue would pay to retry a job that can never
succeed. Config errors exit 2. All five stopReasons are enumerated: a default branch
would map "length" (truncated output) to silent success.
The turn budget is pi's only spend bound -- there is no max-turns anywhere upstream and
the loop is a bare while(true). The listener is synchronous because _emit does not await
its listeners, so an async check can overshoot; it counts turn_start itself because the
event delivered to subscribe() carries no turnIndex (the indexed one is on the extension
bus). pi's own retry settings are pinned via SettingsManager.inMemory rather than
inherited, which also keeps a project's .pi/settings.json from overriding our spend
controls.
image/Dockerfile
Base pinned by manifest-list digest (node 22.23.1, amd64 + arm64). Fonts installed --
bookworm-slim ships none, so Chromium renders tofu boxes: screenshots that look
plausible, contain no legible text, and pass every test while gutting the requirement
full Chromium is here for. The agent dir is created and chowned explicitly, because
COPY --chown does not apply to parent directories COPY auto-creates, so the EACCES trap
survives the obvious fix. NPM_CONFIG_ENGINE_STRICT is set because npm i -g never reads a
project .npmrc.
guardrails/HARD_RULES.md
The safety floor, not a persona -- this repo ships no persona. Project instructions
layer after it and can add to it, never remove it. Carries a sentinel the tests assert.
Tests: 22, all offline, no API key, no tokens. The loader assertions need pi, which needs
node >=22.19.0 -- above this machine -- so they skip locally and CI sets
PI_DISPATCH_REQUIRE_LOADER_TESTS=1 to make a skip a hard failure. A skipped assertion is
an unverified one.
CI un-gated and rewritten against what exists: it previously asserted through a
PI_ASSERT_MODE hook that was never built and ran `pi -p "reply OK"`, which needs a paid
key -- a contract test that costs money is a contract test that gets disabled.1 parent ac5ddfe commit 1a0fc0d
16 files changed
Lines changed: 2785 additions & 71 deletions
File tree
- .github/workflows
- guardrails
- image
- runner
- src
- test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | | - | |
12 | | - | |
13 | | - | |
14 | | - | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
15 | 14 | | |
16 | 15 | | |
17 | 16 | | |
18 | 17 | | |
19 | 18 | | |
20 | 19 | | |
21 | 20 | | |
| 21 | + | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
25 | 26 | | |
| 27 | + | |
| 28 | + | |
26 | 29 | | |
27 | 30 | | |
28 | 31 | | |
| |||
32 | 35 | | |
33 | 36 | | |
34 | 37 | | |
35 | | - | |
| 38 | + | |
36 | 39 | | |
37 | 40 | | |
38 | 41 | | |
39 | 42 | | |
40 | 43 | | |
41 | 44 | | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
47 | 58 | | |
48 | 59 | | |
49 | | - | |
| 60 | + | |
50 | 61 | | |
51 | 62 | | |
52 | | - | |
| 63 | + | |
53 | 64 | | |
54 | 65 | | |
55 | 66 | | |
56 | 67 | | |
57 | | - | |
58 | | - | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
59 | 106 | | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
73 | 113 | | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
85 | 121 | | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
94 | 127 | | |
95 | 128 | | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
96 | 137 | | |
97 | | - | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
98 | 144 | | |
99 | | - | |
100 | | - | |
101 | | - | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
102 | 149 | | |
103 | | - | |
104 | | - | |
105 | | - | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
106 | 155 | | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
113 | | - | |
114 | | - | |
115 | | - | |
116 | | - | |
117 | | - | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
118 | 159 | | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | | - | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
0 commit comments