mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
f2a350ba7f
Anthropic announced nested subagent support (depth=5) on 2026-06-09; the runtime currently strips Task at parent->child spawn (upstream denylist, empirically confirmed by our committed probe: level=1 status=NO_AGENT_TOOL, matching ruflo ADR-147). This lands the readiness infrastructure that is independently useful today and activates with zero changes when the gate lifts: - aqe hooks post-task gains --parent-agent-id / --depth (validated: identifier charset, integer 0 <= d <= 32; depth=0 survives as 0); provenance persisted to qe_trajectories.metadata_json (stage 1 — no schema migration) and forwarded into ReasoningBank feedback so learning can segment patterns per hierarchy level - TaskExecution gains optional parentAgentId/depth (additive types) - scripts/probe-nested-spawn-depth.mjs: single-shot empirical probe, committed transcript under docs/probes/ — re-run after every `claude update`; TASK_TOOL_LIVE is the signal for follow-up phases - fresh-project fix found by the E2E: persistTaskOutcome assumed captured_experiences/experience_applications exist, but they are middleware-created — Stream B silently no-oped on fresh installs; canonical DDL now ensured in the hook path Deliberately deferred (documented in ADR-101 P1): tools: frontmatter retrofit on shipped qe-*.md agents — Claude Code frontmatter is a restrictive allowlist and would sever inherited MCP tool access; needs maintainer sign-off + wildcard verification first. 8 unit tests green; E2E on a fresh temp project verified persistence, rejections, and depth=0 boundary. tsc + eslint clean. Part of #520 (improvement 4/7, Tier 2). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
68 lines
2.5 KiB
TypeScript
68 lines
2.5 KiB
TypeScript
/**
|
|
* Unit tests for nested-subagent provenance validation (ADR-101)
|
|
*
|
|
* Mirrors ruflo's ADR-147 P2 validation suite: propagation, omission,
|
|
* depth=0 boundary, invalid id, negative / non-integer / >32 depth.
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
parseNestingProvenance,
|
|
MAX_NESTING_DEPTH,
|
|
} from '../../../../src/cli/commands/hooks-handlers/nesting-provenance';
|
|
|
|
describe('parseNestingProvenance', () => {
|
|
it('should propagate parentAgentId and depth when both are supplied', () => {
|
|
const result = parseNestingProvenance('qe-fleet-commander', '2');
|
|
|
|
expect(result.error).toBeUndefined();
|
|
expect(result.parentAgentId).toBe('qe-fleet-commander');
|
|
expect(result.depth).toBe(2);
|
|
});
|
|
|
|
it('should omit both fields when neither is supplied (top-level lead)', () => {
|
|
const result = parseNestingProvenance(undefined, undefined);
|
|
|
|
expect(result.error).toBeUndefined();
|
|
expect(result.parentAgentId).toBeUndefined();
|
|
expect(result.depth).toBeUndefined();
|
|
});
|
|
|
|
it('should propagate depth=0 as 0, not coerce it to undefined', () => {
|
|
const result = parseNestingProvenance(undefined, '0');
|
|
|
|
expect(result.error).toBeUndefined();
|
|
expect(result.depth).toBe(0);
|
|
});
|
|
|
|
it('should reject an invalid parentAgentId', () => {
|
|
expect(parseNestingProvenance('bad id with spaces').error).toContain('parent-agent-id');
|
|
expect(parseNestingProvenance('-starts-with-dash').error).toContain('parent-agent-id');
|
|
expect(parseNestingProvenance('a'.repeat(129)).error).toContain('parent-agent-id');
|
|
expect(parseNestingProvenance('semi;colon').error).toContain('parent-agent-id');
|
|
});
|
|
|
|
it('should reject negative depth', () => {
|
|
const result = parseNestingProvenance(undefined, '-1');
|
|
|
|
expect(result.error).toContain('>= 0');
|
|
});
|
|
|
|
it('should reject non-integer depth', () => {
|
|
expect(parseNestingProvenance(undefined, '2.5').error).toContain('integer');
|
|
expect(parseNestingProvenance(undefined, 'two').error).toContain('integer');
|
|
});
|
|
|
|
it('should reject depth above the defensive bound and accept the bound itself', () => {
|
|
expect(parseNestingProvenance(undefined, '33').error).toContain('<= 32');
|
|
expect(parseNestingProvenance(undefined, String(MAX_NESTING_DEPTH)).depth).toBe(32);
|
|
});
|
|
|
|
it('should accept identifiers with dots, colons, dashes and underscores', () => {
|
|
const result = parseNestingProvenance('qe.fleet:commander_v3-1');
|
|
|
|
expect(result.error).toBeUndefined();
|
|
expect(result.parentAgentId).toBe('qe.fleet:commander_v3-1');
|
|
});
|
|
});
|