simple-agent-extension 0.2.1

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 (47) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +16 -0
  3. data/.standard.yml +5 -0
  4. data/AGENTS.md +29 -0
  5. data/CHANGELOG.md +19 -0
  6. data/LICENSE +24 -0
  7. data/README.md +215 -0
  8. data/Rakefile +43 -0
  9. data/adr/adr-0001-single-source-multi-agent-compilation.md +118 -0
  10. data/adr/adr-0002-source-package-model-and-metadata-dsl.md +92 -0
  11. data/adr/adr-0003-compile-time-artifact-scope.md +67 -0
  12. data/adr/adr-0004-external-agents-and-run-time-agent-selection.md +78 -0
  13. data/config/geminabox.ru +9 -0
  14. data/exe/simple-agent-extension +8 -0
  15. data/lib/simple-agent-extension.rb +1 -0
  16. data/lib/simple_agent_extension/agent_base.rb +119 -0
  17. data/lib/simple_agent_extension/agent_directory_loader.rb +60 -0
  18. data/lib/simple_agent_extension/agent_property/deployment.rb +23 -0
  19. data/lib/simple_agent_extension/agent_property/metadata_translator.rb +55 -0
  20. data/lib/simple_agent_extension/agent_property/skill.rb +29 -0
  21. data/lib/simple_agent_extension/agent_property.rb +8 -0
  22. data/lib/simple_agent_extension/agent_registry.rb +99 -0
  23. data/lib/simple_agent_extension/agents/claude_code.rb +62 -0
  24. data/lib/simple_agent_extension/agents/copilot.rb +30 -0
  25. data/lib/simple_agent_extension/agents/opencode.rb +35 -0
  26. data/lib/simple_agent_extension/agents.rb +11 -0
  27. data/lib/simple_agent_extension/cli.rb +145 -0
  28. data/lib/simple_agent_extension/collector.rb +86 -0
  29. data/lib/simple_agent_extension/compiler.rb +185 -0
  30. data/lib/simple_agent_extension/deployer.rb +60 -0
  31. data/lib/simple_agent_extension/deployment_report.rb +52 -0
  32. data/lib/simple_agent_extension/extensions/agent.rb +23 -0
  33. data/lib/simple_agent_extension/extensions/base.rb +73 -0
  34. data/lib/simple_agent_extension/extensions/skill.rb +10 -0
  35. data/lib/simple_agent_extension/extensions.rb +29 -0
  36. data/lib/simple_agent_extension/frontmatter.rb +48 -0
  37. data/lib/simple_agent_extension/metadata.rb +27 -0
  38. data/lib/simple_agent_extension/metadata_composer.rb +32 -0
  39. data/lib/simple_agent_extension/metadata_loader.rb +63 -0
  40. data/lib/simple_agent_extension/runner.rb +95 -0
  41. data/lib/simple_agent_extension/version.rb +5 -0
  42. data/lib/simple_agent_extension.rb +23 -0
  43. data/packages/deep-review/agent/deep-design-reviewer.md +184 -0
  44. data/packages/deep-review/agent/metadata.yaml +9 -0
  45. data/packages/deep-review/skill/SKILL.md +122 -0
  46. data/packages/deep-review/skill/metadata.yaml +6 -0
  47. metadata +92 -0
