@kaged/dsl
YAML + Zod project DSL parser and validator with federated config, URI-prefix enforcement, local overlays, and recursive project-reference resolution
15
source files
10
test files
~12.0k
lines
✓ 516 pass
tests
pass
typecheck
1w
lint
Test results 516
compileProjectDsl: single-level resolution > resolves one project reference and attaches _compiled
[23.78ms]
compileProjectDsl: single-level resolution > preserves declared name/description/overrides alongside _compiled
[6.04ms]
compileProjectDsl: single-level resolution > regular subagents pass through unchanged
[9.94ms]
compileProjectDsl: single-level resolution > no project references: returns input unchanged
[1.76ms]
compileProjectDsl: single-level resolution > hasProjectReferences true when any ref exists
[2.92ms]
compileProjectDsl: recursion > A → B → C with paths relative to each parent's root
[5.39ms]
compileProjectDsl: recursion > silo boundary: nested ref at project:/data is NOT resolved against parent root
[9.64ms]
compileProjectDsl: cycle detection > unbounded chain is blocked at the depth limit
[9.65ms]
compileProjectDsl: cycle detection > diamond (A → B and A → C, pointing to distinct D's) is NOT flagged
[14.88ms]
compileProjectDsl: depth limit > aborts at the default depth limit (16)
[53.10ms]
compileProjectDsl: depth limit > custom maxDepth=2 stops at depth 2
[10.28ms]
compileProjectDsl: depth limit > depth equal to limit is permitted
[9.22ms]
compileProjectDsl: overrides > simple top-level override of primary.model
[4.19ms]
compileProjectDsl: overrides > nested .local.yaml is applied BEFORE the parent's overrides
[4.65ms]
compileProjectDsl: failure modes > missing nested project.yaml yields nested_project_missing diagnostic
[2.40ms]
compileProjectDsl: failure modes > invalid nested project.yaml yields a compile diagnostic
[5.36ms]
compileProjectDsl: failure modes > diagnostic path traces to the offending map key
[2.83ms]
compileProjectDsl: failure modes > warnings from nested cage:disabled bubble up with map-key prefix
[2.89ms]
compileProjectDsl: injectable readFile > uses provided readFile in place of node:fs
[1.66ms]
resolveCompaction > returns parent when child is undefined
[0.040ms]
resolveCompaction > child fields override parent fields
[0.050ms]
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.030ms]
resolveCompaction > all child fields specified produces fully overridden result
[0.050ms]
compileProjectDsl: compaction materialization > agent with no compaction gets DEFAULT_COMPACTION materialized
[1.01ms]
compileProjectDsl: compaction materialization > primary compaction is materialized with resolved defaults
[1.42ms]
compileProjectDsl: compaction materialization > subagent inherits parent compaction when unset
[5.78ms]
compileProjectDsl: compaction materialization > subagent overrides specific fields, inherits the rest
[2.43ms]
compileProjectDsl: compaction materialization > three-level inheritance: grandchild inherits from child override
[2.41ms]
compileProjectDsl: compaction materialization > project-reference boundary resets compaction to default
[2.97ms]
compileProjectDsl: compaction materialization > nested project with its own compaction uses that, not parent's
[2.44ms]
compileProjectDsl: compaction materialization > plugins pass through unchanged during compilation
[1.95ms]
DEFAULT_COMPACTION defaults (ADR-0024 amendment §7) > includes summarize block with default prompt path
[0.030ms]
DEFAULT_COMPACTION defaults (ADR-0024 amendment §7) > summarize block omits model (agent uses its own)
[0.020ms]
CompactionSummarizeSchema: optional model and prompt > accepts summarize block with prompt only (no model)
[0.940ms]
CompactionSummarizeSchema: optional model and prompt > accepts summarize block with model only (no prompt)
[0.730ms]
CompactionSummarizeSchema: optional model and prompt > accepts summarize block with no model and no prompt
[0.740ms]
worked examples > minimal.yaml validates successfully
[1.35ms]
worked examples > single-subagent.yaml validates successfully
[2.38ms]
worked examples > multi-subagent.yaml validates successfully
[3.18ms]
worked examples > network-allowlist.yaml validates successfully
[2.88ms]
worked examples > with-plugins.yaml validates successfully
[2.01ms]
worked examples > insecure.yaml validates successfully
[2.31ms]
worked examples > portable.yaml validates successfully
[2.72ms]
worked examples > insecure.yaml emits cage:disabled warning
[2.27ms]
worked examples > minimal.yaml has no subagents on primary
[0.780ms]
worked examples > with-plugins.yaml has plugins
[1.98ms]
schema: accepts valid > minimal valid project
[0.660ms]
schema: accepts valid > project with description
[0.700ms]
schema: accepts valid > primary with parameters
[0.780ms]
schema: accepts valid > subagent with cage: disabled is valid
[1.43ms]
schema: accepts valid > cage with all optional fields
[1.60ms]
schema: accepts valid > model alias: various valid names
[2.45ms]
schema: accepts valid > project slug: various valid slugs
[1.86ms]
schema: accepts valid > system_prompt: URI-prefixed paths accepted
[1.87ms]
schema: accepts valid > system_prompt: naked path rejected
[1.03ms]
schema: accepts valid > plugins with all fields
[0.860ms]
schema: accepts valid > primary with per-agent tools
[0.920ms]
schema: rejects invalid > missing version
[0.720ms]
schema: rejects invalid > version: 2 is rejected
[0.750ms]
schema: rejects invalid > version: "1" (string) is rejected
[0.690ms]
schema: rejects invalid > missing project
[0.650ms]
schema: rejects invalid > missing primary
[0.550ms]
schema: rejects invalid > unknown top-level field
[0.870ms]
schema: rejects invalid > model alias with colon is rejected
[0.730ms]
schema: rejects invalid > reserved model alias rejected
[3.21ms]
schema: rejects invalid > reserved subagent name rejected
[3.80ms]
schema: rejects invalid > absolute path in system_prompt rejected
[0.710ms]
schema: rejects invalid > path with .. escape rejected
[0.670ms]
schema: rejects invalid > absolute path in cage.fs rejected
[1.38ms]
schema: rejects invalid > description over 280 chars rejected
[0.780ms]
schema: rejects invalid > description at 280 chars accepted
[0.660ms]
schema: rejects invalid > more than 64 subagents rejected
[28.17ms]
schema: rejects invalid > cage value that is neither object nor disabled
[1.04ms]
schema: rejects invalid > cage.state invalid enum value
[0.830ms]
schema: rejects invalid > cage.seccomp invalid enum value
[0.780ms]
schema: rejects invalid > cage.limits.memory_mb below minimum
[0.770ms]
schema: rejects invalid > mount.mode invalid value
[0.760ms]
schema: rejects invalid > YAML syntax error throws DslError
[0.480ms]
schema: rejects invalid > top-level subagents key rejected (ADR-0022)
[0.520ms]
schema: rejects invalid > top-level interconnect key rejected (ADR-0022)
[0.520ms]
schema: rejects invalid > top-level cage_defaults key rejected (ADR-0022)
[0.490ms]
schema: rejects invalid > top-level tools key rejected (ADR-0022)
[0.440ms]
cross-reference validation > root agent cage must be disabled
[0.480ms]
cross-reference validation > principal_scope: root-only tool on non-root agent rejected
[0.720ms]
cross-reference validation > principal_scope: root-only tool on root agent accepted
[0.400ms]
cross-reference validation > tool-name collision: project-ref name override matches sibling map key
[0.980ms]
cross-reference validation > no errors when subagents have unique names
[0.890ms]
error messages > errors include doc links
[0.450ms]
error messages > unknown field includes did-you-mean
[0.460ms]
error messages > DslError has diagnostics array
[0.510ms]
error messages > YAML syntax error has line info
[0.780ms]
error messages > file path appears in diagnostic when provided
[0.440ms]
utilities > parse() returns data + warnings separately
[0.470ms]
utilities > validateCrossRefs returns empty for valid DSL
[0.450ms]
tasks block: accepts valid > project without tasks is valid
[0.440ms]
tasks block: accepts valid > project with empty tasks map is valid
[0.510ms]
tasks block: accepts valid > single named task with all fields
[0.740ms]
tasks block: accepts valid > minimal task (command only)
[0.530ms]
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.560ms]
tasks block: accepts valid > task name with hyphens and underscores
[1.45ms]
tasks block: rejects invalid > reserved task name: adhoc
[0.550ms]
tasks block: rejects invalid > reserved task name: all
[0.510ms]
tasks block: rejects invalid > reserved task name: new
[0.480ms]
tasks block: rejects invalid > task name starting with digit rejected
[0.510ms]
tasks block: rejects invalid > task name with uppercase rejected
[0.510ms]
tasks block: rejects invalid > single-char task name rejected
[0.510ms]
tasks block: rejects invalid > task missing command rejected
[0.510ms]
tasks block: rejects invalid > task with empty command rejected
[0.550ms]
tasks block: rejects invalid > task description over 280 chars rejected
[0.630ms]
tasks block: rejects invalid > task with absolute cwd rejected
[0.540ms]
tasks block: rejects invalid > task with .. cwd rejected
[0.560ms]
tasks block: rejects invalid > task with unknown field rejected (strict mode)
[0.580ms]
tasks block: rejects invalid > more than 64 tasks rejected
[2.23ms]
tasks block: rejects invalid > task env with non-string value rejected
[0.470ms]
tasks block: cross-reference validation > unique task names pass validation
[0.430ms]
workflows block: accepts valid > project without workflows is valid
[0.350ms]
workflows block: accepts valid > project with empty workflows map is valid
[0.430ms]
workflows block: accepts valid > single workflow with all fields
[2.05ms]
workflows block: accepts valid > minimal workflow (required fields only)
[0.680ms]
workflows block: accepts valid > workflow name with dots, hyphens, underscores
[2.07ms]
workflows block: accepts valid > multiple workflows
[0.950ms]
workflows block: accepts valid > workflow with empty inputs map
[0.550ms]
workflows block: accepts valid > input required defaults to true
[0.630ms]
workflows block: rejects invalid > reserved workflow name: adhoc
[0.560ms]
workflows block: rejects invalid > reserved workflow name: all
[0.570ms]
workflows block: rejects invalid > workflow name starting with digit rejected
[0.570ms]
workflows block: rejects invalid > workflow name with uppercase rejected
[0.580ms]
workflows block: rejects invalid > single-char workflow name rejected
[0.570ms]
workflows block: rejects invalid > workflow missing description rejected
[0.580ms]
workflows block: rejects invalid > workflow missing steps rejected (ADR-0038)
[0.530ms]
workflows block: rejects invalid > workflow with empty steps array rejected (ADR-0038)
[0.550ms]
workflows block: rejects invalid > workflow missing inputs rejected
[0.610ms]
workflows block: rejects invalid > workflow missing tools rejected
[0.620ms]
workflows block: rejects invalid > workflow description over 280 chars rejected
[0.690ms]
workflows block: rejects invalid > workflow with system_prompt rejected (ADR-0038 removed field)
[1.00ms]
workflows block: rejects invalid > workflow input with invalid type rejected
[0.720ms]
workflows block: rejects invalid > workflow input missing type rejected
[0.770ms]
workflows block: rejects invalid > workflow with unknown field rejected (strict)
[0.810ms]
workflows block: rejects invalid > workflow input with unknown field rejected (strict)
[0.840ms]
workflows block: rejects invalid > workflow tools with unknown field rejected (strict)
[0.730ms]
workflows block: rejects invalid > workflow invokable_by with invalid value rejected
[0.730ms]
workflows block: rejects invalid > workflow timeout_seconds zero rejected
[0.740ms]
workflows block: rejects invalid > more than 64 workflows rejected
[13.97ms]
workflow steps: accepts valid > agent step with all fields
[1.52ms]
workflow steps: accepts valid > confirm step with message and show
[0.790ms]
workflow steps: accepts valid > task step referencing project task
[0.890ms]
workflow steps: accepts valid > multi-step workflow with bindings across steps
[0.970ms]
workflow steps: accepts valid > step with on_fail: continue is accepted
[0.740ms]
workflow steps: accepts valid > 32 steps accepted (max)
[2.16ms]
workflow steps: accepts valid > workflow-level model override accepted (ADR-0038)
[0.720ms]
workflow steps: accepts valid > workflow without model inherits (model undefined, ADR-0038)
[0.710ms]
workflow steps: accepts valid > per-step model override on agent step accepted (ADR-0038)
[0.800ms]
workflow steps: accepts valid > workflow-level and step-level model overrides coexist (ADR-0038)
[0.830ms]
workflow steps: rejects invalid > step with invalid kind rejected
[0.840ms]
workflow steps: rejects invalid > step missing id rejected
[0.730ms]
workflow steps: rejects invalid > step missing kind rejected
[0.750ms]
workflow steps: rejects invalid > duplicate step IDs rejected
[0.740ms]
workflow steps: rejects invalid > 33 steps rejected (over max 32)
[2.18ms]
workflow steps: rejects invalid > agent step missing prompt rejected
[0.570ms]
workflow steps: rejects invalid > agent step with naked prompt path rejected
[0.630ms]
workflow steps: rejects invalid > confirm step missing message rejected
[0.570ms]
workflow steps: rejects invalid > task step missing task rejected
[0.580ms]
workflow steps: rejects invalid > step output with file type rejected
[0.660ms]
workflow steps: rejects invalid > more than 8 outputs per step rejected
[0.880ms]
workflow steps: rejects invalid > more than 16 with entries per step rejected
[0.890ms]
workflow steps: rejects invalid > step id too short (1 char) rejected
[0.640ms]
workflow steps: rejects invalid > step id with uppercase rejected
[0.740ms]
workflow steps: rejects invalid > step with unknown field rejected (strict)
[0.830ms]
workflow steps: rejects invalid > step on_fail naming a non-verb non-step target is a cross-ref error (ADR-0048: on_fail is no longer an enum)
[0.800ms]
workflow steps: rejects invalid > confirm step show over 8 entries rejected
[0.940ms]
workflow steps: rejects invalid > model on confirm step rejected (ADR-0038)
[0.840ms]
workflow steps: rejects invalid > model on task step rejected (ADR-0038)
[0.870ms]
binding syntax validation > valid input binding accepted
[0.870ms]
binding syntax validation > valid step output binding accepted
[0.930ms]
binding syntax validation > mixed text and bindings accepted
[1.00ms]
binding syntax validation > unclosed binding rejected at schema level
[0.820ms]
binding syntax validation > empty binding rejected at schema level
[0.850ms]
binding syntax validation > unknown binding root rejected at schema level
[0.800ms]
binding syntax validation > binding without output segment rejected
[2.24ms]
workflow steps: cross-reference validation > unknown input reference produces error
[1.07ms]
workflow steps: cross-reference validation > forward step reference is statically legal (ADR-0048 §6 withdraws step_ref_forward; runtime-checked instead)
[3.93ms]
workflow steps: cross-reference validation > reference to a step that exists nowhere produces step_ref_unknown_step
[0.720ms]
workflow steps: cross-reference validation > undeclared output reference produces error
[0.750ms]
workflow steps: cross-reference validation > dependency on an on_fail:continue step is statically legal (ADR-0048 §6 withdraws step_ref_skippable)
[0.760ms]
workflow steps: cross-reference validation > file input in confirm binding produces error
[0.640ms]
workflow steps: cross-reference validation > non-file input in confirm binding is valid
[0.580ms]
workflow steps: cross-reference validation > task step referencing unknown task produces error
[0.640ms]
workflow steps: cross-reference validation > task step referencing long_running task produces error
[0.650ms]
workflow steps: cross-reference validation > step tools not subset of workflow tools produces error
[0.650ms]
workflow steps: cross-reference validation > step tools that are a subset pass validation
[0.600ms]
workflow steps: cross-reference validation > step tools matching namespace glob pass validation
[0.610ms]
workflow steps: cross-reference validation > valid backward step reference passes
[0.670ms]
workflow steps: cross-reference validation > step without on_fail defaults to abort (no skippable error)
[0.680ms]
workflow steps ADR-0048: routing schema > on_success and on_fail accept reserved verbs
[0.750ms]
workflow steps ADR-0048: routing schema > on_success and on_fail accept a step id as target
[0.810ms]
workflow steps ADR-0048: routing schema > a step id equal to a reserved routing word is rejected
[0.710ms]
workflow steps ADR-0048: routing schema > routing to a target that is neither a verb nor a real step id produces step_route_unknown
[0.640ms]
workflow steps ADR-0048: bounded loops > max_runs accepts a positive integer
[0.640ms]
workflow steps ADR-0048: bounded loops > max_runs below 1 is rejected
[0.660ms]
workflow steps ADR-0048: bounded loops > a back-edge target without max_runs produces step_loop_unbounded
[0.730ms]
workflow steps ADR-0048: bounded loops > a back-edge target with max_runs is valid
[0.660ms]
workflow steps ADR-0048: kind compare > a numeric compare step parses with a/b/comparison and routing
[0.820ms]
workflow steps ADR-0048: kind compare > compare with the in verb accepts a YAML array for b
[0.780ms]
workflow steps ADR-0048: kind compare > an unknown comparison verb is rejected
[0.680ms]
workflow steps ADR-0048: kind compare > compare may not carry timeout_seconds
[0.720ms]
workflow steps ADR-0048: task parameters and implicit outputs > a task step accepts a with array of argv tokens
[0.680ms]
workflow steps ADR-0048: task parameters and implicit outputs > a downstream agent may bind a task step's implicit stdout/stderr/exit_code
[0.700ms]
workflow steps ADR-0048: task parameters and implicit outputs > binding a task output other than stdout/stderr/exit_code produces step_ref_undeclared_output
[0.690ms]
workflow steps ADR-0048: task parameters and implicit outputs > a task step still rejects an outputs block
[0.650ms]
workflow steps ADR-0048: run_count binding root > self.run_count and steps.<id>.run_count are bindable in an agent with block
[0.670ms]
workflow steps ADR-0048: run_count binding root > run_count referencing an unknown step produces step_ref_unknown_step
[0.620ms]
mcp block: accepts valid > minimal stdio server
[0.590ms]
mcp block: accepts valid > sse server with auth and headers
[0.640ms]
mcp block: accepts valid > stdio server with env and cwd
[0.490ms]
mcp block: accepts valid > null server removes entry (nullification)
[0.480ms]
mcp block: accepts valid > header auth with custom header name
[0.470ms]
mcp block: accepts valid > no-auth server (auth omitted)
[0.410ms]
mcp block: rejects invalid > stdio without command
[0.510ms]
mcp block: rejects invalid > sse without url
[0.430ms]
mcp block: rejects invalid > stdio with url (wrong transport fields)
[0.530ms]
mcp block: rejects invalid > sse with command (wrong transport fields)
[0.520ms]
mcp block: rejects invalid > header auth without secret_ref
[0.490ms]
mcp block: rejects invalid > invalid server key (uppercase)
[0.490ms]
mcp block: rejects invalid > invalid transport type
[0.460ms]
mcp block: rejects invalid > unknown field in server block
[0.490ms]
mcp block: rejects invalid > more than 16 servers
[1.19ms]
wiki: accepts valid > minimal wiki with path only
[0.400ms]
wiki: accepts valid > wiki with home override
[0.430ms]
wiki: accepts valid > wiki with config:/ scoped path
[0.380ms]
wiki: accepts valid > wiki with full git config
[0.470ms]
wiki: accepts valid > wiki with git block, all defaults
[0.410ms]
wiki: accepts valid > project without wiki is valid
[0.340ms]
wiki: rejects invalid > wiki with naked path (no URI prefix)
[0.490ms]
wiki: rejects invalid > wiki with path traversal
[0.410ms]
wiki: rejects invalid > wiki home with subdirectory path
[0.410ms]
wiki: rejects invalid > auto_push without auto_commit
[0.420ms]
wiki: rejects invalid > unknown field in wiki block
[0.410ms]
wiki: rejects invalid > unknown field in wiki.git block
[0.420ms]
wiki: rejects invalid > wiki missing required path
[0.420ms]
deepMergeDsl > scalars: overlay replaces base
[0.070ms]
deepMergeDsl > absent keys: base value passes through
[0.030ms]
deepMergeDsl > null: removes the key (nullification)
[0.040ms]
deepMergeDsl > objects: deep-merge recursively
[0.030ms]
deepMergeDsl > objects: deep-merge multiple levels
[0.030ms]
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.020ms]
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.030ms]
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.030ms]
parseUri > rejects naked path
[0.040ms]
parseUri > rejects absolute path
[0.030ms]
parseUri > rejects empty string
[0.030ms]
parseUri > rejects unknown prefix
[0.020ms]
parseUri > double-slash: second slash becomes part of path
[0.020ms]
parseUri > rejects .. escape at start
[0.020ms]
parseUri > rejects .. escape in middle
[0.020ms]
parseUri > rejects bare .. path
[0.020ms]
parseUri > allows segments containing .. in names
[0.020ms]
resolveUri > project:/ resolves to project root
[0.070ms]
resolveUri > config:/ resolves to .kaged/ under project root
[0.020ms]
resolveUri > project:/ single file
[0.030ms]
resolveUri > config:/ deeply nested
[0.030ms]
resolveUri > throws on invalid URI
[0.030ms]
shadowPath > config:/ with extension inserts .local before ext
[0.060ms]
shadowPath > config:/ with .json extension
[0.030ms]
shadowPath > config:/ with .yaml extension
[0.010ms]
shadowPath > config:/ without extension appends .local
[0.020ms]
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.780ms]
loadProjectDsl > overlay merges: overrides primary.model
[0.620ms]
loadProjectDsl > overlay merges: adds description
[0.520ms]
loadProjectDsl > overlay rejects version override
[0.620ms]
loadProjectDsl > overlay rejects project override
[0.440ms]
loadProjectDsl > overlay merges: deep-merges primary parameters
[0.570ms]
loadProjectDsl > overlay merges: subagents record deep-merges (empty overlay preserves base)
[0.940ms]
loadProjectDsl > overlay merges: subagent nullified via null value
[0.650ms]
loadProjectDsl > overlay YAML syntax error throws DslError
[0.470ms]
loadProjectDsl > base YAML syntax error throws DslError
[0.190ms]
loadProjectDsl > merged result that fails schema throws DslError
[0.590ms]
loadProjectDsl > overlay file path appears in forbidden-key error
[0.260ms]
loadProjectDsl > overlay with cage:disabled produces warning
[0.910ms]
PrefixedPathSchema integration > project:/ in system_prompt accepted
[0.410ms]
PrefixedPathSchema integration > config:/ in system_prompt accepted
[0.420ms]
PrefixedPathSchema integration > naked path in system_prompt rejected
[0.470ms]
PrefixedPathSchema integration > project:/ in task cwd accepted
[0.500ms]
PrefixedPathSchema integration > naked path in task cwd rejected
[0.540ms]
ToolOverrideSchema integration > tools with overrides accepted
[0.540ms]
ToolOverrideSchema integration > tools with null value (disable) accepted
[0.450ms]
ToolOverrideSchema integration > tools with parameters accepted
[0.500ms]
ToolOverrideSchema integration > project without tools is valid
[0.410ms]
ToolOverrideSchema integration > overlay merges tools
[0.590ms]
per-agent plugins: accepts valid > primary with a single plugin override (registry + per-agent)
[1.20ms]
per-agent plugins: accepts valid > primary with enabled: true override
[0.630ms]
per-agent plugins: accepts valid > minimal per-agent override (only enabled)
[0.570ms]
per-agent plugins: accepts valid > multiple plugins on same agent
[0.730ms]
per-agent plugins: accepts valid > pre_compact hook accepted
[0.600ms]
per-agent plugins: accepts valid > subagent with its own plugin override
[0.920ms]
per-agent plugins: accepts valid > registry entry with source field
[0.540ms]
per-agent plugins: accepts valid > registry entry with git: source
[0.530ms]
per-agent plugins: accepts valid > registry entry with project: source
[0.540ms]
per-agent plugins: accepts valid > registry entry with config: source
[0.530ms]
per-agent plugins: rejects invalid > unknown field in per-agent plugin declaration
[0.680ms]
per-agent plugins: rejects invalid > invalid hook name rejected
[0.630ms]
per-agent plugins: rejects invalid > plugin slot name starting with digit rejected
[0.590ms]
per-agent plugins: rejects invalid > plugin slot name with uppercase rejected
[0.570ms]
per-agent plugins: rejects invalid > more than 16 plugins per agent rejected
[1.45ms]
per-agent plugins: rejects invalid > registry entry without package rejected
[0.590ms]
per-agent plugins: rejects invalid > invalid source prefix rejected
[0.540ms]
per-agent plugins: rejects invalid > per-agent package field rejected (belongs in registry)
[0.640ms]
per-agent plugins: rejects invalid > per-agent source field rejected (belongs in registry)
[0.590ms]
per-agent plugins: cross-ref validation > per-agent slot not in project registry → error
[0.150ms]
per-agent plugins: cross-ref validation > per-agent slot in project registry → no error
[0.040ms]
per-agent plugins: cross-ref validation > subagent per-agent slot not in project registry → error
[0.090ms]
per-agent plugins: cross-ref validation > primary-only hooks on subagent → warning
[0.080ms]
per-agent plugins: cross-ref validation > pre_compact on subagent → no warning
[0.050ms]
per-agent plugins: cross-ref validation > primary-only hooks on root agent → no warning
[0.040ms]
per-agent compaction: accepts valid > compaction with drop strategy (default)
[0.550ms]
per-agent compaction: accepts valid > compaction with summarize strategy
[0.590ms]
per-agent compaction: accepts valid > compaction with delegate strategy
[0.650ms]
per-agent compaction: accepts valid > compaction with checkpoint strategy
[0.570ms]
per-agent compaction: accepts valid > compaction with auto_resume_timeout_sec: null
[0.490ms]
per-agent compaction: accepts valid > compaction with always_keep predicates
[0.560ms]
per-agent compaction: accepts valid > minimal compaction (no fields = all defaults)
[0.450ms]
per-agent compaction: accepts valid > compaction on subagent
[0.790ms]
per-agent compaction: rejects invalid > invalid strategy rejected
[0.510ms]
per-agent compaction: rejects invalid > upper_threshold > 1.0 rejected
[0.530ms]
per-agent compaction: rejects invalid > lower_threshold < 0 rejected
[0.530ms]
per-agent compaction: rejects invalid > unknown field in compaction rejected
[0.580ms]
per-agent compaction: rejects invalid > unknown field in summarize sub-block rejected
[0.580ms]
per-agent compaction: rejects invalid > summarize model must be a valid alias
[0.560ms]
per-agent compaction: rejects invalid > summarize prompt must use URI prefix
[0.540ms]
per-agent compaction: rejects invalid > delegate fallback_strategy rejects checkpoint
[0.550ms]
per-agent compaction: cross-ref validation > lower_threshold >= upper_threshold → error
[0.120ms]
per-agent compaction: cross-ref validation > lower_threshold == upper_threshold → error
[0.060ms]
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.030ms]
per-agent compaction: cross-ref validation > strategy summarize without summarize block → error
[0.040ms]
per-agent compaction: cross-ref validation > strategy delegate without delegate block → error
[0.040ms]
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.050ms]
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.26ms]
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.100ms]
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.040ms]
resolveRootTools: no overrides > returns only default-enabled tools from available list
[0.140ms]
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.160ms]
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.040ms]
resolveRootTools: single layer opt-in > empty object has no effect
[0.030ms]
resolveRootTools: single layer opt-in > overrides for unknown tools are ignored
[0.020ms]
resolveRootTools: single layer opt-in > multiple tools enabled in one layer
[0.030ms]
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.040ms]
resolveRootTools: layered overrides > operator enables, project has empty object = stays enabled
[0.030ms]
resolveRootTools: layered overrides > operator enables disabled-by-default, project disables default-enabled
[0.060ms]
resolveRootTools: layered overrides > non-overlapping layers compose correctly
[0.040ms]
compileProjectDsl: resolvedRootTools > resolvedRootTools is null when availableTools not provided
[0.900ms]
compileProjectDsl: resolvedRootTools > resolvedRootTools returns only default-enabled tools when no overrides
[0.590ms]
compileProjectDsl: resolvedRootTools > operator overrides can enable disabled-by-default tools
[0.610ms]
compileProjectDsl: resolvedRootTools > operator overrides can disable default-enabled tools
[0.550ms]
compileProjectDsl: resolvedRootTools > DSL primary.tools applied as second layer
[0.640ms]
compileProjectDsl: resolvedRootTools > DSL overrides take precedence over operator overrides
[0.640ms]
compileProjectDsl: resolvedRootTools > operator disables + DSL re-enables = tool is enabled
[0.570ms]
compileProjectDsl: resolvedRootTools > materialized tools reflect default-disabled state
[0.540ms]
compileProjectDsl: recursive tool materialization > inline subagent gets materialized tools with nothing enabled by default
[0.900ms]
compileProjectDsl: recursive tool materialization > inline subagent tools opt-in via its own tools block
[0.940ms]
compileProjectDsl: recursive tool materialization > deeply nested subagent gets materialized tools
[1.22ms]
compileProjectDsl: recursive tool materialization > root agent still gets kaged.* enabled by default
[0.850ms]
compileProjectDsl: recursive tool materialization > operator overrides apply to subagents too
[0.950ms]
compileProjectDsl: recursive tool materialization > no materialization when availableTools not provided
[0.830ms]
project references: accepts valid > minimal project reference (path only)
[1.21ms]
project references: accepts valid > project reference with name override
[0.810ms]
project references: accepts valid > project reference with description override
[0.770ms]
project references: accepts valid > project reference with overrides block (opaque to parser)
[0.830ms]
project references: accepts valid > project reference with all four fields
[0.810ms]
project references: accepts valid > multiple project references coexist
[1.29ms]
project references: accepts valid > project reference coexists with regular subagent
[1.16ms]
project references: accepts valid > project reference with nested deeper path
[0.690ms]
project references: rejects invalid > config:/ scheme is rejected
[0.800ms]
project references: rejects invalid > git:/ scheme is rejected (deferred)
[1.45ms]
project references: rejects invalid > https:/ scheme is rejected (deferred)
[0.690ms]
project references: rejects invalid > file:/ scheme is rejected
[0.690ms]
project references: rejects invalid > naked path (no prefix) — parser treats as AgentSpec and fails
[0.690ms]
project references: rejects invalid > '..' escape in path is rejected
[0.660ms]
project references: rejects invalid > empty path portion is rejected
[0.720ms]
project references: rejects invalid > double-slash (project://) is rejected
[0.720ms]
project references: rejects invalid > mixed shape: path: + cage: is rejected
[0.830ms]
project references: rejects invalid > mixed shape: path: + model: is rejected
[0.740ms]
project references: rejects invalid > mixed shape: path: + system_prompt: is rejected
[0.740ms]
project references: rejects invalid > unknown field on project reference is rejected
[0.730ms]
project references: rejects invalid > name override violating subagent-name pattern is rejected
[0.700ms]
project references: rejects invalid > name override using reserved name is rejected
[1.95ms]
project references: rejects invalid > description exceeding 280 chars is rejected
[0.740ms]
project references: rejects invalid > overrides containing 'version' is rejected
[0.710ms]
project references: rejects invalid > overrides containing 'project' is rejected
[0.700ms]
project references: rejects invalid > overrides is not a plain object (string) — rejected
[0.690ms]
project references: cross-reference validation > a project-ref's `name:` override colliding with another map key is an error
[1.02ms]
project references: cross-reference validation > two project-refs whose `name:` overrides match each other is an error
[0.870ms]
project references: cross-reference validation > two project-refs at distinct map keys without name-collision validate
[0.860ms]
project references: cross-reference validation > a project-ref's `name:` matching its own map key is fine (redundant but valid)
[0.670ms]
project references: warnings > project references do not produce cage:disabled warnings
[0.650ms]
project references: warnings > cage:disabled warning still fires on adjacent regular subagent
[0.960ms]
project references: type guard > isProjectReference correctly discriminates
[1.10ms]
PromptPathSchema: accepts valid > single prefixed path
[1.64ms]
PromptPathSchema: accepts valid > single config:/ path
[0.060ms]
PromptPathSchema: accepts valid > array of one path
[0.070ms]
PromptPathSchema: accepts valid > array of multiple paths
[0.050ms]
PromptPathSchema: accepts valid > mixed project:/ and config:/ in array
[0.030ms]
PromptPathSchema: rejects invalid > empty string
[0.090ms]
PromptPathSchema: rejects invalid > naked path (no URI prefix)
[0.030ms]
PromptPathSchema: rejects invalid > empty array
[0.040ms]
PromptPathSchema: rejects invalid > array with naked path
[0.040ms]
PromptPathSchema: rejects invalid > array with .. escape
[0.030ms]
PromptPathSchema: rejects invalid > number is rejected
[0.060ms]
PromptPathSchema: rejects invalid > array with empty string entry
[0.030ms]
normalizePromptPath > string becomes one-element array
[0.040ms]
normalizePromptPath > array passes through unchanged
[0.020ms]
composePromptContents > single entry is trimmed
[0.050ms]
composePromptContents > multiple entries joined with blank line
[0.020ms]
composePromptContents > entries are trimmed before joining
[0.020ms]
composePromptContents > empty array yields empty string
[0.010ms]
composePromptContents > single entry array — no double newline
[0.020ms]
composePromptContents > three entries produce two blank-line joins
[0.020ms]
DSL round-trip: compound prompts > primary system_prompt accepts string (backwards compat)
[0.660ms]
DSL round-trip: compound prompts > primary system_prompt accepts array
[0.490ms]
DSL round-trip: compound prompts > subagent system_prompt accepts array
[0.740ms]
DSL round-trip: compound prompts > workflow step prompt accepts array
[0.750ms]
DSL round-trip: compound prompts > compaction summarize prompt accepts array
[0.510ms]
DSL round-trip: compound prompts > compaction summarize prompt accepts single string
[0.440ms]
DSL round-trip: compound prompts > rejects naked path in system_prompt array
[0.470ms]
DSL round-trip: compound prompts > rejects empty array for system_prompt
[0.430ms]
ProjectGitRootSchema > accepts project:/ (project root)
[0.090ms]
ProjectGitRootSchema > accepts project:/packages/foo
[0.050ms]
ProjectGitRootSchema > accepts project:/models.dev
[0.030ms]
ProjectGitRootSchema > rejects config:/ scheme (not accepted in MVP)
[0.050ms]
ProjectGitRootSchema > rejects absolute host path
[0.030ms]
ProjectGitRootSchema > rejects bare relative path
[0.030ms]
ProjectGitRootSchema > rejects ../ traversal
[0.020ms]
ProjectGitRootSchema > rejects mid-path ../ traversal
[0.030ms]
ProjectGitRootSchema > rejects empty string
[0.050ms]
ProjectGitRootSchema > rejects git:/ scheme (not the same as the git: DSL field)
[0.030ms]
ProjectDslSchema git field — default behavior > omitted git field defaults to [project:/]
[0.660ms]
ProjectDslSchema git field — default behavior > explicit git: [project:/] is accepted
[0.470ms]
ProjectDslSchema git field — default behavior > explicit multi-root git list is accepted
[0.460ms]
ProjectDslSchema git field — rejection > empty git list is rejected
[0.520ms]
ProjectDslSchema git field — rejection > config:/ root is rejected
[0.420ms]
ProjectDslSchema git field — rejection > absolute path root is rejected
[0.430ms]
ProjectDslSchema git field — rejection > > 32 roots is rejected
[0.740ms]
ProjectDslSchema git field — rejection > ../ traversal in a root is rejected
[0.420ms]
ProjectDslSchema git field — strict object > unknown field git_foo is rejected (strict)
[0.420ms]
ProjectDslSchema git field — strict object > did-you-mean hint includes git for git_x typo
[0.420ms]
parseSkillFile > parse valid file with all fields
[0.650ms]
parseSkillFile > parse file without name field — uses fallback
[0.130ms]
parseSkillFile > reject missing description
[0.310ms]
parseSkillFile > reject empty description
[0.120ms]
parseSkillFile > reject description over 280 chars
[0.180ms]
parseSkillFile > accept description exactly 280 chars
[0.130ms]
parseSkillFile > handle unknown frontmatter fields
[0.130ms]
parseSkillFile > handle file without frontmatter
[0.060ms]
parseSkillFile > handle empty body
[0.080ms]
parseSkillFile > handle malformed YAML frontmatter
[0.230ms]
parseSkillFile > reject invalid name pattern
[0.160ms]
parseSkillFile > frontmatter name overrides fallback
[0.110ms]
parseSkillFile > globs parsed as array
[0.160ms]
skillGlobMatch > wildcard matches everything
[0.110ms]
skillGlobMatch > exact match
[0.070ms]
skillGlobMatch > prefix glob
[0.050ms]
skillGlobMatch > multiple patterns — any match wins
[0.030ms]
skillGlobMatch > empty patterns — no match
[0.010ms]
discoverSkills > discover single skill
[0.930ms]
discoverSkills > discover multiple skills — sorted alphabetically
[0.770ms]
discoverSkills > no skills directory — returns empty
[0.110ms]
discoverSkills > missing SKILL.md — warning + skip
[0.120ms]
discoverSkills > parse error — warning + skip
[0.370ms]
discoverSkills > duplicate skill name — warning + skip duplicate
[0.360ms]
discoverSkills > glob filter — only matching skills
[0.450ms]
discoverSkills > non-directory entry is skipped
[0.230ms]
buildSkillCatalog > returns null for empty array
[0.060ms]
buildSkillCatalog > builds catalog with skills
[0.070ms]
buildSkillCatalog > excludes hidden skills
[0.040ms]
formatSkillActivation > formats with body and path hint
[0.080ms]
formatSkillActivation > includes skill_resources when resources provided
[0.040ms]
formatSkillActivation > omits skill_resources when no resources
[0.030ms]
DSL skills field (ADR-0044) > skills defaults to ['*'] when omitted
[0.750ms]
DSL skills field (ADR-0044) > skills accepts explicit glob patterns
[0.530ms]
DSL skills field (ADR-0044) > skills accepts ['*'] explicitly
[0.490ms]
DSL skills field (ADR-0044) > skills accepts empty array
[0.420ms]
DSL skills field (ADR-0044) > skills on subagent
[0.810ms]
DSL skills field (ADR-0044) > skills rejects non-string array elements
[0.500ms]