@danmoisan/drm-copilot-mcp 0.0.1 → 0.0.6
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.
- package/out/mcp-server.js +98 -21
- package/package.json +21 -5
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/MEMORY.md +15 -3
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_branch_base_check_unmerged_pr_deps.md +16 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_every_change_through_lifecycle.md +15 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_policy_compliance_not_optional.md +18 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_potential_to_issue_creates_github_issue.md +13 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_remediation_plan_em_dash_required.md +13 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_small_bug_uses_minor_audit.md +13 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_test_files_count_against_500_cap.md +13 -0
- package/resources/claude-customizations/.claude/agents/atomic-executor.md +7 -7
- package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +4 -5
- package/resources/claude-customizations/.claude/agents/feature-review.md +7 -3
- package/resources/claude-customizations/.claude/agents/orchestrator.md +22 -3
- package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +1 -1
- package/resources/claude-customizations/.claude/agents/pr-author.md +78 -0
- package/resources/claude-customizations/.claude/agents/task-researcher.md +10 -6
- package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +303 -0
- package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +300 -0
- package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +40 -14
- package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +148 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +210 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +333 -0
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +216 -0
- package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +84 -15
- package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +1 -1
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +75 -5
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +93 -0
- package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +136 -0
- package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +90 -6
- package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +46 -0
- package/resources/claude-customizations/.claude/rules/benchmark-baselines.md +35 -0
- package/resources/claude-customizations/.claude/rules/ci-workflows.md +36 -0
- package/resources/claude-customizations/.claude/rules/csharp.md +62 -16
- package/resources/claude-customizations/.claude/rules/general-code-change.md +12 -3
- package/resources/claude-customizations/.claude/rules/general-unit-test.md +47 -2
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +39 -0
- package/resources/claude-customizations/.claude/rules/powershell.md +5 -5
- package/resources/claude-customizations/.claude/rules/python.md +4 -3
- package/resources/claude-customizations/.claude/rules/quality-tiers.md +51 -0
- package/resources/claude-customizations/.claude/rules/typescript.md +37 -8
- package/resources/claude-customizations/.claude/settings.json +59 -12
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +2 -2
- package/resources/claude-customizations/.claude/skills/csharp-qa-gate/SKILL.md +25 -10
- package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +0 -1
- package/resources/claude-customizations/.claude/skills/execute-hard-lock/SKILL.md +6 -6
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +8 -8
- package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +17 -6
- package/resources/claude-customizations/.claude/skills/human-exception-runbook/SKILL.md +52 -0
- package/resources/claude-customizations/.claude/skills/human-exception-runbook/example.runbook.md +36 -0
- package/resources/claude-customizations/.claude/skills/invoke-csharp-engineer/SKILL.md +4 -4
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +172 -5
- package/resources/claude-customizations/.claude/skills/policy-audit-template-usage/SKILL.md +3 -3
- package/resources/claude-customizations/.claude/skills/powershell-qa-gate/SKILL.md +4 -4
- package/resources/claude-customizations/.claude/skills/pr-base-branch-merge-base/SKILL.md +3 -3
- package/resources/claude-customizations/.claude/skills/python-qa-gate/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +90 -17
- package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +5 -3
- package/resources/claude-customizations/.claude-variants/csharp-legacy/agents/csharp-typed-engineer.md +69 -0
- package/resources/claude-customizations/.claude-variants/csharp-legacy/rules/csharp.md +96 -0
- package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +77 -0
- package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +64 -0
- package/resources/claude-customizations/pack-manifests/core.json +65 -0
- package/resources/claude-customizations/pack-manifests/csharp-legacy.json +11 -0
- package/resources/claude-customizations/pack-manifests/csharp-modern.json +12 -0
- package/resources/claude-customizations/pack-manifests/powershell.json +14 -0
- package/resources/claude-customizations/pack-manifests/python.json +14 -0
- package/resources/claude-customizations/pack-manifests/typescript.json +9 -0
- package/resources/claude-dir-customizations/.mcp.json +3 -3
- package/resources/codex-and-agents-customizations/.agents/README.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +52 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +16 -8
- package/resources/codex-and-agents-customizations/.agents/skills/benchmark-baselines/SKILL.md +44 -0
- package/resources/codex-and-agents-customizations/.agents/skills/ci-workflows/SKILL.md +45 -0
- package/resources/codex-and-agents-customizations/.agents/skills/commit-message/SKILL.md +3 -11
- package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +1 -5
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/SKILL.md +1 -6
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-orchestration-state-machine/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/execute-hard-lock/SKILL.md +8 -17
- package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +41 -25
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +1 -6
- package/resources/codex-and-agents-customizations/.agents/skills/fill-feature-docs/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +86 -0
- package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +111 -0
- package/resources/codex-and-agents-customizations/.agents/skills/human-exception-runbook/SKILL.md +57 -0
- package/resources/codex-and-agents-customizations/.agents/skills/human-exception-runbook/example.runbook.md +36 -0
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-csharp-engineer/SKILL.md +0 -9
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +0 -9
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-python-engineer/SKILL.md +0 -9
- package/resources/codex-and-agents-customizations/.agents/skills/make-skill-template/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +206 -18
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +48 -0
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +94 -10
- package/resources/codex-and-agents-customizations/.agents/skills/policy-audit-template-usage/SKILL.md +3 -8
- package/resources/codex-and-agents-customizations/.agents/skills/policy-compliance-order/SKILL.md +0 -10
- package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +4 -8
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +1 -6
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-orchestration-state-machine/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-qa-gate/SKILL.md +3 -9
- package/resources/codex-and-agents-customizations/.agents/skills/pr-author/SKILL.md +1 -9
- package/resources/codex-and-agents-customizations/.agents/skills/pr-base-branch-merge-base/SKILL.md +4 -9
- package/resources/codex-and-agents-customizations/.agents/skills/pr-context-artifacts/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/python/SKILL.md +1 -5
- package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +1 -6
- package/resources/codex-and-agents-customizations/.agents/skills/python-qa-gate/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/python-suppressions/SKILL.md +2 -6
- package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +57 -0
- package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +96 -72
- package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/agents/openai.yaml +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +0 -10
- package/resources/codex-and-agents-customizations/.agents/skills/review-epic/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/review-feature/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/review-staged/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/self-explanatory-code-commenting/SKILL.md +2 -6
- package/resources/codex-and-agents-customizations/.agents/skills/skill-canonical-location-audit/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/tonality/SKILL.md +86 -0
- package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +297 -0
- package/resources/codex-and-agents-customizations/.agents/skills/translate-copilot-to-claude/SKILL.md +0 -22
- package/resources/codex-and-agents-customizations/.agents/skills/typescript/SKILL.md +1 -5
- package/resources/codex-and-agents-customizations/.agents/skills/typescript-suppressions/SKILL.md +2 -6
- package/resources/codex-and-agents-customizations/.agents/skills/update-status/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +5 -5
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +91 -63
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-executor.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/config.toml +111 -137
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +303 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +300 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +148 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-pr-author-skill.ps1 +336 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +84 -18
- package/resources/codex-and-agents-customizations/.codex/prompts/orchestrate-work.md +4 -3
- package/resources/codex-and-agents-customizations/.codex/scripts/post-codex-worktree-session.ps1 +5 -0
- package/resources/codex-and-agents-customizations/.github/workflows/_validate-orchestrator-state.yml +68 -0
- package/resources/codex-and-agents-customizations/.github/workflows/validate-orchestrator-state.yml +15 -0
- package/resources/config/orchestration-routing.json +76 -0
- package/resources/customizations/.github/agents/Powershell DI Unit Test Engineer.agent.md +1 -1
- package/resources/customizations/.github/agents/atomic_executor.agent.md +1 -1
- package/resources/customizations/.github/agents/atomic_planning.agent.md +10 -10
- package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +6 -2
- package/resources/customizations/.github/agents/feature-review.agent.md +2 -2
- package/resources/customizations/.github/agents/orchestrator.agent.md +6 -2
- package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +4 -4
- package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +10 -10
- package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +6 -2
- package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +2 -2
- package/resources/customizations/.github/agents/pr-author.agent.md +25 -0
- package/resources/customizations/.github/agents/python-orchestrator.agent.md +6 -2
- package/resources/customizations/.github/agents/staged-review.agent.md +1 -1
- package/resources/customizations/.github/agents/task-researcher.agent.md +4 -4
- package/resources/customizations/.github/instructions/powershell-code-change.instructions.md +6 -6
- package/resources/customizations/.github/prompts/fillout-prd-feature.prompt.md +1 -1
- package/resources/customizations/.github/prompts/generate-commit-message-repo.prompt.md +1 -1
- package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +5 -3
- package/resources/customizations/.github/prompts/orchestrate-work.prompt.md +5 -3
- package/resources/customizations/.github/prompts/research-issue.prompt.md +4 -3
- package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +14 -1
- package/resources/customizations/.github/skills/feature-promotion-lifecycle/SKILL.md +11 -7
- package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +10 -1
- package/resources/customizations/.github/skills/pr-base-branch-merge-base/SKILL.md +2 -2
- package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +5 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +7 -0
- package/resources/scripts/dev_tools/_orchestrator_state_human_interaction.py +127 -0
- package/resources/scripts/dev_tools/_orchestrator_state_routing.py +216 -0
- package/resources/scripts/dev_tools/push_down_claude_customizations.py +276 -61
- package/resources/scripts/dev_tools/push_down_claude_filesystem.py +472 -0
- package/resources/scripts/dev_tools/push_down_claude_pack_selection.py +401 -0
- package/resources/scripts/dev_tools/validate_orchestration_artifacts.py +103 -411
- package/resources/scripts/dev_tools/validate_orchestration_review_artifacts.py +107 -0
- package/resources/scripts/dev_tools/validate_orchestrator_state.py +505 -0
- package/resources/scripts/dev_tools/validate_policy_audit_artifact.py +472 -0
- package/resources/templates/push_down_claude_customizations.py +162 -117
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_repo_root_is_source_of_truth.md +0 -11
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_vsce_verify_package_location.md +0 -19
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/project_extension_location.md +0 -11
- package/resources/claude-customizations/.claude/agent-memory/prd-feature/MEMORY.md +0 -1
- package/resources/claude-customizations/.claude/agent-memory/prd-feature/project_push_down_pattern.md +0 -13
- package/resources/claude-customizations/.claude/agent-memory/task-researcher/MEMORY.md +0 -3
- package/resources/claude-customizations/.claude/agent-memory/task-researcher/project_push_down_claude_dir.md +0 -11
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
SubagentStop hook that verifies the pr-author agent reported a created or updated PR.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Invoked by the Claude Code SubagentStop hook for the pr-author matcher. Reads the agent
|
|
7
|
+
transcript JSON from the CLAUDE_HOOK_INPUT environment variable, extracts the .output field,
|
|
8
|
+
and confirms the output reports that a pull request was created or updated. The hook allows
|
|
9
|
+
(exit 0) when the output contains:
|
|
10
|
+
- a GitHub PR URL (github.com/<owner>/<repo>/pull/<n>), or
|
|
11
|
+
- a PR reference of the form "PR #<n>", or
|
|
12
|
+
- a "gh pr create"/"gh pr edit" confirmation that includes a PR number.
|
|
13
|
+
The hook blocks (exit 1) when:
|
|
14
|
+
- CLAUDE_HOOK_INPUT is empty,
|
|
15
|
+
- the input is not valid JSON,
|
|
16
|
+
- the .output field is empty, or
|
|
17
|
+
- the output contains no PR URL or PR number.
|
|
18
|
+
|
|
19
|
+
.NOTES
|
|
20
|
+
Compatible with PowerShell 7+. No external module dependencies.
|
|
21
|
+
|
|
22
|
+
Enforcement strength: this validator is a policy guardrail, not a cryptographic or security
|
|
23
|
+
control. It confirms that the pr-author agent's final output references a PR; it cannot verify
|
|
24
|
+
that the referenced PR actually exists on GitHub, and the output text is forgeable by any actor
|
|
25
|
+
that controls the agent transcript. It MUST NOT be described as tamper-proof or as a security
|
|
26
|
+
boundary.
|
|
27
|
+
#>
|
|
28
|
+
[CmdletBinding()]
|
|
29
|
+
param()
|
|
30
|
+
|
|
31
|
+
function Test-PrAuthorOutputReportsPr {
|
|
32
|
+
<#
|
|
33
|
+
.SYNOPSIS
|
|
34
|
+
Return $true when the supplied output text reports a created or updated PR.
|
|
35
|
+
.DESCRIPTION
|
|
36
|
+
Detection helper (injectable boundary for tests). Matches a GitHub PR URL, a "PR #<n>"
|
|
37
|
+
reference, or a "gh pr create"/"gh pr edit" confirmation that contains a PR number.
|
|
38
|
+
.PARAMETER OutputText
|
|
39
|
+
The pr-author agent's final output text extracted from CLAUDE_HOOK_INPUT.output.
|
|
40
|
+
.OUTPUTS
|
|
41
|
+
System.Boolean
|
|
42
|
+
#>
|
|
43
|
+
[CmdletBinding()]
|
|
44
|
+
[OutputType([bool])]
|
|
45
|
+
param(
|
|
46
|
+
[string] $OutputText
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
if ([string]::IsNullOrWhiteSpace($OutputText)) {
|
|
50
|
+
return $false
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# GitHub PR URL, e.g. https://github.com/owner/repo/pull/123
|
|
54
|
+
if ($OutputText -match '(?i)github\.com/[^\s/]+/[^\s/]+/pull/\d+') {
|
|
55
|
+
return $true
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# Explicit PR number reference, e.g. "PR #123".
|
|
59
|
+
if ($OutputText -match '(?i)\bPR\s*#\d+') {
|
|
60
|
+
return $true
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# gh pr create / gh pr edit confirmation that includes a PR number anywhere in the output.
|
|
64
|
+
if (($OutputText -match '(?i)\bgh\s+pr\s+(create|edit)\b') -and ($OutputText -match '#\d+')) {
|
|
65
|
+
return $true
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return $false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function Get-PrAuthorOutputDecision {
|
|
72
|
+
<#
|
|
73
|
+
.SYNOPSIS
|
|
74
|
+
Parse CLAUDE_HOOK_INPUT and return an allow-or-block decision with exit code semantics.
|
|
75
|
+
.DESCRIPTION
|
|
76
|
+
Returns an ordered dictionary with 'allowed' (bool) and 'reason' (string). Allowed is $true
|
|
77
|
+
only when the parsed .output reports a PR per Test-PrAuthorOutputReportsPr. Empty input,
|
|
78
|
+
malformed JSON, empty output, and PR-less output all yield allowed = $false.
|
|
79
|
+
.PARAMETER HookInputRaw
|
|
80
|
+
The raw JSON transcript payload supplied by Claude Code via CLAUDE_HOOK_INPUT.
|
|
81
|
+
.OUTPUTS
|
|
82
|
+
System.Collections.Specialized.OrderedDictionary
|
|
83
|
+
#>
|
|
84
|
+
[CmdletBinding()]
|
|
85
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
86
|
+
param(
|
|
87
|
+
[string] $HookInputRaw
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
if ([string]::IsNullOrWhiteSpace($HookInputRaw)) {
|
|
91
|
+
return [ordered]@{
|
|
92
|
+
allowed = $false
|
|
93
|
+
reason = 'PR_AUTHOR_OUTPUT_MISSING: CLAUDE_HOOK_INPUT is empty; the pr-author agent produced no transcript to validate.'
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
$parsed = $HookInputRaw | ConvertFrom-Json -ErrorAction Stop
|
|
99
|
+
} catch {
|
|
100
|
+
return [ordered]@{
|
|
101
|
+
allowed = $false
|
|
102
|
+
reason = 'PR_AUTHOR_OUTPUT_MALFORMED: CLAUDE_HOOK_INPUT is not valid JSON.'
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
$outputText = [string]$parsed.output
|
|
107
|
+
if ([string]::IsNullOrWhiteSpace($outputText)) {
|
|
108
|
+
return [ordered]@{
|
|
109
|
+
allowed = $false
|
|
110
|
+
reason = 'PR_AUTHOR_OUTPUT_EMPTY: the pr-author agent output is empty; it must report the created or updated PR URL or number.'
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (Test-PrAuthorOutputReportsPr -OutputText $outputText) {
|
|
115
|
+
return [ordered]@{ allowed = $true }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return [ordered]@{
|
|
119
|
+
allowed = $false
|
|
120
|
+
reason = 'PR_AUTHOR_OUTPUT_NO_PR: the pr-author agent output does not reference a PR URL or PR number; it must report the created or updated pull request.'
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# Allow dot-sourcing in tests without executing the entrypoint.
|
|
125
|
+
if ($MyInvocation.InvocationName -eq '.') {
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
$decision = Get-PrAuthorOutputDecision -HookInputRaw $env:CLAUDE_HOOK_INPUT
|
|
130
|
+
|
|
131
|
+
if (-not $decision['allowed']) {
|
|
132
|
+
Write-Error $decision['reason']
|
|
133
|
+
exit 1
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
exit 0
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
.DESCRIPTION
|
|
6
6
|
Blocks termination of the task-researcher subagent unless the agent's
|
|
7
7
|
final output advertises a `research-path` token, the path is rooted
|
|
8
|
-
under
|
|
9
|
-
|
|
8
|
+
under one of the two tracked research roots
|
|
9
|
+
(docs/features/<feature>/research/ for feature-associated research, or
|
|
10
|
+
docs/research/ for one-off research), matches the documented filename
|
|
11
|
+
convention, and the file exists on disk.
|
|
10
12
|
|
|
11
13
|
.NOTES
|
|
12
14
|
Reads the hook payload from CLAUDE_HOOK_INPUT as JSON. Exits 0 to allow
|
|
@@ -64,7 +66,20 @@ function Test-IsUnderResearchRoot {
|
|
|
64
66
|
)
|
|
65
67
|
|
|
66
68
|
$normalized = $Path -replace '\\', '/'
|
|
67
|
-
|
|
69
|
+
|
|
70
|
+
# Accept either of the two tracked research roots:
|
|
71
|
+
# - feature-associated research: docs/features/<...>/research/<file>
|
|
72
|
+
# (a feature-folder path that contains a /research/ segment), or
|
|
73
|
+
# - one-off research: docs/research/<file>.
|
|
74
|
+
# The /research/ segment requirement distinguishes feature research from
|
|
75
|
+
# other files under docs/features/ (for example spec.md or plan files).
|
|
76
|
+
$isFeatureResearch =
|
|
77
|
+
$normalized.StartsWith('docs/features/', [System.StringComparison]::OrdinalIgnoreCase) -and
|
|
78
|
+
$normalized.IndexOf('/research/', [System.StringComparison]::OrdinalIgnoreCase) -ge 0
|
|
79
|
+
$isOneOffResearch =
|
|
80
|
+
$normalized.StartsWith('docs/research/', [System.StringComparison]::OrdinalIgnoreCase)
|
|
81
|
+
|
|
82
|
+
return ($isFeatureResearch -or $isOneOffResearch)
|
|
68
83
|
}
|
|
69
84
|
|
|
70
85
|
function Test-IsValidResearchFileName {
|
|
@@ -83,6 +98,69 @@ function Test-IsValidResearchFileName {
|
|
|
83
98
|
)
|
|
84
99
|
}
|
|
85
100
|
|
|
101
|
+
function Test-AutomationFeasibilitySection {
|
|
102
|
+
<#
|
|
103
|
+
.SYNOPSIS
|
|
104
|
+
Enforces the '## Automation Feasibility' section for applicable
|
|
105
|
+
autonomous-execution research artifacts.
|
|
106
|
+
.DESCRIPTION
|
|
107
|
+
Returns a hashtable with keys:
|
|
108
|
+
- Ok: $true when the artifact is not applicable, or it is
|
|
109
|
+
applicable and contains the '## Automation Feasibility'
|
|
110
|
+
section.
|
|
111
|
+
- Message: rejection message; $null on success.
|
|
112
|
+
|
|
113
|
+
Detection is narrow (OD-45-7): the section is required only when the
|
|
114
|
+
research filename or the agent output contains an autonomous-execution
|
|
115
|
+
token (for example 'autonomous-execution' or 'human-interaction').
|
|
116
|
+
Non-matching research artifacts pass unaffected.
|
|
117
|
+
|
|
118
|
+
ReadFileContent is an injectable scriptblock so tests can supply the
|
|
119
|
+
research file body without writing temporary files. It defaults to
|
|
120
|
+
Get-Content -Raw.
|
|
121
|
+
#>
|
|
122
|
+
[CmdletBinding()]
|
|
123
|
+
[OutputType([hashtable])]
|
|
124
|
+
param(
|
|
125
|
+
[Parameter(Mandatory = $true)]
|
|
126
|
+
[string] $ResearchFilePath,
|
|
127
|
+
|
|
128
|
+
[Parameter(Mandatory = $true)]
|
|
129
|
+
[AllowEmptyString()]
|
|
130
|
+
[string] $AgentOutput,
|
|
131
|
+
|
|
132
|
+
[Parameter(Mandatory = $false)]
|
|
133
|
+
[scriptblock] $ReadFileContent = { param($Path) Get-Content -LiteralPath $Path -Raw -ErrorAction Stop }
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
$detectionPattern = 'autonomous-execution|human-interaction'
|
|
137
|
+
$fileName = [System.IO.Path]::GetFileName(($ResearchFilePath -replace '\\', '/'))
|
|
138
|
+
|
|
139
|
+
$isApplicable = ([regex]::IsMatch($fileName, $detectionPattern, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)) -or
|
|
140
|
+
([regex]::IsMatch($AgentOutput, $detectionPattern, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase))
|
|
141
|
+
|
|
142
|
+
if (-not $isApplicable) {
|
|
143
|
+
return @{ Ok = $true; Message = $null }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
$content = & $ReadFileContent $ResearchFilePath
|
|
147
|
+
if ([string]::IsNullOrWhiteSpace($content)) {
|
|
148
|
+
return @{ Ok = $false; Message = "task-researcher hook: autonomous-execution research artifact '$ResearchFilePath' is empty; it must include an '## Automation Feasibility' section." }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
$hasSection = [regex]::IsMatch(
|
|
152
|
+
$content,
|
|
153
|
+
'(?m)^\s{0,3}#{2,}\s+Automation\s+Feasibility\s*$',
|
|
154
|
+
[System.Text.RegularExpressions.RegexOptions]::IgnoreCase
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
if (-not $hasSection) {
|
|
158
|
+
return @{ Ok = $false; Message = "task-researcher hook: autonomous-execution research artifact '$ResearchFilePath' is missing the required '## Automation Feasibility' section." }
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return @{ Ok = $true; Message = $null }
|
|
162
|
+
}
|
|
163
|
+
|
|
86
164
|
function Invoke-TaskResearcherOutputValidation {
|
|
87
165
|
[CmdletBinding()]
|
|
88
166
|
[OutputType([hashtable])]
|
|
@@ -111,21 +189,26 @@ function Invoke-TaskResearcherOutputValidation {
|
|
|
111
189
|
|
|
112
190
|
$researchPath = Get-ResearchPathFromOutput -AgentOutput $agentOutput
|
|
113
191
|
if ($null -eq $researchPath) {
|
|
114
|
-
return @{ Ok = $false; Message = 'task-researcher hook: agent output does not advertise a research-path. Researcher must report `research-path: <path>` pointing to
|
|
192
|
+
return @{ Ok = $false; Message = 'task-researcher hook: agent output does not advertise a research-path. Researcher must report `research-path: <path>` pointing to docs/features/<feature>/research/ (feature-associated) or docs/research/ (one-off).' }
|
|
115
193
|
}
|
|
116
194
|
|
|
117
195
|
if (-not (Test-IsUnderResearchRoot -Path $researchPath)) {
|
|
118
|
-
return @{ Ok = $false; Message = "task-researcher hook: research-path '$researchPath' is not under
|
|
196
|
+
return @{ Ok = $false; Message = "task-researcher hook: research-path '$researchPath' is not under a tracked research root. All research artifacts must be written to docs/features/<feature>/research/ (feature-associated) or docs/research/ (one-off)." }
|
|
119
197
|
}
|
|
120
198
|
|
|
121
199
|
if (-not (Test-IsValidResearchFileName -Path $researchPath)) {
|
|
122
|
-
return @{ Ok = $false; Message = "task-researcher hook: research-path '$researchPath' does not match the required filename convention
|
|
200
|
+
return @{ Ok = $false; Message = "task-researcher hook: research-path '$researchPath' does not match the required filename convention `<timestamp>-<short-name>-research.md` under docs/features/<feature>/research/ or docs/research/." }
|
|
123
201
|
}
|
|
124
202
|
|
|
125
203
|
if (-not (Test-ResearchFile -Path $researchPath)) {
|
|
126
204
|
return @{ Ok = $false; Message = "task-researcher hook: researcher advertised research-path '$researchPath' but no file exists at that location." }
|
|
127
205
|
}
|
|
128
206
|
|
|
207
|
+
$feasibilityResult = Test-AutomationFeasibilitySection -ResearchFilePath $researchPath -AgentOutput $agentOutput
|
|
208
|
+
if (-not $feasibilityResult.Ok) {
|
|
209
|
+
return @{ Ok = $false; Message = $feasibilityResult.Message }
|
|
210
|
+
}
|
|
211
|
+
|
|
129
212
|
return @{ Ok = $true; Message = $null }
|
|
130
213
|
}
|
|
131
214
|
|
|
@@ -140,3 +223,4 @@ if (-not $result.Ok) {
|
|
|
140
223
|
}
|
|
141
224
|
|
|
142
225
|
exit 0
|
|
226
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.ts"
|
|
4
|
+
- "**/*.cs"
|
|
5
|
+
description: Architecture boundary enforcement rules for the No-COM architecture.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Architecture Boundaries
|
|
9
|
+
|
|
10
|
+
Architecture boundary enforcement is a uniform gate across all tiers (T1–T4). Violations block PRs.
|
|
11
|
+
|
|
12
|
+
## Enforcement Tools
|
|
13
|
+
|
|
14
|
+
- **TypeScript:** `dependency-cruiser`. Configuration file pattern: `.dependency-cruiser.cjs`.
|
|
15
|
+
- **.NET (when the backend exists):** `NetArchTest.Rules`. Test project naming pattern: `*.ArchitectureTests`.
|
|
16
|
+
|
|
17
|
+
## No-COM Architecture Rules (enforceable assertions)
|
|
18
|
+
|
|
19
|
+
Production code in this repository must satisfy each of the following assertions. Each assertion is enforced by `dependency-cruiser` (TypeScript) or `NetArchTest.Rules` (.NET) where applicable; legacy import utilities, when added, must satisfy the same assertions.
|
|
20
|
+
|
|
21
|
+
1. New runtime code must not reference VSTO APIs (`Microsoft.Office.Tools.*`).
|
|
22
|
+
2. New runtime code must not reference Outlook desktop automation APIs (`Microsoft.Office.Interop.Outlook`).
|
|
23
|
+
3. New runtime code must not expose COM-visible interfaces (`[ComVisible(true)]` attribute is banned in production code).
|
|
24
|
+
4. New runtime code must not use Ribbon extensibility callbacks tied to the desktop object model.
|
|
25
|
+
5. New runtime code must not depend on local Outlook event streams.
|
|
26
|
+
6. New runtime code must not depend on Outlook user-defined fields as the primary state store.
|
|
27
|
+
7. Mailbox data must be accessed only through Office.js or Microsoft Graph.
|
|
28
|
+
8. Business behavior must be implemented in the backend or in host-neutral domain or application modules.
|
|
29
|
+
9. Client UI must be implemented as web UI.
|
|
30
|
+
10. Legacy integration, when required, must be limited to offline data import from files or exported data.
|
|
31
|
+
|
|
32
|
+
## Layer Boundary Assertions (TypeScript)
|
|
33
|
+
|
|
34
|
+
- `src/taskpane/` and `src/commands/` must not import from backend internals.
|
|
35
|
+
- Domain modules must not import from Office.js, Microsoft Graph SDK, or any infrastructure adapter.
|
|
36
|
+
- Adapters may import from domain; domain must not import from adapters.
|
|
37
|
+
|
|
38
|
+
## Layer Boundary Assertions (.NET, applies once the backend exists)
|
|
39
|
+
|
|
40
|
+
- `TaskMaster.Domain` must have zero references to Outlook PIA, VSTO, or Office.js types.
|
|
41
|
+
- `TaskMaster.Application` may depend on `TaskMaster.Domain` only.
|
|
42
|
+
- Adapter projects may depend on `TaskMaster.Domain` and `TaskMaster.Application`; domain may not depend on adapters.
|
|
43
|
+
|
|
44
|
+
## Enforcement Outcome
|
|
45
|
+
|
|
46
|
+
Violations of any rule above are PR-blocking findings. CI runs the architecture-boundary stage on every PR; a non-zero violation count fails the stage and prevents merge.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Benchmark Baseline Provenance
|
|
2
|
+
|
|
3
|
+
This rule governs performance baselines used by benchmark regression gates. It exists because a baseline captured on a developer workstation was compared against a `windows-latest` runner, producing deterministic latency regressions that the benchmark gate could not survive (issue #26, PR #30).
|
|
4
|
+
|
|
5
|
+
## Runner-Environment Parity (Required)
|
|
6
|
+
|
|
7
|
+
Performance baselines must be captured in the same runner environment class against which they are compared. A baseline captured on a developer workstation must not be committed for comparison against a CI runner.
|
|
8
|
+
|
|
9
|
+
## Prohibited: Unknown Processor
|
|
10
|
+
|
|
11
|
+
A baseline whose `HostEnvironmentInfo.ProcessorName` is the literal string `"Unknown processor"` is rejected. This value indicates the baseline was captured in an environment where the processor could not be identified (typically a virtualized or developer workstation), which violates runner-environment parity.
|
|
12
|
+
|
|
13
|
+
- Tooling MUST reject any baseline JSON where `HostEnvironmentInfo.ProcessorName == "Unknown processor"`.
|
|
14
|
+
- The rejection is a Blocking finding; the baseline must be recaptured on the target runner class.
|
|
15
|
+
|
|
16
|
+
## Required: Sibling Provenance File
|
|
17
|
+
|
|
18
|
+
Every committed baseline file MUST have a sibling `baseline.provenance.json` in the same directory. The provenance file records, at minimum:
|
|
19
|
+
|
|
20
|
+
- `runner_class` — the runner environment class that produced the baseline (for example `windows-latest`).
|
|
21
|
+
- `host_signature` — a stable signature of the host (for example a hashed or labeled description of the CPU/core configuration).
|
|
22
|
+
- `workflow_run_url` — the URL of the workflow run that produced the baseline.
|
|
23
|
+
|
|
24
|
+
- Tooling MUST reject a baseline that has no sibling `baseline.provenance.json`.
|
|
25
|
+
- The rejection is a Blocking finding; the baseline must be recaptured with provenance recorded.
|
|
26
|
+
|
|
27
|
+
## Enforcement
|
|
28
|
+
|
|
29
|
+
- The validator `scripts/benchmarks/Test-BaselineProvenance.ps1` enforces both rejection conditions above and accepts a runner-captured baseline whose `ProcessorName` is a real processor and whose sibling `baseline.provenance.json` is present.
|
|
30
|
+
- The feature-review policy rule `modified-workflow-needs-green-run` (see `.claude/skills/feature-review-workflow/SKILL.md`) provides a second line of defense: a diff under `scripts/benchmarks/**` is Blocking unless a green workflow run against the branch head is present in remediation inputs.
|
|
31
|
+
|
|
32
|
+
## Scope
|
|
33
|
+
|
|
34
|
+
- This rule applies to any baseline consumed by a benchmark regression gate.
|
|
35
|
+
- It does not change which checks are required by branch protection; it constrains the provenance of the data those checks consume.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# CI Workflow Authoring
|
|
2
|
+
|
|
3
|
+
This rule governs GitHub Actions workflow steps that run PowerShell (`pwsh`). It exists because a `pwsh` step that intentionally invoked a failing nested command left `$LASTEXITCODE == 1` after its verification logic had already succeeded, leaking a failure to GitHub Actions even though the step's intent was satisfied (issue #26, PR #30).
|
|
4
|
+
|
|
5
|
+
## Deliberately-Failing Nested Command Pattern (Required)
|
|
6
|
+
|
|
7
|
+
A workflow step whose `run:` block intentionally invokes a command expected to fail — for example a negative-path self-validation that asserts a gate catches a synthetic regression — MUST not allow the residual non-zero exit code to propagate to GitHub Actions.
|
|
8
|
+
|
|
9
|
+
For any such step, the `run:` block MUST do one of the following:
|
|
10
|
+
|
|
11
|
+
1. Reset the exit code explicitly after the expected failure:
|
|
12
|
+
```powershell
|
|
13
|
+
& ./some-tool --expect-failure
|
|
14
|
+
$LASTEXITCODE = 0
|
|
15
|
+
```
|
|
16
|
+
2. Or terminate the success path with an explicit zero exit:
|
|
17
|
+
```powershell
|
|
18
|
+
if ($verificationSucceeded) { exit 0 } else { exit 1 }
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
A `pwsh` step terminates with the exit code of the last external command unless the script explicitly resets it or calls `exit`. Negative-path verification steps therefore require an explicit reset or explicit `exit 0`.
|
|
22
|
+
|
|
23
|
+
## Rationale
|
|
24
|
+
|
|
25
|
+
- GitHub Actions interprets a step's process exit code as the step result. A leaked `$LASTEXITCODE` from an intentionally-failing nested command causes a passing verification to report failure.
|
|
26
|
+
- No local toolchain stage executes a workflow's `run:` block, so this defect is invisible to local feature-review. This textual rule is the artifact local review cites when reading workflow YAML.
|
|
27
|
+
|
|
28
|
+
## Enforcement
|
|
29
|
+
|
|
30
|
+
- Local feature-review cites this rule when reviewing diffs that add or modify `pwsh` steps with deliberately-failing nested commands.
|
|
31
|
+
- The feature-review policy rule `modified-workflow-needs-green-run` (see `.claude/skills/feature-review-workflow/SKILL.md`) requires a green workflow run against the branch head before a workflow change can merge, which exercises the exit-code path on the runner.
|
|
32
|
+
|
|
33
|
+
## Scope
|
|
34
|
+
|
|
35
|
+
- This rule applies to any workflow step whose `run:` block uses `shell: pwsh` (or the repo default `pwsh`) and intentionally invokes a failing nested command.
|
|
36
|
+
- It does not change required-check configuration or branch protection.
|
|
@@ -2,43 +2,83 @@
|
|
|
2
2
|
paths:
|
|
3
3
|
- "**/*.cs"
|
|
4
4
|
- "**/*.csproj"
|
|
5
|
-
description: C#-specific toolchain and coding standards.
|
|
5
|
+
description: C#-specific toolchain and coding standards (No-COM, xUnit).
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# C# Code Standards
|
|
9
9
|
|
|
10
|
-
This rule file summarizes the C#-specific policies for this repository.
|
|
10
|
+
This rule file summarizes the C#-specific policies for this repository. It targets the No-COM .NET foundation: xUnit, NSubstitute, FluentAssertions, `dotnet build`, the analyzer stack, `TimeProvider`, and uniform coverage thresholds.
|
|
11
11
|
|
|
12
12
|
## Toolchain
|
|
13
13
|
|
|
14
|
-
1. **Formatting — CSharpier**: All C# source files must be formatted with CSharpier. Do not use `dotnet format`. Command: `dotnet tool
|
|
15
|
-
2. **Linting — .NET Analyzers**: C# code must pass Roslyn/.NET analyzer diagnostics.
|
|
16
|
-
3. **Type Checking — Nullable Analysis**:
|
|
17
|
-
4. **Testing —
|
|
14
|
+
1. **Formatting — CSharpier**: All C# source files must be formatted with CSharpier. Do not use `dotnet format`. Command: `dotnet tool restore` followed by `dotnet csharpier check .` (or `dotnet csharpier .` to auto-format).
|
|
15
|
+
2. **Linting — .NET Analyzers**: C# code must pass Roslyn/.NET analyzer diagnostics. Analyzer enforcement is centralized in `Directory.Build.props` (`AnalysisLevel=latest-all`, `AnalysisMode=All`, `TreatWarningsAsErrors=true`). Command: `dotnet build` runs analyzers as part of the build.
|
|
16
|
+
3. **Type Checking — Nullable Analysis**: Nullable reference types are enabled solution-wide via `Directory.Build.props` (`Nullable=enable`, `TreatWarningsAsErrors=true`). Command: `dotnet build` enforces nullable warnings as errors.
|
|
17
|
+
4. **Testing — xUnit + NSubstitute + FluentAssertions**: Run tests with: `dotnet test --collect:"XPlat Code Coverage"`.
|
|
18
18
|
|
|
19
|
-
Run the toolchain in order: format → lint → type-check → test. Restart from step 1 if any step fails or changes files.
|
|
19
|
+
Run the toolchain in order: format → lint → type-check → architecture → test. Restart from step 1 if any step fails or changes files.
|
|
20
20
|
|
|
21
21
|
## Coding Standards
|
|
22
22
|
|
|
23
|
-
- **Naming**: `PascalCase` for types and public members. `camelCase` for locals and private fields/parameters.
|
|
23
|
+
- **Naming**: `PascalCase` for types and public members. `camelCase` for locals and private fields/parameters. Private fields use `_camelCase`. Interfaces use the `I` prefix. Async methods carry the `Async` suffix.
|
|
24
24
|
- **Null safety**: Keep nullable reference types enabled. Model optional values with nullable annotations and guard clauses.
|
|
25
25
|
- **Composition over inheritance**: Keep classes cohesive and scoped to one responsibility. Favor composition unless polymorphism is a clear requirement.
|
|
26
26
|
- **Async/await**: Use `async`/`await` for I/O-bound operations. Prefer `using`/`await using` for disposable resources.
|
|
27
27
|
- **Exceptions**: Fail fast with explicit exceptions. Avoid broad `catch (Exception)` unless at a defined boundary with added context.
|
|
28
28
|
- **Public surface**: Keep public API surface intentional and minimal. Prefer `internal` for non-public APIs.
|
|
29
29
|
- **XML docs**: Public APIs should include XML documentation comments when behavior or contract is non-obvious.
|
|
30
|
+
- **File-scoped namespaces**: Required (`csharp_style_namespace_declarations = file_scoped:error` in `.editorconfig`).
|
|
30
31
|
|
|
31
32
|
## Testing Standards
|
|
32
33
|
|
|
33
|
-
- Use **
|
|
34
|
-
- Use
|
|
35
|
-
-
|
|
36
|
-
- Use
|
|
34
|
+
- Use **xUnit** as the test framework with `[Fact]` and `[Theory]` attributes.
|
|
35
|
+
- Use **`[Theory]` + `[InlineData]`** for parameterized tests.
|
|
36
|
+
- Use **`IClassFixture<T>`** to share expensive setup across tests within a class.
|
|
37
|
+
- Use **NSubstitute** for test doubles. Example: `var sut = Substitute.For<IService>(); sut.Get().Returns(value);`.
|
|
38
|
+
- Prefer **FluentAssertions** for assertions; use xUnit `Assert` only when FluentAssertions is not practical.
|
|
37
39
|
- Follow Arrange–Act–Assert structure.
|
|
38
40
|
- No external dependencies in unit tests.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
|
|
42
|
+
### Coverage
|
|
43
|
+
|
|
44
|
+
- Line coverage line >= 85% and branch coverage branch >= 75% uniform across all tiers (T1–T4). No tier-specific lower floor is used.
|
|
45
|
+
- Mutation score mutation >= 75% on T1 modules (via Stryker.NET).
|
|
41
46
|
- Coverage regression on changed lines is a blocking finding.
|
|
47
|
+
- Interface-only files with no executable behavior — files consisting solely of `interface` declarations or abstract contracts — may be omitted from coverage measurement. Such files legitimately report 0% executable coverage. This is a clarification only; it does not lower any coverage threshold.
|
|
48
|
+
|
|
49
|
+
### Property-Based and Mutation Testing
|
|
50
|
+
|
|
51
|
+
- **CsCheck**: at least one property-based test per pure function on T1 and T2 modules.
|
|
52
|
+
- **Stryker.NET**: mutation testing required on T1 modules with a mutation score mutation >= 75%. Runs in pre-merge or nightly pipelines.
|
|
53
|
+
|
|
54
|
+
### Golden Tests
|
|
55
|
+
|
|
56
|
+
- **Verify.Xunit**: required for T1 classifier-output modules, tested against a versioned corpus.
|
|
57
|
+
|
|
58
|
+
## Analyzer Stack
|
|
59
|
+
|
|
60
|
+
All projects reference the following analyzer packages via `<PackageReference>` with `PrivateAssets="all"` (versions pinned centrally in `Directory.Packages.props`):
|
|
61
|
+
|
|
62
|
+
- `Meziantou.Analyzer` — `PrivateAssets="all"`
|
|
63
|
+
- `SonarAnalyzer.CSharp` — `PrivateAssets="all"`
|
|
64
|
+
- `Roslynator.Analyzers` — `PrivateAssets="all"`
|
|
65
|
+
- `AsyncFixer` — `PrivateAssets="all"`
|
|
66
|
+
- `SecurityCodeScan.VS2019` — `PrivateAssets="all"`
|
|
67
|
+
- `Microsoft.CodeAnalysis.BannedApiAnalyzers` — `PrivateAssets="all"`
|
|
68
|
+
|
|
69
|
+
The shared `<ItemGroup>` lives in `Directory.Build.props` so the stack applies to every project automatically.
|
|
70
|
+
|
|
71
|
+
## Banned APIs
|
|
72
|
+
|
|
73
|
+
The following APIs are banned outside an explicit allowlist; enforcement is via `Microsoft.CodeAnalysis.BannedApiAnalyzers` against `BannedSymbols.txt` (at solution root, wired through `Directory.Build.props` as an `<AdditionalFiles>` entry):
|
|
74
|
+
|
|
75
|
+
- `DateTime.Now` (use `TimeProvider.GetLocalNow()` on an injected `TimeProvider`).
|
|
76
|
+
- `DateTime.UtcNow` (use `TimeProvider.GetUtcNow()` on an injected `TimeProvider`).
|
|
77
|
+
- `Random.Shared` (inject a seeded `Random` or use a deterministic seam).
|
|
78
|
+
- `Thread.Sleep` (banned; use cooperative awaits and fake-time advancement).
|
|
79
|
+
- `Task.Delay` (banned in production paths; tests must use `FakeTimeProvider`).
|
|
80
|
+
|
|
81
|
+
Tests inject `TimeProvider` via `Microsoft.Extensions.TimeProvider.Testing`'s `FakeTimeProvider` rather than calling `DateTime.UtcNow` or `Task.Delay` directly.
|
|
42
82
|
|
|
43
83
|
## Deterministic Test Rules
|
|
44
84
|
|
|
@@ -48,9 +88,15 @@ Unit tests must not depend on network, mutable machine PATH or profile state, im
|
|
|
48
88
|
|
|
49
89
|
Introduce the smallest seam that enables reliable unit testing. Apply in this order of preference:
|
|
50
90
|
|
|
51
|
-
1. **Interface seam (preferred)** — extract boundary calls into narrow purpose-specific interfaces (for example, `IProcessRunner`, `IFileSystem
|
|
91
|
+
1. **Interface seam (preferred)** — extract boundary calls into narrow purpose-specific interfaces (for example, `IProcessRunner`, `IFileSystem`). Keep interfaces minimal.
|
|
52
92
|
2. **Injectable delegate seam** — use a narrow `Func<>`/`Action<>` delegate for a single call path when a full interface is excessive. Default behavior must remain safe and deterministic.
|
|
53
|
-
3. **Adapter seam for static or third-party APIs** — wrap the static or third-party call behind a small adapter so tests can
|
|
93
|
+
3. **Adapter seam for static or third-party APIs** — wrap the static or third-party call behind a small adapter so tests can substitute the adapter with NSubstitute.
|
|
94
|
+
|
|
95
|
+
### Clock Seam
|
|
96
|
+
|
|
97
|
+
- **`TimeProvider` is preferred** for new code (since .NET 8). Inject `TimeProvider` and use `GetUtcNow()` / `GetLocalNow()` / `CreateTimer()`.
|
|
98
|
+
- Test code injects `FakeTimeProvider` from **`Microsoft.Extensions.TimeProvider.Testing`** to advance simulated time deterministically.
|
|
99
|
+
- `IClock` legacy: acceptable only in legacy or pre-.NET 8 contexts that have not yet been migrated. New code must use `TimeProvider`.
|
|
54
100
|
|
|
55
101
|
## Prohibited Behaviors
|
|
56
102
|
|
|
@@ -24,16 +24,25 @@ Apply these priorities in order when designing or changing code:
|
|
|
24
24
|
- Keep methods small and focused. Avoid god objects.
|
|
25
25
|
- Use interfaces/abstract types/protocols when multiple implementations are likely.
|
|
26
26
|
|
|
27
|
+
## Module Rigor Tiers
|
|
28
|
+
|
|
29
|
+
Module rigor tiers (T1–T4) and the uniform-versus-tier-dependent gate matrix are defined in `.claude/rules/quality-tiers.md`. Every project must be classified in `quality-tiers.yml` at repo root.
|
|
30
|
+
|
|
27
31
|
## Mandatory Toolchain Loop
|
|
28
32
|
|
|
29
|
-
Run the full toolchain in this exact order and repeat until all
|
|
33
|
+
Run the full seven-stage toolchain in this exact order and repeat until all stages pass in a single pass:
|
|
30
34
|
|
|
31
35
|
1. **Formatting** (e.g., Black, Prettier, CSharpier, Invoke-Formatter)
|
|
32
36
|
2. **Linting** (e.g., Ruff, ESLint, PSScriptAnalyzer, .NET analyzers)
|
|
33
37
|
3. **Type checking** (e.g., Pyright, TSC, nullable analysis; skip for PowerShell)
|
|
34
|
-
4. **
|
|
38
|
+
4. **Architecture-boundary tests** (e.g., dependency-cruiser, NetArchTest.Rules)
|
|
39
|
+
5. **Unit tests** (e.g., Pytest, Vitest, MSTest, Pester) including property-based tests where applicable per `quality-tiers.md`
|
|
40
|
+
6. **Contract / schema compatibility checks** (e.g., oasdiff, schema-snapshot diff)
|
|
41
|
+
7. **Integration tests**
|
|
42
|
+
|
|
43
|
+
**Restart from step 1** if any stage fails or auto-fixes any files. Do not stop the loop until all seven stages complete without errors in a single pass.
|
|
35
44
|
|
|
36
|
-
|
|
45
|
+
Mutation testing and golden tests run in pre-merge or nightly pipelines, not the per-commit loop.
|
|
37
46
|
|
|
38
47
|
## File Size Limit
|
|
39
48
|
|
|
@@ -20,11 +20,30 @@ Every unit test must satisfy all five of these properties:
|
|
|
20
20
|
|
|
21
21
|
## Coverage Requirements
|
|
22
22
|
|
|
23
|
-
- **
|
|
24
|
-
- **
|
|
23
|
+
- **Line coverage must remain >= 85% across all tiers (T1–T4).**
|
|
24
|
+
- **Branch coverage must remain >= 75% across all tiers (T1–T4).**
|
|
25
25
|
- Code changes or refactors must not reduce coverage for the lines that were changed.
|
|
26
|
+
- Tier-specific lower coverage thresholds are not used in this repository. See `.claude/rules/quality-tiers.md` for the full tier system.
|
|
26
27
|
- Coverage is a supporting metric, not the sole quality gate. Untested critical behavior is not acceptable even if the overall percentage looks good.
|
|
27
28
|
- Configure coverage tooling to exclude test files (e.g., `tests/`) so metrics reflect application code, not tests.
|
|
29
|
+
- Type-only / interface-only modules with no executable behavior may be omitted from coverage measurement. Examples: Python `Protocol`-only modules consumed only under `TYPE_CHECKING`, TypeScript interface/type-only files, and C# interface-only files. Such modules legitimately report 0% executable coverage and may be excluded from measurement. This is a clarification only; it does not lower any coverage threshold.
|
|
30
|
+
|
|
31
|
+
## Coverage Exclusion Policy
|
|
32
|
+
|
|
33
|
+
No production file may be excluded from coverage measurement. Every production source file is in the denominator of the coverage metric, regardless of whether its lines are reachable in the test environment.
|
|
34
|
+
|
|
35
|
+
The correct response to a file that contains untestable lines is to refactor it — extract all logic into host-neutral, testable modules and leave only the thinnest possible wiring in the host-bound entry point. The entry point's uncovered lines then represent a real and visible cost in the coverage metric, which creates ongoing pressure to keep those files minimal.
|
|
36
|
+
|
|
37
|
+
**Permitted `exclude` entries** (non-production paths only):
|
|
38
|
+
- Build output directories: `dist/**`, `lib/**`, `lib-amd/**`.
|
|
39
|
+
- Test files and test infrastructure: `**/*.test.ts`, `tests/**`, `src/test-support/**`.
|
|
40
|
+
- Config files that are not production code: `vitest.config.ts`, `eslint.config.mjs`, `.dependency-cruiser.cjs`, `webpack.config.js`.
|
|
41
|
+
- `node_modules/**`.
|
|
42
|
+
|
|
43
|
+
**Prohibited `exclude` entries:**
|
|
44
|
+
- Any path under `src/` that contains production runtime code, regardless of whether it is auto-generated, host-bound, or difficult to test.
|
|
45
|
+
|
|
46
|
+
**Enforcement:** Feature-review agents must treat any `exclude` entry that matches a production source path as a **Blocking** finding.
|
|
28
47
|
|
|
29
48
|
## Scenario Completeness
|
|
30
49
|
|
|
@@ -54,7 +73,33 @@ Assertions must produce clear, actionable failure messages.
|
|
|
54
73
|
- **Creation and use of temporary files in tests is strictly prohibited.**
|
|
55
74
|
- Tests must not rely on mutable global state or external configuration that can change between runs.
|
|
56
75
|
|
|
76
|
+
## Test File Location
|
|
77
|
+
|
|
78
|
+
Test files must live in a `tests/` directory tree that mirrors the production source structure. The test for `src/foo/bar.ts` belongs at `tests/foo/bar.test.ts`; the test for `scripts/powershell/Foo.ps1` belongs at `tests/scripts/powershell/Foo.Tests.ps1`. Language-specific rules may add further naming conventions (framework suffix, file extension) on top of this universal layout requirement.
|
|
79
|
+
|
|
80
|
+
Colocation — placing test files alongside production source files in `src/` or equivalent — is not permitted. An agent that creates or moves a test file into the production source tree has violated this rule.
|
|
81
|
+
|
|
57
82
|
## Documentation
|
|
58
83
|
|
|
59
84
|
- Each test must clearly communicate its purpose via a descriptive name and/or a short docstring or comment summarizing the scenario and expected outcome.
|
|
60
85
|
- Group related tests logically within the same file or test class.
|
|
86
|
+
|
|
87
|
+
## Test Categories
|
|
88
|
+
|
|
89
|
+
The following test categories apply across the repository, with tier-dependent obligations per `.claude/rules/quality-tiers.md`:
|
|
90
|
+
|
|
91
|
+
- **Unit tests** — required for all tiers (T1–T4). Cover single units of behavior in isolation.
|
|
92
|
+
- **Property-based tests** — required for T1 and T2 modules: at least one property test per pure function. Use `fast-check` (TypeScript) or `hypothesis` (Python) where applicable.
|
|
93
|
+
- **Golden / snapshot tests** — required only for T1 classifier-output modules, tested against a versioned corpus. Snapshot tests are otherwise discouraged unless stable and intentional.
|
|
94
|
+
- **Contract / schema tests** — required at every host-service boundary (e.g., Office.js, Microsoft Graph, internal API contracts).
|
|
95
|
+
- **Mutation tests** — required for T1 modules: mutation score >= 75%. Run in pre-merge or nightly pipelines.
|
|
96
|
+
- **Integration tests** — required where adapters interact with external systems; scoped per tier in the gate matrix.
|
|
97
|
+
|
|
98
|
+
## Determinism Infrastructure
|
|
99
|
+
|
|
100
|
+
All test code must be deterministic. The following infrastructure requirements apply uniformly:
|
|
101
|
+
|
|
102
|
+
- **Controllable clock** — use a `Clock` interface (TypeScript) or `TimeProvider` (.NET) injected into code under test. Do not read wall-clock time directly in production code under test.
|
|
103
|
+
- **Seeded RNG** — randomness must be supplied via a seedable interface; on test failure the seed must be printed so the failure is reproducible.
|
|
104
|
+
- **Banned APIs in test code** — `setTimeout`, `Thread.Sleep`, `Task.Delay`, real wall-clock waits, and `Date.now()` outside the clock interface are prohibited in tests.
|
|
105
|
+
- **Virtual scheduler / fake timers / `FakeTimeProvider`** — async tests must use the framework's fake-timer facility (`vi.useFakeTimers()` for Vitest, `FakeTimeProvider` for .NET) to advance simulated time deterministically.
|