Skip to content

Commit 800b6da

Browse files
committed
refactor(layering): fold the four cutover policies into one parametrized gate
ADR 0019 §8: the per-command cutover gates consolidate into one parametrized runtime-command-cutover gate driven by a table of migrated commands. Adding a migrated command adds a row; the mechanism carries one planted-red proof instead of one per command. Part of #1739 (wave 0)
1 parent a63a99f commit 800b6da

21 files changed

Lines changed: 2060 additions & 1851 deletions

scripts/layering/check.ts

Lines changed: 19 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@
3333
// owner" shape as R7's SessionState ownership, applied to bin.ts's `--help` fast path.
3434
// - Over PLATFORM PACKAGE COMPOSITION: six private metadata façades meet at the exact root
3535
// composition file; premature implementation loading and forbidden cross-boundary edges fail (R13).
36-
// - Over the DEVICES COMMAND CUTOVER: the handler calls the neutral inventory gateway and no
37-
// superseded inventory module, import, or identifier remains in production (R13).
38-
// - Over COMMAND-ATOMIC RUNTIME CUTOVERS: retired logs, network, and record routes/admission cannot
39-
// coexist with their operation-fact-derived descriptor and handler paths (R14-R16).
36+
// - Over COMMAND-ATOMIC RUNTIME CUTOVERS: one parametrized gate reads the migrated-command
37+
// table (devices R17, logs R14, network R15, record R16) and proves each command keeps
38+
// exactly one platform-execution path — retired routes, admission, modules, and widened
39+
// runtime access cannot coexist with its operation-fact-derived descriptor and handler.
4040
// Only `(root)` is unranked among src/ zones (see `UNRANKED_ZONES` in model.ts):
4141
// it holds entrypoints and composition roots. Extracted workspace package zones
4242
// are classified separately and held behind R11 instead of the src folder spine.
@@ -87,33 +87,16 @@ import {
8787
platformPackagePolicySummary,
8888
} from './platform-package-policy.ts';
8989
import {
90-
checkDeviceInventoryCutover,
91-
deviceInventoryCutoverSummary,
92-
} from './device-inventory-cutover-policy.ts';
90+
checkRuntimeCommandCutover,
91+
runtimeCommandCutoverSummary,
92+
} from './runtime-command-cutover-policy.ts';
9393
import {
9494
listUntrackedProductionTypeScriptFiles,
9595
readTrackedPlatformPackageDeclarations,
9696
} from './platform-package-repository.ts';
9797
import { policyLead, policyViolation, ZONE_POLICIES } from './zone-policy.ts';
98-
import {
99-
logsLegacyRouteViolations,
100-
logsRuntimeNarrowingViolations,
101-
logsSessionStateOwnershipViolations,
102-
sourceExecutedUsingDeclarationViolations,
103-
} from './logs-runtime-cutover-policy.ts';
10498
import { contractsImplementationAuthorityViolations } from './contracts-implementation-policy.ts';
10599
import { selectorPipelineOwnershipViolations } from './selector-pipeline-ownership.ts';
106-
import {
107-
networkLegacyRouteViolations,
108-
networkRuntimeNarrowingViolations,
109-
networkRuntimeRouteViolations,
110-
} from './network-runtime-cutover-policy.ts';
111-
import {
112-
recordLegacyRouteViolations,
113-
recordRuntimeNarrowingViolations,
114-
recordRuntimeRouteViolations,
115-
} from './record-runtime-cutover-policy.ts';
116-
import { recordRuntimeDaemonMechanicsViolations } from './record-runtime-mechanics-policy.ts';
117100
import { recordRuntimeRegistryJoinViolations } from './record-runtime-registry-policy.ts';
118101

119102
const repoRoot = execFileSync('git', ['rev-parse', '--show-toplevel'], {
@@ -192,16 +175,6 @@ function checkCycles(edges: readonly ResolvedImportEdge[]): LayeringViolation[]
192175
}));
193176
}
194177

195-
function checkLogsRuntimeCutover(sources: ReadonlyMap<string, string>): LayeringViolation[] {
196-
const production = [...sources].map(([file, source]) => ({ path: file, source }));
197-
return [
198-
...logsLegacyRouteViolations(production),
199-
...logsRuntimeNarrowingViolations(production),
200-
...logsSessionStateOwnershipViolations(production),
201-
...sourceExecutedUsingDeclarationViolations(production),
202-
];
203-
}
204-
205178
function checkContractsImplementationAuthority(
206179
sources: ReadonlyMap<string, string>,
207180
): LayeringViolation[] {
@@ -210,32 +183,14 @@ function checkContractsImplementationAuthority(
210183
);
211184
}
212185

