@boyingliu01/xp-gate 0.18.4 → 0.19.0
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/adapter-common.sh +27 -0
- package/adapters/powershell.sh +85 -33
- package/adapters/typescript.sh +8 -8
- package/build-integrity/__tests__/import-resolver.test.ts +22 -0
- package/build-integrity/__tests__/orchestrator.test.ts +1 -2
- package/build-integrity/gate-10.ts +7 -0
- package/gate-12-file-hygiene.sh +230 -0
- package/hooks/adapter-common.sh +27 -0
- package/hooks/gate-10.sh +105 -0
- package/hooks/lib/validate-code-walkthrough.cjs +125 -0
- package/hooks/pre-commit +130 -33
- package/hooks/pre-push +15 -91
- package/lib/__tests__/check-version.test.js +67 -46
- package/lib/__tests__/doctor.test.js +7 -6
- package/lib/__tests__/gate-runner.test.js +267 -0
- package/lib/__tests__/init.test.js +32 -0
- package/lib/__tests__/npm-package.test.js +13 -0
- package/lib/__tests__/phase-transition.test.js +604 -12
- package/lib/__tests__/sprint-discovery.test.ts +0 -1
- package/lib/__tests__/sprint-status-rework.test.js +100 -1
- package/lib/__tests__/update-hooks.test.js +143 -10
- package/lib/__tests__/upgrade-exec.test.js +73 -45
- package/lib/__tests__/upgrade-skill-sync.test.js +1 -1
- package/lib/__tests__/upgrade.test.js +12 -11
- package/lib/doctor.js +2 -1
- package/lib/gate-runner.js +68 -23
- package/lib/init.js +34 -5
- package/lib/install-skill.js +1 -0
- package/lib/mattpocock-license-verdict.json +1 -1
- package/lib/phase-transition.js +203 -23
- package/lib/shared-paths.js +2 -0
- package/lib/sprint-status.js +15 -4
- package/lib/test-alignment.ts +3 -13
- package/lib/update-hooks.js +129 -15
- package/mock-policy/AGENTS.md +5 -5
- package/mock-policy/__tests__/gate-m3.test.ts +0 -5
- package/mock-policy/__tests__/scope-scanner.test.ts +8 -2
- package/mutation/AGENTS.md +4 -4
- package/mutation/__tests__/mutmut-runner.test.ts +443 -2
- package/mutation/__tests__/update-baseline.test.ts +0 -2
- package/mutation/runners/mutmut-runner.ts +67 -17
- package/package.json +9 -3
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/skills/clipboard-vision/SKILL.md +47 -0
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/claude-code/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/plugins/claude-code/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/plugins/claude-code/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/claude-code/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/claude-code/skills/delphi-review/.delphi-config.json.example +24 -27
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +13 -12
- package/plugins/claude-code/skills/delphi-review/INSTALL.md +15 -17
- package/plugins/claude-code/skills/delphi-review/SKILL.md +30 -29
- package/plugins/claude-code/skills/delphi-review/opencode.json.delphi.example +6 -10
- package/plugins/claude-code/skills/delphi-review/references/code-walkthrough.md +33 -49
- package/plugins/claude-code/skills/delphi-review/references/requirements.md +66 -54
- package/plugins/claude-code/skills/delphi-review/references/round-templates.md +7 -7
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +5 -5
- package/plugins/claude-code/skills/sprint-flow/SKILL.md +4 -4
- package/plugins/claude-code/skills/sprint-flow/references/components/skill-invocations.md +2 -2
- package/plugins/claude-code/skills/sprint-flow/references/force-levels.md +37 -35
- package/plugins/claude-code/skills/sprint-flow/references/orchestration-rules.md +1 -1
- package/plugins/claude-code/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/plugins/claude-code/skills/sprint-flow/references/phase-2-design.md +24 -14
- package/plugins/claude-code/skills/sprint-flow/references/phase-4-verify.md +6 -6
- package/plugins/claude-code/skills/sprint-flow/references/phase-overview.md +2 -2
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +4 -4
- package/plugins/dsh/README.md +25 -0
- package/plugins/dsh/cordis.patch.yml +7 -0
- package/plugins/dsh/package.json +70 -0
- package/plugins/dsh/scripts/prepack.cjs +82 -0
- package/plugins/dsh/skills/admin-template-guidelines/SKILL.md +113 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-1.md +91 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-2.md +155 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-3.md +70 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-4.md +154 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-5.md +126 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-6.md +180 -0
- package/plugins/dsh/skills/batch-grill-me/SKILL.md +15 -0
- package/plugins/dsh/skills/clipboard-vision/SKILL.md +47 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/dsh/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/plugins/dsh/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/plugins/dsh/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/dsh/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/dsh/skills/delphi-review/.delphi-config.json.example +70 -0
- package/plugins/dsh/skills/delphi-review/AGENTS.md +67 -0
- package/plugins/dsh/skills/delphi-review/INSTALL.md +150 -0
- package/plugins/dsh/skills/delphi-review/SKILL.md +559 -0
- package/plugins/dsh/skills/delphi-review/hooks.md +19 -0
- package/plugins/dsh/skills/delphi-review/opencode.json.delphi.example +52 -0
- package/plugins/dsh/skills/delphi-review/references/code-walkthrough.md +470 -0
- package/plugins/dsh/skills/delphi-review/references/orchestrator-dispatch.md +64 -0
- package/plugins/dsh/skills/delphi-review/references/requirements.md +402 -0
- package/plugins/dsh/skills/delphi-review/references/round-templates.md +134 -0
- package/plugins/dsh/skills/delphi-review/scripts/verify-consensus.sh +43 -0
- package/plugins/dsh/skills/delphi-review/tools.md +29 -0
- package/plugins/dsh/skills/domain-modeling/ADR-FORMAT.md +64 -0
- package/plugins/dsh/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/plugins/dsh/skills/domain-modeling/SKILL.md +74 -0
- package/plugins/dsh/skills/grill-with-docs/SKILL.md +7 -0
- package/plugins/dsh/skills/grilling/SKILL.md +12 -0
- package/plugins/dsh/skills/improve-codebase-architecture/SKILL.md +156 -0
- package/plugins/dsh/skills/ralph-loop/SKILL.md +198 -0
- package/plugins/dsh/skills/ralph-loop/references/components.md +113 -0
- package/plugins/dsh/skills/ralph-loop/references/phase-2-build-ralph.md +89 -0
- package/plugins/dsh/skills/ralph-loop/references/workflow-details.md +99 -0
- package/plugins/dsh/skills/ralph-loop/templates/progress-log.md +36 -0
- package/plugins/dsh/skills/sprint-flow/AGENTS.md +103 -0
- package/plugins/dsh/skills/sprint-flow/SKILL.md +411 -0
- package/plugins/dsh/skills/sprint-flow/__tests__/sprint-flow.test.cjs +457 -0
- package/plugins/dsh/skills/sprint-flow/hooks.md +18 -0
- package/plugins/dsh/skills/sprint-flow/references/components/memory.md +87 -0
- package/plugins/dsh/skills/sprint-flow/references/components/middleware.md +75 -0
- package/plugins/dsh/skills/sprint-flow/references/components/skill-invocations.md +105 -0
- package/plugins/dsh/skills/sprint-flow/references/components/system-prompt.md +27 -0
- package/plugins/dsh/skills/sprint-flow/references/components/tool-descriptions.md +95 -0
- package/plugins/dsh/skills/sprint-flow/references/force-levels.md +204 -0
- package/plugins/dsh/skills/sprint-flow/references/orchestration-rules.md +236 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-1-prep.md +294 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-2-design.md +273 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-3-build.md +348 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-4-verify.md +225 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-5-ship.md +222 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-6-close.md +266 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-overview.md +486 -0
- package/plugins/dsh/skills/sprint-flow/scripts/verify-sprint-state.sh +38 -0
- package/plugins/dsh/skills/sprint-flow/templates/auto-estimate-learning-log.md +136 -0
- package/plugins/dsh/skills/sprint-flow/templates/auto-estimate-output-template.md +110 -0
- package/plugins/dsh/skills/sprint-flow/templates/emergent-issues-template.md +120 -0
- package/plugins/dsh/skills/sprint-flow/templates/pain-document-template.md +115 -0
- package/plugins/dsh/skills/sprint-flow/templates/sprint-progress-template.md +140 -0
- package/plugins/dsh/skills/sprint-flow/templates/sprint-summary-template.md +121 -0
- package/plugins/dsh/skills/sprint-flow/tools.md +29 -0
- package/plugins/dsh/skills/test-driven-development/SKILL.md +371 -0
- package/plugins/dsh/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +56 -0
- package/plugins/dsh/skills/test-specification-alignment/SKILL.md +205 -0
- package/plugins/dsh/skills/test-specification-alignment/references/alignment-verification-algorithm.md +493 -0
- package/plugins/dsh/skills/test-specification-alignment/references/phase2-constraint-enforcement.md +431 -0
- package/plugins/dsh/skills/test-specification-alignment/references/specification-format.md +348 -0
- package/plugins/dsh/skills/to-issues/SKILL.md +277 -0
- package/plugins/dsh/src/command.test.ts +102 -0
- package/plugins/dsh/src/command.ts +75 -0
- package/plugins/dsh/src/contract.test.ts +62 -0
- package/plugins/dsh/src/gate-runner.test.ts +100 -0
- package/plugins/dsh/src/gate-runner.ts +58 -0
- package/plugins/dsh/src/index.test.ts +97 -0
- package/plugins/dsh/src/index.ts +94 -0
- package/plugins/dsh/src/skills.test.ts +58 -0
- package/plugins/dsh/tsconfig.build.json +13 -0
- package/plugins/dsh/tsconfig.json +19 -0
- package/plugins/dsh/vitest.config.ts +8 -0
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/scripts/prepack.cjs +1 -0
- package/plugins/opencode/skills/clipboard-vision/SKILL.md +47 -0
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/opencode/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/plugins/opencode/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/plugins/opencode/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/opencode/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/opencode/skills/delphi-review/.delphi-config.json.example +24 -27
- package/plugins/opencode/skills/delphi-review/AGENTS.md +13 -12
- package/plugins/opencode/skills/delphi-review/INSTALL.md +15 -17
- package/plugins/opencode/skills/delphi-review/SKILL.md +30 -29
- package/plugins/opencode/skills/delphi-review/opencode.json.delphi.example +6 -10
- package/plugins/opencode/skills/delphi-review/references/code-walkthrough.md +33 -49
- package/plugins/opencode/skills/delphi-review/references/requirements.md +66 -54
- package/plugins/opencode/skills/delphi-review/references/round-templates.md +7 -7
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +5 -5
- package/plugins/opencode/skills/sprint-flow/SKILL.md +4 -4
- package/plugins/opencode/skills/sprint-flow/references/components/skill-invocations.md +2 -2
- package/plugins/opencode/skills/sprint-flow/references/force-levels.md +37 -35
- package/plugins/opencode/skills/sprint-flow/references/orchestration-rules.md +1 -1
- package/plugins/opencode/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/plugins/opencode/skills/sprint-flow/references/phase-2-design.md +24 -14
- package/plugins/opencode/skills/sprint-flow/references/phase-4-verify.md +6 -6
- package/plugins/opencode/skills/sprint-flow/references/phase-overview.md +2 -2
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +4 -4
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/clipboard-vision/SKILL.md +47 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/SKILL.md +47 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/qoder/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/qoder/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/qoder/skills/delphi-review/.delphi-config.json.example +3 -11
- package/plugins/qoder/skills/delphi-review/AGENTS.md +15 -17
- package/plugins/qoder/skills/delphi-review/INSTALL.md +12 -12
- package/plugins/qoder/skills/delphi-review/SKILL.md +58 -25
- package/plugins/qoder/skills/delphi-review/opencode.json.delphi.example +1 -1
- package/plugins/qoder/skills/delphi-review/references/code-walkthrough.md +33 -49
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +5 -5
- package/plugins/qoder/skills/sprint-flow/SKILL.md +2 -2
- package/plugins/qoder/skills/sprint-flow/references/force-levels.md +38 -35
- package/plugins/qoder/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/plugins/qoder/skills/sprint-flow/references/phase-2-design.md +48 -6
- package/plugins/qoder/skills/sprint-flow/references/phase-4-verify.md +5 -6
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +4 -4
- package/principles/AGENTS.md +4 -4
- package/scripts/__tests__/sync-package-content.test.js +30 -0
- package/scripts/delphi-external-review.cjs +194 -63
- package/scripts/sync-package-content.js +9 -2
- package/skills/clipboard-vision/SKILL.md +47 -0
- package/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/skills/delphi-review/.delphi-config.json.example +24 -27
- package/skills/delphi-review/AGENTS.md +13 -12
- package/skills/delphi-review/INSTALL.md +15 -17
- package/skills/delphi-review/SKILL.md +30 -29
- package/skills/delphi-review/opencode.json.delphi.example +6 -10
- package/skills/delphi-review/references/code-walkthrough.md +33 -49
- package/skills/delphi-review/references/requirements.md +66 -54
- package/skills/delphi-review/references/round-templates.md +7 -7
- package/skills/sprint-flow/AGENTS.md +5 -5
- package/skills/sprint-flow/SKILL.md +4 -4
- package/skills/sprint-flow/references/components/skill-invocations.md +2 -2
- package/skills/sprint-flow/references/force-levels.md +37 -35
- package/skills/sprint-flow/references/orchestration-rules.md +1 -1
- package/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/skills/sprint-flow/references/phase-2-design.md +24 -14
- package/skills/sprint-flow/references/phase-4-verify.md +6 -6
- package/skills/sprint-flow/references/phase-overview.md +2 -2
- package/skills/test-specification-alignment/AGENTS.md +4 -4
- package/sprint-gate.sh +291 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
|
|
6
|
+
const REQUIRED_ROLES = ['architecture', 'technical', 'feasibility'];
|
|
7
|
+
const VALIDITY_MS = 60 * 60 * 1000;
|
|
8
|
+
|
|
9
|
+
function fail(message) {
|
|
10
|
+
console.error(`Invalid code walkthrough evidence: ${message}`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function isPlainObject(value) {
|
|
15
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
16
|
+
const prototype = Object.getPrototypeOf(value);
|
|
17
|
+
return prototype === Object.prototype || prototype === null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function parseTimestamp(value, field) {
|
|
21
|
+
const match = typeof value === 'string'
|
|
22
|
+
? value.match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?Z$/)
|
|
23
|
+
: null;
|
|
24
|
+
if (!match) fail(`${field} must be a canonical UTC ISO-8601 timestamp.`);
|
|
25
|
+
|
|
26
|
+
const [, yearText, monthText, dayText, hourText, minuteText, secondText, fractionText] = match;
|
|
27
|
+
const year = Number(yearText);
|
|
28
|
+
const month = Number(monthText);
|
|
29
|
+
const day = Number(dayText);
|
|
30
|
+
const hour = Number(hourText);
|
|
31
|
+
const minute = Number(minuteText);
|
|
32
|
+
const second = Number(secondText);
|
|
33
|
+
const milliseconds = Number((fractionText || '').padEnd(3, '0') || 0);
|
|
34
|
+
const date = new Date(0);
|
|
35
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
36
|
+
date.setUTCHours(hour, minute, second, milliseconds);
|
|
37
|
+
|
|
38
|
+
if (date.getUTCFullYear() !== year
|
|
39
|
+
|| date.getUTCMonth() !== month - 1
|
|
40
|
+
|| date.getUTCDate() !== day
|
|
41
|
+
|| date.getUTCHours() !== hour
|
|
42
|
+
|| date.getUTCMinutes() !== minute
|
|
43
|
+
|| date.getUTCSeconds() !== second
|
|
44
|
+
|| date.getUTCMilliseconds() !== milliseconds) {
|
|
45
|
+
fail(`${field} must contain a valid UTC calendar date and time.`);
|
|
46
|
+
}
|
|
47
|
+
return date.getTime();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function validateExpert(expert, index, roles, models) {
|
|
51
|
+
if (!isPlainObject(expert)) fail(`experts[${index}] must be a plain object.`);
|
|
52
|
+
if (!REQUIRED_ROLES.includes(expert.role)) fail(`experts[${index}].role is invalid.`);
|
|
53
|
+
if (roles.has(expert.role)) fail(`expert role ${expert.role} is duplicated.`);
|
|
54
|
+
roles.add(expert.role);
|
|
55
|
+
|
|
56
|
+
if (expert.verdict !== 'APPROVED') fail(`expert ${expert.role} must be APPROVED.`);
|
|
57
|
+
if (expert.result_type !== 'delphi_expert_result') {
|
|
58
|
+
fail(`expert ${expert.role} must be a successful Delphi expert result.`);
|
|
59
|
+
}
|
|
60
|
+
if (expert.error !== undefined || expert.fallback !== undefined) {
|
|
61
|
+
fail(`expert ${expert.role} contains an error or fallback marker.`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (typeof expert.requested_model !== 'string' || expert.requested_model.trim() === '') {
|
|
65
|
+
fail(`expert ${expert.role} requested_model must be non-empty.`);
|
|
66
|
+
}
|
|
67
|
+
const model = expert.requested_model.trim();
|
|
68
|
+
if (models.has(model)) fail(`requested_model ${model} is duplicated.`);
|
|
69
|
+
models.add(model);
|
|
70
|
+
|
|
71
|
+
if (expert.resolved_model !== null
|
|
72
|
+
&& (typeof expert.resolved_model !== 'string' || expert.resolved_model.trim() === '')) {
|
|
73
|
+
fail(`expert ${expert.role} resolved_model must be non-empty or null.`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function validateEvidence(evidence, expectedCommit, expectedBranch, now) {
|
|
78
|
+
if (!isPlainObject(evidence)) fail('top level must be a plain object.');
|
|
79
|
+
if (evidence.commit !== expectedCommit) fail('commit does not match HEAD.');
|
|
80
|
+
if (evidence.branch !== expectedBranch) fail('branch does not match the current branch.');
|
|
81
|
+
if (evidence.verdict !== 'APPROVED') fail('top-level verdict must be APPROVED.');
|
|
82
|
+
|
|
83
|
+
const timestamp = parseTimestamp(evidence.timestamp, 'timestamp');
|
|
84
|
+
const expires = parseTimestamp(evidence.expires, 'expires');
|
|
85
|
+
if (timestamp > now) fail('timestamp is in the future.');
|
|
86
|
+
if (expires <= now) fail('evidence has expired.');
|
|
87
|
+
if (expires - timestamp !== VALIDITY_MS) {
|
|
88
|
+
fail('expires must be exactly one hour after timestamp.');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (typeof evidence.consensus_ratio !== 'number'
|
|
92
|
+
|| !Number.isFinite(evidence.consensus_ratio)
|
|
93
|
+
|| evidence.consensus_ratio < 0.90
|
|
94
|
+
|| evidence.consensus_ratio > 1) {
|
|
95
|
+
fail('consensus_ratio must be a finite number from 0.90 through 1.');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!Array.isArray(evidence.experts) || evidence.experts.length !== 3) {
|
|
99
|
+
fail('experts must contain exactly three records.');
|
|
100
|
+
}
|
|
101
|
+
const roles = new Set();
|
|
102
|
+
const models = new Set();
|
|
103
|
+
evidence.experts.forEach((expert, index) => {
|
|
104
|
+
validateExpert(expert, index, roles, models);
|
|
105
|
+
});
|
|
106
|
+
if (roles.size !== REQUIRED_ROLES.length) fail('all required expert roles must be present.');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function main() {
|
|
110
|
+
const [file, expectedCommit, expectedBranch, nowValue] = process.argv.slice(2);
|
|
111
|
+
if (!file || !expectedCommit || !expectedBranch) {
|
|
112
|
+
fail('usage: validate-code-walkthrough.cjs <file> <commit> <branch> [now].');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let evidence;
|
|
116
|
+
try {
|
|
117
|
+
evidence = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
118
|
+
} catch {
|
|
119
|
+
fail('file must exist and contain valid JSON.');
|
|
120
|
+
}
|
|
121
|
+
const now = nowValue === undefined ? Date.now() : parseTimestamp(nowValue, 'current time');
|
|
122
|
+
validateEvidence(evidence, expectedCommit, expectedBranch, now);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
main();
|
package/hooks/pre-commit
CHANGED
|
@@ -14,7 +14,21 @@
|
|
|
14
14
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
15
|
ADAPTER_DIR=""
|
|
16
16
|
GLOBAL_ADAPTER_DIR="$HOME/.config/xp-gate/adapters"
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
# BEGIN GIT CONTEXT FALLBACK
|
|
19
|
+
if ! declare -F run_without_git_context >/dev/null 2>&1; then
|
|
20
|
+
run_without_git_context() (
|
|
21
|
+
unset GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_CONFIG GIT_CONFIG_PARAMETERS
|
|
22
|
+
unset GIT_CONFIG_COUNT GIT_OBJECT_DIRECTORY GIT_DIR GIT_WORK_TREE
|
|
23
|
+
unset GIT_IMPLICIT_WORK_TREE GIT_GRAFT_FILE GIT_INDEX_FILE
|
|
24
|
+
unset GIT_NO_REPLACE_OBJECTS GIT_REPLACE_REF_BASE GIT_PREFIX
|
|
25
|
+
unset GIT_SHALLOW_FILE GIT_COMMON_DIR
|
|
26
|
+
"$@"
|
|
27
|
+
)
|
|
28
|
+
fi
|
|
29
|
+
# END GIT CONTEXT FALLBACK
|
|
30
|
+
|
|
31
|
+
PROJECT_GITHOOKS="$(run_without_git_context git rev-parse --show-toplevel 2>/dev/null)/githooks"
|
|
18
32
|
GATES_DIR="$SCRIPT_DIR/gates"
|
|
19
33
|
|
|
20
34
|
if [ -f "$GLOBAL_ADAPTER_DIR/adapter-common.sh" ]; then
|
|
@@ -89,7 +103,7 @@ _quality_report_on_exit() {
|
|
|
89
103
|
}
|
|
90
104
|
trap '_quality_report_on_exit' EXIT
|
|
91
105
|
|
|
92
|
-
PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "$(cd "$SCRIPT_DIR/../.." 2>/dev/null || echo "$SCRIPT_DIR/..")" )"
|
|
106
|
+
PROJECT_ROOT="$(run_without_git_context git rev-parse --show-toplevel 2>/dev/null || echo "$(cd "$SCRIPT_DIR/../.." 2>/dev/null || echo "$SCRIPT_DIR/..")" )"
|
|
93
107
|
if [[ -d "$PROJECT_ROOT/node_modules/.bin" ]]; then
|
|
94
108
|
export PATH="$PROJECT_ROOT/node_modules/.bin:$PATH"
|
|
95
109
|
fi
|
|
@@ -415,6 +429,17 @@ if [ -z "$CHANGED_FILES" ]; then
|
|
|
415
429
|
exit 0
|
|
416
430
|
fi
|
|
417
431
|
|
|
432
|
+
any_changed_files_match() {
|
|
433
|
+
local pattern
|
|
434
|
+
|
|
435
|
+
for pattern in "$@"; do
|
|
436
|
+
if printf '%s\n' "$CHANGED_FILES" | grep -qE -- "$pattern"; then
|
|
437
|
+
return 0
|
|
438
|
+
fi
|
|
439
|
+
done
|
|
440
|
+
return 1
|
|
441
|
+
}
|
|
442
|
+
|
|
418
443
|
# ============================================================================
|
|
419
444
|
# Multi-language detection (v0.15.0+): detect all languages from changed file
|
|
420
445
|
# extensions, with fallback to manifest-based detection.
|
|
@@ -1387,6 +1412,14 @@ else
|
|
|
1387
1412
|
# --- BLOCK path: New .ts/.tsx files without corresponding tests ---
|
|
1388
1413
|
NEW_TS_FILES=$(git diff --cached --name-only --diff-filter=A | grep -E '\.(ts|tsx)$' | grep -v '\.d\.ts$' | grep -v '__tests__' | grep -v '\.test\.' | grep -v '\.spec\.' | grep -v '__snapshots__' | grep -v 'node_modules/' | grep -v '\.next/' | grep -v '\.nuxt/' | grep -v 'dist/' | grep -v 'build/' | grep -v '.turbo/' | grep -v '.cache/' | grep -Ev '(^|/)vitest\.config\.' | grep -Ev '(^|/)vite\.config\.' | grep -Ev '(^|/)jest\.config\.' | grep -Ev '(^|/)tsconfig\.' | grep -Ev '(^|/)eslint\.' | grep -Ev '(^|/)prettier\.' | grep -Ev '(^|/)tailwind\.' || true)
|
|
1389
1414
|
|
|
1415
|
+
# Strip PROJECT_SUBDIR prefix from new-TS-file paths when running in a subdirectory.
|
|
1416
|
+
# git diff returns repo-root relative paths, but after cd "$PROJECT_SUBDIR"
|
|
1417
|
+
# the test-pairing [ -f ] checks below need subdirectory-relative paths.
|
|
1418
|
+
# (Mirrors the v0.14.31+ Gate 5b fix at lines 1603-1608; Gate 5a was missing it.)
|
|
1419
|
+
if [ -n "$PROJECT_SUBDIR" ] && [ "$PROJECT_SUBDIR" != "." ] && [ -n "$NEW_TS_FILES" ]; then
|
|
1420
|
+
NEW_TS_FILES=$(echo "$NEW_TS_FILES" | sed "s|^${PROJECT_SUBDIR}/||")
|
|
1421
|
+
fi
|
|
1422
|
+
|
|
1390
1423
|
if [ -n "$NEW_TS_FILES" ]; then
|
|
1391
1424
|
BLOCKED_TS_FILES=0
|
|
1392
1425
|
for src_file in $NEW_TS_FILES; do
|
|
@@ -1749,8 +1782,8 @@ else
|
|
|
1749
1782
|
if [ "${CURRENT_LANG}" != "unknown" ] && [ -n "$ADAPTER_FILE" ]; then
|
|
1750
1783
|
if source "$ADAPTER_FILE" 2>/dev/null; then
|
|
1751
1784
|
# TypeScript: vitest with smart test selection
|
|
1752
|
-
if [ "$CURRENT_LANG" = "typescript" ] && [ -f "package.json" ]; then
|
|
1753
|
-
if npx vitest --version >/dev/null 2>&1; then
|
|
1785
|
+
if [ "$CURRENT_LANG" = "typescript" ] && { [ -f "package.json" ] || [ -f "$PROJECT_ROOT/package.json" ]; }; then
|
|
1786
|
+
if run_without_git_context npx vitest --version >/dev/null 2>&1; then
|
|
1754
1787
|
# Count changed test files to decide strategy
|
|
1755
1788
|
CHANGED_TEST_FILE_COUNT=$(echo "$CHANGED_TEST_FILES" | grep -c '.' || echo "0")
|
|
1756
1789
|
|
|
@@ -1766,7 +1799,7 @@ else
|
|
|
1766
1799
|
if [ "$CHANGED_TEST_FILE_COUNT" -gt 20 ] 2>/dev/null; then
|
|
1767
1800
|
# Too many test files changed → full run
|
|
1768
1801
|
echo "Too many test files changed ($CHANGED_TEST_FILE_COUNT), running full test suite..."
|
|
1769
|
-
TESTS_OUTPUT=$(npx vitest run --coverage 2>&1)
|
|
1802
|
+
TESTS_OUTPUT=$(run_without_git_context npx vitest run --coverage 2>&1)
|
|
1770
1803
|
TESTS_EXIT_CODE=$?
|
|
1771
1804
|
echo "$TESTS_OUTPUT" | head -60
|
|
1772
1805
|
if [ "$TESTS_EXIT_CODE" -ne 0 ]; then
|
|
@@ -1781,7 +1814,7 @@ else
|
|
|
1781
1814
|
SMART_TARGETS=$(echo "$CHANGED_TEST_FILES" | tr '\n' ' ')
|
|
1782
1815
|
echo "Running ${CHANGED_TEST_FILE_COUNT} changed test file(s) with coverage..."
|
|
1783
1816
|
echo " Target(s): $(echo "$CHANGED_TEST_FILES" | tr '\n' ' ')"
|
|
1784
|
-
TESTS_OUTPUT=$(npx vitest run --coverage $SMART_TARGETS 2>&1)
|
|
1817
|
+
TESTS_OUTPUT=$(run_without_git_context npx vitest run --coverage $SMART_TARGETS 2>&1)
|
|
1785
1818
|
TESTS_EXIT_CODE=$?
|
|
1786
1819
|
echo "$TESTS_OUTPUT" | head -60
|
|
1787
1820
|
if [ "$TESTS_EXIT_CODE" -ne 0 ]; then
|
|
@@ -1825,7 +1858,7 @@ else
|
|
|
1825
1858
|
if [ -n "$RELATED_TESTS" ]; then
|
|
1826
1859
|
echo " Found related test(s): $(echo "$RELATED_TESTS" | tr '\n' ' ')"
|
|
1827
1860
|
PARTIAL_TEST_RUN=true # Mark as partial run for coverage check
|
|
1828
|
-
TESTS_OUTPUT=$(npx vitest run --coverage $RELATED_TESTS 2>&1)
|
|
1861
|
+
TESTS_OUTPUT=$(run_without_git_context npx vitest run --coverage $RELATED_TESTS 2>&1)
|
|
1829
1862
|
TESTS_EXIT_CODE=$?
|
|
1830
1863
|
echo "$TESTS_OUTPUT" | head -60
|
|
1831
1864
|
if [ "$TESTS_EXIT_CODE" -ne 0 ]; then
|
|
@@ -1838,7 +1871,7 @@ else
|
|
|
1838
1871
|
else
|
|
1839
1872
|
# No related tests found → full run is safest
|
|
1840
1873
|
echo " No related test files found, running full test suite..."
|
|
1841
|
-
TESTS_OUTPUT=$(npx vitest run --coverage 2>&1)
|
|
1874
|
+
TESTS_OUTPUT=$(run_without_git_context npx vitest run --coverage 2>&1)
|
|
1842
1875
|
TESTS_EXIT_CODE=$?
|
|
1843
1876
|
echo "$TESTS_OUTPUT" | head -60
|
|
1844
1877
|
if [ "$TESTS_EXIT_CODE" -ne 0 ]; then
|
|
@@ -1857,7 +1890,7 @@ else
|
|
|
1857
1890
|
fi
|
|
1858
1891
|
else
|
|
1859
1892
|
# Fallback: run_tests without coverage
|
|
1860
|
-
TESTS_OUTPUT=$(run_tests 2>&1)
|
|
1893
|
+
TESTS_OUTPUT=$(run_without_git_context run_tests 2>&1)
|
|
1861
1894
|
TESTS_EXIT_CODE=$?
|
|
1862
1895
|
echo "$TESTS_OUTPUT" | head -50
|
|
1863
1896
|
if [ "$TESTS_EXIT_CODE" -ne 0 ]; then
|
|
@@ -1868,15 +1901,21 @@ else
|
|
|
1868
1901
|
fi
|
|
1869
1902
|
else
|
|
1870
1903
|
# Non-TypeScript: standard adapter test flow
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1904
|
+
# Skip for Python — tests already run with coverage in the coverage section below.
|
|
1905
|
+
# Running them here first without coverage doubles pre-commit time for no benefit.
|
|
1906
|
+
if [ "$CURRENT_LANG" = "python" ]; then
|
|
1907
|
+
echo "ℹ️ Python tests deferred to coverage section (single pass)."
|
|
1908
|
+
else
|
|
1909
|
+
TESTS_OUTPUT=$(run_without_git_context run_tests 2>&1)
|
|
1910
|
+
TESTS_EXIT_CODE=$?
|
|
1911
|
+
echo "$TESTS_OUTPUT" | head -50
|
|
1912
|
+
if [ "$TESTS_EXIT_CODE" -ne 0 ]; then
|
|
1913
|
+
echo ""
|
|
1914
|
+
echo "❌ BLOCKED - Tests FAILED"
|
|
1915
|
+
exit 1
|
|
1916
|
+
else
|
|
1917
|
+
echo "✅ PASSED - Unit tests passed."
|
|
1918
|
+
fi
|
|
1880
1919
|
fi
|
|
1881
1920
|
fi
|
|
1882
1921
|
else
|
|
@@ -2056,6 +2095,45 @@ else
|
|
|
2056
2095
|
echo "⚠️ coverage/lcov.info not found. Ensure dart test --coverage was run."
|
|
2057
2096
|
fi
|
|
2058
2097
|
;;
|
|
2098
|
+
"powershell")
|
|
2099
|
+
COVERAGE_ENFORCED=true
|
|
2100
|
+
if [ -f "coverage.xml" ]; then
|
|
2101
|
+
COVERAGE_RESULT=$(node -e "
|
|
2102
|
+
try {
|
|
2103
|
+
const fs = require('fs');
|
|
2104
|
+
const xml = fs.readFileSync('coverage.xml', 'utf8');
|
|
2105
|
+
const counters = [...xml.matchAll(/<counter\b([^>]*)\/?\s*>/gi)]
|
|
2106
|
+
.map((match) => Object.fromEntries(
|
|
2107
|
+
[...match[1].matchAll(/([\w:-]+)\s*=\s*(['\"])(.*?)\2/g)]
|
|
2108
|
+
.map((attribute) => [attribute[1].toUpperCase(), attribute[3]])
|
|
2109
|
+
))
|
|
2110
|
+
.filter((counter) => counter.TYPE === 'LINE');
|
|
2111
|
+
const reportCounter = counters.at(-1);
|
|
2112
|
+
const missed = Number(reportCounter?.MISSED);
|
|
2113
|
+
const covered = Number(reportCounter?.COVERED);
|
|
2114
|
+
const total = missed + covered;
|
|
2115
|
+
if (!Number.isFinite(missed) || !Number.isFinite(covered) || missed < 0 || covered < 0 || total <= 0) {
|
|
2116
|
+
throw new Error('invalid LINE counter');
|
|
2117
|
+
}
|
|
2118
|
+
const percentage = (covered / total) * 100;
|
|
2119
|
+
console.log((percentage < 80 ? 'below' : 'pass') + '|' + Math.round(percentage));
|
|
2120
|
+
} catch (error) { console.log('parse_error'); }
|
|
2121
|
+
" 2>/dev/null) || COVERAGE_RESULT="parse_error"
|
|
2122
|
+
if [ "$COVERAGE_RESULT" != "parse_error" ] && [ -n "$COVERAGE_RESULT" ]; then
|
|
2123
|
+
COVERAGE_STATUS=${COVERAGE_RESULT%%|*}
|
|
2124
|
+
COVERAGE_PERCENT=${COVERAGE_RESULT#*|}
|
|
2125
|
+
if [ "$COVERAGE_STATUS" = "below" ]; then
|
|
2126
|
+
echo "❌ BLOCKED - PowerShell coverage ${COVERAGE_PERCENT}% below 80% threshold"
|
|
2127
|
+
exit 1
|
|
2128
|
+
fi
|
|
2129
|
+
echo "PowerShell coverage: ${COVERAGE_PERCENT}% ✅ (≥ 80%)"
|
|
2130
|
+
else
|
|
2131
|
+
echo "⚠️ Could not parse PowerShell coverage from coverage.xml"
|
|
2132
|
+
fi
|
|
2133
|
+
else
|
|
2134
|
+
echo "⚠️ coverage.xml not found. Ensure Pester coverage was run."
|
|
2135
|
+
fi
|
|
2136
|
+
;;
|
|
2059
2137
|
*)
|
|
2060
2138
|
# Default: attempt to parse coverage from common formats regardless of language
|
|
2061
2139
|
COVERAGE_ENFORCED=true
|
|
@@ -2165,6 +2243,15 @@ elif has_project_lang "powershell" 2>/dev/null || [ "$PROJECT_LANG" = "powershel
|
|
|
2165
2243
|
echo " ⏭️ SKIPPED - Architecture validation (no tool for PowerShell)"
|
|
2166
2244
|
|
|
2167
2245
|
else
|
|
2246
|
+
run_archlint() (
|
|
2247
|
+
cd "$PROJECT_ROOT" || exit 1
|
|
2248
|
+
if [ "$ARCHLINT_KIND" = "npx" ]; then
|
|
2249
|
+
npx @archlinter/cli "$@"
|
|
2250
|
+
else
|
|
2251
|
+
"$ARCHLINT_BIN" "$@"
|
|
2252
|
+
fi
|
|
2253
|
+
)
|
|
2254
|
+
|
|
2168
2255
|
# Check if architecture config exists (use $PROJECT_ROOT — hook may have cd'd to a subdir via ARCH-03)
|
|
2169
2256
|
if [ -f "$PROJECT_ROOT/architecture.yaml" ] || [ -f "$PROJECT_ROOT/.architecturerc" ] || [ -f "$PROJECT_ROOT/.archlint.yaml" ]; then
|
|
2170
2257
|
# Different architecture tools per language
|
|
@@ -2172,12 +2259,14 @@ else
|
|
|
2172
2259
|
case "$CURRENT_LANG" in
|
|
2173
2260
|
"typescript")
|
|
2174
2261
|
# Try local npx install first (devDependencies), fall back to global
|
|
2175
|
-
if
|
|
2176
|
-
|
|
2262
|
+
if [ -x "$PROJECT_ROOT/node_modules/.bin/archlint" ]; then
|
|
2263
|
+
ARCHLINT_KIND="binary"
|
|
2264
|
+
ARCHLINT_BIN="$PROJECT_ROOT/node_modules/.bin/archlint"
|
|
2177
2265
|
elif command -v archlint >/dev/null 2>&1; then
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2266
|
+
ARCHLINT_KIND="binary"
|
|
2267
|
+
ARCHLINT_BIN="$(command -v archlint)"
|
|
2268
|
+
elif (cd "$PROJECT_ROOT" && npx @archlinter/cli --version) >/dev/null 2>&1; then
|
|
2269
|
+
ARCHLINT_KIND="npx"
|
|
2181
2270
|
else
|
|
2182
2271
|
echo " ❌ BLOCKED - archlint not installed"
|
|
2183
2272
|
echo " Install with: npm install --save-dev @archlinter/cli"
|
|
@@ -2186,17 +2275,17 @@ else
|
|
|
2186
2275
|
echo " Running archlint for TypeScript..."
|
|
2187
2276
|
# Use diff mode with baseline for ratchet (block only new violations)
|
|
2188
2277
|
# Fall back to raw scan if no baseline exists (new project, zero-tolerance)
|
|
2189
|
-
if [ -f "
|
|
2190
|
-
ARCHLINT_OUTPUT=$(
|
|
2278
|
+
if [ -f "$PROJECT_ROOT/.architecture-baseline.json" ]; then
|
|
2279
|
+
ARCHLINT_OUTPUT=$(run_archlint diff .architecture-baseline.json --fail-on high 2>&1)
|
|
2191
2280
|
else
|
|
2192
|
-
ARCHLINT_OUTPUT=$(
|
|
2281
|
+
ARCHLINT_OUTPUT=$(run_archlint scan . -f table --quiet 2>&1)
|
|
2193
2282
|
fi
|
|
2194
2283
|
ARCHLINT_EXIT=$?
|
|
2195
2284
|
echo "$ARCHLINT_OUTPUT" | tail -30
|
|
2196
2285
|
if [ "$ARCHLINT_EXIT" -ne 0 ]; then
|
|
2197
2286
|
echo ""
|
|
2198
2287
|
echo "❌ BLOCKED - Architecture violations detected"
|
|
2199
|
-
|
|
2288
|
+
if [ -f "$PROJECT_ROOT/.architecture-baseline.json" ]; then
|
|
2200
2289
|
echo "New architecture violations found (baseline ratchet mode)."
|
|
2201
2290
|
echo "Fix the new violations above, or update the baseline with: npx @archlinter/cli snapshot -o .architecture-baseline.json"
|
|
2202
2291
|
else
|
|
@@ -2205,7 +2294,7 @@ else
|
|
|
2205
2294
|
fi
|
|
2206
2295
|
exit 1
|
|
2207
2296
|
fi
|
|
2208
|
-
|
|
2297
|
+
if [ -f "$PROJECT_ROOT/.architecture-baseline.json" ]; then
|
|
2209
2298
|
echo " ✅ TypeScript architecture validation completed (baseline ratchet mode)."
|
|
2210
2299
|
else
|
|
2211
2300
|
echo " ✅ TypeScript architecture validation completed (zero-tolerance mode). no baseline found."
|
|
@@ -2276,7 +2365,7 @@ else
|
|
|
2276
2365
|
echo " Required configuration file (.archlint.yaml / architecture.yaml) is NOT found."
|
|
2277
2366
|
echo " Architecture Quality Gate requires explicit architecture constraints."
|
|
2278
2367
|
echo ""
|
|
2279
|
-
|
|
2368
|
+
echo " Quick fix: create .archlint.yaml with: npx archlint init --no-interactive"
|
|
2280
2369
|
echo " Or architecture.yaml for custom Clean Architecture layer definitions:"
|
|
2281
2370
|
echo " layers:"
|
|
2282
2371
|
echo " - name: api"
|
|
@@ -2451,13 +2540,20 @@ echo ""
|
|
|
2451
2540
|
|
|
2452
2541
|
# ============================================================================
|
|
2453
2542
|
# GATE 10: Semgrep SAST Security Scan
|
|
2454
|
-
# Extracted to gate-10.sh for maintainability
|
|
2543
|
+
# Extracted to gate-10.sh for maintainability
|
|
2455
2544
|
# v0.14.3+ (Issue #312): Skip if no code files changed
|
|
2545
|
+
# v0.18.5 (Issue #397): Fix gate status variable collision — SAST now sets GATE_10_STATUS
|
|
2456
2546
|
# ============================================================================
|
|
2457
2547
|
if any_changed_files_match "\.(ts|tsx|js|jsx|py|go|java|kt|cpp|c|swift|m|dart|sh)$"; then
|
|
2458
|
-
|
|
2548
|
+
if [ -f "$GATE_DIR/gate-10.sh" ]; then
|
|
2549
|
+
source "$GATE_DIR/gate-10.sh"
|
|
2550
|
+
else
|
|
2551
|
+
echo "⏭️ SKIPPED - SAST (gate-10.sh not found)"
|
|
2552
|
+
GATE_10_STATUS="SKIP"
|
|
2553
|
+
fi
|
|
2459
2554
|
else
|
|
2460
2555
|
echo "✅ PASSED - No code files changed, skipping SAST scan."
|
|
2556
|
+
GATE_10_STATUS="PASS"
|
|
2461
2557
|
fi
|
|
2462
2558
|
# ============================================================================
|
|
2463
2559
|
|
|
@@ -2561,7 +2657,7 @@ generate_quality_report() {
|
|
|
2561
2657
|
# ── Branch-level quality status file ─────────────────────────────────────────────
|
|
2562
2658
|
local STATUS_DIR=".xp-gate/quality-status"
|
|
2563
2659
|
local REPORT_FILE="${STATUS_DIR}/${BRANCH}.json"
|
|
2564
|
-
mkdir -p "$
|
|
2660
|
+
mkdir -p "$(dirname "$REPORT_FILE")"
|
|
2565
2661
|
|
|
2566
2662
|
cat > "$REPORT_FILE" << ENDJSON
|
|
2567
2663
|
{
|
|
@@ -2714,5 +2810,6 @@ if [ -n "$_wp_marker" ] && [ "$_wp_marker" != "/xp-gate-precommit-marker" ]; the
|
|
|
2714
2810
|
fi
|
|
2715
2811
|
|
|
2716
2812
|
generate_quality_report
|
|
2813
|
+
_QUALITY_REPORT_DONE=1
|
|
2717
2814
|
|
|
2718
|
-
exit 0
|
|
2815
|
+
exit 0
|
package/hooks/pre-push
CHANGED
|
@@ -867,116 +867,47 @@ if [ ! -f "$RESULT_FILE" ]; then
|
|
|
867
867
|
exit 1
|
|
868
868
|
fi
|
|
869
869
|
|
|
870
|
-
# Check
|
|
871
|
-
if ! command -v
|
|
870
|
+
# Check Node.js availability (MANDATORY - zero degradation)
|
|
871
|
+
if ! command -v node >/dev/null 2>&1; then
|
|
872
872
|
echo ""
|
|
873
873
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
874
874
|
echo " ❌ ENVIRONMENT ERROR - PUSH BLOCKED"
|
|
875
875
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
876
876
|
echo ""
|
|
877
|
-
echo "
|
|
877
|
+
echo "Node.js is NOT installed. Required for JSON validation."
|
|
878
878
|
echo ""
|
|
879
|
-
echo "Install
|
|
880
|
-
echo " apt-get install jq (Debian/Ubuntu)"
|
|
881
|
-
echo " brew install jq (macOS)"
|
|
882
|
-
echo " choco install jq (Windows)"
|
|
883
|
-
echo ""
|
|
884
|
-
echo "Zero degradation principle: Cannot proceed without jq."
|
|
879
|
+
echo "Install Node.js 18 or newer, then retry the push."
|
|
885
880
|
echo ""
|
|
886
881
|
exit 1
|
|
887
882
|
fi
|
|
888
883
|
|
|
889
|
-
#
|
|
890
|
-
|
|
891
|
-
# HEAD, its content references HEAD^ (the commit being reviewed), not HEAD.
|
|
892
|
-
CURRENT_COMMIT=$(git rev-parse HEAD)
|
|
893
|
-
if git diff-tree --no-commit-id --name-only -r HEAD 2>/dev/null | grep -qx "$RESULT_FILE"; then
|
|
894
|
-
# Result file is part of the latest commit — compare against the reviewed commit
|
|
895
|
-
EXPECTED_COMMIT=$(git rev-parse HEAD^ 2>/dev/null || echo "unknown")
|
|
896
|
-
else
|
|
897
|
-
# Result file is working-tree only — compare against current HEAD
|
|
898
|
-
EXPECTED_COMMIT="$CURRENT_COMMIT"
|
|
899
|
-
fi
|
|
884
|
+
# Walkthrough evidence must bind to the exact feature-branch HEAD being pushed.
|
|
885
|
+
EXPECTED_COMMIT=$(git rev-parse HEAD)
|
|
900
886
|
|
|
901
|
-
#
|
|
902
|
-
|
|
887
|
+
# Validate the complete evidence contract without interpolating JSON into shell.
|
|
888
|
+
HOOK_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
|
889
|
+
MW_VALIDATOR="$HOOK_DIR/lib/validate-code-walkthrough.cjs"
|
|
890
|
+
if [ ! -f "$MW_VALIDATOR" ]; then
|
|
903
891
|
echo ""
|
|
904
892
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
905
893
|
echo " ❌ RESULT FILE INVALID - PUSH BLOCKED"
|
|
906
894
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
907
895
|
echo ""
|
|
908
|
-
echo "
|
|
896
|
+
echo "Gate MW validator is missing: $MW_VALIDATOR"
|
|
909
897
|
echo ""
|
|
910
898
|
echo "Re-run: /delphi-review --mode code-walkthrough"
|
|
911
899
|
echo ""
|
|
912
900
|
exit 1
|
|
913
901
|
fi
|
|
914
902
|
|
|
915
|
-
RESULT_COMMIT=$(jq -r '.commit' "$RESULT_FILE")
|
|
916
|
-
RESULT_VERDICT=$(jq -r '.verdict' "$RESULT_FILE")
|
|
917
|
-
RESULT_EXPIRES=$(jq -r '.expires' "$RESULT_FILE")
|
|
918
|
-
RESULT_BRANCH=$(jq -r '.branch' "$RESULT_FILE")
|
|
919
903
|
CURRENT_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
920
|
-
|
|
921
|
-
# Branch match check (optional but recommended)
|
|
922
|
-
if [ "$RESULT_BRANCH" != "$CURRENT_BRANCH" ]; then
|
|
923
|
-
echo ""
|
|
924
|
-
echo "⚠️ WARNING: Result file is for different branch"
|
|
925
|
-
echo " Expected: $CURRENT_BRANCH"
|
|
926
|
-
echo " Found: $RESULT_BRANCH"
|
|
927
|
-
echo ""
|
|
928
|
-
echo "Proceeding with commit verification..."
|
|
929
|
-
fi
|
|
930
|
-
|
|
931
|
-
# Commit match check
|
|
932
|
-
if [ "$RESULT_COMMIT" != "$EXPECTED_COMMIT" ]; then
|
|
904
|
+
if ! VALIDATION_ERROR=$(node "$MW_VALIDATOR" "$RESULT_FILE" "$EXPECTED_COMMIT" "$CURRENT_BRANCH" "$CURRENT_TIME" 2>&1); then
|
|
933
905
|
echo ""
|
|
934
906
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
935
907
|
echo " ❌ RESULT FILE OUTDATED - PUSH BLOCKED"
|
|
936
908
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
937
909
|
echo ""
|
|
938
|
-
echo "
|
|
939
|
-
echo " Expected: $EXPECTED_COMMIT"
|
|
940
|
-
echo " Found: $RESULT_COMMIT"
|
|
941
|
-
echo ""
|
|
942
|
-
echo "Re-run: /delphi-review --mode code-walkthrough"
|
|
943
|
-
echo ""
|
|
944
|
-
exit 1
|
|
945
|
-
fi
|
|
946
|
-
|
|
947
|
-
# Verdict check
|
|
948
|
-
if [ "$RESULT_VERDICT" != "APPROVED" ]; then
|
|
949
|
-
echo ""
|
|
950
|
-
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
951
|
-
echo " ❌ GATE MW: CODE WALKTHROUGH NOT APPROVED - PUSH BLOCKED"
|
|
952
|
-
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
953
|
-
echo ""
|
|
954
|
-
echo "Verdict: $RESULT_VERDICT"
|
|
955
|
-
echo ""
|
|
956
|
-
|
|
957
|
-
# Show issues if available
|
|
958
|
-
ISSUES=$(jq -r '.issues[] | "- [\(.severity)] \(.description)"' "$RESULT_FILE" 2>/dev/null)
|
|
959
|
-
if [ -n "$ISSUES" ]; then
|
|
960
|
-
echo "Issues found:"
|
|
961
|
-
echo "$ISSUES"
|
|
962
|
-
echo ""
|
|
963
|
-
fi
|
|
964
|
-
|
|
965
|
-
echo "Fix issues and re-run: /delphi-review --mode code-walkthrough"
|
|
966
|
-
echo ""
|
|
967
|
-
exit 1
|
|
968
|
-
fi
|
|
969
|
-
|
|
970
|
-
# Expiration check
|
|
971
|
-
if [[ "$CURRENT_TIME" > "$RESULT_EXPIRES" ]]; then
|
|
972
|
-
echo ""
|
|
973
|
-
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
974
|
-
echo " ❌ RESULT FILE EXPIRED - PUSH BLOCKED"
|
|
975
|
-
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
976
|
-
echo ""
|
|
977
|
-
echo "Code walkthrough result has expired:"
|
|
978
|
-
echo " Expired: $RESULT_EXPIRES"
|
|
979
|
-
echo " Current: $CURRENT_TIME"
|
|
910
|
+
echo "$VALIDATION_ERROR"
|
|
980
911
|
echo ""
|
|
981
912
|
echo "Re-run: /delphi-review --mode code-walkthrough"
|
|
982
913
|
echo ""
|
|
@@ -996,16 +927,9 @@ echo "Files changed: $FILES_CHANGED"
|
|
|
996
927
|
echo "Lines: +$LINES_ADDED -$LINES_DELETED"
|
|
997
928
|
echo ""
|
|
998
929
|
echo "Code walkthrough result:"
|
|
999
|
-
echo " Commit: $
|
|
930
|
+
echo " Commit: $EXPECTED_COMMIT"
|
|
1000
931
|
echo " Verdict: APPROVED"
|
|
1001
|
-
echo " Expires: $RESULT_EXPIRES"
|
|
1002
|
-
|
|
1003
|
-
# Show confidence if available
|
|
1004
|
-
CONFIDENCE=$(jq -r '.confidence' "$RESULT_FILE" 2>/dev/null)
|
|
1005
|
-
if [ -n "$CONFIDENCE" ] && [ "$CONFIDENCE" != "null" ]; then
|
|
1006
|
-
echo " Confidence: $CONFIDENCE/10"
|
|
1007
|
-
fi
|
|
1008
932
|
|
|
1009
933
|
echo ""
|
|
1010
934
|
echo "Proceeding with push..."
|
|
1011
|
-
exit 0
|
|
935
|
+
exit 0
|