$ErrorActionPreference = 'Stop' $root = Split-Path -Parent $PSScriptRoot $page = Get-Content -Raw -Encoding UTF8 -LiteralPath (Join-Path $root 'pages/profile/m07-feedback.vue') foreach ($required in @( '@/utils/api.js', 'appApi.submitFeedback(', 'createRequestController', 'isRequestCancelled', ':aria-pressed=', 'role="group"', 'feedback-result--error', 'feedback-result--success', 'feedback-result--uncertain', 'feedbackState.value = "success"', 'feedbackState.value = "error"', 'feedbackState.value = "uncertain"', 'lastUncertainSnapshot', 'isFeedbackResultUncertain', 'formSnapshot.value === submittedSnapshot', 'submittedWithPendingEditsMessage', ':focus="feedbackContentFocused"', 'nextTick(', "feedbackResultTone === 'error' ? 'assertive' : 'polite'", 'const submittedSnapshot = formSnapshot.value', 'baseline.value = submittedSnapshot', 'requestController.abort()', 'pageActive = false', ':disabled="feedbackState === ''submitting''"', 'value: "bug"', 'value: "advice"', 'value: "complaint"', 'value: "other"', '@click="selectFeedbackType(type.value)"' )) { if (-not $page.Contains($required)) { throw "M07 contract is missing: $required" } } foreach ($retired in @('setTimeout(', 'errors.feedbackType')) { if ($page.Contains($retired)) { throw "M07 retired path remains: $retired" } } if ($page -notmatch '(?s)const submitFeedback = async \(\) =>.*?feedbackState\.value = "submitting";.*?await appApi\.submitFeedback\(') { throw 'M07 must use one locked async submit path and the API owner' } if ($page -notmatch '(?s)onUnload\(\(\) => \{.*?pageActive = false;.*?requestController\.abort\(\);') { throw 'M07 unload must deactivate the page before aborting the request' } Write-Output 'M07-FEEDBACK-SUBMIT-CONTRACT PASS'