@ryuenn3123/agentic-senior-core 3.0.50 → 4.0.1
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 +3 -1
- package/.agent-context/prompts/research-design.md +165 -0
- package/.agent-context/review-checklists/pr-checklist.md +1 -0
- package/.agent-context/rules/api-docs.md +63 -47
- package/.agent-context/rules/architecture.md +133 -120
- package/.agent-context/rules/database-design.md +36 -18
- 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 -16
- package/.agent-context/rules/event-driven.md +35 -18
- package/.agent-context/rules/frontend-architecture.md +103 -76
- 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 -8
- package/.agent-context/rules/performance.md +32 -12
- package/.agent-context/rules/realtime.md +26 -9
- package/.agent-context/rules/security.md +39 -20
- package/.agent-context/rules/testing.md +36 -16
- package/AGENTS.md +21 -20
- package/README.md +10 -1
- package/lib/cli/commands/init.mjs +12 -0
- package/lib/cli/commands/upgrade.mjs +11 -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/research-dossier-migration.mjs +165 -0
- package/lib/cli/project-scaffolder/design-contract/sections/audits.mjs +96 -0
- package/lib/cli/project-scaffolder/design-contract/sections/conceptual-anchor.mjs +233 -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 +456 -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/research-dossier-validators.mjs +104 -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 +61 -896
- package/lib/cli/project-scaffolder/design-contract.mjs +151 -556
- package/lib/cli/project-scaffolder/prompt-builders.mjs +9 -0
- 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 +27 -2
- package/scripts/validate/coverage-checks.mjs +1 -1
- package/scripts/validate.mjs +94 -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/architecture/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.',
|
|
@@ -300,6 +301,12 @@ export const REQUIRED_UI_DESIGN_AUTOMATION_SNIPPETS = [
|
|
|
300
301
|
'Motion/Palette Decision',
|
|
301
302
|
'product categories are heuristics',
|
|
302
303
|
'perform live web research',
|
|
304
|
+
'research-design.md',
|
|
305
|
+
'researchDossier.metadata',
|
|
306
|
+
'researchVerifiedAt',
|
|
307
|
+
'freshnessWindowDays',
|
|
308
|
+
'Anti-repeat ledger',
|
|
309
|
+
'user-explicit redesign',
|
|
303
310
|
],
|
|
304
311
|
},
|
|
305
312
|
{
|
|
@@ -351,6 +358,24 @@ export const REQUIRED_UI_DESIGN_AUTOMATION_SNIPPETS = [
|
|
|
351
358
|
'A new dependency, package count, or vague performance concern is not a blocker by itself.',
|
|
352
359
|
'default component-kit styling without product rationale',
|
|
353
360
|
'genericity findings that cannot name the exact drift signal',
|
|
361
|
+
'research-design.md',
|
|
362
|
+
'Section 3-5 gates from `research-design.md`',
|
|
363
|
+
],
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
path: '.agent-context/prompts/research-design.md',
|
|
367
|
+
snippets: [
|
|
368
|
+
'# Research-Design Brief',
|
|
369
|
+
'## Section 3 — Category Code Identification',
|
|
370
|
+
'## Section 4 — Morphological Exploration',
|
|
371
|
+
'## Section 5 — Anchor Candidates',
|
|
372
|
+
'specificity self-test',
|
|
373
|
+
'uncomfortable combination',
|
|
374
|
+
'STRONG PASS',
|
|
375
|
+
'DISCARD',
|
|
376
|
+
'Anti-Repeat Ledger Gate',
|
|
377
|
+
'previousAnchors',
|
|
378
|
+
'previousMotionSignatures',
|
|
354
379
|
],
|
|
355
380
|
},
|
|
356
381
|
{
|
|
@@ -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,11 +152,13 @@ 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',
|
|
151
159
|
'scripts/generate-sbom.mjs',
|
|
152
160
|
'.agent-context/policies/llm-judge-threshold.json',
|
|
161
|
+
'.agent-context/prompts/research-design.md',
|
|
153
162
|
'mcp.json',
|
|
154
163
|
'AGENTS.md',
|
|
155
164
|
'CLAUDE.md',
|
|
@@ -358,7 +367,7 @@ async function validatePackageMetadata() {
|
|
|
358
367
|
console.log('\nChecking package metadata...');
|
|
359
368
|
|
|
360
369
|
const packageJson = JSON.parse(await readTextFile(PACKAGE_JSON_PATH));
|
|
361
|
-
const versionPattern =
|
|
370
|
+
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
371
|
|
|
363
372
|
if (typeof packageJson.version !== 'string' || !versionPattern.test(packageJson.version)) {
|
|
364
373
|
fail('package.json version must be a semantic version string');
|
|
@@ -495,6 +504,84 @@ async function validateDocumentationFlow() {
|
|
|
495
504
|
}
|
|
496
505
|
}
|
|
497
506
|
|
|
507
|
+
async function validateFileSizeAudit() {
|
|
508
|
+
console.log('\nChecking file size threshold (audit:file-size)...');
|
|
509
|
+
const report = runAuditFileSize();
|
|
510
|
+
|
|
511
|
+
if (report.passed) {
|
|
512
|
+
pass(`File size audit clean: ${report.scannedFileCount} files scanned, ${report.exemptedCount} exempted under @file-size-exception`);
|
|
513
|
+
} else {
|
|
514
|
+
for (const violation of report.violations) {
|
|
515
|
+
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.`);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
async function validateCacheLayerContractAudit() {
|
|
521
|
+
console.log('\nChecking cache layer contract (audit:cache-layer-contract)...');
|
|
522
|
+
const report = runCacheLayerContractAudit();
|
|
523
|
+
|
|
524
|
+
if (report.passed) {
|
|
525
|
+
pass(`Cache layer contract audit clean: ${report.providerCount} provider(s), ${report.fixtureCount} fixture(s), ${report.resultCount} result row(s)`);
|
|
526
|
+
} else {
|
|
527
|
+
for (const violation of report.violations) {
|
|
528
|
+
fail(`Cache layer contract violation [${violation.kind}]: ${violation.detail}`);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
async function validateRuleIdUniquenessAudit() {
|
|
534
|
+
console.log('\nChecking rule ID uniqueness (audit:rule-id-uniqueness)...');
|
|
535
|
+
const report = runRuleIdUniquenessAudit();
|
|
536
|
+
|
|
537
|
+
if (report.passed) {
|
|
538
|
+
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`);
|
|
539
|
+
} else {
|
|
540
|
+
for (const violation of report.violations) {
|
|
541
|
+
fail(`Rule ID violation [${violation.kind}] in ${violation.file}: ${violation.detail}`);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
async function validateReflectionCitationAudit() {
|
|
547
|
+
console.log('\nChecking reflection citations (audit:reflection-citations)...');
|
|
548
|
+
const report = runReflectionCitationAudit();
|
|
549
|
+
|
|
550
|
+
if (report.passed) {
|
|
551
|
+
pass(`Reflection citation audit clean: ${report.surfaceCount} surface(s), ${report.knownRuleIdCount} known rule ID(s)`);
|
|
552
|
+
} else {
|
|
553
|
+
for (const violation of report.violations) {
|
|
554
|
+
fail(`Reflection citation violation [${violation.kind}] in ${violation.file}: ${violation.detail}`);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
async function validateCachingScopeHygieneAudit() {
|
|
560
|
+
console.log('\nChecking caching scope hygiene (audit:caching-scope-hygiene)...');
|
|
561
|
+
const report = runCachingScopeHygieneAudit();
|
|
562
|
+
|
|
563
|
+
if (report.passed) {
|
|
564
|
+
pass(`Caching scope hygiene clean: ${report.surfaceCount} public surface(s), ${report.totalClaimCount} caching saving claim(s) all integration-scoped`);
|
|
565
|
+
} else {
|
|
566
|
+
for (const violation of report.violations) {
|
|
567
|
+
fail(`Caching scope hygiene violation [${violation.kind}] in ${violation.file}:${violation.line}: ${violation.detail}`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
async function validateReleaseBundleAudit() {
|
|
573
|
+
console.log('\nChecking release benchmark bundle (audit:release-bundle)...');
|
|
574
|
+
const report = runReleaseBundleAudit();
|
|
575
|
+
|
|
576
|
+
if (report.passed) {
|
|
577
|
+
pass(`Release bundle integrity clean: ${report.artifactCount} artifact(s), release_target=${report.releaseTarget}, release_status=${report.releaseStatus}`);
|
|
578
|
+
} else {
|
|
579
|
+
for (const violation of report.violations) {
|
|
580
|
+
fail(`Release bundle violation [${violation.kind}]: ${violation.detail}`);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
498
585
|
async function validateMcpConfiguration() {
|
|
499
586
|
console.log('\nChecking MCP configuration...');
|
|
500
587
|
|
|
@@ -576,6 +663,12 @@ async function main() {
|
|
|
576
663
|
await validateHumanWritingGovernance(coverageValidationContext);
|
|
577
664
|
await validateInstructionAdapters(coverageValidationContext);
|
|
578
665
|
await validateSkillPurgeSurface(coverageValidationContext);
|
|
666
|
+
await validateCacheLayerContractAudit();
|
|
667
|
+
await validateReflectionCitationAudit();
|
|
668
|
+
await validateCachingScopeHygieneAudit();
|
|
669
|
+
await validateReleaseBundleAudit();
|
|
670
|
+
await validateFileSizeAudit();
|
|
671
|
+
await validateRuleIdUniquenessAudit();
|
|
579
672
|
|
|
580
673
|
console.log('\n===============================================');
|
|
581
674
|
console.log(' RESULTS');
|