@danmoisan/drm-copilot-mcp 1.1.22 → 1.1.24
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 +18758 -12030
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/MEMORY.md +1 -1
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_test_files_count_against_500_cap.md +4 -4
- package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +1 -1
- package/resources/claude-customizations/.claude/agents/discovery-capability-sentinel.md +34 -0
- package/resources/claude-customizations/.claude/agents/feature-review.md +8 -0
- package/resources/claude-customizations/.claude/agents/legacy-parity-analyst.md +101 -43
- package/resources/claude-customizations/.claude/agents/migration-coverage-reviewer.md +107 -43
- package/resources/claude-customizations/.claude/agents/orchestrator.md +1 -1
- package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +60 -6
- package/resources/claude-customizations/.claude/agents/parallel-planner.md +21 -1
- package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +1 -1
- package/resources/claude-customizations/.claude/agents/pr-author.md +48 -7
- package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +1 -1
- package/resources/claude-customizations/.claude/agents/requirements-reconciler.md +117 -42
- package/resources/claude-customizations/.claude/agents/runtime-characterization-analyst.md +103 -43
- package/resources/claude-customizations/.claude/hooks/discovery-capability-probe.ps1 +102 -0
- package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +69 -194
- package/resources/claude-customizations/.claude/hooks/enforce-discovery-planning-gate.ps1 +87 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-green-ci.ps1 +325 -0
- package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +29 -7
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +23 -10
- package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +29 -21
- package/resources/claude-customizations/.claude/hooks/enforce-file-size-limit.ps1 +244 -0
- package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +51 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +168 -48
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +1 -1
- package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +22 -11
- package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +59 -20
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +51 -4
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +72 -11
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.current-branch.ps1 +149 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.epic-base-branch.ps1 +4 -2
- package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +59 -20
- package/resources/claude-customizations/.claude/hooks/record-discovery-agent-start.ps1 +132 -0
- package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +56 -212
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-feature-folder.ps1 +146 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-rejection.ps1 +172 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +34 -4
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadius.psm1 +9 -3
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusAggregate.psm1 +154 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusCombination.psm1 +228 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +1 -0
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusNormalization.psm1 +4 -2
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusScheduling.psm1 +63 -16
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +56 -1
- package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusWriteIntent.psm1 +4 -1
- package/resources/claude-customizations/.claude/lib/cleanup-manifest/CleanupWorktreeManifest.psm1 +33 -6
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexDeployment.psm1 +5 -1
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexDiscoveryRouting.psm1 +161 -0
- package/resources/claude-customizations/.claude/lib/codex-routing/CodexTopology.psm1 +25 -2
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryAuthority.psd1 +90 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryAuthority.psm1 +493 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryAuthorization.psm1 +479 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryCapability.psm1 +468 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryEncoding.psm1 +156 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryManifest.psm1 +384 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryObservation.psm1 +217 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryPayload.psm1 +276 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryPlanning.psm1 +336 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryRootGrant.psm1 +164 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryStart.psm1 +440 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryStartRecord.psm1 +296 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryStop.psm1 +336 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryToolRouting.psm1 +228 -0
- package/resources/claude-customizations/.claude/lib/discovery-validation/DiscoveryValidation.psm1 +188 -341
- package/resources/claude-customizations/.claude/lib/file-size-policy/FileSizePolicy.psm1 +434 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateFeatureFolderRecord.psm1 +166 -0
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +8 -3
- package/resources/claude-customizations/.claude/lib/worktree-resolution/HookTargetWorktree.psm1 +391 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunRecordPath.psm1 +185 -0
- package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +11 -21
- package/resources/claude-customizations/.claude/rules/general-code-change.md +4 -1
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +29 -5
- package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +152 -20
- package/resources/claude-customizations/.claude/rules/powershell.md +1 -1
- package/resources/claude-customizations/.claude/rules/shell.md +1 -1
- package/resources/claude-customizations/.claude/settings.json +36 -3
- package/resources/claude-customizations/.claude/skills/adjudicate-unspecified-behavior/SKILL.md +135 -0
- package/resources/claude-customizations/.claude/skills/characterize-runtime-behavior/SKILL.md +134 -0
- package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +2 -2
- package/resources/claude-customizations/.claude/skills/discovery-behavior-reconciliation/SKILL.md +28 -47
- package/resources/claude-customizations/.claude/skills/discovery-coverage-ledger/SKILL.md +32 -48
- package/resources/claude-customizations/.claude/skills/discovery-parity-matrix/SKILL.md +28 -45
- package/resources/claude-customizations/.claude/skills/discovery-repo-inventory/SKILL.md +28 -62
- package/resources/claude-customizations/.claude/skills/discovery-runtime-characterization/SKILL.md +28 -45
- package/resources/claude-customizations/.claude/skills/discovery-validate-artifacts/SKILL.md +28 -61
- package/resources/claude-customizations/.claude/skills/discovery-workflow/SKILL.md +55 -139
- package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +25 -10
- package/resources/claude-customizations/.claude/skills/extract-feature-contract/SKILL.md +133 -0
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -0
- package/resources/claude-customizations/.claude/skills/generate-acceptance-scenarios/SKILL.md +132 -0
- package/resources/claude-customizations/.claude/skills/initialize-discovery-workspace/SKILL.md +131 -0
- package/resources/claude-customizations/.claude/skills/inventory-legacy-system/SKILL.md +131 -0
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +15 -7
- package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +25 -2
- package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +11 -1
- package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +124 -14
- package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +72 -7
- package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +15 -0
- package/resources/claude-customizations/.claude/skills/parallel-run/SKILL.md +6 -0
- package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +56 -9
- package/resources/claude-customizations/.claude/skills/reconcile-parity-matrix/SKILL.md +131 -0
- package/resources/claude-customizations/.claude/skills/review-discovery-coverage/SKILL.md +129 -0
- package/resources/claude-customizations/.claude/skills/validate-discovery-artifacts/SKILL.md +127 -0
- package/resources/claude-customizations/.claude-variants/csharp-legacy/agents/csharp-typed-engineer.md +1 -1
- package/resources/claude-customizations/config/blast-radius.json +6 -5
- package/resources/claude-customizations/config/orchestration-routing.json +61 -1
- package/resources/claude-customizations/pack-manifests/core.json +40 -1
- package/resources/codex-and-agents-customizations/.agents/skills/adjudicate-unspecified-behavior/SKILL.md +135 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +7 -0
- package/resources/codex-and-agents-customizations/.agents/skills/characterize-runtime-behavior/SKILL.md +134 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-behavior-reconciliation/SKILL.md +46 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-coverage-ledger/SKILL.md +50 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-parity-matrix/SKILL.md +46 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-repo-inventory/SKILL.md +46 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-runtime-characterization/SKILL.md +46 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-validate-artifacts/SKILL.md +46 -0
- package/resources/codex-and-agents-customizations/.agents/skills/discovery-workflow/SKILL.md +62 -0
- package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +25 -10
- package/resources/codex-and-agents-customizations/.agents/skills/extract-feature-contract/SKILL.md +133 -0
- package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +8 -0
- package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +4 -1
- package/resources/codex-and-agents-customizations/.agents/skills/generate-acceptance-scenarios/SKILL.md +132 -0
- package/resources/codex-and-agents-customizations/.agents/skills/initialize-discovery-workspace/SKILL.md +131 -0
- package/resources/codex-and-agents-customizations/.agents/skills/inventory-legacy-system/SKILL.md +131 -0
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +8 -0
- package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/reconcile-parity-matrix/SKILL.md +131 -0
- package/resources/codex-and-agents-customizations/.agents/skills/review-discovery-coverage/SKILL.md +129 -0
- package/resources/codex-and-agents-customizations/.agents/skills/validate-discovery-artifacts/SKILL.md +127 -0
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/discovery-capability-sentinel.toml +51 -0
- package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +8 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c1.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c2.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c3-elevated.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c3.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst-c4.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/legacy-parity-analyst.toml +148 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c1.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c2.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c3-elevated.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c3.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer-c4.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/migration-coverage-reviewer.toml +154 -0
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c1.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c2.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c3-elevated.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c3.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler-c4.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/requirements-reconciler.toml +164 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c1.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c2.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c3-elevated.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c3.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst-c4.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/agents/runtime-characterization-analyst.toml +150 -0
- package/resources/codex-and-agents-customizations/.codex/config.toml +39 -3
- package/resources/codex-and-agents-customizations/.codex/hooks/codex-agent-profile-attestation.ps1 +22 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/discovery-capability-probe.ps1 +97 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/discovery-start-attestation.ps1 +171 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +4 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-discovery-artifact-gate.ps1 +187 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-discovery-planning-gate.ps1 +149 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-file-size-limit.ps1 +282 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +239 -2
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +6 -15
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +54 -15
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +54 -15
- package/resources/codex-and-agents-customizations/.codex/hooks/file-size-policy.ps1 +421 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/hook-target-worktree.ps1 +369 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/record-subagent-routing-attestation.ps1 +8 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +22 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/validate-discovery-agent-output.ps1 +178 -0
- package/resources/codex-and-agents-customizations/.codex/scripts/Resolve-CodexTopology.ps1 +11 -1
- package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/AGENTS.md +4 -1
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +65 -1
- package/resources/config/orchestration-routing.json +61 -1
- package/resources/customizations/.github/agents/commentary-remediation.agent.md +1 -1
- package/resources/customizations/.github/agents/discovery-capability-sentinel.agent.md +34 -0
- package/resources/customizations/.github/agents/legacy-parity-analyst.agent.md +122 -0
- package/resources/customizations/.github/agents/migration-coverage-reviewer.agent.md +128 -0
- package/resources/customizations/.github/agents/python-execution-only-typed.agent.md +1 -1
- package/resources/customizations/.github/agents/python-typed-engineer.agent.md +1 -1
- package/resources/customizations/.github/agents/requirements-reconciler.agent.md +138 -0
- package/resources/customizations/.github/agents/runtime-characterization-analyst.agent.md +124 -0
- package/resources/customizations/.github/hooks/discovery-agent-start.ps1 +139 -0
- package/resources/customizations/.github/hooks/discovery-agent-stop.ps1 +123 -0
- package/resources/customizations/.github/hooks/discovery-capability-probe.ps1 +98 -0
- package/resources/customizations/.github/hooks/discovery-planning.ps1 +91 -0
- package/resources/customizations/.github/hooks/discovery-pre-tool.ps1 +123 -0
- package/resources/customizations/.github/hooks/discovery.json +35 -0
- package/resources/customizations/.github/instructions/general-code-change.instructions.md +5 -2
- package/resources/customizations/.github/instructions/mermaid.instructions.md +85 -0
- package/resources/customizations/.github/instructions/powershell-code-change.instructions.md +1 -1
- package/resources/customizations/.github/prompts/execute-hard-lock.prompt.md +105 -0
- package/resources/customizations/.github/prompts/remediate-comments.prompt.md +1 -1
- package/resources/customizations/.github/skills/adjudicate-unspecified-behavior/SKILL.md +135 -0
- package/resources/customizations/.github/skills/characterize-runtime-behavior/SKILL.md +134 -0
- package/resources/customizations/.github/skills/discovery-behavior-reconciliation/SKILL.md +46 -0
- package/resources/customizations/.github/skills/discovery-coverage-ledger/SKILL.md +50 -0
- package/resources/customizations/.github/skills/discovery-parity-matrix/SKILL.md +46 -0
- package/resources/customizations/.github/skills/discovery-repo-inventory/SKILL.md +46 -0
- package/resources/customizations/.github/skills/discovery-runtime-characterization/SKILL.md +46 -0
- package/resources/customizations/.github/skills/discovery-validate-artifacts/SKILL.md +46 -0
- package/resources/customizations/.github/skills/discovery-workflow/SKILL.md +62 -0
- package/resources/customizations/.github/skills/execution-environment/SKILL.md +25 -10
- package/resources/customizations/.github/skills/extract-feature-contract/SKILL.md +133 -0
- package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +8 -0
- package/resources/customizations/.github/skills/generate-acceptance-scenarios/SKILL.md +132 -0
- package/resources/customizations/.github/skills/initialize-discovery-workspace/SKILL.md +131 -0
- package/resources/customizations/.github/skills/inventory-legacy-system/SKILL.md +131 -0
- package/resources/customizations/.github/skills/reconcile-parity-matrix/SKILL.md +131 -0
- package/resources/customizations/.github/skills/review-discovery-coverage/SKILL.md +129 -0
- package/resources/customizations/.github/skills/validate-discovery-artifacts/SKILL.md +127 -0
- package/resources/lib/codex-routing/CodexDeployment.psm1 +5 -1
- package/resources/lib/codex-routing/CodexDiscoveryRouting.psm1 +161 -0
- package/resources/lib/codex-routing/CodexTopology.psm1 +25 -2
- package/resources/lib/discovery-validation/DiscoveryAuthority.psd1 +90 -0
- package/resources/lib/discovery-validation/DiscoveryAuthority.psm1 +493 -0
- package/resources/lib/discovery-validation/DiscoveryAuthorization.psm1 +479 -0
- package/resources/lib/discovery-validation/DiscoveryCapability.psm1 +468 -0
- package/resources/lib/discovery-validation/DiscoveryEncoding.psm1 +156 -0
- package/resources/lib/discovery-validation/DiscoveryManifest.psm1 +384 -0
- package/resources/lib/discovery-validation/DiscoveryObservation.psm1 +217 -0
- package/resources/lib/discovery-validation/DiscoveryPayload.psm1 +276 -0
- package/resources/lib/discovery-validation/DiscoveryPlanning.psm1 +336 -0
- package/resources/lib/discovery-validation/DiscoveryRootGrant.psm1 +164 -0
- package/resources/lib/discovery-validation/DiscoveryStart.psm1 +440 -0
- package/resources/lib/discovery-validation/DiscoveryStartRecord.psm1 +296 -0
- package/resources/lib/discovery-validation/DiscoveryStop.psm1 +336 -0
- package/resources/lib/discovery-validation/DiscoveryToolRouting.psm1 +228 -0
- package/resources/powershell/PoshQC/PoshQC.Parallel.psm1 +452 -0
- package/resources/powershell/PoshQC/PoshQC.Shard.psm1 +106 -8
- package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +12 -0
- package/resources/powershell/PoshQC/PoshQC.psd1 +6 -1
- package/resources/powershell/PoshQC/PoshQC.psm1 +7 -1
- package/resources/powershell/PoshQC/README.md +20 -1
- package/resources/templates/policy_audit/code-review.yyyy-MM-ddTHH-mm.md +4 -4
- package/resources/templates/policy_audit/policy-audit.yyyy-MM-ddTHH-mm.md +4 -4
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
@{
|
|
2
|
+
# Module manifest for the shared native discovery authority primitives (issue #980).
|
|
3
|
+
# Importing this manifest loads the authority, manifest and payload modules together
|
|
4
|
+
# so a hook can take every narrow primitive from one import.
|
|
5
|
+
RootModule = 'DiscoveryAuthority.psm1'
|
|
6
|
+
ModuleVersion = '2.0.0'
|
|
7
|
+
GUID = '5d0b8e5e-3b7f-4a2a-9f8e-2f9f0b9a7c11'
|
|
8
|
+
Author = 'drm-copilot'
|
|
9
|
+
CompanyName = 'drm-copilot'
|
|
10
|
+
Copyright = '(c) drm-copilot. All rights reserved.'
|
|
11
|
+
Description = 'Protected discovery authority location, activation decoding, input-manifest freshness and host envelope normalization for native discovery hooks.'
|
|
12
|
+
PowerShellVersion = '7.0'
|
|
13
|
+
NestedModules = @('DiscoveryEncoding.psm1', 'DiscoveryManifest.psm1', 'DiscoveryPayload.psm1', 'DiscoveryAuthorization.psm1', 'DiscoveryStartRecord.psm1', 'DiscoveryStart.psm1', 'DiscoveryStop.psm1', 'DiscoveryPlanning.psm1', 'DiscoveryCapability.psm1')
|
|
14
|
+
FunctionsToExport = @(
|
|
15
|
+
'Get-DiscoverySha256Hex',
|
|
16
|
+
'Get-DiscoveryBytesSha256Hex',
|
|
17
|
+
'ConvertTo-DiscoveryCanonicalJson',
|
|
18
|
+
'Invoke-DiscoveryStoreSeam',
|
|
19
|
+
'Resolve-DiscoveryAuthorityLocation',
|
|
20
|
+
'Get-DiscoveryTrustedHostEnvironment',
|
|
21
|
+
'Get-DiscoveryPartitionName',
|
|
22
|
+
'Get-DiscoverySessionDirectory',
|
|
23
|
+
'Get-DiscoveryInvocationDirectory',
|
|
24
|
+
'Get-DiscoveryActivationRecordPath',
|
|
25
|
+
'Test-DiscoveryProtectedPath',
|
|
26
|
+
'Test-DiscoveryRelativePath',
|
|
27
|
+
'ConvertFrom-DiscoveryActivationRecord',
|
|
28
|
+
'Read-DiscoveryActivationState',
|
|
29
|
+
'ConvertFrom-DiscoveryInputManifest',
|
|
30
|
+
'Get-DiscoveryInputManifestDigest',
|
|
31
|
+
'Get-DiscoveryWorkspacePath',
|
|
32
|
+
'Compare-DiscoveryInputManifest',
|
|
33
|
+
'Assert-DiscoveryInputsFresh',
|
|
34
|
+
'Test-DiscoverySupportedHost',
|
|
35
|
+
'ConvertFrom-DiscoveryHostEnvelope',
|
|
36
|
+
'ConvertFrom-DiscoveryHostPayloadText',
|
|
37
|
+
'Get-DiscoveryTrustedPrincipal',
|
|
38
|
+
'Get-DiscoveryHostEnvironmentValueTable',
|
|
39
|
+
'Get-DiscoveryPayloadAnomalyReason',
|
|
40
|
+
'Resolve-DiscoveryToolName',
|
|
41
|
+
'Get-DiscoveryToolOperation',
|
|
42
|
+
'Get-DiscoveryInputDigest',
|
|
43
|
+
'Find-DiscoveryAgentInvocation',
|
|
44
|
+
'ConvertTo-DiscoveryToolAuthorizationRecord',
|
|
45
|
+
'Get-DiscoveryDefaultStore',
|
|
46
|
+
'Get-DiscoveryAuthorizationNonce',
|
|
47
|
+
'Get-DiscoveryPreToolDecision',
|
|
48
|
+
'ConvertTo-DiscoveryPreToolHookOutput',
|
|
49
|
+
'Get-DiscoveryAgentRole',
|
|
50
|
+
'Get-DiscoveryAgentProfilePath',
|
|
51
|
+
'ConvertFrom-DiscoveryMarkdownProfile',
|
|
52
|
+
'Read-DiscoveryAgentProfile',
|
|
53
|
+
'ConvertTo-DiscoveryUtcInstant',
|
|
54
|
+
'ConvertTo-DiscoveryStartedInvocationText',
|
|
55
|
+
'ConvertTo-DiscoveryStartAttestationRecord',
|
|
56
|
+
'Get-DiscoveryStartStore',
|
|
57
|
+
'Get-DiscoveryStartAttestationFileName',
|
|
58
|
+
'Find-DiscoveryPreparedInvocation',
|
|
59
|
+
'Get-DiscoveryDeploymentMismatch',
|
|
60
|
+
'Get-DiscoveryCapabilityMismatch',
|
|
61
|
+
'Get-DiscoveryStartDecision',
|
|
62
|
+
'Get-DiscoveryAuthorizationNonceText',
|
|
63
|
+
'ConvertTo-DiscoveryStartHookOutput',
|
|
64
|
+
'Find-DiscoveryAgentInvocationForStop',
|
|
65
|
+
'Get-DiscoveryReceiptMismatch',
|
|
66
|
+
'Get-DiscoveryStopDecision',
|
|
67
|
+
'Get-DiscoveryStopVerdict',
|
|
68
|
+
'Get-DiscoveryPlanningTarget',
|
|
69
|
+
'Get-DiscoveryPlanningPrerequisite',
|
|
70
|
+
'Get-DiscoveryPlanningReceiptMismatch',
|
|
71
|
+
'Get-DiscoveryPlanningDecision',
|
|
72
|
+
'ConvertTo-DiscoveryPlanningHookOutput',
|
|
73
|
+
'Get-DiscoverySentinelRequest',
|
|
74
|
+
'Get-DiscoverySentinelEnvelope',
|
|
75
|
+
'Get-DiscoverySessionObservation',
|
|
76
|
+
'Get-DiscoveryObservationEvidence',
|
|
77
|
+
'Get-DiscoveryHostRegistration',
|
|
78
|
+
'Test-DiscoveryHandlerRegistered',
|
|
79
|
+
'Get-DiscoveryCapabilityObservation',
|
|
80
|
+
'Write-DiscoveryCapabilityAttestation'
|
|
81
|
+
)
|
|
82
|
+
CmdletsToExport = @()
|
|
83
|
+
VariablesToExport = @()
|
|
84
|
+
AliasesToExport = @()
|
|
85
|
+
PrivateData = @{
|
|
86
|
+
PSData = @{
|
|
87
|
+
Tags = @('drm-copilot', 'discovery', 'hooks')
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Narrow protected-authority primitives shared by every native discovery hook.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Resolves the fixed external discovery authority location from trusted host
|
|
7
|
+
values and decodes the protected activation record, with the same rules the
|
|
8
|
+
TypeScript gateway (`discovery-authority-paths.ts`, `discovery-authority-record.ts`)
|
|
9
|
+
and the Python runtime (`RegistryLocation.for_host`, `ActivationRecord`) apply:
|
|
10
|
+
|
|
11
|
+
<user-state>/drm-copilot/discovery/workspaces/<sha256(canonical workspace)>
|
|
12
|
+
|
|
13
|
+
Windows hashes the invariant-lowercase native spelling; POSIX preserves case.
|
|
14
|
+
Session and invocation partitions are the digest of their correlation id.
|
|
15
|
+
Hashing and canonical JSON come from the sibling DiscoveryEncoding module so
|
|
16
|
+
hooks reproduce the runtime's digests byte for byte.
|
|
17
|
+
|
|
18
|
+
The module performs no writes, invokes no process and never reads a profile,
|
|
19
|
+
locator or request field to decide a location; every input is a trusted host
|
|
20
|
+
value or an injected seam. A malformed or foreign record is a typed failure,
|
|
21
|
+
never a silent pass.
|
|
22
|
+
|
|
23
|
+
.NOTES
|
|
24
|
+
Issue #980. Compatible with PowerShell 7+. No external module dependencies.
|
|
25
|
+
Mirrored byte-identically under extensions/drm-copilot/resources/claude-customizations/
|
|
26
|
+
and published to the Codex bundle under .codex/lib/discovery-validation/.
|
|
27
|
+
CONVENTION: this module fails fast at module scope and imports its siblings with -ErrorAction Stop.
|
|
28
|
+
#>
|
|
29
|
+
|
|
30
|
+
Set-StrictMode -Version Latest
|
|
31
|
+
$ErrorActionPreference = 'Stop'
|
|
32
|
+
|
|
33
|
+
Import-Module (Join-Path $PSScriptRoot 'DiscoveryEncoding.psm1') -ErrorAction Stop
|
|
34
|
+
|
|
35
|
+
$script:AuthorityRootSegments = @('drm-copilot', 'discovery')
|
|
36
|
+
$script:ActivationRecordFile = 'activation.json'
|
|
37
|
+
$script:ActivationSchemaVersion = '2.0.0'
|
|
38
|
+
$script:ActivationRecordKeys = @(
|
|
39
|
+
'action', 'generation', 'locator_sha256', 'previous_sha256',
|
|
40
|
+
'profile_path', 'profile_sha256', 'schema_version', 'workspace_sha256'
|
|
41
|
+
)
|
|
42
|
+
$script:CorrelationIdPattern = '^[A-Za-z0-9][A-Za-z0-9._:-]*$'
|
|
43
|
+
$script:Sha256Pattern = '^[0-9a-f]{64}$'
|
|
44
|
+
$script:RelativePathPattern = '^(?!/)(?!.*(?:^|/)\.{1,2}(?:/|$))[^/\\:\u2028\u2029*?<>|]+(?:/[^/\\:\u2028\u2029*?<>|]+)*$'
|
|
45
|
+
$script:ReservedDevicePattern = '^(?:CON|PRN|AUX|NUL|CONIN\$|CONOUT\$|CLOCK\$|(?:COM|LPT)[1-9\u00b9\u00b2\u00b3])$'
|
|
46
|
+
$script:MaxRecordBytes = 65536
|
|
47
|
+
|
|
48
|
+
function ConvertTo-DiscoveryCanonicalPath {
|
|
49
|
+
<#
|
|
50
|
+
.SYNOPSIS
|
|
51
|
+
Splits a trusted absolute path into anchor and segments the way pathlib does.
|
|
52
|
+
.DESCRIPTION
|
|
53
|
+
Separators collapse, `.` segments vanish, trailing separators are dropped and
|
|
54
|
+
`..` is rejected because an unresolved alias cannot identify a workspace.
|
|
55
|
+
#>
|
|
56
|
+
[CmdletBinding()]
|
|
57
|
+
[OutputType([hashtable])]
|
|
58
|
+
param(
|
|
59
|
+
[Parameter(Mandatory = $true)][string]$Path,
|
|
60
|
+
[Parameter(Mandatory = $true)][ValidateSet('windows', 'macos', 'linux')][string]$Platform,
|
|
61
|
+
[Parameter(Mandatory = $true)][string]$Label
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
$windowsPlatform = $Platform -eq 'windows'
|
|
65
|
+
$driveRooted = $windowsPlatform -and ($Path -match '^[A-Za-z]:[\\/]')
|
|
66
|
+
$uncRooted = $windowsPlatform -and ($Path -match '^[\\/]{2}[^\\/]+[\\/][^\\/]+')
|
|
67
|
+
if (-not ($driveRooted -or $uncRooted -or ((-not $windowsPlatform) -and $Path.StartsWith('/')))) {
|
|
68
|
+
throw "AUTHORITY_PATH_NOT_CANONICAL: $Label must be an absolute path: $Path"
|
|
69
|
+
}
|
|
70
|
+
$unified = $(if ($windowsPlatform) { $Path.Replace('\', '/') } else { $Path })
|
|
71
|
+
# The anchor is the drive, the UNC share or the POSIX root (two leading
|
|
72
|
+
# slashes stay a distinct root, as pathlib treats them).
|
|
73
|
+
if ($driveRooted) {
|
|
74
|
+
$anchor = $unified.Substring(0, 2) + '/'
|
|
75
|
+
$rest = $unified.Substring(3)
|
|
76
|
+
} elseif ($uncRooted) {
|
|
77
|
+
$match = [regex]::Match($unified, '^//([^/]+)/([^/]+)/?')
|
|
78
|
+
$anchor = '//' + $match.Groups[1].Value + '/' + $match.Groups[2].Value + '/'
|
|
79
|
+
$rest = $unified.Substring($match.Length)
|
|
80
|
+
} else {
|
|
81
|
+
$anchor = $(if ($unified -match '^//(?!/)') { '//' } else { '/' })
|
|
82
|
+
$rest = $unified.TrimStart('/')
|
|
83
|
+
}
|
|
84
|
+
$parts = [System.Collections.Generic.List[string]]::new()
|
|
85
|
+
# Drop empty and current-directory segments; reject parent references.
|
|
86
|
+
foreach ($segment in $rest.Split('/')) {
|
|
87
|
+
if ($segment -eq '' -or $segment -eq '.') { continue }
|
|
88
|
+
if ($segment -eq '..') {
|
|
89
|
+
throw "AUTHORITY_PATH_NOT_CANONICAL: $Label must not contain parent references: $Path"
|
|
90
|
+
}
|
|
91
|
+
$parts.Add($segment)
|
|
92
|
+
}
|
|
93
|
+
return @{ Platform = $Platform; Anchor = $anchor; Parts = $parts.ToArray() }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function ConvertTo-DiscoveryPosixPath {
|
|
97
|
+
<#
|
|
98
|
+
.SYNOPSIS
|
|
99
|
+
Renders a canonical path in POSIX spelling (what PurePath.as_posix prints).
|
|
100
|
+
#>
|
|
101
|
+
[CmdletBinding()]
|
|
102
|
+
[OutputType([string])]
|
|
103
|
+
param([Parameter(Mandatory = $true)][hashtable]$CanonicalPath)
|
|
104
|
+
|
|
105
|
+
$joined = ($CanonicalPath.Parts -join '/')
|
|
106
|
+
if ($joined -eq '') { return $CanonicalPath.Anchor }
|
|
107
|
+
return $CanonicalPath.Anchor + $joined
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function Test-DiscoveryCanonicalPathWithin {
|
|
111
|
+
<#
|
|
112
|
+
.SYNOPSIS
|
|
113
|
+
True when Candidate equals or descends from Ancestor (case-insensitive on Windows).
|
|
114
|
+
#>
|
|
115
|
+
[CmdletBinding()]
|
|
116
|
+
[OutputType([bool])]
|
|
117
|
+
param(
|
|
118
|
+
[Parameter(Mandatory = $true)][hashtable]$Candidate,
|
|
119
|
+
[Parameter(Mandatory = $true)][hashtable]$Ancestor
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
$fold = $Candidate.Platform -eq 'windows'
|
|
123
|
+
$comparison = $(if ($fold) { [System.StringComparison]::OrdinalIgnoreCase } else { [System.StringComparison]::Ordinal })
|
|
124
|
+
if (-not [string]::Equals($Candidate.Anchor, $Ancestor.Anchor, $comparison)) { return $false }
|
|
125
|
+
if ($Candidate.Parts.Count -lt $Ancestor.Parts.Count) { return $false }
|
|
126
|
+
# Every ancestor segment must match the candidate prefix segment-wise.
|
|
127
|
+
for ($index = 0; $index -lt $Ancestor.Parts.Count; $index++) {
|
|
128
|
+
if (-not [string]::Equals($Candidate.Parts[$index], $Ancestor.Parts[$index], $comparison)) { return $false }
|
|
129
|
+
}
|
|
130
|
+
return $true
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function Resolve-DiscoveryAuthorityLocation {
|
|
134
|
+
<#
|
|
135
|
+
.SYNOPSIS
|
|
136
|
+
Resolves the protected authority location for a workspace from trusted host values.
|
|
137
|
+
.DESCRIPTION
|
|
138
|
+
Mirrors `RegistryLocation.for_host`: the workspace, home and state roots must
|
|
139
|
+
be absolute without parent references, the state root is fixed platform
|
|
140
|
+
policy (LocalApplicationData, ~/Library/Application Support, or
|
|
141
|
+
$XDG_STATE_HOME / ~/.local/state), and the authority root must lie outside
|
|
142
|
+
the workspace. No directory is created or read.
|
|
143
|
+
.OUTPUTS
|
|
144
|
+
[pscustomobject] Platform, Workspace, WorkspaceSha256, Root, Directory (POSIX spelling).
|
|
145
|
+
#>
|
|
146
|
+
[CmdletBinding()]
|
|
147
|
+
[OutputType([pscustomobject])]
|
|
148
|
+
param(
|
|
149
|
+
[Parameter(Mandatory = $true)][string]$WorkspaceRoot,
|
|
150
|
+
[Parameter(Mandatory = $true)][ValidateSet('windows', 'macos', 'linux')][string]$Platform,
|
|
151
|
+
[Parameter(Mandatory = $true)][string]$UserHome,
|
|
152
|
+
[Parameter(Mandatory = $false)][AllowNull()][AllowEmptyString()][string]$LocalAppData,
|
|
153
|
+
[Parameter(Mandatory = $false)][AllowNull()][AllowEmptyString()][string]$XdgStateHome
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
$workspace = ConvertTo-DiscoveryCanonicalPath -Path $WorkspaceRoot -Platform $Platform -Label 'Workspace root'
|
|
157
|
+
$userHomePath = ConvertTo-DiscoveryCanonicalPath -Path $UserHome -Platform $Platform -Label 'User home'
|
|
158
|
+
# Platform state locations are fixed policy; profiles cannot redirect them.
|
|
159
|
+
switch ($Platform) {
|
|
160
|
+
'windows' {
|
|
161
|
+
if ([string]::IsNullOrEmpty($LocalAppData)) {
|
|
162
|
+
throw 'AUTHORITY_STATE_ROOT_MISSING: Trusted LocalApplicationData is required on Windows.'
|
|
163
|
+
}
|
|
164
|
+
$base = ConvertTo-DiscoveryCanonicalPath -Path $LocalAppData -Platform $Platform -Label 'LocalApplicationData'
|
|
165
|
+
}
|
|
166
|
+
'macos' {
|
|
167
|
+
$base = @{ Platform = $Platform; Anchor = $userHomePath.Anchor; Parts = @($userHomePath.Parts + @('Library', 'Application Support')) }
|
|
168
|
+
}
|
|
169
|
+
default {
|
|
170
|
+
if (-not [string]::IsNullOrEmpty($XdgStateHome)) {
|
|
171
|
+
$base = ConvertTo-DiscoveryCanonicalPath -Path $XdgStateHome -Platform $Platform -Label 'XDG_STATE_HOME'
|
|
172
|
+
} else {
|
|
173
|
+
$base = @{ Platform = $Platform; Anchor = $userHomePath.Anchor; Parts = @($userHomePath.Parts + @('.local', 'state')) }
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
$root = @{ Platform = $Platform; Anchor = $base.Anchor; Parts = @($base.Parts + $script:AuthorityRootSegments) }
|
|
178
|
+
if (Test-DiscoveryCanonicalPathWithin -Candidate $root -Ancestor $workspace) {
|
|
179
|
+
throw 'AUTHORITY_INSIDE_WORKSPACE: Activation authority must remain outside the workspace.'
|
|
180
|
+
}
|
|
181
|
+
$posixWorkspace = ConvertTo-DiscoveryPosixPath -CanonicalPath $workspace
|
|
182
|
+
# Windows identity uses native separators and invariant lowercase, matching
|
|
183
|
+
# the Python runtime and the TypeScript gateway; POSIX preserves case.
|
|
184
|
+
$identity = $(if ($Platform -eq 'windows') { $posixWorkspace.Replace('/', '\').ToLowerInvariant() } else { $posixWorkspace })
|
|
185
|
+
$digest = Get-DiscoverySha256Hex -Text $identity
|
|
186
|
+
$directory = @{ Platform = $Platform; Anchor = $root.Anchor; Parts = @($root.Parts + @('workspaces', $digest)) }
|
|
187
|
+
return [pscustomobject]@{
|
|
188
|
+
Platform = $Platform
|
|
189
|
+
Workspace = $posixWorkspace
|
|
190
|
+
WorkspaceSha256 = $digest
|
|
191
|
+
Root = ConvertTo-DiscoveryPosixPath -CanonicalPath $root
|
|
192
|
+
Directory = ConvertTo-DiscoveryPosixPath -CanonicalPath $directory
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function Get-DiscoveryTrustedHostEnvironment {
|
|
197
|
+
<#
|
|
198
|
+
.SYNOPSIS
|
|
199
|
+
Returns the trusted host environment (platform, home, state roots) with injectable seams.
|
|
200
|
+
.DESCRIPTION
|
|
201
|
+
Defaults read the running host, never a profile or request value. Tests pass
|
|
202
|
+
every value explicitly so no assertion depends on the machine.
|
|
203
|
+
#>
|
|
204
|
+
[CmdletBinding()]
|
|
205
|
+
[OutputType([hashtable])]
|
|
206
|
+
param(
|
|
207
|
+
[Parameter(Mandatory = $false)][AllowNull()][string]$Platform,
|
|
208
|
+
[Parameter(Mandatory = $false)][AllowNull()][string]$UserHome,
|
|
209
|
+
[Parameter(Mandatory = $false)][AllowNull()][string]$LocalAppData,
|
|
210
|
+
[Parameter(Mandatory = $false)][AllowNull()][string]$XdgStateHome
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
if ([string]::IsNullOrEmpty($Platform)) {
|
|
214
|
+
$Platform = $(if ($IsWindows) { 'windows' } elseif ($IsMacOS) { 'macos' } else { 'linux' })
|
|
215
|
+
}
|
|
216
|
+
if ([string]::IsNullOrEmpty($UserHome)) { $UserHome = [System.Environment]::GetFolderPath('UserProfile') }
|
|
217
|
+
# An omitted [string] parameter arrives as an empty string, never as $null, so the
|
|
218
|
+
# host defaults apply on the empty value as well.
|
|
219
|
+
if ([string]::IsNullOrEmpty($LocalAppData)) { $LocalAppData = [System.Environment]::GetFolderPath('LocalApplicationData') }
|
|
220
|
+
if ([string]::IsNullOrEmpty($XdgStateHome)) { $XdgStateHome = [string]$env:XDG_STATE_HOME }
|
|
221
|
+
return @{ Platform = $Platform; UserHome = $UserHome; LocalAppData = $LocalAppData; XdgStateHome = $XdgStateHome }
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function Get-DiscoveryPartitionName {
|
|
225
|
+
<#
|
|
226
|
+
.SYNOPSIS
|
|
227
|
+
Returns the digest partition name for a correlation identifier.
|
|
228
|
+
.DESCRIPTION
|
|
229
|
+
Correlation identifiers may contain `:` and are case-sensitive, so the
|
|
230
|
+
partition is the SHA-256 of the identifier rather than the identifier itself.
|
|
231
|
+
#>
|
|
232
|
+
[CmdletBinding()]
|
|
233
|
+
[OutputType([string])]
|
|
234
|
+
param([Parameter(Mandatory = $true)][AllowEmptyString()][string]$CorrelationId)
|
|
235
|
+
|
|
236
|
+
if ($CorrelationId -cnotmatch $script:CorrelationIdPattern -or $CorrelationId.Length -gt 256) {
|
|
237
|
+
throw "AUTHORITY_PARTITION_INVALID: Partition identifier is not a valid correlation id: $CorrelationId"
|
|
238
|
+
}
|
|
239
|
+
return Get-DiscoverySha256Hex -Text $CorrelationId
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function Get-DiscoverySessionDirectory {
|
|
243
|
+
<#
|
|
244
|
+
.SYNOPSIS
|
|
245
|
+
Returns `<directory>/sessions/<sha256(sessionId)>`.
|
|
246
|
+
#>
|
|
247
|
+
[CmdletBinding()]
|
|
248
|
+
[OutputType([string])]
|
|
249
|
+
param(
|
|
250
|
+
[Parameter(Mandatory = $true)][pscustomobject]$Location,
|
|
251
|
+
[Parameter(Mandatory = $true)][string]$SessionId
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
return "$($Location.Directory)/sessions/$(Get-DiscoveryPartitionName -CorrelationId $SessionId)"
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function Get-DiscoveryInvocationDirectory {
|
|
258
|
+
<#
|
|
259
|
+
.SYNOPSIS
|
|
260
|
+
Returns `<session>/invocations/<sha256(invocationId)>`.
|
|
261
|
+
#>
|
|
262
|
+
[CmdletBinding()]
|
|
263
|
+
[OutputType([string])]
|
|
264
|
+
param(
|
|
265
|
+
[Parameter(Mandatory = $true)][pscustomobject]$Location,
|
|
266
|
+
[Parameter(Mandatory = $true)][string]$SessionId,
|
|
267
|
+
[Parameter(Mandatory = $true)][string]$InvocationId
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
$session = Get-DiscoverySessionDirectory -Location $Location -SessionId $SessionId
|
|
271
|
+
return "$session/invocations/$(Get-DiscoveryPartitionName -CorrelationId $InvocationId)"
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function Get-DiscoveryActivationRecordPath {
|
|
275
|
+
<#
|
|
276
|
+
.SYNOPSIS
|
|
277
|
+
Returns the path of the fixed activation record for a location.
|
|
278
|
+
#>
|
|
279
|
+
[CmdletBinding()]
|
|
280
|
+
[OutputType([string])]
|
|
281
|
+
param([Parameter(Mandatory = $true)][pscustomobject]$Location)
|
|
282
|
+
|
|
283
|
+
return "$($Location.Directory)/$($script:ActivationRecordFile)"
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function Test-DiscoveryProtectedPath {
|
|
287
|
+
<#
|
|
288
|
+
.SYNOPSIS
|
|
289
|
+
True only when Candidate lies inside the protected directory and outside the workspace.
|
|
290
|
+
.DESCRIPTION
|
|
291
|
+
Analyst-authored reports, invocation mirrors and workspace copies of the
|
|
292
|
+
registry are editable; a path anywhere other than the protected directory
|
|
293
|
+
can never be read as native authority.
|
|
294
|
+
#>
|
|
295
|
+
[CmdletBinding()]
|
|
296
|
+
[OutputType([bool])]
|
|
297
|
+
param(
|
|
298
|
+
[Parameter(Mandatory = $true)][pscustomobject]$Location,
|
|
299
|
+
[Parameter(Mandatory = $true)][string]$Candidate
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
$target = ConvertTo-DiscoveryCanonicalPath -Path $Candidate -Platform $Location.Platform -Label 'Candidate path'
|
|
303
|
+
$workspace = ConvertTo-DiscoveryCanonicalPath -Path $Location.Workspace -Platform $Location.Platform -Label 'Workspace root'
|
|
304
|
+
$directory = ConvertTo-DiscoveryCanonicalPath -Path $Location.Directory -Platform $Location.Platform -Label 'Authority directory'
|
|
305
|
+
if (Test-DiscoveryCanonicalPathWithin -Candidate $target -Ancestor $workspace) { return $false }
|
|
306
|
+
return Test-DiscoveryCanonicalPathWithin -Candidate $target -Ancestor $directory
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function Test-DiscoveryRelativePath {
|
|
310
|
+
<#
|
|
311
|
+
.SYNOPSIS
|
|
312
|
+
True when Path is a normalized workspace-relative path (no root, no `.`/`..`, no reserved device).
|
|
313
|
+
#>
|
|
314
|
+
[CmdletBinding()]
|
|
315
|
+
[OutputType([bool])]
|
|
316
|
+
param([Parameter(Mandatory = $true)][AllowEmptyString()][string]$Path)
|
|
317
|
+
|
|
318
|
+
if ($Path -cnotmatch $script:RelativePathPattern) { return $false }
|
|
319
|
+
# Every segment, with and without its extension, must avoid Windows device names.
|
|
320
|
+
foreach ($segment in $Path.Split('/')) {
|
|
321
|
+
$stem = $segment.Split('.')[0]
|
|
322
|
+
if ($segment -cmatch $script:ReservedDevicePattern -or $stem -cmatch $script:ReservedDevicePattern) { return $false }
|
|
323
|
+
}
|
|
324
|
+
return $true
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function Test-DiscoveryDigestText {
|
|
328
|
+
<#
|
|
329
|
+
.SYNOPSIS
|
|
330
|
+
True when Value is a lowercase 64-hex SHA-256 string.
|
|
331
|
+
#>
|
|
332
|
+
[CmdletBinding()]
|
|
333
|
+
[OutputType([bool])]
|
|
334
|
+
param([Parameter(Mandatory = $false)][AllowNull()][object]$Value)
|
|
335
|
+
|
|
336
|
+
return ($Value -is [string]) -and ($Value -cmatch $script:Sha256Pattern)
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function ConvertFrom-DiscoveryActivationRecord {
|
|
340
|
+
<#
|
|
341
|
+
.SYNOPSIS
|
|
342
|
+
Decodes protected activation text, rejecting malformed data and foreign workspaces.
|
|
343
|
+
.DESCRIPTION
|
|
344
|
+
Field rules mirror `ActivationRecord.__post_init__` and the TypeScript
|
|
345
|
+
decoder: exactly the eight keys, schema version 2.0.0, action activate or
|
|
346
|
+
deactivate, a positive integer generation whose textual literal is a plain
|
|
347
|
+
integer, lowercase digests, a workspace-relative profile path, and
|
|
348
|
+
all-or-nothing profile/locator bindings. A workspace digest that differs from
|
|
349
|
+
WorkspaceSha256 is `ACTIVATION_WORKSPACE`; every other defect is
|
|
350
|
+
`ACTIVATION_INVALID`.
|
|
351
|
+
.OUTPUTS
|
|
352
|
+
[pscustomobject] The validated record with the eight snake_case members.
|
|
353
|
+
#>
|
|
354
|
+
[CmdletBinding()]
|
|
355
|
+
[OutputType([pscustomobject])]
|
|
356
|
+
param(
|
|
357
|
+
[Parameter(Mandatory = $true)][AllowEmptyString()][string]$Text,
|
|
358
|
+
[Parameter(Mandatory = $true)][string]$WorkspaceSha256
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
if ([System.Text.Encoding]::UTF8.GetByteCount($Text) -gt $script:MaxRecordBytes) {
|
|
362
|
+
throw 'ACTIVATION_INVALID: Protected activation record exceeds the size limit.'
|
|
363
|
+
}
|
|
364
|
+
try {
|
|
365
|
+
$parsed = ConvertFrom-Json -InputObject $Text -Depth 4 -NoEnumerate
|
|
366
|
+
} catch {
|
|
367
|
+
throw 'ACTIVATION_INVALID: Protected activation record is not JSON.'
|
|
368
|
+
}
|
|
369
|
+
if ($null -eq $parsed -or $parsed -isnot [System.Management.Automation.PSCustomObject]) {
|
|
370
|
+
throw 'ACTIVATION_INVALID: Protected activation record is malformed.'
|
|
371
|
+
}
|
|
372
|
+
[string[]]$keys = @($parsed.PSObject.Properties.Name)
|
|
373
|
+
[Array]::Sort($keys, [System.StringComparer]::Ordinal)
|
|
374
|
+
if (($keys -join ',') -ne ($script:ActivationRecordKeys -join ',')) {
|
|
375
|
+
throw 'ACTIVATION_INVALID: Protected activation record is malformed.'
|
|
376
|
+
}
|
|
377
|
+
$generation = $parsed.generation
|
|
378
|
+
# ConvertFrom-Json turns `1.0` into a double, so the textual literal is checked
|
|
379
|
+
# as well to keep parity with Python's exact-int rule.
|
|
380
|
+
$integerLiteral = [regex]::IsMatch($Text, '"generation"\s*:\s*[1-9][0-9]*\s*[,}]')
|
|
381
|
+
if ($parsed.schema_version -ne $script:ActivationSchemaVersion -or
|
|
382
|
+
($parsed.action -ne 'activate' -and $parsed.action -ne 'deactivate') -or
|
|
383
|
+
-not ($generation -is [int32] -or $generation -is [int64]) -or $generation -lt 1 -or -not $integerLiteral) {
|
|
384
|
+
throw 'ACTIVATION_INVALID: Invalid activation version, action or generation.'
|
|
385
|
+
}
|
|
386
|
+
$digests = @($parsed.workspace_sha256, $parsed.profile_sha256, $parsed.locator_sha256, $parsed.previous_sha256)
|
|
387
|
+
foreach ($digest in $digests) {
|
|
388
|
+
if ($null -ne $digest -and -not (Test-DiscoveryDigestText -Value $digest)) {
|
|
389
|
+
throw 'ACTIVATION_INVALID: Activation digests must be lowercase SHA-256.'
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (-not (Test-DiscoveryDigestText -Value $parsed.workspace_sha256)) {
|
|
393
|
+
throw 'ACTIVATION_INVALID: Activation requires a canonical workspace digest.'
|
|
394
|
+
}
|
|
395
|
+
if ($null -ne $parsed.profile_path) {
|
|
396
|
+
if ($parsed.profile_path -isnot [string]) {
|
|
397
|
+
throw 'ACTIVATION_INVALID: Activation profile path must be a string or null.'
|
|
398
|
+
}
|
|
399
|
+
if (-not (Test-DiscoveryRelativePath -Path $parsed.profile_path)) {
|
|
400
|
+
throw 'ACTIVATION_INVALID: Activation profile path is not workspace-relative.'
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
# Bindings are all-or-nothing, and activation always carries them.
|
|
404
|
+
$linked = @($parsed.profile_path, $parsed.profile_sha256, $parsed.locator_sha256)
|
|
405
|
+
$incomplete = @($linked | Where-Object { $null -eq $_ }).Count -gt 0
|
|
406
|
+
$anyBound = @($linked | Where-Object { $null -ne $_ }).Count -gt 0
|
|
407
|
+
if ($incomplete -and ($parsed.action -eq 'activate' -or $anyBound)) {
|
|
408
|
+
throw 'ACTIVATION_INVALID: Activation profile and locator bindings must be complete.'
|
|
409
|
+
}
|
|
410
|
+
if ($parsed.workspace_sha256 -ne $WorkspaceSha256) {
|
|
411
|
+
throw 'ACTIVATION_WORKSPACE: Protected activation belongs to another workspace.'
|
|
412
|
+
}
|
|
413
|
+
return [pscustomobject]@{
|
|
414
|
+
action = [string]$parsed.action
|
|
415
|
+
generation = [int64]$generation
|
|
416
|
+
locator_sha256 = $parsed.locator_sha256
|
|
417
|
+
previous_sha256 = $parsed.previous_sha256
|
|
418
|
+
profile_path = $parsed.profile_path
|
|
419
|
+
profile_sha256 = $parsed.profile_sha256
|
|
420
|
+
schema_version = $script:ActivationSchemaVersion
|
|
421
|
+
workspace_sha256 = [string]$parsed.workspace_sha256
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function Read-DiscoveryActivationState {
|
|
426
|
+
<#
|
|
427
|
+
.SYNOPSIS
|
|
428
|
+
Reads the protected activation record and classifies the workspace state.
|
|
429
|
+
.DESCRIPTION
|
|
430
|
+
Status is `missing` when no record exists, `invalid` when the text fails the
|
|
431
|
+
decoder (Reason carries the typed message), `inactive` when the record is a
|
|
432
|
+
deactivate tombstone, and `active` otherwise. The read goes through the
|
|
433
|
+
ReadText seam, which returns the file text or $null; the default reads the
|
|
434
|
+
path with UTF-8 and returns $null for a missing file.
|
|
435
|
+
.OUTPUTS
|
|
436
|
+
[pscustomobject] Status, Record, Reason, Path.
|
|
437
|
+
#>
|
|
438
|
+
[CmdletBinding()]
|
|
439
|
+
[OutputType([pscustomobject])]
|
|
440
|
+
param(
|
|
441
|
+
[Parameter(Mandatory = $true)][pscustomobject]$Location,
|
|
442
|
+
[Parameter(Mandatory = $false)][scriptblock]$ReadText = {
|
|
443
|
+
param([string]$Path)
|
|
444
|
+
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { return $null }
|
|
445
|
+
return [System.IO.File]::ReadAllText($Path, [System.Text.Encoding]::UTF8)
|
|
446
|
+
}
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
$path = Get-DiscoveryActivationRecordPath -Location $Location
|
|
450
|
+
$text = & $ReadText $path
|
|
451
|
+
if ($null -eq $text) {
|
|
452
|
+
return [pscustomobject]@{ Status = 'missing'; Record = $null; Reason = 'no protected activation record exists'; Path = $path }
|
|
453
|
+
}
|
|
454
|
+
try {
|
|
455
|
+
$record = ConvertFrom-DiscoveryActivationRecord -Text ([string]$text) -WorkspaceSha256 $Location.WorkspaceSha256
|
|
456
|
+
} catch {
|
|
457
|
+
return [pscustomobject]@{ Status = 'invalid'; Record = $null; Reason = $_.Exception.Message; Path = $path }
|
|
458
|
+
}
|
|
459
|
+
$status = $(if ($record.action -eq 'deactivate') { 'inactive' } else { 'active' })
|
|
460
|
+
return [pscustomobject]@{ Status = $status; Record = $record; Reason = $null; Path = $path }
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function Invoke-DiscoveryStoreSeam {
|
|
464
|
+
<#
|
|
465
|
+
.SYNOPSIS
|
|
466
|
+
Invokes one injected store seam (ReadText, ListEntries, WriteText, ReplaceText, EnsureDirectory) with its arguments.
|
|
467
|
+
.DESCRIPTION
|
|
468
|
+
Hooks hand their store as a hashtable of script blocks; this is the single
|
|
469
|
+
place that invokes one, so every store call is a typed script-block seam and
|
|
470
|
+
never a dynamic command position.
|
|
471
|
+
#>
|
|
472
|
+
[CmdletBinding()]
|
|
473
|
+
[OutputType([object])]
|
|
474
|
+
param(
|
|
475
|
+
[Parameter(Mandatory = $true)][scriptblock]$Seam,
|
|
476
|
+
[Parameter(Mandatory = $false)][AllowEmptyCollection()][object[]]$Arguments = @()
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
return & $Seam @Arguments
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
Export-ModuleMember -Function `
|
|
483
|
+
Invoke-DiscoveryStoreSeam, `
|
|
484
|
+
Resolve-DiscoveryAuthorityLocation, `
|
|
485
|
+
Get-DiscoveryTrustedHostEnvironment, `
|
|
486
|
+
Get-DiscoveryPartitionName, `
|
|
487
|
+
Get-DiscoverySessionDirectory, `
|
|
488
|
+
Get-DiscoveryInvocationDirectory, `
|
|
489
|
+
Get-DiscoveryActivationRecordPath, `
|
|
490
|
+
Test-DiscoveryProtectedPath, `
|
|
491
|
+
Test-DiscoveryRelativePath, `
|
|
492
|
+
ConvertFrom-DiscoveryActivationRecord, `
|
|
493
|
+
Read-DiscoveryActivationState
|