Format authored monorepo code with pinned language formatters
This commit is contained in:
@@ -11,50 +11,109 @@ import { checkerIdentity } from "./checked-build.js";
|
||||
const execFile = promisify(callback);
|
||||
export async function authoringWorklist(start: string) {
|
||||
const context = await authoringContext(start);
|
||||
const entries: {directory: string; resourceId?: string; phase: string; message: string; next: string}[] = [];
|
||||
const entries: { directory: string; resourceId?: string; phase: string; message: string; next: string }[] = [];
|
||||
const dependencies = new Map<string, string[]>();
|
||||
for (const resource of context.resources) {
|
||||
const root = path.join(context.workbench, resource.directory);
|
||||
const add = (phase: string, message: string) => entries.push({directory: resource.directory, resourceId: resource.resourceId, phase, message,
|
||||
next: phase === "syntax" ? `qx-workspace inspect ${resource.directory}` : phase === "evolution" ? "Inspect evolution.json in the check output; resolve its named migration/review requirements before cutover" : `cd ${resource.directory} && qx-workspace check`});
|
||||
const add = (phase: string, message: string) =>
|
||||
entries.push({
|
||||
directory: resource.directory,
|
||||
resourceId: resource.resourceId,
|
||||
phase,
|
||||
message,
|
||||
next:
|
||||
phase === "syntax"
|
||||
? `qx-workspace inspect ${resource.directory}`
|
||||
: phase === "evolution"
|
||||
? "Inspect evolution.json in the check output; resolve its named migration/review requirements before cutover"
|
||||
: `cd ${resource.directory} && qx-workspace check`,
|
||||
});
|
||||
try {
|
||||
if (await fs.realpath(root) !== root) throw new Error("Registered checkout crosses a symlink");
|
||||
if ((await fs.realpath(root)) !== root) throw new Error("Registered checkout crosses a symlink");
|
||||
const inspected = await inspectAuthoringRepository(root);
|
||||
for (const file of inspected.files) if (file.currentErrors.length) add("syntax", `${file.file}: ${JSON.stringify(file.currentErrors)}${file.status === "historical" ? `; historical contract available at ${file.revision}` : ""}`);
|
||||
for (const file of inspected.files)
|
||||
if (file.currentErrors.length)
|
||||
add(
|
||||
"syntax",
|
||||
`${file.file}: ${JSON.stringify(file.currentErrors)}${file.status === "historical" ? `; historical contract available at ${file.revision}` : ""}`,
|
||||
);
|
||||
const lock = await loadQuixosLock(path.join(root, "quixos.lock"));
|
||||
if (!lock.ok) add("resolution", lock.diagnostics.map(entry => `${entry.fileName}: ${entry.message}`).join("\n"));
|
||||
else dependencies.set(resource.directory, lock.lock.resources.flatMap(dependency => {
|
||||
const selected = context.resources.find(entry => entry.kind === dependency.kind && entry.source?.repository === dependency.source.repository);
|
||||
if (selected?.source && selected.source.commit !== dependency.source.commit) add("propagation", `Dependency ${dependency.binding} has advanced; check will repin it automatically`);
|
||||
return selected ? [selected.directory] : [];
|
||||
}));
|
||||
if (!lock.ok)
|
||||
add("resolution", lock.diagnostics.map((entry) => `${entry.fileName}: ${entry.message}`).join("\n"));
|
||||
else
|
||||
dependencies.set(
|
||||
resource.directory,
|
||||
lock.lock.resources.flatMap((dependency) => {
|
||||
const selected = context.resources.find(
|
||||
(entry) => entry.kind === dependency.kind && entry.source?.repository === dependency.source.repository,
|
||||
);
|
||||
if (selected?.source && selected.source.commit !== dependency.source.commit)
|
||||
add("propagation", `Dependency ${dependency.binding} has advanced; check will repin it automatically`);
|
||||
return selected ? [selected.directory] : [];
|
||||
}),
|
||||
);
|
||||
let record;
|
||||
try { record = JSON.parse(await fs.readFile(path.join(context.workbench, ".quixos/checks", checkRecordName(resource.directory)), "utf8")); }
|
||||
catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; }
|
||||
try {
|
||||
record = JSON.parse(
|
||||
await fs.readFile(
|
||||
path.join(context.workbench, ".quixos/checks", checkRecordName(resource.directory)),
|
||||
"utf8",
|
||||
),
|
||||
);
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
||||
}
|
||||
if (!record?.commit) {
|
||||
if (record?.blockers?.length) add(record.phase, record.blockers.join("\n"));
|
||||
else add("unchecked", "No immutable candidate check recorded yet");
|
||||
continue;
|
||||
}
|
||||
if (record.checker !== checkerIdentity()) add("unchecked", "The installed checker changed since the last check");
|
||||
const env = {...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1"};
|
||||
const commit = (await execFile("jj", ["--ignore-working-copy", "log", "--no-graph", "-r", "@", "-T", "commit_id"], {cwd: root, env})).stdout.trim();
|
||||
const dirty = await execFile("git", ["diff", "--quiet", "--no-ext-diff", record.commit, "--"], {cwd: root}).then(() => false, () => true);
|
||||
const untracked = (await execFile("git", ["ls-files", "--others", "--exclude-standard"], {cwd: root})).stdout;
|
||||
if (commit !== record.commit || dirty || untracked) add("unchecked", `Edits are newer than the last check (${record.commit.slice(0, 12)})`);
|
||||
const env = { ...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1" };
|
||||
const commit = (
|
||||
await execFile("jj", ["--ignore-working-copy", "log", "--no-graph", "-r", "@", "-T", "commit_id"], {
|
||||
cwd: root,
|
||||
env,
|
||||
})
|
||||
).stdout.trim();
|
||||
const dirty = await execFile("git", ["diff", "--quiet", "--no-ext-diff", record.commit, "--"], {
|
||||
cwd: root,
|
||||
}).then(
|
||||
() => false,
|
||||
() => true,
|
||||
);
|
||||
const untracked = (await execFile("git", ["ls-files", "--others", "--exclude-standard"], { cwd: root })).stdout;
|
||||
if (commit !== record.commit || dirty || untracked)
|
||||
add("unchecked", `Edits are newer than the last check (${record.commit.slice(0, 12)})`);
|
||||
else if (record.blockers?.length) add(record.phase, record.blockers.join("\n"));
|
||||
} catch (error) { add("inspection", String(error).slice(0, 3000)); }
|
||||
} catch (error) {
|
||||
add("inspection", String(error).slice(0, 3000));
|
||||
}
|
||||
}
|
||||
// Fixed-point propagation, independent of registration order.
|
||||
const blocked = new Set(entries.map(entry => entry.directory));
|
||||
const blocked = new Set(entries.map((entry) => entry.directory));
|
||||
let changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
for (const [directory, required] of dependencies) if (!blocked.has(directory)) {
|
||||
const waiting = required.filter(dependency => blocked.has(dependency));
|
||||
if (waiting.length) { blocked.add(directory); changed = true; entries.push({directory, phase: "dependency", message: `Waiting for ${waiting.join(", ")}`, next: "Resolve the named repositories, then rerun check"}); }
|
||||
}
|
||||
for (const [directory, required] of dependencies)
|
||||
if (!blocked.has(directory)) {
|
||||
const waiting = required.filter((dependency) => blocked.has(dependency));
|
||||
if (waiting.length) {
|
||||
blocked.add(directory);
|
||||
changed = true;
|
||||
entries.push({
|
||||
directory,
|
||||
phase: "dependency",
|
||||
message: `Waiting for ${waiting.join(", ")}`,
|
||||
next: "Resolve the named repositories, then rerun check",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return { workbench: context.workbench, verificationEvidence: false, worklist: entries,
|
||||
note: "Derived authoring guidance, not activation approval. Independent repositories can be delegated separately; join writers before a root check." };
|
||||
return {
|
||||
workbench: context.workbench,
|
||||
verificationEvidence: false,
|
||||
worklist: entries,
|
||||
note: "Derived authoring guidance, not activation approval. Independent repositories can be delegated separately; join writers before a root check.",
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user