ci(refactor): [ENG-AUDIT-01] verify dedicated task completion commits

This commit is contained in:
Harvey Zhao committed 2026-09-12 09:45:21 +08:00
1 parent 4f324a8438
commit e0aacc5d6a
12 files changed
+450 -6

No files matched your search

+99
View File
@@ -0,0 +1,99 @@
import assert from 'node:assert/strict'
import { execFileSync } from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
const root = fileURLToPath(new URL('../../', import.meta.url))
export const commitPolicy = {
bootstrapCommit: '570600d2f6ffd580a890e0b87dfa9843bf8dd8d0',
bootstrapTask: 'DOC-05',
bundledTasks: ['DOC-01', 'DOC-02', 'DOC-03', 'DOC-04'],
}
export function auditCommits(directory = root, policy = commitPolicy) {
const git = args => execFileSync('git', args, { cwd: directory, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024, stdio: ['ignore', 'pipe', 'pipe'] }).trimEnd()
assert.equal(git(['rev-parse', '--is-shallow-repository']), 'false', 'Commit audit requires full Git history; use checkout fetch-depth: 0')
const head = git(['rev-parse', 'HEAD'])
const current = JSON.parse(fs.readFileSync(path.join(directory, 'refactor/tasks.json'), 'utf8'))
git(['merge-base', '--is-ancestor', current.baselineCommit, head])
git(['merge-base', '--is-ancestor', policy.bootstrapCommit, head])
const snapshots = new Map()
function snapshot(commit) {
if (!snapshots.has(commit)) {
const entry = git(['ls-tree', commit, '--', 'refactor/tasks.json'])
const tasks = entry ? JSON.parse(git(['show', `${commit}:refactor/tasks.json`])).tasks : []
snapshots.set(commit, new Map(tasks.map(task => [task.id, task])))
}
return snapshots.get(commit)
}
const history = git(['log', '--full-history', '--reverse', '--format=%H%x00%P%x00%s', head, '--', 'refactor/tasks.json']).split('\n').filter(Boolean)
const completions = []
for (const line of history) {
const [commit, parentText, subject] = line.split('\0')
const parents = parentText.split(' ').filter(Boolean).map(snapshot)
const newlyDone = [...snapshot(commit).values()].filter(task => task.status === 'done' && parents.every(parent => parent.get(task.id)?.status !== 'done'))
if (!newlyDone.length)
continue
const bootstrap = commit === policy.bootstrapCommit
const exceptions = bootstrap ? policy.bundledTasks : []
const dedicated = newlyDone.filter(task => !exceptions.includes(task.id))
assert.equal(dedicated.length, 1, `${commit}: one completion commit must complete exactly one task; found ${dedicated.map(task => task.id).join(', ')}`)
if (bootstrap) {
assert.equal(dedicated[0].id, policy.bootstrapTask, 'Bootstrap exception is restricted to DOC-05')
assert.deepEqual(newlyDone.map(task => task.id).sort(), [...policy.bundledTasks, policy.bootstrapTask].sort(), 'Bootstrap exception task set changed')
}
const id = dedicated[0].id
assert(/^[A-Z]+(?:-[A-Z]+)*-\d{2}$/.test(id), 'Invalid task ID')
// Older subjects use parentheses or plain tokens; punctuation does not erase a real task commit.
assert(new RegExp(`(?:^|[^A-Z0-9-])${id}(?:$|[^A-Z0-9-])`).test(subject), `${commit}: completion subject does not identify exact task ${id}`)
const changes = git(['diff-tree', '--root', '-m', '--no-commit-id', '--name-only', '-r', '-z', commit]).split('\0').filter(Boolean)
assert(changes.includes('refactor/tasks.json'), `${commit}: missing task-state change`)
assert(changes.includes('refactor/plan.md'), `${commit}: missing generated plan change`)
const tree = new Set(git(['ls-tree', '-r', '--name-only', '-z', commit, '--', 'refactor']).split('\0').filter(Boolean))
for (const task of newlyDone) {
assert(task.evidence?.length, `${commit}: ${task.id} has no evidence`)
const local = task.evidence.filter(file => !/^https?:\/\//.test(file)).map((file) => {
const name = file.split('#')[0]
const normalized = path.posix.normalize(name)
assert(name && !name.includes('\\') && !path.posix.isAbsolute(name) && !/^[A-Z]:/i.test(name) && normalized !== '..' && !normalized.startsWith('../'), `${commit}: evidence escapes refactor`)
return `refactor/${normalized}`
})
assert(local.length, `${commit}: ${task.id} needs durable local completion evidence`)
for (const file of local) assert(tree.has(file), `${commit}: ${task.id} evidence missing at completion: ${file}`)
if (!exceptions.includes(task.id))
assert(local.some(file => changes.includes(file)), `${commit}: ${task.id} has no evidence updated in its completion commit`)
completions.push({ task: task.id, commit, subject, bundledWith: exceptions.includes(task.id) ? policy.bootstrapTask : null, evidence: local, changedEvidence: local.filter(file => changes.includes(file)) })
}
}
const currentTasks = new Map(current.tasks.map(task => [task.id, task]))
const committed = snapshot(head)
for (const completion of completions)
assert(currentTasks.has(completion.task), `Historical completed task was removed: ${completion.task}`)
for (const task of current.tasks.filter(task => task.status === 'done')) {
assert.equal(committed.get(task.id)?.status, 'done', `Uncommitted completion: ${task.id}; commit this task before the final audit`)
assert(completions.some(completion => completion.task === task.id), `No dedicated completion found for ${task.id}`)
}
return {
schemaVersion: 1,
head,
baselineCommit: current.baselineCommit,
policy,
doneTasks: current.tasks.filter(task => task.status === 'done').length,
dedicatedCommits: new Set(completions.filter(item => !item.bundledWith).map(item => item.commit)).size,
completions,
limitation: 'Verifies actual completion transitions, task identity and durable evidence co-committed with the plan; does not prove the semantic quality of implementation or tests.',
}
}
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
assert(process.argv.slice(2).every(arg => arg === '--report'), 'Use --report or no arguments')
const report = auditCommits()
if (process.argv.includes('--report')) {
const directory = path.join(root, 'refactor/.cache/ci')
fs.mkdirSync(directory, { recursive: true })
fs.writeFileSync(path.join(directory, 'commit-audit.json'), `${JSON.stringify(report, null, 2)}\n`)
}
console.log(`Commit audit: ${report.doneTasks} done tasks, ${report.dedicatedCommits} dedicated completion commits, ${report.policy.bundledTasks.length} documented bootstrap tasks; HEAD ${report.head}`)
}
+118
View File
@@ -0,0 +1,118 @@
import assert from 'node:assert/strict'
import { execFileSync } from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
// eslint-disable-next-line test/no-import-node-test -- Exercise real isolated Git histories, not mocked command output.
import test from 'node:test'
import { fileURLToPath } from 'node:url'
import { auditCommits } from './commit-audit.mjs'
const cache = fileURLToPath(new URL('../.cache/', import.meta.url))
function repository(t) {
fs.mkdirSync(cache, { recursive: true })
const directory = fs.mkdtempSync(path.join(cache, 'commit-audit-test-'))
t.after(() => {
const relative = path.relative(cache, directory)
assert(relative.startsWith('commit-audit-test-') && !relative.includes(path.sep))
fs.rmSync(directory, { recursive: true, force: true })
})
const git = args => execFileSync('git', args, {
cwd: directory,
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
env: { ...process.env, GIT_AUTHOR_NAME: 'Audit Fixture', GIT_AUTHOR_EMAIL: 'audit@example.test', GIT_COMMITTER_NAME: 'Audit Fixture', GIT_COMMITTER_EMAIL: 'audit@example.test', GIT_CONFIG_NOSYSTEM: '1', GIT_CONFIG_GLOBAL: process.platform === 'win32' ? 'NUL' : '/dev/null' },
}).trim()
const write = (file, text) => {
fs.mkdirSync(path.dirname(path.join(directory, file)), { recursive: true })
fs.writeFileSync(path.join(directory, file), text)
}
const commit = (subject) => {
git(['add', '.'])
git(['-c', 'core.hooksPath=', '-c', 'commit.gpgSign=false', 'commit', '-m', subject])
return git(['rev-parse', 'HEAD'])
}
git(['init', '-b', 'main'])
write('README.md', 'Test-only Git repository\n')
const baselineCommit = commit('Baseline')
const bundledTasks = ['DOC-01', 'DOC-02', 'DOC-03', 'DOC-04']
const data = { baselineCommit, tasks: [...bundledTasks, 'DOC-05'].map(id => ({ id, status: 'done', evidence: ['changes/bootstrap.md'] })) }
for (const id of ['TEST-01', 'TEST-02']) data.tasks.push({ id, status: 'doing', evidence: [] })
const save = (plan = true) => {
write('refactor/tasks.json', JSON.stringify(data))
if (plan)
write('refactor/plan.md', data.tasks.map(task => `${task.id}: ${task.status}`).join('\n'))
}
write('refactor/changes/bootstrap.md', 'Initial documentation and the per-task rule\n')
save()
const bootstrapCommit = commit('docs: [DOC-05] establish initial documentation')
const policy = { bootstrapCommit, bootstrapTask: 'DOC-05', bundledTasks }
const finish = (ids = ['TEST-01'], options = {}) => {
for (const id of ids) {
const task = data.tasks.find(task => task.id === id)
task.status = 'done'
task.evidence = options.evidence || [`changes/${id}.md`]
if (options.writeEvidence !== false)
write(`refactor/changes/${id}.md`, `${id}: tested completion\n`)
}
save(options.plan !== false)
if (options.commit !== false)
return commit(options.subject || `test: [${ids[0]}] completed work`)
}
return { directory, git, write, commit, save, finish, data, policy, audit: () => auditCommits(directory, policy) }
}
test('Commit audit proves dedicated transitions and only the pinned bootstrap bundling', (t) => {
const repo = repository(t)
const first = repo.finish()
const second = repo.finish(['TEST-02'], { subject: 'test(TEST-02): historical punctuation remains traceable' })
const report = repo.audit()
assert.equal(report.doneTasks, 7)
assert.equal(report.dedicatedCommits, 3)
assert.deepEqual(report.completions.filter(item => item.bundledWith).map(item => item.task), repo.policy.bundledTasks)
assert.equal(report.completions.find(item => item.task === 'TEST-01').commit, first)
assert.equal(report.completions.find(item => item.task === 'TEST-02').commit, second)
assert.throws(() => auditCommits(repo.directory, { ...repo.policy, bundledTasks: [...repo.policy.bundledTasks, 'TEST-01'] }), /Bootstrap exception task set changed/)
})
test('Commit audit rejects combined task completion, misleading subjects and missing co-committed evidence', (t) => {
for (const [ids, options, expected] of [
[['TEST-01', 'TEST-02'], {}, /exactly one task/],
[['TEST-01'], { subject: 'test: [TEST-010] not the completed task' }, /exact task TEST-01/],
[['TEST-01'], { plan: false }, /missing generated plan change/],
[['TEST-01'], { writeEvidence: false }, /evidence missing at completion/],
[['TEST-01'], { evidence: ['changes/bootstrap.md'], writeEvidence: false }, /no evidence updated/],
[['TEST-01'], { evidence: ['../README.md'] }, /evidence escapes refactor/],
]) {
const repo = repository(t)
repo.finish(ids, options)
assert.throws(repo.audit, expected)
}
})
test('Commit audit rejects uncommitted done state and shallow or removed completion history', (t) => {
const repo = repository(t)
repo.finish(['TEST-01'], { commit: false })
assert.throws(repo.audit, /Uncommitted completion: TEST-01/)
repo.commit('test: [TEST-01] finish with proof')
repo.data.tasks = repo.data.tasks.filter(task => task.id !== 'TEST-01')
repo.save()
assert.throws(repo.audit, /Historical completed task was removed/)
repo.write('.git/shallow', `${repo.git(['rev-parse', 'HEAD'])}\n`)
assert.throws(repo.audit, /requires full Git history/)
})
test('Commit audit finds feature-branch completion through a real merge without calling the merge a second completion', (t) => {
const repo = repository(t)
repo.git(['checkout', '-b', 'feature'])
const completed = repo.finish()
repo.git(['checkout', 'main'])
repo.write('notes.md', 'Unrelated main branch work\n')
repo.commit('docs: independent work')
repo.git(['-c', 'core.hooksPath=', '-c', 'commit.gpgSign=false', 'merge', '--no-ff', 'feature', '-m', 'Merge feature branch'])
const report = repo.audit()
assert.equal(report.doneTasks, 6)
assert.equal(report.dedicatedCommits, 2)
assert.equal(report.completions.filter(item => item.task === 'TEST-01').length, 1)
assert.equal(report.completions.find(item => item.task === 'TEST-01').commit, completed)
})