@danmoisan/drm-copilot-mcp 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/mcp-server.js +93 -20
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agents/orchestrator.md +6 -2
- package/resources/claude-customizations/.claude/agents/pr-author.md +78 -0
- package/resources/claude-customizations/.claude/agents/task-researcher.md +10 -6
- package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +68 -10
- package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +28 -1
- package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +40 -14
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +210 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +153 -10
- package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +136 -0
- package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +22 -6
- package/resources/claude-customizations/.claude/settings.json +22 -0
- package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +0 -1
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +76 -2
- package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +5 -3
- package/resources/claude-customizations/.claude-variants/csharp-legacy/agents/csharp-typed-engineer.md +69 -0
- package/resources/claude-customizations/.claude-variants/csharp-legacy/rules/csharp.md +96 -0
- package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +77 -0
- package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +64 -0
- package/resources/claude-customizations/pack-manifests/core.json +65 -0
- package/resources/claude-customizations/pack-manifests/csharp-legacy.json +11 -0
- package/resources/claude-customizations/pack-manifests/csharp-modern.json +12 -0
- package/resources/claude-customizations/pack-manifests/powershell.json +14 -0
- package/resources/claude-customizations/pack-manifests/python.json +14 -0
- package/resources/claude-customizations/pack-manifests/typescript.json +9 -0
- package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +30 -13
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +113 -10
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +33 -8
- package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +12 -7
- package/resources/codex-and-agents-customizations/.codex/config.toml +65 -6
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +303 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +300 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +148 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-pr-author-skill.ps1 +336 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +83 -17
- package/resources/config/orchestration-routing.json +2 -10
- package/resources/customizations/.github/agents/pr-author.agent.md +25 -0
- package/resources/customizations/.github/agents/task-researcher.agent.md +4 -4
- package/resources/customizations/.github/prompts/fillout-prd-feature.prompt.md +1 -1
- package/resources/customizations/.github/prompts/research-issue.prompt.md +4 -3
- package/resources/scripts/dev_tools/push_down_claude_customizations.py +253 -224
- package/resources/scripts/dev_tools/push_down_claude_filesystem.py +472 -0
- package/resources/scripts/dev_tools/push_down_claude_pack_selection.py +401 -0
- package/resources/scripts/dev_tools/validate_orchestrator_state.py +106 -29
- package/resources/scripts/dev_tools/validate_policy_audit_artifact.py +24 -0
- package/resources/templates/push_down_claude_customizations.py +128 -304
package/out/mcp-server.js
CHANGED
|
@@ -15890,7 +15890,24 @@ var REPO_AUTOMATION_TOOL_DEFINITIONS = [
|
|
|
15890
15890
|
inputSchema: {
|
|
15891
15891
|
type: "object",
|
|
15892
15892
|
properties: {
|
|
15893
|
-
workspace_root: workspaceRootProperty
|
|
15893
|
+
workspace_root: workspaceRootProperty,
|
|
15894
|
+
packs: {
|
|
15895
|
+
type: "array",
|
|
15896
|
+
items: {
|
|
15897
|
+
type: "string"
|
|
15898
|
+
},
|
|
15899
|
+
description: "Optional language pack names to publish (for example 'core', 'typescript'). When omitted, the full tree is published. 'core' is always included."
|
|
15900
|
+
},
|
|
15901
|
+
csharp_variant: {
|
|
15902
|
+
type: "string",
|
|
15903
|
+
enum: ["modern", "legacy"],
|
|
15904
|
+
description: "Optional C# toolchain variant to source ('modern' default or 'legacy')."
|
|
15905
|
+
},
|
|
15906
|
+
memory_mode: {
|
|
15907
|
+
type: "string",
|
|
15908
|
+
enum: ["overwrite", "merge", "skip"],
|
|
15909
|
+
description: "Optional agent-memory handling mode: 'overwrite' (default), 'merge', or 'skip'."
|
|
15910
|
+
}
|
|
15894
15911
|
},
|
|
15895
15912
|
additionalProperties: false
|
|
15896
15913
|
}
|
|
@@ -16203,6 +16220,56 @@ var REPO_AUTOMATION_TOOLS = [
|
|
|
16203
16220
|
"validate_orchestration_artifacts"
|
|
16204
16221
|
];
|
|
16205
16222
|
|
|
16223
|
+
// ../../extensions/drm-copilot/src/mcp-tool-inputs-push-down.ts
|
|
16224
|
+
function resolveClaudePacksField(rawValue) {
|
|
16225
|
+
if (rawValue === void 0) {
|
|
16226
|
+
return void 0;
|
|
16227
|
+
}
|
|
16228
|
+
if (!Array.isArray(rawValue)) {
|
|
16229
|
+
throw new Error("Field 'packs' must be an array of strings when provided.");
|
|
16230
|
+
}
|
|
16231
|
+
return rawValue.map(
|
|
16232
|
+
(entry, index) => normalizeRequiredText(entry, `packs[${index}]`)
|
|
16233
|
+
);
|
|
16234
|
+
}
|
|
16235
|
+
function resolveCsharpVariantField(rawValue) {
|
|
16236
|
+
if (rawValue === void 0) {
|
|
16237
|
+
return void 0;
|
|
16238
|
+
}
|
|
16239
|
+
const variant = normalizeRequiredText(rawValue, "csharp_variant");
|
|
16240
|
+
if (variant !== "modern" && variant !== "legacy") {
|
|
16241
|
+
throw new Error("Field 'csharp_variant' must be 'modern' or 'legacy'.");
|
|
16242
|
+
}
|
|
16243
|
+
return variant;
|
|
16244
|
+
}
|
|
16245
|
+
function resolveMemoryModeField(rawValue) {
|
|
16246
|
+
if (rawValue === void 0) {
|
|
16247
|
+
return void 0;
|
|
16248
|
+
}
|
|
16249
|
+
const mode = normalizeRequiredText(rawValue, "memory_mode");
|
|
16250
|
+
if (mode !== "overwrite" && mode !== "merge" && mode !== "skip") {
|
|
16251
|
+
throw new Error(
|
|
16252
|
+
"Field 'memory_mode' must be 'overwrite', 'merge', or 'skip'."
|
|
16253
|
+
);
|
|
16254
|
+
}
|
|
16255
|
+
return mode;
|
|
16256
|
+
}
|
|
16257
|
+
function resolvePushDownClaudeCustomizationsToolInput(rawInput, fallbackWorkspaceRoot) {
|
|
16258
|
+
const args = asToolArgumentObject(rawInput);
|
|
16259
|
+
const packs = resolveClaudePacksField(args["packs"]);
|
|
16260
|
+
const csharpVariant = resolveCsharpVariantField(args["csharp_variant"]);
|
|
16261
|
+
const memoryMode = resolveMemoryModeField(args["memory_mode"]);
|
|
16262
|
+
return {
|
|
16263
|
+
workspaceRoot: normalizeWorkspaceRoot(
|
|
16264
|
+
args["workspace_root"],
|
|
16265
|
+
fallbackWorkspaceRoot
|
|
16266
|
+
),
|
|
16267
|
+
...packs === void 0 ? {} : { packs },
|
|
16268
|
+
...csharpVariant === void 0 ? {} : { csharpVariant },
|
|
16269
|
+
...memoryMode === void 0 ? {} : { memoryMode }
|
|
16270
|
+
};
|
|
16271
|
+
}
|
|
16272
|
+
|
|
16206
16273
|
// ../../extensions/drm-copilot/src/mcp-tool-inputs.ts
|
|
16207
16274
|
function asToolArgumentObject(rawInput) {
|
|
16208
16275
|
if (rawInput === void 0) {
|
|
@@ -16339,15 +16406,6 @@ function resolvePushDownCodexAndAgentsCustomizationsToolInput(rawInput, fallback
|
|
|
16339
16406
|
)
|
|
16340
16407
|
};
|
|
16341
16408
|
}
|
|
16342
|
-
function resolvePushDownClaudeCustomizationsToolInput(rawInput, fallbackWorkspaceRoot) {
|
|
16343
|
-
const args = asToolArgumentObject(rawInput);
|
|
16344
|
-
return {
|
|
16345
|
-
workspaceRoot: normalizeWorkspaceRoot(
|
|
16346
|
-
args["workspace_root"],
|
|
16347
|
-
fallbackWorkspaceRoot
|
|
16348
|
-
)
|
|
16349
|
-
};
|
|
16350
|
-
}
|
|
16351
16409
|
function resolveNewPotentialBugEntryToolInput(rawInput, fallbackWorkspaceRoot) {
|
|
16352
16410
|
const args = asToolArgumentObject(rawInput);
|
|
16353
16411
|
return {
|
|
@@ -16853,6 +16911,30 @@ function buildValidateOrchestrationArtifactsArgs(input) {
|
|
|
16853
16911
|
return args;
|
|
16854
16912
|
}
|
|
16855
16913
|
|
|
16914
|
+
// ../../extensions/drm-copilot/src/repo-automation-service-push-down.ts
|
|
16915
|
+
function buildPushDownClaudeCustomizationsOptions(input) {
|
|
16916
|
+
const args = ["--destination", input.workspaceRoot];
|
|
16917
|
+
if (input.packs !== void 0 && input.packs.length > 0) {
|
|
16918
|
+
args.push("--packs", input.packs.join(","));
|
|
16919
|
+
}
|
|
16920
|
+
if (input.csharpVariant !== void 0) {
|
|
16921
|
+
args.push("--csharp-variant", input.csharpVariant);
|
|
16922
|
+
}
|
|
16923
|
+
if (input.memoryMode !== void 0) {
|
|
16924
|
+
args.push("--memory-mode", input.memoryMode);
|
|
16925
|
+
}
|
|
16926
|
+
return {
|
|
16927
|
+
tool: "push_down_claude_customizations",
|
|
16928
|
+
runtimeKind: "python",
|
|
16929
|
+
bundledRelativePath: "resources/templates/push_down_claude_customizations.py",
|
|
16930
|
+
workspaceRoot: input.workspaceRoot,
|
|
16931
|
+
invocationId: input.invocationId ?? "push_down_claude_customizations",
|
|
16932
|
+
args,
|
|
16933
|
+
summary: "Pushed bundled Claude Code customizations into the destination workspace.",
|
|
16934
|
+
stdoutArtifactPattern: /Wrote push-down summary artifact to:\s*(.+)/i
|
|
16935
|
+
};
|
|
16936
|
+
}
|
|
16937
|
+
|
|
16856
16938
|
// ../../extensions/drm-copilot/src/repo-automation-service-workflows.ts
|
|
16857
16939
|
var path5 = __toESM(require("node:path"));
|
|
16858
16940
|
|
|
@@ -17091,16 +17173,7 @@ var DefaultRepoAutomationService = class {
|
|
|
17091
17173
|
});
|
|
17092
17174
|
}
|
|
17093
17175
|
async pushDownClaudeCustomizations(input) {
|
|
17094
|
-
return this.executeScript(
|
|
17095
|
-
tool: "push_down_claude_customizations",
|
|
17096
|
-
runtimeKind: "python",
|
|
17097
|
-
bundledRelativePath: "resources/templates/push_down_claude_customizations.py",
|
|
17098
|
-
workspaceRoot: input.workspaceRoot,
|
|
17099
|
-
invocationId: input.invocationId ?? "push_down_claude_customizations",
|
|
17100
|
-
args: ["--destination", input.workspaceRoot],
|
|
17101
|
-
summary: "Pushed bundled Claude Code customizations into the destination workspace.",
|
|
17102
|
-
stdoutArtifactPattern: /Wrote push-down summary artifact to:\s*(.+)/i
|
|
17103
|
-
});
|
|
17176
|
+
return this.executeScript(buildPushDownClaudeCustomizationsOptions(input));
|
|
17104
17177
|
}
|
|
17105
17178
|
async newPotentialBugEntry(input) {
|
|
17106
17179
|
return this.executeScript({
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: orchestrator
|
|
3
3
|
description: Deterministic repository orchestrator that estimates change budget, selects small or large workflow path, delegates to specialist subagents, persists checkpoint state, and enforces completion gates proactively.
|
|
4
4
|
tools:
|
|
5
|
-
- "Agent(atomic-planner,atomic-executor,feature-review,task-researcher,prd-feature,staged-review,epic-review,status-updater,python-typed-engineer,powershell-typed-engineer,csharp-typed-engineer,typescript-engineer)"
|
|
5
|
+
- "Agent(atomic-planner,atomic-executor,feature-review,task-researcher,prd-feature,staged-review,epic-review,status-updater,pr-author,python-typed-engineer,powershell-typed-engineer,csharp-typed-engineer,typescript-engineer)"
|
|
6
6
|
- Read
|
|
7
7
|
- Grep
|
|
8
8
|
- Glob
|
|
@@ -69,10 +69,14 @@ Delegate exclusively through configured subagents:
|
|
|
69
69
|
- `atomic-planner` — generates phased implementation plans (planning only)
|
|
70
70
|
- `atomic-executor` — executes approved plans task-by-task (execution only)
|
|
71
71
|
- `feature-review` — produces policy, code, and feature audit artifacts
|
|
72
|
-
- `task-researcher` — performs deep research and writes findings to `
|
|
72
|
+
- `task-researcher` — performs deep research and writes findings to the research path the orchestrator resolves before delegating: `docs/features/<feature>/research/` when an active `feature-folder` is in scope in `orchestrator-state.json`, otherwise `docs/research/` for one-off research. The orchestrator passes the resolved path in the delegation prompt.
|
|
73
73
|
|
|
74
74
|
For required delegated steps, delegation is mandatory. If a handoff cannot be started, resumed, or completed, stop execution and record blocked state. Do not perform the step locally.
|
|
75
75
|
|
|
76
|
+
## PR Creation Delegation
|
|
77
|
+
|
|
78
|
+
PR creation and PR body edits must be delegated to `Agent(pr-author)`. The orchestrator must not call `gh pr create` or `gh pr edit --body*` directly from the main thread; those commands are blocked by the `enforce-pr-author-skill.ps1` PreToolUse hook unless a valid authorization sentinel issued by the `pr-author` agent is present. The orchestrator first produces the PR-context artifact via `mcp__drm-copilot__collect_pr_context`, then delegates to `Agent(pr-author)`, which authors the PR body via the `pr-author` skill, writes and deletes the authorization sentinel around the `gh` command, and reports the resulting PR URL or PR number.
|
|
79
|
+
|
|
76
80
|
## Checkpoint Persistence
|
|
77
81
|
|
|
78
82
|
Update `artifacts/orchestration/orchestrator-state.json` after every completed step with:
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pr-author
|
|
3
|
+
description: Project-scoped agent that runs the pr-author skill to produce a GitHub-ready PR body from the canonical PR-context bundle, then opens or updates the pull request. Sole authorized caller of gh pr create and gh pr edit --body*. Writes a short-lived authorization sentinel immediately before each gh command and deletes it afterward.
|
|
4
|
+
model: sonnet
|
|
5
|
+
skills:
|
|
6
|
+
- pr-author
|
|
7
|
+
memory: project
|
|
8
|
+
tools:
|
|
9
|
+
- Read
|
|
10
|
+
- "Bash(git log *)"
|
|
11
|
+
- "Bash(git rev-parse *)"
|
|
12
|
+
- "Bash(gh pr create *)"
|
|
13
|
+
- "Bash(gh pr edit *)"
|
|
14
|
+
- "Write(/artifacts/**)"
|
|
15
|
+
hooks:
|
|
16
|
+
SubagentStop:
|
|
17
|
+
- matcher: "pr-author"
|
|
18
|
+
hooks:
|
|
19
|
+
- type: command
|
|
20
|
+
command: pwsh -NoProfile -File .claude/hooks/validate-pr-author-output.ps1
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# PR Author Agent
|
|
24
|
+
|
|
25
|
+
You are the dedicated pull-request authoring agent. Your sole responsibility is to consume the
|
|
26
|
+
canonical PR-context bundle, produce the PR body using the `pr-author` skill, and open or update the
|
|
27
|
+
pull request. You are the only agent authorized to run `gh pr create` and `gh pr edit --body*`. The
|
|
28
|
+
orchestrator and all other agents are blocked from these commands by the
|
|
29
|
+
`enforce-pr-author-skill.ps1` PreToolUse hook.
|
|
30
|
+
|
|
31
|
+
## Skill
|
|
32
|
+
|
|
33
|
+
Apply the `pr-author` skill (`.claude/skills/pr-author/SKILL.md`) as the canonical workflow for
|
|
34
|
+
authoring the PR body. The skill produces the PR body text from the PR-context bundle
|
|
35
|
+
(`artifacts/pr_context.summary.txt` and `artifacts/pr_context.appendix.txt`) and its enumerated
|
|
36
|
+
additional context files. The skill itself only authors body text; opening and editing the pull
|
|
37
|
+
request via `gh pr create` / `gh pr edit` is this agent's responsibility, not the skill's.
|
|
38
|
+
|
|
39
|
+
## Authorization Sentinel Write/Delete Protocol
|
|
40
|
+
|
|
41
|
+
Before any `gh pr create` or `gh pr edit --body*` command, you MUST perform these steps in order:
|
|
42
|
+
|
|
43
|
+
1. Run `git rev-parse HEAD` to obtain the current `head_sha`.
|
|
44
|
+
2. Write `artifacts/pr_author_authorization.json` with exactly these fields:
|
|
45
|
+
- `issued_by`: `"pr-author"`
|
|
46
|
+
- `issued_at`: the current time as a UTC ISO-8601 timestamp (for example `2026-06-24T16:00:00Z`)
|
|
47
|
+
- `head_sha`: the value from step 1
|
|
48
|
+
- `ttl_seconds`: `120`
|
|
49
|
+
3. Issue the `gh pr create` or `gh pr edit --body-file ...` command immediately, within the 120-second
|
|
50
|
+
TTL. The PreToolUse hook verifies that the sentinel is present, that `issued_by` is exactly
|
|
51
|
+
`pr-author`, and that the sentinel has not expired before allowing the command.
|
|
52
|
+
4. Delete `artifacts/pr_author_authorization.json` after the `gh` command completes, on both success
|
|
53
|
+
and failure. The TTL also expires abandoned sentinels, but explicit deletion is required.
|
|
54
|
+
|
|
55
|
+
The sentinel filename is exactly `artifacts/pr_author_authorization.json`. The PR body must be passed
|
|
56
|
+
via `--body-file`; inline `--body` is blocked by the hook (Case A).
|
|
57
|
+
|
|
58
|
+
## Final Output Requirement
|
|
59
|
+
|
|
60
|
+
After opening or updating the pull request, report the resulting PR URL
|
|
61
|
+
(`https://github.com/<owner>/<repo>/pull/<n>`) or the PR number (`PR #<n>`) in your final output. The
|
|
62
|
+
`validate-pr-author-output.ps1` SubagentStop hook blocks completion when the final output contains no
|
|
63
|
+
PR URL or PR number.
|
|
64
|
+
|
|
65
|
+
## Enforcement Strength (Honest Disclosure)
|
|
66
|
+
|
|
67
|
+
The authorization sentinel is a **policy guardrail, not a cryptographic or security control.** Any
|
|
68
|
+
actor with `Write(/artifacts/**)` access can forge `artifacts/pr_author_authorization.json`, because
|
|
69
|
+
all agents share the same filesystem and the runtime exposes no native agent-identity signal at Bash
|
|
70
|
+
PreToolUse time. The mechanism prevents accidental bypass (such as the PR #228 pattern where the
|
|
71
|
+
orchestrator wrote the body file and called `gh pr create` directly) and requires a deliberate,
|
|
72
|
+
documented act to circumvent. It is not tamper-proof and is not a security boundary.
|
|
73
|
+
|
|
74
|
+
## Standing Rules
|
|
75
|
+
|
|
76
|
+
Repository tone is defined in `CLAUDE.md` and `.claude/rules/tonality.md`. Reference only files
|
|
77
|
+
listed under "Additional context files" in the PR-context bundle; do not cite or summarize files
|
|
78
|
+
outside that enumeration. Do not invent issue or PR numbers.
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: task-researcher
|
|
3
|
-
description: Research specialist that performs deep investigation and writes structured findings
|
|
3
|
+
description: Research specialist that performs deep investigation and writes structured findings to the orchestrator-supplied research path under docs/features/<feature>/research/ (feature-associated) or docs/research/ (one-off).
|
|
4
4
|
model: sonnet
|
|
5
5
|
tools:
|
|
6
6
|
- Read
|
|
7
7
|
- Grep
|
|
8
8
|
- Glob
|
|
9
9
|
- WebFetch
|
|
10
|
-
- "Write(/
|
|
10
|
+
- "Write(/docs/features/**/research/**)"
|
|
11
|
+
- "Write(/docs/research/**)"
|
|
11
12
|
- evidence-and-timestamp-conventions
|
|
12
13
|
memory: project
|
|
13
14
|
hooks:
|
|
@@ -20,13 +21,16 @@ hooks:
|
|
|
20
21
|
|
|
21
22
|
# Task Researcher Agent
|
|
22
23
|
|
|
23
|
-
You are a research-only specialist. You perform deep analysis for task planning and write structured research notes. You do not make changes to source code, configurations, or project files outside
|
|
24
|
+
You are a research-only specialist. You perform deep analysis for task planning and write structured research notes. You do not make changes to source code, configurations, or project files outside the research root the orchestrator supplies.
|
|
24
25
|
|
|
25
26
|
## Output Location
|
|
26
27
|
|
|
27
|
-
Write
|
|
28
|
+
Write each research artifact to the research path the orchestrator supplies in the delegation prompt. There are two tracked research roots:
|
|
28
29
|
|
|
29
|
-
- `
|
|
30
|
+
- Feature-associated research: `docs/features/<feature>/research/<timestamp>-<short-name>-research.md` (for example `docs/features/active/<feature>/research/<timestamp>-<short-name>-research.md`).
|
|
31
|
+
- One-off research not tied to a feature: `docs/research/<timestamp>-<short-name>-research.md`.
|
|
32
|
+
|
|
33
|
+
The orchestrator resolves which root to use from whether an active feature folder is in scope and passes the exact path in the delegation prompt; do not infer the feature folder independently. The filename convention `<timestamp>-<short-name>-research.md` is unchanged.
|
|
30
34
|
|
|
31
35
|
## Core Principles
|
|
32
36
|
|
|
@@ -67,7 +71,7 @@ Write all research artifacts to `artifacts/research/` using the filename convent
|
|
|
67
71
|
|
|
68
72
|
## Constraints
|
|
69
73
|
|
|
70
|
-
- Write only to `
|
|
74
|
+
- Write only to the orchestrator-supplied research path under `docs/features/<feature>/research/` (feature-associated) or `docs/research/` (one-off). Do not modify source code or configurations.
|
|
71
75
|
- Ground all findings in verified evidence.
|
|
72
76
|
- Keep discussion of non-selected approaches brief.
|
|
73
77
|
- Do not claim nested worker delegation.
|
|
@@ -134,6 +134,53 @@ function Get-OutOfOrderPair {
|
|
|
134
134
|
return $null
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
function Test-StepHasPrefix {
|
|
138
|
+
[CmdletBinding()]
|
|
139
|
+
[OutputType([bool])]
|
|
140
|
+
param(
|
|
141
|
+
[Parameter(Mandatory)]
|
|
142
|
+
[string] $StepEntry,
|
|
143
|
+
|
|
144
|
+
[Parameter(Mandatory)]
|
|
145
|
+
[string] $Prefix
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
return $StepEntry -eq $Prefix -or $StepEntry.StartsWith("$Prefix" + '_') -or $StepEntry.StartsWith("$Prefix.") -or $StepEntry.StartsWith("$Prefix-")
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function Get-MissingPrerequisiteForAdvancedStep {
|
|
152
|
+
[CmdletBinding()]
|
|
153
|
+
param(
|
|
154
|
+
[Parameter(Mandatory)]
|
|
155
|
+
[AllowEmptyCollection()]
|
|
156
|
+
[string[]] $CompletedSteps
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
$hasPromotion = $false
|
|
160
|
+
$hasPlanning = $false
|
|
161
|
+
foreach ($step in $CompletedSteps) {
|
|
162
|
+
if (Test-StepHasPrefix -StepEntry $step -Prefix 'S3_promotion') {
|
|
163
|
+
$hasPromotion = $true
|
|
164
|
+
}
|
|
165
|
+
if (Test-StepHasPrefix -StepEntry $step -Prefix 'S4_atomic_planning') {
|
|
166
|
+
$hasPlanning = $true
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
foreach ($step in $CompletedSteps) {
|
|
171
|
+
$index = Get-CanonicalStepIndex -StepEntry $step
|
|
172
|
+
if ($index -ge 5 -and (-not $hasPromotion -or -not $hasPlanning)) {
|
|
173
|
+
return [pscustomobject]@{
|
|
174
|
+
Step = $step
|
|
175
|
+
MissingPromotion = -not $hasPromotion
|
|
176
|
+
MissingPlanning = -not $hasPlanning
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return $null
|
|
182
|
+
}
|
|
183
|
+
|
|
137
184
|
function Test-IsCheckpointPath {
|
|
138
185
|
[CmdletBinding()]
|
|
139
186
|
[OutputType([bool])]
|
|
@@ -204,10 +251,6 @@ function Invoke-CheckpointMonotonicDecision {
|
|
|
204
251
|
}
|
|
205
252
|
}
|
|
206
253
|
|
|
207
|
-
if ($steps.Count -lt 2) {
|
|
208
|
-
return [ordered]@{ decision = 'allow' }
|
|
209
|
-
}
|
|
210
|
-
|
|
211
254
|
$rollbackHistory = $null
|
|
212
255
|
if ($payload.PSObject.Properties.Name -contains 'rollback_history') {
|
|
213
256
|
$rollbackHistory = $payload.rollback_history
|
|
@@ -216,15 +259,30 @@ function Invoke-CheckpointMonotonicDecision {
|
|
|
216
259
|
return [ordered]@{ decision = 'allow' }
|
|
217
260
|
}
|
|
218
261
|
|
|
219
|
-
$pair = Get-OutOfOrderPair -CompletedSteps $steps
|
|
220
|
-
if ($null -
|
|
221
|
-
return [ordered]@{
|
|
262
|
+
$pair = if ($steps.Count -ge 2) { Get-OutOfOrderPair -CompletedSteps $steps } else { $null }
|
|
263
|
+
if ($null -ne $pair) {
|
|
264
|
+
return [ordered]@{
|
|
265
|
+
decision = 'block'
|
|
266
|
+
reason = "CHECKPOINT_ORDER_BLOCKED: completed_steps lists '$($pair.EarlierEntry)' at position $($pair.EarlierPos) before '$($pair.LaterEntry)' at position $($pair.LaterPos), but the canonical orchestrator workflow requires the later step to follow the earlier one. Reorder completed_steps or, if a rollback occurred, record it in rollback_history."
|
|
267
|
+
}
|
|
222
268
|
}
|
|
223
269
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
270
|
+
$missingPrerequisite = Get-MissingPrerequisiteForAdvancedStep -CompletedSteps $steps
|
|
271
|
+
if ($null -ne $missingPrerequisite) {
|
|
272
|
+
$missing = @()
|
|
273
|
+
if ($missingPrerequisite.MissingPromotion) {
|
|
274
|
+
$missing += 'S3_promotion'
|
|
275
|
+
}
|
|
276
|
+
if ($missingPrerequisite.MissingPlanning) {
|
|
277
|
+
$missing += 'S4_atomic_planning'
|
|
278
|
+
}
|
|
279
|
+
return [ordered]@{
|
|
280
|
+
decision = 'block'
|
|
281
|
+
reason = "CHECKPOINT_ORDER_BLOCKED: completed_steps lists '$($missingPrerequisite.Step)' before required prerequisite step(s): $($missing -join ', '). Record promotion and planning completion before implementation, review, PR, CI, or DONE steps."
|
|
282
|
+
}
|
|
227
283
|
}
|
|
284
|
+
|
|
285
|
+
return [ordered]@{ decision = 'allow' }
|
|
228
286
|
}
|
|
229
287
|
|
|
230
288
|
# Guard allows dot-sourcing in tests without executing the entrypoint.
|
|
@@ -188,6 +188,28 @@ function Get-MissingCompletionEvidence {
|
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
if ($issueNum -eq '232') {
|
|
192
|
+
$prGate = $null
|
|
193
|
+
if ($null -ne $Payload -and ($Payload.PSObject.Properties.Name -contains 'pr_gate')) {
|
|
194
|
+
$prGate = $Payload.pr_gate
|
|
195
|
+
}
|
|
196
|
+
if ($null -eq $prGate -or $prGate -isnot [System.Management.Automation.PSCustomObject]) {
|
|
197
|
+
$missing += 'pr_gate (object with pr_number, pr_url, head_branch, and head_sha)'
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
foreach ($field in @('pr_number', 'pr_url', 'head_branch', 'head_sha')) {
|
|
201
|
+
if (-not (Get-CheckpointStringValue -Payload $prGate -Name $field)) {
|
|
202
|
+
$missing += "pr_gate.$field"
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
$prHeadSha = Get-CheckpointStringValue -Payload $prGate -Name 'head_sha'
|
|
206
|
+
$ciHeadSha = Get-CheckpointStringValue -Payload $ciGate -Name 'head_sha'
|
|
207
|
+
if ($prHeadSha -and $ciHeadSha -and $prHeadSha -ne $ciHeadSha) {
|
|
208
|
+
$missing += 'ci_gate.head_sha matching pr_gate.head_sha'
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
191
213
|
return [string[]]$missing
|
|
192
214
|
}
|
|
193
215
|
|
|
@@ -249,9 +271,14 @@ function Invoke-CompletionConsistencyDecision {
|
|
|
249
271
|
return [ordered]@{ decision = 'allow' }
|
|
250
272
|
}
|
|
251
273
|
|
|
274
|
+
$issueContext = ''
|
|
275
|
+
if ((Get-CheckpointStringValue -Payload $payload -Name 'issue-num') -eq '232') {
|
|
276
|
+
$issueContext = ' Issue #232 requires pr_gate evidence and current-head ci_gate evidence.'
|
|
277
|
+
}
|
|
278
|
+
|
|
252
279
|
return [ordered]@{
|
|
253
280
|
decision = 'block'
|
|
254
|
-
reason = "COMPLETION_CONSISTENCY_BLOCKED: the checkpoint asserts completion but is missing required completion evidence: $($missing -join ', ')
|
|
281
|
+
reason = "COMPLETION_CONSISTENCY_BLOCKED: the checkpoint asserts completion but is missing required completion evidence: $($missing -join ', ').$issueContext A completion-asserting checkpoint must include a non-empty issue-num, a non-empty feature-folder, and a ci_gate object with conclusion == 'success' and a non-empty head_sha. Supply the missing evidence or remove the completion assertion."
|
|
255
282
|
}
|
|
256
283
|
}
|
|
257
284
|
|
|
@@ -17,12 +17,15 @@
|
|
|
17
17
|
- artifacts/evidence/
|
|
18
18
|
- artifacts/regression-testing/
|
|
19
19
|
- artifacts/post-change/
|
|
20
|
+
- artifacts/research/
|
|
20
21
|
|
|
21
22
|
All other paths pass through, including canonical evidence paths of the form
|
|
22
23
|
<FEATURE>/evidence/<kind>/ and permitted artifacts/ sub-paths such as
|
|
23
|
-
artifacts/orchestration/, artifacts/
|
|
24
|
-
artifacts/
|
|
25
|
-
|
|
24
|
+
artifacts/orchestration/, artifacts/pr_context, artifacts/reviews/,
|
|
25
|
+
artifacts/status/, artifacts/python/, artifacts/pester/, and
|
|
26
|
+
artifacts/csharp/. Research output is no longer an artifacts/ sub-path; it
|
|
27
|
+
is written to the tracked roots docs/features/<feature>/research/
|
|
28
|
+
(feature-associated) or docs/research/ (one-off).
|
|
26
29
|
|
|
27
30
|
If the file_path resolves to a forbidden prefix, the script writes a JSON response
|
|
28
31
|
to stdout with 'decision': 'block' and exits with code 0 so Claude Code surfaces
|
|
@@ -61,7 +64,8 @@ function Test-EvidenceLocationForbidden {
|
|
|
61
64
|
'artifacts/coverage/',
|
|
62
65
|
'artifacts/evidence/',
|
|
63
66
|
'artifacts/regression-testing/',
|
|
64
|
-
'artifacts/post-change/'
|
|
67
|
+
'artifacts/post-change/',
|
|
68
|
+
'artifacts/research/'
|
|
65
69
|
)
|
|
66
70
|
|
|
67
71
|
# Match the prefix either at the start of the string or after any directory separator,
|
|
@@ -133,18 +137,40 @@ function Invoke-EvidenceLocationDecision {
|
|
|
133
137
|
return [ordered]@{ decision = 'allow' }
|
|
134
138
|
}
|
|
135
139
|
|
|
140
|
+
function Invoke-EvidenceLocationEntryPoint {
|
|
141
|
+
<#
|
|
142
|
+
.SYNOPSIS
|
|
143
|
+
Runs the evidence-location decision and returns the process exit code.
|
|
144
|
+
.DESCRIPTION
|
|
145
|
+
Wraps the dispatch logic that the hook entry point performs so it can be
|
|
146
|
+
exercised by unit tests. On success it writes the compact JSON decision to
|
|
147
|
+
the output stream and returns 0. On a hard failure (malformed JSON) it
|
|
148
|
+
writes the error record and returns 1. This function does not call exit;
|
|
149
|
+
the thin entry-point wiring converts the returned code into a process exit.
|
|
150
|
+
.PARAMETER ToolInputRaw
|
|
151
|
+
The raw JSON string from $env:CLAUDE_TOOL_INPUT.
|
|
152
|
+
#>
|
|
153
|
+
[CmdletBinding()]
|
|
154
|
+
[OutputType([int])]
|
|
155
|
+
param(
|
|
156
|
+
[string] $ToolInputRaw = $env:CLAUDE_TOOL_INPUT
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
$decision = Invoke-EvidenceLocationDecision -ToolInputRaw $ToolInputRaw
|
|
161
|
+
} catch {
|
|
162
|
+
Write-Error $_
|
|
163
|
+
return 1
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
$decision | ConvertTo-Json -Compress | Write-Output
|
|
167
|
+
|
|
168
|
+
return 0
|
|
169
|
+
}
|
|
170
|
+
|
|
136
171
|
# Guard allows dot-sourcing in tests without executing the entrypoint.
|
|
137
172
|
if ($MyInvocation.InvocationName -eq '.') {
|
|
138
173
|
return
|
|
139
174
|
}
|
|
140
175
|
|
|
141
|
-
|
|
142
|
-
$decision = Invoke-EvidenceLocationDecision -ToolInputRaw $env:CLAUDE_TOOL_INPUT
|
|
143
|
-
} catch {
|
|
144
|
-
Write-Error $_
|
|
145
|
-
exit 1
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
$decision | ConvertTo-Json -Compress | Write-Output
|
|
149
|
-
|
|
150
|
-
exit 0
|
|
176
|
+
exit (Invoke-EvidenceLocationEntryPoint)
|