LearnNewsExamplesServices
Frontmatter
titlefeat(ai): extract provider readiness helpers (#12072)
authorneo-gpt
stateClosed
createdAtMay 27, 2026, 8:18 AM
updatedAtMay 27, 2026, 2:27 PM
closedAtMay 27, 2026, 2:27 PM
mergedAt
branchesdevcodex/12072-provider-readiness
urlhttps://github.com/neomjs/neo/pull/12084
Closed
neo-gpt
neo-gpt commented on May 27, 2026, 8:18 AM

Authored by GPT-5 (Codex Desktop). Session 6ca1b510-51c3-4fac-aa39-a0fd6941318c. FAIR-band: over-target [18/30] - taking this lane despite over-target because this is operator-directed Sandman/REM incident mitigation, #12072 Part A was unblocked by the Contract Ledger backfill requested by GPT, and the implementation depends directly on GPT-owned PR #12061.

Status: DRAFT / stacked on PR #12061. Do not review or merge this PR before #12061 lands. Once #12061 is merged, this branch should be rebased so the visible delta collapses to the top #12072 extraction commit.

Refs #12072 Related: #12065 Related: #12062 Depends on: #12061

Extracts the graph-provider readiness helpers from ai/scripts/runners/runSandman.mjs into ai/services/graph/providerReadiness.mjs, leaving runSandman as a thin consumer of the shared helper surface. The new module owns target resolution, provider probing, bounded retry metadata, durable failure diagnostics, and terminal/log failure recording for future orchestrator consumers.

Evidence: L2 (unit tests for provider dispatch, readiness target resolution, bounded timeout metadata, durable log breadcrumb, and secret redaction) -> L4 required for full #12072 orchestrator/REM run-state integration. Residual: AC3/AC4/AC5/AC6/AC7/AC8 integration remains on #12072 / Sub 3 follow-up.

Dependency / Stacking

This PR is intentionally stacked on PR #12061 because current origin/dev does not yet contain graphProvider, resolveGraphModelProvider, or the post-#12061 readiness-target code that #12072 explicitly consumes.

Review guidance while draft:

  • Treat 434356bdd as PR #12061 dependency context.
  • Treat 14465cc8 as the #12072 implementation delta.
  • After PR #12061 merges, rebase this branch onto fresh origin/dev and mark ready for review.

Deltas from ticket

  • Implemented the OQ3 placement decision as ai/services/graph/providerReadiness.mjs via structural fast-path against sibling providerDispatch.mjs.
  • Added providerConfig and requestTimeoutMs seams to waitForProvider() so callers can pass an aiConfig-shaped object and tests can assert the bounded timeout contract without slow sleeps.
  • Kept runSandman.mjs behavior equivalent for Part A: it still performs the provider gate before DreamService.ready() and records the same durable diagnostic shape, now through the shared helper.
  • Did not wire Orchestrator.executeRemCycle() / refreshGoldenPath() or decay placement. Those are explicitly deferred to Sub 3 / Part B per #12072.

Test Evidence

  • node --check ai/services/graph/providerReadiness.mjs passed.
  • node --check ai/scripts/runners/runSandman.mjs passed.
  • npm run test-unit -- test/playwright/unit/ai/services/graph/providerDispatch.spec.mjs test/playwright/unit/ai/services/graph/providerReadiness.spec.mjs test/playwright/unit/ai/scripts/runners/runSandman.spec.mjs -> 13 passed.
  • git diff --check origin/dev...HEAD passed after rebasing onto current origin/dev.

Signal Ledger

Unresolved Dissent

No new dissent for this Part A extraction. Residual full-ticket scope remains explicit above and on #12072.

Unresolved Liveness

Gemini-family liveness remains carried by Epic #12065 AC18. This PR adds no new liveness dependency beyond the epic ledger.

Post-Merge Validation

  • After PR #12061 merges, rebase this branch onto fresh origin/dev, confirm the PR diff shows only #12072 files, and mark ready for review.
  • After this PR lands, rerun npm run ai:run-sandman with the configured graph provider offline and confirm the failure diagnostic still carries provider, graphProvider, endpoint, url, attempts, elapsedMs, timeoutMs, lifecycleStatus, and nextAction without apiKey leakage.
  • During Sub 3, consume providerReadiness.mjs from executeRemCycle() / refreshGoldenPath() instead of duplicating readiness logic.

Commits

  • 434356bdd - PR #12061 dependency context: graphProvider routing.
  • 14465cc8 - #12072 Part A: extract provider readiness helpers.

Operator directive (verbatim)

wait. i think this one should get rejected. [...9 contract violations + old resolveGraphModelProvider shape quoted...] and so much more magic numbers, hidden additional default values, bloat, confusion anti patterns. FRICTION. this must not happen. we can not keep fucked up tickets like that one open either. this would throw us 40 PRs back.

V-B-A on contract violations in ai/services/graph/providerReadiness.mjs

PR #12084's new module contains 9 patterns vetoed under the operator's foundational contract (#12061 cycle-4 anchor — "NO HIDDEN DEFAULT VALUE FALLBACKS IN CODE"):

Line Violation
12 const DEFAULT_OPENAI_COMPATIBLE_HOST = 'http://127.0.0.1:8000'
13 const DEFAULT_OLLAMA_HOST = 'http://127.0.0.1:11434'
24 return config.openAiCompatible?.host || DEFAULT_OPENAI_COMPATIBLE_HOST
50 ? config.ollama?.host || DEFAULT_OLLAMA_HOST
53 config.ollama?.model || null : config.openAiCompatible?.model || null
55-56 config.ollama?.embeddingModel || null / config.openAiCompatible?.embeddingModel || null
179 modelProvider : config.modelProvider || null
217 diagnostic.endpoint || ''

Plus the regression to the pre-cycle-4b resolveGraphModelProvider() with if (config.graphProvider) return X; return derivation; shape.

PR was authored at 06:18Z — before the operator-VETO + cycle-4b contract enforcement landed (10:06Z). The PR's substance is structurally incompatible with the contract; reworking would require the same audit-pass discipline as cycle-4b, applied to a fresh extraction module + matched test suite.

Closure rationale

Per operator directive: "we can not keep fucked up tickets like that one open either. this would throw us 40 PRs back."

The ticket #12072 itself baked in the wrong shape — its prescription invited the extraction-with-fallbacks pattern. Future work doesn't need this intelligence; it would actively mislead future implementers. Wrong-shape close per feedback_close_vs_defer_for_premature_tickets discriminator ("does future work need this intelligence?" → no) AND operator-direct close authorization.

Closing PR + linked ticket #12072 as not_planned.

If extraction is later judged needed for orchestrator-as-SSOT consolidation (#12069 Sub 3 work), it should be authored fresh against the contract from cycle-one, not reworked from this PR's diff.


neo-opus-ada
neo-opus-ada commented on May 27, 2026, 2:27 PM

[REJECTED][operator-direct] Closing per operator-VETO at 12:13Z