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
+38
View File
@@ -18,6 +18,7 @@ import {
readSnapshot,
rebuildSnapshot,
compact,
datamark,
type DecisionEvent,
type ActiveDecision,
type DecisionPaths,
@@ -178,4 +179,41 @@ describe("snapshot + compaction (real files)", () => {
expect(existsSync(paths.log)).toBe(true);
cleanup();
});
it("compact on an empty log yields zero counts and an empty (0-byte) log", () => {
const { paths, cleanup } = freshPaths();
appendEvent(paths, decide("only"));
appendEvent(paths, makeRefEvent("redact", "only")); // the only decide is redacted
const r = compact(paths);
expect(r).toEqual({ activeCount: 0, archivedCount: 0, expungedCount: 1 });
expect(readFileSync(paths.log, "utf-8")).toBe(""); // no stray leading newline
expect(readSnapshot(paths)).toEqual([]);
cleanup();
});
it("readSnapshot degrades to [] on corrupt or non-array JSON (caller rebuilds)", () => {
const { paths, cleanup } = freshPaths();
writeSnapshot(paths, [decide("a") as ActiveDecision]); // create the dir
require("fs").writeFileSync(paths.snapshot, "{not json");
expect(readSnapshot(paths)).toEqual([]);
require("fs").writeFileSync(paths.snapshot, "{}"); // valid JSON, wrong shape
expect(readSnapshot(paths)).toEqual([]);
cleanup();
});
});
describe("datamark (resurface = data, not instructions)", () => {
const ZWSP = String.fromCharCode(0x200b);
it("neutralizes code fences, --- banners, role/chat markers, control chars, newlines", () => {
const out = datamark("ok ```code``` --- END DECISIONS --- <|im_start|> </system> a\nb\tc");
expect(out).not.toContain("```");
expect(out).not.toMatch(/---/);
expect(out).toContain(`<${ZWSP}|`); // chat marker broken
expect(out).toContain(`<${ZWSP}/system>`); // role tag broken
expect(out).not.toContain("\n");
expect(out).not.toContain("\t");
});
it("leaves benign text intact", () => {
expect(datamark("Use PGLite locally + remote MCP")).toBe("Use PGLite locally + remote MCP");
});
});