docs: close Phase 0 acceptance gate

This commit is contained in:
chick
2026-07-16 08:07:58 +08:00
parent c7da857aff
commit 43a69d8617
10 changed files with 473 additions and 23 deletions
@@ -0,0 +1,116 @@
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { upstream58e2204Operations } from './upstream-58e2204.ts';
import { DENY_REASONS, selectReadonlyOperations } from '../../test-fixtures/scripts/collector.ts';
type Risk='R0'|'R1'|'R2'|'R3';
export type RuntimeFixtureStatus='captured-readonly'|'denied-readonly-candidate'|'not-eligible-readonly-capture'|'not-eligible-readonly-capture-parameterized-r0';
const routeByOwner:Record<string,string>={fleet:'/fleet','instances-new':'/instances/new','settings-instance':'/settings/instances/:id',overview:'/instances/:id/overview',cellular:'/instances/:id/cellular','device-network':'/instances/:id/device-network',messages:'/instances/:id/messages',calls:'/instances/:id/calls',esim:'/instances/:id/esim',notifications:'/instances/:id/notifications',automation:'/instances/:id/automation',ota:'/instances/:id/ota'};
const dedicatedAuth=new Set(['postAuthSetup','postAuthPassword','postAuthSettings','postAuthLogin','postAuthLogout']);
const resourceBulk=new Set(['postSmsBatchDelete','postNotificationsQueueRetryAll','postNotificationsQueueClear']);
const fixtureManifest=JSON.parse(readFileSync(fileURLToPath(new URL('../../test-fixtures/src/manifest.json',import.meta.url)),'utf8'));
const fixtureRoot=fileURLToPath(new URL('../../test-fixtures/',import.meta.url));
const selected=selectReadonlyOperations(upstream58e2204Operations as any[]);
const selectedIds=new Set(selected.selected.map((x:any)=>x.operationId));
const deniedById=new Map(selected.denied.map((x:any)=>[x.operationId,x.denyReason]));
const observations=new Map<string,{aliases:Set<string>,categories:Set<string>,count:number}>();
for(const file of fixtureManifest.files){const envelope=JSON.parse(readFileSync(`${fixtureRoot}${file.path}`,'utf8'));const current=observations.get(envelope.operationId)??{aliases:new Set(),categories:new Set(),count:0};current.aliases.add(envelope.sourceInstanceAlias);current.categories.add(envelope.statusCategory);current.count++;observations.set(envelope.operationId,current);}
export const fixtureDisposition58e2204=upstream58e2204Operations.map((op:any)=>{const observed=observations.get(op.operationId);if(selectedIds.has(op.operationId))return {operationId:op.operationId,runtimeFixtureStatus:'captured-readonly' as RuntimeFixtureStatus,fixtureCount:observed?.count??0,aliases:[...(observed?.aliases??[])].sort(),observedCategories:[...(observed?.categories??[])].sort(),reason:'collector-selected fully-bound GET R0; two redacted real captures'};const deny=deniedById.get(op.operationId);if(deny)return {operationId:op.operationId,runtimeFixtureStatus:'denied-readonly-candidate' as RuntimeFixtureStatus,fixtureCount:0,aliases:[],observedCategories:[],reason:deny};const parameterized=op.method==='GET'&&op.riskLevel==='R0'&&/[{}]/.test(op.pathTemplate);return {operationId:op.operationId,runtimeFixtureStatus:(parameterized?'not-eligible-readonly-capture-parameterized-r0':'not-eligible-readonly-capture') as RuntimeFixtureStatus,fixtureCount:0,aliases:[],observedCategories:[],reason:parameterized?'R0 read requires bound path parameters; collector accepts only fully-bound Registry paths':'not a collector-eligible fully-bound GET R0 operation'};});
const fixtureById=new Map(fixtureDisposition58e2204.map(x=>[x.operationId,x]));
export const acceptancePolicyCatalog={
preconditionPolicyId:{'query-capability':'Registered instance, authorized actor, and runtime capability decision are required.','command-current-state':'Refresh current state when available and bind the actor decision to its revision.','job-fresh-preflight':'Required fresh preflight; inability to refresh blocks execution.','auth-dedicated':'Dedicated auth endpoint, actor/session/instance rate limits, and fresh preflight of authentication state are required.'},
stalePolicyId:{'read-retain':'Retain last success as stale and permit an explicit safe refresh.','write-revision':'Refresh current state or require an explicit recorded revision-bound override.','high-risk-block':'Fresh preflight is mandatory; stale, expired, or unknown state blocks execution.','auth-no-replay':'Credential state is never inferred from stale data; require actor-initiated auth flow and no replay.'},
unsupportedPolicyId:{'version-probe':'404/405/501 is capability evidence for this frozen version, not permanent product unsupported; retain probe evidence.','auth-version-probe':'Auth 404/405/501 is version evidence; do not downgrade to a generic proxy or expose credentials.','ota-version-probe':'OTA 404/405/501 is frozen-version evidence; preserve release/status probe context and never infer global support.'},
retryPolicyId:{'safe-read':'Safe reads may retry within deadline and retain stale success.','verify-write':'Writes require outcome verification before actor-initiated retry.','new-job-lineage':'Retry creates a new Job lineage (retryOfJobId/rootJobId); never replay successful items.','auth-actor-only':'No automatic replay; require an actor-initiated dedicated flow.'},
resultPolicyId:{'route-result':'Structured result returns to the owning route with a redacted audit link.','job-result':'/jobs/:jobId contains per-item result, attempt lineage, and redacted audit.','auth-result':'Dedicated auth jobs return through /jobs/:jobId; session flows return a credential metadata-only audit result and link.'}
} as const;
/* Exact declarative UI partition. Every operation ID occurs in exactly one group; construction below throws on overlap/omission. */
const uiGroups:Record<string,string[]>= {
detail:['getDevice','getSim','getNetwork','getHealth','getAuthStatus','getAuthSettings'],
table:['getCells','getNetworkInterfaces','getNetworkOperators','getDeviceNetworkWlanProfiles','getEsimProfiles','getCalls','getCallHistory','getSmsList','getSmsConversation','getNotificationsLogs','getNotificationsQueue','getAutomationLogs'],
'dedicated-auth-flow':['postAuthSetup','postAuthPassword','postAuthSettings','postAuthLogin','postAuthLogout'],
'dedicated-upload-flow':['postOtaUpload'],
'job-action':['postBandLock','postDeviceNetworkDdnsConfig','postDeviceNetworkDdnsLogsClear','postDeviceNetworkWlanForget','postNetworkRegisterManual','postNetworkRegisterAuto','postCellLock','postCellLockUnlockAll','postBasebandRestart','postWorkMode','postEsimConfig','postEsimLpacRepair','deleteEsimProfilesIccid','postCallSettings','deleteCallHistoryId','postCallHistoryClear','postSmsSend','postSmsBatchDelete','deleteSmsConversationPhoneNumber','deleteSmsMessageId','postSmsClear','postSystemReboot','postServiceRestart','postNotificationsConfig','postNotificationsTestChannel','postNotificationsLogsClear','postNotificationsQueueRetryAll','postNotificationsQueueClear','deleteNotificationsQueueId','postNotificationsQueueIdRetry','postAutomationConfig','postAutomationLogsClear','postAutomationTestTaskId','postOtaApply','postOtaOnlinePrepare'],
form:['postSimCache','postRadioMode','postDeviceNetworkWlanEnabled','postDeviceNetworkWlanConnect','postDeviceNetworkWlanProfile','postApn','postData','postRoaming','postAirplaneMode','postEsimProfiles','postEsimProfilesIccidRename','postCallDial','postCallHangup','postCallAnswer','postCallVolume','postCallForwarding','postOtaLatestRelease'],
action:['postSimDetailsRefresh','postCellMonitorStart','postCellMonitorStop','postDeviceNetworkDdnsSync','postDeviceNetworkWlanScan','postDeviceNetworkWlanDisconnect','postEsimProfilesIccidEnable','postCallHangupAll','postOtaCancel'],
'read-panel':['getRadioMode','getBandLock','getNetworkConnectionAddresses','getDeviceNetworkDdnsConfig','getDeviceNetworkDdnsStatus','getDeviceNetworkDdnsLogs','getDeviceNetworkWlanStatus','getNetworkSignalStrength','getLocationCellInfo','getNetworkOperatorsScan','getApn','getCellLock','getData','getRoaming','getAirplaneMode','getBasebandRestartStatus','getWorkMode','getEsimConfig','getEsimLpacStatus','getEsimEuicc','getCallVolume','getCallForwarding','getCallSettings','getImsStatus','getVoicemailStatus','getSmsStats','getStats','getStatsCpu','getConnectivity','getNotificationsConfig','getAutomationConfig','getOtaStatus']
};
const strategyById=new Map<string,string>();for(const [strategy,ids] of Object.entries(uiGroups))for(const id of ids){if(strategyById.has(id))throw new Error(`duplicate acceptance override ${id}`);strategyById.set(id,strategy);}
const operationIds=new Set(upstream58e2204Operations.map((x:any)=>x.operationId));if(strategyById.size!==operationIds.size||[...operationIds].some(id=>!strategyById.has(id)))throw new Error('acceptance override partition must exactly cover frozen 117 operations');
const route=(owner:string)=>routeByOwner[owner];
export const surfaceGroups58e2204=[
{surfaceId:'overview/device',primaryRoute:route('overview'),ids:['getDevice']},{surfaceId:'overview/sim',primaryRoute:route('overview'),ids:['getSim','postSimDetailsRefresh','postSimCache']},{surfaceId:'overview/stats',primaryRoute:route('overview'),ids:['getStats','getStatsCpu']},{surfaceId:'overview/auth-health',primaryRoute:route('overview'),ids:['getConnectivity']},{surfaceId:'overview/system-actions',primaryRoute:route('overview'),ids:['postSystemReboot','postServiceRestart']},
{surfaceId:'cellular/serving-network',primaryRoute:route('cellular'),ids:['getNetwork']},{surfaceId:'cellular/cells',primaryRoute:route('cellular'),ids:['getCells','postCellMonitorStart','postCellMonitorStop','getLocationCellInfo']},{surfaceId:'cellular/signal',primaryRoute:route('cellular'),ids:['getNetworkSignalStrength']},{surfaceId:'cellular/operator-registration',primaryRoute:route('cellular'),ids:['getNetworkOperators','getNetworkOperatorsScan','postNetworkRegisterManual','postNetworkRegisterAuto']},{surfaceId:'cellular/radio-locks',primaryRoute:route('cellular'),ids:['getRadioMode','postRadioMode','getBandLock','postBandLock','getCellLock','postCellLock','postCellLockUnlockAll']},{surfaceId:'cellular/data-apn',primaryRoute:route('cellular'),ids:['getApn','postApn','getData','postData','getRoaming','postRoaming','getAirplaneMode','postAirplaneMode','postBasebandRestart','getBasebandRestartStatus']},
{surfaceId:'device-network/interfaces',primaryRoute:route('device-network'),ids:['getNetworkInterfaces','getNetworkConnectionAddresses']},{surfaceId:'device-network/ddns',primaryRoute:route('device-network'),ids:['getDeviceNetworkDdnsConfig','postDeviceNetworkDdnsConfig','getDeviceNetworkDdnsStatus','postDeviceNetworkDdnsSync','getDeviceNetworkDdnsLogs','postDeviceNetworkDdnsLogsClear']},{surfaceId:'device-network/wlan',primaryRoute:route('device-network'),ids:['getDeviceNetworkWlanStatus','postDeviceNetworkWlanEnabled','postDeviceNetworkWlanScan','getDeviceNetworkWlanProfiles','postDeviceNetworkWlanForget','postDeviceNetworkWlanConnect','postDeviceNetworkWlanDisconnect','postDeviceNetworkWlanProfile']},
{surfaceId:'calls/live',primaryRoute:route('calls'),ids:['getCalls','postCallDial','postCallHangup','postCallHangupAll','postCallAnswer']},{surfaceId:'calls/volume',primaryRoute:route('calls'),ids:['getCallVolume','postCallVolume']},{surfaceId:'calls/forwarding',primaryRoute:route('calls'),ids:['getCallForwarding','postCallForwarding']},{surfaceId:'calls/settings',primaryRoute:route('calls'),ids:['getCallSettings','postCallSettings']},{surfaceId:'calls/history',primaryRoute:route('calls'),ids:['getCallHistory','deleteCallHistoryId','postCallHistoryClear']},{surfaceId:'calls/ims-voicemail',primaryRoute:route('calls'),ids:['getImsStatus','getVoicemailStatus']},
{surfaceId:'messages/stats',primaryRoute:route('messages'),ids:['getSmsStats']},{surfaceId:'messages/list',primaryRoute:route('messages'),ids:['getSmsList']},{surfaceId:'messages/conversation',primaryRoute:route('messages'),ids:['getSmsConversation']},{surfaceId:'messages/compose',primaryRoute:route('messages'),ids:['postSmsSend']},{surfaceId:'messages/destructive',primaryRoute:route('messages'),ids:['postSmsBatchDelete','deleteSmsConversationPhoneNumber','deleteSmsMessageId','postSmsClear']},
{surfaceId:'esim/mode',primaryRoute:route('esim'),ids:['getWorkMode','postWorkMode']},{surfaceId:'esim/config',primaryRoute:route('esim'),ids:['getEsimConfig','postEsimConfig']},{surfaceId:'esim/lpac',primaryRoute:route('esim'),ids:['getEsimLpacStatus','postEsimLpacRepair']},{surfaceId:'esim/euicc',primaryRoute:route('esim'),ids:['getEsimEuicc']},{surfaceId:'esim/profiles',primaryRoute:route('esim'),ids:['getEsimProfiles','postEsimProfiles','postEsimProfilesIccidEnable','postEsimProfilesIccidRename','deleteEsimProfilesIccid']},
{surfaceId:'notifications/config',primaryRoute:route('notifications'),ids:['getNotificationsConfig','postNotificationsConfig','postNotificationsTestChannel']},{surfaceId:'notifications/logs',primaryRoute:route('notifications'),ids:['getNotificationsLogs','postNotificationsLogsClear']},{surfaceId:'notifications/queue',primaryRoute:route('notifications'),ids:['getNotificationsQueue','postNotificationsQueueRetryAll','postNotificationsQueueClear','deleteNotificationsQueueId','postNotificationsQueueIdRetry']},
{surfaceId:'automation/config',primaryRoute:route('automation'),ids:['getAutomationConfig','postAutomationConfig']},{surfaceId:'automation/logs',primaryRoute:route('automation'),ids:['getAutomationLogs','postAutomationLogsClear']},{surfaceId:'automation/task-test',primaryRoute:route('automation'),ids:['postAutomationTestTaskId']},
{surfaceId:'ota/status',primaryRoute:route('ota'),ids:['getOtaStatus','postOtaCancel']},{surfaceId:'ota/release',primaryRoute:route('ota'),ids:['postOtaLatestRelease','postOtaOnlinePrepare']},{surfaceId:'ota/upload',primaryRoute:route('ota'),ids:['postOtaUpload']},{surfaceId:'ota/apply',primaryRoute:route('ota'),ids:['postOtaApply']},
{surfaceId:'settings/auth-settings',primaryRoute:route('settings-instance'),ids:['postAuthPassword','getAuthSettings','postAuthSettings','postAuthLogout']},{surfaceId:'settings/auth-entry',primaryRoute:route('instances-new'),ids:['getHealth','getAuthStatus','postAuthSetup','postAuthLogin']}
] as const;
const policies=(preconditionPolicyId:string,stalePolicyId:string,retryPolicyId:string,resultPolicyId:string,unsupportedPolicyId='version-probe')=>({preconditionPolicyId,stalePolicyId,unsupportedPolicyId,retryPolicyId,resultPolicyId});
export const policyGroups58e2204=[
{groupId:'query-list',ids:['getCells','getNetworkInterfaces','getDeviceNetworkDdnsLogs','getDeviceNetworkWlanProfiles','getNetworkOperators','getEsimProfiles','getCalls','getCallHistory','getSmsList','getSmsConversation','getNotificationsLogs','getNotificationsQueue','getAutomationLogs'],policies:policies('query-capability','read-retain','safe-read','route-result')},
{groupId:'query-detail',ids:['getDevice','getSim','getNetwork','getRadioMode','getBandLock','getNetworkConnectionAddresses','getDeviceNetworkDdnsConfig','getDeviceNetworkDdnsStatus','getDeviceNetworkWlanStatus','getNetworkSignalStrength','getLocationCellInfo','getApn','getCellLock','getData','getRoaming','getAirplaneMode','getBasebandRestartStatus','getWorkMode','getEsimConfig','getEsimLpacStatus','getEsimEuicc','getCallVolume','getCallForwarding','getCallSettings','getImsStatus','getVoicemailStatus','getSmsStats','getNotificationsConfig','getAutomationConfig','getOtaStatus'],policies:policies('query-capability','read-retain','safe-read','route-result')},
{groupId:'query-composite',ids:['getStats','getStatsCpu','getConnectivity'],policies:policies('command-current-state','read-retain','safe-read','route-result')},
{groupId:'query-active-scan',ids:['getNetworkOperatorsScan'],policies:policies('command-current-state','write-revision','verify-write','route-result')},
{groupId:'query-auth-detail',ids:['getAuthSettings','getHealth','getAuthStatus'],policies:policies('query-capability','auth-no-replay','auth-actor-only','auth-result','auth-version-probe')},
{groupId:'direct-reversible-setting',ids:['postSimDetailsRefresh','postSimCache','postCellMonitorStart','postCellMonitorStop','postRadioMode','postDeviceNetworkDdnsSync','postDeviceNetworkWlanEnabled','postDeviceNetworkWlanScan','postDeviceNetworkWlanConnect','postDeviceNetworkWlanDisconnect','postDeviceNetworkWlanProfile','postApn','postData','postRoaming','postAirplaneMode','postEsimProfiles','postEsimProfilesIccidEnable','postEsimProfilesIccidRename','postCallDial','postCallHangup','postCallHangupAll','postCallAnswer','postCallVolume','postCallForwarding'],policies:policies('command-current-state','write-revision','verify-write','route-result')},
{groupId:'session-auth-flow',ids:['postAuthLogin','postAuthLogout'],policies:policies('auth-dedicated','auth-no-replay','auth-actor-only','auth-result','auth-version-probe')},
{groupId:'high-risk-network-job',ids:['postBandLock','postDeviceNetworkDdnsConfig','postDeviceNetworkDdnsLogsClear','postDeviceNetworkWlanForget','postNetworkRegisterManual','postNetworkRegisterAuto','postCellLock','postCellLockUnlockAll','postBasebandRestart','postWorkMode','postEsimConfig','postEsimLpacRepair','postCallSettings','postSmsSend','postNotificationsConfig','postNotificationsTestChannel','postNotificationsQueueRetryAll','postNotificationsQueueIdRetry','postAutomationConfig','postAutomationTestTaskId'],policies:policies('job-fresh-preflight','high-risk-block','new-job-lineage','job-result')},
{groupId:'destructive-cleanup-job',ids:['deleteEsimProfilesIccid','deleteCallHistoryId','postCallHistoryClear','postSmsBatchDelete','deleteSmsConversationPhoneNumber','deleteSmsMessageId','postSmsClear','postSystemReboot','postServiceRestart','postNotificationsLogsClear','postNotificationsQueueClear','deleteNotificationsQueueId','postAutomationLogsClear'],policies:policies('job-fresh-preflight','high-risk-block','new-job-lineage','job-result')},
{groupId:'ota-direct-control',ids:['postOtaLatestRelease','postOtaCancel'],policies:policies('command-current-state','write-revision','verify-write','route-result')},
{groupId:'ota-job',ids:['postOtaUpload','postOtaOnlinePrepare','postOtaApply'],policies:policies('job-fresh-preflight','high-risk-block','new-job-lineage','job-result','ota-version-probe')},
{groupId:'high-risk-auth-flow',ids:['postAuthPassword','postAuthSettings','postAuthSetup'],policies:policies('auth-dedicated','auth-no-replay','new-job-lineage','auth-result','auth-version-probe')}
] as const;
const readScenario=(empty:boolean,partial:boolean)=>({empty:{applicable:empty,rationale:empty?'Successful collection may contain no rows.':'Detail resource has no collection-empty rendering.'},partial:{applicable:partial,rationale:partial?'Independent response sections may succeed or fail.':'Atomic detail read has no meaningful partial rendering.'},'policy-forbidden':{applicable:false,rationale:'Safe read has no write-confirmation policy; support remains orthogonal.'},'unknown-result':{applicable:false,rationale:'Read failure is an error/freshness state, not unknown write outcome.'},'owner-switch':{applicable:true,rationale:'Owner switch discards responses with a mismatched instance key.'}});
const writeScenario=(partial:boolean)=>({empty:{applicable:false,rationale:'Command result is not a collection-empty state.'},partial:{applicable:partial,rationale:partial?'Job items may independently succeed, fail, or be skipped.':'Single-target command has no meaningful partial result.'},'policy-forbidden':{applicable:true,rationale:'Write can be blocked by capability, authorization, risk, or preflight policy.'},'unknown-result':{applicable:true,rationale:'Disconnect or timeout requires outcome verification before retry.'},'owner-switch':{applicable:true,rationale:'Owner switch preserves execution lineage but discards mismatched route responses.'}});
const activeScanScenario={empty:{applicable:true,rationale:'A successful active operator scan may return no operators.'},partial:{applicable:true,rationale:'The scan response may contain usable operators alongside per-result failures or incomplete entries.'},'policy-forbidden':{applicable:true,rationale:'Active scan may be blocked by capability, authorization, preflight, or active-scan policy.'},'unknown-result':{applicable:false,rationale:'A timed-out scan result may be discarded; it is not an unknown persistent write outcome.'},'owner-switch':{applicable:true,rationale:'Owner switch discards scan responses with a mismatched instance key.'}};
export const scenarioGroups58e2204=[
{profileId:'list-read',ids:['getCells','getNetworkInterfaces','getDeviceNetworkDdnsLogs','getDeviceNetworkWlanProfiles','getNetworkOperators','getEsimProfiles','getCalls','getCallHistory','getSmsList','getSmsConversation','getNotificationsLogs','getNotificationsQueue','getAutomationLogs'],scenarios:readScenario(true,true)},
{profileId:'detail-read',ids:['getDevice','getSim','getNetwork','getRadioMode','getBandLock','getNetworkConnectionAddresses','getDeviceNetworkDdnsConfig','getDeviceNetworkDdnsStatus','getDeviceNetworkWlanStatus','getNetworkSignalStrength','getLocationCellInfo','getApn','getCellLock','getData','getRoaming','getAirplaneMode','getBasebandRestartStatus','getWorkMode','getEsimConfig','getEsimLpacStatus','getEsimEuicc','getCallVolume','getCallForwarding','getCallSettings','getImsStatus','getVoicemailStatus','getSmsStats','getNotificationsConfig','getAutomationConfig','getOtaStatus','getAuthSettings','getHealth','getAuthStatus'],scenarios:readScenario(false,false)},
{profileId:'composite-read',ids:['getStats','getStatsCpu','getConnectivity'],scenarios:readScenario(false,true)},
{profileId:'active-scan',ids:['getNetworkOperatorsScan'],scenarios:activeScanScenario},
{profileId:'direct-write',ids:['postSimDetailsRefresh','postSimCache','postCellMonitorStart','postCellMonitorStop','postRadioMode','postDeviceNetworkDdnsSync','postDeviceNetworkWlanEnabled','postDeviceNetworkWlanScan','postDeviceNetworkWlanConnect','postDeviceNetworkWlanDisconnect','postDeviceNetworkWlanProfile','postApn','postData','postRoaming','postAirplaneMode','postEsimProfiles','postEsimProfilesIccidEnable','postEsimProfilesIccidRename','postCallDial','postCallHangup','postCallHangupAll','postCallAnswer','postCallVolume','postCallForwarding'],scenarios:writeScenario(false)},
{profileId:'job-single',ids:['postBandLock','postDeviceNetworkDdnsConfig','postDeviceNetworkDdnsLogsClear','postDeviceNetworkWlanForget','postNetworkRegisterManual','postNetworkRegisterAuto','postCellLock','postCellLockUnlockAll','postWorkMode','postEsimConfig','postEsimLpacRepair','postCallSettings','postSmsSend','postNotificationsConfig','postNotificationsTestChannel','postNotificationsQueueIdRetry','postAutomationConfig','postAutomationTestTaskId','deleteEsimProfilesIccid','deleteCallHistoryId','postCallHistoryClear','deleteSmsConversationPhoneNumber','deleteSmsMessageId','postSmsClear','postBasebandRestart','postSystemReboot','postServiceRestart','postNotificationsLogsClear','deleteNotificationsQueueId','postAutomationLogsClear'],scenarios:writeScenario(false)},
{profileId:'job-resource-bulk',ids:['postSmsBatchDelete','postNotificationsQueueRetryAll','postNotificationsQueueClear'],scenarios:writeScenario(true)},
{profileId:'ota-direct',ids:['postOtaLatestRelease','postOtaCancel'],scenarios:writeScenario(false)},
{profileId:'ota-job-single',ids:['postOtaUpload','postOtaOnlinePrepare','postOtaApply'],scenarios:writeScenario(false)},
{profileId:'auth-flow',ids:['postAuthPassword','postAuthSettings','postAuthSetup','postAuthLogin','postAuthLogout'],scenarios:writeScenario(false)}
] as const;
const versionEvidencePolicy='Persist the audited commit plus runtime capability probes; 404/405/501 is version-scoped evidence and never a permanent global verdict.';
export const availabilityGroups58e2204=[
{groupId:'captured-real-read',realAcceptance:'REAL_READ',availability:'planned',reason:'Frozen-scope read has two redacted captured responses; runtime support is still decided per registered instance.',versionEvidencePolicy,ids:['getDevice','getSim','getNetwork','getCells','getRadioMode','getBandLock','getNetworkInterfaces','getNetworkConnectionAddresses','getDeviceNetworkDdnsConfig','getDeviceNetworkDdnsStatus','getDeviceNetworkWlanStatus','getDeviceNetworkWlanProfiles','getNetworkSignalStrength','getLocationCellInfo','getNetworkOperators','getApn','getCellLock','getData','getRoaming','getAirplaneMode','getBasebandRestartStatus','getWorkMode','getEsimConfig','getEsimLpacStatus','getCalls','getCallVolume','getCallForwarding','getCallSettings','getImsStatus','getVoicemailStatus','getSmsStats','getStats','getStatsCpu','getNotificationsConfig','getAutomationConfig','getOtaStatus','getAuthSettings','getHealth','getAuthStatus']},
{groupId:'denied-read-candidate',realAcceptance:'REAL_READ_DEFERRED',availability:'planned',reason:'Frozen-scope read is modeled but real capture was explicitly denied; runtime support is probed per instance.',versionEvidencePolicy,ids:['getDeviceNetworkDdnsLogs','getNetworkOperatorsScan','getEsimEuicc','getEsimProfiles','getCallHistory','getSmsList','getSmsConversation','getConnectivity','getNotificationsLogs','getNotificationsQueue','getAutomationLogs']},
{groupId:'write-later',realAcceptance:'REAL_WRITE_LATER',availability:'planned',reason:'Frozen-scope reversible command is contract-planned for a later controlled real-write acceptance phase.',versionEvidencePolicy,ids:['postSimDetailsRefresh','postSimCache','postCellMonitorStart','postCellMonitorStop','postRadioMode','postDeviceNetworkDdnsSync','postDeviceNetworkWlanEnabled','postDeviceNetworkWlanScan','postDeviceNetworkWlanConnect','postDeviceNetworkWlanDisconnect','postDeviceNetworkWlanProfile','postApn','postData','postRoaming','postAirplaneMode','postEsimProfiles','postEsimProfilesIccidEnable','postEsimProfilesIccidRename','postCallDial','postCallHangup','postCallHangupAll','postCallAnswer','postCallVolume','postCallForwarding','postOtaLatestRelease','postOtaCancel','postAuthLogin','postAuthLogout']},
{groupId:'high-risk-simulated',realAcceptance:'SIMULATED_HIGH_RISK',availability:'planned',reason:'Frozen-scope high-risk operation is planned with simulation-only acceptance until controlled safety authorization exists.',versionEvidencePolicy,ids:['postBandLock','postDeviceNetworkDdnsConfig','postDeviceNetworkDdnsLogsClear','postDeviceNetworkWlanForget','postNetworkRegisterManual','postNetworkRegisterAuto','postCellLock','postCellLockUnlockAll','postBasebandRestart','postWorkMode','postEsimConfig','postEsimLpacRepair','deleteEsimProfilesIccid','postCallSettings','deleteCallHistoryId','postCallHistoryClear','postSmsSend','postSmsBatchDelete','deleteSmsConversationPhoneNumber','deleteSmsMessageId','postSmsClear','postSystemReboot','postServiceRestart','postNotificationsConfig','postNotificationsTestChannel','postNotificationsLogsClear','postNotificationsQueueRetryAll','postNotificationsQueueClear','deleteNotificationsQueueId','postNotificationsQueueIdRetry','postAutomationConfig','postAutomationLogsClear','postAutomationTestTaskId','postOtaUpload','postOtaOnlinePrepare','postOtaApply','postAuthPassword','postAuthSettings','postAuthSetup']}
] as const;
function exactLookup(groups:readonly any[],label:string){const map=new Map<string,any>();for(const group of groups)for(const id of group.ids){if(map.has(id))throw new Error(`${label} overlap: ${id}`);map.set(id,group);}for(const id of map.keys())if(!operationIds.has(id))throw new Error(`${label} unknown operation: ${id}`);if(map.size!==operationIds.size||[...operationIds].some(id=>!map.has(id)))throw new Error(`${label} must exactly cover frozen 117 operations`);return map;}
const surfaceById=exactLookup(surfaceGroups58e2204,'surface');const policyById=exactLookup(policyGroups58e2204,'policy');const scenarioById=exactLookup(scenarioGroups58e2204,'scenario');const availabilityById=exactLookup(availabilityGroups58e2204,'availability');
export const operationAcceptanceOverrides58e2204=Object.fromEntries(upstream58e2204Operations.map((op:any)=>{const surface=surfaceById.get(op.operationId);const policy=policyById.get(op.operationId);const scenario=scenarioById.get(op.operationId);const availability=availabilityById.get(op.operationId);if(!surface||!policy||!scenario||!availability)throw new Error(`missing exact acceptance decision: ${op.operationId}`);return [op.operationId,{surfaceId:surface.surfaceId,primaryRoute:surface.primaryRoute,uiStrategy:strategyById.get(op.operationId),...policy.policies,scenarioAcceptance:scenario.scenarios,availability:availability.availability,availabilityReason:availability.reason,versionEvidencePolicy:availability.versionEvidencePolicy,realAcceptance:availability.realAcceptance}];}));
export const operationAcceptance58e2204=upstream58e2204Operations.map((op:any)=>{const override:any=operationAcceptanceOverrides58e2204[op.operationId];const policies:any=acceptancePolicyCatalog;const high=['R2','R3'].includes(op.riskLevel);const auth=dedicatedAuth.has(op.operationId);const scenarios=override.scenarioAcceptance;return {operationId:op.operationId,method:op.method,pathTemplate:op.pathTemplate,upstreamDomain:op.upstreamDomain,primaryRoute:override.primaryRoute,surfaceId:override.surfaceId,uiStrategy:override.uiStrategy,availability:override.availability,availabilityReason:override.availabilityReason,versionEvidencePolicy:override.versionEvidencePolicy,requiredStates:{request:['idle','loading','success','error'],freshness:['fresh','stale','expired','unknown'],support:['supported','unsupported','auth-required','degraded','unknown'],scenarios:Object.entries(scenarios).filter(([,v]:any)=>v.applicable).map(([k])=>k),naRationale:'Non-applicable scenarios retain an explicit rationale in scenarioAcceptance.'},scenarioAcceptance:scenarios,riskLevel:op.riskLevel as Risk,confirmationUX:op.confirmationPolicy,executionMode:auth?'dedicated-flow':high?'preparation-job':'direct',executionDerivation:auth?'Registry executionPolicy=dedicatedFlow binds the dedicated authentication orchestration.':high?'Registry capability=job plus product prepare-confirm-execute policy derives preparation-job orchestration.':'Registry capability plus acceptance policy derives direct structured execution.',executionPolicySource:'Registry supplies risk, confirmation, capability and dedicatedFlow; acceptance policy supplies orchestration.',...Object.fromEntries(['preconditionPolicyId','stalePolicyId','unsupportedPolicyId','retryPolicyId','resultPolicyId'].map(k=>[k,override[k]])),preconditions:policies.preconditionPolicyId[override.preconditionPolicyId],stalePolicy:policies.stalePolicyId[override.stalePolicyId],unsupportedPolicy:policies.unsupportedPolicyId[override.unsupportedPolicyId],retryRecovery:policies.retryPolicyId[override.retryPolicyId],resultDestination:policies.resultPolicyId[override.resultPolicyId],resourceBulk:resourceBulk.has(op.operationId),fleetBatchable:op.operationId==='getHealth',partialAggregationPolicy:op.operationId==='getHealth'?'per-item':resourceBulk.has(op.operationId)?'resource-item':'not-applicable',realAcceptance:override.realAcceptance,evidenceIds:[`registry:${op.operationId}`,...op.sourceEvidence.map((x:string)=>`upstream:${x}`),`fixture:${fixtureById.get(op.operationId)?.runtimeFixtureStatus}`],...(op.sessionSensitive?{sessionSubtype:'session-sensitive'}:{})};});
const flow=(flowId:string,route:string,risk:string,riskSubtype:string,confirmation:string,preconditions:string,result:string,failureRecovery:string,secretPolicy:string,evidence:string)=>({flowId,route,risk,riskSubtype,confirmation,preconditions,result,failureRecovery,secretPolicy,evidence});
export const controlPlaneAcceptance=[
flow('instance-create','/instances/new','R1','configuration','diff-confirm','canonical origin and unique stable ID','created instance revision and audit','retain draft on validation or reconcile failure','secret reference only; never return a value','WF-01 + product policy'),
flow('instance-update','/settings/instances/:id','R1','configuration','revision-diff','current revision','updated revision and audit','409 refreshes diff without losing edits','preserve/set/clear explicit; never return a value','WF-01 + product policy'),
flow('instance-delete','/settings/instances/:id','R3','destructive','strong target restatement','fresh revision and no conflicting job','new jobId with two-phase deleting to tombstone result','before boundary restore; after boundary manual recovery','destroy reference without reading secret value','WF-13'),
flow('secret-set','/settings/instances/:id','R1','secret-reference','explicit set','authorized SecretStore write','new reference metadata and audit','atomic rollback on reference write failure','never return or log secret value','WF-01'),flow('secret-preserve','/settings/instances/:id','R1','secret-reference','explicit preserve','existing reference metadata','unchanged reference metadata and audit','revision conflict refreshes metadata only','never read or return secret value','WF-01'),flow('secret-clear','/settings/instances/:id','R1','secret-reference','explicit clear','fresh revision','reference removed and audit','atomic rollback or explicit recovery state','never return destroyed secret value','WF-01'),
flow('config-import-preview','/settings/instances','R0','preview','none','validated upload and canonical origins','redacted diff/conflict/count preview','fix file and regenerate preview','never include secret values in preview','IA settings import'),flow('config-import-confirm','/settings/instances','R2','bulk-configuration','explicit preview token','fresh unexpired preview','new jobId with per-instance items','retry failed items in new lineage','references only; never import/export secret values','IA settings import'),
flow('credential-verify','/instances/new','R1','session-sensitive','actor initiated','rate limit and registered target','credential metadata-only result','no automatic replay; actor may retry after backoff','ephemeral input, never response/audit value','WF-01'),flow('saved-secret-login','/instances/new','R1','session-sensitive','actor initiated','saved reference and rate limit','session metadata-only result','no automatic replay; return to login flow','resolve server-side; never expose value','WF-12'),flow('temporary-secret-login','/instances/new','R1','session-sensitive','actor initiated','one-shot input and rate limit','session metadata-only result','no automatic replay; require new actor input','memory-only and never saved or returned','WF-12'),flow('logout','/settings/instances/:id','R1','session-sensitive','actor initiated','active session','session cleared and metadata audit','no replay; reconcile auth status','no credential value involved','WF-12'),flow('401-recovery','/instances/:id/overview','R1','session-recovery','actor chooses login','401 evidence and retained stale data','return to owner; retry R0 only','never replay writes; preserve draft for new prepare','temporary/saved secret follows selected login policy','WF-12'),
flow('auth-setup','/instances/new','R3','authentication-change','strong confirmation','fresh dedicated auth preflight','new jobId and metadata-only audit','new lineage after state verification','password never returned or audited','Registry postAuthSetup'),flow('auth-password-change','/settings/instances/:id','R3','authentication-change','strong confirmation','fresh dedicated auth preflight','new jobId and metadata-only audit','new lineage after auth-status verification','old/new password never returned or audited','Registry postAuthPassword'),flow('auth-settings-read','/settings/instances/:id','R0','authentication-read','none','registered instance and actor','structured auth settings','safe actor retry','no password/cookie/token fields','Registry getAuthSettings'),flow('auth-settings-write','/settings/instances/:id','R2','authentication-change','explicit confirmation','fresh dedicated auth preflight','new jobId and metadata-only audit','new lineage; no replay','never return credential values','Registry postAuthSettings'),
flow('job-cancel','/jobs/:jobId','R1','job-control','explicit confirmation','cancelable current phase','immutable cancellation event','show non-cancelable boundary and reconcile','no secret data in event','IA jobs'),flow('job-retry','/jobs/:jobId','R2','job-control','explicit failed-item selection','source terminal state and fresh preflight','new jobId with retryOf/root/source lineage','never mutate source or replay successful items','redacted parameter summary only','WF-10'),flow('audit-export','/audit','R0','data-export','explicit scope','authorized filters','redacted export artifact','regenerate from immutable audit','redaction applies; no sensitiveFields','WF-11'),flow('system-settings-update','/settings/system','R1','configuration','revision-diff','safe bounds and current revision','updated revision and audit','409 refreshes diff; invalid bounds rejected','no secrets accepted','IA settings system')
];
const esc=(v:any)=>String(v).replace(/\|/g,'\\|').replace(/\n/g,' ');
export function renderOperationAcceptanceMatrix(){const controlRows=controlPlaneAcceptance.map(x=>`| \`${x.flowId}\` | \`${x.route}\` | ${x.risk} / ${x.riskSubtype} | ${x.confirmation} | ${esc(x.preconditions)} | ${esc(x.result)} | ${esc(x.failureRecovery)} | ${esc(x.secretPolicy)} | ${esc(x.evidence)} |`).join('\n');const rows=operationAcceptance58e2204.map((x:any)=>{const f:any=fixtureById.get(x.operationId);const scenarios=Object.entries(x.scenarioAcceptance).map(([k,v]:any)=>`${k}:${v.applicable?'Y':'N'} (${v.rationale})`).join('<br>');const policies=`${x.preconditionPolicyId}/${x.stalePolicyId}/${x.unsupportedPolicyId}/${x.retryPolicyId}/${x.resultPolicyId}`;const categories=f.observedCategories.length?[...f.observedCategories].sort().join(','):'none';return `| \`${x.operationId}\` | ${x.method} \`${x.pathTemplate}\` | ${x.upstreamDomain} | \`${x.primaryRoute}\`<br>\`${x.surfaceId}\` | ${x.uiStrategy} | ${x.availability}: ${esc(x.availabilityReason)}<br>${esc(x.versionEvidencePolicy)} | ${scenarios} | ${x.riskLevel}; ${x.confirmationUX}<br>${x.executionMode} | ${policies}<br>${esc(x.preconditions)}<br>${esc(x.stalePolicy)}<br>${esc(x.unsupportedPolicy)}<br>${esc(x.retryRecovery)}<br>${esc(x.resultDestination)} | ${x.realAcceptance} | ${f.runtimeFixtureStatus}; n=${f.fixtureCount}; categories=${categories}; ${esc(f.reason)} | ${x.evidenceIds.map((e:string)=>`\`${e}\``).join('<br>')} |`;}).join('\n');return `# Operation acceptance matrix — upstream 58e2204\n\n> Generated from the structured ledger. Registry is the sole source for risk, confirmation, capability, and dedicated-flow facts; orchestration is bound by acceptance policy. \`registeredOperation\` is not a synonym for preparation Job.\n\nREAL_READ means captured evidence and is restricted to \`captured-readonly\`. REAL_READ_DEFERRED marks denied candidates without a real fixture. Fixture disposition is metadata-only: runtime status, count, safe observed status categories, and reason; it never renders fixture body/response, alias, identity, or URL.\n\n## Control-plane acceptance (independent flows)\n\n| flowId | route | risk / subtype | confirmation | preconditions | result | failure recovery | secret policy | evidence |\n|---|---|---|---|---|---|---|---|---|\n${controlRows}\n\n## Upstream operation acceptance (exact 117)\n\n| operationId | method/path | domain | route/surface | UI | availability/version evidence | scenario applicability | risk/confirmation/execution | policy IDs and resolved policies | real class | fixture disposition (metadata-only: runtime status / n / categories / reason) | evidence |\n|---|---|---|---|---|---|---|---|---|---|---|---|\n${rows}\n`;}
export const fixtureDenyReasons58e2204=DENY_REASONS;
@@ -0,0 +1,69 @@
import test from 'node:test';
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 { 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());};
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: 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('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);});
@@ -0,0 +1,59 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { upstream58e2204Operations } from '../src/upstream-58e2204.ts';
const PHASE_0_2_COMMIT='9dffadbec271227da7ffb192f31dc78c6ee3c2be';
const BASELINE_PATH='packages/operation-registry/src/upstream-58e2204.ts';
const repoRoot=fileURLToPath(new URL('../../../',import.meta.url));
const safetyFields=['operationId','riskLevel','confirmationPolicy','capability','executionPolicy'] as const;
type SafetyRow=Record<(typeof safetyFields)[number],string>;
function git(...args:string[]){return execFileSync('git',args,{cwd:repoRoot,encoding:'utf8',stdio:['ignore','pipe','pipe']}).trim();}
/** Parse only the JSON array literal assigned to the frozen raw operations constant; never evaluate TypeScript. */
export function parseFrozenSafetyBaseline(source:string):SafetyRow[]{
const marker='const rawUpstream58e2204Operations = ';
const start=source.indexOf('[',source.indexOf(marker)+marker.length);
assert.ok(source.includes(marker)&&start>=0,'frozen operation array marker missing');
let quoted=false,escaped=false,depth=0,end=-1;
for(let i=start;i<source.length;i++){
const ch=source[i];
if(quoted){if(escaped)escaped=false;else if(ch==='\\')escaped=true;else if(ch==='"')quoted=false;continue;}
if(ch==='"'){quoted=true;continue;}if(ch==='[')depth++;else if(ch===']'&&--depth===0){end=i+1;break;}
}
assert.ok(end>start,'unterminated frozen operation array');
const parsed=JSON.parse(source.slice(start,end));
assert.ok(Array.isArray(parsed),'frozen operation baseline is not an array');
return parsed.map((row:any,index:number)=>Object.fromEntries(safetyFields.map(field=>{assert.equal(typeof row?.[field],'string',`baseline row ${index} missing ${field}`);return [field,row[field]];})) as SafetyRow);
}
export function assertExactSafetyBaseline(actual:readonly SafetyRow[],expected:readonly SafetyRow[]){
assert.equal(expected.length,117,'independent baseline must contain exactly 117 operations');
assert.equal(new Set(expected.map(x=>x.operationId)).size,117,'independent baseline operationIds must be unique');
assert.equal(actual.length,117,'current Registry must contain exactly 117 operations');
assert.equal(new Set(actual.map(x=>x.operationId)).size,117,'current Registry operationIds must be unique');
const sort=(rows:readonly SafetyRow[])=>[...rows].sort((a,b)=>a.operationId.localeCompare(b.operationId));
assert.deepEqual(sort(actual),sort(expected),'current Registry safety fields differ from frozen Phase 0.2 Git object; update requires explicit safety review');
}
const project=(rows:readonly any[]):SafetyRow[]=>rows.map(row=>Object.fromEntries(safetyFields.map(field=>[field,row[field]])) as SafetyRow);
test('independent Phase 0.2 Git object freezes all 117 Registry safety decisions',()=>{
assert.equal(git('rev-parse','9dffadb'),PHASE_0_2_COMMIT);
assert.equal(git('cat-file','-t',PHASE_0_2_COMMIT),'commit');
const baseline=parseFrozenSafetyBaseline(git('show',`${PHASE_0_2_COMMIT}:${BASELINE_PATH}`));
assertExactSafetyBaseline(project(upstream58e2204Operations),baseline);
});
test('independent safety comparison rejects risk, confirmation, capability and execution-policy mutations',()=>{
const baseline=parseFrozenSafetyBaseline(git('show',`${PHASE_0_2_COMMIT}:${BASELINE_PATH}`));
const mutations:[string,string,string][]=[
['postData','riskLevel','R0'],
['postSmsSend','confirmationPolicy','none'],
['postSmsSend','capability','query'],
['postSmsSend','executionPolicy','dedicatedFlow']
];
for(const [operationId,field,value] of mutations){const current=structuredClone(project(upstream58e2204Operations));(current.find(x=>x.operationId===operationId)! as any)[field]=value;assert.throws(()=>assertExactSafetyBaseline(current,baseline),/safety fields differ/,`${operationId}:${field}`);}
});