fix(gsd): address remaining adversarial review findings for wave 3

1. hasImplementationArtifacts "unknown" now blocks completion instead of
   warn-and-proceed. Both auto-dispatch.ts and auto-recovery.ts updated
   to treat "unknown" as a stop condition, preventing milestone completion
   when git status cannot be verified.

2. Audit log SAFE_KEYS allowlist expanded to include "id", "error", and
   "count" fields. SPLIT BRAIN logError entries now persist the entity ID
   and rollback error details to audit-log.jsonl for triage/repair.
This commit is contained in:
Jeremy 2026-04-07 13:50:49 -05:00
parent 42141d8979
commit a9c62adf22
3 changed files with 8 additions and 3 deletions

View file

@ -776,7 +776,11 @@ export const DISPATCH_RULES: DispatchRule[] = [
};
}
if (artifactCheck === "unknown") {
logWarning("dispatch", `Implementation artifact check inconclusive for ${mid} — proceeding with caution`);
return {
action: "stop",
reason: `Cannot verify implementation artifacts for milestone ${mid}: git check was inconclusive. Resolve git issues and retry.`,
level: "error",
};
}
// Verification class compliance: if operational verification was planned,

View file

@ -393,7 +393,8 @@ export function verifyExpectedArtifact(
// A milestone with only .gsd/ plan files and zero implementation code is
// not genuinely complete — the LLM wrote plan files but skipped actual work.
if (unitType === "complete-milestone") {
if (hasImplementationArtifacts(base) === "absent") return false;
const artifactResult = hasImplementationArtifacts(base);
if (artifactResult === "absent" || artifactResult === "unknown") return false;
}
return true;

View file

@ -295,7 +295,7 @@ function _sanitizeForAudit(entry: LogEntry): LogEntry {
};
if (entry.context) {
// Allowlist: only persist known-safe structured keys
const SAFE_KEYS = new Set(["fn", "tool", "mid", "sid", "tid", "worktree"]);
const SAFE_KEYS = new Set(["fn", "tool", "mid", "sid", "tid", "worktree", "id", "error", "count"]);
const filtered: Record<string, string> = {};
for (const [k, v] of Object.entries(entry.context)) {
if (SAFE_KEYS.has(k)) {