213-
function checkNetworkRuntimeCutover(sources: ReadonlyMap<string, string>): LayeringViolation[] {
214-
const production = [...sources].map(([file, source]) => ({ path: file, source }));
215-
return [
216-
...networkLegacyRouteViolations(production),
217-
...networkRuntimeNarrowingViolations(production),
218-
...networkRuntimeRouteViolations(production),
219-
].map((violation) => {
220-
const separator = violation.indexOf(': ');
221-
return {
222-
rule: 'R15 network-runtime-cutover',
223-
file: separator < 0 ? '(network runtime)' : violation.slice(0, separator),
224-
line: 1,
225-
message: separator < 0 ? violation : violation.slice(separator + 2),
226-
};
227-
});
228-
}
229-
230-
function checkRecordRuntimeCutover(sources: ReadonlyMap<string, string>): LayeringViolation[] {
186+
/**
187+
* The record registry-join scan is a descriptor-shape check, not a cutover claim, so it
188+
* stays its own module. Record's daemon-mechanics scan is the row's lifecycle proof and
189+
* runs from the cutover table. Both report under R16.
190+
*/
191+
function checkRecordRuntimeRegistryJoin(sources: ReadonlyMap<string, string>): LayeringViolation[] {
231192
const production = [...sources].map(([file, source]) => ({ path: file, source }));
232-
return [
233-
...recordLegacyRouteViolations(production),
234-
...recordRuntimeDaemonMechanicsViolations(production),
235-
...recordRuntimeNarrowingViolations(production),
236-
...recordRuntimeRegistryJoinViolations(production),
237-
...recordRuntimeRouteViolations(production),
238-
].map((violation) => {
193+
return recordRuntimeRegistryJoinViolations(production).map((violation) => {
239194
const separator = violation.indexOf(': ');
240195
return {
241196
rule: 'R16 record-runtime-cutover',
@@ -619,7 +574,7 @@ function report(
619574
`inside its declared owner (R7); every zero-dep CI job resolves without ` +
620575
`node_modules (R8); ${typeCycleNote(typeCycle)}; ${daemonModularitySummary()}; ` +
621576
`${packageBoundariesSummary(repoRoot)}; ${platformPackagePolicySummary()}; ` +
622-
`${deviceInventoryCutoverSummary()}; and bin.ts imports normalizeCliCommandAlias, ` +
577+
`${runtimeCommandCutoverSummary()}; and bin.ts imports normalizeCliCommandAlias, ` +
623578
`actually passes it into buildCommandUsageText, and holds no local alias literals ` +
624579
`(R12).\n`,
625580
);
@@ -669,11 +624,10 @@ export type LayeringRule = (context: LayeringContext) => LayeringViolation[];
669624
export const LAYERING_RULE_IDS = [
670625
'zone-policies',
671626
'value-import-cycles',
672-
'logs-runtime-cutover',
627+
'runtime-command-cutover',
628+
'record-runtime-registry-join',
673629
'contracts-implementation-authority',
674630
'selector-pipeline-ownership',
675-
'network-runtime-cutover',
676-
'record-runtime-cutover',
677631
'back-edges',
678632
'type-spine-inversions',
679633
'session-state-ownership',
@@ -682,21 +636,19 @@ export const LAYERING_RULE_IDS = [
682636
'bin-alias-fast-path',
683637
'package-boundaries',
684638
'platform-package-policy',
685-
'device-inventory-cutover',
686639
] as const;
687640

688641
export type LayeringRuleId = (typeof LAYERING_RULE_IDS)[number];
689642

690643
export const LAYERING_RULES: Readonly<Record<LayeringRuleId, LayeringRule>> = {
691644
'zone-policies': (context) => checkLayeringRules(context.edges),
692645
'value-import-cycles': (context) => checkCycles(context.edges),
693-
'logs-runtime-cutover': (context) => checkLogsRuntimeCutover(context.sources),
646+
'runtime-command-cutover': (context) => checkRuntimeCommandCutover(context.sources),
647+
'record-runtime-registry-join': (context) => checkRecordRuntimeRegistryJoin(context.sources),
694648
'contracts-implementation-authority': (context) =>
695649
checkContractsImplementationAuthority(context.sources),
696650
'selector-pipeline-ownership': (context) =>
697651
selectorPipelineOwnershipViolations(context.edges, workspaceSpecifierTargets(repoRoot)),
698-
'network-runtime-cutover': (context) => checkNetworkRuntimeCutover(context.sources),
699-
'record-runtime-cutover': (context) => checkRecordRuntimeCutover(context.sources),
700652
'back-edges': (context) => checkBackEdges(context.edges),
701653
'type-spine-inversions': (context) => checkTypeInversions(context.edges),
702654
'session-state-ownership': (context) => checkSessionStateOwnership(context.sources),
@@ -715,7 +667,6 @@ export const LAYERING_RULES: Readonly<Record<LayeringRuleId, LayeringRule>> = {
715667
readTrackedPlatformPackageDeclarations(repoRoot),
716668
{ untrackedProductionFiles: listUntrackedProductionTypeScriptFiles(repoRoot) },
717669
),
718-
'device-inventory-cutover': (context) => checkDeviceInventoryCutover(context.sources),
719670
};
720671

721672
export function main(): number {

scripts/layering/record-runtime-policy-ast.ts renamed to scripts/layering/cutover-policy-ast.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
export type RecordRuntimeProductionSource = Readonly<{ path: string; source: string }>;
1+
/** A production file the cutover policies scan: repo-relative path plus its source. */
2+
export type ProductionSource = Readonly<{ path: string; source: string }>;
23

34
export function memberName(node: Record<string, unknown>): string | undefined {
45
const property = node.property as Record<string, unknown> | undefined;

scripts/layering/device-inventory-cutover-policy.test.ts

Lines changed: 0 additions & 100 deletions
This file was deleted.

0 commit comments

Comments
 (0)