@hegemonart/get-design-done 1.14.4 → 1.14.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.claude-plugin/marketplace.json +7 -2
  2. package/.claude-plugin/plugin.json +6 -1
  3. package/CHANGELOG.md +106 -0
  4. package/README.md +17 -0
  5. package/agents/design-executor.md +41 -0
  6. package/agents/design-figma-writer.md +61 -1
  7. package/agents/design-verifier.md +2 -2
  8. package/connections/connections.md +2 -0
  9. package/connections/figma.md +10 -0
  10. package/connections/preview.md +9 -5
  11. package/hooks/budget-enforcer.js +2 -2
  12. package/hooks/gdd-bash-guard.js +49 -0
  13. package/hooks/gdd-decision-injector.js +196 -0
  14. package/hooks/gdd-mcp-circuit-breaker.js +140 -0
  15. package/hooks/gdd-protected-paths.js +114 -0
  16. package/hooks/hooks.json +36 -0
  17. package/package.json +1 -1
  18. package/reference/STATE-TEMPLATE.md +10 -1
  19. package/reference/config-schema.md +29 -0
  20. package/reference/cycle-handoff-preamble.md +22 -0
  21. package/reference/figma-sandbox.md +19 -0
  22. package/reference/mcp-budget.default.json +13 -0
  23. package/reference/meta-rules.md +66 -0
  24. package/reference/protected-paths.default.json +18 -0
  25. package/reference/registry.json +34 -0
  26. package/reference/registry.schema.json +52 -0
  27. package/reference/retrieval-contract.md +30 -0
  28. package/reference/schemas/mcp-budget.schema.json +21 -0
  29. package/reference/schemas/protected-paths.schema.json +19 -0
  30. package/reference/shared-preamble.md +6 -57
  31. package/scripts/aggregate-agent-metrics.js +9 -0
  32. package/scripts/build-intel.cjs +20 -0
  33. package/scripts/injection-patterns.cjs +42 -1
  34. package/scripts/lib/blast-radius.cjs +97 -0
  35. package/scripts/lib/dangerous-patterns.cjs +118 -0
  36. package/scripts/lib/glob-match.cjs +57 -0
  37. package/scripts/lib/reference-registry.cjs +101 -0
  38. package/skills/connections/SKILL.md +477 -0
  39. package/skills/new-project/SKILL.md +1 -1
  40. package/skills/pause/SKILL.md +3 -0
  41. package/skills/progress/SKILL.md +12 -0
  42. package/skills/reflect/SKILL.md +2 -0
  43. package/skills/resume/SKILL.md +3 -0
  44. package/skills/scan/SKILL.md +8 -0
  45. package/skills/verify/SKILL.md +4 -3
