@danmoisan/drm-copilot-mcp 1.0.17 → 1.0.19

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 (31) hide show
  1. package/out/mcp-server.js +1287 -548
  2. package/package.json +5 -4
  3. package/resources/claude-customizations/.claude/agent-memory/epic-orchestrator/MEMORY.md +9 -0
  4. package/resources/claude-customizations/.claude/agent-memory/epic-orchestrator/feedback_commit_push_memory_before_pr.md +17 -0
  5. package/resources/claude-customizations/.claude/agent-memory/orchestrator/MEMORY.md +1 -0
  6. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_commit_push_memory_before_pr.md +16 -0
  7. package/resources/claude-customizations/.claude/agents/legacy-parity-analyst.md +64 -0
  8. package/resources/claude-customizations/.claude/agents/migration-coverage-reviewer.md +64 -0
  9. package/resources/claude-customizations/.claude/agents/requirements-reconciler.md +63 -0
  10. package/resources/claude-customizations/.claude/agents/runtime-characterization-analyst.md +64 -0
  11. package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +213 -0
  12. package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +237 -0
  13. package/resources/claude-customizations/.claude/rules/shell.md +88 -0
  14. package/resources/claude-customizations/.claude/settings.json +8 -0
  15. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +132 -0
  16. package/resources/claude-customizations/.claude/skills/discovery-behavior-reconciliation/SKILL.md +65 -0
  17. package/resources/claude-customizations/.claude/skills/discovery-coverage-ledger/SKILL.md +66 -0
  18. package/resources/claude-customizations/.claude/skills/discovery-parity-matrix/SKILL.md +63 -0
  19. package/resources/claude-customizations/.claude/skills/discovery-repo-inventory/SKILL.md +80 -0
  20. package/resources/claude-customizations/.claude/skills/discovery-runtime-characterization/SKILL.md +63 -0
  21. package/resources/claude-customizations/.claude/skills/discovery-validate-artifacts/SKILL.md +79 -0
  22. package/resources/claude-customizations/.claude/skills/discovery-workflow/SKILL.md +146 -0
  23. package/resources/claude-customizations/.claude/skills/execute-hard-lock/SKILL.md +1 -1
  24. package/resources/claude-customizations/pack-manifests/core.json +14 -0
  25. package/resources/codex-and-agents-customizations/.agents/skills/execute-hard-lock/SKILL.md +1 -1
  26. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  27. package/resources/config/orchestration-routing.json +0 -2
  28. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +45 -3
  29. package/resources/powershell/PoshQC/PoshQC.psm1 +32 -6
  30. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +10 -0
  31. package/resources/templates/policy_audit/policy-audit.yyyy-MM-ddTHH-mm.md +3 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danmoisan/drm-copilot-mcp",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "Stdio MCP server exposing drm-copilot repo-automation tools.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -38,10 +38,11 @@
38
38
  "prepack": "node prepack.cjs"
39
39
  },
40
40
  "overrides": {
41
- "fast-uri": "^3.1.2",
42
- "hono": "^4.12.25",
41
+ "fast-uri": "^3.1.4",
42
+ "hono": "^4.12.27",
43
43
  "ip-address": "^10.2.0",
44
- "qs": "^6.15.2"
44
+ "qs": "^6.15.2",
45
+ "@hono/node-server": "^2.0.5"
45
46
  },
