mirror of
https://github.com/garrytan/gstack.git
synced 2026-06-18 15:50:11 +02:00
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:
+4
-18
@@ -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;
|
||||
|
||||
+16
-25
@@ -19,42 +19,29 @@
|
||||
*/
|
||||
|
||||
import { existsSync } from "fs";
|
||||
import { spawnSync } from "child_process";
|
||||
import {
|
||||
decisionPaths,
|
||||
readSnapshot,
|
||||
rebuildSnapshot,
|
||||
readEvents,
|
||||
filterByScope,
|
||||
datamark,
|
||||
type ActiveDecision,
|
||||
} from "../lib/gstack-decision";
|
||||
import { resolveSlug, gitBranch, flagValue } from "../lib/bin-context";
|
||||
|
||||
const HERE = import.meta.dir;
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
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;
|
||||
}
|
||||
function flagValue(name: string): string | undefined {
|
||||
const i = args.indexOf(name);
|
||||
return i >= 0 ? args[i + 1] : undefined;
|
||||
}
|
||||
|
||||
const slug = resolveSlug();
|
||||
const slug = resolveSlug(`${HERE}/gstack-slug`);
|
||||
const paths = decisionPaths(slug);
|
||||
const queryRaw = flagValue("--query");
|
||||
const queryRaw = flagValue(args, "--query");
|
||||
const query = queryRaw?.toLowerCase();
|
||||
const scope = flagValue("--scope");
|
||||
const branch = flagValue("--branch") ?? gitBranch();
|
||||
const issue = flagValue("--issue");
|
||||
const recent = flagValue("--recent") ? parseInt(flagValue("--recent") as string, 10) : undefined;
|
||||
const scope = flagValue(args, "--scope");
|
||||
const branch = flagValue(args, "--branch") ?? gitBranch();
|
||||
const issue = flagValue(args, "--issue");
|
||||
const recentRaw = flagValue(args, "--recent");
|
||||
const recent = recentRaw ? parseInt(recentRaw, 10) : undefined;
|
||||
const showAll = args.includes("--all");
|
||||
const asJson = args.includes("--json");
|
||||
const semantic = args.includes("--semantic");
|
||||
@@ -89,9 +76,13 @@ if (asJson) {
|
||||
}
|
||||
|
||||
for (const d of rows) {
|
||||
const scopeTag = d.scope === "repo" ? "" : ` [${d.scope}${d.branch ? `:${d.branch}` : ""}${d.issue ? `:${d.issue}` : ""}]`;
|
||||
console.log(`- ${d.decision}${scopeTag} (${d.source}, ${d.date.slice(0, 10)})`);
|
||||
if (d.rationale) console.log(` why: ${d.rationale}`);
|
||||
// Datamark all stored free-text (decision, rationale, branch/issue) — it lands in
|
||||
// agent context via Context Recovery, so treat it as DATA, not instructions.
|
||||
const branchTag = d.branch ? `:${datamark(d.branch)}` : "";
|
||||
const issueTag = d.issue ? `:${datamark(d.issue)}` : "";
|
||||
const scopeTag = d.scope === "repo" ? "" : ` [${d.scope}${branchTag}${issueTag}]`;
|
||||
console.log(`- ${datamark(d.decision ?? "")}${scopeTag} (${d.source}, ${d.date.slice(0, 10)})`);
|
||||
if (d.rationale) console.log(` why: ${datamark(d.rationale)}`);
|
||||
}
|
||||
|
||||
// OPTIONAL gbrain enhancement. Lazy import so the reliable path above never loads
|
||||
|
||||
Reference in New Issue
Block a user