/*
run-backfill-revisions — coordinates backfilling revision counts for tracked entities using Git history.
- Do not handle entity creation or deletion beyond revision updates.
- Do not perform any operations related to transport or configuration orchestration.
- Do not parse raw content from files or directories.
- RFC-1097: step 6 — compass.migrate codemod run
Mechanical v1 to v2 header migration across the workspace: 942 files rewritten — CHANGE_SUMMARY windows collapsed into history, forbidden v1 blocks stripped, KEY_DECISIONS seeded from @ai-invariant comments (5 files) or TODO placeholders (103 files), blocks reordered to canonical order.
- RFC-1097: sweep — werkstatt-engine clean
Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics.
*/
/**
* Annotation revision counts from Git history for all tracked entities.
* Updates entities and manifests with accurate revision numbers.
*/
import path from "node:path";
import { getFileRevisionFromHistory } from "./git.ts";
import {
getManifestFileName,
loadDirectoryManifest,
saveDirectoryManifest,
upsertManifestRecord,
} from "./manifests.ts";
import { readJsonFile, writeJsonFile } from "./json.ts";
import { deletedLogPath } from "./paths.ts";
import { loadEntitiesById, loadPathsCurrent, saveEntitiesById } from "./registry.ts";
import type { DeletedLogItem } from "./types.ts";
export async function runBackfillRevisions(args: { cwd: string }): Promise {
const { cwd } = args;
const entities = await loadEntitiesById(cwd);
const paths = await loadPathsCurrent(cwd);
const deletedLog = await readJsonFile(deletedLogPath(cwd)).catch(() => []);
const deletedByEntityId = new Map(deletedLog.map((item) => [item.entityId, item]));
const manifestCache = new Map>>();
let updatedEntities = 0;
let updatedActiveRecords = 0;
let updatedDeletedLog = 0;
for (const [entityId, entity] of Object.entries(entities)) {
const repoPath = entity.currentPath;
if (!repoPath) {
continue;
}
const revision = await getFileRevisionFromHistory(cwd, repoPath);
if (revision === entity.revision) {
continue;
}
entity.revision = revision;
updatedEntities += 1;
const deletedItem = deletedByEntityId.get(entityId);
if (deletedItem) {
deletedItem.lastRevision = revision;
updatedDeletedLog += 1;
}
if (entity.status !== "active" || paths[repoPath] !== entityId) {
continue;
}
const repoDir = path.posix.dirname(repoPath);
const fileName = getManifestFileName(repoPath);
let manifest = manifestCache.get(repoDir);
if (manifest === undefined) {
manifest = await loadDirectoryManifest(cwd, repoDir);
manifestCache.set(repoDir, manifest);
}
if (!manifest) {
continue;
}
upsertManifestRecord(manifest, fileName, {
entityId,
createdAt: entity.createdAt,
updatedAt: entity.updatedAt,
revision: entity.revision,
contentHash: entity.contentHash,
gitSha: entity.gitSha,
status: entity.status,
});
updatedActiveRecords += 1;
}
await saveEntitiesById(cwd, entities);
await Promise.all(
Array.from(manifestCache.values())
.filter((manifest): manifest is NonNullable => Boolean(manifest))
.map((manifest) => saveDirectoryManifest(cwd, manifest)),
);
if (updatedDeletedLog > 0) {
await writeJsonFile(deletedLogPath(cwd), deletedLog);
}
console.log("");
console.log("Integrity revision backfill");
console.log(` updated entities ${updatedEntities}`);
console.log(` updated manifest rows ${updatedActiveRecords}`);
console.log(` updated deleted log ${updatedDeletedLog}`);
}