Files
proffesor-for-testing__agen…/tests/unit/cli/hooks/nesting-provenance.test.ts
Dragan Spiridonov f2a350ba7f feat(agents): ADR-101 nested-subagent readiness — provenance, probe, validation
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>
2026-06-10 10:06:05 +00:00

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');
});
});