feat(contracts): share notification, organization and device capability models

Move the Hub channel table, organization tags and device capability definitions
into the workspace packages so the control plane and the console validate the
same contract, and refresh the frozen upstream evidence for the new paths.
This commit is contained in:
chick
2026-09-05 18:52:54 +08:00
parent 03fa2e6c7b
commit f11877f13e
19 changed files with 15535 additions and 11768 deletions
@@ -3,67 +3,477 @@ import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import { upstream58e2204Operations } from '../src/upstream-58e2204.ts';
import { operationAcceptance58e2204, operationAcceptanceOverrides58e2204, acceptancePolicyCatalog, controlPlaneAcceptance, fixtureDisposition58e2204, renderOperationAcceptanceMatrix, surfaceGroups58e2204, policyGroups58e2204, scenarioGroups58e2204, availabilityGroups58e2204 } from '../src/acceptance-58e2204.ts';
import {
operationAcceptance58e2204,
operationAcceptanceOverrides58e2204,
acceptancePolicyCatalog,
controlPlaneAcceptance,
fixtureDisposition58e2204,
renderOperationAcceptanceMatrix,
surfaceGroups58e2204,
policyGroups58e2204,
scenarioGroups58e2204,
availabilityGroups58e2204,
} from '../src/acceptance-58e2204.ts';
import { DENY_REASONS, selectReadonlyOperations } from '../../test-fixtures/scripts/collector.ts';
const fixtureManifestPath=fileURLToPath(new URL('../../test-fixtures/src/manifest.json',import.meta.url));
const matrixPath=fileURLToPath(new URL('../../../docs/product/operation-acceptance-matrix.md',import.meta.url));
const acceptanceSourcePath=fileURLToPath(new URL('../src/acceptance-58e2204.ts',import.meta.url));
const iaPath=fileURLToPath(new URL('../../../docs/product/information-architecture.md',import.meta.url));
const manifest=JSON.parse(await readFile(fixtureManifestPath,'utf8'));
const byId=new Map(upstream58e2204Operations.map((o:any)=>[o.operationId,o]));
const canonicalRoutes=new Set(['/fleet','/instances/new','/instances/:id/overview','/instances/:id/cellular','/instances/:id/device-network','/instances/:id/messages','/instances/:id/calls','/instances/:id/esim','/instances/:id/notifications','/instances/:id/automation','/instances/:id/ota','/settings/instances/:id']);
const assertExactPartition=(groups:readonly {ids:readonly string[]}[])=>{const ids=groups.flatMap(g=>[...g.ids]);assert.equal(ids.length,117);assert.equal(new Set(ids).size,117);assert.deepEqual([...ids].sort(),[...byId.keys()].sort());};
const fixtureManifestPath = fileURLToPath(
new URL('../../test-fixtures/src/manifest.json', import.meta.url),
);
const matrixPath = fileURLToPath(
new URL('../../../docs/product/operation-acceptance-matrix.md', import.meta.url),
);
const acceptanceSourcePath = fileURLToPath(
new URL('../src/acceptance-58e2204.ts', import.meta.url),
);
const iaPath = fileURLToPath(
new URL('../../../docs/product/information-architecture.md', import.meta.url),
);
const manifest = JSON.parse(await readFile(fixtureManifestPath, 'utf8'));
const byId = new Map(upstream58e2204Operations.map((o: any) => [o.operationId, o]));
const canonicalRoutes = new Set([
'/fleet',
'/instances/new',
'/instances/:id/overview',
'/instances/:id/cellular',
'/instances/:id/device-network',
'/instances/:id/messages',
'/instances/:id/calls',
'/instances/:id/esim',
'/instances/:id/notifications',
'/instances/:id/automation',
'/instances/:id/ota',
'/settings/instances/:id',
]);
const assertExactPartition = (groups: readonly { ids: readonly string[] }[]) => {
const ids = groups.flatMap((g) => [...g.ids]);
assert.equal(ids.length, 117);
assert.equal(new Set(ids).size, 117);
assert.deepEqual([...ids].sort(), [...byId.keys()].sort());
};
test('RED→GREEN: surface/policy/scenario/availability are literal exact operation partitions',()=>{
for(const groups of [surfaceGroups58e2204,policyGroups58e2204,scenarioGroups58e2204,availabilityGroups58e2204])assertExactPartition(groups);
assert.ok(surfaceGroups58e2204.filter(g=>g.surfaceId.startsWith('calls/')).length>=5);
assert.ok(surfaceGroups58e2204.filter(g=>g.surfaceId.startsWith('cellular/')).length>=5);
for(const group of surfaceGroups58e2204)for(const id of group.ids){const row=operationAcceptance58e2204.find(x=>x.operationId===id)!;assert.equal(row.surfaceId,group.surfaceId);assert.equal(row.primaryRoute,group.primaryRoute);}
const tuples=new Set(policyGroups58e2204.map(g=>Object.values(g.policies).join('/')));assert.ok(policyGroups58e2204.length>=8);assert.ok(tuples.size>=8);
const policy=(id:string)=>policyGroups58e2204.find(g=>g.ids.includes(id))!;assert.match(policy('getSmsList').groupId,/list/);assert.match(policy('getHealth').groupId,/detail/);assert.match(policy('getNetworkOperatorsScan').groupId,/scan/);assert.match(policy('postData').groupId,/direct/);assert.match(policy('postAuthLogin').groupId,/auth/);assert.match(policy('postSmsBatchDelete').groupId,/destructive/);assert.match(policy('postOtaApply').groupId,/ota/);
assert.ok(scenarioGroups58e2204.length>=8);const scenario=(id:string)=>scenarioGroups58e2204.find(g=>g.ids.includes(id))!.scenarios;assert.equal(scenario('getHealth').empty.applicable,false);assert.equal(scenario('getSmsList').empty.applicable,true);assert.equal(scenario('getStats').partial.applicable,true);assert.equal(scenario('getAuthStatus').partial.applicable,false);assert.equal(scenario('getNetworkOperatorsScan').empty.applicable,true);assert.equal(scenario('getNetworkOperatorsScan').partial.applicable,true);assert.equal(scenario('getNetworkOperatorsScan')['policy-forbidden'].applicable,true);assert.equal(scenario('getNetworkOperatorsScan')['unknown-result'].applicable,false);for(const id of ['postSmsBatchDelete','postNotificationsQueueRetryAll','postNotificationsQueueClear'])assert.equal(scenario(id).partial.applicable,true,id);for(const id of ['postSystemReboot','postServiceRestart','deleteCallHistoryId','postBandLock','postOtaApply','postOtaUpload','postOtaOnlinePrepare','postOtaLatestRelease','postOtaCancel','postData'])assert.equal(scenario(id).partial.applicable,false,id);for(const id of ['postData','postSmsBatchDelete']){assert.equal(scenario(id)['policy-forbidden'].applicable,true);assert.equal(scenario(id)['unknown-result'].applicable,true);}for(const id of ['getHealth','getSmsList']){assert.equal(scenario(id)['policy-forbidden'].applicable,false);assert.equal(scenario(id)['unknown-result'].applicable,false);}for(const group of scenarioGroups58e2204)assert.equal(group.scenarios['owner-switch'].applicable,true);
assert.ok(availabilityGroups58e2204.length>=4);for(const group of availabilityGroups58e2204)assert.ok(group.reason.length>20);
test('RED→GREEN: surface/policy/scenario/availability are literal exact operation partitions', () => {
for (const groups of [
surfaceGroups58e2204,
policyGroups58e2204,
scenarioGroups58e2204,
availabilityGroups58e2204,
])
assertExactPartition(groups);
assert.ok(surfaceGroups58e2204.filter((g) => g.surfaceId.startsWith('calls/')).length >= 5);
assert.ok(surfaceGroups58e2204.filter((g) => g.surfaceId.startsWith('cellular/')).length >= 5);
for (const group of surfaceGroups58e2204)
for (const id of group.ids) {
const row = operationAcceptance58e2204.find((x) => x.operationId === id)!;
assert.equal(row.surfaceId, group.surfaceId);
assert.equal(row.primaryRoute, group.primaryRoute);
}
const tuples = new Set(policyGroups58e2204.map((g) => Object.values(g.policies).join('/')));
assert.ok(policyGroups58e2204.length >= 8);
assert.ok(tuples.size >= 8);
const policy = (id: string) => policyGroups58e2204.find((g) => g.ids.includes(id))!;
assert.match(policy('getSmsList').groupId, /list/);
assert.match(policy('getHealth').groupId, /detail/);
assert.match(policy('getNetworkOperatorsScan').groupId, /scan/);
assert.match(policy('postData').groupId, /direct/);
assert.match(policy('postAuthLogin').groupId, /auth/);
assert.match(policy('postSmsBatchDelete').groupId, /destructive/);
assert.match(policy('postOtaApply').groupId, /ota/);
assert.ok(scenarioGroups58e2204.length >= 8);
const scenario = (id: string) => scenarioGroups58e2204.find((g) => g.ids.includes(id))!.scenarios;
assert.equal(scenario('getHealth').empty.applicable, false);
assert.equal(scenario('getSmsList').empty.applicable, true);
assert.equal(scenario('getStats').partial.applicable, true);
assert.equal(scenario('getAuthStatus').partial.applicable, false);
assert.equal(scenario('getNetworkOperatorsScan').empty.applicable, true);
assert.equal(scenario('getNetworkOperatorsScan').partial.applicable, true);
assert.equal(scenario('getNetworkOperatorsScan')['policy-forbidden'].applicable, true);
assert.equal(scenario('getNetworkOperatorsScan')['unknown-result'].applicable, false);
for (const id of [
'postSmsBatchDelete',
'postNotificationsQueueRetryAll',
'postNotificationsQueueClear',
])
assert.equal(scenario(id).partial.applicable, true, id);
for (const id of [
'postSystemReboot',
'postServiceRestart',
'deleteCallHistoryId',
'postBandLock',
'postOtaApply',
'postOtaUpload',
'postOtaOnlinePrepare',
'postOtaLatestRelease',
'postOtaCancel',
'postData',
])
assert.equal(scenario(id).partial.applicable, false, id);
for (const id of ['postData', 'postSmsBatchDelete']) {
assert.equal(scenario(id)['policy-forbidden'].applicable, true);
assert.equal(scenario(id)['unknown-result'].applicable, true);
}
for (const id of ['getHealth', 'getSmsList']) {
assert.equal(scenario(id)['policy-forbidden'].applicable, false);
assert.equal(scenario(id)['unknown-result'].applicable, false);
}
for (const group of scenarioGroups58e2204)
assert.equal(group.scenarios['owner-switch'].applicable, true);
assert.ok(availabilityGroups58e2204.length >= 4);
for (const group of availabilityGroups58e2204) assert.ok(group.reason.length > 20);
});
test('RED→GREEN: acceptance ledger is exact, Registry-bound and IA-owned',()=>{
assert.equal(operationAcceptance58e2204.length,117);
assert.equal(new Set(operationAcceptance58e2204.map(x=>x.operationId)).size,117);
assert.deepEqual([...operationAcceptance58e2204.map(x=>x.operationId)].sort(),[...byId.keys()].sort());
for(const row of operationAcceptance58e2204){const op:any=byId.get(row.operationId);assert.ok(op);assert.equal(row.method,op.method);assert.equal(row.pathTemplate,op.pathTemplate);assert.equal(row.upstreamDomain,op.upstreamDomain);assert.equal(row.riskLevel,op.riskLevel);assert.equal(row.confirmationUX,op.confirmationPolicy);assert.ok(canonicalRoutes.has(row.primaryRoute),row.operationId);assert.ok(row.surfaceId);assert.ok(row.uiStrategy);assert.match(row.availability,/^(planned|unsupported-version|deferred-with-reason)$/);if(row.availability==='deferred-with-reason')assert.ok(row.availabilityReason);assert.ok(row.requiredStates.request.length&&row.requiredStates.freshness.length&&row.requiredStates.support.length);assert.ok(row.requiredStates.scenarios.length||row.requiredStates.naRationale);assert.ok(row.evidenceIds.length);}
test('RED→GREEN: acceptance ledger is exact, Registry-bound and IA-owned', () => {
assert.equal(operationAcceptance58e2204.length, 117);
assert.equal(new Set(operationAcceptance58e2204.map((x) => x.operationId)).size, 117);
assert.deepEqual(
[...operationAcceptance58e2204.map((x) => x.operationId)].sort(),
[...byId.keys()].sort(),
);
for (const row of operationAcceptance58e2204) {
const op: any = byId.get(row.operationId);
assert.ok(op);
assert.equal(row.method, op.method);
assert.equal(row.pathTemplate, op.pathTemplate);
assert.equal(row.upstreamDomain, op.upstreamDomain);
assert.equal(row.riskLevel, op.riskLevel);
assert.equal(row.confirmationUX, op.confirmationPolicy);
assert.ok(canonicalRoutes.has(row.primaryRoute), row.operationId);
assert.ok(row.surfaceId);
assert.ok(row.uiStrategy);
assert.match(row.availability, /^(planned|unsupported-version|deferred-with-reason)$/);
if (row.availability === 'deferred-with-reason') assert.ok(row.availabilityReason);
assert.ok(
row.requiredStates.request.length &&
row.requiredStates.freshness.length &&
row.requiredStates.support.length,
);
assert.ok(row.requiredStates.scenarios.length || row.requiredStates.naRationale);
assert.ok(row.evidenceIds.length);
}
});
test('RED→GREEN: exact overrides and policy-bound acceptance are auditable',()=>{
assert.deepEqual(Object.keys(operationAcceptanceOverrides58e2204).sort(),[...byId.keys()].sort());
const policyFields=['preconditionPolicyId','stalePolicyId','unsupportedPolicyId','retryPolicyId','resultPolicyId'];
for(const row of operationAcceptance58e2204){const override:any=(operationAcceptanceOverrides58e2204 as any)[row.operationId];assert.ok(override);assert.ok(row.availabilityReason?.length>20,row.operationId);assert.ok(row.versionEvidencePolicy?.length>20,row.operationId);assert.equal(row.surfaceId,override.surfaceId);assert.equal(row.uiStrategy,override.uiStrategy);for(const scenario of ['empty','partial','policy-forbidden','unknown-result','owner-switch']){assert.equal(typeof row.scenarioAcceptance[scenario].applicable,'boolean');assert.ok(row.scenarioAcceptance[scenario].rationale.length>8);}for(const field of policyFields){assert.equal((row as any)[field],override[field]);assert.ok((acceptancePolicyCatalog as any)[field][(row as any)[field]],`${row.operationId}:${field}`);}}
test('RED→GREEN: exact overrides and policy-bound acceptance are auditable', () => {
assert.deepEqual(
Object.keys(operationAcceptanceOverrides58e2204).sort(),
[...byId.keys()].sort(),
);
const policyFields = [
'preconditionPolicyId',
'stalePolicyId',
'unsupportedPolicyId',
'retryPolicyId',
'resultPolicyId',
];
for (const row of operationAcceptance58e2204) {
const override: any = (operationAcceptanceOverrides58e2204 as any)[row.operationId];
assert.ok(override);
assert.ok(row.availabilityReason?.length > 20, row.operationId);
assert.ok(row.versionEvidencePolicy?.length > 20, row.operationId);
assert.equal(row.surfaceId, override.surfaceId);
assert.equal(row.uiStrategy, override.uiStrategy);
for (const scenario of [
'empty',
'partial',
'policy-forbidden',
'unknown-result',
'owner-switch',
]) {
assert.equal(typeof row.scenarioAcceptance[scenario].applicable, 'boolean');
assert.ok(row.scenarioAcceptance[scenario].rationale.length > 8);
}
for (const field of policyFields) {
assert.equal((row as any)[field], override[field]);
assert.ok(
(acceptancePolicyCatalog as any)[field][(row as any)[field]],
`${row.operationId}:${field}`,
);
}
}
});
test('real acceptance is an explicit availability decision coherent with fixture disposition',async()=>{const fixture=new Map(fixtureDisposition58e2204.map(x=>[x.operationId,x]));const valid=new Set(['REAL_READ','REAL_READ_DEFERRED','REAL_WRITE_LATER','SIMULATED_HIGH_RISK','CONTRACT_ONLY']);for(const group of availabilityGroups58e2204){assert.ok(valid.has(group.realAcceptance),group.groupId);for(const id of group.ids){const row=operationAcceptance58e2204.find(x=>x.operationId===id)!;assert.equal(row.realAcceptance,group.realAcceptance,id);const status=fixture.get(id)!.runtimeFixtureStatus;if(status==='captured-readonly')assert.equal(group.realAcceptance,'REAL_READ',id);if(status==='denied-readonly-candidate')assert.equal(group.realAcceptance,'REAL_READ_DEFERRED',id);if(group.realAcceptance==='REAL_READ')assert.equal(status,'captured-readonly',id);if(group.realAcceptance==='REAL_READ_DEFERRED')assert.equal(status,'denied-readonly-candidate',id);}}const source=await readFile(acceptanceSourcePath,'utf8');assert.doesNotMatch(source,/function\s+realAcceptance\s*\(/);assert.doesNotMatch(source,/realAcceptanceIntent/);assert.equal(operationAcceptance58e2204.filter(x=>x.realAcceptance==='REAL_READ').length,39);assert.equal(operationAcceptance58e2204.filter(x=>x.realAcceptance==='REAL_READ_DEFERRED').length,11);});
test('Phase 1 bootstrap gate defers Fleet component/E2E evidence to the Phase 5 implementation gate',async()=>{const ia=await readFile(iaPath,'utf8');const bootstrap=ia.match(/### Phase 0 → Phase 1 workspace\/contract bootstrap gate([\s\S]*?)(?=### Phase 5 implementation gate)/)?.[1];const phase5=ia.match(/### Phase 5 implementation gate([\s\S]*)/)?.[1];assert.ok(bootstrap);assert.ok(phase5);assert.doesNotMatch(bootstrap!,/Fleet[^\n]*组件\/E2E/);assert.match(phase5!,/Fleet[^\n]*组件\/E2E/);assert.match(phase5!,/\[ \][^\n]*Fleet/);assert.match(bootstrap!,/\[x\][^\n]*最终独立规格与质量\/安全复审均已通过/);assert.doesNotMatch(bootstrap!,/PENDING final independent review/);assert.match(ia,/E2E[^\n]*N\/A[^\n]*Phase 5/);});
test('control-plane flows are independent, structured, and preserve safety invariants',()=>{const ids=['instance-create','instance-update','instance-delete','secret-set','secret-preserve','secret-clear','config-import-preview','config-import-confirm','credential-verify','saved-secret-login','temporary-secret-login','logout','401-recovery','auth-setup','auth-password-change','auth-settings-read','auth-settings-write','job-cancel','job-retry','audit-export','system-settings-update'];assert.deepEqual(controlPlaneAcceptance.map(x=>x.flowId).sort(),ids.sort());for(const flow of controlPlaneAcceptance)for(const field of ['route','risk','riskSubtype','confirmation','preconditions','result','failureRecovery','secretPolicy','evidence'])assert.ok(String((flow as any)[field]).length>1,`${flow.flowId}:${field}`);assert.match(controlPlaneAcceptance.find(x=>x.flowId==='instance-delete')!.result,/new jobId.*two-phase/i);for(const id of ['secret-set','secret-preserve','secret-clear'])assert.match(controlPlaneAcceptance.find(x=>x.flowId===id)!.secretPolicy,/never.*value/i);for(const id of ['saved-secret-login','temporary-secret-login'])assert.match(controlPlaneAcceptance.find(x=>x.flowId===id)!.failureRecovery,/no automatic replay/i);assert.match(controlPlaneAcceptance.find(x=>x.flowId==='job-retry')!.result,/new jobId.*lineage/i);});
test('product risk prose has no conflicting action summaries',async()=>{const root=fileURLToPath(new URL('../../../docs/product/',import.meta.url));const names=['project-charter.md','personas-and-workflows.md','information-architecture.md','current-system-audit.md'];const docs=(await Promise.all(names.map(n=>readFile(`${root}${n}`,'utf8')))).join('\n');for(const expected of [/notifications config[^\n]*R2[^\n]*Job/i,/automation config[^\n]*R2[^\n]*Job/i,/eSIM[^\n]*download[^\n]*R1[^\n]*direct/i,/WLAN connect[^\n]*R1[^\n]*forget[^\n]*R2/i,/DDNS config[^\n]*R2[^\n]*Job/i,/baseband restart[^\n]*R3[^\n]*status[^\n]*R0/i])assert.match(docs,expected);assert.doesNotMatch(docs,/Notifications[^\n]*config R1/i);assert.doesNotMatch(docs,/Automation[^\n]*config R1/i);});
test('R2/R3, dedicated auth, write strategy, and split bulk/fleet policy are gated',()=>{
for(const row of operationAcceptance58e2204){const op:any=byId.get(row.operationId);if(['R2','R3'].includes(row.riskLevel)){assert.equal(row.executionMode,op.executionPolicy==='dedicatedFlow'?'dedicated-flow':'preparation-job');assert.match(row.preconditions,/fresh preflight/i);assert.match(row.confirmationUX,/explicit|strong/);assert.match(row.resultDestination,/jobs\/:jobId/);assert.match(row.retryRecovery,/new Job lineage/i);}if(row.riskLevel==='R3')assert.equal(op.capability,'job');if(row.method!=='GET'){assert.notEqual(row.uiStrategy,'read-panel');assert.notEqual(row.realAcceptance,'REAL_READ');assert.equal(row.fleetBatchable,false);}}
const auth=operationAcceptance58e2204.filter(x=>['postAuthSetup','postAuthPassword','postAuthSettings','postAuthLogin','postAuthLogout'].includes(x.operationId));assert.equal(auth.length,5);assert.ok(auth.every(x=>x.uiStrategy==='dedicated-auth-flow'&&x.executionMode==='dedicated-flow'));for(const id of ['postAuthLogin','postAuthLogout']){const row=auth.find(x=>x.operationId===id)!;assert.equal(row.sessionSubtype,'session-sensitive');assert.match(row.retryRecovery,/no automatic replay/i);assert.match(row.resultDestination,/metadata-only audit/i);}
const sms=operationAcceptance58e2204.find(x=>x.operationId==='postSmsBatchDelete')!;assert.equal(sms.resourceBulk,true);assert.equal(sms.fleetBatchable,false);for(const id of ['postNotificationsQueueRetryAll','postNotificationsQueueClear'])assert.equal(operationAcceptance58e2204.find(x=>x.operationId===id)!.resourceBulk,true,id);for(const id of ['postSmsClear','postCallHistoryClear','postNotificationsLogsClear','postAutomationLogsClear'])assert.equal(operationAcceptance58e2204.find(x=>x.operationId===id)!.resourceBulk,false,id);
const health=operationAcceptance58e2204.find(x=>x.operationId==='getHealth')!;assert.equal(health.fleetBatchable,true);assert.equal(health.partialAggregationPolicy,'per-item');
test('real acceptance is an explicit availability decision coherent with fixture disposition', async () => {
const fixture = new Map(fixtureDisposition58e2204.map((x) => [x.operationId, x]));
const valid = new Set([
'REAL_READ',
'REAL_READ_DEFERRED',
'REAL_WRITE_LATER',
'SIMULATED_HIGH_RISK',
'CONTRACT_ONLY',
]);
for (const group of availabilityGroups58e2204) {
assert.ok(valid.has(group.realAcceptance), group.groupId);
for (const id of group.ids) {
const row = operationAcceptance58e2204.find((x) => x.operationId === id)!;
assert.equal(row.realAcceptance, group.realAcceptance, id);
const status = fixture.get(id)!.runtimeFixtureStatus;
if (status === 'captured-readonly') assert.equal(group.realAcceptance, 'REAL_READ', id);
if (status === 'denied-readonly-candidate')
assert.equal(group.realAcceptance, 'REAL_READ_DEFERRED', id);
if (group.realAcceptance === 'REAL_READ') assert.equal(status, 'captured-readonly', id);
if (group.realAcceptance === 'REAL_READ_DEFERRED')
assert.equal(status, 'denied-readonly-candidate', id);
}
}
const source = await readFile(acceptanceSourcePath, 'utf8');
assert.doesNotMatch(source, /function\s+realAcceptance\s*\(/);
assert.doesNotMatch(source, /realAcceptanceIntent/);
assert.equal(
operationAcceptance58e2204.filter((x) => x.realAcceptance === 'REAL_READ').length,
39,
);
assert.equal(
operationAcceptance58e2204.filter((x) => x.realAcceptance === 'REAL_READ_DEFERRED').length,
11,
);
});
test('fixture disposition is authoritative and exactly follows collector plus 78-file manifest',()=>{
assert.equal(fixtureDisposition58e2204.length,117);assert.equal(new Set(fixtureDisposition58e2204.map(x=>x.operationId)).size,117);
const selected=selectReadonlyOperations(upstream58e2204Operations as any[]);assert.equal(selected.selected.length,39);assert.equal(selected.denied.length,11);assert.equal(Object.keys(DENY_REASONS).length,11);
const selectedIds=new Set(selected.selected.map((x:any)=>x.operationId));const denied=new Map(selected.denied.map((x:any)=>[x.operationId,x.denyReason]));
const filesById=new Map<string,any[]>();for(const file of manifest.files){const match=/instance-[12]--([^.]+)\.json$/.exec(file.path);assert.ok(match);const list=filesById.get(match[1])??[];list.push(file);filesById.set(match[1],list);}
for(const d of fixtureDisposition58e2204){if(selectedIds.has(d.operationId)){assert.equal(d.runtimeFixtureStatus,'captured-readonly');assert.equal(d.fixtureCount,2);assert.equal(filesById.get(d.operationId)?.length,2);assert.deepEqual(d.aliases,['instance-1','instance-2']);assert.ok(d.observedCategories.length);}else if(denied.has(d.operationId)){assert.equal(d.runtimeFixtureStatus,'denied-readonly-candidate');assert.equal(d.reason,denied.get(d.operationId));assert.equal(d.fixtureCount,0);}else {assert.match(d.runtimeFixtureStatus,/^not-eligible-readonly-capture/);assert.equal(d.fixtureCount,0);}}
assert.equal(manifest.realFixtureCount,78);
test('Phase 1 bootstrap gate defers Fleet component/E2E evidence to the Phase 5 implementation gate', async () => {
const ia = await readFile(iaPath, 'utf8');
const bootstrap = ia.match(
/### Phase 0 → Phase 1 workspace\/contract bootstrap gate([\s\S]*?)(?=### Phase 5 implementation gate)/,
)?.[1];
const phase5 = ia.match(/### Phase 5 implementation gate([\s\S]*)/)?.[1];
assert.ok(bootstrap);
assert.ok(phase5);
assert.doesNotMatch(bootstrap!, /Fleet[^\n]*组件\/E2E/);
assert.match(phase5!, /Fleet[^\n]*组件\/E2E/);
assert.match(phase5!, /\[ \][^\n]*Fleet/);
assert.match(bootstrap!, /\[x\][^\n]*最终独立规格与质量\/安全复审均已通过/);
assert.doesNotMatch(bootstrap!, /PENDING final independent review/);
assert.match(ia, /E2E[^\n]*N\/A[^\n]*Phase 5/);
});
test('product risk prose defers to Registry and known conflicts stay corrected',async()=>{const productRoot=fileURLToPath(new URL('../../../docs/product/',import.meta.url));const workflows=await readFile(`${productRoot}personas-and-workflows.md`,'utf8');const charter=await readFile(`${productRoot}project-charter.md`,'utf8');assert.doesNotMatch(workflows,/下载 profile[^\n]*R2 Job/);assert.match(workflows,/WLAN connect 为 R1/);assert.match(workflows,/notifications config 为 R2 Job/);assert.match(workflows,/fleetBatchable=true/);assert.match(charter,/resourceBulk.*fleetBatchable/);for(const id of ['postEsimProfiles','postEsimProfilesIccidEnable','postDeviceNetworkWlanConnect','postNotificationsConfig','postAutomationConfig','postDeviceNetworkDdnsConfig','postBasebandRestart']){assert.ok(operationAcceptance58e2204.find(x=>x.operationId===id),id);}});
test('control-plane flows are independent, structured, and preserve safety invariants', () => {
const ids = [
'instance-create',
'instance-update',
'instance-delete',
'secret-set',
'secret-preserve',
'secret-clear',
'config-import-preview',
'config-import-confirm',
'credential-verify',
'saved-secret-login',
'temporary-secret-login',
'logout',
'401-recovery',
'auth-setup',
'auth-password-change',
'auth-settings-read',
'auth-settings-write',
'job-cancel',
'job-retry',
'audit-export',
'system-settings-update',
];
assert.deepEqual(controlPlaneAcceptance.map((x) => x.flowId).sort(), ids.sort());
for (const flow of controlPlaneAcceptance)
for (const field of [
'route',
'risk',
'riskSubtype',
'confirmation',
'preconditions',
'result',
'failureRecovery',
'secretPolicy',
'evidence',
])
assert.ok(String((flow as any)[field]).length > 1, `${flow.flowId}:${field}`);
assert.match(
controlPlaneAcceptance.find((x) => x.flowId === 'instance-delete')!.result,
/new jobId.*two-phase/i,
);
for (const id of ['secret-set', 'secret-preserve', 'secret-clear'])
assert.match(
controlPlaneAcceptance.find((x) => x.flowId === id)!.secretPolicy,
/never.*value/i,
);
for (const id of ['saved-secret-login', 'temporary-secret-login'])
assert.match(
controlPlaneAcceptance.find((x) => x.flowId === id)!.failureRecovery,
/no automatic replay/i,
);
assert.match(
controlPlaneAcceptance.find((x) => x.flowId === 'job-retry')!.result,
/new jobId.*lineage/i,
);
});
test('generated matrix is synchronized and contains each operation exactly once',async()=>{const doc=await readFile(matrixPath,'utf8');assert.equal(doc,renderOperationAcceptanceMatrix());const ids=[...doc.matchAll(/^\| `([^`]+)` \|/gm)].map(x=>x[1]).filter(id=>byId.has(id));assert.equal(ids.length,117);assert.deepEqual(ids.sort(),[...byId.keys()].sort());});
test('product risk prose has no conflicting action summaries', async () => {
const root = fileURLToPath(new URL('../../../docs/product/', import.meta.url));
const names = [
'project-charter.md',
'personas-and-workflows.md',
'information-architecture.md',
'current-system-audit.md',
];
const docs = (await Promise.all(names.map((n) => readFile(`${root}${n}`, 'utf8')))).join('\n');
for (const expected of [
/notifications config[^\n]*R2[^\n]*Job/i,
/automation config[^\n]*R2[^\n]*Job/i,
/eSIM[^\n]*download[^\n]*R1[^\n]*direct/i,
/WLAN connect[^\n]*R1[^\n]*forget[^\n]*R2/i,
/DDNS config[^\n]*R2[^\n]*Job/i,
/baseband restart[^\n]*R3[^\n]*status[^\n]*R0/i,
])
assert.match(docs, expected);
assert.doesNotMatch(docs, /Notifications[^\n]*config R1/i);
assert.doesNotMatch(docs, /Automation[^\n]*config R1/i);
});
test('R2/R3, dedicated auth, write strategy, and split bulk/fleet policy are gated', () => {
for (const row of operationAcceptance58e2204) {
const op: any = byId.get(row.operationId);
if (['R2', 'R3'].includes(row.riskLevel)) {
assert.equal(
row.executionMode,
op.executionPolicy === 'dedicatedFlow' ? 'dedicated-flow' : 'preparation-job',
);
assert.match(row.preconditions, /fresh preflight/i);
assert.match(row.confirmationUX, /explicit|strong/);
assert.match(row.resultDestination, /jobs\/:jobId/);
assert.match(row.retryRecovery, /new Job lineage/i);
}
if (row.riskLevel === 'R3') assert.equal(op.capability, 'job');
if (row.method !== 'GET') {
assert.notEqual(row.uiStrategy, 'read-panel');
assert.notEqual(row.realAcceptance, 'REAL_READ');
assert.equal(row.fleetBatchable, false);
}
}
const auth = operationAcceptance58e2204.filter((x) =>
[
'postAuthSetup',
'postAuthPassword',
'postAuthSettings',
'postAuthLogin',
'postAuthLogout',
].includes(x.operationId),
);
assert.equal(auth.length, 5);
assert.ok(
auth.every(
(x) => x.uiStrategy === 'dedicated-auth-flow' && x.executionMode === 'dedicated-flow',
),
);
for (const id of ['postAuthLogin', 'postAuthLogout']) {
const row = auth.find((x) => x.operationId === id)!;
assert.equal(row.sessionSubtype, 'session-sensitive');
assert.match(row.retryRecovery, /no automatic replay/i);
assert.match(row.resultDestination, /metadata-only audit/i);
}
const sms = operationAcceptance58e2204.find((x) => x.operationId === 'postSmsBatchDelete')!;
assert.equal(sms.resourceBulk, true);
assert.equal(sms.fleetBatchable, false);
for (const id of ['postNotificationsQueueRetryAll', 'postNotificationsQueueClear'])
assert.equal(
operationAcceptance58e2204.find((x) => x.operationId === id)!.resourceBulk,
true,
id,
);
for (const id of [
'postSmsClear',
'postCallHistoryClear',
'postNotificationsLogsClear',
'postAutomationLogsClear',
])
assert.equal(
operationAcceptance58e2204.find((x) => x.operationId === id)!.resourceBulk,
false,
id,
);
const health = operationAcceptance58e2204.find((x) => x.operationId === 'getHealth')!;
assert.equal(health.fleetBatchable, true);
assert.equal(health.partialAggregationPolicy, 'per-item');
});
test('matrix renders metadata-only safe fixture categories for every operation',()=>{const doc=renderOperationAcceptanceMatrix();const safeCategories=new Set(['success','unsupported','auth-required']);const fixture=new Map(fixtureDisposition58e2204.map(x=>[x.operationId,x]));const rows=[...doc.matchAll(/^\| `([^`]+)` \|.*?\| ([^|]*categories=([^;|]+);[^|]*) \|/gm)].filter(match=>byId.has(match[1]));assert.equal(rows.length,117);for(const [,id,metadata,rendered] of rows){const disposition=fixture.get(id)!;const expected=disposition.observedCategories.length?[...disposition.observedCategories].sort().join(','):'none';assert.equal(rendered.trim(),expected,id);for(const category of disposition.observedCategories)assert.ok(safeCategories.has(category),`${id}:${category}`);assert.doesNotMatch(metadata,/"(?:response|body|sourceInstanceAlias)"\s*:|https?:\/\//i,id);}assert.match(doc,/metadata-only/i);});
test('fixture disposition is authoritative and exactly follows collector plus 78-file manifest', () => {
assert.equal(fixtureDisposition58e2204.length, 117);
assert.equal(new Set(fixtureDisposition58e2204.map((x) => x.operationId)).size, 117);
const selected = selectReadonlyOperations(upstream58e2204Operations as any[]);
assert.equal(selected.selected.length, 39);
assert.equal(selected.denied.length, 11);
assert.equal(Object.keys(DENY_REASONS).length, 11);
const selectedIds = new Set(selected.selected.map((x: any) => x.operationId));
const denied = new Map(selected.denied.map((x: any) => [x.operationId, x.denyReason]));
const filesById = new Map<string, any[]>();
for (const file of manifest.files) {
const match = /instance-[12]--([^.]+)\.json$/.exec(file.path);
assert.ok(match);
const list = filesById.get(match[1]) ?? [];
list.push(file);
filesById.set(match[1], list);
}
for (const d of fixtureDisposition58e2204) {
if (selectedIds.has(d.operationId)) {
assert.equal(d.runtimeFixtureStatus, 'captured-readonly');
assert.equal(d.fixtureCount, 2);
assert.equal(filesById.get(d.operationId)?.length, 2);
assert.deepEqual(d.aliases, ['instance-1', 'instance-2']);
assert.ok(d.observedCategories.length);
} else if (denied.has(d.operationId)) {
assert.equal(d.runtimeFixtureStatus, 'denied-readonly-candidate');
assert.equal(d.reason, denied.get(d.operationId));
assert.equal(d.fixtureCount, 0);
} else {
assert.match(d.runtimeFixtureStatus, /^not-eligible-readonly-capture/);
assert.equal(d.fixtureCount, 0);
}
}
assert.equal(manifest.realFixtureCount, 78);
});
test('product risk prose defers to Registry and known conflicts stay corrected', async () => {
const productRoot = fileURLToPath(new URL('../../../docs/product/', import.meta.url));
const workflows = await readFile(`${productRoot}personas-and-workflows.md`, 'utf8');
const charter = await readFile(`${productRoot}project-charter.md`, 'utf8');
assert.doesNotMatch(workflows, /下载 profile[^\n]*R2 Job/);
assert.match(workflows, /WLAN connect 为 R1/);
assert.match(workflows, /notifications config 为 R2 Job/);
assert.match(workflows, /fleetBatchable=true/);
assert.match(charter, /resourceBulk.*fleetBatchable/);
for (const id of [
'postEsimProfiles',
'postEsimProfilesIccidEnable',
'postDeviceNetworkWlanConnect',
'postNotificationsConfig',
'postAutomationConfig',
'postDeviceNetworkDdnsConfig',
'postBasebandRestart',
]) {
assert.ok(
operationAcceptance58e2204.find((x) => x.operationId === id),
id,
);
}
});
test('generated matrix is synchronized and contains each operation exactly once', async () => {
const doc = await readFile(matrixPath, 'utf8');
assert.equal(doc, renderOperationAcceptanceMatrix());
const ids = [...doc.matchAll(/^\| `([^`]+)` \|/gm)].map((x) => x[1]).filter((id) => byId.has(id));
assert.equal(ids.length, 117);
assert.deepEqual(ids.sort(), [...byId.keys()].sort());
});
test('matrix renders metadata-only safe fixture categories for every operation', () => {
const doc = renderOperationAcceptanceMatrix();
const safeCategories = new Set(['success', 'unsupported', 'auth-required']);
const fixture = new Map(fixtureDisposition58e2204.map((x) => [x.operationId, x]));
const rows = [
...doc.matchAll(/^\| `([^`]+)` \|.*?\| ([^|]*categories=([^;|]+);[^|]*) \|/gm),
].filter((match) => byId.has(match[1]));
assert.equal(rows.length, 117);
for (const [, id, metadata, rendered] of rows) {
const disposition = fixture.get(id)!;
const expected = disposition.observedCategories.length
? [...disposition.observedCategories].sort().join(',')
: 'none';
assert.equal(rendered.trim(), expected, id);
for (const category of disposition.observedCategories)
assert.ok(safeCategories.has(category), `${id}:${category}`);
assert.doesNotMatch(metadata, /"(?:response|body|sourceInstanceAlias)"\s*:|https?:\/\//i, id);
}
assert.match(doc, /metadata-only/i);
});