@@ -0,0 +1,66 @@
1
+ # Meta-Rules (L0)
2
+
3
+ These rules are framework-invariant across the GDD pipeline. They do not change between cycles, phases, or tasks. Every agent imports `reference/shared-preamble.md`, which aggregates `reference/meta-rules.md` first.
4
+
5
+ **Tier: L0** (frozen prefix; stabilizes the Anthropic 5-min prompt-cache prefix across agent spawns — the churning L2 body below does NOT invalidate this).
6
+
7
+ ---
8
+
9
+ ## Required Reading Discipline
10
+
11
+ When the orchestrator's prompt contains a `<required_reading>` block, you MUST read every file it lists with the `Read` tool before taking any other action. Paths prefixed with `@` are file paths — pass them directly to `Read`. Skipping required reading is a hard violation: you will produce stale output that the downstream verifier catches, wasting a full spawn cycle.
12
+
13
+ ## Writes Protocol
14
+
15
+ Only write files declared in your frontmatter `writes:` list. Agents with `reads-only: true` must never call `Write` or `Edit` on any file. If the task appears to require writing outside your declared scope, stop and return a `<blocker>` in STATE.md rather than expanding your write surface.
16
+
17
+ If your agent runs in a phase that enforces atomic commits (most do), commit only files in your declared `writes:` list. Use the repo commit convention: `docs(phase-N-P): short imperative description` for documentation-class changes, `feat(phase-N-P): ...` for new capability, `fix(phase-N-P): ...` for bug fixes. Phase and plan numbers come from `.design/STATE.md` `phase:` and the invoking plan's frontmatter.
18
+
19
+ ## Deviation Handling
20
+
21
+ If an expected file is missing, a required reading entry fails to load, or the prompt references an artifact that contradicts STATE.md, **stop** before taking any destructive action. Return a structured blocker to STATE.md and terminate your response with your completion marker:
22
+
23
+ ```markdown
24
+ <blocker>
25
+ type: missing-artifact | stale-state | contract-violation
26
+ detail: <one sentence>
27
+ suggested-fix: <one sentence or leave blank>
28
+ </blocker>
29
+
30
+ ## {STAGE} COMPLETE
31
+ ```
32
+
33
+ ## Completion Markers
34
+
35
+ Valid completion markers per agent class (from `agents/README.md` §Completion Markers):
36
+ - Research agent → `## RESEARCH COMPLETE`
37
+ - Planning agent → `## PLANNING COMPLETE`
38
+ - Execution agent → `## EXECUTION COMPLETE`
39
+ - Verification agent → `## VERIFICATION COMPLETE`
40
+ - Stage-specific agents → the stage name: `## SCAN COMPLETE`, `## DISCOVER COMPLETE`, `## PLAN COMPLETE`, `## DESIGN COMPLETE`, `## VERIFY COMPLETE`.
41
+
42
+ The orchestrator detects failure by reading STATE.md for a `<blocker>`, not by the absence of a marker. Always emit the marker.
43
+
44
+ ## Context-Exhaustion & Budget Awareness
45
+
46
+ A PostToolUse hook at `hooks/context-exhaustion.js` watches your tool output for the string `<context-exhaustion>` in your response. If you determine you cannot finish the task in the remaining context, emit:
47
+
48
+ ```xml
49
+ <context-exhaustion>
50
+ reason: <one-sentence cause — e.g., "required_reading totals 47KB exceeding remaining context">
51
+ resume-hint: <one-sentence instruction for a resumption spawn>
52
+ </context-exhaustion>
53
+ ```
54
+
55
+ …before your completion marker. The hook captures this into STATE.md so the orchestrator can re-spawn you with a narrower scope. Do not guess when you're near exhaustion — only emit when a concrete obstacle (file too large to read, required diff too wide) forced the call.
56
+
57
+ A PreToolUse hook at `hooks/budget-enforcer.js` intercepts every `Task` spawn (including the one that invoked you). The hook may:
58
+ - **Short-circuit** your spawn with a cached result from `.design/cache-manifest.json` (transparent — you never run).
59
+ - **Downgrade** your tier to Haiku at the 80% per-task cap soft-threshold, silently (`auto_downgrade_on_cap: true` in `.design/budget.json`, D-03).
60
+ - **Hard-block** your spawn at the 100% per-task or per-phase cap with an actionable error (D-02).
61
+
62
+ Implication for you as the agent: **do not assume a specific model tier is live.** Your output must be correct whether you run on Haiku, Sonnet, or Opus. If a task genuinely requires reasoning density beyond Haiku, the `size_budget` + `default-tier` combination should have been set at authoring time so the router routes it correctly — the remedy is a frontmatter update (a Phase 11 reflector proposal), not a mid-run assumption.
63
+
64
+ ---
65
+
66
+ *Framework-invariant meta-rules. Aggregated by `reference/shared-preamble.md`.*
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "./schemas/protected-paths.schema.json",
3
+ "version": 1,
4
+ "description": "Paths the plugin refuses to Edit/Write or mutate via Bash without an explicit user override. User additions in .design/config.json.protected_paths MERGE into this list — they can extend but cannot reduce the default set.",
5
+ "protected_paths": [
6
+ "reference/**",
7
+ ".design/archive/**",
8
+ ".design/config.json",
9
+ ".design/telemetry/**",
10
+ "skills/**",
11
+ "commands/**",
12
+ "hooks/**",
13
+ ".git/**",
14
+ ".claude/settings.json",
15
+ ".claude-plugin/plugin.json",
16
+ ".claude-plugin/marketplace.json"
17
+ ]
18
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "$schema": "./schemas/registry.schema.json",
3
+ "version": 1,
4
+ "generated_at": "2026-04-24T00:00:00.000Z",
5
+ "entries": [
6
+ { "name": "shared-preamble", "path": "reference/shared-preamble.md", "type": "preamble", "tier": "L0", "description": "L0 aggregator — imports meta-rules first and exposes Framework Identity + Ordering Convention + Pre-Warming" },
7
+ { "name": "meta-rules", "path": "reference/meta-rules.md", "type": "meta-rules", "tier": "L0", "description": "Framework-invariant rules: Required Reading Discipline, Writes Protocol, Deviation Handling, Completion Markers, Context-Exhaustion & Budget awareness" },
8
+ { "name": "cycle-handoff-preamble", "path": "reference/cycle-handoff-preamble.md", "type": "preamble", "description": "Framing prose for archived cycle material — read as reference, not as current request" },
9
+ { "name": "retrieval-contract", "path": "reference/retrieval-contract.md", "type": "preamble", "description": "3-layer search → metadata → full-doc protocol with per-row token-cost labels" },
10
+ { "name": "heuristics", "path": "reference/heuristics.md", "type": "heuristic", "tier": "L2", "description": "NNG heuristics + GDD-specific rubrics used by auditor / verifier" },
11
+ { "name": "anti-patterns", "path": "reference/anti-patterns.md", "type": "heuristic", "tier": "L2", "description": "Catalog of design anti-patterns surfaced during audit" },
12
+ { "name": "checklists", "path": "reference/checklists.md", "type": "heuristic", "tier": "L2", "description": "Per-category gate checklists" },
13
+ { "name": "accessibility", "path": "reference/accessibility.md", "type": "heuristic", "description": "WCAG + focus + keyboard-nav heuristics referenced by a11y-mapper" },
14
+ { "name": "debugger-philosophy", "path": "reference/debugger-philosophy.md", "type": "heuristic", "description": "Philosophy guide for systematic-debugging agents" },
15
+ { "name": "parallelism-rules", "path": "reference/parallelism-rules.md", "type": "heuristic", "description": "Rules for serial/parallel agent dispatch and Touches conflict detection" },
16
+ { "name": "priority-matrix", "path": "reference/priority-matrix.md", "type": "heuristic", "description": "Severity × Effort priority-score formula used by gap prioritizers" },
17
+ { "name": "project-skills-guide", "path": "reference/project-skills-guide.md", "type": "heuristic", "description": "Guide for authoring project-skill artifacts (sketch/spike wrap-ups)" },
18
+ { "name": "audit-scoring", "path": "reference/audit-scoring.md", "type": "output-contract", "description": "6-pillar + 7-category audit score rubric and output schema" },
19
+ { "name": "review-format", "path": "reference/review-format.md", "type": "output-contract", "description": "Format of code-review output documents" },
20
+ { "name": "authority-feeds", "path": "reference/authority-feeds.md", "type": "authority-feed", "description": "Whitelist of design-authority feed sources for the watcher" },
21
+ { "name": "motion", "path": "reference/motion.md", "type": "motion", "description": "Motion vocabulary seed (extended in Phase 18)" },
22
+ { "name": "typography", "path": "reference/typography.md", "type": "heuristic", "description": "Typography-system heuristics used by visual-hierarchy-mapper" },
23
+ { "name": "ai-native-tool-interface","path": "reference/ai-native-tool-interface.md","type": "surfaces", "description": "AI-native tool interface contract used across connections" },
24
+ { "name": "intel-schema", "path": "reference/intel-schema.md", "type": "schema", "description": "Schema documentation for .design/intel/ files" },
25
+ { "name": "config-schema", "path": "reference/config-schema.md", "type": "schema", "description": "Schema documentation for .design/config.json" },
26
+ { "name": "DEPRECATIONS", "path": "reference/DEPRECATIONS.md", "type": "schema", "description": "Deprecation records and redirect mappings" },
27
+ { "name": "STATE-TEMPLATE", "path": "reference/STATE-TEMPLATE.md", "type": "schema", "description": "Template for .design/STATE.md scaffolded by /gdd:scan" },
28
+ { "name": "model-prices", "path": "reference/model-prices.md", "type": "data", "description": "Anthropic model pricing table used by the telemetry aggregator" },
29
+ { "name": "model-tiers", "path": "reference/model-tiers.md", "type": "data", "description": "Per-agent default tier map and rationale" },
30
+ { "name": "figma-sandbox", "path": "reference/figma-sandbox.md", "type": "defaults", "description": "Four Figma plugin-sandbox pitfalls encoded as hard rules" },
31
+ { "name": "mcp-budget.default", "path": "reference/mcp-budget.default.json", "type": "defaults", "description": "Default MCP per-task budget (calls + consecutive-timeout thresholds)" },
32
+ { "name": "protected-paths.default", "path": "reference/protected-paths.default.json","type": "defaults", "description": "Default glob list the plugin refuses to Edit/Write/mutate-via-Bash without override" }
33
+ ]
34
+ }
@@ -0,0 +1,52 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://github.com/hegemonart/get-design-done/reference/schemas/registry.schema.json",
4
+ "title": "Reference Registry",
5
+ "description": "Typed index of every reference/*.md (and reference/*.json default) in the plugin. Enables agents to query by type instead of grep-hunting import strings. Round-trip enforced: every reference/*.md must appear in entries[], every entry must resolve to an existing file.",
6
+ "type": "object",
7
+ "required": ["version", "generated_at", "entries"],
8
+ "properties": {
9
+ "$schema": { "type": "string" },
10
+ "version": { "const": 1 },
11
+ "generated_at": { "type": "string", "format": "date-time" },
12
+ "entries": {
13
+ "type": "array",
14
+ "items": {
15
+ "type": "object",
16
+ "required": ["name", "path", "type"],
17
+ "properties": {
18
+ "name": { "type": "string", "minLength": 1, "pattern": "^[a-z0-9][a-z0-9-._]*$" },
19
+ "path": { "type": "string", "minLength": 1 },
20
+ "type": {
21
+ "type": "string",
22
+ "enum": [
23
+ "easing",
24
+ "taxonomy",
25
+ "preamble",
26
+ "output-contract",
27
+ "heuristic",
28
+ "schema",
29
+ "defaults",
30
+ "surfaces",
31
+ "motion",
32
+ "influences",
33
+ "meta-rules",
34
+ "data",
35
+ "authority-feed",
36
+ "principles",
37
+ "emotional-design",
38
+ "experience",
39
+ "palette",
40
+ "style-vocabulary"
41
+ ]
42
+ },
43
+ "tier": { "enum": ["L0", "L1", "L2"] },
44
+ "description": { "type": "string" },
45
+ "registered_at": { "type": "string", "format": "date-time" }
46
+ },
47
+ "additionalProperties": false
48
+ }
49
+ }
50
+ },
51
+ "additionalProperties": false
52
+ }
@@ -0,0 +1,30 @@
1
+ # Retrieval Contract — 3-Layer Search
2
+
3
+ When an agent or skill needs information from `.design/` artifacts or the `reference/` library, apply this protocol in order. Token economy matters — ladder from cheapest to most expensive.
4
+
5
+ ## Layer 1 — Search (~50–100 tokens per hit)
6
+
7
+ Open `reference/registry.json` or the intel index at `.design/intel/`. Read one row per candidate: `{name, path, type, tier, description}`. This is enough to decide whether to descend further.
8
+
9
+ - `list({type})` — everything tagged with a given type (e.g., `"heuristic"`, `"motion"`, `"preamble"`).
10
+ - `find(name)` — direct lookup for a specific reference file by short name.
11
+
12
+ ## Layer 2 — Metadata (~200–300 tokens per hit)
13
+
14
+ Open the candidate file's frontmatter + first 20 lines + heading outline. Decide if the full body is on the critical path.
15
+
16
+ ## Layer 3 — Full document (~500–1000+ tokens)
17
+
18
+ Read the file with the `Read` tool. Only do this when layers 1 and 2 confirmed the doc is load-bearing for the current task.
19
+
20
+ ## Token economy
21
+
22
+ A `/gdd:recall "term"` query that returns 5 Layer-1 hits ≈ 400 tokens. Opening all 5 full docs blind ≈ 4000 tokens. **Always ladder.** Skipping to Layer 3 is the #1 cause of context exhaustion in long pipeline runs.
23
+
24
+ ## FTS5 backend (Phase 19.5)
25
+
26
+ Layer 1 becomes `scripts/lib/design-search.cjs` — same protocol, same output shape, but backed by `.design/search.db` instead of grep. Agents do not need to change anything; the backend swap is transparent.
27
+
28
+ ---
29
+
30
+ *Imported by every skill that reads `.design/` artifacts: `/gdd:progress`, `/gdd:resume`, `/gdd:reflect`, `/gdd:pause`, `/gdd:recall` (Phase 19.5+), `/gdd:timeline` (Phase 19.5+). Tier: preamble. Phase: 14.5.*
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://github.com/hegemonart/get-design-done/reference/schemas/mcp-budget.schema.json",
4
+ "title": "MCP Budget",
5
+ "description": "Thresholds for the MCP circuit-breaker hook. Applies to use_figma / use_paper / use_pencil and any tracked_tools entry the user extends.",
6
+ "type": "object",
7
+ "required": ["version", "max_calls_per_task", "max_consecutive_timeouts"],
8
+ "properties": {
9
+ "$schema": { "type": "string" },
10
+ "version": { "const": 1 },
11
+ "description": { "type": "string" },
12
+ "max_calls_per_task": { "type": "integer", "minimum": 0, "maximum": 10000 },
13
+ "max_consecutive_timeouts": { "type": "integer", "minimum": 0, "maximum": 1000 },
14
+ "reset_on_success": { "type": "boolean" },
15
+ "tracked_tools": {
16
+ "type": "array",
17
+ "items": { "type": "string", "minLength": 1 }
18
+ }
19
+ },
20
+ "additionalProperties": false
21
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://github.com/hegemonart/get-design-done/reference/schemas/protected-paths.schema.json",
4
+ "title": "Protected Paths",
5
+ "description": "Glob list describing paths the plugin refuses to Edit/Write or mutate via destructive Bash. User additions MERGE with this default list; users cannot reduce the default set.",
6
+ "type": "object",
7
+ "required": ["version", "protected_paths"],
8
+ "properties": {
9
+ "$schema": { "type": "string" },
10
+ "version": { "const": 1 },
11
+ "description": { "type": "string" },
12
+ "protected_paths": {
13
+ "type": "array",
14
+ "minItems": 1,
15
+ "items": { "type": "string", "minLength": 1 }
16
+ }
17
+ },
18
+ "additionalProperties": false
19
+ }
@@ -1,6 +1,10 @@
1
1
  # GSD Agent Shared Preamble
