@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.
Files changed (84) hide show
  1. package/.agent-context/prompts/bootstrap-design.md +2 -1
  2. package/.agent-context/review-checklists/pr-checklist.md +1 -0
  3. package/.agent-context/rules/api-docs.md +63 -45
  4. package/.agent-context/rules/architecture.md +133 -118
  5. package/.agent-context/rules/database-design.md +36 -16
  6. package/.agent-context/rules/docker-runtime.md +66 -43
  7. package/.agent-context/rules/efficiency-vs-hype.md +38 -17
  8. package/.agent-context/rules/error-handling.md +35 -14
  9. package/.agent-context/rules/event-driven.md +35 -18
  10. package/.agent-context/rules/frontend-architecture.md +103 -74
  11. package/.agent-context/rules/git-workflow.md +81 -197
  12. package/.agent-context/rules/microservices.md +42 -41
  13. package/.agent-context/rules/naming-conv.md +27 -6
  14. package/.agent-context/rules/performance.md +32 -10
  15. package/.agent-context/rules/realtime.md +26 -9
  16. package/.agent-context/rules/security.md +39 -19
  17. package/.agent-context/rules/testing.md +36 -15
  18. package/AGENTS.md +9 -9
  19. package/README.md +10 -1
  20. package/lib/cli/commands/init.mjs +1 -0
  21. package/lib/cli/compiler.mjs +1 -0
  22. package/lib/cli/detector/constants.mjs +135 -0
  23. package/lib/cli/detector/design-evidence/collector.mjs +256 -0
  24. package/lib/cli/detector/design-evidence/constants.mjs +39 -0
  25. package/lib/cli/detector/design-evidence/file-traversal.mjs +83 -0
  26. package/lib/cli/detector/design-evidence/structured-attribute-evidence.mjs +117 -0
  27. package/lib/cli/detector/design-evidence/summary.mjs +109 -0
  28. package/lib/cli/detector/design-evidence/utility-helpers.mjs +122 -0
  29. package/lib/cli/detector/design-evidence.mjs +25 -610
  30. package/lib/cli/detector/stack-detection.mjs +243 -0
  31. package/lib/cli/detector/ui-signals.mjs +150 -0
  32. package/lib/cli/detector/workspace-scan.mjs +177 -0
  33. package/lib/cli/detector.mjs +20 -688
  34. package/lib/cli/memory-continuity.mjs +1 -0
  35. package/lib/cli/project-scaffolder/design-contract/sections/audits.mjs +96 -0
  36. package/lib/cli/project-scaffolder/design-contract/sections/conceptual-anchor.mjs +116 -0
  37. package/lib/cli/project-scaffolder/design-contract/sections/execution-handoff.mjs +211 -0
  38. package/lib/cli/project-scaffolder/design-contract/seed-signals.mjs +79 -0
  39. package/lib/cli/project-scaffolder/design-contract/signal-vocab.mjs +64 -0
  40. package/lib/cli/project-scaffolder/design-contract/validation/anchor-validators.mjs +222 -0
  41. package/lib/cli/project-scaffolder/design-contract/validation/audit-validators.mjs +117 -0
  42. package/lib/cli/project-scaffolder/design-contract/validation/completeness.mjs +83 -0
  43. package/lib/cli/project-scaffolder/design-contract/validation/execution-validators.mjs +328 -0
  44. package/lib/cli/project-scaffolder/design-contract/validation/helpers.mjs +8 -0
  45. package/lib/cli/project-scaffolder/design-contract/validation/structural-validators.mjs +79 -0
  46. package/lib/cli/project-scaffolder/design-contract/validation/system-validators.mjs +256 -0
  47. package/lib/cli/project-scaffolder/design-contract/validation.mjs +59 -896
  48. package/lib/cli/project-scaffolder/design-contract.mjs +147 -557
  49. package/mcp.json +30 -9
  50. package/package.json +17 -2
  51. package/scripts/audit-cache-layer-contract.mjs +258 -0
  52. package/scripts/audit-caching-scope-hygiene.mjs +263 -0
  53. package/scripts/audit-file-size.mjs +219 -0
  54. package/scripts/audit-reflection-citations.mjs +163 -0
  55. package/scripts/audit-release-bundle.mjs +170 -0
  56. package/scripts/audit-rule-id-uniqueness.mjs +313 -0
  57. package/scripts/benchmark-evidence-bundle.mjs +1 -0
  58. package/scripts/build-release-benchmark-bundle.mjs +204 -0
  59. package/scripts/context-triggered-audit.mjs +1 -0
  60. package/scripts/documentation-boundary-audit.mjs +1 -0
  61. package/scripts/explain-on-demand-audit.mjs +2 -1
  62. package/scripts/frontend-usability-audit.mjs +10 -10
  63. package/scripts/llm-judge/checklist-loader.mjs +45 -0
  64. package/scripts/llm-judge/constants.mjs +66 -0
  65. package/scripts/llm-judge/diff-collection.mjs +74 -0
  66. package/scripts/llm-judge/prompting.mjs +78 -0
  67. package/scripts/llm-judge/providers.mjs +111 -0
  68. package/scripts/llm-judge/verdict.mjs +134 -0
  69. package/scripts/llm-judge.mjs +21 -482
  70. package/scripts/mcp-server/tool-registry.mjs +55 -0
  71. package/scripts/mcp-server/tools.mjs +137 -1
  72. package/scripts/migrate-rule-format/id-prefix-table.mjs +37 -0
  73. package/scripts/migrate-rule-format/parse-legacy.mjs +180 -0
  74. package/scripts/migrate-rule-format/render-new.mjs +169 -0
  75. package/scripts/migrate-rule-format/roundtrip-validate.mjs +89 -0
  76. package/scripts/migrate-rule-format.mjs +192 -0
  77. package/scripts/release-gate/constants.mjs +1 -1
  78. package/scripts/release-gate/static-checks.mjs +1 -1
  79. package/scripts/rules-guardian-audit.mjs +5 -2
  80. package/scripts/single-source-lazy-loading-audit.mjs +2 -1
  81. package/scripts/ui-design-judge/git-input.mjs +3 -0
  82. package/scripts/validate/config.mjs +3 -2
  83. package/scripts/validate/coverage-checks.mjs +1 -1
  84. 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 = /^\d+\.\d+\.\d+$/;
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 x.y.z format`);
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: 110 },
400
+ { path: '.agent-context/rules/frontend-architecture.md', maxLines: 140 },
401
401
  ];
402
402
 
403
403
  for (const requiredBootstrapReceiptSnippet of requiredBootstrapReceiptSnippets) {
@@ -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 = /^\d+\.\d+\.\d+$/;
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');