@ryuenn3123/agentic-senior-core 3.0.49 → 4.0.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/.agent-context/prompts/bootstrap-design.md +2 -1
- package/.agent-context/review-checklists/pr-checklist.md +1 -0
- package/.agent-context/rules/api-docs.md +63 -45
- package/.agent-context/rules/architecture.md +133 -118
- package/.agent-context/rules/database-design.md +36 -16
- package/.agent-context/rules/docker-runtime.md +66 -43
- package/.agent-context/rules/efficiency-vs-hype.md +38 -17
- package/.agent-context/rules/error-handling.md +35 -14
- package/.agent-context/rules/event-driven.md +35 -18
- package/.agent-context/rules/frontend-architecture.md +103 -74
- package/.agent-context/rules/git-workflow.md +81 -197
- package/.agent-context/rules/microservices.md +42 -41
- package/.agent-context/rules/naming-conv.md +27 -6
- package/.agent-context/rules/performance.md +32 -10
- package/.agent-context/rules/realtime.md +26 -9
- package/.agent-context/rules/security.md +39 -19
- package/.agent-context/rules/testing.md +36 -15
- package/AGENTS.md +9 -9
- package/README.md +10 -1
- package/lib/cli/commands/init.mjs +1 -0
- package/lib/cli/compiler.mjs +1 -0
- package/lib/cli/detector/constants.mjs +135 -0
- package/lib/cli/detector/design-evidence/collector.mjs +256 -0
- package/lib/cli/detector/design-evidence/constants.mjs +39 -0
- package/lib/cli/detector/design-evidence/file-traversal.mjs +83 -0
- package/lib/cli/detector/design-evidence/structured-attribute-evidence.mjs +117 -0
- package/lib/cli/detector/design-evidence/summary.mjs +109 -0
- package/lib/cli/detector/design-evidence/utility-helpers.mjs +122 -0
- package/lib/cli/detector/design-evidence.mjs +25 -610
- package/lib/cli/detector/stack-detection.mjs +243 -0
- package/lib/cli/detector/ui-signals.mjs +150 -0
- package/lib/cli/detector/workspace-scan.mjs +177 -0
- package/lib/cli/detector.mjs +20 -688
- package/lib/cli/memory-continuity.mjs +1 -0
- package/lib/cli/project-scaffolder/design-contract/sections/audits.mjs +96 -0
- package/lib/cli/project-scaffolder/design-contract/sections/conceptual-anchor.mjs +116 -0
- package/lib/cli/project-scaffolder/design-contract/sections/execution-handoff.mjs +211 -0
- package/lib/cli/project-scaffolder/design-contract/seed-signals.mjs +79 -0
- package/lib/cli/project-scaffolder/design-contract/signal-vocab.mjs +64 -0
- package/lib/cli/project-scaffolder/design-contract/validation/anchor-validators.mjs +222 -0
- package/lib/cli/project-scaffolder/design-contract/validation/audit-validators.mjs +117 -0
- package/lib/cli/project-scaffolder/design-contract/validation/completeness.mjs +83 -0
- package/lib/cli/project-scaffolder/design-contract/validation/execution-validators.mjs +328 -0
- package/lib/cli/project-scaffolder/design-contract/validation/helpers.mjs +8 -0
- package/lib/cli/project-scaffolder/design-contract/validation/structural-validators.mjs +79 -0
- package/lib/cli/project-scaffolder/design-contract/validation/system-validators.mjs +256 -0
- package/lib/cli/project-scaffolder/design-contract/validation.mjs +59 -896
- package/lib/cli/project-scaffolder/design-contract.mjs +147 -557
- package/mcp.json +30 -9
- package/package.json +17 -2
- package/scripts/audit-cache-layer-contract.mjs +258 -0
- package/scripts/audit-caching-scope-hygiene.mjs +263 -0
- package/scripts/audit-file-size.mjs +219 -0
- package/scripts/audit-reflection-citations.mjs +163 -0
- package/scripts/audit-release-bundle.mjs +170 -0
- package/scripts/audit-rule-id-uniqueness.mjs +313 -0
- package/scripts/benchmark-evidence-bundle.mjs +1 -0
- package/scripts/build-release-benchmark-bundle.mjs +204 -0
- package/scripts/context-triggered-audit.mjs +1 -0
- package/scripts/documentation-boundary-audit.mjs +1 -0
- package/scripts/explain-on-demand-audit.mjs +2 -1
- package/scripts/frontend-usability-audit.mjs +10 -10
- package/scripts/llm-judge/checklist-loader.mjs +45 -0
- package/scripts/llm-judge/constants.mjs +66 -0
- package/scripts/llm-judge/diff-collection.mjs +74 -0
- package/scripts/llm-judge/prompting.mjs +78 -0
- package/scripts/llm-judge/providers.mjs +111 -0
- package/scripts/llm-judge/verdict.mjs +134 -0
- package/scripts/llm-judge.mjs +21 -482
- package/scripts/mcp-server/tool-registry.mjs +55 -0
- package/scripts/mcp-server/tools.mjs +137 -1
- package/scripts/migrate-rule-format/id-prefix-table.mjs +37 -0
- package/scripts/migrate-rule-format/parse-legacy.mjs +180 -0
- package/scripts/migrate-rule-format/render-new.mjs +169 -0
- package/scripts/migrate-rule-format/roundtrip-validate.mjs +89 -0
- package/scripts/migrate-rule-format.mjs +192 -0
- package/scripts/release-gate/constants.mjs +1 -1
- package/scripts/release-gate/static-checks.mjs +1 -1
- package/scripts/rules-guardian-audit.mjs +5 -2
- package/scripts/single-source-lazy-loading-audit.mjs +2 -1
- package/scripts/ui-design-judge/git-input.mjs +3 -0
- package/scripts/validate/config.mjs +3 -2
- package/scripts/validate/coverage-checks.mjs +1 -1
- package/scripts/validate.mjs +93 -1
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @ts-check
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* scripts/migrate-rule-format.mjs
|
|
6
|
+
*
|
|
7
|
+
* Phase 1 Task 1.2 migration helper. Converts a legacy v3 rule file into the
|
|
8
|
+
* canonical v4 format defined in docs/plan/format-spec.md. Output is written
|
|
9
|
+
* to a `.candidate.md` sibling so the human migrator can review the diff
|
|
10
|
+
* before replacing the original.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* node scripts/migrate-rule-format.mjs <path-to-rule-file>
|
|
14
|
+
* node scripts/migrate-rule-format.mjs <path> --json
|
|
15
|
+
* node scripts/migrate-rule-format.mjs <path> --apply
|
|
16
|
+
*
|
|
17
|
+
* Flags:
|
|
18
|
+
* --json Print the structured report to stdout (JSON only, no human prose).
|
|
19
|
+
* --apply Overwrite the source file with the rendered v4 content. Default
|
|
20
|
+
* behavior writes a .candidate.md file and leaves the source alone.
|
|
21
|
+
*
|
|
22
|
+
* The helper does not commit, does not stage, and does not call any network.
|
|
23
|
+
*
|
|
24
|
+
* Exit codes:
|
|
25
|
+
* 0 — rendered cleanly + roundtrip overlap >= 95%
|
|
26
|
+
* 1 — roundtrip overlap below threshold OR parser warnings present
|
|
27
|
+
* 2 — input path missing or filename not in the locked prefix table
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
31
|
+
import { basename, dirname, resolve } from 'node:path';
|
|
32
|
+
import { fileURLToPath } from 'node:url';
|
|
33
|
+
|
|
34
|
+
import { countTokens } from '../benchmarks/token-usage/lib/token-counter.mjs';
|
|
35
|
+
import { getPrefixEntry } from './migrate-rule-format/id-prefix-table.mjs';
|
|
36
|
+
import { parseLegacyRuleFile } from './migrate-rule-format/parse-legacy.mjs';
|
|
37
|
+
import { renderNewFormat } from './migrate-rule-format/render-new.mjs';
|
|
38
|
+
import { roundtripSubstanceCheck } from './migrate-rule-format/roundtrip-validate.mjs';
|
|
39
|
+
|
|
40
|
+
const SCRIPT_FILE_PATH = fileURLToPath(import.meta.url);
|
|
41
|
+
const REPOSITORY_ROOT = resolve(dirname(SCRIPT_FILE_PATH), '..');
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @param {string} ruleFileAbsolutePath
|
|
45
|
+
* @returns {Promise<{
|
|
46
|
+
* sourcePath: string,
|
|
47
|
+
* filename: string,
|
|
48
|
+
* prefix: string,
|
|
49
|
+
* sectionAssignments: Array<{ sectionTitle: string, sectionId: string, itemCount: number }>,
|
|
50
|
+
* warnings: string[],
|
|
51
|
+
* roundtrip: ReturnType<typeof roundtripSubstanceCheck>,
|
|
52
|
+
* tokenSavings: { original: number, rendered: number, deltaPercent: number },
|
|
53
|
+
* candidatePath: string,
|
|
54
|
+
* rendered: string,
|
|
55
|
+
* }>}
|
|
56
|
+
*/
|
|
57
|
+
export async function migrateOneRuleFile(ruleFileAbsolutePath) {
|
|
58
|
+
const filename = basename(ruleFileAbsolutePath);
|
|
59
|
+
const prefixEntry = getPrefixEntry(filename);
|
|
60
|
+
const originalSource = readFileSync(ruleFileAbsolutePath, 'utf8');
|
|
61
|
+
const parsed = parseLegacyRuleFile(originalSource);
|
|
62
|
+
const { rendered, sectionAssignments, warnings } = renderNewFormat(prefixEntry, parsed);
|
|
63
|
+
const roundtrip = roundtripSubstanceCheck(originalSource, rendered);
|
|
64
|
+
|
|
65
|
+
const originalTokenCount = await countTokens(originalSource, 'openai', 'gpt-4o-2024-08-06');
|
|
66
|
+
const renderedTokenCount = await countTokens(rendered, 'openai', 'gpt-4o-2024-08-06');
|
|
67
|
+
const tokenSavings = {
|
|
68
|
+
original: originalTokenCount.token_count,
|
|
69
|
+
rendered: renderedTokenCount.token_count,
|
|
70
|
+
deltaPercent: Math.round(
|
|
71
|
+
((renderedTokenCount.token_count - originalTokenCount.token_count) / originalTokenCount.token_count) * 10000,
|
|
72
|
+
) / 100,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const candidatePath = ruleFileAbsolutePath.replace(/\.md$/, '.candidate.md');
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
sourcePath: ruleFileAbsolutePath,
|
|
79
|
+
filename,
|
|
80
|
+
prefix: prefixEntry.prefix,
|
|
81
|
+
sectionAssignments,
|
|
82
|
+
warnings,
|
|
83
|
+
roundtrip,
|
|
84
|
+
tokenSavings,
|
|
85
|
+
candidatePath,
|
|
86
|
+
rendered,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function formatHumanReport(report) {
|
|
91
|
+
const lines = [];
|
|
92
|
+
lines.push('=================================================');
|
|
93
|
+
lines.push(` migrate-rule-format: ${report.filename}`);
|
|
94
|
+
lines.push('=================================================');
|
|
95
|
+
lines.push(` Prefix : ${report.prefix}`);
|
|
96
|
+
lines.push(` Section count : ${report.sectionAssignments.length}`);
|
|
97
|
+
lines.push(` Token (orig) : ${report.tokenSavings.original}`);
|
|
98
|
+
lines.push(` Token (new) : ${report.tokenSavings.rendered}`);
|
|
99
|
+
const sign = report.tokenSavings.deltaPercent <= 0 ? '' : '+';
|
|
100
|
+
lines.push(` Token delta : ${sign}${report.tokenSavings.deltaPercent}%`);
|
|
101
|
+
lines.push(` Roundtrip : ${report.roundtrip.passed ? 'PASS' : 'FAIL'} (${report.roundtrip.overlapPercent}% overlap, min ${report.roundtrip.minimumRequired}%)`);
|
|
102
|
+
lines.push('');
|
|
103
|
+
|
|
104
|
+
lines.push(' Sections assigned:');
|
|
105
|
+
for (const assignment of report.sectionAssignments) {
|
|
106
|
+
lines.push(` ${assignment.sectionId.padEnd(12)} ${assignment.itemCount} items ${assignment.sectionTitle}`);
|
|
107
|
+
}
|
|
108
|
+
lines.push('');
|
|
109
|
+
|
|
110
|
+
if (report.warnings.length > 0) {
|
|
111
|
+
lines.push(' Warnings:');
|
|
112
|
+
for (const warning of report.warnings) {
|
|
113
|
+
lines.push(` ! ${warning}`);
|
|
114
|
+
}
|
|
115
|
+
lines.push('');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (report.roundtrip.lostWords.length > 0) {
|
|
119
|
+
lines.push(` Substantial words present in original, missing in rendered (${report.roundtrip.lostWords.length} of distinct):`);
|
|
120
|
+
for (const word of report.roundtrip.lostWords.slice(0, 30)) {
|
|
121
|
+
lines.push(` - ${word}`);
|
|
122
|
+
}
|
|
123
|
+
if (report.roundtrip.lostWords.length > 30) {
|
|
124
|
+
lines.push(` ... ${report.roundtrip.lostWords.length - 30} more (see JSON report)`);
|
|
125
|
+
}
|
|
126
|
+
lines.push('');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return lines.join('\n');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function runCli() {
|
|
133
|
+
const argv = process.argv.slice(2);
|
|
134
|
+
if (argv.length === 0) {
|
|
135
|
+
console.error('usage: node scripts/migrate-rule-format.mjs <path-to-rule-file> [--json] [--apply]');
|
|
136
|
+
process.exit(2);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const inputPath = argv[0];
|
|
140
|
+
const jsonMode = argv.includes('--json');
|
|
141
|
+
const applyMode = argv.includes('--apply');
|
|
142
|
+
const ruleFileAbsolutePath = resolve(REPOSITORY_ROOT, inputPath);
|
|
143
|
+
|
|
144
|
+
let report;
|
|
145
|
+
try {
|
|
146
|
+
report = await migrateOneRuleFile(ruleFileAbsolutePath);
|
|
147
|
+
} catch (migrationError) {
|
|
148
|
+
if (jsonMode) {
|
|
149
|
+
process.stdout.write(`${JSON.stringify({ passed: false, error: migrationError.message }, null, 2)}\n`);
|
|
150
|
+
} else {
|
|
151
|
+
console.error(`migrate-rule-format failed: ${migrationError.message}`);
|
|
152
|
+
}
|
|
153
|
+
process.exit(2);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (applyMode) {
|
|
157
|
+
writeFileSync(ruleFileAbsolutePath, report.rendered, 'utf8');
|
|
158
|
+
} else {
|
|
159
|
+
writeFileSync(report.candidatePath, report.rendered, 'utf8');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const exitCode = report.roundtrip.passed && report.warnings.length === 0 ? 0 : 1;
|
|
163
|
+
|
|
164
|
+
if (jsonMode) {
|
|
165
|
+
process.stdout.write(`${JSON.stringify({
|
|
166
|
+
passed: report.roundtrip.passed,
|
|
167
|
+
filename: report.filename,
|
|
168
|
+
prefix: report.prefix,
|
|
169
|
+
sectionCount: report.sectionAssignments.length,
|
|
170
|
+
sectionAssignments: report.sectionAssignments,
|
|
171
|
+
warnings: report.warnings,
|
|
172
|
+
roundtrip: report.roundtrip,
|
|
173
|
+
tokenSavings: report.tokenSavings,
|
|
174
|
+
candidatePath: applyMode ? report.sourcePath : report.candidatePath,
|
|
175
|
+
mode: applyMode ? 'apply' : 'candidate',
|
|
176
|
+
}, null, 2)}\n`);
|
|
177
|
+
process.exit(exitCode);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log(formatHumanReport(report));
|
|
181
|
+
if (applyMode) {
|
|
182
|
+
console.log(` APPLIED in place: ${report.sourcePath}`);
|
|
183
|
+
} else {
|
|
184
|
+
console.log(` Candidate written: ${report.candidatePath}`);
|
|
185
|
+
console.log(' Review the diff, then re-run with --apply when satisfied.');
|
|
186
|
+
}
|
|
187
|
+
process.exit(exitCode);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (import.meta.url === `file://${process.argv[1].replace(/\\/g, '/')}` || process.argv[1].endsWith('migrate-rule-format.mjs')) {
|
|
191
|
+
runCli();
|
|
192
|
+
}
|
|
@@ -7,7 +7,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
7
7
|
const __dirname = dirname(__filename);
|
|
8
8
|
|
|
9
9
|
export const REPOSITORY_ROOT = resolve(__dirname, '..', '..');
|
|
10
|
-
export const VERSION_PATTERN =
|
|
10
|
+
export const VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
11
11
|
export const FRONTEND_AUDIT_SCRIPT_PATH = 'scripts/frontend-usability-audit.mjs';
|
|
12
12
|
export const UI_DESIGN_JUDGE_SCRIPT_PATH = 'scripts/ui-design-judge.mjs';
|
|
13
13
|
export const DOCUMENTATION_BOUNDARY_AUDIT_SCRIPT_PATH = 'scripts/documentation-boundary-audit.mjs';
|
|
@@ -47,7 +47,7 @@ export function runStaticReleaseChecks(results, diagnostics) {
|
|
|
47
47
|
if (!releaseVersion || !VERSION_PATTERN.test(releaseVersion)) {
|
|
48
48
|
pushResult(results, false, 'version-semver', `Invalid package version: ${String(releaseVersion)}`);
|
|
49
49
|
} else {
|
|
50
|
-
pushResult(results, true, 'version-semver', `Version ${releaseVersion} matches
|
|
50
|
+
pushResult(results, true, 'version-semver', `Version ${releaseVersion} matches SemVer format`);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
const changelogContent = readText(changelogPath);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// @file-size-exception: Pre-existing audit script (sequential rules-guardian checks); planned for split in Phase 1.
|
|
3
4
|
/**
|
|
4
5
|
* rules-guardian-audit.mjs
|
|
5
6
|
*
|
|
@@ -36,7 +37,7 @@ const SUPPORTED_WORKFLOWS = new Set([
|
|
|
36
37
|
const CORE_PATTERN_SIGNALS = [
|
|
37
38
|
{
|
|
38
39
|
pattern: 'layer-separation',
|
|
39
|
-
snippet: '## Layer Boundaries (Mandatory)',
|
|
40
|
+
snippet: '## ARCH-009: Layer Boundaries (Mandatory)',
|
|
40
41
|
},
|
|
41
42
|
{
|
|
42
43
|
pattern: 'agent-decides-topology',
|
|
@@ -53,7 +54,7 @@ const CORE_PATTERN_SIGNALS = [
|
|
|
53
54
|
];
|
|
54
55
|
|
|
55
56
|
const REQUIRED_ARCHITECTURE_RULE_SNIPPETS = [
|
|
56
|
-
'## Rules as Guardian (Cross-Session Consistency)',
|
|
57
|
+
'## ARCH-005: Rules as Guardian (Cross-Session Consistency)',
|
|
57
58
|
'Session handoff must include active architecture contract summary.',
|
|
58
59
|
'Detect drift before changing runtime choices, topology, public contracts, or core patterns.',
|
|
59
60
|
'Direction changes require explicit user confirmation before applying changes.',
|
|
@@ -100,6 +101,7 @@ function runGitFileQuery(commandArguments) {
|
|
|
100
101
|
cwd: REPOSITORY_ROOT,
|
|
101
102
|
encoding: 'utf8',
|
|
102
103
|
maxBuffer: 1024 * 1024,
|
|
104
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
103
105
|
});
|
|
104
106
|
|
|
105
107
|
return parseGitFileList(rawOutput);
|
|
@@ -114,6 +116,7 @@ function runGitRawQuery(commandArguments) {
|
|
|
114
116
|
cwd: REPOSITORY_ROOT,
|
|
115
117
|
encoding: 'utf8',
|
|
116
118
|
maxBuffer: 1024 * 1024,
|
|
119
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
117
120
|
});
|
|
118
121
|
} catch {
|
|
119
122
|
return '';
|
|
@@ -55,7 +55,7 @@ const STACK_CATALOG_FILE_NAMES = [
|
|
|
55
55
|
const MAX_EAGER_STACK_MENTIONS = 4;
|
|
56
56
|
|
|
57
57
|
const REQUIRED_ARCHITECTURE_RULE_SNIPPETS = [
|
|
58
|
-
'## Single Source of Truth and Lazy Rule Loading',
|
|
58
|
+
'## ARCH-007: Single Source of Truth and Lazy Rule Loading',
|
|
59
59
|
'Canonical rule source is AGENTS.md.',
|
|
60
60
|
'Load global domain rules lazily based on touched scope.',
|
|
61
61
|
'Do not create or load stack-specific governance adapters as the baseline.',
|
|
@@ -121,6 +121,7 @@ function runGitFileQuery(commandArguments) {
|
|
|
121
121
|
cwd: REPOSITORY_ROOT,
|
|
122
122
|
encoding: 'utf8',
|
|
123
123
|
maxBuffer: 1024 * 1024,
|
|
124
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
124
125
|
});
|
|
125
126
|
|
|
126
127
|
return parseGitFileList(rawOutput);
|
|
@@ -62,6 +62,7 @@ export function collectPullRequestDiff() {
|
|
|
62
62
|
cwd: REPOSITORY_ROOT,
|
|
63
63
|
encoding: /** @type {'utf-8'} */ ('utf-8'),
|
|
64
64
|
maxBuffer: 1024 * 1024 * 8,
|
|
65
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
65
66
|
});
|
|
66
67
|
} catch {
|
|
67
68
|
try {
|
|
@@ -70,6 +71,7 @@ export function collectPullRequestDiff() {
|
|
|
70
71
|
cwd: REPOSITORY_ROOT,
|
|
71
72
|
encoding: /** @type {'utf-8'} */ ('utf-8'),
|
|
72
73
|
maxBuffer: 1024 * 1024 * 8,
|
|
74
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
73
75
|
});
|
|
74
76
|
} catch {
|
|
75
77
|
return '';
|
|
@@ -103,6 +105,7 @@ export function collectChangedFiles() {
|
|
|
103
105
|
cwd: REPOSITORY_ROOT,
|
|
104
106
|
encoding: /** @type {'utf-8'} */ ('utf-8'),
|
|
105
107
|
maxBuffer: 1024 * 1024 * 2,
|
|
108
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
106
109
|
});
|
|
107
110
|
return output.split(/\r?\n/u).map((filePath) => filePath.trim()).filter(Boolean);
|
|
108
111
|
} catch {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @file-size-exception: Pure config table aggregation (snippet expectation lists); deferred to Phase 1 split.
|
|
1
2
|
export const ALLOWED_SEVERITIES = new Set(['critical', 'high', 'medium', 'low']);
|
|
2
3
|
export const THIN_ADAPTER_PATHS = [
|
|
3
4
|
'CLAUDE.md',
|
|
@@ -22,7 +23,7 @@ export const REQUIRED_HUMAN_WRITING_SNIPPETS = [
|
|
|
22
23
|
{
|
|
23
24
|
path: '.agent-context/rules/api-docs.md',
|
|
24
25
|
snippets: [
|
|
25
|
-
'## Human Writing Standard (Mandatory)',
|
|
26
|
+
'## API-006: Human Writing Standard (Mandatory)',
|
|
26
27
|
'This applies to documentation, release notes, onboarding text, review summaries, and agent-facing explanations.',
|
|
27
28
|
'Style baseline findings are advisory by default and must not block endpoint-change commits that already include accurate docs/spec updates.',
|
|
28
29
|
'Write formal project docs in English by default',
|
|
@@ -195,7 +196,7 @@ export const REQUIRED_UNIVERSAL_SOP_SNIPPETS = [
|
|
|
195
196
|
{
|
|
196
197
|
path: '.agent-context/rules/architecture.md',
|
|
197
198
|
snippets: [
|
|
198
|
-
'## Universal SOP Baseline (Mandatory)',
|
|
199
|
+
'## ARCH-003: Universal SOP Baseline (Mandatory)',
|
|
199
200
|
'Root `README.md` must exist for every fresh or existing project',
|
|
200
201
|
'`docs/doc-index.md` must exist whenever `docs/` exists',
|
|
201
202
|
'Security and testing are non-negotiable baseline requirements.',
|
|
@@ -397,7 +397,7 @@ export async function validateInstructionAdapters(context) {
|
|
|
397
397
|
const instructionFootprintLimits = [
|
|
398
398
|
{ path: 'AGENTS.md', maxLines: 180 },
|
|
399
399
|
{ path: '.agent-context/prompts/bootstrap-design.md', maxLines: 180 },
|
|
400
|
-
{ path: '.agent-context/rules/frontend-architecture.md', maxLines:
|
|
400
|
+
{ path: '.agent-context/rules/frontend-architecture.md', maxLines: 140 },
|
|
401
401
|
];
|
|
402
402
|
|
|
403
403
|
for (const requiredBootstrapReceiptSnippet of requiredBootstrapReceiptSnippets) {
|
package/scripts/validate.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// @file-size-exception: Pre-existing 600 LOC main validator orchestrator; planned for split in Phase 1 governance refactor.
|
|
3
4
|
/**
|
|
4
5
|
* validate.mjs — Repository Integrity Validator
|
|
5
6
|
*
|
|
@@ -17,6 +18,12 @@ import { readdir, readFile, stat } from 'node:fs/promises';
|
|
|
17
18
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
18
19
|
import { fileURLToPath } from 'node:url';
|
|
19
20
|
import { ALLOWED_SEVERITIES } from './validate/config.mjs';
|
|
21
|
+
import { runCacheLayerContractAudit } from './audit-cache-layer-contract.mjs';
|
|
22
|
+
import { runCachingScopeHygieneAudit } from './audit-caching-scope-hygiene.mjs';
|
|
23
|
+
import { runAuditFileSize } from './audit-file-size.mjs';
|
|
24
|
+
import { runReflectionCitationAudit } from './audit-reflection-citations.mjs';
|
|
25
|
+
import { runReleaseBundleAudit } from './audit-release-bundle.mjs';
|
|
26
|
+
import { runRuleIdUniquenessAudit } from './audit-rule-id-uniqueness.mjs';
|
|
20
27
|
import {
|
|
21
28
|
validateDependencyFreshnessAutomationCoverage,
|
|
22
29
|
validateDetectionTransparencyCoverage,
|
|
@@ -145,6 +152,7 @@ async function validateRequiredFiles() {
|
|
|
145
152
|
'scripts/rules-guardian-audit.mjs',
|
|
146
153
|
'scripts/explain-on-demand-audit.mjs',
|
|
147
154
|
'scripts/single-source-lazy-loading-audit.mjs',
|
|
155
|
+
'scripts/audit-cache-layer-contract.mjs',
|
|
148
156
|
'scripts/sync-thin-adapters.mjs',
|
|
149
157
|
'scripts/v3-purge-audit.mjs',
|
|
150
158
|
'scripts/release-gate.mjs',
|
|
@@ -358,7 +366,7 @@ async function validatePackageMetadata() {
|
|
|
358
366
|
console.log('\nChecking package metadata...');
|
|
359
367
|
|
|
360
368
|
const packageJson = JSON.parse(await readTextFile(PACKAGE_JSON_PATH));
|
|
361
|
-
const versionPattern =
|
|
369
|
+
const versionPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
362
370
|
|
|
363
371
|
if (typeof packageJson.version !== 'string' || !versionPattern.test(packageJson.version)) {
|
|
364
372
|
fail('package.json version must be a semantic version string');
|
|
@@ -495,6 +503,84 @@ async function validateDocumentationFlow() {
|
|
|
495
503
|
}
|
|
496
504
|
}
|
|
497
505
|
|
|
506
|
+
async function validateFileSizeAudit() {
|
|
507
|
+
console.log('\nChecking file size threshold (audit:file-size)...');
|
|
508
|
+
const report = runAuditFileSize();
|
|
509
|
+
|
|
510
|
+
if (report.passed) {
|
|
511
|
+
pass(`File size audit clean: ${report.scannedFileCount} files scanned, ${report.exemptedCount} exempted under @file-size-exception`);
|
|
512
|
+
} else {
|
|
513
|
+
for (const violation of report.violations) {
|
|
514
|
+
fail(`File exceeds ${report.threshold} LOC: ${violation.filePath} (${violation.lineCount} LOC). Split into focused submodules or declare a justified // @file-size-exception: <reason> marker in the first 5 lines.`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
async function validateCacheLayerContractAudit() {
|
|
520
|
+
console.log('\nChecking cache layer contract (audit:cache-layer-contract)...');
|
|
521
|
+
const report = runCacheLayerContractAudit();
|
|
522
|
+
|
|
523
|
+
if (report.passed) {
|
|
524
|
+
pass(`Cache layer contract audit clean: ${report.providerCount} provider(s), ${report.fixtureCount} fixture(s), ${report.resultCount} result row(s)`);
|
|
525
|
+
} else {
|
|
526
|
+
for (const violation of report.violations) {
|
|
527
|
+
fail(`Cache layer contract violation [${violation.kind}]: ${violation.detail}`);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
async function validateRuleIdUniquenessAudit() {
|
|
533
|
+
console.log('\nChecking rule ID uniqueness (audit:rule-id-uniqueness)...');
|
|
534
|
+
const report = runRuleIdUniquenessAudit();
|
|
535
|
+
|
|
536
|
+
if (report.passed) {
|
|
537
|
+
pass(`Rule ID audit clean: ${report.migratedFileCount} migrated rule file(s), ${report.skippedFileCount} pre-migration, ${report.knownSectionIdCount} section ID(s), ${report.refMentionCount} [REF:] mention(s) all resolve`);
|
|
538
|
+
} else {
|
|
539
|
+
for (const violation of report.violations) {
|
|
540
|
+
fail(`Rule ID violation [${violation.kind}] in ${violation.file}: ${violation.detail}`);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
async function validateReflectionCitationAudit() {
|
|
546
|
+
console.log('\nChecking reflection citations (audit:reflection-citations)...');
|
|
547
|
+
const report = runReflectionCitationAudit();
|
|
548
|
+
|
|
549
|
+
if (report.passed) {
|
|
550
|
+
pass(`Reflection citation audit clean: ${report.surfaceCount} surface(s), ${report.knownRuleIdCount} known rule ID(s)`);
|
|
551
|
+
} else {
|
|
552
|
+
for (const violation of report.violations) {
|
|
553
|
+
fail(`Reflection citation violation [${violation.kind}] in ${violation.file}: ${violation.detail}`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
async function validateCachingScopeHygieneAudit() {
|
|
559
|
+
console.log('\nChecking caching scope hygiene (audit:caching-scope-hygiene)...');
|
|
560
|
+
const report = runCachingScopeHygieneAudit();
|
|
561
|
+
|
|
562
|
+
if (report.passed) {
|
|
563
|
+
pass(`Caching scope hygiene clean: ${report.surfaceCount} public surface(s), ${report.totalClaimCount} caching saving claim(s) all integration-scoped`);
|
|
564
|
+
} else {
|
|
565
|
+
for (const violation of report.violations) {
|
|
566
|
+
fail(`Caching scope hygiene violation [${violation.kind}] in ${violation.file}:${violation.line}: ${violation.detail}`);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
async function validateReleaseBundleAudit() {
|
|
572
|
+
console.log('\nChecking release benchmark bundle (audit:release-bundle)...');
|
|
573
|
+
const report = runReleaseBundleAudit();
|
|
574
|
+
|
|
575
|
+
if (report.passed) {
|
|
576
|
+
pass(`Release bundle integrity clean: ${report.artifactCount} artifact(s), release_target=${report.releaseTarget}, release_status=${report.releaseStatus}`);
|
|
577
|
+
} else {
|
|
578
|
+
for (const violation of report.violations) {
|
|
579
|
+
fail(`Release bundle violation [${violation.kind}]: ${violation.detail}`);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
498
584
|
async function validateMcpConfiguration() {
|
|
499
585
|
console.log('\nChecking MCP configuration...');
|
|
500
586
|
|
|
@@ -576,6 +662,12 @@ async function main() {
|
|
|
576
662
|
await validateHumanWritingGovernance(coverageValidationContext);
|
|
577
663
|
await validateInstructionAdapters(coverageValidationContext);
|
|
578
664
|
await validateSkillPurgeSurface(coverageValidationContext);
|
|
665
|
+
await validateCacheLayerContractAudit();
|
|
666
|
+
await validateReflectionCitationAudit();
|
|
667
|
+
await validateCachingScopeHygieneAudit();
|
|
668
|
+
await validateReleaseBundleAudit();
|
|
669
|
+
await validateFileSizeAudit();
|
|
670
|
+
await validateRuleIdUniquenessAudit();
|
|
579
671
|
|
|
580
672
|
console.log('\n===============================================');
|
|
581
673
|
console.log(' RESULTS');
|