2
2
 
3
- > **This file is imported via `@reference/shared-preamble.md` as the first line of every agent body in `agents/*.md`. Its placement is load-bearing for Anthropic's 5-minute prompt cache (see `reference/model-tiers.md` and phase 10.1 decision D-08 Layer A): because every agent opens with the identical preamble prefix, the second and subsequent agent spawns in a session pay `cached_input_per_1m` rates rather than full `input_per_1m` rates for these bytes. Do not inline this content into agent bodies — always import.**
3
+ > **This file is imported via `@reference/shared-preamble.md` as the first line of every agent body in `agents/*.md`. Its placement is load-bearing for Anthropic's 5-minute prompt cache (see `reference/model-tiers.md` and Phase 10.1 decision D-08 Layer A): because every agent opens with the identical preamble prefix, the second and subsequent agent spawns in a session pay `cached_input_per_1m` rates rather than full `input_per_1m` rates for these bytes. Do not inline this content into agent bodies — always import.**
4
+ >
5
+ > **As of Phase 14.5 this file is an aggregator.** The framework-invariant subsections (Required Reading Discipline, Writes Protocol, Deviation Handling, Completion Markers, Context-Exhaustion & Budget awareness) live in `reference/meta-rules.md` (tier L0) so the L2 heuristics/anti-patterns/checklists churn never invalidates the L0 prefix.
6
+
7
+ @reference/meta-rules.md
4
8
 
