@blundergoat/goat-flow
Version:
AI coding agent harness and local dashboard for Claude Code, OpenAI Codex, Google Antigravity, and GitHub Copilot - setup audits, guardrails, structured skills, deny hooks, and persistent learning loops.
111 lines • 4.42 kB
JavaScript
import { pass, fail } from "./helpers.js";
import { collectMarkdownFiles } from "./helpers.js";
const VERIFIED_ON = "2026-04-18";
/** Return the recovery provenance. */
function recoveryProvenance(type, paths, sourceType = "spec") {
return {
source_type: sourceType,
source_urls: [],
verified_on: VERIFIED_ON,
normative_level: type === "integrity"
? "MUST"
: type === "advisory"
? "SHOULD"
: "BEST_PRACTICE",
evidence_paths: paths,
};
}
/**
* Count markdown checkbox markers without interpreting task completion.
*
* Recovery checks only report whether milestone notes contain local workflow
* state; they intentionally avoid scoring incomplete checkboxes as failures.
*/
function countTaskMarkers(content) {
return content.match(/- \[[ xX]\]/g)?.length ?? 0;
}
const milestoneTracking = {
id: "milestone-tracking",
name: "Milestone tracking configured",
concern: "recovery",
type: "integrity",
provenance: recoveryProvenance("integrity", [
"docs/harness-audit.md",
".goat-flow/architecture.md",
".goat-flow/plans/README.md",
]),
/** Run the Milestone tracking configured check. */
run: (ctx) => {
const plansDir = ".goat-flow/plans";
const buildDetails = (fileCount) => ({
recovery: ctx.agents.map((af) => ({
agent: af.agent.id,
dir: plansDir,
fileCount,
})),
});
if (!ctx.fs.exists(plansDir)) {
return fail(["No plans directory found"], ["Create .goat-flow/plans/ for milestone tracking"], [
"Create .goat-flow/plans/ so optional task, roadmap, and milestone notes have a stable home.",
], buildDetails(0));
}
const allMdFiles = collectMarkdownFiles(ctx.fs, plansDir);
if (allMdFiles.length === 0) {
return pass([
"Plans directory exists (empty - valid for new projects; plan tracking is optional)",
], buildDetails(0));
}
const markerCounts = [];
for (const markdownFile of allMdFiles) {
const content = ctx.fs.readFile(markdownFile);
if (content)
markerCounts.push(countTaskMarkers(content));
}
const totalMarkers = markerCounts.reduce((sum, count) => sum + count, 0);
const findings = [
`Plans directory exists with ${allMdFiles.length} markdown file(s) and ${totalMarkers} checkbox marker(s)`,
"Task and milestone content is optional local workflow state; checkbox completion, status, testing gates, and roadmap progress are not audited.",
];
return pass(findings, buildDetails(allMdFiles.length));
},
};
const sessionLogs = {
id: "session-logs",
name: "Session logs directory",
concern: "recovery",
type: "integrity",
provenance: recoveryProvenance("integrity", [
"docs/harness-audit.md",
".goat-flow/architecture.md",
]),
/** Run the Session logs directory check. */
run: (ctx) => {
const logsDir = ".goat-flow/logs/sessions";
const buildDetails = (fileCount) => ({
recovery: ctx.agents.map((af) => ({
agent: af.agent.id,
dir: logsDir,
fileCount,
})),
});
if (!ctx.fs.exists(logsDir)) {
return fail(["No session logs directory"], ["Create .goat-flow/logs/sessions/ directory"], [
"Create .goat-flow/logs/sessions/ and start logging sessions for continuity between conversations.",
], buildDetails(0));
}
let fileCount = 0;
try {
fileCount = ctx.fs
.listDir(logsDir)
.filter((fileName) => fileName.endsWith(".md")).length;
}
catch {
return fail(["Session logs path exists but is not readable as a directory"], ["Ensure .goat-flow/logs/sessions/ is a directory, not a file"], [
"Remove or rename the file at .goat-flow/logs/sessions and recreate as a directory.",
], buildDetails(0));
}
return pass(["Session logs directory exists"], buildDetails(fileCount));
},
};
export const RECOVERY_CHECKS = [milestoneTracking, sessionLogs];
//# sourceMappingURL=check-recovery.js.map