@danmoisan/drm-copilot-mcp 1.0.19 → 1.0.21
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 +71 -8
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agents/atomic-executor.md +2 -2
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +14 -6
- package/resources/claude-customizations/.claude/lib/model-routing/ModelRouting.psm1 +34 -14
- package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1 +30 -18
- package/resources/claude-customizations/.claude/rules/general-code-change.md +1 -1
- package/resources/claude-customizations/.claude/rules/general-unit-test.md +2 -2
- package/resources/claude-customizations/.claude/rules/typescript.md +5 -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 +2 -2
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +4 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +4 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +4 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +4 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +4 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +4 -0
- package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +9 -65
- package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +9 -65
- package/resources/codex-and-agents-customizations/.codex/hooks/codex-pretooluse-file-mapping.ps1 +474 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +20 -101
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +16 -3
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +14 -5
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +23 -5
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +30 -52
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +28 -27
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +19 -49
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +19 -49
- package/resources/codex-and-agents-customizations/pack-manifests/core.json +1 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +19 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-pr-author-skill.ps1 +0 -500
package/out/mcp-server.js
CHANGED
|
@@ -3646,7 +3646,12 @@ var require_fast_uri = __commonJS({
|
|
|
3646
3646
|
}
|
|
3647
3647
|
function resolve4(baseURI, relativeURI, options) {
|
|
3648
3648
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3649
|
-
const
|
|
3649
|
+
const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
|
|
3650
|
+
const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
|
|
3651
|
+
if (baseMalformed || relativeMalformed) {
|
|
3652
|
+
throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
|
|
3653
|
+
}
|
|
3654
|
+
const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
|
|
3650
3655
|
schemelessOptions.skipEscape = true;
|
|
3651
3656
|
return serialize(resolved, schemelessOptions);
|
|
3652
3657
|
}
|
|
@@ -3772,6 +3777,7 @@ var require_fast_uri = __commonJS({
|
|
|
3772
3777
|
}
|
|
3773
3778
|
var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
|
|
3774
3779
|
var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
|
|
3780
|
+
var AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
|
|
3775
3781
|
function getParseError(parsed, matches) {
|
|
3776
3782
|
if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
|
|
3777
3783
|
return 'URI path must start with "/" when authority is present.';
|
|
@@ -3806,6 +3812,20 @@ var require_fast_uri = __commonJS({
|
|
|
3806
3812
|
parsed.error = "URI authority must not contain a literal backslash.";
|
|
3807
3813
|
malformedAuthorityOrPort = true;
|
|
3808
3814
|
}
|
|
3815
|
+
const introducerMatch = uri.match(AUTHORITY_INTRODUCER_REGION);
|
|
3816
|
+
if (introducerMatch !== null) {
|
|
3817
|
+
const region = introducerMatch[1];
|
|
3818
|
+
const normalizedRegion = region.replace(/[\t\n\r]/g, "");
|
|
3819
|
+
if (normalizedRegion.length >= 2) {
|
|
3820
|
+
if (normalizedRegion.slice(0, 2) !== "//") {
|
|
3821
|
+
parsed.error = parsed.error || "URI authority must not contain a literal backslash.";
|
|
3822
|
+
malformedAuthorityOrPort = true;
|
|
3823
|
+
} else if (region.length !== normalizedRegion.length) {
|
|
3824
|
+
parsed.error = parsed.error || "URI authority introducer must not contain whitespace.";
|
|
3825
|
+
malformedAuthorityOrPort = true;
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3809
3829
|
const matches = uri.match(URI_PARSE);
|
|
3810
3830
|
if (matches) {
|
|
3811
3831
|
parsed.scheme = matches[1];
|
|
@@ -23307,7 +23327,10 @@ function validateCodexModelRoutingReceipts(value) {
|
|
|
23307
23327
|
}
|
|
23308
23328
|
function delegatedAgentNames(state) {
|
|
23309
23329
|
const result = /* @__PURE__ */ new Set();
|
|
23310
|
-
|
|
23330
|
+
let receipts = state["delegation_receipts"];
|
|
23331
|
+
if (isObject4(receipts)) {
|
|
23332
|
+
receipts = receipts["agents"];
|
|
23333
|
+
}
|
|
23311
23334
|
if (!Array.isArray(receipts)) {
|
|
23312
23335
|
return result;
|
|
23313
23336
|
}
|
|
@@ -23701,7 +23724,10 @@ function validateCodexTopologyReceipts(value) {
|
|
|
23701
23724
|
}
|
|
23702
23725
|
function delegatedAgentNames2(state) {
|
|
23703
23726
|
const result = /* @__PURE__ */ new Set();
|
|
23704
|
-
|
|
23727
|
+
let receipts = state["delegation_receipts"];
|
|
23728
|
+
if (isObject5(receipts)) {
|
|
23729
|
+
receipts = receipts["agents"];
|
|
23730
|
+
}
|
|
23705
23731
|
if (!Array.isArray(receipts)) {
|
|
23706
23732
|
return result;
|
|
23707
23733
|
}
|
|
@@ -25354,6 +25380,9 @@ function stateList(state, key, expected) {
|
|
|
25354
25380
|
return value;
|
|
25355
25381
|
}
|
|
25356
25382
|
function listReceipts(receipts) {
|
|
25383
|
+
if (isObject8(receipts)) {
|
|
25384
|
+
receipts = receipts["agents"];
|
|
25385
|
+
}
|
|
25357
25386
|
if (!Array.isArray(receipts)) {
|
|
25358
25387
|
return [];
|
|
25359
25388
|
}
|
|
@@ -25764,7 +25793,10 @@ function isObject12(value) {
|
|
|
25764
25793
|
}
|
|
25765
25794
|
function delegatedAgents(state) {
|
|
25766
25795
|
const agents = /* @__PURE__ */ new Set();
|
|
25767
|
-
|
|
25796
|
+
let receipts = state["delegation_receipts"];
|
|
25797
|
+
if (isObject12(receipts)) {
|
|
25798
|
+
receipts = receipts["agents"];
|
|
25799
|
+
}
|
|
25768
25800
|
if (Array.isArray(receipts)) {
|
|
25769
25801
|
for (const receipt of receipts) {
|
|
25770
25802
|
if (!isObject12(receipt)) {
|
|
@@ -25867,6 +25899,27 @@ var STEP_STATUS_KEYS = [
|
|
|
25867
25899
|
"step9_status",
|
|
25868
25900
|
"step10_status"
|
|
25869
25901
|
];
|
|
25902
|
+
var AGENT_RECEIPT_NAMESPACE_KEY = "agents";
|
|
25903
|
+
var STEP_SPECIFIC_EXTRA_STATUS = /* @__PURE__ */ new Map([
|
|
25904
|
+
["step6_status", /* @__PURE__ */ new Set(["blocked_remediation_loop_limit"])],
|
|
25905
|
+
[
|
|
25906
|
+
"step9_status",
|
|
25907
|
+
/* @__PURE__ */ new Set(["passed", "failed_remediation_required", "blocked_ci_loop_limit"])
|
|
25908
|
+
]
|
|
25909
|
+
]);
|
|
25910
|
+
var COMPLETION_BLOCKING_STEP_STATUS = /* @__PURE__ */ new Set([
|
|
25911
|
+
"pending",
|
|
25912
|
+
"blocked",
|
|
25913
|
+
"failed_remediation_required",
|
|
25914
|
+
"blocked_ci_loop_limit",
|
|
25915
|
+
"blocked_remediation_loop_limit"
|
|
25916
|
+
]);
|
|
25917
|
+
function isValidStepStatus(key, value) {
|
|
25918
|
+
if (VALID_STEP_STATUS.has(value)) {
|
|
25919
|
+
return true;
|
|
25920
|
+
}
|
|
25921
|
+
return STEP_SPECIFIC_EXTRA_STATUS.get(key)?.has(value) === true;
|
|
25922
|
+
}
|
|
25870
25923
|
function isObject13(value) {
|
|
25871
25924
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25872
25925
|
}
|
|
@@ -25895,12 +25948,22 @@ function validateListDelegationReceipts(receipts) {
|
|
|
25895
25948
|
}
|
|
25896
25949
|
function validateNamespacedDelegationReceipts(receipts) {
|
|
25897
25950
|
const errors = [];
|
|
25898
|
-
const unsupportedKeys = Object.keys(receipts).filter(
|
|
25951
|
+
const unsupportedKeys = Object.keys(receipts).filter(
|
|
25952
|
+
(key) => key !== AGENT_RECEIPT_NAMESPACE_KEY && key !== PROMOTION_RECEIPT_NAMESPACE_KEY
|
|
25953
|
+
).sort();
|
|
25899
25954
|
for (const key of unsupportedKeys) {
|
|
25900
25955
|
errors.push(
|
|
25901
25956
|
`Checkpoint delegation_receipts object contains unsupported key: ${key}`
|
|
25902
25957
|
);
|
|
25903
25958
|
}
|
|
25959
|
+
if (AGENT_RECEIPT_NAMESPACE_KEY in receipts) {
|
|
25960
|
+
const agentReceipts = receipts[AGENT_RECEIPT_NAMESPACE_KEY];
|
|
25961
|
+
if (!Array.isArray(agentReceipts)) {
|
|
25962
|
+
errors.push("Checkpoint delegation_receipts.agents must be a list.");
|
|
25963
|
+
} else {
|
|
25964
|
+
errors.push(...validateListDelegationReceipts(agentReceipts));
|
|
25965
|
+
}
|
|
25966
|
+
}
|
|
25904
25967
|
const promotionReceipts = receipts[PROMOTION_RECEIPT_NAMESPACE_KEY];
|
|
25905
25968
|
if (promotionReceipts === void 0 || promotionReceipts === null) {
|
|
25906
25969
|
return errors;
|
|
@@ -25951,7 +26014,7 @@ function validateOrchestratorStateText(text, options = {}) {
|
|
|
25951
26014
|
}
|
|
25952
26015
|
for (const key of STEP_STATUS_KEYS) {
|
|
25953
26016
|
const value = stateMap[key];
|
|
25954
|
-
if (value !== void 0 && value !== null && !(typeof value === "string" &&
|
|
26017
|
+
if (value !== void 0 && value !== null && !(typeof value === "string" && isValidStepStatus(key, value))) {
|
|
25955
26018
|
errors.push(`Checkpoint has invalid ${key}: ${String(value)}`);
|
|
25956
26019
|
}
|
|
25957
26020
|
}
|
|
@@ -25982,7 +26045,7 @@ function validateOrchestratorStateText(text, options = {}) {
|
|
|
25982
26045
|
if (options.requireComplete === true) {
|
|
25983
26046
|
for (const key of STEP_STATUS_KEYS) {
|
|
25984
26047
|
const value = stateMap[key];
|
|
25985
|
-
if (value
|
|
26048
|
+
if (COMPLETION_BLOCKING_STEP_STATUS.has(value)) {
|
|
25986
26049
|
errors.push(
|
|
25987
26050
|
`Checkpoint completion validation failed: ${key} is ${String(value)}.`
|
|
25988
26051
|
);
|
|
@@ -26322,7 +26385,7 @@ function validatePlanText(text) {
|
|
|
26322
26385
|
const seenPhases = [];
|
|
26323
26386
|
const expectedTaskNum = /* @__PURE__ */ new Map();
|
|
26324
26387
|
let foundTask = false;
|
|
26325
|
-
const lines = text.split(
|
|
26388
|
+
const lines = text.split(/\r\n|\n|\r/);
|
|
26326
26389
|
lines.forEach((line, lineIndex) => {
|
|
26327
26390
|
const lineNumber = lineIndex + 1;
|
|
26328
26391
|
if (line.startsWith("### Phase ")) {
|
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@ tools:
|
|
|
15
15
|
- "Bash(npx prettier *)"
|
|
16
16
|
- "Bash(npx eslint *)"
|
|
17
17
|
- "Bash(npx tsc *)"
|
|
18
|
-
- "Bash(npx
|
|
18
|
+
- "Bash(npx jest *)"
|
|
19
19
|
- "Bash(pwsh *)"
|
|
20
20
|
- "Bash(git *)"
|
|
21
21
|
- "mcp__drm-copilot__run_poshqc_format"
|
|
@@ -76,7 +76,7 @@ For each task:
|
|
|
76
76
|
Use the scoped tool patterns for quality gates:
|
|
77
77
|
|
|
78
78
|
- **Python**: `poetry run black`, `poetry run ruff`, `poetry run pyright`, `poetry run pytest`
|
|
79
|
-
- **TypeScript**: `npx prettier`, `npx eslint`, `npx tsc`, `npx
|
|
79
|
+
- **TypeScript**: `npx prettier`, `npx eslint`, `npx tsc`, `npx jest`
|
|
80
80
|
- **PowerShell**: MCP server functions (`mcp__drm-copilot__run_poshqc_format`, `mcp__drm-copilot__run_poshqc_analyze`, `mcp__drm-copilot__run_poshqc_test`, `mcp__drm-copilot__run_poshqc_analyze_autofix`)
|
|
81
81
|
- **Git**: `git diff`, `git status`, `git log`
|
|
82
82
|
|
|
@@ -165,9 +165,15 @@ function Invoke-RoutingContractValidation {
|
|
|
165
165
|
string is unchanged for every existing caller of this hook.
|
|
166
166
|
|
|
167
167
|
Returns a hashtable with keys:
|
|
168
|
-
- HasErrors: $true when the validator reported a non-zero exit
|
|
169
|
-
|
|
170
|
-
|
|
168
|
+
- HasErrors: $true only when the validator reported a non-zero exit
|
|
169
|
+
code; $false when it exited 0. The exit code is the sole
|
|
170
|
+
discriminator, because the validator prints its success
|
|
171
|
+
line to stdout on a clean pass and the default Invoker
|
|
172
|
+
captures with 2>&1, so output text is present on success.
|
|
173
|
+
- ErrorText: the validator's combined captured output text, carried
|
|
174
|
+
through unchanged: the error lines on a failure, and the
|
|
175
|
+
success line (Python CLI) or empty (portable fallback)
|
|
176
|
+
on a clean pass.
|
|
171
177
|
#>
|
|
172
178
|
[CmdletBinding()]
|
|
173
179
|
[OutputType([hashtable])]
|
|
@@ -219,9 +225,11 @@ function Invoke-RoutingContractValidation {
|
|
|
219
225
|
$outputText = ([string]$result.Output).Trim()
|
|
220
226
|
}
|
|
221
227
|
|
|
222
|
-
# The
|
|
223
|
-
#
|
|
224
|
-
|
|
228
|
+
# The exit code is the complete failure discriminator: the validator prints every
|
|
229
|
+
# error to stderr and returns non-zero, and prints its success line to stdout and
|
|
230
|
+
# returns 0. Because the default invoker captures with 2>&1, the success line lands
|
|
231
|
+
# in $outputText on a clean pass, so output text must not influence this decision.
|
|
232
|
+
$hasErrors = ($exitCode -ne 0)
|
|
225
233
|
return @{ HasErrors = $hasErrors; ErrorText = $outputText }
|
|
226
234
|
}
|
|
227
235
|
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
- Resolve-DelegationModel port of scripts/dev_tools/resolve_delegation_model.py
|
|
12
12
|
|
|
13
13
|
Both functions are pure and deterministic: they read no file at runtime and
|
|
14
|
-
encode only the fixed band ordering, the
|
|
15
|
-
preferred overlay, and the disabled-mode clamp
|
|
14
|
+
encode only the fixed band ordering, the floor-signal name set, the base
|
|
15
|
+
complexity-to-model table, the preferred overlay, and the disabled-mode clamp
|
|
16
|
+
as module-scope constants.
|
|
16
17
|
Those literals are pinned to config/orchestration-routing.json (model_policy /
|
|
17
18
|
model_budget) by a static config-parity Pester test, and the Python modules
|
|
18
19
|
remain the validator's authoritative reference. This module is one half of a
|
|
@@ -29,6 +30,20 @@ $script:BAND_ORDER = @('C1', 'C2', 'C3', 'C4')
|
|
|
29
30
|
# The lowest band, returned when no floor signal is present (LOWEST_BAND).
|
|
30
31
|
$script:LOWEST_BAND = 'C1'
|
|
31
32
|
|
|
33
|
+
# The catalog signal names flagged "floor": true in model_policy.complexity. Only
|
|
34
|
+
# a signal named here contributes a floor candidate; a "floor": false name and an
|
|
35
|
+
# unknown name each contribute nothing. Hard-coded (never read from disk) because
|
|
36
|
+
# this module is pushed down to consumer repositories that do not ship
|
|
37
|
+
# config/orchestration-routing.json; a static parity Pester test pins this set to
|
|
38
|
+
# the config's "floor": true entries. Mirrors FLOOR_SIGNAL_NAMES in
|
|
39
|
+
# scripts/dev_tools/compute_complexity_floor.py.
|
|
40
|
+
$script:FLOOR_SIGNAL_NAMES = @(
|
|
41
|
+
'classifier_or_model_logic',
|
|
42
|
+
'auth_or_token_handling',
|
|
43
|
+
'concurrency_or_ordering',
|
|
44
|
+
'cross_module_contract_change'
|
|
45
|
+
)
|
|
46
|
+
|
|
32
47
|
# Every present floor signal contributes this uniform candidate band, per the
|
|
33
48
|
# model_policy.complexity contract (each [floor] signal contributes C3).
|
|
34
49
|
$script:FLOOR_CANDIDATE_BAND = 'C3'
|
|
@@ -78,21 +93,22 @@ function Get-ComplexityFloor {
|
|
|
78
93
|
.DESCRIPTION
|
|
79
94
|
Faithful PowerShell port of compute_complexity_floor
|
|
80
95
|
(scripts/dev_tools/compute_complexity_floor.py). Returns the deterministic
|
|
81
|
-
lower-bound complexity band implied by the
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
96
|
+
lower-bound complexity band implied by the recorded signal names: the
|
|
97
|
+
function intersects the input with FLOOR_SIGNAL_NAMES, each surviving
|
|
98
|
+
[floor] signal contributes a candidate band of C3, the floor is the maximum
|
|
99
|
+
triggered candidate band, and the floor never exceeds C3 (C4 is never
|
|
100
|
+
floor-forced). With no floor signal present the floor is the lowest band C1.
|
|
101
|
+
The function is pure: it reads no file and does not mutate its input, and
|
|
102
|
+
the result is independent of input ordering.
|
|
87
103
|
|
|
88
104
|
.PARAMETER SignalsPresent
|
|
89
|
-
The
|
|
90
|
-
|
|
91
|
-
floor
|
|
92
|
-
no floor signal is present.
|
|
105
|
+
The full set of recorded signal names, as written to the checkpoint's
|
|
106
|
+
signals_present[] array. The caller does not pre-filter: names outside
|
|
107
|
+
FLOOR_SIGNAL_NAMES ("floor": false catalog names and unknown names alike)
|
|
108
|
+
contribute nothing. An empty collection means no floor signal is present.
|
|
93
109
|
|
|
94
110
|
.OUTPUTS
|
|
95
|
-
System.String. The floor band: C1 when no
|
|
111
|
+
System.String. The floor band: C1 when no recorded name is a floor signal,
|
|
96
112
|
otherwise the maximum triggered candidate band clamped to at most C3.
|
|
97
113
|
C4 is never returned.
|
|
98
114
|
#>
|
|
@@ -104,9 +120,13 @@ function Get-ComplexityFloor {
|
|
|
104
120
|
[string[]] $SignalsPresent
|
|
105
121
|
)
|
|
106
122
|
|
|
123
|
+
# Keep only the recorded names that are floor signals. A "floor": false catalog
|
|
124
|
+
# name and an unknown name both drop out here and contribute no candidate band.
|
|
125
|
+
$triggered = @($SignalsPresent | Where-Object { $script:FLOOR_SIGNAL_NAMES -contains $_ })
|
|
126
|
+
|
|
107
127
|
# With no present floor signal there is no candidate band to raise the floor
|
|
108
128
|
# above the lowest band, so the floor is C1 (mirrors the empty-input guard).
|
|
109
|
-
if (
|
|
129
|
+
if ($triggered.Count -eq 0) {
|
|
110
130
|
return $script:LOWEST_BAND
|
|
111
131
|
}
|
|
112
132
|
|
package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
checkpoint-presence checks (required keys, step-status validity, blocked_reason
|
|
15
15
|
validity) from `scripts/dev_tools/validate_orchestrator_state.py`. The base
|
|
16
16
|
constants below are pinned to `REQUIRED_STATE_KEYS`, `STEP_STATUS_KEYS`,
|
|
17
|
-
`VALID_STEP_STATUS`,
|
|
17
|
+
`VALID_STEP_STATUS`, `VALID_BLOCKED_REASONS`, and `STEP_SPECIFIC_EXTRA_STATUS`.
|
|
18
18
|
|
|
19
19
|
Every public function FAILS CLOSED: a missing checkpoint file, invalid JSON, a
|
|
20
20
|
missing required key, an invalid step status, or an unmet readiness condition all
|
|
@@ -83,6 +83,15 @@ $script:VALID_STEP_STATUS = @(
|
|
|
83
83
|
'completed'
|
|
84
84
|
)
|
|
85
85
|
|
|
86
|
+
# Per-key additive step-status vocabulary layered on VALID_STEP_STATUS: each value
|
|
87
|
+
# below is valid only on its owning key and is still rejected on every other step
|
|
88
|
+
# key. Pinned to STEP_SPECIFIC_EXTRA_STATUS in
|
|
89
|
+
# scripts/dev_tools/_orchestrator_state_step_status.py.
|
|
90
|
+
$script:STEP_SPECIFIC_EXTRA_STATUS = @{
|
|
91
|
+
step6_status = @('blocked_remediation_loop_limit')
|
|
92
|
+
step9_status = @('passed', 'failed_remediation_required', 'blocked_ci_loop_limit')
|
|
93
|
+
}
|
|
94
|
+
|
|
86
95
|
# The allowed blocked_reason vocabulary. Pinned to VALID_BLOCKED_REASONS in the
|
|
87
96
|
# primary validator.
|
|
88
97
|
$script:VALID_BLOCKED_REASONS = @(
|
|
@@ -226,11 +235,11 @@ function Get-OrchestratorStateBasePresenceError {
|
|
|
226
235
|
Return the base checkpoint-presence errors, mirroring the primary validator.
|
|
227
236
|
.DESCRIPTION
|
|
228
237
|
Private base check. Emits one error string per missing required key, one per
|
|
229
|
-
step5_status..step10_status value outside VALID_STEP_STATUS
|
|
230
|
-
blocked_reason is present with a
|
|
231
|
-
mirrors the base block of
|
|
232
|
-
(required keys, step-status
|
|
233
|
-
before any mode-specific gate.
|
|
238
|
+
step5_status..step10_status value outside VALID_STEP_STATUS and that key's
|
|
239
|
+
STEP_SPECIFIC_EXTRA_STATUS set, and one when blocked_reason is present with a
|
|
240
|
+
value outside VALID_BLOCKED_REASONS. This mirrors the base block of
|
|
241
|
+
scripts/dev_tools/validate_orchestrator_state.py (required keys, step-status
|
|
242
|
+
validity, blocked_reason validity) that runs before any mode-specific gate.
|
|
234
243
|
.PARAMETER State
|
|
235
244
|
The parsed checkpoint PSCustomObject.
|
|
236
245
|
.OUTPUTS
|
|
@@ -254,12 +263,16 @@ function Get-OrchestratorStateBasePresenceError {
|
|
|
254
263
|
}
|
|
255
264
|
}
|
|
256
265
|
|
|
257
|
-
# Every present step status must be a member of the
|
|
258
|
-
# step key contributes no error (mirrors the
|
|
266
|
+
# Every present step status must be a member of the shared vocabulary or of that
|
|
267
|
+
# key's additive extra set; an absent step key contributes no error (mirrors the
|
|
268
|
+
# primary validator's None guard).
|
|
259
269
|
foreach ($key in $script:STEP_STATUS_KEYS) {
|
|
260
270
|
$field = Get-OrchestratorStateField -State $State -Name $key
|
|
271
|
+
$extra = @()
|
|
272
|
+
if ($script:STEP_SPECIFIC_EXTRA_STATUS.ContainsKey($key)) { $extra = @($script:STEP_SPECIFIC_EXTRA_STATUS[$key]) }
|
|
261
273
|
if ($field.Present -and $null -ne $field.Value -and
|
|
262
|
-
($script:VALID_STEP_STATUS -notcontains [string]$field.Value)
|
|
274
|
+
($script:VALID_STEP_STATUS -notcontains [string]$field.Value) -and
|
|
275
|
+
($extra -notcontains [string]$field.Value)) {
|
|
263
276
|
$errors.Add("Checkpoint has invalid $key`: $($field.Value)")
|
|
264
277
|
}
|
|
265
278
|
}
|
|
@@ -279,12 +292,11 @@ function Get-OrchestratorStatePrCreationReadinessError {
|
|
|
279
292
|
.SYNOPSIS
|
|
280
293
|
Return the PR-creation-readiness errors, parity with the Python reference.
|
|
281
294
|
.DESCRIPTION
|
|
282
|
-
Private readiness check mirroring
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
present. It does not enforce completion, CI, PR, or routing-contract gates.
|
|
295
|
+
Private readiness check mirroring validate_orchestrator_state_pr_creation_readiness in
|
|
296
|
+
_orchestrator_state_pr_creation_readiness.py: steps 5-8 must not be pending, blocked, or
|
|
297
|
+
blocked_remediation_loop_limit; blocked_reason must be `none` or absent; and the
|
|
298
|
+
local_execution_overrides / delegation_bypasses lists must be empty when present. It does
|
|
299
|
+
not enforce completion, CI, PR, or routing-contract gates.
|
|
288
300
|
.PARAMETER State
|
|
289
301
|
The parsed checkpoint PSCustomObject.
|
|
290
302
|
.OUTPUTS
|
|
@@ -299,11 +311,11 @@ function Get-OrchestratorStatePrCreationReadinessError {
|
|
|
299
311
|
|
|
300
312
|
$errors = [System.Collections.Generic.List[string]]::new()
|
|
301
313
|
|
|
302
|
-
# Reject an upstream step recorded as pending or
|
|
303
|
-
# finished before the first PR of a branch is created.
|
|
314
|
+
# Reject an upstream step recorded as pending, blocked, or blocked_remediation_loop_limit; steps
|
|
315
|
+
# 5-8 must have finished before the first PR of a branch is created.
|
|
304
316
|
foreach ($key in $script:PR_CREATION_READY_STEP_KEYS) {
|
|
305
317
|
$field = Get-OrchestratorStateField -State $State -Name $key
|
|
306
|
-
if ($field.Present -and (
|
|
318
|
+
if ($field.Present -and (@('pending', 'blocked', 'blocked_remediation_loop_limit') -contains $field.Value)) {
|
|
307
319
|
$errors.Add("Checkpoint PR-creation readiness validation failed: $key is $($field.Value).")
|
|
308
320
|
}
|
|
309
321
|
}
|
|
@@ -36,7 +36,7 @@ Run the full seven-stage toolchain in this exact order and repeat until all stag
|
|
|
36
36
|
2. **Linting** (e.g., Ruff, ESLint, PSScriptAnalyzer, .NET analyzers)
|
|
37
37
|
3. **Type checking** (e.g., Pyright, TSC, nullable analysis; skip for PowerShell)
|
|
38
38
|
4. **Architecture-boundary tests** (e.g., dependency-cruiser, NetArchTest.Rules)
|
|
39
|
-
5. **Unit tests** (e.g., Pytest,
|
|
39
|
+
5. **Unit tests** (e.g., Pytest, Jest, MSTest, Pester) including property-based tests where applicable per `quality-tiers.md`
|
|
40
40
|
6. **Contract / schema compatibility checks** (e.g., oasdiff, schema-snapshot diff)
|
|
41
41
|
7. **Integration tests**
|
|
42
42
|
|
|
@@ -37,7 +37,7 @@ The correct response to a file that contains untestable lines is to refactor it
|
|
|
37
37
|
**Permitted `exclude` entries** (non-production paths only):
|
|
38
38
|
- Build output directories: `dist/**`, `lib/**`, `lib-amd/**`.
|
|
39
39
|
- Test files and test infrastructure: `**/*.test.ts`, `tests/**`, `src/test-support/**`.
|
|
40
|
-
- Config files that are not production code: `
|
|
40
|
+
- Config files that are not production code: `jest.config.cjs`, `eslint.config.mjs`, `.dependency-cruiser.cjs`, `webpack.config.js`.
|
|
41
41
|
- `node_modules/**`.
|
|
42
42
|
|
|
43
43
|
**Prohibited `exclude` entries:**
|
|
@@ -102,4 +102,4 @@ All test code must be deterministic. The following infrastructure requirements a
|
|
|
102
102
|
- **Controllable clock** — use a `Clock` interface (TypeScript) or `TimeProvider` (.NET) injected into code under test. Do not read wall-clock time directly in production code under test.
|
|
103
103
|
- **Seeded RNG** — randomness must be supplied via a seedable interface; on test failure the seed must be printed so the failure is reproducible.
|
|
104
104
|
- **Banned APIs in test code** — `setTimeout`, `Thread.Sleep`, `Task.Delay`, real wall-clock waits, and `Date.now()` outside the clock interface are prohibited in tests.
|
|
105
|
-
- **Virtual scheduler / fake timers / `FakeTimeProvider`** — async tests must use the framework's fake-timer facility (`
|
|
105
|
+
- **Virtual scheduler / fake timers / `FakeTimeProvider`** — async tests must use the framework's fake-timer facility (`jest.useFakeTimers()` for Jest, `FakeTimeProvider` for .NET) to advance simulated time deterministically.
|
|
@@ -13,7 +13,7 @@ This rule file summarizes the TypeScript-specific policies for this repository.
|
|
|
13
13
|
1. **Formatting — Prettier**: All TypeScript must be formatted with the repository Prettier configuration. Command: `npm run format`
|
|
14
14
|
2. **Linting — ESLint**: TypeScript must pass ESLint using the repository configuration. Command: `npm run lint`
|
|
15
15
|
3. **Type Checking — TSC**: TypeScript must pass the compiler type-check. Avoid `any`; prefer `unknown` plus narrowing. Command: `npm run typecheck`
|
|
16
|
-
4. **Testing —
|
|
16
|
+
4. **Testing — Jest**: All TypeScript unit tests must use Jest. Command: `npm run test:unit`
|
|
17
17
|
|
|
18
18
|
Run the toolchain in order: format → lint → type-check → test. Restart from step 1 if any step fails or changes files.
|
|
19
19
|
|
|
@@ -39,16 +39,16 @@ Run the toolchain in order: format → lint → type-check → test. Restart fro
|
|
|
39
39
|
|
|
40
40
|
## Testing Standards
|
|
41
41
|
|
|
42
|
-
- Use **
|
|
42
|
+
- Use **Jest** as the test framework.
|
|
43
43
|
- Name test files `*.test.ts`.
|
|
44
44
|
- Unit tests must not require the Outlook host runtime.
|
|
45
45
|
- Follow Arrange–Act–Assert structure.
|
|
46
46
|
- Each test targets one behavior.
|
|
47
|
-
- Use `
|
|
47
|
+
- Use `jest.spyOn` or `jest.mock` for targeted mocking; reset mocks with `afterEach(() => { jest.resetAllMocks(); })`.
|
|
48
48
|
- No external dependencies (network, filesystem temp files, external processes) in unit tests.
|
|
49
49
|
- Avoid snapshot tests unless stable and intentional.
|
|
50
50
|
- Coverage thresholds follow the uniform tier rule defined in `.claude/rules/quality-tiers.md`: line coverage >= 85% and branch coverage >= 75% across all tiers (T1–T4).
|
|
51
|
-
- Coverage command: `npm run test:coverage` (the
|
|
51
|
+
- Coverage command: `npm run test:unit:coverage` (the root `package.json` script runs `node run-jest.cjs --coverage`).
|
|
52
52
|
- Coverage regression on changed lines is a blocking finding.
|
|
53
53
|
- Interface/type-only files with no executable behavior — files consisting solely of `interface` or `type` declarations — may be omitted from coverage measurement. Such files legitimately report 0% executable coverage. This is a clarification only; it does not lower any coverage threshold.
|
|
54
54
|
|
|
@@ -70,5 +70,5 @@ Layer rules and the No-COM architecture assertions are defined in `.claude/rules
|
|
|
70
70
|
## Runtime Determinism
|
|
71
71
|
|
|
72
72
|
- `Date`, `Math.random`, and `setTimeout` access must flow through an injected `Clock` / `Random` interface.
|
|
73
|
-
- Tests use
|
|
73
|
+
- Tests use Jest fake timers (`jest.useFakeTimers()`).
|
|
74
74
|
- Prefer `await flushPromises()` over `setTimeout(0)` for awaiting micro-tasks.
|
package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md
CHANGED
|
@@ -41,7 +41,7 @@ Run the full seven-stage toolchain in this exact order and repeat until all stag
|
|
|
41
41
|
2. **Linting** (e.g., Ruff, ESLint, PSScriptAnalyzer, .NET analyzers)
|
|
42
42
|
3. **Type checking** (e.g., Pyright, TSC, nullable analysis; skip for PowerShell)
|
|
43
43
|
4. **Architecture-boundary tests** (e.g., dependency-cruiser, NetArchTest.Rules)
|
|
44
|
-
5. **Unit tests** (e.g., Pytest,
|
|
44
|
+
5. **Unit tests** (e.g., Pytest, Jest, MSTest, Pester) including property-based tests where applicable per `quality-tiers.md`
|
|
45
45
|
6. **Contract / schema compatibility checks** (e.g., oasdiff, schema-snapshot diff)
|
|
46
46
|
7. **Integration tests**
|
|
47
47
|
|
|
@@ -42,7 +42,7 @@ The correct response to a file that contains untestable lines is to refactor it
|
|
|
42
42
|
**Permitted `exclude` entries** (non-production paths only):
|
|
43
43
|
- Build output directories: `dist/**`, `lib/**`, `lib-amd/**`.
|
|
44
44
|
- Test files and test infrastructure: `**/*.test.ts`, `tests/**`, `src/test-support/**`.
|
|
45
|
-
- Config files that are not production code: `
|
|
45
|
+
- Config files that are not production code: `jest.config.cjs`, `eslint.config.mjs`, `.dependency-cruiser.cjs`, `webpack.config.js`.
|
|
46
46
|
- `node_modules/**`.
|
|
47
47
|
|
|
48
48
|
**Prohibited `exclude` entries:**
|
|
@@ -107,4 +107,4 @@ All test code must be deterministic. The following infrastructure requirements a
|
|
|
107
107
|
- **Controllable clock** — use a `Clock` interface (TypeScript) or `TimeProvider` (.NET) injected into code under test. Do not read wall-clock time directly in production code under test.
|
|
108
108
|
- **Seeded RNG** — randomness must be supplied via a seedable interface; on test failure the seed must be printed so the failure is reproducible.
|
|
109
109
|
- **Banned APIs in test code** — `setTimeout`, `Thread.Sleep`, `Task.Delay`, real wall-clock waits, and `Date.now()` outside the clock interface are prohibited in tests.
|
|
110
|
-
- **Virtual scheduler / fake timers / `FakeTimeProvider`** — async tests must use the framework's fake-timer facility (`
|
|
110
|
+
- **Virtual scheduler / fake timers / `FakeTimeProvider`** — async tests must use the framework's fake-timer facility (`jest.useFakeTimers()` for Jest, `FakeTimeProvider` for .NET) to advance simulated time deterministically.
|
|
@@ -142,6 +142,10 @@ Update it after every completed step with:
|
|
|
142
142
|
- `completed_steps`, `next_step`, `last_updated`
|
|
143
143
|
- `step5_status` through `step10_status`
|
|
144
144
|
- `delegation_receipts`, `blocked_reason`
|
|
145
|
+
- New checkpoints must store `delegation_receipts` as an object with a strict
|
|
146
|
+
`agents` list and an opaque `promotion` object. Strict receipt fields remain
|
|
147
|
+
unchanged, promotion payload values remain opaque, and readers retain
|
|
148
|
+
legacy top-level-list and promotion-only compatibility.
|
|
145
149
|
- `skill_receipts`, `mcp_call_receipts`
|
|
146
150
|
- `local_execution_overrides`, `delegation_bypasses`, `lifecycle_operations`
|
|
147
151
|
- raw promotion MCP receipts under `delegation_receipts.promotion.potential_entry`, `delegation_receipts.promotion.issue`, and `delegation_receipts.promotion.feature_folder`
|
|
@@ -142,6 +142,10 @@ Update it after every completed step with:
|
|
|
142
142
|
- `completed_steps`, `next_step`, `last_updated`
|
|
143
143
|
- `step5_status` through `step10_status`
|
|
144
144
|
- `delegation_receipts`, `blocked_reason`
|
|
145
|
+
- New checkpoints must store `delegation_receipts` as an object with a strict
|
|
146
|
+
`agents` list and an opaque `promotion` object. Strict receipt fields remain
|
|
147
|
+
unchanged, promotion payload values remain opaque, and readers retain
|
|
148
|
+
legacy top-level-list and promotion-only compatibility.
|
|
145
149
|
- `skill_receipts`, `mcp_call_receipts`
|
|
146
150
|
- `local_execution_overrides`, `delegation_bypasses`, `lifecycle_operations`
|
|
147
151
|
- raw promotion MCP receipts under `delegation_receipts.promotion.potential_entry`, `delegation_receipts.promotion.issue`, and `delegation_receipts.promotion.feature_folder`
|
package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml
CHANGED
|
@@ -142,6 +142,10 @@ Update it after every completed step with:
|
|
|
142
142
|
- `completed_steps`, `next_step`, `last_updated`
|
|
143
143
|
- `step5_status` through `step10_status`
|
|
144
144
|
- `delegation_receipts`, `blocked_reason`
|
|
145
|
+
- New checkpoints must store `delegation_receipts` as an object with a strict
|
|
146
|
+
`agents` list and an opaque `promotion` object. Strict receipt fields remain
|
|
147
|
+
unchanged, promotion payload values remain opaque, and readers retain
|
|
148
|
+
legacy top-level-list and promotion-only compatibility.
|
|
145
149
|
- `skill_receipts`, `mcp_call_receipts`
|
|
146
150
|
- `local_execution_overrides`, `delegation_bypasses`, `lifecycle_operations`
|
|
147
151
|
- raw promotion MCP receipts under `delegation_receipts.promotion.potential_entry`, `delegation_receipts.promotion.issue`, and `delegation_receipts.promotion.feature_folder`
|
|
@@ -142,6 +142,10 @@ Update it after every completed step with:
|
|
|
142
142
|
- `completed_steps`, `next_step`, `last_updated`
|
|
143
143
|
- `step5_status` through `step10_status`
|
|
144
144
|
- `delegation_receipts`, `blocked_reason`
|
|
145
|
+
- New checkpoints must store `delegation_receipts` as an object with a strict
|
|
146
|
+
`agents` list and an opaque `promotion` object. Strict receipt fields remain
|
|
147
|
+
unchanged, promotion payload values remain opaque, and readers retain
|
|
148
|
+
legacy top-level-list and promotion-only compatibility.
|
|
145
149
|
- `skill_receipts`, `mcp_call_receipts`
|
|
146
150
|
- `local_execution_overrides`, `delegation_bypasses`, `lifecycle_operations`
|
|
147
151
|
- raw promotion MCP receipts under `delegation_receipts.promotion.potential_entry`, `delegation_receipts.promotion.issue`, and `delegation_receipts.promotion.feature_folder`
|
|
@@ -142,6 +142,10 @@ Update it after every completed step with:
|
|
|
142
142
|
- `completed_steps`, `next_step`, `last_updated`
|
|
143
143
|
- `step5_status` through `step10_status`
|
|
144
144
|
- `delegation_receipts`, `blocked_reason`
|
|
145
|
+
- New checkpoints must store `delegation_receipts` as an object with a strict
|
|
146
|
+
`agents` list and an opaque `promotion` object. Strict receipt fields remain
|
|
147
|
+
unchanged, promotion payload values remain opaque, and readers retain
|
|
148
|
+
legacy top-level-list and promotion-only compatibility.
|
|
145
149
|
- `skill_receipts`, `mcp_call_receipts`
|
|
146
150
|
- `local_execution_overrides`, `delegation_bypasses`, `lifecycle_operations`
|
|
147
151
|
- raw promotion MCP receipts under `delegation_receipts.promotion.potential_entry`, `delegation_receipts.promotion.issue`, and `delegation_receipts.promotion.feature_folder`
|
|
@@ -142,6 +142,10 @@ Update it after every completed step with:
|
|
|
142
142
|
- `completed_steps`, `next_step`, `last_updated`
|
|
143
143
|
- `step5_status` through `step10_status`
|
|
144
144
|
- `delegation_receipts`, `blocked_reason`
|
|
145
|
+
- New checkpoints must store `delegation_receipts` as an object with a strict
|
|
146
|
+
`agents` list and an opaque `promotion` object. Strict receipt fields remain
|
|
147
|
+
unchanged, promotion payload values remain opaque, and readers retain
|
|
148
|
+
legacy top-level-list and promotion-only compatibility.
|
|
145
149
|
- `skill_receipts`, `mcp_call_receipts`
|
|
146
150
|
- `local_execution_overrides`, `delegation_bypasses`, `lifecycle_operations`
|
|
147
151
|
- raw promotion MCP receipts under `delegation_receipts.promotion.potential_entry`, `delegation_receipts.promotion.issue`, and `delegation_receipts.promotion.feature_folder`
|