@kaged/dsl
YAML + Zod project DSL parser and validator with federated config, URI-prefix enforcement, local overlays, and recursive project-reference resolution
13
source files
6
test files
~8.1k
lines
✓ 333 pass
tests
pass
typecheck
clean
lint
Test results 333
compileProjectDsl: single-level resolution > resolves one project reference and attaches _compiled
[23.98ms]
compileProjectDsl: single-level resolution > preserves declared name/description/overrides alongside _compiled
[6.02ms]
compileProjectDsl: single-level resolution > regular subagents pass through unchanged
[11.04ms]
compileProjectDsl: single-level resolution > no project references: returns input unchanged
[1.61ms]
compileProjectDsl: single-level resolution > hasProjectReferences true when any ref exists
[3.50ms]
compileProjectDsl: recursion > A → B → C with paths relative to each parent's root
[5.09ms]
compileProjectDsl: recursion > silo boundary: nested ref at project:/data is NOT resolved against parent root
[7.00ms]
compileProjectDsl: cycle detection > unbounded chain is blocked at the depth limit
[5.79ms]
compileProjectDsl: cycle detection > diamond (A → B and A → C, pointing to distinct D's) is NOT flagged
[11.09ms]
compileProjectDsl: depth limit > aborts at the default depth limit (16)
[26.07ms]
compileProjectDsl: depth limit > custom maxDepth=2 stops at depth 2
[9.57ms]
compileProjectDsl: depth limit > depth equal to limit is permitted
[5.29ms]
compileProjectDsl: overrides > simple top-level override of primary.model
[3.15ms]
compileProjectDsl: overrides > nested .local.yaml is applied BEFORE the parent's overrides
[6.12ms]
compileProjectDsl: failure modes > missing nested project.yaml yields nested_project_missing diagnostic
[1.57ms]
compileProjectDsl: failure modes > invalid nested project.yaml yields a compile diagnostic
[3.85ms]
compileProjectDsl: failure modes > diagnostic path traces to the offending map key
[3.36ms]
compileProjectDsl: failure modes > warnings from nested cage:disabled bubble up with map-key prefix
[7.73ms]
compileProjectDsl: injectable readFile > uses provided readFile in place of node:fs
[1.75ms]
resolveCompaction > returns parent when child is undefined
[0.050ms]
resolveCompaction > child fields override parent fields
[0.060ms]
resolveCompaction > child strategy overrides parent strategy
[0.070ms]
resolveCompaction > child summarize block overrides parent summarize block
[0.050ms]
resolveCompaction > child always_keep replaces parent always_keep (no merge)
[0.050ms]
resolveCompaction > all child fields specified produces fully overridden result
[0.060ms]
compileProjectDsl: compaction materialization > agent with no compaction gets DEFAULT_COMPACTION materialized
[1.13ms]
compileProjectDsl: compaction materialization > primary compaction is materialized with resolved defaults
[1.67ms]
compileProjectDsl: compaction materialization > subagent inherits parent compaction when unset
[2.20ms]
compileProjectDsl: compaction materialization > subagent overrides specific fields, inherits the rest
[2.11ms]
compileProjectDsl: compaction materialization > three-level inheritance: grandchild inherits from child override
[5.71ms]
compileProjectDsl: compaction materialization > project-reference boundary resets compaction to default
[2.70ms]
compileProjectDsl: compaction materialization > nested project with its own compaction uses that, not parent's
[2.49ms]
compileProjectDsl: compaction materialization > plugins pass through unchanged during compilation
[2.02ms]
DEFAULT_COMPACTION defaults (ADR-0024 amendment §7) > includes summarize block with default prompt path
[0.040ms]
DEFAULT_COMPACTION defaults (ADR-0024 amendment §7) > summarize block omits model (agent uses its own)
[0.030ms]
CompactionSummarizeSchema: optional model and prompt > accepts summarize block with prompt only (no model)
[0.660ms]
CompactionSummarizeSchema: optional model and prompt > accepts summarize block with model only (no prompt)
[0.660ms]
CompactionSummarizeSchema: optional model and prompt > accepts summarize block with no model and no prompt
[0.710ms]
worked examples > minimal.yaml validates successfully
[1.14ms]
worked examples > single-subagent.yaml validates successfully
[2.25ms]
worked examples > multi-subagent.yaml validates successfully
[3.07ms]
worked examples > network-allowlist.yaml validates successfully
[3.09ms]
worked examples > with-plugins.yaml validates successfully
[2.29ms]
worked examples > insecure.yaml validates successfully
[2.26ms]
worked examples > portable.yaml validates successfully
[2.77ms]
worked examples > insecure.yaml emits cage:disabled warning
[2.17ms]
worked examples > minimal.yaml has no subagents on primary
[0.760ms]
worked examples > with-plugins.yaml has plugins
[1.95ms]
schema: accepts valid > minimal valid project
[0.650ms]
schema: accepts valid > project with description
[0.690ms]
schema: accepts valid > primary with parameters
[0.740ms]
schema: accepts valid > subagent with cage: disabled is valid
[1.16ms]
schema: accepts valid > cage with all optional fields
[1.62ms]
schema: accepts valid > model alias: various valid names
[2.38ms]
schema: accepts valid > project slug: various valid slugs
[1.79ms]
schema: accepts valid > system_prompt: URI-prefixed paths accepted
[2.12ms]
schema: accepts valid > system_prompt: naked path rejected
[1.00ms]
schema: accepts valid > plugins with all fields
[0.910ms]
schema: accepts valid > primary with per-agent tools
[0.920ms]
schema: rejects invalid > missing version
[0.670ms]
schema: rejects invalid > version: 2 is rejected
[0.720ms]
schema: rejects invalid > version: "1" (string) is rejected
[0.670ms]
schema: rejects invalid > missing project
[0.640ms]
schema: rejects invalid > missing primary
[0.510ms]
schema: rejects invalid > unknown top-level field
[0.820ms]
schema: rejects invalid > model alias with colon is rejected
[0.710ms]
schema: rejects invalid > reserved model alias rejected
[3.14ms]
schema: rejects invalid > reserved subagent name rejected
[3.44ms]
schema: rejects invalid > absolute path in system_prompt rejected
[0.720ms]
schema: rejects invalid > path with .. escape rejected
[0.670ms]
schema: rejects invalid > absolute path in cage.fs rejected
[1.42ms]
schema: rejects invalid > description over 280 chars rejected
[0.760ms]
schema: rejects invalid > description at 280 chars accepted
[0.670ms]
schema: rejects invalid > more than 64 subagents rejected
[24.79ms]
schema: rejects invalid > cage value that is neither object nor disabled
[1.37ms]
schema: rejects invalid > cage.state invalid enum value
[1.23ms]
schema: rejects invalid > cage.seccomp invalid enum value
[4.75ms]
schema: rejects invalid > cage.limits.memory_mb below minimum
[1.01ms]
schema: rejects invalid > mount.mode invalid value
[0.890ms]
schema: rejects invalid > YAML syntax error throws DslError
[0.560ms]
schema: rejects invalid > top-level subagents key rejected (ADR-0022)
[0.610ms]
schema: rejects invalid > top-level interconnect key rejected (ADR-0022)
[0.590ms]
schema: rejects invalid > top-level cage_defaults key rejected (ADR-0022)
[0.570ms]
schema: rejects invalid > top-level tools key rejected (ADR-0022)
[0.520ms]
cross-reference validation > root agent cage must be disabled
[0.540ms]
cross-reference validation > principal_scope: root-only tool on non-root agent rejected
[0.870ms]
cross-reference validation > principal_scope: root-only tool on root agent accepted
[0.480ms]
cross-reference validation > tool-name collision: project-ref name override matches sibling map key
[1.01ms]
cross-reference validation > no errors when subagents have unique names
[0.860ms]
error messages > errors include doc links
[0.460ms]
error messages > unknown field includes did-you-mean
[0.430ms]
error messages > DslError has diagnostics array
[0.490ms]
error messages > YAML syntax error has line info
[0.840ms]
error messages > file path appears in diagnostic when provided
[0.420ms]
utilities > parse() returns data + warnings separately
[0.440ms]
utilities > validateCrossRefs returns empty for valid DSL
[0.420ms]
tasks block: accepts valid > project without tasks is valid
[0.430ms]
tasks block: accepts valid > project with empty tasks map is valid
[0.510ms]
tasks block: accepts valid > single named task with all fields
[0.780ms]
tasks block: accepts valid > minimal task (command only)
[0.520ms]
tasks block: accepts valid > long_running task
[0.510ms]
tasks block: accepts valid > confirm task
[0.500ms]
tasks block: accepts valid > multiple tasks with groups
[0.550ms]
tasks block: accepts valid > task name with hyphens and underscores
[1.39ms]
tasks block: rejects invalid > reserved task name: adhoc
[0.490ms]
tasks block: rejects invalid > reserved task name: all
[0.530ms]
tasks block: rejects invalid > reserved task name: new
[0.500ms]
tasks block: rejects invalid > task name starting with digit rejected
[0.500ms]
tasks block: rejects invalid > task name with uppercase rejected
[0.490ms]
tasks block: rejects invalid > single-char task name rejected
[0.540ms]
tasks block: rejects invalid > task missing command rejected
[0.500ms]
tasks block: rejects invalid > task with empty command rejected
[0.540ms]
tasks block: rejects invalid > task description over 280 chars rejected
[0.610ms]
tasks block: rejects invalid > task with absolute cwd rejected
[0.530ms]
tasks block: rejects invalid > task with .. cwd rejected
[0.530ms]
tasks block: rejects invalid > task with unknown field rejected (strict mode)
[0.580ms]
tasks block: rejects invalid > more than 64 tasks rejected
[2.40ms]
tasks block: rejects invalid > task env with non-string value rejected
[0.540ms]
tasks block: cross-reference validation > unique task names pass validation
[0.510ms]
workflows block: accepts valid > project without workflows is valid
[0.400ms]
workflows block: accepts valid > project with empty workflows map is valid
[0.470ms]
workflows block: accepts valid > single workflow with all fields
[1.71ms]
workflows block: accepts valid > minimal workflow (required fields only)
[0.700ms]
workflows block: accepts valid > workflow name with dots, hyphens, underscores
[2.22ms]
workflows block: accepts valid > multiple workflows
[0.900ms]
workflows block: accepts valid > workflow with empty inputs map
[0.560ms]
workflows block: accepts valid > input required defaults to true
[0.620ms]
workflows block: rejects invalid > reserved workflow name: adhoc
[0.590ms]
workflows block: rejects invalid > reserved workflow name: all
[0.600ms]
workflows block: rejects invalid > workflow name starting with digit rejected
[0.610ms]
workflows block: rejects invalid > workflow name with uppercase rejected
[0.570ms]
workflows block: rejects invalid > single-char workflow name rejected
[0.570ms]
workflows block: rejects invalid > workflow missing description rejected
[0.550ms]
workflows block: rejects invalid > workflow missing system_prompt rejected
[0.570ms]
workflows block: rejects invalid > workflow missing inputs rejected
[0.560ms]
workflows block: rejects invalid > workflow missing tools rejected
[0.530ms]
workflows block: rejects invalid > workflow description over 280 chars rejected
[0.630ms]
workflows block: rejects invalid > workflow with naked system_prompt path rejected
[0.610ms]
workflows block: rejects invalid > workflow input with invalid type rejected
[0.640ms]
workflows block: rejects invalid > workflow input missing type rejected
[0.670ms]
workflows block: rejects invalid > workflow with unknown field rejected (strict)
[0.680ms]
workflows block: rejects invalid > workflow input with unknown field rejected (strict)
[0.700ms]
workflows block: rejects invalid > workflow tools with unknown field rejected (strict)
[0.630ms]
workflows block: rejects invalid > workflow invokable_by with invalid value rejected
[0.660ms]
workflows block: rejects invalid > workflow timeout_seconds zero rejected
[0.650ms]
workflows block: rejects invalid > more than 64 workflows rejected
[9.22ms]
deepMergeDsl > scalars: overlay replaces base
[2.66ms]
deepMergeDsl > absent keys: base value passes through
[0.070ms]
deepMergeDsl > null: removes the key (nullification)
[0.040ms]
deepMergeDsl > objects: deep-merge recursively
[0.040ms]
deepMergeDsl > objects: deep-merge multiple levels
[0.040ms]
deepMergeDsl > arrays: overlay replaces base entirely
[0.030ms]
deepMergeDsl > arrays: empty overlay array replaces base array
[0.020ms]
deepMergeDsl > type mismatch: overlay replaces base
[0.040ms]
deepMergeDsl > type mismatch: object replaced by scalar
[0.020ms]
deepMergeDsl > type mismatch: scalar replaced by object
[0.020ms]
deepMergeDsl > does not mutate base or overlay
[0.070ms]
deepMergeDsl > empty overlay returns base clone
[0.030ms]
deepMergeDsl > empty base returns overlay clone
[0.070ms]
deepMergeDsl > nullify nested key
[0.030ms]
deepMergeDsl > overlay adds new keys
[0.020ms]
parseUri > parses project:/ prefix
[0.040ms]
parseUri > parses config:/ prefix
[0.020ms]
parseUri > parses deeply nested path
[0.020ms]
parseUri > parses single-segment path
[0.020ms]
parseUri > rejects naked path
[0.060ms]
parseUri > rejects absolute path
[0.030ms]
parseUri > rejects empty string
[0.020ms]
parseUri > rejects unknown prefix
[0.020ms]
parseUri > double-slash: second slash becomes part of path
[0.040ms]
parseUri > rejects .. escape at start
[0.020ms]
parseUri > rejects .. escape in middle
[0.030ms]
parseUri > rejects bare .. path
[0.020ms]
parseUri > allows segments containing .. in names
[0.020ms]
resolveUri > project:/ resolves to project root
[0.060ms]
resolveUri > config:/ resolves to .kaged/ under project root
[0.020ms]
resolveUri > project:/ single file
[0.020ms]
resolveUri > config:/ deeply nested
[0.020ms]
resolveUri > throws on invalid URI
[0.020ms]
shadowPath > config:/ with extension inserts .local before ext
[0.070ms]
shadowPath > config:/ with .json extension
[0.020ms]
shadowPath > config:/ with .yaml extension
[0.010ms]
shadowPath > config:/ without extension appends .local
[0.030ms]
shadowPath > config:/ deeply nested path
[0.020ms]
shadowPath > project:/ returns null (no shadowing)
[0.010ms]
shadowPath > config:/ with dot in directory name but no extension in filename
[0.010ms]
loadProjectDsl > no overlay: behaves like parseDsl
[0.820ms]
loadProjectDsl > overlay merges: overrides primary.model
[0.580ms]
loadProjectDsl > overlay merges: adds description
[0.500ms]
loadProjectDsl > overlay rejects version override
[0.620ms]
loadProjectDsl > overlay rejects project override
[0.460ms]
loadProjectDsl > overlay merges: deep-merges primary parameters
[0.580ms]
loadProjectDsl > overlay merges: subagents record deep-merges (empty overlay preserves base)
[0.960ms]
loadProjectDsl > overlay merges: subagent nullified via null value
[0.640ms]
loadProjectDsl > overlay YAML syntax error throws DslError
[0.490ms]
loadProjectDsl > base YAML syntax error throws DslError
[0.240ms]
loadProjectDsl > merged result that fails schema throws DslError
[0.540ms]
loadProjectDsl > overlay file path appears in forbidden-key error
[0.260ms]
loadProjectDsl > overlay with cage:disabled produces warning
[0.860ms]
PrefixedPathSchema integration > project:/ in system_prompt accepted
[0.390ms]
PrefixedPathSchema integration > config:/ in system_prompt accepted
[0.380ms]
PrefixedPathSchema integration > naked path in system_prompt rejected
[0.460ms]
PrefixedPathSchema integration > project:/ in task cwd accepted
[0.510ms]
PrefixedPathSchema integration > naked path in task cwd rejected
[0.530ms]
ToolOverrideSchema integration > tools with overrides accepted
[0.550ms]
ToolOverrideSchema integration > tools with null value (disable) accepted
[0.490ms]
ToolOverrideSchema integration > tools with parameters accepted
[0.470ms]
ToolOverrideSchema integration > project without tools is valid
[0.460ms]
ToolOverrideSchema integration > overlay merges tools
[0.550ms]
per-agent plugins: accepts valid > primary with a single plugin override (registry + per-agent)
[1.11ms]
per-agent plugins: accepts valid > primary with enabled: true override
[0.610ms]
per-agent plugins: accepts valid > minimal per-agent override (only enabled)
[0.550ms]
per-agent plugins: accepts valid > multiple plugins on same agent
[0.700ms]
per-agent plugins: accepts valid > pre_compact hook accepted
[0.580ms]
per-agent plugins: accepts valid > subagent with its own plugin override
[0.880ms]
per-agent plugins: accepts valid > registry entry with source field
[0.450ms]
per-agent plugins: accepts valid > registry entry with git: source
[0.440ms]
per-agent plugins: accepts valid > registry entry with project: source
[0.480ms]
per-agent plugins: accepts valid > registry entry with config: source
[0.430ms]
per-agent plugins: rejects invalid > unknown field in per-agent plugin declaration
[0.580ms]
per-agent plugins: rejects invalid > invalid hook name rejected
[0.520ms]
per-agent plugins: rejects invalid > plugin slot name starting with digit rejected
[0.480ms]
per-agent plugins: rejects invalid > plugin slot name with uppercase rejected
[0.460ms]
per-agent plugins: rejects invalid > more than 16 plugins per agent rejected
[1.24ms]
per-agent plugins: rejects invalid > registry entry without package rejected
[0.500ms]
per-agent plugins: rejects invalid > invalid source prefix rejected
[0.490ms]
per-agent plugins: rejects invalid > per-agent package field rejected (belongs in registry)
[0.510ms]
per-agent plugins: rejects invalid > per-agent source field rejected (belongs in registry)
[0.490ms]
per-agent plugins: cross-ref validation > per-agent slot not in project registry → error
[0.120ms]
per-agent plugins: cross-ref validation > per-agent slot in project registry → no error
[0.030ms]
per-agent plugins: cross-ref validation > subagent per-agent slot not in project registry → error
[0.070ms]
per-agent plugins: cross-ref validation > primary-only hooks on subagent → warning
[0.070ms]
per-agent plugins: cross-ref validation > pre_compact on subagent → no warning
[0.040ms]
per-agent plugins: cross-ref validation > primary-only hooks on root agent → no warning
[0.030ms]
per-agent compaction: accepts valid > compaction with drop strategy (default)
[0.470ms]
per-agent compaction: accepts valid > compaction with summarize strategy
[0.500ms]
per-agent compaction: accepts valid > compaction with delegate strategy
[0.590ms]
per-agent compaction: accepts valid > compaction with checkpoint strategy
[0.490ms]
per-agent compaction: accepts valid > compaction with auto_resume_timeout_sec: null
[0.440ms]
per-agent compaction: accepts valid > compaction with always_keep predicates
[0.480ms]
per-agent compaction: accepts valid > minimal compaction (no fields = all defaults)
[0.410ms]
per-agent compaction: accepts valid > compaction on subagent
[0.650ms]
per-agent compaction: rejects invalid > invalid strategy rejected
[0.460ms]
per-agent compaction: rejects invalid > upper_threshold > 1.0 rejected
[0.470ms]
per-agent compaction: rejects invalid > lower_threshold < 0 rejected
[0.510ms]
per-agent compaction: rejects invalid > unknown field in compaction rejected
[0.540ms]
per-agent compaction: rejects invalid > unknown field in summarize sub-block rejected
[0.500ms]
per-agent compaction: rejects invalid > summarize model must be a valid alias
[0.480ms]
per-agent compaction: rejects invalid > summarize prompt must use URI prefix
[0.480ms]
per-agent compaction: rejects invalid > delegate fallback_strategy rejects checkpoint
[0.450ms]
per-agent compaction: cross-ref validation > lower_threshold >= upper_threshold → error
[0.100ms]
per-agent compaction: cross-ref validation > lower_threshold == upper_threshold → error
[0.050ms]
per-agent compaction: cross-ref validation > valid threshold ordering → no error
[0.030ms]
per-agent compaction: cross-ref validation > default thresholds (omitted) → no error
[0.020ms]
per-agent compaction: cross-ref validation > strategy summarize without summarize block → error
[0.030ms]
per-agent compaction: cross-ref validation > strategy delegate without delegate block → error
[0.030ms]
per-agent compaction: cross-ref validation > delegate.plugin referencing existing plugin → no error
[0.050ms]
per-agent compaction: cross-ref validation > delegate.plugin referencing missing plugin → cross_reference error
[0.060ms]
per-agent compaction: cross-ref validation > delegate.plugin with no plugins block → cross_reference error
[0.040ms]
per-agent compaction: cross-ref validation > strategy drop with summarize block → no cross-ref error (extra fields ok)
[0.030ms]
round-trip integration > full agent with plugins + compaction + subagents
[1.04ms]
DEFAULT_ROOT_TOOLS > contains 18 tools across 9 namespaces
[0.030ms]
DEFAULT_ROOT_TOOLS > covers all expected namespaces
[0.090ms]
DEFAULT_ROOT_TOOLS > has no duplicates
[0.030ms]
DEFAULT_ROOT_TOOLS > every entry matches naming convention (dotted or singleton)
[0.090ms]
DEFAULT_ENABLED_TOOLS > contains exactly 5 kaged.* tools
[0.020ms]
DEFAULT_ENABLED_TOOLS > all entries are in kaged namespace
[0.030ms]
DEFAULT_ENABLED_TOOLS > is a subset of DEFAULT_ROOT_TOOLS
[0.030ms]
DEFAULT_ENABLED_TOOLS > non-kaged namespaces are not in the set
[0.030ms]
resolveRootTools: no overrides > returns only default-enabled tools from available list
[0.110ms]
resolveRootTools: no overrides > returns empty when no available tools are default-enabled
[0.020ms]
resolveRootTools: no overrides > preserves input order of enabled tools
[0.020ms]
resolveRootTools: no overrides > returns empty array for empty available list
[0.020ms]
resolveRootTools: no overrides > returns all kaged.* when full DEFAULT_ROOT_TOOLS provided
[0.040ms]
resolveRootTools: single layer opt-in > enabled: true opts in a disabled-by-default tool
[0.150ms]
resolveRootTools: single layer opt-in > null disables a default-enabled tool
[0.040ms]
resolveRootTools: single layer opt-in > enabled: false disables a default-enabled tool
[0.030ms]
resolveRootTools: single layer opt-in > enabled: true on already-enabled tool is a no-op
[0.030ms]
resolveRootTools: single layer opt-in > empty object has no effect
[0.040ms]
resolveRootTools: single layer opt-in > overrides for unknown tools are ignored
[0.030ms]
resolveRootTools: single layer opt-in > multiple tools enabled in one layer
[0.050ms]
resolveRootTools: layered overrides > later layer overrides earlier: operator enables, project disables
[0.040ms]
resolveRootTools: layered overrides > later layer overrides earlier: operator disables default, project re-enables
[0.030ms]
resolveRootTools: layered overrides > operator enables, project has empty object = stays enabled
[0.040ms]
resolveRootTools: layered overrides > operator enables disabled-by-default, project disables default-enabled
[0.050ms]
resolveRootTools: layered overrides > non-overlapping layers compose correctly
[0.060ms]
compileProjectDsl: resolvedRootTools > resolvedRootTools is null when availableTools not provided
[0.810ms]
compileProjectDsl: resolvedRootTools > resolvedRootTools returns only default-enabled tools when no overrides
[0.570ms]
compileProjectDsl: resolvedRootTools > operator overrides can enable disabled-by-default tools
[0.600ms]
compileProjectDsl: resolvedRootTools > operator overrides can disable default-enabled tools
[0.550ms]
compileProjectDsl: resolvedRootTools > DSL primary.tools applied as second layer
[0.600ms]
compileProjectDsl: resolvedRootTools > DSL overrides take precedence over operator overrides
[0.610ms]
compileProjectDsl: resolvedRootTools > operator disables + DSL re-enables = tool is enabled
[0.570ms]
compileProjectDsl: resolvedRootTools > materialized tools reflect default-disabled state
[0.530ms]
compileProjectDsl: recursive tool materialization > inline subagent gets materialized tools with nothing enabled by default
[0.870ms]
compileProjectDsl: recursive tool materialization > inline subagent tools opt-in via its own tools block
[0.910ms]
compileProjectDsl: recursive tool materialization > deeply nested subagent gets materialized tools
[1.20ms]
compileProjectDsl: recursive tool materialization > root agent still gets kaged.* enabled by default
[0.810ms]
compileProjectDsl: recursive tool materialization > operator overrides apply to subagents too
[0.940ms]
compileProjectDsl: recursive tool materialization > no materialization when availableTools not provided
[0.790ms]
project references: accepts valid > minimal project reference (path only)
[1.13ms]
project references: accepts valid > project reference with name override
[0.730ms]
project references: accepts valid > project reference with description override
[0.720ms]
project references: accepts valid > project reference with overrides block (opaque to parser)
[0.800ms]
project references: accepts valid > project reference with all four fields
[0.840ms]
project references: accepts valid > multiple project references coexist
[1.25ms]
project references: accepts valid > project reference coexists with regular subagent
[0.900ms]
project references: accepts valid > project reference with nested deeper path
[0.580ms]
project references: rejects invalid > config:/ scheme is rejected
[0.610ms]
project references: rejects invalid > git:/ scheme is rejected (deferred)
[0.600ms]
project references: rejects invalid > https:/ scheme is rejected (deferred)
[0.600ms]
project references: rejects invalid > file:/ scheme is rejected
[0.600ms]
project references: rejects invalid > naked path (no prefix) — parser treats as AgentSpec and fails
[0.630ms]
project references: rejects invalid > '..' escape in path is rejected
[0.610ms]
project references: rejects invalid > empty path portion is rejected
[0.610ms]
project references: rejects invalid > double-slash (project://) is rejected
[0.590ms]
project references: rejects invalid > mixed shape: path: + cage: is rejected
[0.750ms]
project references: rejects invalid > mixed shape: path: + model: is rejected
[0.660ms]
project references: rejects invalid > mixed shape: path: + system_prompt: is rejected
[0.720ms]
project references: rejects invalid > unknown field on project reference is rejected
[0.750ms]
project references: rejects invalid > name override violating subagent-name pattern is rejected
[0.660ms]
project references: rejects invalid > name override using reserved name is rejected
[1.82ms]
project references: rejects invalid > description exceeding 280 chars is rejected
[0.720ms]
project references: rejects invalid > overrides containing 'version' is rejected
[0.660ms]
project references: rejects invalid > overrides containing 'project' is rejected
[0.630ms]
project references: rejects invalid > overrides is not a plain object (string) — rejected
[0.640ms]
project references: cross-reference validation > a project-ref's `name:` override colliding with another map key is an error
[0.920ms]
project references: cross-reference validation > two project-refs whose `name:` overrides match each other is an error
[0.820ms]
project references: cross-reference validation > two project-refs at distinct map keys without name-collision validate
[0.770ms]
project references: cross-reference validation > a project-ref's `name:` matching its own map key is fine (redundant but valid)
[0.600ms]
project references: warnings > project references do not produce cage:disabled warnings
[0.590ms]
project references: warnings > cage:disabled warning still fires on adjacent regular subagent
[0.840ms]
project references: type guard > isProjectReference correctly discriminates
[1.22ms]