Skip to content

FLT-104 — Gate 0: Non-Interactive Auth Smoke Test — SPEC

Status: Draft spec (2026-07-17, WT-6022). This is the GO/NO-GO gate for the entire fleet-routine build (FLT-34). No scheduler code (FLT-105+) is written until this passes. Council authority: opus 2026-07-17, report llm-council/reports/2026-07-17_191704_should-the-fleet-harness-flt-1-the-universal.md.

1. Why this gate exists (the risk it retires)

The #1 pre-build risk for routines is silent non-interactive auth failure. Every harness CLI authenticates from an interactive user profile: OAuth/session tokens and DPAPI-protected blobs live under %USERPROFILE% (~/.codex/auth.json, Claude credentials, agy/Gemini session, Grok session, etc.). A scheduled task that runs under SYSTEM or a different account loads a different profile → the token isn't there → the harness fails at 2 AM, silently, forever.

A vault / Credential-Manager sidecar does not fix this: it cannot resurrect a browser-issued OAuth token in SYSTEM context (it only helps for the caller's own API keys via env). The only correct fix is to run the task as Andrew's user, "whether logged on or not," and then to prove, non-interactively, that all six harnesses actually authenticate from that exact context before a single line of scheduler logic is written.

One council seat (streaming gpt-oss:120b) proposed running the smoke test under the SYSTEM account. That is explicitly REJECTED for the reason above.

2. Deliverable

A command:

fleet-routine doctor --register-probe            # one-time, interactive: register the S4U probe task
fleet-routine doctor --noninteractive [--harness <name>...] [--json] [--allow-missing <name>...]
fleet-routine doctor --reset                     # re-register after a password/profile change
  • --register-probe: one-time interactive setup that registers the persistent S4U probe task (see §3). Andrew supplies his password once through the Windows credential prompt; the OS custodies it. This is the ONLY interactive step.
  • --noninteractive: assumes the probe task exists; triggers it and reads its result — no registration, no password. Default: probe all six harnesses.
  • --harness limits to a subset (dev convenience only; the gate requires the full run).
  • --json: machine-readable result (for CI and for the runner's fire-time precheck later).
  • --allow-missing: explicit, logged fail-closed allowlist for a permanently-unavailable seat (see §6). Absent seats are failures, not skips, unless allowlisted here.
  • --reset: re-register the probe task (stale credentials after a Windows password change, or a profile change).

Per C#-first policy this is a C# console tool (fleet-routine CLI, doctor verb). Reuses the fleet-dispatch run-as-user task config verbatim.

3. The execution context under test (the whole point)

The probe must run in the same context a scheduled routine will later run in — otherwise it proves nothing. That context is a Windows Task Scheduler task with:

  • Principal: Andrew's interactive user account (ANDYGREATROOMPC\<user> etc.), NOT SYSTEM, NOT a service account.
  • Logon type: "Run whether user is logged on or not" (S4U / stored-password S4U logon) — this is the exact setting that makes or breaks profile/token availability.
  • RunLevel: Highest (matches fleet-dispatch listeners, live on 4/4 PCs).
  • Working directory, PATH, environment: as WTS provides them under that principal — the probe must NOT paper over PATH/cwd differences by hard-coding absolutes; discovering a PATH gap is a finding.

Probe task lifecycle (council Q2 → Option b, persistent reused task). doctor --register-probe registers ONE persistent task named fleet-routine-doctor, under Andrew's user with S4U logon. Windows Task Scheduler's own credential store (LSA/DPAPI under Andrew's account) custodies the S4U secret — the tool never sees, stores, enters, env-passes, or logs the password. That is the entire point of S4U: the OS is the credential custodian. Thereafter doctor --noninteractive only triggers this task, waits, and reads its result artifact + exit code.

"Throwaway" (earlier draft wording) is reinterpreted as idempotent / no persistent side effects, NOT re-registered per run — per-run re-registration would force the password back into the loop every invocation (interactive prompt breaks the gate; programmatic plaintext is prohibited). The persistent probe still executes in the exact S4U context a real scheduled routine will use, so it proves the right thing; only the one-time registration cost is amortized. This is separate from FLT-106's real routine tasks — doctor's single probe task is the only WTS state it owns.

Implementation note: the probe payload (the per-harness invocations) runs inside the task; doctor --noninteractive the outer command just triggers the task and reads its structured output from a temp result file. The two halves share the result schema in §5.

4. Per-harness probe contract

Each harness gets a no-op / trivial invocation whose only purpose is to prove the round trip: auth succeeds, a model answers, output comes back, exit code is clean. (council Q3 → per-run nonce canary.) The prompt is materialized to a temp file (never argv — see traps):

Respond with exactlyFLEET_OK_and nothing else.

where <nonce> is a fresh per-run random 8-hex value (e.g. FLEET_OK_7f3a91c0), generated once per doctor run and injected into every harness's prompt. Cap output tokens low where the API allows.

A harness PASSES only if all of: 1. process/HTTP call completed within a per-harness timeout, 2. exit status / HTTP status indicates success (subprocess: exit 0; HTTP: 2xx + model present), 3. the normalized captured output contains the exact case-sensitive substring FLEET_OK_<nonce> (strip ANSI/color codes + normalize CRLF before matching). The per-run nonce is the point: a stale result file or a help banner cannot contain a token generated this run, so a match proves a model actually executed this run. No regex; contains is the strictest rule all six reliably satisfy (exact-match fails — reasoning models prepend thinking, chat models add markdown/whitespace, local models truncate). Prompt-echo is a non-issue: the token only exists because the model ran. 4. no interactive prompt was required (no credential/login/consent prompt blocked it), 5. the temp prompt file was read successfully (proves file-based prompt delivery works — the channel all routines will use).

Harness matrix + known traps (all six)

# Harness Invocation shape Auth artifact Known trap (MUST handle)
1 Claude (CLI) claude -p reading prompt file; zero tools subscription creds in user profile tool spawns; keep --tools "" minimal probe
2 Codex (codex exec) codex exec - with prompt on stdin, < NUL after ~/.codex/auth.json (user profile) stdin hang: codex exec waits on stdin forever & silent → ALWAYS redirect < NUL; multiline via Start-Process arg-splits → pipe via stdin
3 Gemini (agy -p) agy -p with prompt file Google/agy session (user profile) hangs if stdin not closed< NUL; agy is --model not -m
4 Grok (grok -p) grok -p with prompt file xAI session (user profile) 32KB argv cap → prompt MUST come via file, never a big arg; grok dies >32KB
5 Odysseus HTTP GET/POST to http://127.0.0.1:7000 health + tiny generate server-side (reachability, not user OAuth) localhost only (council Q1); reachability precheck; HTTP client with model-availability check
6 Ollama HTTP POST http://127.0.0.1:11434/api/generate, stream:false, num_predict tiny local, no auth localhost only (council Q1); model must be pulled; health + model-availability precheck; CPU models slow → generous timeout

Gate 0 tests the LOCAL execution context only. Remote hosts (gaming 10.0.0.7, streaming 10.0.0.26) are NOT probed here — a powered-off/firewalled remote box is a reachability fact, not an auth fact, and folding it in would turn a binary go/no-go into a flaky network inventory. Cross-host reachability + per-host retry/skip is FLT-107. A routine that targets a remote Ollama is a routine-config assertion FLT-107 validates, not a Gate 0 condition.

Harnesses 1–4 are managed-subprocess class (auth = user-profile token → the SYSTEM/S4U risk applies directly). Harnesses 5–6 are HTTP-client class (auth = reachability + model presence, not user OAuth) — they still run inside the task context so PATH/network differences surface.

Encoding: pin UTF-8 on both ends of every child process (write prompt file UTF-8 no-BOM; read stdout as UTF-8) — mismatch corrupts canary text and produces false negatives, not errors.

5. Result schema (--json)

{
  "schemaVersion": 1,
  "ranAtUtc": "2026-07-18T...Z",
  "principal": "ANDYGREATROOMPC\\<user>",      // proves NOT SYSTEM
  "logonType": "S4U",                           // "run whether logged on or not"
  "pc": "ANDYGREATROOMPC",
  "overall": "PASS | FAIL",                     // PASS iff every non-allowlisted harness passed
  "harnesses": [
    {
      "name": "codex",
      "class": "subprocess | http",
      "result": "PASS | FAIL | ALLOWLISTED_MISSING",
      "elapsedMs": 1234,
      "failureClass": null,                     // see enum below when FAIL
      "detail": "canary matched"                // or the captured error, trimmed
    }
    // ... 6 entries
  ]
}

failureClass enum (drives the go/no-go decision + later fire-time behavior): AUTH_PROMPT_REQUIRED · AUTH_TOKEN_MISSING · DPAPI_DECRYPT_FAILED · PATH_OR_EXE_NOT_FOUND · STDIN_HANG_TIMEOUT · NETWORK_UNREACHABLE · MODEL_UNAVAILABLE · CANARY_MISMATCH · NONZERO_EXIT · TIMEOUT.

Exit code: 0 iff overall == PASS; non-zero otherwise. --json still prints the full body.

6. Pass / fail criteria (the gate)

  • GO (unblocks FLT-105+): overall == PASS — every one of the six harnesses PASS, or any FAIL seat is explicitly listed in --allow-missing AND the fleet accepts that seat as permanently fail-closed (a routine targeting it will be refused at fire time, not silently skipped).
  • NO-GO: any non-allowlisted FAIL. Pause the project and fix the underlying auth (re-issue the token interactively, correct the principal/logon type, fix PATH) — do not work around it in scheduler code.
  • Recurring paid/API routines (Claude/Codex/Gemini/Grok) require a full 6/6 pass — no allowlist shortcut — before they may be enabled.

7. Acceptance for FLT-104

  1. fleet-routine doctor --noninteractive --json runs from a real S4U WTS task as Andrew's user (evidence: principal non-SYSTEM + logonType: S4U in the output) and returns a per-harness verdict for all six.
  2. Each of the four subprocess harnesses is invoked via temp-file prompt and (Codex/Gemini) with stdin closed — verified by a deliberate negative test: a harness with a revoked/absent token yields AUTH_*, not a hang or a false PASS.
  3. The throwaway WTS task is reaped (no leftover scheduler state).
  4. Reuses fleet-dispatch run-as config (documented link in the tool), not hand-rolled principal setup.
  5. Runs green on at least the main PC; multi-PC fan-out is FLT-107, not this gate.

8. Explicit non-goals (belong to later phases)

  • No registry, no cron, no run verb, no persistent WTS tasks → FLT-105 / FLT-106.
  • No governor integration, no NATS emit → FLT-105.
  • No multi-PC / JetStream → FLT-107.
  • doctor only proves auth; it never runs a real routine prompt or selects an adapter.

9. Resolved design decisions (council opus 2026-07-17; ranking A>D>B>C)

Report: llm-council/reports/2026-07-17_212623_resolve-3-concrete-design-questions-for.md.

  1. Odysseus/Ollama host targets → LOCALHOST ONLY. 127.0.0.1:7000 / 127.0.0.1:11434. Remote hosts deferred to FLT-107. Gate 0 proves the local S4U execution context; remote-box uptime is a reachability fact that must not enter the auth go/no-go. (§4.)
  2. S4U password → persistent reused probe task, OS-custodied credential. One-time doctor --register-probe (interactive password once, held by Windows LSA/DPAPI); --noninteractive only triggers + reads. "Throwaway" = idempotent/no-side-effects, not re-registered per run. Fail-closed if the task is absent/stale/wrong-user/not-S4U/last-run-logon-failed, printing the exact remediation command; --reset re-registers after a password/profile change. The tool never sees, stores, env-passes, or logs the password. (§3.)
  3. Canary → contains, case-sensitive, per-run nonce. FLEET_OK_<nonce> (fresh 8-hex per run), strip ANSI + normalize CRLF, match as case-sensitive substring, AND require process/transport success. The nonce defeats stale result files and help banners. (§4.)