* test(#3805): audit-uat must honour the audit_acknowledged marker * fix(#3805): route audit-uat's UAT and VERIFICATION scans through the shared acknowledged predicate * chore(#3805): changeset fragment (pr number backfilled after PR creation) * chore(#3805): backfill changeset PR number (4025) --------- Co-authored-by: sim <sim@local>
This commit is contained in:
@@ -1789,6 +1789,13 @@ export = {
|
||||
formatAuditReport,
|
||||
listAuditPhaseTargets,
|
||||
cmdAuditAcknowledge,
|
||||
// #3805: exported so uat.cts's cmdAuditUat routes the SAME artifacts'
|
||||
// suppression through the ONE predicate instead of hand-rolling a tenth
|
||||
// copy outside this file's visibility (the exact defect class the
|
||||
// predicate's own header warns about). The snapshot derivations ride
|
||||
// along so the snapshotKeys cannot drift between the two consumers.
|
||||
isAuditItemAcknowledged,
|
||||
deriveUatGapSnapshotValue,
|
||||
// #2142: exported so src/milestone.cts's archiveQuickTaskDirectories README
|
||||
// index generator shares this ONE discovery rule rather than re-deriving it.
|
||||
resolveQuickTaskSummaryFile,
|
||||
|
||||
30
src/uat.cts
30
src/uat.cts
@@ -35,6 +35,9 @@ const { PHASE_NUMBER_TOKEN_SOURCE, scopeToPhase } = phaseIdMod;
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
import phaseLocator = require('./phase-locator.cjs');
|
||||
const { listMilestonePhaseDirs, getAllArchivedPhaseDirs } = phaseLocator;
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
import auditMod = require('./audit.cjs');
|
||||
const { isAuditItemAcknowledged, deriveUatGapSnapshotValue } = auditMod;
|
||||
import { requireSafePath, sanitizeForDisplay } from './security.cjs';
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports -- config-loader.cjs is an export= CommonJS module
|
||||
import configLoader = require('./config-loader.cjs');
|
||||
@@ -158,6 +161,7 @@ function cmdAuditUat(cwd: string, raw: boolean): void {
|
||||
}
|
||||
|
||||
const results: UatFileResult[] = [];
|
||||
let acknowledgedFiles = 0;
|
||||
|
||||
// Active dirs are milestone-filtered; archived dirs deliberately are NOT.
|
||||
// listMilestonePhaseDirs derives the CURRENT milestone's phase directories
|
||||
@@ -198,7 +202,16 @@ function cmdAuditUat(cwd: string, raw: boolean): void {
|
||||
const uatFilePath = path.join(phaseDir, file);
|
||||
const content = readNormalizedDocument(uatFilePath);
|
||||
const { items, headingsSeen } = parseUatItemsWithStats(content);
|
||||
const status = (extractFrontmatter(content, uatFilePath).status as string || 'unknown');
|
||||
const uatFm = extractFrontmatter(content, uatFilePath) as Record<string, unknown>;
|
||||
const status = ((uatFm.status as string) || 'unknown').toLowerCase();
|
||||
// #3805: honour the audit_acknowledged marker with the SAME snapshot
|
||||
// key audit.cts's scanUatGaps uses ('gap_snapshot', derived value
|
||||
// composed by the shared derivation) — one acknowledgement means the
|
||||
// same thing to both commands.
|
||||
if (isAuditItemAcknowledged(uatFm, { snapshotKey: 'gap_snapshot', currentValue: deriveUatGapSnapshotValue(status, content) })) {
|
||||
acknowledgedFiles++;
|
||||
continue;
|
||||
}
|
||||
// `parse_gap` means the file contained `### N.` test blocks that
|
||||
// yielded no items — NOT merely "zero items and not complete" (#3707
|
||||
// MAJOR: that broader signal false-positived on an all-pass file and on
|
||||
@@ -258,8 +271,17 @@ function cmdAuditUat(cwd: string, raw: boolean): void {
|
||||
for (const file of scopeToPhase(files.filter(f => f.includes('-VERIFICATION') && f.endsWith('.md')), dir)) {
|
||||
const verificationFilePath = path.join(phaseDir, file);
|
||||
const content = readNormalizedDocument(verificationFilePath);
|
||||
const status = extractFrontmatter(content, verificationFilePath).status as string || 'unknown';
|
||||
const verFm = extractFrontmatter(content, verificationFilePath) as Record<string, unknown>;
|
||||
const status = ((verFm.status as string) || 'unknown').toLowerCase();
|
||||
// #3805: same marker, same 'status' snapshot key as scanVerificationGaps,
|
||||
// and the same ORDERING — the open-status gate runs FIRST (a marker on
|
||||
// a file that would never surface is not a suppressed item), then the
|
||||
// acknowledgement suppresses what the gate surfaced.
|
||||
if (status === 'human_needed' || status === 'gaps_found') {
|
||||
if (isAuditItemAcknowledged(verFm, { snapshotKey: 'status', currentValue: status })) {
|
||||
acknowledgedFiles++;
|
||||
continue;
|
||||
}
|
||||
const items = parseVerificationItems(content, status, verificationFilePath);
|
||||
if (items.length > 0) {
|
||||
results.push({
|
||||
@@ -349,7 +371,9 @@ function cmdAuditUat(cwd: string, raw: boolean): void {
|
||||
}
|
||||
}
|
||||
|
||||
output({ results, summary }, raw, undefined);
|
||||
// #3805: acknowledged files surface as a COUNT (audit-open's honesty
|
||||
// model: the marker fired, the items are suppressed, both facts visible).
|
||||
output({ results, summary, acknowledged_files: acknowledgedFiles }, raw, undefined);
|
||||
}
|
||||
|
||||
// ─── cmdRenderCheckpoint ──────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user