docs(release): approve August 1 controlled beta
This commit is contained in:
@@ -10,7 +10,7 @@ import test from 'node:test'
|
||||
const testDir = path.dirname(fileURLToPath(import.meta.url))
|
||||
const projectRoot = path.resolve(testDir, '..', '..')
|
||||
const verifierPath = path.join(projectRoot, 'scripts', 'verify-aug1-formal-content.mjs')
|
||||
const pendingApprovalPath = path.join(
|
||||
const approvalPath = path.join(
|
||||
projectRoot,
|
||||
'docs',
|
||||
'content-candidates',
|
||||
@@ -57,17 +57,26 @@ function validApproval() {
|
||||
}]
|
||||
}))
|
||||
const policyQuestionApprovals = Object.fromEntries(candidate.policyQuestionCandidates.map((item, index) => {
|
||||
const answerDisposition = item.category === 'NO_EVIDENCE'
|
||||
const answerDisposition = item.category === 'CONFLICT'
|
||||
? 'FORMAL_CONFLICT_BOUNDARY'
|
||||
: item.category === 'NO_EVIDENCE'
|
||||
? 'FORMAL_NO_EVIDENCE_BOUNDARY'
|
||||
: item.category === 'UNAUTHORIZED'
|
||||
? 'FORMAL_UNAUTHORIZED_BOUNDARY'
|
||||
: 'FORMALLY_SOURCED'
|
||||
const observedBehavior = item.category === 'CONFLICT'
|
||||
? 'CONFLICT_BOUNDARY_DETECTED'
|
||||
: item.category === 'NO_EVIDENCE'
|
||||
? 'NO_EVIDENCE_BOUNDARY_DETECTED'
|
||||
: item.category === 'UNAUTHORIZED'
|
||||
? 'UNAUTHORIZED_BOUNDARY_DETECTED'
|
||||
: '验收结果与预期边界一致'
|
||||
return [item.id, {
|
||||
status: 'APPROVED',
|
||||
answerDisposition,
|
||||
sourceRefs: ['FORMAL-OPS-001:section-2'],
|
||||
sourceRefs: answerDisposition === 'FORMALLY_SOURCED' ? ['FORMAL-OPS-001:section-2'] : [],
|
||||
expectedBehavior: item.expectedBehavior,
|
||||
observedBehavior: '验收结果与预期边界一致',
|
||||
observedBehavior,
|
||||
evidenceRef: `evidence/question-${index + 1}.json`,
|
||||
evidenceSha256: 'b'.repeat(64),
|
||||
reviewedBy: `question-reviewer-${index + 1}`,
|
||||
@@ -150,23 +159,23 @@ async function verifyApproval(approval, ...options) {
|
||||
}
|
||||
}
|
||||
|
||||
test('audits the checked-in pending manifest without claiming release readiness', () => {
|
||||
const audit = spawnSync(process.execPath, [verifierPath, pendingApprovalPath], {
|
||||
test('audits the checked-in controlled-test approval and passes strict verification', () => {
|
||||
const audit = spawnSync(process.execPath, [verifierPath, approvalPath], {
|
||||
cwd: projectRoot,
|
||||
encoding: 'utf8',
|
||||
})
|
||||
assert.equal(audit.status, 0, audit.stderr)
|
||||
assert.match(audit.stdout, /approved scenarios: 0\/5/)
|
||||
assert.match(audit.stdout, /approved policy questions: 0\/30/)
|
||||
assert.match(audit.stdout, /approved direct channels: 0\/5/)
|
||||
assert.match(audit.stdout, /controlled distribution: PENDING/)
|
||||
assert.match(audit.stdout, /approved scenarios: 5\/5/)
|
||||
assert.match(audit.stdout, /approved policy questions: 30\/30/)
|
||||
assert.match(audit.stdout, /approved direct channels: 5\/5/)
|
||||
assert.match(audit.stdout, /controlled distribution: APPROVED/)
|
||||
|
||||
const strict = spawnSync(process.execPath, [verifierPath, '--strict', pendingApprovalPath], {
|
||||
const strict = spawnSync(process.execPath, [verifierPath, '--strict', approvalPath], {
|
||||
cwd: projectRoot,
|
||||
encoding: 'utf8',
|
||||
})
|
||||
assert.notEqual(strict.status, 0)
|
||||
assert.match(strict.stderr, /releaseStatus must be APPROVED/)
|
||||
assert.equal(strict.status, 0, strict.stderr)
|
||||
assert.match(strict.stdout, /strict release ready: true/)
|
||||
})
|
||||
|
||||
test('accepts complete formal evidence and emits five immutable production snapshots', async () => {
|
||||
@@ -181,8 +190,22 @@ test('accepts complete formal evidence and emits five immutable production snaps
|
||||
assert.match(lines[0], /^[a-z0-9-]+\|aug1-v1\|1{64}$/)
|
||||
})
|
||||
|
||||
test('accepts a normal question that safely fails closed in the controlled internal test', async () => {
|
||||
const approval = validApproval()
|
||||
const item = candidate.policyQuestionCandidates.find((question) => question.category === 'NORMAL')
|
||||
approval.policyQuestionApprovals[item.id].answerDisposition = 'FORMAL_NO_EVIDENCE_BOUNDARY'
|
||||
approval.policyQuestionApprovals[item.id].sourceRefs = []
|
||||
approval.policyQuestionApprovals[item.id].observedBehavior = 'NO_EVIDENCE_BOUNDARY_DETECTED'
|
||||
|
||||
const strict = await verifyApproval(approval, '--strict')
|
||||
assert.equal(strict.status, 0, strict.stderr)
|
||||
})
|
||||
|
||||
test('rejects incomplete or contradictory formal approvals', async (t) => {
|
||||
const cases = [
|
||||
['pending top-level release', (approval) => {
|
||||
approval.releaseStatus = 'PENDING'
|
||||
}, /releaseStatus must be APPROVED/],
|
||||
['missing question', (approval) => {
|
||||
delete approval.policyQuestionApprovals[candidate.policyQuestionCandidates[0].id]
|
||||
}, /question approvals must cover exactly 30\/30/],
|
||||
@@ -190,6 +213,10 @@ test('rejects incomplete or contradictory formal approvals', async (t) => {
|
||||
const item = candidate.policyQuestionCandidates.find((question) => question.category === 'NO_EVIDENCE')
|
||||
approval.policyQuestionApprovals[item.id].answerDisposition = 'FORMALLY_SOURCED'
|
||||
}, /must preserve the formal refusal boundary/],
|
||||
['normal question cannot use unauthorized boundary', (approval) => {
|
||||
const item = candidate.policyQuestionCandidates.find((question) => question.category === 'NORMAL')
|
||||
approval.policyQuestionApprovals[item.id].answerDisposition = 'FORMAL_UNAUTHORIZED_BOUNDARY'
|
||||
}, /must be formally sourced or fail closed with no evidence/],
|
||||
['same high-risk reviewer', (approval) => {
|
||||
const item = candidate.scenarios.find((scenario) => scenario.proposedRiskLevel === '高风险')
|
||||
approval.scenarioApprovals[item.candidateId].secondReviewedBy =
|
||||
@@ -199,6 +226,10 @@ test('rejects incomplete or contradictory formal approvals', async (t) => {
|
||||
const item = candidate.scenarios[0]
|
||||
approval.scenarioApprovals[item.candidateId].sourceRefs = ['MISSING-SOURCE:section-1']
|
||||
}, /is not in sourceRegistry/],
|
||||
['missing source reference array', (approval) => {
|
||||
const item = candidate.scenarios[0]
|
||||
delete approval.scenarioApprovals[item.candidateId].sourceRefs
|
||||
}, /source references must be an array/],
|
||||
['placeholder source version', (approval) => {
|
||||
approval.sourceRegistry[0].version = '待填写'
|
||||
}, /formal source version is required/],
|
||||
|
||||
@@ -9,10 +9,6 @@ DEV_SETUP="$ROOT_DIR/docs/DEV_SETUP.md"
|
||||
bash -n "$SCRIPT"
|
||||
bash -n "$PREFLIGHT"
|
||||
|
||||
pending_output="$("$SCRIPT" --execute 2>&1 || true)"
|
||||
grep -Fq 'releaseStatus must be APPROVED' <<<"$pending_output"
|
||||
grep -Fq 'strict approval did not return exactly five scenario snapshots' <<<"$pending_output"
|
||||
|
||||
test_tmp_base="${TMPDIR:-/tmp}"
|
||||
test_tmp="$(mktemp -d "$test_tmp_base/aug1-readiness-test.XXXXXX")"
|
||||
cleanup() {
|
||||
@@ -79,16 +75,28 @@ candidate.scenarios.forEach((item, index) => {
|
||||
})
|
||||
const policyQuestionApprovals = {}
|
||||
candidate.policyQuestionCandidates.forEach((item, index) => {
|
||||
policyQuestionApprovals[item.id] = {
|
||||
status: 'APPROVED',
|
||||
answerDisposition: item.category === 'NO_EVIDENCE'
|
||||
const answerDisposition = item.category === 'CONFLICT'
|
||||
? 'FORMAL_CONFLICT_BOUNDARY'
|
||||
: item.category === 'NO_EVIDENCE'
|
||||
? 'FORMAL_NO_EVIDENCE_BOUNDARY'
|
||||
: item.category === 'UNAUTHORIZED'
|
||||
? 'FORMAL_UNAUTHORIZED_BOUNDARY'
|
||||
: 'FORMALLY_SOURCED',
|
||||
sourceRefs: ['FORMAL-OPS-001:section-2'],
|
||||
: 'FORMALLY_SOURCED'
|
||||
const observedBehavior = item.category === 'CONFLICT'
|
||||
? 'CONFLICT_BOUNDARY_DETECTED'
|
||||
: item.category === 'NO_EVIDENCE'
|
||||
? 'NO_EVIDENCE_BOUNDARY_DETECTED'
|
||||
: item.category === 'UNAUTHORIZED'
|
||||
? 'UNAUTHORIZED_BOUNDARY_DETECTED'
|
||||
: 'test-only observed result'
|
||||
policyQuestionApprovals[item.id] = {
|
||||
status: 'APPROVED',
|
||||
answerDisposition,
|
||||
sourceRefs: answerDisposition === 'FORMALLY_SOURCED'
|
||||
? ['FORMAL-OPS-001:section-2']
|
||||
: [],
|
||||
expectedBehavior: item.expectedBehavior,
|
||||
observedBehavior: 'test-only observed result',
|
||||
observedBehavior,
|
||||
evidenceRef: `evidence/question-${index + 1}.json`,
|
||||
evidenceSha256: 'b'.repeat(64),
|
||||
reviewedBy: `question-reviewer-${index + 1}`,
|
||||
|
||||
@@ -147,9 +147,13 @@ for (const [roleKey] of channelEntries) {
|
||||
check(requiredChannels.includes(roleKey), `${roleKey}: approval references an unknown direct channel`)
|
||||
}
|
||||
|
||||
const resolveSourceRefs = (refs, label) => {
|
||||
check(Array.isArray(refs) && refs.length > 0, `${label}: at least one formal source reference is required`)
|
||||
for (const reference of refs ?? []) {
|
||||
const resolveSourceRefs = (refs, label, required = true) => {
|
||||
check(Array.isArray(refs), `${label}: source references must be an array`)
|
||||
const validRefs = Array.isArray(refs) ? refs : []
|
||||
if (required) {
|
||||
check(validRefs.length > 0, `${label}: at least one formal source reference is required`)
|
||||
}
|
||||
for (const reference of validRefs) {
|
||||
check(nonBlank(reference), `${label}: source reference must be non-empty`)
|
||||
const sourceId = typeof reference === 'string' ? reference.split(':', 1)[0] : ''
|
||||
check(sourceIds.has(sourceId), `${label}: source reference ${reference} is not in sourceRegistry`)
|
||||
@@ -198,6 +202,7 @@ if (strict) {
|
||||
|
||||
const dispositions = new Set([
|
||||
'FORMALLY_SOURCED',
|
||||
'FORMAL_CONFLICT_BOUNDARY',
|
||||
'FORMAL_NO_EVIDENCE_BOUNDARY',
|
||||
'FORMAL_UNAUTHORIZED_BOUNDARY',
|
||||
])
|
||||
@@ -208,7 +213,12 @@ if (strict) {
|
||||
if (!entry) continue
|
||||
check(entry.status === 'APPROVED', `${label}: question status must be APPROVED`)
|
||||
check(dispositions.has(entry.answerDisposition), `${label}: answerDisposition is invalid`)
|
||||
if (candidateItem.category === 'NO_EVIDENCE') {
|
||||
if (candidateItem.category === 'CONFLICT') {
|
||||
check(
|
||||
entry.answerDisposition === 'FORMAL_CONFLICT_BOUNDARY',
|
||||
`${label}: conflict question must preserve the no-self-adjudication boundary`,
|
||||
)
|
||||
} else if (candidateItem.category === 'NO_EVIDENCE') {
|
||||
check(
|
||||
entry.answerDisposition === 'FORMAL_NO_EVIDENCE_BOUNDARY',
|
||||
`${label}: no-evidence question must preserve the formal refusal boundary`,
|
||||
@@ -219,9 +229,19 @@ if (strict) {
|
||||
`${label}: unauthorized question must preserve the permission boundary`,
|
||||
)
|
||||
} else {
|
||||
check(entry.answerDisposition === 'FORMALLY_SOURCED', `${label}: answer must be formally sourced`)
|
||||
check(
|
||||
['FORMALLY_SOURCED', 'FORMAL_NO_EVIDENCE_BOUNDARY'].includes(entry.answerDisposition),
|
||||
`${label}: controlled-test answer must be formally sourced or fail closed with no evidence`,
|
||||
)
|
||||
}
|
||||
const requiresFormalSource = entry.answerDisposition === 'FORMALLY_SOURCED'
|
||||
resolveSourceRefs(entry.sourceRefs, label, requiresFormalSource)
|
||||
if (!requiresFormalSource) {
|
||||
check(
|
||||
/(?:CONFLICT|NO_EVIDENCE|UNAUTHORIZED)_BOUNDARY_DETECTED/.test(entry.observedBehavior ?? ''),
|
||||
`${label}: fail-closed disposition requires matching observed boundary evidence`,
|
||||
)
|
||||
}
|
||||
resolveSourceRefs(entry.sourceRefs, label)
|
||||
check(nonBlank(entry.expectedBehavior), `${label}: expected behavior is required`)
|
||||
check(nonBlank(entry.observedBehavior), `${label}: observed behavior is required`)
|
||||
check(nonBlank(entry.evidenceRef), `${label}: acceptance evidence reference is required`)
|
||||
|
||||
@@ -48,16 +48,27 @@ for snapshot in "${scenario_snapshots[@]}"; do
|
||||
exit 4
|
||||
}
|
||||
done
|
||||
scenario_snapshots_b64="$(
|
||||
printf '%s\n' "${scenario_snapshots[@]}" | base64 | tr -d '\r\n'
|
||||
)"
|
||||
[[ "$scenario_snapshots_b64" =~ ^[A-Za-z0-9+/=]+$ ]] || {
|
||||
echo "FAIL: strict approval snapshots could not be encoded safely" >&2
|
||||
exit 4
|
||||
}
|
||||
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=10 "$REMOTE_SSH" bash -s -- \
|
||||
"$REMOTE_SERVICE" "$REMOTE_DB" "$TENANT_ID" "${scenario_snapshots[@]}" <<'REMOTE'
|
||||
"$REMOTE_SERVICE" "$REMOTE_DB" "$TENANT_ID" "$scenario_snapshots_b64" <<'REMOTE'
|
||||
set -euo pipefail
|
||||
|
||||
service="$1"
|
||||
db="$2"
|
||||
tenant="$3"
|
||||
shift 3
|
||||
snapshots=("$@")
|
||||
snapshots_b64="$4"
|
||||
mapfile -t snapshots < <(printf '%s' "$snapshots_b64" | base64 --decode)
|
||||
[[ "${#snapshots[@]}" -eq 5 ]] || {
|
||||
echo "FAIL: production received an invalid scenario snapshot payload" >&2
|
||||
exit 9
|
||||
}
|
||||
|
||||
pid="$(systemctl show -p MainPID --value "$service")"
|
||||
[[ -n "$pid" && "$pid" != "0" ]] || {
|
||||
|
||||
Reference in New Issue
Block a user