Close registry gate 7 with owner-approved refresh mechanism

This commit is contained in:
2026-09-10 15:08:40 -05:00
parent 224147eccc
commit 86e009dde5
6 changed files with 317 additions and 9 deletions
@@ -355,8 +355,9 @@ token. Generated files are disposable derivatives of the registry.
host service refreshes central OAuth records and rematerializes affected seats;
launch-time ensure is the final fail-closed gate. OAuth refresh mechanics must
reuse pi's implementation where possible rather than reimplement provider
protocols; the exact noninteractive refresh trigger is an implementation
investigation and acceptance gate.
protocols; the exact noninteractive refresh trigger is resolved by
investigation PI-REFRESH-ROCKO-1 (docs/plans/reviews/2026-09-10_pi0851-refresh-investigation.md):
`pi auth check --provider <p>`, owner-approved 2026-09-10.
## CLI contract (target `mosaic` surface)
@@ -454,6 +455,11 @@ No named accounts currently exist, so migration is state-free:
- Changing runtime choice regenerates atomically; failed generation preserves
previous files and records refusal.
- OAuth central refresh rematerializes affected seats; agents never authenticate.
Refresh driver (owner, 2026-09-10): the periodic host service invokes
`pi auth check --provider <p>` per provider — noninteractive, refresh-if-
expiring (5-minute default window), no credential output. `print-api-key`/
`print-bearer-token` must not be used by the service because they print
credential material to stdout.
- `harness: pi` resolves; unregistered/unsupported harness refuses.
- Harness detection records recognized compatible executables without reading
harness homes; incompatible/ambiguous detections refuse availability.
@@ -488,11 +494,15 @@ No named accounts currently exist, so migration is state-free:
6. **RESOLVED (owner, 2026-09-10):** forked sessions pin the original account
in immutable session metadata; unpinned relaunches materialize the current
profile default; selection applies between executions, never mid-session.
7. **PARTIALLY RESOLVED (owner, 2026-09-10):** the pin moves to pi 0.85.1
(exact). A bounded read-only investigation of the 0.85.1 package will
determine the supported noninteractive OAuth refresh path; owner approves
the mechanism from those findings before implementation. Refresh stays
host-side, fail-closed, no token exposure or duplication.
7. **RESOLVED (owner, 2026-09-10):** the pin moves to pi 0.85.1 (exact).
PI-REFRESH-ROCKO-1 (read-only, package identity verified) established that
0.85.1 supports noninteractive host-side OAuth refresh via one HTTPS POST
using the stored refresh token, no browser, rewriting auth.json in place
under an exclusive lock (pre-existing since 0.83.0/0.84.1). Owner selected
mechanism (a): the periodic host service runs `pi auth check --provider <p>`
— refresh-if-expiring with no token output. `print-*` commands are excluded
because they emit credential material on stdout. Launch-time ensure remains
the final fail-closed gate. No token exposure or duplication.
8. **RESOLVED (owner, 2026-09-10):** local Ollama routes via `host-gateway`
(`http://host.docker.internal:11434/v1`). Remote Ollama supports http and
https; plain http requires an explicit per-provider `allowInsecureTransport:
@@ -509,5 +519,6 @@ No named accounts currently exist, so migration is state-free:
cost. Revisit a separate protected registry root only if re-login pain
proves real (would be a canon change).
Remaining open item before implementation: gate 7's 0.85.1 refresh investigation
findings and the owner's approval of the refresh mechanism it proposes.
Remaining open item before implementation: none. All review gates are
owner-resolved. Implementation itself still requires a separately chartered
and reviewed increment (author, reviewer, allowed paths, suites, rollback).
@@ -0,0 +1,9 @@
# GATE7-FINAL-FILBERT-1
Final focused review closing registry gate 7. Owner ruling 2026-09-10 (Q15: a): the periodic host refresh service invokes `pi auth check --provider <p>`; `print-api-key`/`print-bearer-token` are excluded because they print credential material.
Darkwing edits since commit 224147ec: registry draft gate 7 marked RESOLVED with the mechanism and investigation pointer; refresh-driver sentence added to the resolution/mechanics sections; suites line notes the driver and the print-command exclusion; remaining-open-item text replaced with "none — all gates resolved, implementation needs a separately chartered increment." Companion files: Rocko's read-only investigation (cfab6c00) and its request; SESSIONS registration line.
Verify frozen four-file snapshot /tmp/gate7-final-znmnih6v at SHA256SUMS ea9c539d4ca030278440332fe3f1291e2494e50b475f4a2c46b35b39c290b628, pin 224147ec. Check: (1) draft deltas confined to the gate-7/refresh-driver statements; (2) the investigation's technical claims about 0.85.1 (`pi auth check` default refresh, no TTY/browser, in-place auth.json rewrite under lock, 5-min validity window, print-* emitting secrets, package identity) are internally consistent and cited; flag any claim the file does not actually support; (3) mechanism (a) matches the investigation's findings; (4) no gate is silently reopened or closed beyond owner rulings; no implementation authorization is inferred; (5) no credential material or runtime identifiers appear in any candidate.
Read-only. No live pi commands, no package fetches, no private reads, no shared-log edits. Write ONLY docs/plans/reviews/2026-09-10_gate7-final-verdict.md and reply to Darkwing/default. APPROVED permits committing exactly these reviewed bytes; REQUEST CHANGES names the fix.
@@ -0,0 +1,23 @@
# GATE7-FINAL-FILBERT-1 — verdict
Reviewer: Filbert (independent). Author of the draft edits: Darkwing; investigation author: Rocko (PI-REFRESH-ROCKO-1). Frozen snapshot `/tmp/gate7-final-znmnih6v` at manifest SHA-256 `ea9c539d4ca030278440332fe3f1291e2494e50b475f4a2c46b35b39c290b628`, pin `224147ec…`; all four hashes verified before and after review; snapshot unchanged. Read-only: no live pi commands, no package fetches, no private reads.
## Verdict: APPROVED
Committing exactly these four reviewed files is approved. This closes gate 7 as documentation; it authorizes no implementation, package change, or credential operation.
## Findings by assigned check
1. **Draft deltas confined.** Against pin `224147ec…`, the registry draft changes only: the mechanics-section sentence resolving the refresh trigger via PI-REFRESH-ROCKO-1 with owner approval; the refresh-driver paragraph inside the verification/suites bullet list; gate 7 moving PARTIALLY RESOLVED → RESOLVED (owner, 2026-09-10, mechanism (a)); and the remaining-open-item text. Nothing else moved.
2. **Investigation consistent, cited, and corroborated.** The 0.85.1 claims are internally consistent (shasum `4cd00f65…` appears identically in three places; the `refresh: false` function default vs `refresh: !noRefresh` CLI wiring contradiction is explicitly reconciled; the two independent 5-minute layers — stored-value safety margin and read-time validity window — are explained coherently; the browser/PKCE login flow is correctly separated from the POST-only refresh). Citations name concrete files and unique anchors. Because the investigation itself establishes the capability as pre-existing since 0.83.0/0.84.1, I corroborated read-only against this repository's pinned 0.84.4 install: the same command set (`check`/`print-api-key`/`print-bearer-token`/`--no-refresh`), the verbatim help sentence, the `0o600`/`proper-lockfile`/read-only-refusal anchors in `auth-storage.js`, the refresh timeout constant, and the Anthropic token URL all exist locally as described. One claim the investigation supports only by inference — that `pi auth check` produces no credential output — is corroborated by the pinned package's own help text: "`--credentials` emits the credential," i.e. only on explicit opt-in. Limit recorded honestly: I could not re-derive the 0.85.1-specific evidence (no fetches permitted); the tarball identity rests on the investigation's recorded npm outputs, and my corroboration covers the shared pre-existing behavior.
3. **Mechanism (a) matches the findings.** The owner-selected `pi auth check --provider <p>` is exactly the entry point the investigation verifies as noninteractive, refresh-if-expiring (5-minute default window), no browser/TTY, with the in-place locked `auth.json` rewrite; `print-*` exclusion matches their documented credential-export semantics.
4. **No gate silently moved.** Gates 16 and 810 read RESOLVED at the pin and are unchanged; only gate 7 changed, per the owner's Q15(a) ruling. The new remaining-open text ("none — all gates resolved; implementation needs a separately chartered increment") states the boundary correctly and infers no implementation authority.
5. **No credential material or new identifiers.** The candidates contain no secrets; the investigation records only a public token URL, package digests, and code anchors. The UUID hits in SESSIONS.md are baseline published history; the added registration line carries none.
## Condition
Commit exactly the four reviewed units at manifest `ea9c539d…`. Any byte change, including any later 0.85.1 pin bump or refresh-service work, requires its own chartered and reviewed increment.
@@ -0,0 +1,13 @@
# PI-REFRESH-ROCKO-1 — pi 0.85.1 OAuth refresh investigation (read-only)
Rocko: bounded read-only investigation under your existing seat. No repo writes, no credential reads, no live OAuth attempt, no config changes, no model use beyond your own session.
Target: @earendil-works/pi-coding-agent exactly 0.85.1.
1. Fetch the published package into a temp directory (npm pack or equivalent); verify the tarball identity/integrity the registry provides. Record the exact version and any checksum evidence.
2. Extract and read docs and source for the authentication/credential model: where auth.json lives, its schema for OAuth vs API key, and any noninteractive/headless refresh path (CLI flags, documented env, internal functions exported for it).
3. Answer precisely: (a) does 0.85.1 document or expose a noninteractive token refresh usable host-side without a browser flow; (b) what exact entry point (flag/command/module) drives it; (c) does it rewrite auth.json in place or emit new material; (d) any expiry/staleness metadata it tracks.
4. Note any 0.84.4 → 0.85.x auth-relevant changes you can see in changelogs or release notes.
5. Write findings ONLY to docs/plans/reviews/2026-09-10_pi0851-refresh-investigation.md with exact file/line citations from the extracted package and the package identity evidence. Do not modify the repo beyond that single new file. Do not install anything system-wide; temp directory only.
Report back via agent-send.sh to Darkwing on the default socket when done. This is fact-finding for registry gate 7; it authorizes no implementation and no credential use.
@@ -0,0 +1,250 @@
# PI-REFRESH-ROCKO-1 — pi 0.85.1 OAuth refresh investigation (read-only)
Request: `docs/plans/reviews/2026-09-10_pi0851-refresh-investigation-request.md`.
Scope: bounded read-only investigation of `@earendil-works/[email protected]`
for registry gate 7 (does 0.85.1 support noninteractive host-side OAuth token
refresh?). All work was done against a package fetched into a private temp
directory (`npm pack`/extraction, not this repository); no credentials were
read, no live OAuth flow or model call was made, and no files beyond this one
were written to the repository. Currently pinned version in this repo is
`0.84.4` (`package.json:8`); 0.85.1 is the candidate under evaluation.
## 1. Package identity and integrity evidence
- Registry metadata (`npm view @earendil-works/[email protected] dist --json`):
- `shasum`: `4cd00f653c3dabeb193b46f511044e7fbfe0f947`
- `integrity`: `sha512-FGRN+OHbWaefBPGaTggAdLjrIHW+s2PzLyglz/5dfLzb9of7uuXMXYC0fJIeZTw+shS32o2cuQ9jF7YSDuL/oQ==`
- `fileCount`: 1056, `unpackedSize`: 21935887
- `tarball`: `https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.85.1.tgz`
- Registry-attached npm provenance attestation present (`predicateType:
https://slsa.dev/provenance/v1`), plus two detached signatures under
`dist.signatures` (keyid `SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U`).
- `dist-tags.latest`: `0.85.1`.
- Local fetch (`npm pack @earendil-works/[email protected] --json`) reported
`shasum: 4cd00f653c3dabeb193b46f511044e7fbfe0f947`, `integrity:
sha512-FGRN+OHbWaefBPGaTggAdLjrIHW+s2PzLyglz/5dfLzb9of7uuXMXYC0fJIeZTw+shS32o2cuQ9jF7YSDuL/oQ==` —
matches the registry's `dist` record exactly.
- Local re-hash of the downloaded tarball: `sha1sum` = `4cd00f653c3dabeb193b46f511044e7fbfe0f947`
(matches npm's `shasum` field bit-for-bit), `sha256sum` =
`1f498729649bdce647d1160993b4d92bf3c614cc819213bee2f91dd34f2a7af4` (recorded
as an independent local fingerprint; npm does not publish a sha256 for the
tarball itself, only the sha512 `integrity` digest above, which the local
fetch reproduced).
- Extracted `package.json`: `name: "@earendil-works/pi-coding-agent"`,
`version: "0.85.1"`, `bin.pi: "dist/bundle/cli.js"`. Package identity confirmed.
- Conclusion: the tarball inspected is exactly the registry-published
0.85.1 artifact; no substitution or corruption evidence.
## 2. Auth model: where credentials live, schema, and refresh entry points
### 2.1 `auth.json` location and schema
- Default path: `~/.pi/agent/auth.json` (per `docs/providers.md:26,111`, and
the code default `join(getAgentDir(), "auth.json")` in
`dist/core/auth-storage.js:19` (`FileAuthStorageBackend` constructor) and
`dist/core/auth-storage.js:281` (`AuthStorage.create`)).
- File permissions: written with mode `0o600` (`dist/core/auth-storage.js:15`,
`AUTH_FILE_WRITE_OPTIONS = { encoding: "utf-8", mode: 0o600 }`), parent
directory created with `0o700` (`dist/core/auth-storage.js:23-26`).
- Credential schema, validated on every load (`dist/core/auth-storage.js:184-200`,
inside `FileAuthStorageBackend`'s parse/validate path — the same shape is
re-validated by the writable `AuthStorage` class):
- API key: `{ type: "api_key", key?: string, env?: Record<string,string> }`.
- OAuth: `{ type: "oauth", access: string, refresh: string, expires: number }`
(`dist/core/auth-storage.js:195-199`), where `expires` is a Unix
millisecond timestamp. Any entry not matching one of these two shapes
throws `Invalid auth.json credential for provider "<id>"`.
- Two storage implementations exist:
- `FileAuthStorageBackend`/`ReadOnlyAuthStorage` (`dist/core/auth-storage.js:17`,
`:157`) — read-focused; `ReadOnlyAuthStorage.modify()`
(`dist/core/auth-storage.js:227`) unconditionally throws `"Read-only
credential storage cannot modify auth.json"`.
- `AuthStorage` (`dist/core/auth-storage.js:263`) — the writable, file-lock-backed
store actually used at runtime. Its `modify(provider, fn, options)`
(`dist/core/auth-storage.js:378-390`) takes an exclusive file lock
(`proper-lockfile`, `dist/core/auth-storage.js:33-114`), re-reads current
disk content, calls `fn(currentCredential)`, and if `fn` returns a new
credential object, merges it into the parsed JSON and rewrites the whole
file in place (`JSON.stringify(merged, null, 2)`, same `0o600` write
options). This is the in-place rewrite path referenced in §2.3.
### 2.2 Noninteractive refresh entry points (CLI)
Three CLI subcommands are defined in `dist/cli/auth-command.js` and answer the
brief's question (a)/(b) directly — all are plain argv-parsed commands with no
TTY prompt in their code path:
- `pi auth check --provider <provider> [--model <model>] [--json] [--credentials]
[--no-refresh]` (`dist/cli/auth-command.js:5,23`). Per its own help text
(`dist/cli/auth-command.js:25`): *"Checks refresh expired OAuth credentials
by default; --no-refresh prevents this."* Implementation:
`checkProviderAuth(args, modelRuntime, options = { refresh: false })`
(`dist/cli/auth-check.js:5`) — note the function default is `refresh:
false`, but the CLI wiring passes `refresh: !noRefresh` (i.e. refresh is
the *default* CLI behavior; `--no-refresh` is what sets it to `false`,
matching the printed help text at `auth-command.js:25`). When
`options.refresh` is true, line `dist/cli/auth-check.js:27` calls
`modelRuntime.getAuth(provider)`, which is the code path that performs the
actual token refresh (see §2.3).
- `pi auth print-api-key --provider <provider> [--model <model>]`
(`dist/cli/auth-command.js:6`).
- `pi auth print-bearer-token --provider <provider> [--model <model>]
[--min-expiry <duration>]` (`dist/cli/auth-command.js:7`), default minimum
validity `DEFAULT_BEARER_TOKEN_MIN_EXPIRY_MS = 30 * 60_000` (30 minutes;
bundled at `dist/bundle/chunks/chunk-JVUZSMYM.js`, `resolveCredentialForPrint`).
`--min-expiry` accepts durations like `30m`/`1h` (`dist/cli/auth-command.js:47-53`).
These two `print-*` commands are documented in `CHANGELOG.md:487,497` (added
in 0.83.0, PR #7168): *"Credential export for external clients — `pi auth
print-api-key` and `pi auth print-bearer-token` export configured credentials
with **automatic OAuth refresh** and minimum-validity enforcement."*
`pi auth check` was added earlier still, in 0.84.1 (`CHANGELOG.md:261,268`,
issue #7152). Both predate 0.84.4, the version currently pinned in this repo,
so this capability is not new to 0.85.1 — see §4.
### 2.3 What actually performs the refresh, and how it writes back
The refresh call is lazy/on-demand, triggered by any credential resolution
(`getAuth`) once a stored OAuth credential is within its validity window of
expiring — this is the mechanism both `pi auth check --provider <p>` (no
`--no-refresh`) and `pi auth print-api-key`/`print-bearer-token` invoke, and
it is also what an ordinary `pi` model call does transparently before making
a request. Two equivalent implementations of this "refresh-if-expiring" guard
are bundled (`dist/bundle/chunks/chunk-IDDQWTHI.js`, function
`resolveStoredOAuth`, and a second call site in the same chunk used by
`ModelRuntime.getAuth`, `dist/core/model-runtime.js:339`):
```
DEFAULT_OAUTH_MINIMUM_VALIDITY_MS = 300 * 1000 // 5 minutes
DEFAULT_OAUTH_REFRESH_TIMEOUT_MS = 15 * 1000 // 15 seconds
expiresSoon(credential) = Date.now() + minimumValidityMs >= credential.expires
if (expiresSoon(stored)) {
post = await credentials.modify(providerId, async (current) => {
if (current?.type === "oauth" && expiresSoon(current)) {
const refreshSignal = AbortSignal.any([signal, AbortSignal.timeout(15000)]);
return await oauth.refresh(current, refreshSignal); // network call, no browser
}
});
}
```
(`dist/bundle/chunks/chunk-IDDQWTHI.js`, matched around the `resolveStoredOAuth`
function body; the file is a minified single-line bundle so no meaningful
line number applies beyond line 1 — the function and constant names above are
verbatim from the source and are unique, greppable anchors.)
`credentials.modify(...)` here resolves to `AuthStorage.modify`
(`dist/core/auth-storage.js:378-390`): it re-reads `auth.json` under an
exclusive lock, calls the refresh callback, and — if it returns a value —
**rewrites `auth.json` in place**, replacing only that provider's entry with
the new `{ type: "oauth", access, refresh, expires }` object, preserving all
other providers' entries. It does not append a separate file or emit
material anywhere else; the only output artifact is the updated `auth.json`.
`oauth.refresh` is a per-provider object with a `refresh(credential, signal)`
method. For Anthropic specifically (`dist/bundle/chunks/anthropic.js`,
function `refreshAnthropicToken`, exposed as
`anthropicOAuth.refresh = (credential, signal) => refreshAnthropicToken(credential.refresh, signal)`):
```
TOKEN_URL = "https://platform.claude.com/v1/oauth/token"
async function refreshAnthropicToken(refreshToken, signal) {
const body = await postJson(TOKEN_URL, {
grant_type: "refresh_token",
client_id: CLIENT_ID, // fixed public OAuth client id, base64-decoded in source
refresh_token: refreshToken,
}, signal);
const data = JSON.parse(body);
return {
type: "oauth",
refresh: data.refresh_token,
access: data.access_token,
expires: Date.now() + data.expires_in * 1000 - 300 * 1000, // 5-minute safety margin baked in
};
}
```
This is a single unauthenticated-by-browser HTTPS POST using only the
already-stored `refresh` token — no interactive browser/PKCE flow, no
loopback callback server, no manual code paste. The browser/PKCE dance
(`loginAnthropic`, same file) is a *separate* function used only for the
initial `/login`; refresh does not invoke it.
## 3. Direct answers to the brief's questions
- **(a) Does 0.85.1 document or expose a noninteractive token refresh usable
host-side without a browser flow?** Yes. `pi auth check --provider
<provider>` (default behavior, no `--no-refresh`) and `pi auth
print-api-key`/`pi auth print-bearer-token --provider <provider>` all
trigger the same lazy refresh path described in §2.3 when the stored OAuth
credential is within 5 minutes (or the caller's `--min-expiry`, for
`print-bearer-token`) of expiry. The refresh itself is one HTTPS POST to
the provider's token endpoint with the stored refresh token; no browser or
TTY interaction occurs in this path.
- **(b) What exact entry point drives it?** CLI: `pi auth check --provider
<provider>` (refresh is the default; `--no-refresh` opts out) or `pi auth
print-api-key --provider <provider>` / `pi auth print-bearer-token
--provider <provider> [--min-expiry <duration>]`
(`dist/cli/auth-command.js`, `dist/cli/auth-check.js`). Underlying module
entry point: `ModelRuntime.getAuth()` (`dist/core/model-runtime.js:339`) →
bundled `resolveStoredOAuth`/credential resolution in
`dist/bundle/chunks/chunk-IDDQWTHI.js` → provider-specific `oauth.refresh()`
(e.g. `anthropicOAuth.refresh` in `dist/bundle/chunks/anthropic.js`). There
is no separate `pi auth refresh` subcommand; refresh is a side effect of
the read/check/print commands above, or of ordinary model use.
- **(c) Does it rewrite `auth.json` in place or emit new material?** Rewrites
in place. `AuthStorage.modify` (`dist/core/auth-storage.js:378-390`) merges
the refreshed `{access, refresh, expires}` object into the existing parsed
`auth.json` and writes the whole file back to the same path with `0o600`
permissions under an exclusive lock. No new file or side channel is used.
- **(d) Any expiry/staleness metadata it tracks?** Yes: the stored credential
itself carries `expires` (Unix ms). Two related constants govern refresh
timing: `DEFAULT_OAUTH_MINIMUM_VALIDITY_MS = 300_000` (refresh triggers
once a credential is within 5 minutes of its stored expiry) and
`DEFAULT_OAUTH_REFRESH_TIMEOUT_MS = 15_000` (network timeout for the
refresh call itself). Additionally, both the initial token mint
(`exchangeAuthorizationCode`) and every refresh (`refreshAnthropicToken`)
compute `expires` as `Date.now() + expires_in*1000 - 300_000`, i.e. the
provider's own `expires_in` is shortened by a 5-minute safety margin before
being stored, so the two 5-minute buffers are independent layers (one baked
into the stored value, one applied again at read time).
## 4. 0.84.4 → 0.85.x auth-relevant changes
Reviewed `CHANGELOG.md` sections for `[0.85.1]`, `[0.85.0]`, and `[0.84.4]`
(lines 3-116 of the extracted `CHANGELOG.md`) for anything touching
`auth`/`oauth`/`refresh`/`token`/`credential`: none of the entries in that
range mention authentication, OAuth, or credential handling — the changes in
that window are model-catalog/provider-setting and reasoning-token-cap fixes
unrelated to auth. The `pi auth check` (0.84.1, `CHANGELOG.md:268`, #7152)
and `pi auth print-api-key`/`print-bearer-token` (0.83.0, `CHANGELOG.md:497`,
#7168) commands both predate 0.84.4 and are unchanged in 0.85.1 as far as the
changelog records. The most recent auth-adjacent history visible anywhere in
the full changelog is older: `auth.json` file-permission/ACL hardening
(`CHANGELOG.md:176`), a requirement that extension OAuth `refreshToken`
callbacks honor an abort signal (`CHANGELOG.md:304`), a fix for stalled OAuth
refreshes leaking the credential-store lock (`CHANGELOG.md:409`, #7508), and a
fix for concurrent-process `auth.json` staleness (`CHANGELOG.md:431`, #7319) —
all several versions prior to 0.84.4 and included for context only, not as
0.84.4→0.85.1 deltas.
**Conclusion for gate 7:** 0.85.1 supports a documented, noninteractive,
host-side OAuth refresh (`pi auth check` / `pi auth print-api-key` / `pi auth
print-bearer-token`, all requiring only network access and the existing
`refresh` token in `auth.json`, no browser). This is pre-existing behavior
carried unchanged from 0.83.0/0.84.1 through 0.85.1, not a new capability of
0.85.1 itself, and is a code-level finding suitable for reconciliation with
prior "no headless refresh" assumptions if any are recorded elsewhere in gate
7 material — that reconciliation is Darkwing's/the coordinator's call.
## 5. What this investigation did not do
No credential file was read (real or synthetic). No live OAuth request or
model call was made — all `refresh`/`access`/`expires` behavior above is
static-analysis of bundled source, not observed execution. No package was
installed system-wide; extraction was confined to a private temp directory
under this session's scratchpad and is not part of the repository. No files
other than this one were added to the repository.