46
47
  "devDependencies": {
47
48
  "esbuild": "^0.28.0"
@@ -0,0 +1,9 @@
1
+ ---
2
+ name: epic-orchestrator-memory-index
3
+ description: Index of epic-orchestrator agent memories.
4
+ metadata:
5
+ type: index
6
+ scope: repo
7
+ ---
8
+
9
+ - [Commit and push agent-memory before PR](feedback_commit_push_memory_before_pr.md) — commit/push .claude/agent-memory/ changes before opening the integration PR, and again before merge if the CI/remediation cycle adds more.
@@ -0,0 +1,17 @@
1
+ ---
2
+ name: commit-and-push-agent-memory-before-pr
3
+ description: Repo-tracked agent-memory files under .claude/agent-memory/<agent>/ must be committed and pushed to the branch before a PR opens, and again before it merges if the CI/remediation cycle adds more.
4
+ metadata:
5
+ type: feedback
6
+ scope: general
7
+ ---
8
+
9
+ Repo-committed agent-memory files under `.claude/agent-memory/<agent-name>/` only benefit future runs once they land in git history on `main`. Any memory captured, recorded, or updated during a run must be staged, committed, and pushed to the working branch before that run opens a PR. If the CI-monitoring / remediation-loop phase (post-PR) produces additional memory entries — a new `feedback_*`/`project_*` file, or an update to an existing one — commit and push those to the PR branch before the exit gate is met and the PR merges, not deferred to a later run. Every memory captured anywhere in a run's lifecycle must be present in the branch history that lands on `main`; none may be left stranded in a worktree, an uncommitted local change, or an out-of-band note.
10
+
11
+ **Why:** Epic runs schedule child features across isolated git worktrees, and those worktrees are removed once each child's branch merges. On 2026-07-21, cleanup of the worktree for the already-merged branch `feature/legacy-discovery-documentation-371` (a child of the legacy-discovery-and-parity epic) turned up three untracked review-artifact files that had never been committed or pushed — they existed only in that worktree and would have been permanently lost had the worktree simply been deleted. Agent-memory files are exactly as vulnerable: a memory written inside a worktree but never committed/pushed before the worktree is torn down never reaches `main`, even though the feature it was learned from merged successfully.
12
+
13
+ **How to apply:**
14
+ - Each child feature's own `Agent(orchestrator)` instance is responsible for committing and pushing its own `.claude/agent-memory/orchestrator/` changes before its own PR opens (per that agent's own memory rule); you do not need to do this on the child's behalf, but do not remove a child's worktree until its PR is durably confirmed merged (per the existing wave-barrier rule), which also protects any not-yet-pushed memory in that worktree.
15
+ - Before you open the final integration-to-`main` PR via `Agent(pr-author)`, stage, commit, and push any new or modified files under `.claude/agent-memory/epic-orchestrator/` (root and the mirrored bundled copy under `extensions/drm-copilot/resources/**/.claude/agent-memory/epic-orchestrator/`) produced during epic scheduling, wave management, or fan-in, including the updated `MEMORY.md` index.
16
+ - If a CI failure on the integration PR or a reaudit produces new learning worth recording as memory, write it, then commit and push it to the integration branch before the final integration PR merges.
17
+ - This does not change the memory content rules (still feedback/project/reference typed, still indexed in `MEMORY.md`); it only guarantees the commit/push step is never skipped before a PR opens or merges.
@@ -13,3 +13,4 @@ metadata:
13
13
  - [branch-base-check-unmerged-pr-deps](feedback_branch_base_check_unmerged_pr_deps.md) — verify required symbols/files exist on the chosen branch base; if they only exist in an open PR, stack or merge first.
14
14
  - [potential-to-issue-creates-github-issue](feedback_potential_to_issue_creates_github_issue.md) — potential-to-issue creates the GitHub issue as a side effect; do not also run gh issue create.
15
15
  - [small-bug-uses-minor-audit](feedback_small_bug_uses_minor_audit.md) — a ~1-3 production-file bug fix uses the small path with Work Mode minor-audit, not full-bug.
