feat(harness): schema loader for Centaur agent definitions#389
Conversation
Bridge between Centaur YAML agent definitions and Mitzo's DeliberationConfig/FusionConfig types. Converts declarative deliberation blocks (role, counterpart, protocol) into ready-to-run orchestrator configs with sensible defaults. - buildDeliberationConfig: proposer + challenger pair → config - buildDeliberationConfigFromAgent: single agent + resolver → config - buildFusionConfig: model list → panel/judge/synthesizer config - 17 tests covering happy path, defaults, overrides, and error cases Co-Authored-By: Claude Opus 4.6 <[email protected]>
dimakis
left a comment
There was a problem hiding this comment.
Centaur Review
Found 6 issue(s) (1 warning).
packages/harness/__tests__/schema-loader.test.ts
Clean, well-structured addition with good test coverage for the happy paths and core error cases. The main substantive issue is that the challenger's protocol settings are silently dropped when the proposer has none — a one-line fallback fix. The remaining items are minor test gaps and a misleading JSDoc comment.
- 🔵 missing_tests: No test for counterpart mismatch warnings.
buildDeliberationConfigwarns whenproposerDef.deliberation.counterpart !== challengerDef.identity.name(and vice versa), but this path has no test coverage. A test with mismatched counterpart names would verify the function still succeeds (doesn't throw) while logging the warning.[fixable] - 🔵 missing_tests: No test for
buildDeliberationConfigFromAgentwhen the resolver throws (e.g. counterpart not found). The function doesn't catch resolver errors, so the test should verify the rejection propagates cleanly. This is a likely real-world failure mode.[fixable] - 🔵 missing_tests: No test for
buildDeliberationConfigFromAgentwhen the resolved counterpart has the wrong role (e.g. resolver returns another proposer instead of a challenger). This would exercise the validation inbuildDeliberationConfigthrough the convenience path.[fixable] - 🔵 missing_tests:
buildFusionConfigaccepts ajudgeModelstring that doesn't need to be in thepanelModelslist. There's no test verifying behavior whenjudgeModelis a model not present in the panel — this may be intentional but worth documenting with a test.[fixable]
packages/harness/src/reasoning/schema-loader.ts
Clean, well-structured addition with good test coverage for the happy paths and core error cases. The main substantive issue is that the challenger's protocol settings are silently dropped when the proposer has none — a one-line fallback fix. The remaining items are minor test gaps and a misleading JSDoc comment.
- 🟡 bugs (L121): Protocol settings are only read from the proposer's definition (
proposerDef.deliberation.protocol). WhenbuildDeliberationConfigFromAgentis called with a challenger that has protocol settings but its resolved proposer counterpart does not, the challenger's protocol settings are silently ignored and defaults are used instead. The docstring says 'proposer's take precedence' but it would be more robust to fall back to the challenger's protocol when the proposer's is absent:const protocol = proposerDef.deliberation.protocol ?? challengerDef.deliberation.protocol ?? {};[fixable] - 🔵 style (L191): JSDoc comment says
synthesizerModel'defaults to judge model' but the implementation leaves itundefinedwhen not specified. TheFusionOrchestratormay handle this fallback internally, but the JSDoc onBuildFusionConfigOptionsis misleading about where the default is applied.[fixable]
| } | ||
|
|
||
| // Extract protocol settings (proposer's take precedence) | ||
| const protocol = proposerDef.deliberation.protocol ?? {}; |
There was a problem hiding this comment.
🟡 bugs: Protocol settings are only read from the proposer's definition (proposerDef.deliberation.protocol). When buildDeliberationConfigFromAgent is called with a challenger that has protocol settings but its resolved proposer counterpart does not, the challenger's protocol settings are silently ignored and defaults are used instead. The docstring says 'proposer's take precedence' but it would be more robust to fall back to the challenger's protocol when the proposer's is absent: const protocol = proposerDef.deliberation.protocol ?? challengerDef.deliberation.protocol ?? {}; [fixable]
| budgetUsd?: number; | ||
| /** Override judge model (defaults to first panel model). */ | ||
| judgeModel?: string; | ||
| /** Override synthesizer model (defaults to judge model). */ |
There was a problem hiding this comment.
🔵 style: JSDoc comment says synthesizerModel 'defaults to judge model' but the implementation leaves it undefined when not specified. The FusionOrchestrator may handle this fallback internally, but the JSDoc on BuildFusionConfigOptions is misleading about where the default is applied. [fixable]
- Fall back to challenger protocol when proposer has none - Fix misleading synthesizerModel JSDoc - Add tests: counterpart mismatch, resolver error, wrong-role resolver, out-of-panel judge Co-Authored-By: Claude Opus 4.6 <[email protected]>
dimakis
left a comment
There was a problem hiding this comment.
Centaur Review
Found 2 issue(s).
packages/harness/src/reasoning/schema-loader.ts
Clean, well-tested feature. Schema-loader logic is correct and thoroughly exercised; only minor suggestions around an unused type field and an unrelated formatting change.
- 🔵 unsafe_assumptions (L32): The
phasesfield inAgentDeliberationBlock.protocolis accepted in the type but never read or validated bybuildDeliberationConfig. Agent definitions specifying custom phases (e.g. omitting 'converge') will be silently ignored since the orchestrator hardcodes a fixed 4-phase protocol. Consider either consuming and validating this field or dropping it from the type to avoid misleading callers.[fixable]
packages/protocol/src/tool-summary.ts
Clean, well-tested feature. Schema-loader logic is correct and thoroughly exercised; only minor suggestions around an unused type field and an unrelated formatting change.
- 🔵 style (L94): This is a formatting-only change (line wrapping) unrelated to the schema-loader feature. Ideally it would be a separate commit to keep the PR diff focused.
| role: 'proposer' | 'challenger'; | ||
| counterpart: string; | ||
| protocol?: { | ||
| phases?: string[]; |
There was a problem hiding this comment.
🔵 unsafe_assumptions: The phases field in AgentDeliberationBlock.protocol is accepted in the type but never read or validated by buildDeliberationConfig. Agent definitions specifying custom phases (e.g. omitting 'converge') will be silently ignored since the orchestrator hardcodes a fixed 4-phase protocol. Consider either consuming and validating this field or dropping it from the type to avoid misleading callers. [fixable]
| // Give the shorter field its full length, allocate the rest to the longer | ||
| const budget = TOOL_SUMMARY_MAX_CHARS - 3; // account for ' · ' | ||
| const stypeLen = Math.min(stype.length, Math.max(Math.floor(budget / 2), budget - desc.length)); | ||
| const stypeLen = Math.min( |
There was a problem hiding this comment.
🔵 style: This is a formatting-only change (line wrapping) unrelated to the schema-loader feature. Ideally it would be a separate commit to keep the PR diff focused.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Centaur ReviewFound 4 issue(s).
|
dimakis
left a comment
There was a problem hiding this comment.
Centaur Review
Found 3 issue(s).
packages/harness/src/index.ts
Clean, well-tested schema loader — good validation, correct type narrowing via Omit<..., 'onEvent'>, thorough test coverage. Only minor style and test symmetry suggestions.
- 🔵 style (L105): Two separate
export { ... } from './reasoning/index.js'blocks (lines 100-104 and 105-109) could be consolidated into one. The type exports are already merged into a single block.[fixable]
packages/harness/__tests__/schema-loader.test.ts
Clean, well-tested schema loader — good validation, correct type narrowing via Omit<..., 'onEvent'>, thorough test coverage. Only minor style and test symmetry suggestions.
- 🔵 missing_tests: The 'wrong role' test for
buildDeliberationConfigFromAgentonly covers the proposer-input case (proposer resolves another proposer). The symmetric case — challenger input whose resolver returns another challenger — exercises a different code path (theelsebranch swaps args) and would catch a swap bug that the current test wouldn't. Consider adding a test for it.[fixable]
packages/harness/src/reasoning/schema-loader.ts
Clean, well-tested schema loader — good validation, correct type narrowing via Omit<..., 'onEvent'>, thorough test coverage. Only minor style and test symmetry suggestions.
- 🔵 style (L1): The 17-line module-level JSDoc is useful documentation but conflicts with the project's code style guideline ('never write multi-paragraph docstrings or multi-line comment blocks — one short line max'). Consider shortening to a one-liner or moving the YAML example to a doc or README.
[fixable]
| DEFAULT_FUSION_CONFIG, | ||
| SELF_FUSION_CONFIG, | ||
| } from './reasoning/index.js'; | ||
| export { |
There was a problem hiding this comment.
🔵 style: Two separate export { ... } from './reasoning/index.js' blocks (lines 100-104 and 105-109) could be consolidated into one. The type exports are already merged into a single block. [fixable]
| @@ -0,0 +1,234 @@ | |||
| /** | |||
There was a problem hiding this comment.
🔵 style: The 17-line module-level JSDoc is useful documentation but conflicts with the project's code style guideline ('never write multi-paragraph docstrings or multi-line comment blocks — one short line max'). Consider shortening to a one-liner or moving the YAML example to a doc or README. [fixable]
…etry, trim JSDoc Co-Authored-By: Claude Opus 4.6 <[email protected]>
Summary
DeliberationConfig/FusionConfigtypesbuildDeliberationConfig()— takes proposer + challenger pair, validates roles/counterparts, applies defaultsbuildDeliberationConfigFromAgent()— async resolver-based: pass one agent, it looks up the counterpartbuildFusionConfig()— model name list → panel/judge/synthesizer configFollows up on #387 (reasoning engine). Prerequisite for wiring ContexGin agent definitions into deliberation sessions.
Test plan
vitest run packages/harness/__tests__/schema-loader.test.ts)tsc -bclean🤖 Generated with Claude Code