feat+test+docs: D56 — v1.x cleanup batch #1: AUTH_MISSING tuple test + /health activeSpawns (#34)

Post-Phase-3 cleanup batch #1. Bundles two small v1.x-roadmap deferrals (#4
and #7 in docs/v1x-roadmap.md) into one D-day. No new user-facing feature;
pins existing behaviour into tests + finally wires the ADR-documented
activeSpawns field on /health.

(1) AUTH_MISSING tuple test (v1.x #7, D45 P3 deferral)

New engine-level test in Suite D40 asserts that an AUTH_MISSING hop produces
a fallbackDetail tuple with trigger_type: 'auth_missing' AND that the engine
does NOT advance past it (HARD_TRIGGER_CODES[AUTH_MISSING]=false). Pre-D56 the
behaviour was implicit through neighbouring tests; D56 makes it explicit so a
future refactor that moves the tuple-push past the auth_missing branch fails
this test directly.

Authority: ADR 0004 § Decision (hard-trigger table) + Amendment 5 (tuple shape).

(2) /health activeSpawns integration (v1.x #4, ADR 0002 Amendment 6 forward
note)

handleHealth now surfaces providers.status.<name>.activeSpawns (sourced from
D38 getActiveSpawnCount(name) — already imported at server.mjs:39). The field
is computed BEFORE healthCheck() is awaited so it remains present even when
healthCheck() throws — getActiveSpawnCount is a cheap in-memory counter read.
New Suite 21c-extra test pins the field presence + numeric + non-negative for
every enabled provider in the fixture.

Authority: ADR 0002 Amendment 6 § "Forward note — exposing the counter via
HTTP" — names the path providers.status.<name>.activeSpawns and pins it as
the Phase 1 deferral that becomes due once management endpoints exist (which
they now do, post-Phase-3).

Test count: 601 → 603 (+2 D56 tests; 603/603 pass locally).

Release-kit: under Unreleased per phase_rolling_mode (Phase 3 closed at v0.3.0;
Phase 4 entries also land here once Phase 4 opens — D56 is a cleanup-D-day
that ships under v0.3.x, not a Phase 4 deliverable).

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
dtzp555-max
2026-05-25 18:11:36 +10:00
committed by GitHub
co-authored by taodeng Claude Opus 4.7
parent 179b4707a7
commit 5ebe3dc77c
3 changed files with 66 additions and 3 deletions
+10 -1
View File
@@ -4,7 +4,16 @@ All notable changes to OLP land here. Per `CLAUDE.md` release_kit overlay, this
## Unreleased ## Unreleased
(empty — Phase 4 entries land here once Phase 4 opens) ### D56 — v1.x cleanup batch #1: AUTH_MISSING tuple test + `/health` activeSpawns (post-Phase-3)
Post-Phase-3 cleanup batch. Resolves two small v1.x-roadmap deferrals into one D-day. No new user-facing feature; pins existing behaviour into tests + finally wires the ADR-documented `activeSpawns` field on `/health`.
- **AUTH_MISSING tuple test** (v1.x roadmap #7 / D45 reviewer P3 deferral). New engine-level test in Suite D40 asserts that an `AUTH_MISSING` hop produces a `fallbackDetail` tuple with `trigger_type: 'auth_missing'` AND that the engine does NOT advance past the AUTH_MISSING hop (per ADR 0004 § Decision — `HARD_TRIGGER_CODES[AUTH_MISSING]=false`). Pre-D56 the behaviour was implicit through other engine-path tests; this commit makes it explicit so a future refactor that moves the tuple-push past the auth_missing branch fails this test directly.
- **`/health` activeSpawns integration** (v1.x roadmap #4 / ADR 0002 Amendment 6 forward note). `handleHealth` now surfaces `providers.status.<name>.activeSpawns` (sourced from D38 `getActiveSpawnCount(name)`). The field is set BEFORE `healthCheck()` is awaited so it remains accessible even when `healthCheck()` throws (cheap in-memory counter read). New Suite 21c-extra test pins the field presence + non-negative value for every enabled provider. With no requests in flight: 0; under saturation: equals `hints.maxConcurrent`.
- **Test count:** 601 → 603 (+2 D56 tests).
- **Authority:** `docs/v1x-roadmap.md` #4 + #7; ADR 0002 Amendment 6 (concurrency observability forward note); ADR 0004 Amendment 5 (X-OLP-Fallback-Detail tuple shape the new test asserts).
## v0.3.0 — 2026-05-25 ## v0.3.0 — 2026-05-25
+9 -2
View File
@@ -577,10 +577,17 @@ async function handleHealth(req, res) {
const available = listAllProviderNames().length; const available = listAllProviderNames().length;
const providerStatuses = {}; const providerStatuses = {};
for (const [name, provider] of loadedProviders) { for (const [name, provider] of loadedProviders) {
// D56 / v1.x roadmap #4 (ADR 0002 Amendment 6 forward note): surface
// per-provider active spawn count for capacity-planning observability.
// D38 shipped getActiveSpawnCount; this is the /health integration.
// The field is set BEFORE healthCheck() in case healthCheck throws —
// activeSpawns is cheap (in-memory counter read) and useful even
// when healthCheck fails.
const activeSpawns = getActiveSpawnCount(name);
try { try {
providerStatuses[name] = await provider.healthCheck(); providerStatuses[name] = { ...(await provider.healthCheck()), activeSpawns };
} catch (e) { } catch (e) {
providerStatuses[name] = { ok: false, error: e.message }; providerStatuses[name] = { ok: false, error: e.message, activeSpawns };
} }
} }
sendJSON(res, 200, { sendJSON(res, 200, {
+47
View File
@@ -6247,6 +6247,30 @@ describe('D40 — X-OLP-Fallback-Detail header (issue #7)', () => {
assert.equal(result.fallbackDetail[0].code, 'SPAWN_FAILED'); assert.equal(result.fallbackDetail[0].code, 'SPAWN_FAILED');
}); });
it('engine: AUTH_MISSING terminates chain, fallbackDetail tuple records trigger_type:"auth_missing" (D56, v1.x roadmap #7)', async () => {
// v1.x roadmap #7 (D40 follow-up): explicit pin that the AUTH_MISSING
// path produces a fallbackDetail tuple with trigger_type:"auth_missing"
// BEFORE the engine's early-return at engine.mjs:486. Was implicit via
// other engine-path tests; D56 makes it explicit so any future refactor
// that moves the tuple-push past the auth_missing branch fails this.
const err = new ProviderError('No OAuth token found', 'AUTH_MISSING');
const chain = [
{ provider: 'anthropic', model: 'claude-sonnet-4-6' },
{ provider: 'openai', model: 'gpt-5.5' }, // present to verify AUTH_MISSING does NOT advance
];
const hopFn = async () => { throw err; };
const result = await executeWithFallback(chain, makeIR({ model: 'claude-sonnet-4-6' }), hopFn);
// AUTH_MISSING is HARD_TRIGGER_CODES[AUTH_MISSING]=false (engine.mjs L52);
// chain stops at hop 0 instead of advancing to openai.
assert.equal(result.chunks, null, 'AUTH_MISSING terminates chain');
assert.equal(result.fallbackHops, 0, 'AUTH_MISSING does NOT advance — stays at hop 0');
assert.equal(result.fallbackDetail.length, 1, 'fallbackDetail has exactly 1 tuple (the AUTH_MISSING hop)');
assert.equal(result.fallbackDetail[0].code, 'AUTH_MISSING');
assert.equal(result.fallbackDetail[0].trigger_type, 'auth_missing');
assert.equal(result.fallbackDetail[0].provider, 'anthropic');
assert.equal(result.fallbackDetail[0].hop, 0);
});
it('engine: non-ProviderError exception → tuple code is "UNKNOWN"', async () => { it('engine: non-ProviderError exception → tuple code is "UNKNOWN"', async () => {
const err = new Error('Something unexpected'); // no .code field const err = new Error('Something unexpected'); // no .code field
const chain = [{ provider: 'anthropic', model: 'claude-sonnet-4-6' }]; const chain = [{ provider: 'anthropic', model: 'claude-sonnet-4-6' }];
@@ -10522,6 +10546,29 @@ describe('Suite 21 — D46 owner-vs-guest gating (ADR 0007 §§ 7.1, 7.2)', () =
assert.ok('status' in body.providers, 'owner /health providers.status must be present'); assert.ok('status' in body.providers, 'owner /health providers.status must be present');
}); });
it('21c-extra: owner /health each provider status carries activeSpawns field (D56, v1.x #4 / ADR 0002 Amendment 6)', async () => {
// ADR 0002 Amendment 6 forward note: when surfaced on /health, the
// per-provider concurrency counter lives at providers.status.<name>.
// activeSpawns. D56 wires it. With no requests in flight, the value
// is 0; under saturation it equals hints.maxConcurrent.
const { plaintext_token } = createKey({ name: '21c-extra', owner_tier: 'owner', providers_enabled: '*', olpHome: TMP });
const r = await fetch({
port, method: 'GET', path: '/health',
headers: { Authorization: `Bearer ${plaintext_token}` },
});
assert.equal(r.status, 200);
const body = JSON.parse(r.body);
// At least one provider must be enabled in the fixture
const statusEntries = Object.entries(body.providers.status);
assert.ok(statusEntries.length >= 1, 'fixture has at least one enabled provider');
for (const [name, status] of statusEntries) {
assert.ok('activeSpawns' in status,
`providers.status.${name}.activeSpawns MUST be present (ADR 0002 Amendment 6)`);
assert.equal(typeof status.activeSpawns, 'number');
assert.ok(status.activeSpawns >= 0, 'activeSpawns >= 0');
}
});
it('21d: owner_only_endpoints config opt-out — empty list → guest gets full payload', async () => { it('21d: owner_only_endpoints config opt-out — empty list → guest gets full payload', async () => {
__setAuthConfig({ __setAuthConfig({
allow_anonymous: true, allow_anonymous: true,