@event4u/agent-config 9.17.0 → 9.18.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +122 -171
- package/dist/agent-src/commands/challenge-me/vision.md +5 -0
- package/dist/agent-src/commands/evals/rule-compliance-audit.json +46 -0
- package/dist/agent-src/commands/feature/plan.md +20 -0
- package/dist/agent-src/commands/feature/roadmap.md +19 -0
- package/dist/agent-src/commands/pr/create.md +29 -0
- package/dist/agent-src/commands/roadmap/create.md +29 -0
- package/dist/agent-src/commands/rule-compliance-audit.md +28 -5
- package/dist/agent-src/contexts/execution/plan-confidence-gate.md +133 -0
- package/dist/agent-src/rules/active-remediation.md +2 -0
- package/dist/agent-src/rules/architecture.md +2 -0
- package/dist/agent-src/rules/augment-edit-discipline.md +2 -0
- package/dist/agent-src/rules/brand-consistency.md +10 -62
- package/dist/agent-src/rules/brand-source-of-truth.md +62 -11
- package/dist/agent-src/rules/broken-access-control.md +4 -0
- package/dist/agent-src/rules/cli-output-handling.md +5 -0
- package/dist/agent-src/rules/code-comment-discipline.md +3 -0
- package/dist/agent-src/rules/commit-conventions.md +2 -0
- package/dist/agent-src/rules/cross-source-consistency.md +3 -0
- package/dist/agent-src/rules/design-fidelity.md +2 -0
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +2 -0
- package/dist/agent-src/rules/docker-commands.md +4 -0
- package/dist/agent-src/rules/domain-adoption-policy.md +2 -0
- package/dist/agent-src/rules/domain-safety-disclaimer.md +5 -2
- package/dist/agent-src/rules/engineering-safety-floor.md +2 -0
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +9 -0
- package/dist/agent-src/rules/image-likeness-and-rights.md +4 -0
- package/dist/agent-src/rules/improve-before-implement.md +4 -0
- package/dist/agent-src/rules/laravel-routing.md +4 -0
- package/dist/agent-src/rules/legal-safety-floor.md +3 -0
- package/dist/agent-src/rules/lethal-trifecta-guard.md +3 -0
- package/dist/agent-src/rules/media-governance-routing.md +5 -0
- package/dist/agent-src/rules/minimal-safe-diff.md +2 -0
- package/dist/agent-src/rules/no-roadmap-references.md +2 -0
- package/dist/agent-src/rules/notes-first-reasoning.md +2 -0
- package/dist/agent-src/rules/php-coding.md +2 -0
- package/dist/agent-src/rules/prefer-enums-over-literals.md +3 -0
- package/dist/agent-src/rules/provider-lifecycle-discipline.md +5 -0
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +2 -0
- package/dist/agent-src/rules/roadmap-progress-sync.md +2 -0
- package/dist/agent-src/rules/rule-type-governance.md +2 -0
- package/dist/agent-src/rules/scale-discipline.md +3 -0
- package/dist/agent-src/rules/secret-vcs-guard.md +2 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +9 -2
- package/dist/agent-src/rules/senior-engineering-discipline.md +6 -0
- package/dist/agent-src/rules/skill-quality.md +2 -0
- package/dist/agent-src/rules/source-confidentiality.md +4 -0
- package/dist/agent-src/rules/source-discovery-gate.md +3 -0
- package/dist/agent-src/rules/source-of-truth.md +2 -0
- package/dist/agent-src/rules/strategy-safety-floor.md +2 -0
- package/dist/agent-src/rules/think-before-action.md +5 -0
- package/dist/agent-src/rules/ui-audit-gate.md +2 -0
- package/dist/agent-src/skills/design-intelligence/SKILL.md +6 -180
- package/dist/agent-src/skills/design-intelligence/references/context-and-registers.md +100 -0
- package/dist/agent-src/skills/design-intelligence/references/integration-mapping.md +91 -0
- package/dist/agent-src/skills/design-review/SKILL.md +4 -141
- package/dist/agent-src/skills/design-review/references/review-communication.md +92 -0
- package/dist/agent-src/skills/design-review/references/verification-automation.md +61 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +8 -27
- package/dist/agent-src/skills/existing-ui-audit/references/anti-slop-cross-reference.md +31 -0
- package/dist/agent-src/skills/fe-design/SKILL.md +4 -289
- package/dist/agent-src/skills/fe-design/references/design-patterns.md +227 -0
- package/dist/agent-src/skills/fe-design/references/design-read-and-memory.md +75 -0
- package/dist/agent-src/skills/roadmap-management/SKILL.md +10 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +12 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +12 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/_fix_lane.ts +52 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +8 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/design.ts +6 -0
- package/dist/agent-src/templates/scripts/work_engine/intent/classify.ts +56 -4
- package/dist/cli/registry.js +2 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/chunk-AIIUU73N.js +2040 -0
- package/dist/cli-delegate/{chunk-IMSXPRVH.js → chunk-ELMFOQPW.js} +11 -0
- package/dist/cli-delegate/chunk-LF2KKJ4Q.js +183 -0
- package/dist/cli-delegate/cmd_explain.js +36 -36
- package/dist/cli-delegate/cmd_export.js +1 -1
- package/dist/cli-delegate/cmd_handoff.js +203 -2228
- package/dist/cli-delegate/cmd_prune.js +1 -1
- package/dist/cli-delegate/cmd_route_audit.js +249 -0
- package/dist/cli-delegate/cmd_route_explain.js +285 -0
- package/dist/cli-delegate/cmd_settings_migrate.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +1 -1
- package/dist/cli-delegate/cmd_uninstall.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +57 -57
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/install/install.mjs +11 -0
- package/dist/mcp/registry-manifest.json +1 -1
- package/dist/mcp/server.json +2 -2
- package/dist/router.json +1 -1
- package/dist/server/schemas/settings.js +12 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/package.json +1 -1
- package/src/agent-src/templates/scripts/work_engine/directives/ui/_fix_lane.ts +52 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +8 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/design.ts +6 -0
- package/src/agent-src/templates/scripts/work_engine/intent/classify.ts +56 -4
- package/src/config/agent-settings.template.yml +46 -0
- package/src/config/evaluator-budgets.json +3 -3
- package/src/config/gate-coverage.yml +44 -0
- package/src/scripts/_cli/cmd_explain.ts +41 -36
- package/src/scripts/_cli/cmd_route_audit.ts +290 -0
- package/src/scripts/_cli/cmd_route_explain.ts +269 -0
- package/src/scripts/_dispatch.bash +23 -0
- package/src/scripts/_lib/git_env.ts +40 -0
- package/src/scripts/_lib/md_table.ts +27 -0
- package/src/scripts/_lib/planning_settings.ts +43 -0
- package/src/scripts/_lib/release_highlights.ts +279 -0
- package/src/scripts/_lib/router_match.ts +301 -0
- package/src/scripts/annotate_r1_outcomes.ts +258 -0
- package/src/scripts/check_completion_review.ts +1322 -0
- package/src/scripts/check_release_highlights.ts +60 -152
- package/src/scripts/compile_router.ts +20 -3
- package/src/scripts/dispatch_r2_reviewer.ts +969 -0
- package/src/scripts/eval_ui_triviality.ts +156 -0
- package/src/scripts/lint_abstraction_thresholds.ts +3 -1
- package/src/scripts/lint_plan_risk_register.ts +931 -0
- package/src/scripts/lint_trigger_collisions.ts +257 -0
- package/src/scripts/release.ts +47 -13
- package/src/scripts/router_telemetry.ts +16 -290
- package/src/scripts/schemas/rule.schema.json +10 -0
- package/src/server/schemas/settings.ts +18 -0
|
@@ -11,8 +11,14 @@
|
|
|
11
11
|
* commit types + rule/schema diffs, honest-null markers, removed public
|
|
12
12
|
* surface) and FAILS when a populated generated category meets a `_none_`
|
|
13
13
|
* curated field. It blocks the contradiction only — a human still writes the
|
|
14
|
-
* prose
|
|
15
|
-
*
|
|
14
|
+
* prose, and a filled field is never judged for quality.
|
|
15
|
+
*
|
|
16
|
+
* The classifier lives in `_lib/release_highlights.ts` and is shared with the
|
|
17
|
+
* generator, which now pre-fills each substantiated label instead of writing
|
|
18
|
+
* `_none_` everywhere. That removes the guaranteed first-run red (9.17.0,
|
|
19
|
+
* 9.18.0) without blunting this gate: a machine-written `_none_` was never the
|
|
20
|
+
* failure it was built for — a HUMAN writing one was (9.13.0, 9.14.0), and that
|
|
21
|
+
* still fails here. An unrewritten derived line warns; it never blocks.
|
|
16
22
|
*
|
|
17
23
|
* Derivation is deliberately conservative (documented per-label below):
|
|
18
24
|
* a false red makes every release annoying; a miss only returns the head to
|
|
@@ -25,96 +31,41 @@
|
|
|
25
31
|
* `--from` defaults to the latest reachable release tag before `--to`
|
|
26
32
|
* (default HEAD). Exit codes: 0 plausible · 1 contradiction · 2 usage error.
|
|
27
33
|
*/
|
|
28
|
-
import { spawnSync } from 'node:child_process';
|
|
29
34
|
import * as fs from 'node:fs';
|
|
30
35
|
import * as path from 'node:path';
|
|
31
36
|
import process from 'node:process';
|
|
32
37
|
import { fileURLToPath } from 'node:url';
|
|
33
38
|
|
|
39
|
+
import {
|
|
40
|
+
HEAD_LABELS,
|
|
41
|
+
HEAD_NONE,
|
|
42
|
+
type SpanCommit,
|
|
43
|
+
collect_span_commits,
|
|
44
|
+
derive_categories,
|
|
45
|
+
parse_git_log,
|
|
46
|
+
previous_release_tag,
|
|
47
|
+
stale_draft_labels,
|
|
48
|
+
} from './_lib/release_highlights.js';
|
|
34
49
|
import { extract_changelog_section } from './_lib/release_material.js';
|
|
35
50
|
|
|
36
51
|
const _HERE = fileURLToPath(import.meta.url);
|
|
37
52
|
const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
38
53
|
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
subject: string;
|
|
55
|
-
body: string;
|
|
56
|
-
/** `A` / `M` / `D` / `R…` status per touched path. */
|
|
57
|
-
files: ReadonlyArray<{ status: string; path: string }>;
|
|
58
|
-
breaking: boolean;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Public artefact trees whose deletions count as removed public surface. */
|
|
62
|
-
const _PUBLIC_SURFACE_PREFIXES = [
|
|
63
|
-
'src/skills/',
|
|
64
|
-
'src/rules/',
|
|
65
|
-
'src/agent-src/commands/',
|
|
66
|
-
'src/domains/',
|
|
67
|
-
];
|
|
68
|
-
|
|
69
|
-
const _RULE_OR_SCHEMA_PREFIXES = ['src/rules/', 'src/scripts/schemas/'];
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Derive evidence per curated label from the span. Rules per label:
|
|
73
|
-
*
|
|
74
|
-
* - Security and correctness: conventional scope matching /secur/i, or the
|
|
75
|
-
* whole word "security" in the subject.
|
|
76
|
-
* - Behaviour changes: breaking (`!` / BREAKING CHANGE) commits, diffs
|
|
77
|
-
* touching `src/rules/` or `src/scripts/schemas/`, and deletions under the
|
|
78
|
-
* public artefact trees (removed public surface).
|
|
79
|
-
* - Default changes + migration: subject carrying the whole word "default",
|
|
80
|
-
* "migration", or "migrate".
|
|
81
|
-
* - Honest nulls: subject or body carrying an "honest null" marker.
|
|
82
|
-
* - Known limitations: never derived — pure prose, not gate-checkable.
|
|
83
|
-
*/
|
|
84
|
-
export function derive_categories(commits: readonly SpanCommit[]): Record<string, string[]> {
|
|
85
|
-
const out: Record<string, string[]> = {};
|
|
86
|
-
for (const label of HEAD_LABELS) {
|
|
87
|
-
out[label] = [];
|
|
88
|
-
}
|
|
89
|
-
const evidence = (c: SpanCommit): string => `${c.sha.slice(0, 7)} ${c.subject}`;
|
|
90
|
-
for (const c of commits) {
|
|
91
|
-
const scopeMatch = /^\w+\(([^)]*)\)!?:/u.exec(c.subject);
|
|
92
|
-
const scope = scopeMatch ? scopeMatch[1]! : '';
|
|
93
|
-
if (/secur/iu.test(scope) || /\bsecurity\b/iu.test(c.subject)) {
|
|
94
|
-
out['Security and correctness']!.push(evidence(c));
|
|
95
|
-
}
|
|
96
|
-
const removedPublic = c.files.filter(
|
|
97
|
-
(f) => f.status.startsWith('D') && _PUBLIC_SURFACE_PREFIXES.some((p) => f.path.startsWith(p)),
|
|
98
|
-
);
|
|
99
|
-
const ruleOrSchema = c.files.some((f) =>
|
|
100
|
-
_RULE_OR_SCHEMA_PREFIXES.some((p) => f.path.startsWith(p)),
|
|
101
|
-
);
|
|
102
|
-
if (c.breaking || ruleOrSchema || removedPublic.length > 0) {
|
|
103
|
-
const detail =
|
|
104
|
-
removedPublic.length > 0
|
|
105
|
-
? `${evidence(c)} (removes ${removedPublic.map((f) => f.path).join(', ')})`
|
|
106
|
-
: evidence(c);
|
|
107
|
-
out['Behaviour changes']!.push(detail);
|
|
108
|
-
}
|
|
109
|
-
if (/\b(default|migration|migrate)\b/iu.test(c.subject)) {
|
|
110
|
-
out['Default changes + migration']!.push(evidence(c));
|
|
111
|
-
}
|
|
112
|
-
if (/honest[ -]null/iu.test(`${c.subject}\n${c.body}`)) {
|
|
113
|
-
out['Honest nulls']!.push(evidence(c));
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return out;
|
|
117
|
-
}
|
|
54
|
+
// Labels and derivation live in `_lib/release_highlights.ts` — ONE definition
|
|
55
|
+
// shared with the generator that pre-fills the head. They used to be duplicated
|
|
56
|
+
// here to keep the checker independent of the release pipeline, and that
|
|
57
|
+
// independence is what produced the defect this gate now guards against from
|
|
58
|
+
// the other side: the generator wrote `_none_` into every field while this
|
|
59
|
+
// checker rejected exactly that, so every release PR was red on its first run.
|
|
60
|
+
// Sharing the classifier does not blunt the gate — it still blocks the failure
|
|
61
|
+
// it was built for, a human editing a substantiated line back to `_none_`.
|
|
62
|
+
export {
|
|
63
|
+
HEAD_LABELS,
|
|
64
|
+
HEAD_NONE,
|
|
65
|
+
derive_categories,
|
|
66
|
+
parse_git_log as _parse_git_log,
|
|
67
|
+
type SpanCommit,
|
|
68
|
+
};
|
|
118
69
|
|
|
119
70
|
/**
|
|
120
71
|
* Parse the curated head from a changelog section body. Returns null when
|
|
@@ -162,74 +113,6 @@ export function highlight_contradictions(
|
|
|
162
113
|
return out;
|
|
163
114
|
}
|
|
164
115
|
|
|
165
|
-
// ─── git span collection (CLI only — the core above is pure) ────────────────
|
|
166
|
-
|
|
167
|
-
const _RECORD = '\u001e';
|
|
168
|
-
const _FIELD = '\u001f';
|
|
169
|
-
|
|
170
|
-
export function _parse_git_log(raw: string): SpanCommit[] {
|
|
171
|
-
const commits: SpanCommit[] = [];
|
|
172
|
-
for (const record of raw.split(_RECORD)) {
|
|
173
|
-
if (!record.trim()) {
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
const [head, ...restLines] = record.split('\n');
|
|
177
|
-
const parts = (head ?? '').split(_FIELD);
|
|
178
|
-
if (parts.length < 3) {
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
const sha = parts[0]!.trim();
|
|
182
|
-
const subject = parts[1]!;
|
|
183
|
-
const bodyParts: string[] = [parts.slice(2).join(_FIELD)];
|
|
184
|
-
const files: Array<{ status: string; path: string }> = [];
|
|
185
|
-
for (const line of restLines) {
|
|
186
|
-
const m = /^([A-Z]\d*)\t(.+)$/u.exec(line);
|
|
187
|
-
if (m) {
|
|
188
|
-
// Renames carry two paths; the last tab field is the new path,
|
|
189
|
-
// the first the old — record both so a rename out of a public
|
|
190
|
-
// tree still counts its origin path.
|
|
191
|
-
const segs = m[2]!.split('\t');
|
|
192
|
-
for (const p of segs) {
|
|
193
|
-
files.push({ status: m[1]!, path: p });
|
|
194
|
-
}
|
|
195
|
-
} else {
|
|
196
|
-
// %b is multi-line — everything that is not a name-status
|
|
197
|
-
// line is body continuation (honest-null markers live there).
|
|
198
|
-
bodyParts.push(line);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
const body = bodyParts.join('\n');
|
|
202
|
-
commits.push({
|
|
203
|
-
sha,
|
|
204
|
-
subject,
|
|
205
|
-
body,
|
|
206
|
-
files,
|
|
207
|
-
breaking: /^\w+(\([^)]*\))?!:/u.test(subject) || /BREAKING CHANGE/u.test(body),
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
return commits;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function _git(args: string[]): string {
|
|
214
|
-
const r = spawnSync('git', args, { encoding: 'utf-8', cwd: REPO_ROOT, maxBuffer: 64 * 1024 * 1024 });
|
|
215
|
-
if (r.status !== 0) {
|
|
216
|
-
process.stderr.write(`git ${args.join(' ')} failed: ${r.stderr}\n`);
|
|
217
|
-
process.exit(2);
|
|
218
|
-
}
|
|
219
|
-
return r.stdout;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function _collect_span(from: string, to: string): SpanCommit[] {
|
|
223
|
-
const raw = _git([
|
|
224
|
-
'log',
|
|
225
|
-
`${from}..${to}`,
|
|
226
|
-
'--no-merges',
|
|
227
|
-
'--name-status',
|
|
228
|
-
'--format=%x1e%H%x1f%s%x1f%b',
|
|
229
|
-
]);
|
|
230
|
-
return _parse_git_log(raw);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
116
|
function main(argv: readonly string[]): number {
|
|
234
117
|
let version: string | null = null;
|
|
235
118
|
let from: string | null = null;
|
|
@@ -251,7 +134,13 @@ function main(argv: readonly string[]): number {
|
|
|
251
134
|
return 2;
|
|
252
135
|
}
|
|
253
136
|
if (!from) {
|
|
254
|
-
from =
|
|
137
|
+
from = previous_release_tag(to, REPO_ROOT);
|
|
138
|
+
if (!from) {
|
|
139
|
+
process.stderr.write(
|
|
140
|
+
`no release tag reachable before ${to} — pass --from explicitly\n`,
|
|
141
|
+
);
|
|
142
|
+
return 2;
|
|
143
|
+
}
|
|
255
144
|
}
|
|
256
145
|
const section = extract_changelog_section(fs.readFileSync(changelogPath, 'utf-8'), version);
|
|
257
146
|
if (!section) {
|
|
@@ -263,7 +152,26 @@ function main(argv: readonly string[]): number {
|
|
|
263
152
|
process.stdout.write(`ℹ️ no curated head in the ${version} section — nothing to check\n`);
|
|
264
153
|
return 0;
|
|
265
154
|
}
|
|
266
|
-
|
|
155
|
+
let span: SpanCommit[];
|
|
156
|
+
try {
|
|
157
|
+
span = collect_span_commits(from, to, REPO_ROOT);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
process.stderr.write(`${(err as Error).message}\n`);
|
|
160
|
+
return 2;
|
|
161
|
+
}
|
|
162
|
+
// A generator-derived line that nobody rewrote is a prose gap, never a
|
|
163
|
+
// contradiction: the line's evidence is true, it is just unpolished. Warn
|
|
164
|
+
// so the omission is visible, and keep the exit code owned solely by the
|
|
165
|
+
// `_none_` check — a warning that reds the build is the guaranteed-red
|
|
166
|
+
// failure mode this whole change exists to remove.
|
|
167
|
+
const drafts = stale_draft_labels(curated);
|
|
168
|
+
if (drafts.length > 0) {
|
|
169
|
+
process.stdout.write(
|
|
170
|
+
`⚠️ auto-derived head line(s) not yet rewritten for ${version}: ` +
|
|
171
|
+
`${drafts.join(', ')} — advisory, not blocking.\n`,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
const derived = derive_categories(span);
|
|
267
175
|
const contradictions = highlight_contradictions(curated, derived);
|
|
268
176
|
if (contradictions.length === 0) {
|
|
269
177
|
process.stdout.write(`✅ curated head plausible for ${version} (span ${from}..${to})\n`);
|
|
@@ -61,10 +61,17 @@ export { COMPILE_TIME_TOGGLES };
|
|
|
61
61
|
|
|
62
62
|
// Maps legacy tier values to the router-canonical names. See
|
|
63
63
|
// docs/contracts/rule-router.md § Backward compatibility.
|
|
64
|
+
// `2b` → tier-2 is a recorded decision (2026-08-04): the 21 rules tagged 2b
|
|
65
|
+
// are tier-2 deliberately — before this entry they reached tier-2 only via a
|
|
66
|
+
// silent fallthrough that also swallowed typos. `safety-floor` is deliberately
|
|
67
|
+
// NOT mapped: it is documentation-only on the always-typed trio (which
|
|
68
|
+
// short-circuits to kernel before this map is consulted); on any other rule it
|
|
69
|
+
// is a contract violation and must fail compilation.
|
|
64
70
|
const LEGACY_TIER_MAP: Record<string, string> = {
|
|
65
71
|
'1': 'tier-1',
|
|
66
72
|
'2': 'tier-2',
|
|
67
73
|
'2a': 'tier-2',
|
|
74
|
+
'2b': 'tier-2',
|
|
68
75
|
'3': 'tier-1',
|
|
69
76
|
'mechanical-already': 'tier-1',
|
|
70
77
|
kernel: 'kernel',
|
|
@@ -93,11 +100,21 @@ function _parse_frontmatter(text: string): JsonObject {
|
|
|
93
100
|
: {};
|
|
94
101
|
}
|
|
95
102
|
|
|
96
|
-
function _resolve_tier(rule_type: string, raw_tier: string): string {
|
|
103
|
+
export function _resolve_tier(rule_type: string, raw_tier: string, rule_id: string): string {
|
|
97
104
|
if (rule_type === 'always') {
|
|
98
105
|
return 'kernel';
|
|
99
106
|
}
|
|
100
|
-
|
|
107
|
+
const mapped = LEGACY_TIER_MAP[String(raw_tier)];
|
|
108
|
+
if (mapped === undefined) {
|
|
109
|
+
// A typo'd (or unmapped) tier used to downgrade silently to tier-2 —
|
|
110
|
+
// a zero-injection failure nobody sees. Fail the compile instead.
|
|
111
|
+
throw new Error(
|
|
112
|
+
`compile_router: rule '${rule_id}' has unknown tier '${raw_tier}' — `
|
|
113
|
+
+ `allowed values: ${Object.keys(LEGACY_TIER_MAP).sort().join(', ')} `
|
|
114
|
+
+ `(or type: always, where tier is inert)`,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
return mapped;
|
|
101
118
|
}
|
|
102
119
|
|
|
103
120
|
function _normalize_trigger(item: Json): JsonObject | null {
|
|
@@ -205,7 +222,7 @@ function _collect(): JsonObject {
|
|
|
205
222
|
if (rule_type === 'manual') {
|
|
206
223
|
continue;
|
|
207
224
|
}
|
|
208
|
-
const tier = _resolve_tier(rule_type, String(fm['tier'] ?? ''));
|
|
225
|
+
const tier = _resolve_tier(rule_type, String(fm['tier'] ?? ''), rule_id);
|
|
209
226
|
if (!ALLOWED_TIERS.has(tier)) {
|
|
210
227
|
continue;
|
|
211
228
|
}
|