* feat(#1708): typed documentation-sourced #853 dispatch-flatten Graduate the #853 orchestrator-backgrounding decision from a scattered RUNTIME==='codex' prose check to a typed, documentation-sourced engine decision. Adds a backgroundDispatch dispatch sub-axis (sourced per host: codex+cursor documented true, 9 documented false, 5 undocumented), shouldFlattenDispatch(dispatch) (inline UNLESS background && backgroundDispatch, fail-closed), and a gsd_run query dispatch-should-flatten the plan/execute workflows call. Cursor is newly background-eligible per its docs (inline->background) — a documentation-justified behavior change. No RUNTIME-name residue for this decision. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(#1708): backgroundDispatch citations in matrix + CONTEXT note Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#1708): address review findings on typed dispatch-flatten Code/adversarial review: convert the manager.md/autonomous.md Compound Action preamble from hardcoded 'On Codex' to FLATTEN-based branching (the handlers already use the query; the preamble contradicted them and was wrong for cursor); make shouldFlattenDispatch null-safe + type-honest (accepts raw 'undocumented' registry values); make backgroundDispatch a required descriptor field (matching its siblings, all 16 carry it); strengthen the config.runtime behavioral test; update the bug-853 prose-pin test + comment. Security review clean; Codex confirmed no fail-open. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#1708): backfill backgroundDispatch in role:runtime test fixtures Making backgroundDispatch a required descriptor field broke role:runtime fixtures in capability-manifest-version/capability-registry/host-integration-descriptors tests that build a dispatch object without it (caught by full gsd-test, not scoped npm test). Backfill backgroundDispatch:false into the well-formed fixtures; the deliberately-malformed 'required-field' test fixture is left malformed by design. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#1708): update fix-1521 dispatch-gating assertion to the FLATTEN gate fix-1521 pinned the codex-specific run_in_background prose that #1708 graduated to the typed dispatch-should-flatten/FLATTEN gate. Update its assertions to verify FLATTEN=false gating (not a runtime name) + that the old RUNTIME===codex gate is gone. Caught by full gsd-test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(#1708): add changeset for typed dispatch-flatten Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#1708): remove stray temp PR-body file Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#1708): add issue ref to bug-853 allow-test-rule annotations ADR-456 requires every allow-test-rule exemption to carry a see #NNN reference; the source-text-is-the-product annotations added when migrating the prose assertions lacked it (lint-tests CI gate). Add (see #1708). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -68,6 +68,7 @@ function makeMinimalRuntimeCap(overrides = {}) {
|
||||
maxDepth: 1,
|
||||
background: false,
|
||||
subagentToolkit: 'full',
|
||||
backgroundDispatch: false,
|
||||
},
|
||||
},
|
||||
...overrides,
|
||||
@@ -213,6 +214,7 @@ describe('ADR-1239 validator behavioral: undocumented sentinel passes, bogus fai
|
||||
maxDepth: 'undocumented',
|
||||
background: 'undocumented',
|
||||
subagentToolkit: 'undocumented',
|
||||
backgroundDispatch: 'undocumented',
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -229,6 +231,77 @@ describe('ADR-1239 validator behavioral: undocumented sentinel passes, bogus fai
|
||||
assert.strictEqual(dispatchErrors.length, 0,
|
||||
`Valid boolean dispatch fields must produce no errors; got: ${dispatchErrors.join(', ')}`);
|
||||
});
|
||||
|
||||
// Phase B: backgroundDispatch field validation
|
||||
test('dispatch.backgroundDispatch:true → ZERO validator errors', () => {
|
||||
const cap = makeMinimalRuntimeCap({
|
||||
hostIntegration: {
|
||||
...makeMinimalRuntimeCap().runtime.hostIntegration,
|
||||
dispatch: { ...makeMinimalRuntimeCap().runtime.hostIntegration.dispatch, backgroundDispatch: true },
|
||||
},
|
||||
});
|
||||
const errors = validateCapability(cap, 'test-runtime');
|
||||
const bdErrors = errors.filter((e) => e.includes('backgroundDispatch'));
|
||||
assert.strictEqual(bdErrors.length, 0,
|
||||
`backgroundDispatch:true must produce no errors; got: ${bdErrors.join(', ')}`);
|
||||
});
|
||||
|
||||
test('dispatch.backgroundDispatch:false → ZERO validator errors', () => {
|
||||
const cap = makeMinimalRuntimeCap({
|
||||
hostIntegration: {
|
||||
...makeMinimalRuntimeCap().runtime.hostIntegration,
|
||||
dispatch: { ...makeMinimalRuntimeCap().runtime.hostIntegration.dispatch, backgroundDispatch: false },
|
||||
},
|
||||
});
|
||||
const errors = validateCapability(cap, 'test-runtime');
|
||||
const bdErrors = errors.filter((e) => e.includes('backgroundDispatch'));
|
||||
assert.strictEqual(bdErrors.length, 0,
|
||||
`backgroundDispatch:false must produce no errors; got: ${bdErrors.join(', ')}`);
|
||||
});
|
||||
|
||||
test('dispatch.backgroundDispatch:"undocumented" → ZERO validator errors', () => {
|
||||
const cap = makeMinimalRuntimeCap({
|
||||
hostIntegration: {
|
||||
...makeMinimalRuntimeCap().runtime.hostIntegration,
|
||||
dispatch: { ...makeMinimalRuntimeCap().runtime.hostIntegration.dispatch, backgroundDispatch: 'undocumented' },
|
||||
},
|
||||
});
|
||||
const errors = validateCapability(cap, 'test-runtime');
|
||||
const bdErrors = errors.filter((e) => e.includes('backgroundDispatch'));
|
||||
assert.strictEqual(bdErrors.length, 0,
|
||||
`backgroundDispatch:"undocumented" must produce no errors; got: ${bdErrors.join(', ')}`);
|
||||
});
|
||||
|
||||
test('dispatch.backgroundDispatch:"zzz" → produces a validator error', () => {
|
||||
const cap = makeMinimalRuntimeCap({
|
||||
hostIntegration: {
|
||||
...makeMinimalRuntimeCap().runtime.hostIntegration,
|
||||
dispatch: { ...makeMinimalRuntimeCap().runtime.hostIntegration.dispatch, backgroundDispatch: 'zzz' },
|
||||
},
|
||||
});
|
||||
const errors = validateCapability(cap, 'test-runtime');
|
||||
const bdErrors = errors.filter((e) => e.includes('backgroundDispatch'));
|
||||
assert.ok(bdErrors.length > 0,
|
||||
`bogus value "zzz" for backgroundDispatch must produce a validator error`);
|
||||
});
|
||||
|
||||
test('dispatch without backgroundDispatch key → validator error (required field — all 16 descriptors carry it)', () => {
|
||||
// backgroundDispatch is now REQUIRED (matches sibling fields namedDispatch/nested/background/subagentToolkit/maxDepth).
|
||||
const cap = makeMinimalRuntimeCap({
|
||||
hostIntegration: {
|
||||
...makeMinimalRuntimeCap().runtime.hostIntegration,
|
||||
dispatch: (() => {
|
||||
const d = { ...makeMinimalRuntimeCap().runtime.hostIntegration.dispatch };
|
||||
delete d.backgroundDispatch;
|
||||
return d;
|
||||
})(),
|
||||
},
|
||||
});
|
||||
const errors = validateCapability(cap, 'test-runtime');
|
||||
const bdErrors = errors.filter((e) => e.includes('backgroundDispatch'));
|
||||
assert.ok(bdErrors.length > 0,
|
||||
`Missing backgroundDispatch (required field) must produce a validator error`);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -270,6 +343,7 @@ describe('Fix 3: reserved-key guard on hostIntegration and hostIntegration.dispa
|
||||
maxDepth: 5,
|
||||
background: true,
|
||||
subagentToolkit: 'full',
|
||||
backgroundDispatch: true,
|
||||
},
|
||||
modelMode: 'passive',
|
||||
hookBus: 'host',
|
||||
|
||||
Reference in New Issue
Block a user