fix: pre-landing review fixes (datamark, DRY, compact, coverage)

Addresses the pre-landing review findings (all INFORMATIONAL, no criticals):
- security: datamark resurfaced decision text at the render boundary
  (lib/gstack-decision.ts datamark() — neutralizes code fences, --- banners,
  <|role|>/</system> markers, control chars, newlines). Applied in
  gstack-decision-search human output so stored text can't masquerade as
  instructions in Context Recovery (codex hardening #3 / AC #7). --json stays raw.
- DRY: extract resolveSlug/gitBranch/flagValue to lib/bin-context.ts; both
  decision bins use it instead of duplicating the helpers.
- compact(): batch the archive append (one write, not N) and shrink the
  mid-compact crash window; simplify the opaque branch/issue ternary.
- coverage: learnings-log injection rejection (D2A wiring), search --recent/
  --scope + NaN-safe --recent, datamark-applied, unparseable lock body,
  compact-empty, corrupt-snapshot degrade.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-06-07 19:17:44 -07:00
parent 02eba57f3a
commit 55e7ed9fec
8 changed files with 164 additions and 47 deletions
+4 -18
View File
@@ -26,22 +26,12 @@ import {
compact,
type DecisionEvent,
} from "../lib/gstack-decision";
import { resolveSlug, gitBranch, flagValue } from "../lib/bin-context";
const HERE = import.meta.dir;
function resolveSlug(): string {
const r = spawnSync(`${HERE}/gstack-slug`, { encoding: "utf-8" });
const m = (r.stdout || "").match(/^SLUG=(.+)$/m);
return m ? m[1].trim() : "unknown";
}
function gitBranch(): string | undefined {
const r = spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { encoding: "utf-8" });
const b = (r.stdout || "").trim();
return b && b !== "HEAD" ? b : undefined;
}
const args = process.argv.slice(2);
const slug = resolveSlug();
const slug = resolveSlug(`${HERE}/gstack-slug`);
const paths = decisionPaths(slug);
mkdirSync(dirname(paths.log), { recursive: true });
@@ -49,10 +39,6 @@ function enqueue(): void {
// Fire-and-forget cross-machine sync (no-op when artifacts_sync is off).
spawnSync(`${HERE}/gstack-brain-enqueue`, [`projects/${slug}/decisions.jsonl`], { stdio: "ignore" });
}
function flagValue(name: string): string | undefined {
const i = args.indexOf(name);
return i >= 0 ? args[i + 1] : undefined;
}
if (args.includes("--compact")) {
const r = compact(paths);
@@ -61,8 +47,8 @@ if (args.includes("--compact")) {
process.exit(0);
}
const supersedeId = flagValue("--supersede");
const redactId = flagValue("--redact");
const supersedeId = flagValue(args, "--supersede");
const redactId = flagValue(args, "--redact");
if (supersedeId || redactId) {
const kind = supersedeId ? "supersede" : "redact";
const targetId = (supersedeId || redactId) as string;