@danmoisan/drm-copilot-mcp 1.1.18 → 1.1.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.
- package/out/mcp-server.js +989 -662
- package/package.json +3 -2
- package/resources/claude-customizations/.claude/agents/atomic-executor.md +11 -0
- package/resources/claude-customizations/.claude/agents/atomic-planner.md +10 -0
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +12 -4
- package/resources/claude-customizations/.claude/agents/epic-planner.md +5 -4
- package/resources/claude-customizations/.claude/agents/feature-review.md +6 -4
- package/resources/claude-customizations/.claude/agents/orchestrator.md +5 -4
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +31 -13
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +9 -6
- package/resources/claude-customizations/.claude/agents/pr-author.md +15 -2
- package/resources/claude-customizations/.claude/agents/prd-feature.md +1 -1
- package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +111 -46
- package/resources/claude-customizations/.claude/hooks/enforce-completion-helpers.ps1 +105 -0
- package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +8 -1
- package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +116 -59
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +55 -23
- package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +74 -75
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +127 -35
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +61 -57
- package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +129 -39
- package/resources/claude-customizations/.claude/hooks/enforce-mermaid-validation.ps1 +7 -1
- package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +9 -3
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +39 -51
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +17 -12
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +9 -3
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +65 -50
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +49 -42
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +54 -72
- package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +8 -2
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +279 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +129 -75
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.artifact-root.ps1 +233 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +224 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +69 -24
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +29 -18
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +10 -4
- package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +14 -5
- package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +8 -2
- package/resources/claude-customizations/.claude/hooks/feature-folder-resolution.ps1 +373 -0
- package/resources/claude-customizations/.claude/hooks/feature-review-coverage-thresholds.ps1 +85 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-heredoc.ps1 +119 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-invocation-operands.ps1 +198 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +254 -255
- package/resources/claude-customizations/.claude/hooks/hook-command-payload-powershell.ps1 +201 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-payload.ps1 +484 -0
- package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +27 -110
- package/resources/claude-customizations/.claude/hooks/hook-dependency-guard.ps1 +114 -0
- package/resources/claude-customizations/.claude/hooks/hook-docs-only-diff.ps1 +493 -0
- package/resources/claude-customizations/.claude/hooks/subagentstop-envelope.ps1 +192 -0
- package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +9 -3
- package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +40 -42
- package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +66 -38
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-metrics.ps1 +180 -0
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +195 -0
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +141 -395
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-resolution.ps1 +311 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-routing.ps1 +170 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +115 -190
- package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +64 -32
- package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +39 -44
- package/resources/claude-customizations/.claude/hooks/validate-prd-feature-output.ps1 +43 -14
- package/resources/claude-customizations/.claude/hooks/validate-required-artifact-output.ps1 +66 -36
- package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +31 -34
- package/resources/claude-customizations/.claude/lib/bash/mutate-parallel-run.sh +191 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-cohorts.sh +13 -3
- package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-cli.sh +152 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-ops.sh +193 -0
- package/resources/claude-customizations/.claude/lib/bash/parallel-mutation.sh +322 -0
- package/resources/claude-customizations/.claude/lib/bash/remove-parallel-item.sh +146 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +23 -33
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +86 -6
- package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +91 -19
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.psm1 +346 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +24 -3
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +79 -2
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +3 -3
- package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +5 -5
- package/resources/claude-customizations/.claude/rules/csharp.md +2 -2
- package/resources/claude-customizations/.claude/rules/general-code-change.md +1 -1
- package/resources/claude-customizations/.claude/rules/general-unit-test.md +3 -2
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +37 -4
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +50 -5
- package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +47 -8
- package/resources/claude-customizations/.claude/rules/quality-tiers.md +11 -7
- package/resources/claude-customizations/.claude/rules/shell.md +10 -8
- package/resources/claude-customizations/.claude/rules/typescript.md +1 -1
- package/resources/claude-customizations/.claude/settings.json +3 -4
- package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +5 -1
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh +1 -1
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +2 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +2 -0
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +33 -6
- package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -3
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +14 -6
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +52 -14
- package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +22 -9
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +79 -16
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +63 -7
- package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +35 -12
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +3 -1
- package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +2 -2
- package/resources/claude-customizations/pack-manifests/core.json +23 -0
- package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +5 -5
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +11 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +4 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +10 -0
- package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +8 -0
- package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +17 -1
- package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +2 -1
- package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +13 -5
- package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +3 -2
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +15 -6
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +11 -7
- package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +11 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +10 -0
- package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +2 -1
- package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +6 -6
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +2 -2
- package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +318 -273
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +97 -44
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-helpers.ps1 +105 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +37 -5
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +6 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +54 -41
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +35 -14
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +24 -11
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +13 -4
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/feature-folder-resolution.ps1 +373 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/feature-review-coverage-thresholds.ps1 +85 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-heredoc.ps1 +119 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation-operands.ps1 +198 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +254 -255
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload-powershell.ps1 +201 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload.ps1 +484 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +27 -110
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-dependency-guard.ps1 +114 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-docs-only-diff.ps1 +493 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +8 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +7 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +24 -8
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +9 -0
- package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +2 -2
- package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +4 -4
- package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +2 -2
- package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +1 -1
- package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +4 -0
- package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
- package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
- package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
- package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +25 -28
- package/resources/powershell/PoshQC/PoshQC.psm1 +1 -0
- package/resources/powershell/PoshQC/README.md +8 -2
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +2 -319
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
PowerShell port of the epic wave-barrier Layer 2 ordering check (issue #840).
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Ports validate_wave_barrier_ordering (scripts/dev_tools/_epic_orchestrator_state_wave_barrier.py)
|
|
7
|
+
as reached through validate_epic_orchestrator_state_text: feature extraction, the union
|
|
8
|
+
index with lifecycle-prefix normalization, the start guard, and the status and timing
|
|
9
|
+
checks with status-before-timing precedence. For in-parity inputs the violation strings
|
|
10
|
+
are byte-identical to the Python authority.
|
|
11
|
+
|
|
12
|
+
The checkpoint is parsed with System.Text.Json (JsonDocument), not ConvertFrom-Json:
|
|
13
|
+
ConvertFrom-Json coerces ISO-8601 strings to DateTime and reads property names
|
|
14
|
+
case-insensitively, and either behavior would break parity. Properties are read through
|
|
15
|
+
an ordinal, last-definition-wins enumeration, which matches Python json semantics for a
|
|
16
|
+
duplicate key.
|
|
17
|
+
|
|
18
|
+
An input this port does not evaluate fails closed with an error whose message starts
|
|
19
|
+
with EPIC_WAVE_BARRIER_UNEVALUABLE:. The declared divergence classes are listed in
|
|
20
|
+
tests/scripts/claude-lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.Parity.Tests.ps1.
|
|
21
|
+
|
|
22
|
+
.NOTES
|
|
23
|
+
PowerShell 7+. Reads no file and starts no process: the caller supplies checkpoint text
|
|
24
|
+
it has already read. Mirrored byte-identically into the bundled extension payload.
|
|
25
|
+
CONVENTION: this module fails fast at module scope and imports its siblings with -ErrorAction Stop.
|
|
26
|
+
#>
|
|
27
|
+
|
|
28
|
+
Set-StrictMode -Version Latest
|
|
29
|
+
$ErrorActionPreference = 'Stop'
|
|
30
|
+
|
|
31
|
+
# Lifecycle prefixes a folder hint may carry, tested in this order; the first match is stripped.
|
|
32
|
+
$script:LifecyclePrefixes = @('docs/features/active/', 'docs/features/completed/', 'active/', 'completed/')
|
|
33
|
+
|
|
34
|
+
# The merge statuses that durably confirm a dependency edge, and the literal not-started status.
|
|
35
|
+
$script:MergedStatuses = @('merged', 'worktree_removed')
|
|
36
|
+
$script:NotStartedStatus = 'not_started'
|
|
37
|
+
|
|
38
|
+
# A JSON number token that Python reads as an int; any other number token is not compared.
|
|
39
|
+
$script:IntegerTokenPattern = '^-?(0|[1-9][0-9]*)$'
|
|
40
|
+
|
|
41
|
+
# The fail-closed token and the two violation formats of the Python authority.
|
|
42
|
+
$script:UnevaluableToken = 'EPIC_WAVE_BARRIER_UNEVALUABLE:'
|
|
43
|
+
$script:StatusViolationFormat = 'EPIC_WAVE_BARRIER_VIOLATION: {0} is treated as started while dependency {1} is not merged'
|
|
44
|
+
$script:TimingViolationFormat = 'EPIC_WAVE_BARRIER_VIOLATION: {0} worktree_created_at precedes dependency {1} merge_confirmed_at'
|
|
45
|
+
|
|
46
|
+
# Private: the last property of an object element whose name equals Name (ordinal), so a
|
|
47
|
+
# duplicate key resolves the way Python json does. Found is $false when there is none.
|
|
48
|
+
function Get-EpicWaveBarrierProperty {
|
|
49
|
+
[CmdletBinding()]
|
|
50
|
+
[OutputType([pscustomobject])]
|
|
51
|
+
param(
|
|
52
|
+
[Parameter(Mandatory = $true)] [System.Text.Json.JsonElement] $Element,
|
|
53
|
+
[Parameter(Mandatory = $true)] [string] $Name
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
$found = $false
|
|
57
|
+
$value = $null
|
|
58
|
+
foreach ($property in $Element.EnumerateObject()) {
|
|
59
|
+
if ([string]::Equals($property.Name, $Name, [System.StringComparison]::Ordinal)) {
|
|
60
|
+
$found = $true
|
|
61
|
+
$value = $property.Value
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return [pscustomobject]@{ Found = $found; Value = $value }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# Private: the string value of a named property, or $null when it is absent or not a string.
|
|
68
|
+
function Get-EpicWaveBarrierString {
|
|
69
|
+
[CmdletBinding()]
|
|
70
|
+
[OutputType([string])]
|
|
71
|
+
param(
|
|
72
|
+
[Parameter(Mandatory = $true)] [System.Text.Json.JsonElement] $Element,
|
|
73
|
+
[Parameter(Mandatory = $true)] [string] $Name
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
$property = Get-EpicWaveBarrierProperty -Element $Element -Name $Name
|
|
77
|
+
if ($property.Found -and $property.Value.ValueKind -eq [System.Text.Json.JsonValueKind]::String) {
|
|
78
|
+
return $property.Value.GetString()
|
|
79
|
+
}
|
|
80
|
+
return $null
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# Private: strip the first matching lifecycle prefix (ordinal), as _normalize_folder_hint does.
|
|
84
|
+
function ConvertTo-EpicWaveBarrierFolderHint {
|
|
85
|
+
[CmdletBinding()]
|
|
86
|
+
[OutputType([string])]
|
|
87
|
+
param(
|
|
88
|
+
[Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $Value
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
foreach ($prefix in $script:LifecyclePrefixes) {
|
|
92
|
+
if ($Value.StartsWith($prefix, [System.StringComparison]::Ordinal)) {
|
|
93
|
+
return $Value.Substring($prefix.Length)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return $Value
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# Private: the canonical decimal text of an integer token; BigInteger keeps values beyond
|
|
100
|
+
# 64 bits exact and renders -0 as 0, as Python str(int) does.
|
|
101
|
+
function ConvertTo-EpicWaveBarrierIntegerText {
|
|
102
|
+
[CmdletBinding()]
|
|
103
|
+
[OutputType([string])]
|
|
104
|
+
param(
|
|
105
|
+
[Parameter(Mandatory = $true)] [string] $RawToken
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
$invariant = [System.Globalization.CultureInfo]::InvariantCulture
|
|
109
|
+
return [System.Numerics.BigInteger]::Parse($RawToken, $invariant).ToString($invariant)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# Private: the union-index key of a JSON number. Python indexes an int by value; a
|
|
113
|
+
# non-integer token is outside parity, so it fails closed rather than being compared.
|
|
114
|
+
function Get-EpicWaveBarrierNumberKey {
|
|
115
|
+
[CmdletBinding()]
|
|
116
|
+
[OutputType([string])]
|
|
117
|
+
param(
|
|
118
|
+
[Parameter(Mandatory = $true)] [System.Text.Json.JsonElement] $Element,
|
|
119
|
+
[Parameter(Mandatory = $true)] [string] $Role,
|
|
120
|
+
[Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $Folder
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
$raw = $Element.GetRawText()
|
|
124
|
+
if ($raw -cnotmatch $script:IntegerTokenPattern) {
|
|
125
|
+
throw ("{0} feature '{1}' has a non-integer numeric {2} token '{3}', which this port does not compare" -f
|
|
126
|
+
$script:UnevaluableToken, $Folder, $Role, $raw)
|
|
127
|
+
}
|
|
128
|
+
return 'n:' + (ConvertTo-EpicWaveBarrierIntegerText -RawToken $raw)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
# Private: render a resolved dependency reference as Python str() renders the raw value.
|
|
132
|
+
# Only strings, booleans, and integer numbers resolve, so no other kind reaches this point.
|
|
133
|
+
function Format-EpicWaveBarrierReference {
|
|
134
|
+
[CmdletBinding()]
|
|
135
|
+
[OutputType([string])]
|
|
136
|
+
param(
|
|
137
|
+
[Parameter(Mandatory = $true)] [System.Text.Json.JsonElement] $Element
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
$kind = $Element.ValueKind
|
|
141
|
+
if ($kind -eq [System.Text.Json.JsonValueKind]::String) { return $Element.GetString() }
|
|
142
|
+
if ($kind -eq [System.Text.Json.JsonValueKind]::True) { return 'True' }
|
|
143
|
+
if ($kind -eq [System.Text.Json.JsonValueKind]::False) { return 'False' }
|
|
144
|
+
return (ConvertTo-EpicWaveBarrierIntegerText -RawToken $Element.GetRawText())
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Private: the start guard of feature_has_started. A string worktree_created_at (empty
|
|
148
|
+
# included) starts a feature; otherwise only the exact string not_started leaves it unstarted.
|
|
149
|
+
function Test-EpicWaveBarrierFeatureStarted {
|
|
150
|
+
[CmdletBinding()]
|
|
151
|
+
[OutputType([bool])]
|
|
152
|
+
param(
|
|
153
|
+
[Parameter(Mandatory = $true)] [System.Text.Json.JsonElement] $Feature
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
if ($null -ne (Get-EpicWaveBarrierString -Element $Feature -Name 'worktree_created_at')) { return $true }
|
|
157
|
+
$status = Get-EpicWaveBarrierProperty -Element $Feature -Name 'merge_status'
|
|
158
|
+
if (-not $status.Found) { return $true }
|
|
159
|
+
$isNotStarted = ($status.Value.ValueKind -eq [System.Text.Json.JsonValueKind]::String) -and
|
|
160
|
+
[string]::Equals($status.Value.GetString(), $script:NotStartedStatus, [System.StringComparison]::Ordinal)
|
|
161
|
+
return (-not $isNotStarted)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
# Private: whether a dependency's merge_status is a string equal (ordinal) to a merged status.
|
|
165
|
+
# An absent or non-string status arrives as $null, which the [string] binding turns into an
|
|
166
|
+
# empty string, and an empty string is not a merged status.
|
|
167
|
+
function Test-EpicWaveBarrierMerged {
|
|
168
|
+
[CmdletBinding()]
|
|
169
|
+
[OutputType([bool])]
|
|
170
|
+
param(
|
|
171
|
+
[Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $Status
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
foreach ($merged in $script:MergedStatuses) {
|
|
175
|
+
if ([string]::Equals($Status, $merged, [System.StringComparison]::Ordinal)) { return $true }
|
|
176
|
+
}
|
|
177
|
+
return $false
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
# Private: the union-index key of a feature's issue_num, or $null when it is absent, null,
|
|
181
|
+
# or a string (a string issue_num is never matched by a non-string reference).
|
|
182
|
+
function Get-EpicWaveBarrierIssueKey {
|
|
183
|
+
[CmdletBinding()]
|
|
184
|
+
[OutputType([string])]
|
|
185
|
+
param(
|
|
186
|
+
[Parameter(Mandatory = $true)] [System.Text.Json.JsonElement] $Feature,
|
|
187
|
+
[Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $Folder
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
$issue = Get-EpicWaveBarrierProperty -Element $Feature -Name 'issue_num'
|
|
191
|
+
if (-not $issue.Found) { return $null }
|
|
192
|
+
$kind = $issue.Value.ValueKind
|
|
193
|
+
# Routing table: an array or object cannot be hashed by the authority (fail closed);
|
|
194
|
+
# booleans key as 1 and 0 because Python treats True == 1; numbers key by value.
|
|
195
|
+
if ($kind -eq [System.Text.Json.JsonValueKind]::Array -or $kind -eq [System.Text.Json.JsonValueKind]::Object) {
|
|
196
|
+
throw ("{0} feature '{1}' has an issue_num that is an array or object, which the Python authority cannot index" -f
|
|
197
|
+
$script:UnevaluableToken, $Folder)
|
|
198
|
+
}
|
|
199
|
+
if ($kind -eq [System.Text.Json.JsonValueKind]::True) { return 'n:1' }
|
|
200
|
+
if ($kind -eq [System.Text.Json.JsonValueKind]::False) { return 'n:0' }
|
|
201
|
+
if ($kind -eq [System.Text.Json.JsonValueKind]::Number) {
|
|
202
|
+
return (Get-EpicWaveBarrierNumberKey -Element $issue.Value -Role 'issue_num' -Folder $Folder)
|
|
203
|
+
}
|
|
204
|
+
return $null
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
# Private: the object entries of a list-valued features property, in order; empty when the
|
|
208
|
+
# root is not an object or features is absent or not a list.
|
|
209
|
+
function Get-EpicWaveBarrierFeatureEntry {
|
|
210
|
+
[CmdletBinding()]
|
|
211
|
+
[OutputType([System.Text.Json.JsonElement[]], [object[]])]
|
|
212
|
+
param(
|
|
213
|
+
[Parameter(Mandatory = $true)] [System.Text.Json.JsonElement] $Root
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
$entries = [System.Collections.Generic.List[System.Text.Json.JsonElement]]::new()
|
|
217
|
+
if ($Root.ValueKind -ne [System.Text.Json.JsonValueKind]::Object) { return , $entries.ToArray() }
|
|
218
|
+
$features = Get-EpicWaveBarrierProperty -Element $Root -Name 'features'
|
|
219
|
+
if (-not $features.Found -or $features.Value.ValueKind -ne [System.Text.Json.JsonValueKind]::Array) {
|
|
220
|
+
return , $entries.ToArray()
|
|
221
|
+
}
|
|
222
|
+
# Keep only object-shaped entries, as _extract_features does.
|
|
223
|
+
foreach ($item in $features.Value.EnumerateArray()) {
|
|
224
|
+
if ($item.ValueKind -eq [System.Text.Json.JsonValueKind]::Object) { $entries.Add($item) }
|
|
225
|
+
}
|
|
226
|
+
return , $entries.ToArray()
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
# Private: resolve one depends_on element to a feature folder through the union index, or
|
|
230
|
+
# $null. A string consults the folder hints only; a boolean or number consults issue_num only.
|
|
231
|
+
function Resolve-EpicWaveBarrierReference {
|
|
232
|
+
[CmdletBinding()]
|
|
233
|
+
[OutputType([string])]
|
|
234
|
+
param(
|
|
235
|
+
[Parameter(Mandatory = $true)] [System.Text.Json.JsonElement] $Dependency,
|
|
236
|
+
[Parameter(Mandatory = $true)] [System.Collections.Generic.Dictionary[string, string]] $ByHint,
|
|
237
|
+
[Parameter(Mandatory = $true)] [System.Collections.Generic.Dictionary[string, string]] $ByIssue,
|
|
238
|
+
[Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $Folder
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
$kind = $Dependency.ValueKind
|
|
242
|
+
$key = $null
|
|
243
|
+
$map = $ByIssue
|
|
244
|
+
if ($kind -eq [System.Text.Json.JsonValueKind]::String) {
|
|
245
|
+
$key = ConvertTo-EpicWaveBarrierFolderHint -Value $Dependency.GetString()
|
|
246
|
+
$map = $ByHint
|
|
247
|
+
}
|
|
248
|
+
elseif ($kind -eq [System.Text.Json.JsonValueKind]::True) { $key = 'n:1' }
|
|
249
|
+
elseif ($kind -eq [System.Text.Json.JsonValueKind]::False) { $key = 'n:0' }
|
|
250
|
+
elseif ($kind -eq [System.Text.Json.JsonValueKind]::Number) {
|
|
251
|
+
$key = Get-EpicWaveBarrierNumberKey -Element $Dependency -Role 'depends_on reference' -Folder $Folder
|
|
252
|
+
}
|
|
253
|
+
if ($null -eq $key -or -not $map.ContainsKey($key)) { return $null }
|
|
254
|
+
return $map[$key]
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
# Private: the violation lines for the object entries, in feature and depends_on order.
|
|
258
|
+
function Get-EpicWaveBarrierViolation {
|
|
259
|
+
[CmdletBinding()]
|
|
260
|
+
[OutputType([string])]
|
|
261
|
+
param(
|
|
262
|
+
[Parameter(Mandatory = $true)] [AllowEmptyCollection()] [System.Text.Json.JsonElement[]] $Entry
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
$ordinal = [System.StringComparer]::Ordinal
|
|
266
|
+
$byFolder = [System.Collections.Generic.Dictionary[string, System.Text.Json.JsonElement]]::new($ordinal)
|
|
267
|
+
$byHint = [System.Collections.Generic.Dictionary[string, string]]::new($ordinal)
|
|
268
|
+
$byIssue = [System.Collections.Generic.Dictionary[string, string]]::new($ordinal)
|
|
269
|
+
|
|
270
|
+
# Build the lookups: every string folder by name, and every non-empty string folder by
|
|
271
|
+
# its normalized hint and its issue_num. A later duplicate replaces an earlier one.
|
|
272
|
+
foreach ($feature in $Entry) {
|
|
273
|
+
$folder = Get-EpicWaveBarrierString -Element $feature -Name 'feature_folder'
|
|
274
|
+
if ($null -eq $folder) { continue }
|
|
275
|
+
$byFolder[$folder] = $feature
|
|
276
|
+
if ($folder.Length -eq 0) { continue }
|
|
277
|
+
$byHint[(ConvertTo-EpicWaveBarrierFolderHint -Value $folder)] = $folder
|
|
278
|
+
$issueKey = Get-EpicWaveBarrierIssueKey -Feature $feature -Folder $folder
|
|
279
|
+
if ($null -ne $issueKey) { $byIssue[$issueKey] = $folder }
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
# Check every started feature with a list depends_on; each edge yields at most one line,
|
|
283
|
+
# and the status violation takes precedence over the timing violation.
|
|
284
|
+
foreach ($feature in $Entry) {
|
|
285
|
+
$folder = Get-EpicWaveBarrierString -Element $feature -Name 'feature_folder'
|
|
286
|
+
$dependsOn = Get-EpicWaveBarrierProperty -Element $feature -Name 'depends_on'
|
|
287
|
+
if ($null -eq $folder -or -not $dependsOn.Found -or $dependsOn.Value.ValueKind -ne [System.Text.Json.JsonValueKind]::Array) { continue }
|
|
288
|
+
if (-not (Test-EpicWaveBarrierFeatureStarted -Feature $feature)) { continue }
|
|
289
|
+
$created = Get-EpicWaveBarrierString -Element $feature -Name 'worktree_created_at'
|
|
290
|
+
foreach ($dependency in $dependsOn.Value.EnumerateArray()) {
|
|
291
|
+
$resolved = Resolve-EpicWaveBarrierReference -Dependency $dependency -ByHint $byHint -ByIssue $byIssue -Folder $folder
|
|
292
|
+
if ($null -eq $resolved -or -not $byFolder.ContainsKey($resolved)) { continue }
|
|
293
|
+
$target = $byFolder[$resolved]
|
|
294
|
+
$reference = Format-EpicWaveBarrierReference -Element $dependency
|
|
295
|
+
if (-not (Test-EpicWaveBarrierMerged -Status (Get-EpicWaveBarrierString -Element $target -Name 'merge_status'))) {
|
|
296
|
+
$script:StatusViolationFormat -f $folder, $reference
|
|
297
|
+
continue
|
|
298
|
+
}
|
|
299
|
+
$confirmed = Get-EpicWaveBarrierString -Element $target -Name 'merge_confirmed_at'
|
|
300
|
+
if ($null -ne $confirmed -and $null -ne $created -and [string]::CompareOrdinal($confirmed, $created) -gt 0) {
|
|
301
|
+
$script:TimingViolationFormat -f $folder, $reference
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function Get-OrchestratorStateEpicWaveBarrierError {
|
|
308
|
+
<#
|
|
309
|
+
.SYNOPSIS
|
|
310
|
+
Return the EPIC_WAVE_BARRIER_VIOLATION lines for an epic checkpoint's text.
|
|
311
|
+
.DESCRIPTION
|
|
312
|
+
Parses the text with System.Text.Json default options and applies the Layer 2
|
|
313
|
+
ordering check. Returns an empty array when there is no violation, when the root is
|
|
314
|
+
not an object, or when features is absent or not a list. Throws an error whose
|
|
315
|
+
message starts with EPIC_WAVE_BARRIER_UNEVALUABLE: for text System.Text.Json does
|
|
316
|
+
not parse (including NaN or Infinity literals and nesting deeper than 64 levels), an
|
|
317
|
+
array or object issue_num, and a non-integer number token that the check consults.
|
|
318
|
+
.PARAMETER CheckpointText
|
|
319
|
+
The raw epic checkpoint JSON text.
|
|
320
|
+
.OUTPUTS
|
|
321
|
+
System.String[]
|
|
322
|
+
#>
|
|
323
|
+
[CmdletBinding()]
|
|
324
|
+
[OutputType([string[]], [object[]])]
|
|
325
|
+
param(
|
|
326
|
+
[Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $CheckpointText
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
$document = $null
|
|
330
|
+
try {
|
|
331
|
+
$document = [System.Text.Json.JsonDocument]::Parse($CheckpointText, [System.Text.Json.JsonDocumentOptions]::new())
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
throw ('{0} the checkpoint is not parseable by System.Text.Json with default options (a non-standard literal such as NaN or Infinity, or nesting deeper than 64 levels): {1}' -f
|
|
335
|
+
$script:UnevaluableToken, $_.Exception.Message)
|
|
336
|
+
}
|
|
337
|
+
try {
|
|
338
|
+
$entries = Get-EpicWaveBarrierFeatureEntry -Root $document.RootElement
|
|
339
|
+
return , [string[]] @(Get-EpicWaveBarrierViolation -Entry $entries)
|
|
340
|
+
}
|
|
341
|
+
finally {
|
|
342
|
+
$document.Dispose()
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
Export-ModuleMember -Function Get-OrchestratorStateEpicWaveBarrierError
|
|
@@ -43,6 +43,8 @@ $script:WAIVABLE_TOOLS = [string[]]@($script:POTENTIAL_TO_ISSUE_TOOL, $script:FE
|
|
|
43
43
|
$script:PROMOTION_ENTRY_TOOLS = [string[]]@($script:FEATURE_PROMOTION_ENTRY_TOOL, $script:BUG_PROMOTION_ENTRY_TOOL)
|
|
44
44
|
$script:POTENTIAL_RECORD_PREFIX = 'docs/features/potential/'
|
|
45
45
|
$script:POTENTIAL_RECORD_SUFFIX = '.md'
|
|
46
|
+
# Origins whose promotion-entry waiver may omit the potential_record key (rule 9).
|
|
47
|
+
$script:RECORD_OPTIONAL_ORIGINS = [string[]]@('transferred', 'filed_before_orchestration')
|
|
46
48
|
$script:ISSUE_NUM_PATTERN = '\A[1-9][0-9]*\z'
|
|
47
49
|
|
|
48
50
|
$script:ERROR_NOT_OBJECT = 'Checkpoint issue_adoption must be an object when present.'
|
|
@@ -264,6 +266,13 @@ function Get-AdoptionPotentialRecordError {
|
|
|
264
266
|
The well-formed waived tool list, in checkpoint order.
|
|
265
267
|
.PARAMETER PotentialRecord
|
|
266
268
|
The deserialized potential_record value. May be $null.
|
|
269
|
+
.PARAMETER Origin
|
|
270
|
+
The deserialized origin value. May be $null. When the potential_record
|
|
271
|
+
key is absent and the origin is transferred or filed_before_orchestration,
|
|
272
|
+
nothing is reported.
|
|
273
|
+
.PARAMETER RecordPresent
|
|
274
|
+
Whether the potential_record key is present. A present record, including
|
|
275
|
+
$null, is always validated.
|
|
267
276
|
.OUTPUTS
|
|
268
277
|
System.String[] - zero or more error strings.
|
|
269
278
|
#>
|
|
@@ -275,9 +284,20 @@ function Get-AdoptionPotentialRecordError {
|
|
|
275
284
|
|
|
276
285
|
[Parameter(Mandatory = $true)]
|
|
277
286
|
[AllowNull()]
|
|
278
|
-
[object] $PotentialRecord
|
|
287
|
+
[object] $PotentialRecord,
|
|
288
|
+
|
|
289
|
+
[Parameter()]
|
|
290
|
+
[AllowNull()]
|
|
291
|
+
[object] $Origin,
|
|
292
|
+
|
|
293
|
+
[Parameter(Mandatory = $true)]
|
|
294
|
+
[bool] $RecordPresent
|
|
279
295
|
)
|
|
280
296
|
|
|
297
|
+
if (-not $RecordPresent -and ($Origin -is [string]) -and ($script:RECORD_OPTIONAL_ORIGINS -ccontains [string]$Origin)) {
|
|
298
|
+
return [string[]]@()
|
|
299
|
+
}
|
|
300
|
+
|
|
281
301
|
$recordIsValid = $false
|
|
282
302
|
if ($PotentialRecord -is [string]) {
|
|
283
303
|
$record = [string]$PotentialRecord
|
|
@@ -356,8 +376,9 @@ function Get-OrchestratorStateIssueAdoptionResult {
|
|
|
356
376
|
}
|
|
357
377
|
|
|
358
378
|
$errors.AddRange([string[]]@(Get-AdoptionWaivedToolError -WaivedTool $waived.Value -RouteId $RouteId -RequiredMcpTool $RequiredMcpTool -SuccessfulTool $SuccessfulTool))
|
|
359
|
-
$
|
|
360
|
-
$
|
|
379
|
+
$recordMember = Get-CheckpointObjectMember -Owner $adoption -Name 'potential_record'
|
|
380
|
+
$origin = (Get-CheckpointObjectMember -Owner $adoption -Name 'origin').Value
|
|
381
|
+
$errors.AddRange([string[]]@(Get-AdoptionPotentialRecordError -WaivedTool $waived.Value -PotentialRecord $recordMember.Value -Origin $origin -RecordPresent $recordMember.Present))
|
|
361
382
|
|
|
362
383
|
if ($errors.Count -gt 0) {
|
|
363
384
|
return @{ Errors = $errors.ToArray(); WaivedTools = [string[]]@() }
|
package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1
CHANGED
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
PowerShell 7+. Re-uses its siblings for enumeration, the marker test, the ascent,
|
|
22
22
|
the branch reader, normalisation, the join, the result constructor, and both
|
|
23
23
|
reason-code accessors, re-implementing none. Neither reason-code literal appears
|
|
24
|
-
here. Its only filesystem read is the checkpoint-text seam
|
|
24
|
+
here. Its only filesystem read is still the checkpoint-text seam, including for the
|
|
25
|
+
item-by-pull-request resolver added for issue #850. Mirrored byte-identically.
|
|
25
26
|
CONVENTION: this module fails fast at module scope and imports its siblings with -ErrorAction Stop.
|
|
26
27
|
#>
|
|
27
28
|
|
|
@@ -395,6 +396,81 @@ function Resolve-WorktreeItemTarget {
|
|
|
395
396
|
return (Resolve-WorktreeItemTargetByIssue -Session $session -Issue $issues[0])
|
|
396
397
|
}
|
|
397
398
|
|
|
399
|
+
# Private: whether checkpoint text records a pull request number in pr_gate.pr_number or
|
|
400
|
+
# in a standalone_merge_authorizations entry whose pr_number is a positive JSON integer.
|
|
401
|
+
function Test-WorktreeItemCheckpointRecordsPr {
|
|
402
|
+
[CmdletBinding()]
|
|
403
|
+
[OutputType([bool])]
|
|
404
|
+
param(
|
|
405
|
+
[Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $Text,
|
|
406
|
+
[Parameter(Mandatory = $true)] [long] $PrNumber
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
if ([string]::IsNullOrWhiteSpace($Text)) { return $false }
|
|
410
|
+
$payload = $null
|
|
411
|
+
try { $payload = $Text | ConvertFrom-Json } catch { return $false }
|
|
412
|
+
if ($null -eq $payload -or $payload -isnot [pscustomobject]) { return $false }
|
|
413
|
+
# Names are collected per property, because strict mode throws on .Name of an empty object.
|
|
414
|
+
$names = @($payload.PSObject.Properties | ForEach-Object { $_.Name })
|
|
415
|
+
if ($names -contains 'pr_gate' -and $payload.pr_gate -is [pscustomobject] -and @($payload.pr_gate.PSObject.Properties | ForEach-Object { $_.Name }) -contains 'pr_number') {
|
|
416
|
+
$recorded = [long] 0
|
|
417
|
+
if ([long]::TryParse([string] $payload.pr_gate.pr_number, [ref] $recorded) -and $recorded -eq $PrNumber) { return $true }
|
|
418
|
+
}
|
|
419
|
+
if ($names -notcontains 'standalone_merge_authorizations') { return $false }
|
|
420
|
+
foreach ($entry in @($payload.standalone_merge_authorizations)) {
|
|
421
|
+
if ($entry -isnot [pscustomobject] -or @($entry.PSObject.Properties | ForEach-Object { $_.Name }) -notcontains 'pr_number') { continue }
|
|
422
|
+
$value = $entry.pr_number
|
|
423
|
+
# Only a JSON integer counts; a string, fraction, zero, or negative never matches.
|
|
424
|
+
if (($value -is [int] -or $value -is [long]) -and $value -gt 0 -and [long] $value -eq $PrNumber) { return $true }
|
|
425
|
+
}
|
|
426
|
+
return $false
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function Resolve-WorktreeItemTargetByPrNumber {
|
|
430
|
+
<#
|
|
431
|
+
.SYNOPSIS
|
|
432
|
+
Resolve the live worktree whose orchestrator checkpoint records a pull request number (issue #850).
|
|
433
|
+
.DESCRIPTION
|
|
434
|
+
Keeps each live root whose checkpoint records the number in pr_gate.pr_number or
|
|
435
|
+
in a standalone_merge_authorizations entry. Zero matches resolve NoTarget, one
|
|
436
|
+
resolves, and several are Ambiguous with no tie-break. A number of zero or less
|
|
437
|
+
is NoTarget. Reads only through the checkpoint-text seam.
|
|
438
|
+
.PARAMETER PrNumber
|
|
439
|
+
The pull request number the command names.
|
|
440
|
+
.PARAMETER SessionRoot
|
|
441
|
+
The calling process's path, used only to find the repository and label a result.
|
|
442
|
+
Defaults to the current location when blank.
|
|
443
|
+
#>
|
|
444
|
+
[CmdletBinding()]
|
|
445
|
+
[OutputType([pscustomobject])]
|
|
446
|
+
param(
|
|
447
|
+
[Parameter(Mandatory = $true)] [long] $PrNumber,
|
|
448
|
+
[Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $SessionRoot
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
$session = if ([string]::IsNullOrWhiteSpace($SessionRoot)) { (Get-Location).ProviderPath } else { $SessionRoot }
|
|
452
|
+
$noTarget = "pull request {0} is recorded in pr_gate.pr_number or a standalone_merge_authorizations entry of no live worktree's orchestrator checkpoint" -f $PrNumber
|
|
453
|
+
if ($PrNumber -le 0) {
|
|
454
|
+
return (New-WorktreeResolutionTargetResult -Status 'NoTarget' -SessionRoot $session -Detail $noTarget)
|
|
455
|
+
}
|
|
456
|
+
# Assigned before it is wrapped, for the reason recorded in the branch resolver.
|
|
457
|
+
$liveRoots = Get-WorktreeItemLiveRoot -SessionRoot $session
|
|
458
|
+
$matched = [System.Collections.Generic.List[string]]::new()
|
|
459
|
+
foreach ($root in @($liveRoots)) {
|
|
460
|
+
$text = Get-WorktreeItemCheckpointText -Path (Get-WorktreeItemCheckpointPath -WorktreeRoot $root)
|
|
461
|
+
if (Test-WorktreeItemCheckpointRecordsPr -Text $text -PrNumber $PrNumber) { $matched.Add($root) }
|
|
462
|
+
}
|
|
463
|
+
if ($matched.Count -eq 0) {
|
|
464
|
+
return (New-WorktreeResolutionTargetResult -Status 'NoTarget' -SessionRoot $session -Detail $noTarget)
|
|
465
|
+
}
|
|
466
|
+
if ($matched.Count -eq 1) {
|
|
467
|
+
return (ConvertTo-WorktreeItemResolvedResult -WorktreeRoot $matched[0] -SessionRoot $session -Detail (
|
|
468
|
+
"pull request {0} resolves to the live worktree whose orchestrator checkpoint records it" -f $PrNumber))
|
|
469
|
+
}
|
|
470
|
+
return (New-WorktreeResolutionTargetResult -Status 'Ambiguous' -SessionRoot $session -Candidate $matched.ToArray() -Detail (
|
|
471
|
+
"pull request {0} is recorded in the orchestrator checkpoints of {1} live worktrees; move the stale checkpoint to artifacts/orchestration/handoff/ so that one worktree records it" -f $PrNumber, $matched.Count))
|
|
472
|
+
}
|
|
473
|
+
|
|
398
474
|
Export-ModuleMember -Function `
|
|
399
475
|
Get-WorktreeItemCheckpointRelativePath, `
|
|
400
476
|
Get-WorktreeItemCheckpointPath, `
|
|
@@ -404,4 +480,5 @@ Export-ModuleMember -Function `
|
|
|
404
480
|
Get-WorktreeItemCheckpointIssue, `
|
|
405
481
|
Get-WorktreeItemLiveRoot, `
|
|
406
482
|
ConvertTo-WorktreeItemResolvedResult, `
|
|
407
|
-
Resolve-WorktreeItemTarget
|
|
483
|
+
Resolve-WorktreeItemTarget, `
|
|
484
|
+
Resolve-WorktreeItemTargetByPrNumber
|
package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1
CHANGED
|
@@ -340,7 +340,7 @@ function Resolve-WorktreeParallelTarget {
|
|
|
340
340
|
}
|
|
341
341
|
|
|
342
342
|
# Private: compare two worktree paths, insensitive to separator style and trailing
|
|
343
|
-
# slashes, and case-insensitive
|
|
343
|
+
# slashes, and case-insensitive for drive-letter and UNC (//) paths.
|
|
344
344
|
function Test-WorktreeRunPathEqual {
|
|
345
345
|
[CmdletBinding()]
|
|
346
346
|
[OutputType([bool])]
|
|
@@ -352,8 +352,8 @@ function Test-WorktreeRunPathEqual {
|
|
|
352
352
|
if ([string]::IsNullOrWhiteSpace($Left) -or [string]::IsNullOrWhiteSpace($Right)) { return $false }
|
|
353
353
|
$a = ($Left.Trim() -replace '\\', '/').TrimEnd('/')
|
|
354
354
|
$b = ($Right.Trim() -replace '\\', '/').TrimEnd('/')
|
|
355
|
-
$
|
|
356
|
-
$comparison = if ($
|
|
355
|
+
$isCaseInsensitivePath = ($a -match '^([A-Za-z]:|//)') -or ($b -match '^([A-Za-z]:|//)')
|
|
356
|
+
$comparison = if ($isCaseInsensitivePath) { [System.StringComparison]::OrdinalIgnoreCase } else { [System.StringComparison]::Ordinal }
|
|
357
357
|
return [string]::Equals($a, $b, $comparison)
|
|
358
358
|
}
|
|
359
359
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
paths:
|
|
3
3
|
- "**/*.ts"
|
|
4
4
|
- "**/*.cs"
|
|
5
|
-
description: Architecture boundary enforcement rules for
|
|
5
|
+
description: Architecture boundary enforcement rules for a host-neutral layered architecture.
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Architecture Boundaries
|
|
@@ -14,7 +14,7 @@ Architecture boundary enforcement is a uniform gate across all tiers (T1–T4).
|
|
|
14
14
|
- **TypeScript:** `dependency-cruiser`. Configuration file pattern: `.dependency-cruiser.cjs`.
|
|
15
15
|
- **.NET (when the backend exists):** `NetArchTest.Rules`. Test project naming pattern: `*.ArchitectureTests`.
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## Host-Neutral Architecture Rules (enforceable assertions)
|
|
18
18
|
|
|
19
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
20
|
|
|
@@ -37,9 +37,9 @@ Production code in this repository must satisfy each of the following assertions
|
|
|
37
37
|
|
|
38
38
|
## Layer Boundary Assertions (.NET, applies once the backend exists)
|
|
39
39
|
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
- Adapter projects may depend on
|
|
40
|
+
- `<Product>.Domain` must have zero references to Outlook PIA, VSTO, or Office.js types.
|
|
41
|
+
- `<Product>.Application` may depend on `<Product>.Domain` only.
|
|
42
|
+
- Adapter projects may depend on `<Product>.Domain` and `<Product>.Application`; domain may not depend on adapters.
|
|
43
43
|
|
|
44
44
|
## Enforcement Outcome
|
|
45
45
|
|
|
@@ -2,12 +2,12 @@
|
|
|
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 (xUnit).
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# C# Code Standards
|
|
9
9
|
|
|
10
|
-
This rule file summarizes the C#-specific policies for this repository. It targets
|
|
10
|
+
This rule file summarizes the C#-specific policies for this repository. It targets a host-neutral .NET foundation: xUnit, NSubstitute, FluentAssertions, `dotnet build`, the analyzer stack, `TimeProvider`, and uniform coverage thresholds.
|
|
11
11
|
|
|
12
12
|
## Toolchain
|
|
13
13
|
|
|
@@ -26,7 +26,7 @@ Apply these priorities in order when designing or changing code:
|
|
|
26
26
|
|
|
27
27
|
## Module Rigor Tiers
|
|
28
28
|
|
|
29
|
-
Module rigor tiers (T1–T4) and the uniform-versus-tier-dependent gate matrix are defined in `.claude/rules/quality-tiers.md
|
|
29
|
+
Module rigor tiers (T1–T4) and the uniform-versus-tier-dependent gate matrix are defined in `.claude/rules/quality-tiers.md` and apply only when `quality-tiers.yml` exists at the repository root; in that case every project must be classified in it. A repository without `quality-tiers.yml` has not adopted tiers, and the tier requirements do not apply.
|
|
30
30
|
|
|
31
31
|
## Mandatory Toolchain Loop
|
|
32
32
|
|
|
@@ -20,10 +20,11 @@ Every unit test must satisfy all five of these properties:
|
|
|
20
20
|
|
|
21
21
|
## Coverage Requirements
|
|
22
22
|
|
|
23
|
+
- Threshold precedence: when the repository's root `CLAUDE.md` states line or branch coverage thresholds, those thresholds govern. The 85% line and 75% branch figures are defaults that apply only when the root `CLAUDE.md` states none. This precedence applies to every restatement of these figures in other rule files, agents, and skills. Each metric falls back independently: when the root `CLAUDE.md` states only a line threshold or only a branch threshold, the stated threshold governs that metric and the default governs the other.
|
|
23
24
|
- **Line coverage must remain >= 85% across all tiers (T1–T4).**
|
|
24
25
|
- **Branch coverage must remain >= 75% across all tiers (T1–T4) for languages whose coverage tooling measures branch coverage.** PowerShell (Pester) and bash (kcov) are the exceptions: neither tool measures branch coverage in any output format, so only the line threshold applies to them and there is no branch-coverage gate. This is a threshold exemption only; PowerShell and bash production files remain in the coverage denominator under the Coverage Exclusion Policy below.
|
|
25
26
|
- Code changes or refactors must not reduce coverage for the lines that were changed.
|
|
26
|
-
- Tier-specific lower coverage thresholds are not used
|
|
27
|
+
- Tier-specific lower coverage thresholds are not used. See `.claude/rules/quality-tiers.md` for the full tier system, which applies only when `quality-tiers.yml` exists at the repository root.
|
|
27
28
|
- Coverage is a supporting metric, not the sole quality gate. Untested critical behavior is not acceptable even if the overall percentage looks good.
|
|
28
29
|
- Configure coverage tooling to exclude test files (e.g., `tests/`) so metrics reflect application code, not tests.
|
|
29
30
|
- 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.
|
|
@@ -86,7 +87,7 @@ Colocation — placing test files alongside production source files in `src/` or
|
|
|
86
87
|
|
|
87
88
|
## Test Categories
|
|
88
89
|
|
|
89
|
-
The following test categories apply across the repository, with tier-dependent obligations per `.claude/rules/quality-tiers.md
|
|
90
|
+
The following test categories apply across the repository, with tier-dependent obligations per `.claude/rules/quality-tiers.md`; those tier-dependent obligations apply only when `quality-tiers.yml` exists at the repository root:
|
|
90
91
|
|
|
91
92
|
- **Unit tests** — required for all tiers (T1–T4). Cover single units of behavior in isolation.
|
|
92
93
|
- **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.
|