5
9
  ## Framework Identity
6
10
 
@@ -8,61 +12,6 @@ You are a GSD agent operating under the `get-design-done` plugin contract (see `
8
12
 
9
13
  You are one step in a pipeline. You do not own the pipeline. The orchestrator decides what runs next based on your output.
10
14
 
11
- ## Required Reading Discipline
12
-
13
- When the orchestrator's prompt contains a `<required_reading>` block, you MUST read every file it lists with the `Read` tool before taking any other action. Paths prefixed with `@` are file paths — pass them directly to `Read`. Skipping required reading is a hard violation: you will produce stale output that the downstream verifier catches, wasting a full spawn cycle.
14
-
15
- ## Writes Protocol
16
-
17
- Only write files declared in your frontmatter `writes:` list. Agents with `reads-only: true` must never call `Write` or `Edit` on any file. If the task appears to require writing outside your declared scope, stop and return a `<blocker>` in STATE.md rather than expanding your write surface.
18
-
19
- If your agent runs in a phase that enforces atomic commits (most do), commit only files in your declared `writes:` list. Use the repo commit convention: `docs(phase-N-P): short imperative description` for documentation-class changes, `feat(phase-N-P): ...` for new capability, `fix(phase-N-P): ...` for bug fixes. Phase and plan numbers come from `.design/STATE.md` `phase:` and the invoking plan's frontmatter.
20
-
21
- ## Deviation Handling
22
-
23
- If an expected file is missing, a required reading entry fails to load, or the prompt references an artifact that contradicts STATE.md, **stop** before taking any destructive action. Return a structured blocker to STATE.md and terminate your response with your completion marker:
24
-
25
- ```markdown
26
- <blocker>
27
- type: missing-artifact | stale-state | contract-violation
28
- detail: <one sentence>
29
- suggested-fix: <one sentence or leave blank>
30
- </blocker>
31
-
32
- ## {STAGE} COMPLETE
33
- ```
34
-
35
- Valid completion markers per agent class (from `agents/README.md` §Completion Markers):
36
- - Research agent → `## RESEARCH COMPLETE`
37
- - Planning agent → `## PLANNING COMPLETE`
38
- - Execution agent → `## EXECUTION COMPLETE`
39
- - Verification agent → `## VERIFICATION COMPLETE`
40
- - Stage-specific agents → the stage name: `## SCAN COMPLETE`, `## DISCOVER COMPLETE`, `## PLAN COMPLETE`, `## DESIGN COMPLETE`, `## VERIFY COMPLETE`.
41
-
42
- The orchestrator detects failure by reading STATE.md for a `<blocker>`, not by the absence of a marker. Always emit the marker.
43
-
44
- ## Context-Exhaustion Hook Awareness
45
-
46
- A PostToolUse hook at `hooks/context-exhaustion.js` watches your tool output for the string `<context-exhaustion>` in your response. If you determine you cannot finish the task in the remaining context, emit:
47
-
48
- ```xml
49
- <context-exhaustion>
50
- reason: <one-sentence cause — e.g., "required_reading totals 47KB exceeding remaining context">
51
- resume-hint: <one-sentence instruction for a resumption spawn>
52
- </context-exhaustion>
53
- ```
54
-
55
- …before your completion marker. The hook captures this into STATE.md so the orchestrator can re-spawn you with a narrower scope. Do not guess when you're near exhaustion — only emit when a concrete obstacle (file too large to read, required diff too wide) forced the call.
56
-
57
- ## Budget-Enforcer Hook Awareness (Phase 10.1)
58
-
59
- A PreToolUse hook at `hooks/budget-enforcer.js` intercepts every `Task` spawn (including the one that invoked you). The hook may:
60
- - **Short-circuit** your spawn with a cached result from `.design/cache-manifest.json` (transparent — you never run).
61
- - **Downgrade** your tier to Haiku at the 80% per-task cap soft-threshold, silently (`auto_downgrade_on_cap: true` in `.design/budget.json`, D-03).
62
- - **Hard-block** your spawn at the 100% per-task or per-phase cap with an actionable error (D-02).
63
-
64
- Implication for you as the agent: **do not assume a specific model tier is live.** Your output must be correct whether you run on Haiku, Sonnet, or Opus. If a task genuinely requires reasoning density beyond Haiku, the `size_budget` + `default-tier` combination should have been set at authoring time so the router routes it correctly — the remedy is a frontmatter update (a Phase 11 reflector proposal), not a mid-run assumption.
65
-
66
15
  ## Ordering Convention (D-17)
67
16
 
68
17
  Your agent body is structured in this exact order so the cache prefix stays stable:
@@ -79,4 +28,4 @@ The `/gdd:warm-cache` command (ships in Plan 10.1-02) pre-warms this identical p
79
28
 
80
29
  ---
81
30
 
82
- *Imported by: every file under `agents/*.md` (except `agents/README.md`). Maintained as part of Phase 10.1 (OPT-07). Edits to this file affect every agent simultaneously — verify across the full agent suite before committing.*
31
+ *Imported by: every file under `agents/*.md` (except `agents/README.md`). Maintained as part of Phase 10.1 (OPT-07) and Phase 14.5 (L0/L2 split). Edits to this file affect every agent simultaneously — verify across the full agent suite before committing.*
@@ -76,6 +76,10 @@ function readTelemetryRows() {
76
76
  function aggregate(rows) {
77
77
  const byAgent = new Map();
78
78
  for (const r of rows) {
79
+ // Blocked rows represent a spawn that was denied at the hook — the agent
80
+ // never actually ran, so it must not contribute to spawn counts, cost, or
81
+ // token totals. Skip them here (mirror of the filter in aggregateByPhase).
82
+ if (r.block_reason) continue;
79
83
  const agent = r.agent || 'unknown';
80
84
  if (!byAgent.has(agent)) {
81
85
  byAgent.set(agent, {
@@ -129,6 +133,11 @@ function writeAtomic(filePath, content) {
129
133
  function aggregateByPhase(rows) {
130
134
  const byPhase = {};
131
135
  for (const r of rows) {
136
+ // Blocked rows represent spawns that were denied by the hook — the agent
137
+ // never ran, so their est_cost_usd must not inflate cumulative phase spend.
138
+ // Counting them would make future hard-block and soft-threshold checks
139
+ // stricter than intended on every repeat cap hit.
140
+ if (r.block_reason) continue;
132
141
  const phase = r.phase || 'unknown';
133
142
  byPhase[phase] = (byPhase[phase] || 0) + Number(r.est_cost_usd || 0);
134
143
  }
@@ -435,6 +435,26 @@ async function main() {
435
435
 
436
436
  console.log(` discovered ${allFiles.length} files, ${changed.length} changed`);
437
437
 
438
+ // Phase 14.5: validate reference registry round-trip whenever any reference/*
439
+ // changed (or on --force). Fail the build on dangling/missing/duplicate.
440
+ const referenceChanged = FORCE || changed.some(f => f.startsWith('reference/'));
441
+ if (referenceChanged) {
442
+ try {
443
+ const { validateRegistry } = require(path.join(__dirname, 'lib', 'reference-registry.cjs'));
444
+ const v = validateRegistry({ cwd: ROOT });
445
+ if (!v.ok) {
446
+ console.error('build-intel: reference registry validation failed:');
447
+ if (v.missingInRegistry.length) console.error(' missing in registry:', v.missingInRegistry);
448
+ if (v.danglingInRegistry.length) console.error(' dangling entries:', v.danglingInRegistry);
449
+ if (v.duplicates.length) console.error(' duplicate entries:', v.duplicates);
450
+ process.exit(1);
451
+ }
452
+ console.log(' reference registry: ok');
453
+ } catch (err) {
454
+ if (err && err.code !== 'MODULE_NOT_FOUND') throw err;
455
+ }
456
+ }
457
+
438
458
  // Always rebuild files slice when any file changed
439
459
  const filesSlice = buildFilesSlice(allFiles);
440
460
  writeSlice('files.json', filesSlice);
@@ -3,15 +3,56 @@
3
3
  // hooks/gdd-read-injection-scanner.js (runtime hook) and
4
4
  // scripts/run-injection-scanner-ci.cjs (CI scanner).
5
5
  // Add new patterns here; both consumers pick them up automatically.
6
+ //
7
+ // Phase 14.5 adds three new families: invisible-Unicode obfuscation,
8
+ // HTML-comment instruction hijacks, and secret-exfil trigger patterns.
9
+
10
+ // Zero-width + word-joiner + BOM + bidi overrides. Used for detection
11
+ // AND as a normalization stripper for hooks that run scan after NFKC.
12
+ const _CONTEXT_INVISIBLE_CHARS = /[\u200B-\u200D\u2060\uFEFF\u202A-\u202E]/;
6
13
 
7
14
  const INJECTION_PATTERNS = [
15
+ // ── classic prompt-injection verbs ──────────────────────────────────
8
16
  { name: 'ignore previous', re: /ignore\s+(all\s+)?(previous|prior|above)\s+instructions?/i },
9
17
  { name: 'disregard previous', re: /disregard\s+(all\s+)?(previous|prior|above)\s+instructions?/i },
18
+ { name: 'forget previous', re: /forget\s+(the\s+|all\s+)?(previous|prior|above)/i },
10
19
  { name: 'you are now a different', re: /you\s+are\s+now\s+a\s+different/i },
11
20
  { name: 'system: you are', re: /system\s*:\s*you\s+are/i },
12
21
  { name: 'role tag injection', re: /<\s*\/?\s*(system|assistant|human)\s*>/i },
13
22
  { name: '[INST] fragment', re: /\[INST\]/i },
14
23
  { name: '### instruction fragment',re: /###\s*instruction/i },
24
+
25
+ // ── invisible-Unicode obfuscation (14.5 new family) ─────────────────
26
+ { name: 'invisible-unicode chars', re: _CONTEXT_INVISIBLE_CHARS },
27
+ { name: 'bidi-override instruction', re: /[\u202A-\u202E][^\n]*(ignore|disregard|forget|system\s*:)/i },
28
+
29
+ // ── HTML-comment / hidden-element instruction hijack (14.5 new) ─────
30
+ { name: 'html-comment system', re: /<!--\s*system\s*:/i },
31
+ { name: 'html-comment assistant', re: /<!--\s*assistant\s*:/i },
32
+ { name: 'html-comment ignore', re: /<!--\s*(ignore|disregard|forget)\b/i },
33
+ { name: 'hidden div system', re: /<div\s+[^>]*style\s*=\s*["'][^"']*display\s*:\s*none[^"']*["'][^>]*>\s*(system|ignore|disregard)/i },
34
+ { name: 'hidden span system', re: /<span\s+[^>]*style\s*=\s*["'][^"']*visibility\s*:\s*hidden[^"']*["'][^>]*>\s*(system|ignore|disregard)/i },
35
+ { name: 'zero-font-size trick', re: /style\s*=\s*["'][^"']*font-size\s*:\s*0[^"']*["'][^>]*>\s*(ignore|system|disregard)/i },
36
+
37
+ // ── secret-exfil trigger patterns (14.5 new) ─────────────────────────
38
+ { name: 'curl-with-api-key-env', re: /curl\s+[^|\n]*\$\{?[A-Z][A-Z0-9_]*_(KEY|TOKEN|SECRET|PASSWORD|AUTH)\}?/ },
39
+ { name: 'cat-dotenv', re: /\bcat\s+\.env(\.[a-z]+)?\b/ },
40
+ { name: 'printenv-leak', re: /\bprintenv\b[^\n]{0,80}\|\s*(curl|wget|nc|ssh)/ },
41
+ { name: 'tar-home-netcat', re: /\btar\s+c[fzvj]+\s+-\s+~[^\n]*\|\s*(nc|ssh|curl)/ },
42
+ { name: 'env-dot-leak', re: /process\.env\.[A-Z][A-Z0-9_]*_(KEY|TOKEN|SECRET)\s*[^;,\n]*(fetch|axios|XMLHttpRequest|http\.request)/ },
43
+ { name: 'ssh-key-cat', re: /\bcat\s+~?\/?\.ssh\/id_(rsa|ed25519|ecdsa|dsa)\b/ },
15
44
  ];
16
45
 
17
- module.exports = { INJECTION_PATTERNS };
46
+ /**
47
+ * Apply patterns to content and return matched pattern names (deduped).
48
+ */
49
+ function scan(content) {
50
+ if (typeof content !== 'string' || !content) return [];
51
+ const hits = [];
52
+ for (const { name, re } of INJECTION_PATTERNS) {
53
+ if (re.test(content)) hits.push(name);
54
+ }
55
+ return hits;
56
+ }
57
+
58
+ module.exports = { INJECTION_PATTERNS, _CONTEXT_INVISIBLE_CHARS, scan };
@@ -0,0 +1,97 @@
1
+ 'use strict';
2
+ /**
3
+ * scripts/lib/blast-radius.cjs — per-task blast-radius preflight.
4
+ *
5
+ * Exports:
6
+ * estimate({touchedPaths, diffStats, config?}) →
7
+ * { files, lines, exceeds, overBy, limit: {files, lines} }
8
+ * estimateMCPCalls({toolCalls, config?}) →
9
+ * { count, exceeds, overBy, limit }
10
+ * formatDiffSummary({touchedPaths, diffStats, result}) → string
11
+ * loadConfig(cwd?) → { max_files_per_task, max_lines_per_task, max_mcp_calls_per_task }
12
+ *
13
+ * Config precedence: .design/config.json.blast_radius.{max_files_per_task, max_lines_per_task, max_mcp_calls_per_task}
14
+ * then .design/config.json top-level same keys, then built-in defaults.
15
+ *
16
+ * Zero-value limits DISABLE that dimension.
17
+ */
18
+
19
+ const fs = require('fs');
20
+ const path = require('path');
21
+
22
+ const DEFAULTS = {
23
+ max_files_per_task: 10,
24
+ max_lines_per_task: 400,
25
+ max_mcp_calls_per_task: 30,
26
+ };
27
+
28
+ function loadConfig(cwd) {
29
+ const configPath = path.join(cwd || process.cwd(), '.design', 'config.json');
30
+ let cfg = {};
31
+ try { cfg = JSON.parse(fs.readFileSync(configPath, 'utf8')); } catch { cfg = {}; }
32
+ const br = (cfg && typeof cfg === 'object' && cfg.blast_radius) || {};
33
+ return {
34
+ max_files_per_task: numberOr(br.max_files_per_task, cfg.max_files_per_task, DEFAULTS.max_files_per_task),
35
+ max_lines_per_task: numberOr(br.max_lines_per_task, cfg.max_lines_per_task, DEFAULTS.max_lines_per_task),
36
+ max_mcp_calls_per_task: numberOr(br.max_mcp_calls_per_task, cfg.max_mcp_calls_per_task, DEFAULTS.max_mcp_calls_per_task),
37
+ };
38
+ }
39
+
40
+ function numberOr(...candidates) {
41
+ for (const v of candidates) {
42
+ if (typeof v === 'number' && Number.isFinite(v) && v >= 0) return v;
43
+ }
44
+ return undefined;
45
+ }
46
+
47
+ function estimate({ touchedPaths = [], diffStats = {}, config } = {}) {
48
+ const cfg = config || loadConfig();
49
+ const files = new Set((touchedPaths || []).filter(Boolean)).size;
50
+ const lines = (diffStats.insertions || 0) + (diffStats.deletions || 0);
51
+ const fileLimit = cfg.max_files_per_task;
52
+ const lineLimit = cfg.max_lines_per_task;
53
+ const fileExceeds = fileLimit > 0 && files > fileLimit;
54
+ const lineExceeds = lineLimit > 0 && lines > lineLimit;
55
+ return {
56
+ files,
57
+ lines,
58
+ exceeds: fileExceeds || lineExceeds,
59
+ overBy: {
60
+ files: fileExceeds ? files - fileLimit : 0,
61
+ lines: lineExceeds ? lines - lineLimit : 0,
62
+ },
63
+ limit: { files: fileLimit, lines: lineLimit },
64
+ };
65
+ }
66
+
67
+ function estimateMCPCalls({ toolCalls = [], config } = {}) {
68
+ const cfg = config || loadConfig();
69
+ const count = Array.isArray(toolCalls) ? toolCalls.length : 0;
70
+ const limit = cfg.max_mcp_calls_per_task;
71
+ const exceeds = limit > 0 && count > limit;
72
+ return {
73
+ count,
74
+ exceeds,
75
+ overBy: exceeds ? count - limit : 0,
76
+ limit,
77
+ };
78
+ }
79
+
80
+ function formatDiffSummary({ touchedPaths = [], diffStats = {}, result }) {
81
+ const r = result || estimate({ touchedPaths, diffStats });
82
+ const lines = [];
83
+ lines.push('## Blast-Radius Preflight — Over Limit');
84
+ lines.push('');
85
+ lines.push(`Files touched: ${r.files} (limit ${r.limit.files || 'disabled'})`);
86
+ lines.push(`Lines changed: ${r.lines} (limit ${r.limit.lines || 'disabled'})`);
87
+ if (r.overBy.files) lines.push(`Over by: +${r.overBy.files} files`);
88
+ if (r.overBy.lines) lines.push(`Over by: +${r.overBy.lines} lines`);
89
+ lines.push('');
90
+ lines.push('Touched paths:');
91
+ for (const p of touchedPaths) lines.push(` - ${p}`);
92
+ lines.push('');
93
+ lines.push('To proceed: split the task into ≤-limit chunks, or raise the ceiling in `.design/config.json.blast_radius.{max_files_per_task,max_lines_per_task}`.');
94
+ return lines.join('\n');
95
+ }
96
+
97
+ module.exports = { estimate, estimateMCPCalls, formatDiffSummary, loadConfig, DEFAULTS };