feat(m7-phase5): classifyNormative — детерминированный КАРТА/ЗАКОН + build-loop SE-D (§6)
Новые чистые экспорты в enforce-normative-content-rules: isDisciplineSourcePath (исходники
машин М1–М6 по префиксам enforce-/judge-/floor-/escape-grant/action-journal/receipt-/
shell-content-rules/plan-lock/classify-destructive/path-normalization), contentTouchesLaw
(контент правит правила/дисциплину), classifyNormative → {kind:'CARD'|'LAW'}. КАРТА =
CLAUDE.md/MEMORY.md/memory; ЗАКОН = Pravila/PSR/Tooling + дисциплинарный исходник ВНЕ плана +
контент-правка правил; build-loop: дисциплинарный исходник ПОД sealed-планом → КАРТА; сомнение
→ ЗАКОН. Тотален (null guard). Интеграция в decide — Task 2. 44/44 GREEN.
This commit is contained in:
@@ -40,6 +40,49 @@ export function isProtectiveNormativePath(filePath) {
|
||||
return PROTECTIVE_NORMATIVE_PATTERNS.some((re) => re.test(n));
|
||||
}
|
||||
|
||||
// §6 build-loop (SE-D): исходники машин М1–М6 — правка этих файлов меняет САМИ правила/дисциплину.
|
||||
// Вне запечатанного плана → ЗАКОН (ad-hoc самомодификация требует escape); под планом → КАРТА
|
||||
// (управляется стеной М2 + content-floor + TDD — цикл сборки не клинит).
|
||||
const DISCIPLINE_SOURCE_RE = /(^|\/)tools\/(enforce-|judge-|floor-|escape-grant|action-journal|receipt-|shell-content-rules|plan-lock|classify-destructive|path-normalization)[^/]*\.mjs$/;
|
||||
export function isDisciplineSourcePath(filePath) {
|
||||
if (typeof filePath !== 'string') return false;
|
||||
return DISCIPLINE_SOURCE_RE.test(filePath.replace(/\\/g, '/'));
|
||||
}
|
||||
|
||||
// §6 контент-детектор «правит секцию правил/дисциплины» (сомнение → ЗАКОН): маркеры правки
|
||||
// самих машин/правил в теле (имя enforce-/judge-/floor- + правило/блок/снять/disable/gate).
|
||||
const LAW_CONTENT_RE = /(enforce-|judge-|floor-)[a-z-]+[^.\n]{0,60}(?:правил|правит|блок|снять|disable|переопредел|gate)/iu;
|
||||
export function contentTouchesLaw(content) {
|
||||
return typeof content === 'string' && LAW_CONTENT_RE.test(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* §6 классификатор КАРТА/ЗАКОН. КАРТА (operational) — CLAUDE.md/MEMORY.md/memory: контроллер
|
||||
* ведёт свободно (claude-md-management). ЗАКОН (normative) — Pravila/PSR/Tooling + дисциплинарный
|
||||
* исходник ВНЕ плана + контент, правящий правила: требует escape владельца (М6). build-loop:
|
||||
* дисциплинарный исходник ПОД запечатанным планом (sealedPlanCoversEdit) → КАРТА. Сомнение → ЗАКОН.
|
||||
* @returns {{kind:'CARD'|'LAW', reason:string}}
|
||||
*/
|
||||
export function classifyNormative(filePath, { content = '', sealedPlanCoversEdit = false } = {}) {
|
||||
const n = String(filePath || '').replace(/\\/g, '/');
|
||||
// ЗАКОН-пути: Pravila/PSR/Tooling (нормативные документы-правила).
|
||||
if (/(^|\/)docs\/(Pravila_|Plugin_stack_rules_|Tooling_)[^/]*\.md$/.test(n)) {
|
||||
return { kind: 'LAW', reason: 'normative rules document (Pravila/PSR/Tooling)' };
|
||||
}
|
||||
// Дисциплинарный исходник: build-loop различитель (SE-D).
|
||||
if (isDisciplineSourcePath(n)) {
|
||||
return sealedPlanCoversEdit
|
||||
? { kind: 'CARD', reason: 'discipline source under sealed plan (build-loop)' }
|
||||
: { kind: 'LAW', reason: 'discipline source edited ad-hoc (outside sealed plan)' };
|
||||
}
|
||||
// Контент правит правила/дисциплину машин → ЗАКОН (сомнение → ЗАКОН).
|
||||
if (contentTouchesLaw(content)) {
|
||||
return { kind: 'LAW', reason: 'content edits a rules/discipline section' };
|
||||
}
|
||||
// Иначе — операционная КАРТА (CLAUDE.md/MEMORY.md/memory/заметки).
|
||||
return { kind: 'CARD', reason: 'operational map (CLAUDE.md/MEMORY.md/memory)' };
|
||||
}
|
||||
|
||||
/** Extract the new content a mutating tool would write. */
|
||||
export function extractWrittenContent(toolName, toolInput) {
|
||||
const i = toolInput || {};
|
||||
|
||||
@@ -209,3 +209,40 @@ describe('decide 7.2 (H3): деградация судьи fail-CLOSE тольк
|
||||
expect(r.reason).toMatch(/recovery/i);
|
||||
});
|
||||
});
|
||||
|
||||
import { classifyNormative, isDisciplineSourcePath } from './enforce-normative-content-rules.mjs';
|
||||
|
||||
describe('isDisciplineSourcePath — исходники машин М1–М6', () => {
|
||||
for (const p of ['tools/enforce-floor.mjs', 'tools/judge-orchestrator.mjs', 'tools/floor-signer.mjs', 'tools/escape-grant.mjs', 'tools/action-journal.mjs', 'tools/shell-content-rules.mjs', 'tools/plan-lock.mjs']) {
|
||||
it(`${p} → true`, () => expect(isDisciplineSourcePath(p)).toBe(true));
|
||||
}
|
||||
for (const p of ['tools/cost-aggregator.mjs', 'tools/observer-routing-detector.mjs', 'app/Models/User.php', 'CLAUDE.md']) {
|
||||
it(`${p} → false`, () => expect(isDisciplineSourcePath(p)).toBe(false));
|
||||
}
|
||||
});
|
||||
|
||||
describe('classifyNormative (§6 КАРТА/ЗАКОН + build-loop SE-D)', () => {
|
||||
it('CLAUDE.md / MEMORY.md / memory → CARD (operational, низкий порог)', () => {
|
||||
expect(classifyNormative('CLAUDE.md').kind).toBe('CARD');
|
||||
expect(classifyNormative('MEMORY.md').kind).toBe('CARD');
|
||||
expect(classifyNormative('memory/feedback_x.md').kind).toBe('CARD');
|
||||
});
|
||||
it('Pravila / PSR / Tooling → LAW (правила, что связывают контроллера)', () => {
|
||||
expect(classifyNormative('docs/Pravila_raboty_Claude_v1_1.md').kind).toBe('LAW');
|
||||
expect(classifyNormative('docs/Plugin_stack_rules_v1.md').kind).toBe('LAW');
|
||||
expect(classifyNormative('docs/Tooling_v8_3.md').kind).toBe('LAW');
|
||||
});
|
||||
it('дисциплинарный исходник ВНЕ плана → LAW (ad-hoc самомодификация)', () => {
|
||||
expect(classifyNormative('tools/enforce-floor.mjs', { sealedPlanCoversEdit: false }).kind).toBe('LAW');
|
||||
});
|
||||
it('дисциплинарный исходник ПОД запечатанным планом → CARD (build-loop, SE-D)', () => {
|
||||
expect(classifyNormative('tools/enforce-floor.mjs', { sealedPlanCoversEdit: true }).kind).toBe('CARD');
|
||||
});
|
||||
it('контент правит секцию правил/дисциплины в любом файле → LAW (сомнение → ЗАКОН)', () => {
|
||||
const content = 'обнови правило enforce-floor: блок снять при флаге';
|
||||
expect(classifyNormative('memory/x.md', { content }).kind).toBe('LAW');
|
||||
});
|
||||
it('обычный КАРТА-контент без правило-маркеров → CARD', () => {
|
||||
expect(classifyNormative('memory/x.md', { content: 'урок: rebase ломается на observer-файлах' }).kind).toBe('CARD');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user