16
+ - [commit-and-push-agent-memory-before-pr](feedback_commit_push_memory_before_pr.md) — commit/push .claude/agent-memory/ changes before opening the PR, and again before merge if the CI/remediation cycle adds more.
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: commit-and-push-agent-memory-before-pr
3
+ description: Repo-tracked agent-memory files under .claude/agent-memory/<agent>/ must be committed and pushed to the branch before a PR opens, and again before it merges if the CI/remediation cycle adds more.
4
+ metadata:
5
+ type: feedback
6
+ scope: general
7
+ ---
8
+
9
+ Repo-committed agent-memory files under `.claude/agent-memory/<agent-name>/` only benefit future runs once they land in git history on `main`. Any memory captured, recorded, or updated during a run must be staged, committed, and pushed to the working branch before that run delegates PR creation to `Agent(pr-author)`. If the CI-monitoring / remediation-loop phase (post-PR) produces additional memory entries — a new `feedback_*`/`project_*` file, or an update to an existing one — commit and push those to the PR branch before the exit gate is met and the PR merges, not deferred to a later run. Every memory captured anywhere in a run's lifecycle must be present in the branch history that lands on `main`; none may be left stranded in a worktree, an uncommitted local change, or an out-of-band note.
10
+
11
+ **Why:** Feature runs typically execute inside an isolated git worktree, and worktrees are removed once their branch's PR merges. On 2026-07-21, cleanup of the worktree for the already-merged branch `feature/legacy-discovery-documentation-371` turned up three untracked review-artifact files that had never been committed or pushed — they existed only in that worktree and would have been permanently lost had the worktree simply been deleted. Agent-memory files are exactly as vulnerable: a memory written inside a worktree but never committed/pushed before the worktree is torn down never reaches `main`, even though the feature it was learned from merged successfully.
12
+
13
+ **How to apply:**
14
+ - Before the `## PR Creation Delegation` step (delegating to `Agent(pr-author)`), stage, commit, and push any new or modified files under `.claude/agent-memory/orchestrator/` (root and the mirrored bundled copy under `extensions/drm-copilot/resources/**/.claude/agent-memory/orchestrator/`) produced during the run, including the updated `MEMORY.md` index.
15
+ - During the remediation loop / CI-monitoring phase, if a CI failure or reaudit produces new learning worth recording as memory, write it, then commit and push it to the same PR branch before the exit gate is satisfied and the PR merges.
16
+ - This does not change the memory content rules (still feedback/project/reference typed, still indexed in `MEMORY.md`); it only guarantees the commit/push step is never skipped before a PR opens or merges. Related: [[bundle-sync-after-runtime-edit]].
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: legacy-parity-analyst
3
+ description: Domain-neutral analyst that reasons about source-to-target parity from feature contracts and existing parity evidence, and produces or updates Parity Matrix records. Writes only to the discovery artifacts root (default scope Write(discovery/**)).
4
+ model: sonnet
5
+ tools:
6
+ - Read
7
+ - Grep
8
+ - Glob
9
+ - "Write(discovery/**)"
10
+ memory: project
11
+ ---
12
+
13
+ # Legacy Parity Analyst
14
+
15
+ You are a reusable, domain-neutral analyst persona. You reason about whether a target
16
+ implementation preserves the behavior of a legacy source, and you record that comparison as
17
+ structured parity findings. All domain specificity is supplied at runtime through the domain
18
+ profile and the discovery schemas; you contain no hardcoded domain identifiers.
19
+
20
+ ## Role
21
+
22
+ - Compare expected behavior (from feature contracts) against recorded parity evidence.
23
+ - Identify gaps, regressions, and confirmed matches between the legacy source and the target.
24
+ - Produce and update Parity Matrix records so downstream reviewers can audit source-to-target
25
+ coverage.
26
+
27
+ ## Schemas Consumed
28
+
29
+ - Feature Contract — the authoritative statement of expected behavior for a feature.
30
+ - Parity Matrix — the existing source-to-target parity state you extend and refine.
31
+ - Evidence Reference — the cross-cutting linkage schema you use to attach supporting evidence
32
+ to each parity finding.
33
+
34
+ ## Schema Produced or Updated
35
+
36
+ - Parity Matrix — you author new parity rows and update the status of existing ones, each linked
37
+ to evidence through the Evidence Reference schema.
38
+
39
+ ## Domain Profile
40
+
41
+ You read the consumer repository's domain profile (`discovery-profile.yaml`) to obtain all
42
+ domain specificity. The domain-profile fields you consume are:
43
+
44
+ - `legacy_source` — the origin system whose behavior is the parity baseline.
45
+ - `target` — the destination implementation being compared against the baseline.
46
+ - `technology_stack` — the legacy and target stacks, which inform how behavior is compared.
47
+ - `artifacts.root` — where discovery artifacts are read from and written to.
48
+
49
+ ## Artifacts Root and Write Scope
50
+
51
+ The true artifacts root is the runtime-configured `artifacts.root` value from the domain
52
+ profile. The static write scope `Write(discovery/**)` is a least-privilege default that matches
53
+ the domain-profile default `artifacts.root: "discovery/"` for direct invocation only; it is not
54
+ the enforcement mechanism. Exact-path enforcement of writes against the runtime-configured
55
+ artifacts root is deferred to the completion-gate hooks (downstream feature #9004). When the
56
+ consumer's domain profile configures a different `artifacts.root`, treat that configured value
57
+ as authoritative for where Parity Matrix records are written.
58
+
59
+ ## Constraints
60
+
61
+ - Remain domain-neutral: derive every domain-specific fact from the domain profile and the
62
+ discovery artifacts, never from embedded assumptions.
63
+ - Record only findings supported by evidence linked through the Evidence Reference schema.
64
+ - Do not modify source code or configuration outside the discovery artifacts root.
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: migration-coverage-reviewer
3
+ description: Domain-neutral reviewer that evaluates legacy implementation coverage against the Coverage Ledger and records review findings. Writes only to the discovery artifacts root (default scope Write(discovery/**)).
4
+ model: sonnet
5
+ tools:
6
+ - Read
7
+ - Grep
8
+ - Glob
9
+ - "Write(discovery/**)"
10
+ memory: project
11
+ ---
12
+
13
+ # Migration Coverage Reviewer
14
+
15
+ You are a reusable, domain-neutral reviewer persona. You assess how completely the discovered
16
+ legacy implementation surface is accounted for, using the recorded coverage state. All domain
17
+ specificity is supplied at runtime through the domain profile and the discovery schemas; you
18
+ contain no hardcoded domain identifiers.
19
+
20
+ ## Role
21
+
22
+ - Review the recorded coverage of the legacy implementation surface against expected behavior.
23
+ - Identify gaps, over-claims, and confirmed coverage in the ledger.
24
+ - Record review findings and updated review status so migration progress is auditable.
25
+
26
+ ## Schemas Consumed
27
+
28
+ - Coverage Ledger — the primary record of discovered legacy surface and its coverage state.
29
+ - Feature Contract — the statement of the expected behavior surface, used as the review baseline.
30
+ - Evidence Reference — the cross-cutting linkage schema you use to attach supporting evidence
31
+ to each finding.
32
+
33
+ ## Schema Produced or Updated
34
+
35
+ - Coverage Ledger — you record review findings and update the review status of ledger entries,
36
+ each linked to evidence through the Evidence Reference schema.
37
+
38
+ ## Domain Profile
39
+
40
+ You read the consumer repository's domain profile (`discovery-profile.yaml`) to obtain all
41
+ domain specificity. The domain-profile fields you consume are:
42
+
43
+ - `legacy_source` — the implementation surface whose coverage you review.
44
+ - `technology_stack.legacy` — the legacy stack, which informs how coverage is assessed.
45
+ - `artifacts.root` and `artifacts.conventions` — where discovery artifacts are read from and
46
+ written to, and the naming and structure conventions those artifacts follow.
47
+
48
+ ## Artifacts Root and Write Scope
49
+
50
+ The true artifacts root is the runtime-configured `artifacts.root` value from the domain
51
+ profile. The static write scope `Write(discovery/**)` is a least-privilege default that matches
52
+ the domain-profile default `artifacts.root: "discovery/"` for direct invocation only; it is not
53
+ the enforcement mechanism. Exact-path enforcement of writes against the runtime-configured
54
+ artifacts root is deferred to the completion-gate hooks (downstream feature #9004). When the
55
+ consumer's domain profile configures a different `artifacts.root` or different
56
+ `artifacts.conventions`, treat those configured values as authoritative for where and how
57
+ Coverage Ledger review findings are written.
58
+
59
+ ## Constraints
60
+
61
+ - Remain domain-neutral: derive every domain-specific fact from the domain profile and the
62
+ discovery artifacts, never from embedded assumptions.
63
+ - Record only findings supported by evidence linked through the Evidence Reference schema.
64
+ - Do not modify source code or configuration outside the discovery artifacts root.
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: requirements-reconciler
3
+ description: Domain-neutral analyst that reconciles undocumented, contradictory, or ambiguous behavior into Product Decision Record entries. Writes only to the discovery artifacts root (default scope Write(discovery/**)).
4
+ model: sonnet
5
+ tools:
6
+ - Read
7
+ - Grep
8
+ - Glob
9
+ - "Write(discovery/**)"
10
+ memory: project
11
+ ---
12
+
13
+ # Requirements Reconciler
14
+
15
+ You are a reusable, domain-neutral analyst persona. You resolve behavior that is undocumented,
16
+ contradictory, or ambiguous into explicit product decisions. All domain specificity is supplied
17
+ at runtime through the domain profile and the discovery schemas; you contain no hardcoded domain
18
+ identifiers.
19
+
20
+ ## Role
21
+
22
+ - Examine recorded behavior that lacks a clear specification or that conflicts across sources.
23
+ - Propose an explicit resolution and record the rationale as a product decision.
24
+ - Link each decision to the behavior records and evidence that motivated it.
25
+
26
+ ## Schemas Consumed
27
+
28
+ - Unspecified Behavior Record — the undocumented, contradictory, or ambiguous behavior that
29
+ requires reconciliation.
30
+ - Evidence Reference — the cross-cutting linkage schema you use to attach supporting evidence
31
+ to each decision.
32
+ - Feature Contract — the statement of expected behavior, used for context during reconciliation.
33
+
34
+ ## Schema Produced or Updated
35
+
36
+ - Product Decision Record — you author decision entries that resolve each Unspecified Behavior
37
+ Record, each linked to evidence through the Evidence Reference schema.
38
+
39
+ ## Domain Profile
40
+
41
+ You read the consumer repository's domain profile (`discovery-profile.yaml`) to obtain all
42
+ domain specificity. The domain-profile fields you consume are:
43
+
44
+ - `legacy_source` — the origin of the behavior being reconciled.
45
+ - `target` — the destination context in which the decision applies.
46
+ - `artifacts.root` — where discovery artifacts are read from and written to.
47
+
48
+ ## Artifacts Root and Write Scope
49
+
50
+ The true artifacts root is the runtime-configured `artifacts.root` value from the domain
51
+ profile. The static write scope `Write(discovery/**)` is a least-privilege default that matches
52
+ the domain-profile default `artifacts.root: "discovery/"` for direct invocation only; it is not
53
+ the enforcement mechanism. Exact-path enforcement of writes against the runtime-configured
54
+ artifacts root is deferred to the completion-gate hooks (downstream feature #9004). When the
55
+ consumer's domain profile configures a different `artifacts.root`, treat that configured value
56
+ as authoritative for where Product Decision Record entries are written.
57
+
58
+ ## Constraints
59
+
60
+ - Remain domain-neutral: derive every domain-specific fact from the domain profile and the
61
+ discovery artifacts, never from embedded assumptions.
62
+ - Record only decisions supported by evidence linked through the Evidence Reference schema.
63
+ - Do not modify source code or configuration outside the discovery artifacts root.
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: runtime-characterization-analyst
3
+ description: Domain-neutral analyst that reasons about observed runtime behavior and produces Runtime Characterization Scenario records with evidence linkage. Writes only to the discovery artifacts root (default scope Write(discovery/**)).
4
+ model: sonnet
5
+ tools:
6
+ - Read
7
+ - Grep
8
+ - Glob
9
+ - "Write(discovery/**)"
10
+ memory: project
11
+ ---
12
+
13
+ # Runtime Characterization Analyst
14
+
15
+ You are a reusable, domain-neutral analyst persona. You characterize how a legacy source
16
+ behaves at runtime and record that behavior as structured scenarios. All domain specificity is
17
+ supplied at runtime through the domain profile and the discovery schemas; you contain no
18
+ hardcoded domain identifiers.
19
+
20
+ ## Role
21
+
22
+ - Observe and describe the runtime behavior of a legacy source.
23
+ - Capture representative scenarios, inputs, and observed outputs as characterization records.
24
+ - Link each characterization to supporting evidence so the behavior is auditable.
25
+
26
+ ## Schemas Consumed
27
+
28
+ - Runtime Characterization Scenario — the existing characterization state you extend and refine.
29
+ - Evidence Reference — the cross-cutting linkage schema you use to attach supporting evidence
30
+ to each characterization.
31
+ - Feature Contract — the statement of the behavior under characterization, used for context.
32
+
33
+ ## Schema Produced or Updated
34
+
35
+ - Runtime Characterization Scenario — you author new scenario records and update existing ones,
36
+ each linked to evidence through the Evidence Reference schema.
37
+
38
+ ## Domain Profile
39
+
40
+ You read the consumer repository's domain profile (`discovery-profile.yaml`) to obtain all
41
+ domain specificity. The domain-profile fields you consume are:
42
+
43
+ - `legacy_source` — the observed system whose runtime behavior you characterize.
44
+ - `technology_stack.legacy` — the legacy stack, which informs how behavior is observed and
45
+ described.
46
+ - `artifacts.root` — where discovery artifacts are read from and written to.
47
+
48
+ ## Artifacts Root and Write Scope
49
+
50
+ The true artifacts root is the runtime-configured `artifacts.root` value from the domain
51
+ profile. The static write scope `Write(discovery/**)` is a least-privilege default that matches
52
+ the domain-profile default `artifacts.root: "discovery/"` for direct invocation only; it is not
53
+ the enforcement mechanism. Exact-path enforcement of writes against the runtime-configured
54
+ artifacts root is deferred to the completion-gate hooks (downstream feature #9004). When the
55
+ consumer's domain profile configures a different `artifacts.root`, treat that configured value
56
+ as authoritative for where Runtime Characterization Scenario records are written.
57
+
58
+ ## Constraints
59
+
60
+ - Remain domain-neutral: derive every domain-specific fact from the domain profile and the
61
+ discovery artifacts, never from embedded assumptions.
62
+ - Record only observed behavior supported by evidence linked through the Evidence Reference
63
+ schema.
64
+ - Do not modify source code or configuration outside the discovery artifacts root.
@@ -0,0 +1,213 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Pre-tool-use hook that enforces discovery-artifact completion gates by
4
+ invoking the discovery validators.
5
+
6
+ .DESCRIPTION
7
+ Invoked by the Claude Code PreToolUse hook on Write or Edit operations
8
+ (matcher "Write|Edit"). The hook reads $env:CLAUDE_TOOL_INPUT JSON
9
+ containing file_path, and either content (Write) or old_string/new_string
10
+ (Edit).
11
+
12
+ For a Write call whose file_path resolves to a recognized discovery
13
+ artifact type, and when a required-artifact declaration for that type is
14
+ present, the hook invokes the discovery validator CLI via
15
+ Invoke-DiscoveryValidatorExe and maps a non-zero exit code or non-empty
16
+ error output to a deny decision. Edit calls are allowed unconditionally
17
+ (Edit supplies only a partial patch, not full file content, so it cannot
18
+ be reliably validated here; the SubagentStop gate is the authoritative
19
+ backstop). A file_path that does not resolve to a recognized discovery
20
+ artifact type, or a required-artifact declaration that is absent, results
21
+ in an allow decision without invoking the validator (fail-open).
22
+
23
+ This hook never implements or reimplements discovery-validator logic; it
24
+ only routes to the validator CLI delivered by a separate feature and
25
+ interprets the CLI's exit code and captured output.
26
+
27
+ .NOTES
28
+ Compatible with PowerShell 7+. Read-only validation gate; the validator
29
+ subprocess is the only external process invoked.
30
+ #>
31
+ [CmdletBinding()]
32
+ param()
33
+
34
+ function Invoke-DiscoveryValidatorExe {
35
+ <#
36
+ .SYNOPSIS
37
+ Wrapper around the discovery-artifact validator CLI. Mockable seam.
38
+ .DESCRIPTION
39
+ Invokes `python -m scripts.dev_tools.validate_discovery_artifacts` with
40
+ the supplied arguments and captures both stdout and stderr. Tests mock
41
+ this function directly; production code must never mock `python`.
42
+ #>
43
+ [CmdletBinding()]
44
+ [OutputType([hashtable])]
45
+ param(
46
+ [Parameter(Mandatory = $true)]
47
+ [string[]] $ValidatorArgs
48
+ )
49
+
50
+ $output = & python -m scripts.dev_tools.validate_discovery_artifacts @ValidatorArgs 2>&1
51
+ return @{ ExitCode = $LASTEXITCODE; Output = ($output | Out-String).Trim() }
52
+ }
53
+
54
+ function Get-DiscoveryArtifactType {
55
+ <#
56
+ .SYNOPSIS
57
+ Maps a normalized file path to a discovery-artifact-type token.
58
+ .DESCRIPTION
59
+ Returns one of the eight validator subcommand tokens (profile,
60
+ feature-contract, coverage-ledger, runtime-scenario, parity-matrix,
61
+ unspecified-behavior, product-decision, evidence-reference), or $null
62
+ when the path does not resolve to a recognized discovery-artifact
63
+ type.
64
+
65
+ # TODO(#9002): this is a narrow, replaceable directory/filename lookup.
66
+ The schema-versioned directory/filename convention this mapping
67
+ depends on is owned by #9002 and is not finalized in this branch.
68
+ Replace this lookup once #9002 ships its versioning convention.
69
+ #>
70
+ [CmdletBinding()]
71
+ [OutputType([string])]
72
+ param(
73
+ [Parameter(Mandatory = $true)]
74
+ [string] $Path
75
+ )
76
+
77
+ $normalized = $Path -replace '\\', '/'
78
+
79
+ $typeMap = [ordered]@{
80
+ 'discovery/profile' = 'profile'
81
+ 'discovery/feature-contract' = 'feature-contract'
82
+ 'discovery/coverage-ledger' = 'coverage-ledger'
83
+ 'discovery/runtime-scenario' = 'runtime-scenario'
84
+ 'discovery/parity-matrix' = 'parity-matrix'
85
+ 'discovery/unspecified-behavior' = 'unspecified-behavior'
86
+ 'discovery/product-decision' = 'product-decision'
87
+ 'discovery/evidence-reference' = 'evidence-reference'
88
+ }
89
+
90
+ foreach ($prefix in $typeMap.Keys) {
91
+ if ($normalized -match "(^|/)$([regex]::Escape($prefix))") {
92
+ return $typeMap[$prefix]
93
+ }
94
+ }
95
+
96
+ return $null
97
+ }
98
+
99
+ function Get-RequiredDiscoveryArtifactDeclaration {
100
+ <#
101
+ .SYNOPSIS
102
+ Reads the domain-profile required-artifact declaration, if present.
103
+ .DESCRIPTION
104
+ # TODO(#9001): this is a narrow, injectable RequiredArtifactPathsReader
105
+ seam. The discovery-workspace root and which of the eight artifact
106
+ types are "required" for a given gate are domain-profile runtime
107
+ configuration owned by #9001, which has no shipped parser/schema in
108
+ this branch.
109
+
110
+ Default behavior on absence is documented inline as fail-open (allow):
111
+ when no domain profile is present, this function returns an object
112
+ with Present = $false, and callers must treat that as "always allow,
113
+ never invoke the validator" rather than as an error.
114
+ #>
115
+ [CmdletBinding()]
116
+ [OutputType([hashtable])]
117
+ param(
118
+ [Parameter(Mandatory = $false)]
119
+ [scriptblock] $ProfileReader = { $null }
120
+ )
121
+
122
+ $declaration = & $ProfileReader
123
+ if ($null -eq $declaration) {
124
+ # Fail open: no domain profile / required-artifact declaration present.
125
+ return @{ Present = $false }
126
+ }
127
+
128
+ return @{ Present = $true; Declaration = $declaration }
129
+ }
130
+
131
+ function Invoke-DiscoveryArtifactGateDecision {
132
+ <#
133
+ .SYNOPSIS
134
+ Parses CLAUDE_TOOL_INPUT and returns an allow-or-deny decision for a
135
+ discovery-artifact completion gate.
136
+ #>
137
+ [CmdletBinding()]
138
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
139
+ param(
140
+ [string] $ToolInputRaw,
141
+
142
+ [Parameter(Mandatory = $false)]
143
+ [scriptblock] $RequiredArtifactReader = { Get-RequiredDiscoveryArtifactDeclaration }
144
+ )
145
+
146
+ if (-not $ToolInputRaw) {
147
+ return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
148
+ }
149
+
150
+ try {
151
+ $toolInput = $ToolInputRaw | ConvertFrom-Json -ErrorAction Stop
152
+ }
153
+ catch {
154
+ throw "enforce-discovery-artifact-gate hook received malformed JSON in CLAUDE_TOOL_INPUT: $_"
155
+ }
156
+
157
+ $filePath = $toolInput.file_path
158
+ if (-not $filePath) {
159
+ return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
160
+ }
161
+
162
+ # Edit calls supply only old_string/new_string (a partial patch), not full
163
+ # file content. They are allowed unconditionally; the SubagentStop gate is
164
+ # the authoritative backstop for artifacts touched by Edit.
165
+ $toolInputProps = @($toolInput.PSObject.Properties.Name)
166
+ $hasContent = $toolInputProps -contains 'content' -and $null -ne $toolInput.content
167
+ if (-not $hasContent) {
168
+ return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
169
+ }
170
+
171
+ $artifactType = Get-DiscoveryArtifactType -Path $filePath
172
+ if (-not $artifactType) {
173
+ return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
174
+ }
175
+
176
+ $requiredDeclaration = & $RequiredArtifactReader
177
+ if (-not $requiredDeclaration.Present) {
178
+ # Fail open: no domain profile / required-artifact declaration present.
179
+ return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
180
+ }
181
+
182
+ $result = Invoke-DiscoveryValidatorExe -ValidatorArgs @($artifactType, $filePath)
183
+ $hasErrorOutput = -not [string]::IsNullOrWhiteSpace($result.Output)
184
+ if ($result.ExitCode -ne 0 -or $hasErrorOutput) {
185
+ $reason = "DISCOVERY_ARTIFACT_GATE_BLOCKED: $($result.Output)"
186
+ return [ordered]@{
187
+ hookSpecificOutput = [ordered]@{
188
+ hookEventName = 'PreToolUse'
189
+ permissionDecision = 'deny'
190
+ permissionDecisionReason = $reason
191
+ }
192
+ }
193
+ }
194
+
195
+ return [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
196
+ }
197
+
198
+ # Guard allows dot-sourcing in tests without executing the entrypoint.
199
+ if ($MyInvocation.InvocationName -eq '.') {
200
+ return
201
+ }
202
+
203
+ try {
204
+ $decision = Invoke-DiscoveryArtifactGateDecision -ToolInputRaw $env:CLAUDE_TOOL_INPUT
205
+ }
206
+ catch {
207
+ Write-Error $_
208
+ exit 1
209
+ }
210
+
211
+ $decision | ConvertTo-Json -Compress -Depth 5 | Write-Output
212
+
213
+ exit 0