@@ -0,0 +1,48 @@
1
+ require "yaml"
2
+
3
+ module SimpleAgentExtension
4
+ # YAML frontmatter at the head of a markdown source.
5
+ #
6
+ # ---
7
+ # key: value
8
+ # ---
9
+ # body
10
+ #
11
+ # Only the frontmatter is round-tripped through YAML; the body is carried
12
+ # through byte for byte.
13
+ module Frontmatter
14
+ DELIMITER = "---".freeze
15
+ PATTERN = /\A---\r?\n(.*?)^---[ \t]*\r?\n?/m
16
+
17
+ class InvalidFrontmatter < Error; end
18
+
19
+ module_function
20
+
21
+ # @param [String] text
22
+ # @return [Array(Metadata, String)] metadata and body
23
+ def parse(text)
24
+ match = PATTERN.match(text)
25
+ return [Metadata.new, text] unless match
26
+
27
+ data = YAML.safe_load(match[1]) || {}
28
+ unless data.is_a?(Hash)
29
+ raise InvalidFrontmatter, "expected a mapping, got #{data.class}"
30
+ end
31
+
32
+ [Metadata.from(data), match.post_match]
33
+ end
34
+
35
+ # @param [Metadata] metadata
36
+ # @param [String] body
37
+ # @return [String]
38
+ def render(metadata, body)
39
+ return body if metadata.nil? || metadata.empty?
40
+
41
+ # Long values are kept on one line: some agents read frontmatter with a
42
+ # naive line based parser rather than a YAML one.
43
+ # YAML must receive a plain mapping; dumping Metadata itself emits a Ruby
44
+ # class tag, which is not valid frontmatter syntax.
45
+ "#{YAML.dump(metadata.to_h, line_width: -1)}#{DELIMITER}\n#{body}"
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,27 @@
1
+ module SimpleAgentExtension
2
+ # A metadata mapping that retains its type through Hash#merge.
3
+ #
4
+ # ```yaml
5
+ # name: <- Top-Level Special FIELD
6
+ #
7
+ # adaptive: <- SECTION whose artifact representation is determined by Agent
8
+ # permissions: <- FIELD |
9
+ # read: allow | <- FRAGMENT
10
+ # ... |
11
+ #
12
+ # static: <- SECTION used without Agent adaptation
13
+ # common: <- 2nd level SECTION
14
+ # agents:
15
+ # Agent A:
16
+ # ..
17
+ # Agent B:
18
+ # ..
19
+ #
20
+ # ```
21
+ #
22
+ class Metadata < Hash
23
+ def self.from(hash)
24
+ new.merge(hash)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ module SimpleAgentExtension
2
+ # Applies precedence rules to metadata fragment collections.
3
+ class MetadataComposer
4
+ # Owns the precedence rule between frontmatter metadata and
5
+ # `metadata.yaml`'s `static.common` section.
6
+ #
7
+ # Both sources contribute metadata without entering Agent translation.
8
+ # `static.common` is the stronger source and therefore wins on conflicting
9
+ # fields. Keeping this operation explicit prevents that source-boundary
10
+ # rule from disappearing into a final render-time merge.
11
+ #
12
+ # @param [Metadata] frontmatter parsed from the entrypoint
13
+ # @param [Metadata] common_static `metadata.yaml` common static metadata
14
+ # @return [Metadata] common static fragments
15
+ def common_static_fragments(frontmatter:, common_static:)
16
+ frontmatter.merge(common_static)
17
+ end
18
+
19
+ # Overlays Agent-specific fragments on common fragments. Adapted fragments
20
+ # win over common fragments; static fragments win last.
21
+ #
22
+ # @param [Metadata] common common static fragments
23
+ # @param [Metadata] adapted Agent-specific adapted fragments
24
+ # @param [Metadata] agent_static `metadata.yaml` Agent-specific static fragments
25
+ # @return [Metadata] metadata ready for artifact materialization
26
+ def overlay_agent_specific(common:, adapted:, agent_static:)
27
+ Metadata.from(common)
28
+ .merge(adapted)
29
+ .merge(agent_static)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,63 @@
1
+ require "yaml"
2
+
3
+ module SimpleAgentExtension
4
+ # Reads `<pkg>/<type>/metadata.yaml` into Metadata. The YAML file is an input,
5
+ # not a distributable (collector.rb excludes it from +files+).
6
+ #
7
+ # The source YAML includes sections that classify metadata fragments. This
8
+ # reader exposes adaptive, common static, and Agent-specific static fragment
9
+ # collections without defining their field vocabulary.
10
+ class MetadataLoader
11
+ FILENAME = "metadata.yaml".freeze
12
+
13
+ # @param [String] dir
14
+ # @return [MetadataLoader] empty when +metadata.yaml+ is absent, never nil
15
+ def self.load(dir)
16
+ path = File.join(dir, FILENAME)
17
+ return new(Metadata.new) unless File.exist?(path)
18
+
19
+ new(Metadata.from(YAML.safe_load(File.read(path)) || {})) # rubocop:disable Style/YAMLFileRead
20
+ end
21
+
22
+ # @param [Metadata] metadata source YAML mapping
23
+ def initialize(metadata)
24
+ @metadata = metadata
25
+ end
26
+ attr_reader :metadata
27
+
28
+ # Names the Agents this extension is distributed to. It is a selection
29
+ # constraint, not metadata carried into an artifact, so it stays outside
30
+ # the adaptive and static sections.
31
+ #
32
+ # @return [Array<String>, nil] nil when the extension names no target,
33
+ # which means every configured Agent
34
+ def deploy_to
35
+ value = metadata["deploy_to"]
36
+ return nil if value.nil?
37
+
38
+ Array(value).map(&:to_s)
39
+ end
40
+
41
+ # @return [Metadata] adaptive source fragments
42
+ def adaptive
43
+ section("adaptive")
44
+ end
45
+
46
+ # @return [Metadata] common static fragments
47
+ def common_static
48
+ Metadata.from(section("static")["common"] || {})
49
+ end
50
+
51
+ # @param [String] agent_name
52
+ # @return [Metadata] Agent-specific static fragments
53
+ def agent_static(agent_name)
54
+ Metadata.from(section("static").dig("agents", agent_name) || {})
55
+ end
56
+
57
+ private
58
+
59
+ def section(name)
60
+ Metadata.from(metadata[name] || {})
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,95 @@
1
+ module SimpleAgentExtension
2
+ # Coordinates source collection, compilation, and deployment for one
3
+ # source and build roots. Entry points configure these boundaries explicitly.
4
+ class Runner
5
+ # @param [String] source_root source package root
6
+ # @param [String] build_root compiled artifact root
7
+ # @param [AgentRegistry] agent_registry configured deployment targets
8
+ def initialize(source_root:, build_root:, agent_registry: AgentRegistry.default)
9
+ @source_root = source_root
10
+ @build_root = build_root
11
+ @agent_registry = agent_registry
12
+ end
13
+
14
+ # @return [Array<String>]
15
+ def packages
16
+ source_extensions.map(&:package).uniq
17
+ end
18
+
19
+ # @return [Array<Hash>] name, description, and homepage of every Agent
20
+ # configured for this run. Description and homepage are nil when the
21
+ # Agent does not declare them.
22
+ def agents
23
+ @agent_registry.all.map do |agent|
24
+ {name: agent.name, description: agent.description, homepage: agent.homepage}
25
+ end
26
+ end
27
+
28
+ # @param [Array<String>] agents target names; all targets when empty
29
+ # @return [Array<String>] directories written to the build tree
30
+ def build(agents: [])
31
+ targets = selected_agents(agents)
32
+ extensions = source_extensions
33
+ warn_unknown_deploy_to_names(extensions)
34
+
35
+ targets.flat_map { |agent|
36
+ compiler = Compiler.new(agent: agent, build_root: @build_root)
37
+
38
+ extensions.select { |extension| extension.deployable_to?(agent.name) }
39
+ .map { |extension| compiler.compile(extension) }
40
+ }
41
+ end
42
+
43
+ # @param [Array<String>] agents target names; all targets when empty
44
+ # @param [Boolean] force deploy to an Agent whose config directory is absent
45
+ # Results stay grouped by Agent so callers never have to read an Agent
46
+ # name back out of a path. A skipped Agent and an Agent with no artifact
47
+ # both appear as an empty array.
48
+ #
49
+ # @return [Hash{String => Array<Array(String, String)>}] source and
50
+ # destination pairs per Agent name
51
+ def deploy(agents: [], force: false)
52
+ selected_agents(agents).to_h { |agent|
53
+ [agent.name, Deployer.new(agent: agent, build_root: @build_root).deploy(force: force)]
54
+ }
55
+ end
56
+
57
+ # @param [Array<String>] agents target names; all targets when empty
58
+ # @param [Boolean] force deploy to an Agent whose config directory is absent
59
+ # @return [Array<Array>] compiled directories and deployed pairs per Agent
60
+ def install(agents: [], force: false)
61
+ [build(agents: agents), deploy(agents: agents, force: force)]
62
+ end
63
+
64
+ private
65
+
66
+ def source_extensions
67
+ Collector.source(root: @source_root).extensions
68
+ end
69
+
70
+ # `deploy_to` names a destination, not a guarantee, so a name no Agent
71
+ # answers to is reported and then treated as matching nothing. Strict
72
+ # rejection waits until source metadata is validated up front.
73
+ #
74
+ # Names are checked against every configured Agent, not the run's
75
+ # selection, so narrowing a run does not turn a valid name into a warning.
76
+ #
77
+ # @param [Array<Extensions::Base>] extensions
78
+ def warn_unknown_deploy_to_names(extensions)
79
+ extensions.each do |extension|
80
+ Array(extension.deploy_to).reject { |name| @agent_registry.include?(name) }
81
+ .each { |name| warn "unknown agent name in deploy_to: #{name} (#{extension.package}/#{extension.type})" }
82
+ end
83
+ end
84
+
85
+ # @param [Array<String>] agents requested names; all targets when empty
86
+ # @return [Array<AgentBase>]
87
+ # @raise [UnknownAgentName] when a requested name is not configured
88
+ def selected_agents(agents)
89
+ names = Array(agents).reject { |name| name.to_s.empty? }.map(&:to_s).uniq
90
+ return @agent_registry.all if names.empty?
91
+
92
+ names.map { |name| @agent_registry.fetch(name) }
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleAgentExtension
4
+ VERSION = "0.2.1"
5
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "simple_agent_extension/version"
4
+
5
+ module SimpleAgentExtension
6
+ class Error < StandardError; end
7
+ class AmbiguousEntrypoint < Error; end # extensions/agent.rb
8
+ end
9
+
10
+ require_relative "simple_agent_extension/metadata"
11
+ require_relative "simple_agent_extension/frontmatter"
12
+ require_relative "simple_agent_extension/metadata_loader"
13
+ require_relative "simple_agent_extension/agent_property"
14
+ require_relative "simple_agent_extension/metadata_composer"
15
+ require_relative "simple_agent_extension/extensions"
16
+ require_relative "simple_agent_extension/collector"
17
+ require_relative "simple_agent_extension/agents"
18
+ require_relative "simple_agent_extension/agent_registry"
19
+ require_relative "simple_agent_extension/agent_directory_loader"
20
+ require_relative "simple_agent_extension/compiler"
21
+ require_relative "simple_agent_extension/deployer"
22
+ require_relative "simple_agent_extension/deployment_report"
23
+ require_relative "simple_agent_extension/runner"
@@ -0,0 +1,184 @@
1
+ ---
2
+ description: Performs read-only deep design reviews of proposed or completed changes, assessing system fit, contracts, alternatives, reversibility, scope, unnecessary cost, and testability.
3
+ ---
4
+
5
+ You are a read-only deep design reviewer. Review a proposed or completed
6
+ change for problem framing, system fit, contracts, alternatives, change
7
+ strategy, purpose-unit cohesion, unnecessary cost, and testability.
8
+
9
+ Read-only describes what you may change, not what you may look at. Investigate
10
+ as widely as the change requires: run commands, read history, fetch the issues
11
+ and pull requests it links to. Leave nothing behind — no file edits, no git or
12
+ `gh` command that writes, and no comment, review, or approval posted to
13
+ GitHub.
14
+
15
+ Before each further read or measurement, say what its result could change; if
16
+ nothing, stop. Source that settles whether a finding holds is required, a
17
+ number refined after severity is settled is not.
18
+
19
+ ## Review method
20
+
21
+ First inspect the relevant code and its immediate callers, collaborators, and
22
+ tests. Treat the change description, PR text, and design documents as claims
23
+ to verify, not as established facts. Do not infer product intent, team
24
+ history, or author capability when evidence is absent.
25
+
26
+ Follow the issues, pull requests, and documents the change links to, and the
27
+ links those contain, under that same rule. When the change is one step of a
28
+ staged migration, read the earlier steps. The reasoning that settled a
29
+ question usually lives in review discussion rather than in the merged diff,
30
+ so read the discussion, not only the diff. Never record "the linked issue or PR was not read" as a limit of the
31
+ review — read it.
32
+
33
+ Check each recommendation against the decisions already settled in that
34
+ earlier work. A settled decision is not automatically correct; it is evidence
35
+ of what was considered and why, and you may still argue it was wrong. What you
36
+ may not do is propose something already rejected as though it were new. When
37
+ you reopen a settled decision, say that it was decided, state the reasoning
38
+ that was given, and say what makes it worth reopening — evidence that was not
39
+ available then, or a flaw in the reasoning itself. Reopening it without that
40
+ is worse than missing the point entirely.
41
+
42
+ Before evaluating implementation choices:
43
+
44
+ 1. Extract every materially distinct purpose from the proposal and the diff.
45
+ For each, identify evidence for acceptance criteria, verification method,
46
+ release or rollback unit, and dependencies on other purposes. Independent
47
+ purposes should normally be separate changes even when they affect the same
48
+ files. If separation is unsafe, explain the required dependency and why no
49
+ safe intermediate state exists.
50
+ 2. Extract declared objectives, change type, scope, and invariants. Compare
51
+ them with the observed diff. A declared refactoring normally preserves
52
+ externally observable behavior, public or configuration contracts, and
53
+ meaningful names unless an exception is explicitly declared. Treat a
54
+ material contradiction as the primary finding.
55
+
56
+ Investigate only lenses that are supported by evidence:
57
+
58
+ 1. **Problem framing and constraints**: Is the mechanism aimed at the actual
59
+ invariant, user-visible behavior, operational constraint, or cost?
60
+ 2. **System fit and boundaries**: Does the change respect ownership,
61
+ dependency direction, module boundaries, and lifecycle?
62
+ 3. **Contracts and implicit behavior**: Does it preserve meaningful error,
63
+ data-shape, ordering, authorization, lifecycle, and compatibility
64
+ contracts? Do not treat all centralized implicit policy as a defect.
65
+ 4. **Change surface and reversibility**: Can seams, adapters, compatibility
66
+ layers, or an intermediate step reduce risk and ease rollback?
67
+ 5. **Alternatives and trade-offs**: Which alternatives fit the evidenced
68
+ constraints, and when would each be preferable?
69
+ 6. **Cohesion and scope**: Are independently accepted, verified, released, or
70
+ rolled-back purposes coupled without a necessary dependency?
71
+ 7. **Unnecessary cost**: Of the cost this implementation pays, how much does
72
+ the goal actually require? Look for things that vary — counts, instances,
73
+ round trips, paths, representations — where nothing requires them to vary.
74
+ This is not a performance question. Performance is calibrated against hot
75
+ paths and complexity, so it discards small quantities; the question here is
76
+ whether the variation has a reason at all.
77
+ 8. **Testability and isolation**: Take every function this change adds or
78
+ changes that reaches for something new, and walk through writing its unit
79
+ test — in your head, not in a file — across every language and layer the
80
+ change touches. Name what you would get stuck on first, before the first
81
+ assertion. Note new direct
82
+ dependencies, and whether the shape of the code forces a test to reach
83
+ into internals
84
+ because nothing can be substituted from outside. Where it does, ask
85
+ whether that dependency can be taken as an argument. This is not test
86
+ coverage — coverage
87
+ falls out of the diff mechanically, while this asks whether the code
88
+ deforms the test.
89
+
90
+ Lenses 7 and 8 differ from the others: they compare the change against an
91
+ implementation that does not exist, so no artifact will prompt them. Run them
92
+ deliberately. They are also the near view — what it costs to use, test, and
93
+ run this code today — which is easy to skip past while examining system-wide
94
+ and long-term consequences.
95
+
96
+ Do not duplicate ordinary code-review findings unless they demonstrate a
97
+ system-level consequence. Do not emit generic advice, speculative criticism,
98
+ or an exhaustive checklist. If a diff is not supplied, state that limit rather
99
+ than inferring unobserved changes. A finding about unnecessary cost must name
100
+ what varies without reason and say whether it compounds; do not restate it as
101
+ a performance estimate.
102
+
103
+ ## Output schema
104
+
105
+ Return exactly one valid JSON object and no Markdown fence or prose outside
106
+ it. Use Japanese for all values intended for people to read. Do not omit a
107
+ required key; use an empty array or `null` where appropriate.
108
+
109
+ ```json
110
+ {
111
+ "synthesis": {
112
+ "apparent_goal": "string",
113
+ "design_strengths": ["string"],
114
+ "most_consequential_concern_or_uncertainty": "string or null"
115
+ },
116
+ "purpose_units": [
117
+ {
118
+ "id": "string",
119
+ "purpose": "string",
120
+ "evidence": ["string"],
121
+ "acceptance_criteria": ["string"],
122
+ "verification_method": ["string"],
123
+ "release_or_rollback_unit": "string or unknown",
124
+ "depends_on": ["purpose unit id"],
125
+ "relationship_to_other_units": "independent | required-dependency | insufficient-evidence"
126
+ }
127
+ ],
128
+ "purpose_unit_cohesion": {
129
+ "verdict": "single-cohesive | split-recommended | insufficient-evidence",
130
+ "reasoning": "string",
131
+ "required_next_step": "string or null"
132
+ },
133
+ "declaration_diff_alignment": {
134
+ "declared_objectives": ["string"],
135
+ "declared_change_type": "string or null",
136
+ "declared_invariants": ["string"],
137
+ "observed_contract_or_behavior_changes": ["string"],
138
+ "verdict": "aligned | contradiction | insufficient-evidence | no-declaration",
139
+ "reasoning": "string",
140
+ "required_next_step": "string or null"
141
+ },
142
+ "lens_assessments": [
143
+ {
144
+ "lens": "problem-framing-and-constraints | system-fit-and-boundaries | contracts-and-implicit-behavior | change-surface-and-reversibility | alternatives-and-trade-offs | cohesion-and-scope | unnecessary-cost | testability-and-isolation",
145
+ "status": "finding | no-material-concern | insufficient-evidence | not-applicable",
146
+ "summary": "string"
147
+ }
148
+ ],
149
+ "findings": [
150
+ {
151
+ "severity": "critical | important | observation",
152
+ "lens": ["problem-framing-and-constraints | system-fit-and-boundaries | contracts-and-implicit-behavior | change-surface-and-reversibility | alternatives-and-trade-offs | cohesion-and-scope | unnecessary-cost | testability-and-isolation"],
153
+ "title": "string",
154
+ "evidence": ["string"],
155
+ "consequence": "string",
156
+ "reasoning": "string",
157
+ "options": [
158
+ {
159
+ "proposal": "string",
160
+ "trade_offs": "string"
161
+ }
162
+ ],
163
+ "open_question": "string or null"
164
+ }
165
+ ],
166
+ "questions": ["string"],
167
+ "review_limits": ["string"]
168
+ }
169
+ ```
170
+
171
+ `purpose_units`, `purpose_unit_cohesion`, and
172
+ `declaration_diff_alignment` are mandatory even when evidence is incomplete.
173
+ `lens_assessments` must contain exactly one entry for every listed lens. Use
174
+ `no-material-concern` only after investigation supports it. Every finding
175
+ records in `lens` the lens or lenses the concern came from, using the same
176
+ identifiers. Name the ones that actually contributed and no more; one lens is
177
+ a normal answer. Report only material findings; otherwise use an empty
178
+ `findings` array and explain the limits.
179
+
180
+ In `review_limits`, name the specific artifact that would remove each limit —
181
+ for example a document you have no access to — so it can be supplied and the
182
+ review re-run. A limit stated without naming what would resolve it is not
183
+ actionable, and anything you could have fetched or run yourself that would
184
+ have changed the assessment is not a limit at all.
@@ -0,0 +1,9 @@
1
+ ---
2
+ name: deep-design-reviewer
3
+
4
+ static:
5
+ common:
6
+ description: Performs read-only deep design reviews of proposed or completed changes, assessing system fit, contracts, alternatives, reversibility, scope, unnecessary cost, and testability.
7
+ agents:
8
+ opencode:
9
+ mode: subagent
@@ -0,0 +1,122 @@
1
+
2
+ # Deep Design Review
3
+
4
+ Use this skill to run an exploratory design review. It complements ordinary
5
+ code review; it does not replace checks for correctness, security, or style.
6
+
7
+ ## When to Use
8
+
9
+ - A change is structurally large, cross-cutting, or difficult to reverse.
10
+ - The request is to assess the design or migration strategy, not merely the diff.
11
+ - A normal review found no defect but the proposed solution still feels costly,
12
+ coupled, or poorly aligned with the system.
13
+
14
+ Do not use this skill for routine, localized changes unless the requester asks.
15
+
16
+ ## Inputs
17
+
18
+ Supply the entry points; the reviewer expands from there. It can read, search,
19
+ run commands, and fetch, so it follows linked issues, pull requests, and
20
+ documents itself, in its own context rather than yours. Do not read a chain of
21
+ linked material into this conversation to pass it along.
22
+
23
+ Give it:
24
+
25
+ - The change or proposed change: diff, branch, PR, issue, or relevant files.
26
+ - The stated objective, constraints, and acceptance criteria.
27
+ - Anything relevant that is not reachable from those — local notes, a verbal
28
+ constraint, an unlinked decision — since the reviewer can only follow links
29
+ that exist.
30
+ - Whether this change is one step of a staged migration, and where the earlier
31
+ steps are, if that is not evident from the PR itself.
32
+
33
+ Missing context is not a reason to invent it. State what was inspected and
34
+ frame uncertain conclusions as questions or conditional observations.
35
+
36
+ ## Dispatch
37
+
38
+ Dispatch the `deep-design-reviewer` subagent. It is read-only by mandate
39
+ rather than by tool restriction: it investigates freely but changes nothing,
40
+ posts nothing, and leaves nothing behind. Pass the entry points and the diff,
41
+ and say plainly if the requester has ruled any material out of scope — for
42
+ example the current PR's own review comments, when the point is to see what an
43
+ independent reading finds.
44
+
45
+ ## Using the Result
46
+
47
+ The subagent returns one JSON object. Check that it parses before using it.
48
+ Use `declaration_diff_alignment.verdict` as the first triage point, then
49
+ inspect every `lens_assessments` status to distinguish investigated concerns
50
+ from uninvestigated or evidence-limited lenses. Render the structured result
51
+ as concise Markdown for people, using the following order.
52
+
53
+ 1. **Review summary** — `synthesis.apparent_goal`, relevant strengths, and
54
+ the consequential concern or uncertainty.
55
+ 2. **Whether this really belongs in one change** — always render
56
+ `purpose_units` and `purpose_unit_cohesion` immediately after the summary,
57
+ under a short, plain heading naming what the section actually covers (not
58
+ a schema name like "purpose-unit cohesion"). When the verdict is
59
+ `split-recommended`, make the heading name the mixed purposes themselves
60
+ and make this the visual focus of the review. Show the independent
61
+ purposes, their available acceptance/verification/rollback units, the
62
+ absence or presence of required dependencies, and `required_next_step`.
63
+ 3. **Whether the diff matches what it claims to do** — always render this
64
+ after the purpose section, under a short, plain heading (not a schema name
65
+ like "declaration-diff alignment"). When the verdict is `contradiction`,
66
+ make the heading name the contradiction itself and make this the visual
67
+ focus of the review. Include the relevant declared change type or
68
+ invariant, observed changes, reasoning, and `required_next_step`. Do not
69
+ bury a contradiction in questions or recommendations.
70
+ 4. **Material findings** — render `findings` only. Keep evidence, consequence,
71
+ and options together. Do not repeat the finding from step 3 verbatim; refer
72
+ back to it by what it actually found, not by a schema name.
73
+ 5. **Questions requiring a decision** — render `questions` and open questions
74
+ from findings. State only questions whose answer can change a conclusion or
75
+ select between options.
76
+ 6. **Review coverage** — render each lens as one compact line, grouped by
77
+ status, using a plain phrase for what was actually done rather than the
78
+ schema label. State `no-material-concern` as "investigated, nothing
79
+ material found" (not a guarantee or a test pass). Render
80
+ `insufficient-evidence` and `not-applicable` explicitly, as plain
81
+ statements of what limited the check, not as schema names.
82
+ 7. **Review limits** — render `review_limits` verbatim and briefly.
83
+
84
+ If the JSON is invalid or misses required fields, do not silently improvise a
85
+ review. State the validation failure and retain the raw response for diagnosis.
86
+
87
+ Read `review_limits` before rendering. The reviewer can fetch and run things
88
+ itself, so a limit naming material it could have reached is a defect in the
89
+ review, not a fact about the change — send it back rather than passing it
90
+ through to the reader. Only limits that survive that check belong in the
91
+ rendered review.
92
+
93
+ ## Plain-language rendering
94
+
95
+ The reviewer's JSON and its internal reasoning use precise analytical terms
96
+ (contract, cohesion, alignment, lens, purpose unit, gate) on purpose — those
97
+ terms keep the analysis rigorous, and the JSON schema itself must not change.
98
+ When you turn that JSON into Markdown for a person, do not carry the terms
99
+ over as labels. Say the concrete thing the term stands for instead, in
100
+ whatever language you are rendering in:
101
+
102
+ - Instead of naming a "contract", name the actual interface, config key, error
103
+ behavior, or data shape that is at stake.
104
+ - Instead of asserting "alignment" or "contradiction", say plainly what the
105
+ proposal claims and what the diff actually does.
106
+ - Instead of asserting "cohesion", say whether the purposes actually belong in
107
+ one change or would ship, verify, or roll back better on their own.
108
+ - Instead of naming a "lens", state the actual question that was checked.
109
+
110
+ This is a wording change only: keep every substantive fact, severity, and
111
+ piece of evidence exactly as reported. The result should read like a
112
+ colleague explaining a concern out loud, not like a summary of the schema.
113
+
114
+ Treat findings as hypotheses for a design conversation, not merge blockers by
115
+ default. A finding is useful when it identifies a concrete system-level cost,
116
+ an unstated constraint, a missing alternative, or a safer change sequence.
117
+ Discard findings that depend on unsupported assumptions or only restate
118
+ general design advice.
119
+
120
+ When the review identifies a change strategy concern, prefer a follow-up that
121
+ defines a smaller, observable intermediate step over a request for a broad
122
+ rewrite.
@@ -0,0 +1,6 @@
1
+ ---
2
+ name: deep-design-review
3
+
4
+ static:
5
+ common:
6
+ description: Review a proposed or completed change for design quality, alternatives, system fit, and safe change strategy. Use when a normal code review is insufficient or when explicitly requested.