You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 1e75ed0
Browse filesBrowse the repository at this point in the historyBrowse files
fix(triggers): run.secrets refuses every steering name the pinned sources read (#511)
The steering set was derived from pi-ai and the SDKs it imports, one hop
deep, with capital-only names and four accessor spellings. That left
bindable: the lowercase twins google-auth-library reads beside the
uppercase forms (google_application_credentials, measured planting a
credential file), GOOGLE_CLOUD_QUOTA_PROJECT, every variable pi reads
for its own configuration (PI_CODING_AGENT_DIR first), and every name
read through a form the spelling list did not have.
The derivation now:
- reads both copies of pi-ai: the hoisted one and the one nested under
pi-coding-agent that the runner dispatches through;
- follows each SDK's imports that its package.json also declares in
dependencies, one hop further (google-auth-library, @smithy/core, the
AWS credential chain, debug, ws);
- counts, after stripping comments with a small tokenizer (checked
against esbuild's parser on every scanned file), every occurrence
that can reach the environment instead of matching accessor
spellings (worker/test/helpers/env-reads.mjs): every env token
(process.env, ctx.env, a parameter named env, { env: e } = process),
every process["env"], every use of an alias of one (const v = env(),
const e = process.env), every call of a helper (derived: any named
function one of whose own parameters is the key of a read). Each occurrence
names a variable or is a site, and every file's sites are pinned with
a count (worker/test/fixtures/provider-steering-sites.json), so a new
occurrence anywhere names something or changes a count;
- adds pi's own PI_* reads from pi-coding-agent's dist (not
dist/bundle/) and the pi packages it declares, evaluating the agent
and session directory keys through pi's config.js, minus
CONTAINER_ENV_NAMES and the names the runner assigns before pi runs
(PI_OFFLINE, PI_TELEMETRY, derived from image/runner/src).
Every name found is reserved and matching stays exact, so the twins are
literal members and openai_base_url stays bindable. The hand-written
residuals shrink from nine to four. HOMEDRIVE and HOMEPATH join the
runtime subtractions (the same home-directory helper as HOME).
A job queued before the set widened, a stored scheduler template, or a
receiver older than the worker can still carry such a name, and the
container env would write it. The processor now asks the same
RESERVED_ENV_NAMES of the job at the call site, before the secrets
resolver is called (so no wiring of the resolver can skip it), and
refuses it pre-spend as secret-name-reserved, naming the key.
Specs: INT-TRIGGERS-FILE-CONTRACT AMENDED (the steering family, the
pre-spend re-check), INT-CONTAINER-RUNTIME-CONTRACT AMENDED (the stale
PI_CODING_AGENT_DIR claim, the steering-table note), REQ-TRIGGER-SECRETS
AMENDED (load and pre-spend clauses), DES-TRIGGERS-UNIFIED-FILE AMENDED
(#511 is another narrowing, the release notes must name it),
CONST-PI-VERSION-PINNED evidence amended (the second upgrade gate covers
pi's own reads). docs/secrets.md names the new families and the
PI_FORWARD_ENV route.
Checked unchanged: the per-provider pre-spend reservation, the
key-variable bound, the closed container env map, admin/build.mjs
inlining (the bundle carries the new members).
Signed-off-by: Rob Boerman <robboerman@live.nl>
Copy file name to clipboardExpand all lines: specs/constitution.md
+5-1Lines changed: 5 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -947,7 +947,10 @@ passing, on the record — issue #80.)
947
947
it answers which variable a provider's key is read from, and `worker/test/env-allowlist.test.mjs` round
948
948
trips it. The second is `PROVIDER_STEERING_VARS`, which answers which variables STEER a provider -- where
949
949
the request goes, and with which credentials -- and is derived from the pinned artifacts in both
950
-
directions by `worker/test/provider-steering.test.mjs`. Both exist because the same fact holds: a pi bump
950
+
directions by `worker/test/provider-steering.test.mjs`. From issue #511 the second gate also covers
951
+
pi's OWN reads in its `PI_*` namespace (pi-coding-agent and the pi packages it declares), and follows the
952
+
SDKs' declared dependencies one hop further, so a bump that makes pi read a new configuration variable
953
+
fails it too. Both exist because the same fact holds: a pi bump
951
954
that quietly changes what the runtime reads from the environment is otherwise indistinguishable from
952
955
nothing happening, and here the failure mode is a trigger choosing the host this deployment's own
953
956
credential is sent to.
@@ -971,6 +974,7 @@ passing, on the record — issue #80.)
971
974
972
975
| Date | Change |
973
976
|---|---|
977
+
| 2026-09-30 | Issue #511 (the steering scan misses lowercase twins, a second SDK hop and pi's own reads). **No article changed.****`CONST-PI-VERSION-PINNED`**: the evidence's second upgrade gate, `PROVIDER_STEERING_VARS`, now also covers pi's own `PI_*` reads and a second declared-dependency hop; the rule, the pin and the first gate UNCHANGED, checked. |
974
978
| 2026-09-30 | Issue #509, the pi 0.80.7 -> 0.99.1 bump. **No article changed.** **`CONST-PI-VERSION-PINNED`**: the statement's version string reads **0.99.1**, and the evidence records that the `[Unreleased]` model/auth change it cited has shipped; the rule, and its two upgrade gates (`findEnvKeys` and `PROVIDER_STEERING_VARS`), UNCHANGED, checked: the steering gate named every new and stale name at this bump. The evidence convention gains one parenthetical: the pin moved and `ModelRuntime` now exists at it, the lesson did not move. **`CONST-NO-CONTEXT-FILES-MANDATORY` evidence re-verified at 0.99.1** (the trust branch, the `projectTrusted ?? true` default, the loader's two merge branches and `extensionsOverride`, at new line numbers; `SettingsManager.inMemory` now accepts options the runner does not pass), the 0.80.7 block kept as the record. **`CONST-RETRY-INFRA-ONLY` UNCHANGED, checked**: the new `retry-unresumable` exit `2` is a determinate stop after a paid turn and its tool effects, which a queue retry would repeat on a fresh budget, not an infrastructure failure; its #449 evidence block stays the 0.80.7 record. `CONST-MERGE-NEVER-AUTOMATIC`, `CONST-BUDGET-BEFORE-TOKENS` UNCHANGED, checked. |
975
979
| 2026-09-29 | Issue #477, with PR #478's gate rounds 1 and 2. **No article changed.****`CONST-ISOLATION-CONTAINER-PER-JOB` UNCHANGED, checked**: doctor now judges an unquoted `PI_BACKEND_FLOOR` from `.env` rather than calling it unread; the worker's boot refusal on the floor was already reading it. **`CONST-RETRY-INFRA-ONLY` UNCHANGED, checked**: cancel's wording changes no retry, removal or record, and a `VALKEY_URL` that names no database, or one the server does not have, is a configuration refusal (exit 2, not restarted into the same answer), as a refused owner is. **`CONST-TOKEN-SCOPED-PER-JOB` UNCHANGED, checked**: the foreground worker still reads no `.env`, and the `.env` writer hands no value to any new process. |
976
980
| 2026-09-29 | Issue #476 (the netns keeper judged too young on every stack start). **No article changed.****`CONST-RETRY-INFRA-ONLY` UNCHANGED, checked**: a keeper that is only young is held through the delayed set, which spends no attempt, like the pause, wait and podman.service holds; a keeper that does not hold, and the new `netns-keeper-crash-loop`, stay infrastructure throws that the queue retries. **`CONST-BUDGET-BEFORE-TOKENS` UNCHANGED, checked**: the hold sits where the keeper's check was, in the egress preflight before any reservation, and a held or crash-looped job reserves nothing. **`CONST-ISOLATION-CONTAINER-PER-JOB` UNCHANGED, checked**: no job runs any earlier than before; every pickup is judged by every keeper rule again. With PR #479's gate, an operator's cancel reaching a job the processor would hold or retry is a policy return, never a retry or a later run (`CONST-RETRY-INFRA-ONLY`). |
Copy file name to clipboardExpand all lines: specs/design.md
+10Lines changed: 10 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -535,6 +535,15 @@ money with no upstream turn limit (`REQ-RUNNER-TURN-BUDGET`).
535
535
secret of that name could otherwise have been meant to override it. No shipped trigger has a reason
536
536
to bind any of these. The next release's notes must name it; the receiver image's `:latest` refuses
537
537
such a file from the merge on.
538
+
**Issue #511 narrows the same field again**, and the same two obligations apply. The steering set now
539
+
also holds what google-auth-library and the AWS SDK's core read one dependency hop further in (the
540
+
lowercase `google_application_credentials`, `GOOGLE_CLOUD_QUOTA_PROJECT`, `AWS_CONFIG_FILE` among them),
541
+
and pi's own `PI_*` reads such as `PI_CODING_AGENT_DIR`. A file binding one of them used to load and now
542
+
refuses at parse in all three loaders. The less obvious members are the reason to call it out:
543
+
`DEBUG` and the Google and AWS runtime-detection names (`K_CONFIGURATION`, `AWS_EXECUTION_ENV`) are
544
+
ordinary words an operator could have bound for a tool of their own. A job already queued, or a stored
545
+
cron schedule, that binds such a name is refused pre-spend as `secret-name-reserved` rather than run
546
+
with it. The next release's notes must name #511 and say that such a name moves to `PI_FORWARD_ENV`.
538
547
- **Rejected**: a compat union accepting both old shapes (the repo bans backwards-compat shims,
539
548
`.claude/rules/legacy-removal.md`) · two independent validators (they drift) · a third shared package
540
549
(unnecessary — the one-way worker dependency already exists).
@@ -6433,4 +6442,5 @@ a tunnel.
6433
6442
| 2026-09-29 | Issue #492, with PR #493's review folded in. **`DES-COST-FOLD-BY-SCAN` AMENDED**: the aggregation sentence gains its one exception, a bucket every run of which ONE declared plan covers, with no run a floor, keeps `plan:<id>` instead of demoting to `estimated` (the review found the first cut dropped a floor's `≥` and joined two ids with a character an id may hold), so the fold agrees with `REQ-COST-ANALYTICS` (b) and with its own per-model bucket; mixed buckets still demote with coverage, and zero-rated and unknown buckets are unchanged (the issue is about plan coverage; widening the exception to them is a separate decision). The daily chart draws a plan day with the same 1px dashed sliver as before; only its tooltip now reads `plan:<id>`. **`DES-ADMIN-VIA-PI-EXTENSION` UNCHANGED, checked**: the topology's layout rules and the label-fit gate are untouched (the gate exits 0, with a new page of an armed and a spent one-shot and a long plan badge; the documented fixture's page probes clean). docs/costs.md gains the sentence saying what a wholly plan-covered bucket reads and what `dispatch_costs` returns for it. **Code evidence**: admin/src/costs.mjs; admin/test/costs.test.mjs. |
6434
6443
| 2026-09-30 | Issue #509, the pi 0.80.7 -> 0.99.1 bump, one row for the runner, worker and admin halves. **`DES-USAGE-METER-VIA-API-PROVIDER-REGISTRY` AMENDED in place** (ID kept, it is an address): at pi 0.99.1 the api-provider registry is off the session path, so the process-wide meter moved to ModelRuntime.prototype, with the registry kept as a second half for legacy extension calls and an AsyncLocalStorage deduplicating the composed-provider path where the two meet; a Rejected entry records why patching the prototype is not the rejected `dist/` patch; the Must-handle list is marked as the registry half's; compaction and branch summaries now land in `otherTotal`. The observe-don't-consume design, the fallback rule and the brake are UNCHANGED. **`DES-ADMIN-VIA-PI-EXTENSION` AMENDED**, the supported version (the pin, `0.99.1`), the evidence line numbers re-read at 0.99.1, and the column-width bullet: pi-tui 0.99.1 gives 472 spacing marks a column and walks a cluster from after its base, so the #417 doubling is gone (its pinned counts, 13,545 and 3,273 doubled forms and 6,446 breaking predecessors, are now zero, re-pinned rather than dropped) and the matched-leader rule is re-derived for the opposite correction: a spacing mark that LEADS a cluster draws nothing, so `panel.mjs` bundles the leading part of a run with its base as one step as wide as the base alone, and a cluster made only of non-printing code points as one zero step. The segmenter probe, the memo and the context stand-in are kept; the stand-in's safe direction flipped (a wrong join is now an under-count) and the predecessor sweep pins the new count (1,111,984 predecessors lead, fifteen join). New pinned arms: 472 spacing marks against 242 tails (1,888 and 124 narrower forms), and against the five emoji modifiers (exact at a string's start, 155 exact after a letter, the rest the declared skin-tone over-count). Residuals restated: the U+FFA0 under-count is gone, a joined piece now draws one column wider and is clipped by its frame, a Devanagari conjunct no longer over-counts. **`DES-SUBSCRIPTIONS-ARE-COUNTERFACTUAL-ONLY` AMENDED**, the first Why bullet: kimi-coding, zai and zai-coding-cn now carry implied API-equivalent rates, so prepaid runs record a positive cost rather than 0, and a zero rate is no longer a subscription signal; the declaration's role is unchanged. **The `run.excludeTools` decision AMENDED**: the known set and the runner's derivation are the eight built-ins of pi 0.99.1 (`powershell` added). The package-discovery Rejected entry re-checked: `collectPackageResources` still falls through to the convention dirs at 0.99.1. PR #510's review found two cuts the new rule left measuring text where it is not drawn, both fixed at the rule: a cut that strips a dangling joiner re-measures and re-cuts (a spacing mark and a joiner draw nothing together, the mark alone a column; thirty-one marks, swept), and every cutter takes an `afterSpace` context, used by both frame titles and the divider's label and meta (an 8-column box drew its top line at 9; the divider missed its width in 13,380 swept forms at the previous commit). **UNCHANGED, checked**: `OQ-035` (the substitution class is untouched; `interpreted` keeps every mark as composing), `REQ-TOPOLOGY-GRAPH`, `REQ-INSIGHTS-HTML-EXPORT` (the chip estimate is still one-sided; its exact-count pin moved from 185,319 to 185,583 for the 264 BMP spacing marks), `DES-COST-FOLD-BY-SCAN` (classification order unchanged: an owned plan first, so a declared plan reads `plan:<id>` across the rate change), counterfactual-only subscriptions. **Code evidence**: image/runner/src/usage-meter.mjs, model-runtime.mjs; admin/src/panel.mjs -> widthSteps, leaderSteps, planRun, contextOf, NON_PRINTING, SPACING; admin/src/style.mjs -> visibleLen (comments); admin/test/width-leaders.test.mjs, width-predecessors.test.mjs, width.test.mjs, graph-html.test.mjs, control-bytes.test.mjs, helpers/renderer.mjs (pin 0.99.1). |
6435
6444
| 2026-09-30 | Issue #508 (plain HTTP reached every port of a listed host). **`DES-EGRESS-DENY-ON-A-DEDICATED-NETWORK` AMENDED**, the Decision and one new bullet: the proxy passes a `CONNECT` to 443 or a plain request to 80, both by hostname to a listed host, by `http_access deny !Safe_ports !CONNECT` right before `http_access allow allowed`; the owner chose port 80 over HTTPS only because some package mirrors and redirects still use plain HTTP; a rule after the allow and a `dst` rule ahead of `allowed` are rejected; `doctor`'s canary gains a third probe that reads the rule back; the job image's git ignores `HTTP_PROXY` for an `http://` remote, so with the policy on a forge must be `https://` on 443. |
6445
+
| 2026-09-30 | Issue #511 (the steering scan misses lowercase twins, a second SDK hop and pi's own reads). **`DES-TRIGGERS-UNIFIED-FILE` AMENDED**, the narrowing list: #511 reserves more names in `run.secrets` (the second SDK hop, the lowercase twins, pi's own `PI_*` reads), refusing files that used to load, and already queued jobs binding such a name refuse pre-spend; the release notes must name it and point at `PI_FORWARD_ENV`. Every other decision in the entry UNCHANGED, checked. |
6436
6446
| 2026-10-02 | Issue #503, part 3 (the measured host routes). **`DES-PODMAN-NATIVE-ROOTLESS-BACKEND` AMENDED**, one Why bullet: the routes from the egress proxy to a model server on the host, measured 2026-09-30 on rootless Podman 4.9.3 (slirp4netns), rootless 5.8.1 (pasta) and rootful 5.8.1, stating only what the issue's measurement comment and its addendum publish; the host's own LAN address is refuted under pasta only, and the issue's "LAN endpoints only for Podman 4.x" fallback is refuted. **`DES-CONTAINER-BACKEND-REGISTRY` AMENDED**, one bullet: `HOST_ROUTES` and `hostRouteFor` sit in the same leaf as a separate table, a reachability fact that is no declaration word, never read by the floor and refusing nothing. `hostRouteFor` answers `works` or `refuted` only for a host-local address (refuted everywhere), the two alias names and an IPv4 address in the host's own address list; another address or name is `lan`, an ordinary outbound route that is never `works`; anything else, a host not in a declared form, a bad host address entry, an IPv6 or link-local literal, an inexact version or a missing runtime field, is `unmeasured`. `PROXY_LOCAL_ADDRESSES` in the leaf is the one host-local set: `model-endpoints.mjs` builds `LOCAL_ADDRESSES` from it and refuses a declared host in it. Nothing reads the table yet (doctor and the proxy come in later parts). **`INT-CONTAINER-RUNTIME-CONTRACT` UNCHANGED, checked**: no job flag moves. **Code evidence**: worker/src/backends.mjs -> HOST_ROUTES, hostRouteFor, PROXY_LOCAL_ADDRESSES, isProxyLocalHost; worker/src/model-endpoints.mjs -> LOCAL_ADDRESSES; worker/test/backends.test.mjs, backends-doc.test.mjs, podman-doc.test.mjs, model-endpoints.test.mjs; docs/backends.md, docs/podman.md. |
0 commit comments