mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-17 10:25:33 +02:00
refactor(memory-ingest): reunite preparePages with its docblock; pin disambiguateSlugs wiring
The #2724 disambiguateSlugs block was inserted between preparePages' docblock and the function, orphaning the secret-scanning policy doc onto the wrong symbol. Reordered. A call-site pin now asserts the prepare→stage flow actually invokes disambiguateSlugs, so a refactor can't drop the call while every unit test stays green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
8079803f2a
commit
781cc13779
+22
-22
@@ -1303,28 +1303,6 @@ async function probeMode(args: CliArgs): Promise<ProbeReport> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepare phase: walk sources, apply incremental + optional-secret-scan filters,
|
|
||||||
* parse transcripts/artifacts into PageRecord, render bodies with
|
|
||||||
* frontmatter. Returns the PreparedPage[] to stage + counts of files
|
|
||||||
* filtered at each gate.
|
|
||||||
*
|
|
||||||
* Secret scanning policy (post 2026-05-10 perf review):
|
|
||||||
*
|
|
||||||
* The actual cross-machine exfiltration boundary is `gstack-brain-sync`,
|
|
||||||
* which runs a regex-based secret scanner on the staged diff before
|
|
||||||
* `git commit` (see bin/gstack-brain-sync:78-110: AWS keys, GitHub
|
|
||||||
* tokens, OpenAI keys, PEM blocks, JWTs, bearer-token-in-JSON). That's
|
|
||||||
* the right place — it gates content leaving the machine.
|
|
||||||
*
|
|
||||||
* memory-ingest, by contrast, moves data from one local file to a
|
|
||||||
* local PGLite database. Scanning every source file at ingest time
|
|
||||||
* doesn't change exposure (the secret already lives in plaintext
|
|
||||||
* where the user keeps their transcripts and artifacts) but costs
|
|
||||||
* ~470s on cold runs. We removed the per-file gitleaks gate as
|
|
||||||
* redundant defense-in-depth and made it opt-in via `--scan-secrets`
|
|
||||||
* for users who want belt-and-suspenders.
|
|
||||||
*/
|
|
||||||
/**
|
/**
|
||||||
* Disambiguate colliding page slugs before staging (#2724).
|
* Disambiguate colliding page slugs before staging (#2724).
|
||||||
*
|
*
|
||||||
@@ -1362,6 +1340,28 @@ export function disambiguateSlugs(pages: PreparedPage[]): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare phase: walk sources, apply incremental + optional-secret-scan filters,
|
||||||
|
* parse transcripts/artifacts into PageRecord, render bodies with
|
||||||
|
* frontmatter. Returns the PreparedPage[] to stage + counts of files
|
||||||
|
* filtered at each gate.
|
||||||
|
*
|
||||||
|
* Secret scanning policy (post 2026-05-10 perf review):
|
||||||
|
*
|
||||||
|
* The actual cross-machine exfiltration boundary is `gstack-brain-sync`,
|
||||||
|
* which runs a regex-based secret scanner on the staged diff before
|
||||||
|
* `git commit` (see bin/gstack-brain-sync:78-110: AWS keys, GitHub
|
||||||
|
* tokens, OpenAI keys, PEM blocks, JWTs, bearer-token-in-JSON). That's
|
||||||
|
* the right place — it gates content leaving the machine.
|
||||||
|
*
|
||||||
|
* memory-ingest, by contrast, moves data from one local file to a
|
||||||
|
* local PGLite database. Scanning every source file at ingest time
|
||||||
|
* doesn't change exposure (the secret already lives in plaintext
|
||||||
|
* where the user keeps their transcripts and artifacts) but costs
|
||||||
|
* ~470s on cold runs. We removed the per-file gitleaks gate as
|
||||||
|
* redundant defense-in-depth and made it opt-in via `--scan-secrets`
|
||||||
|
* for users who want belt-and-suspenders.
|
||||||
|
*/
|
||||||
function preparePages(
|
function preparePages(
|
||||||
args: CliArgs,
|
args: CliArgs,
|
||||||
ctx: WalkContext,
|
ctx: WalkContext,
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
* ("accounted for N-1 of N staged ... Refusing to advance state").
|
* ("accounted for N-1 of N staged ... Refusing to advance state").
|
||||||
*/
|
*/
|
||||||
import { describe, it, expect } from "bun:test";
|
import { describe, it, expect } from "bun:test";
|
||||||
|
import { readFileSync } from "fs";
|
||||||
|
import { join } from "path";
|
||||||
import { disambiguateSlugs } from "../bin/gstack-memory-ingest";
|
import { disambiguateSlugs } from "../bin/gstack-memory-ingest";
|
||||||
|
|
||||||
const mk = (slug: string, source_path: string) => ({
|
const mk = (slug: string, source_path: string) => ({
|
||||||
@@ -62,4 +64,39 @@ describe("regression: disambiguateSlugs resolves colliding staged slugs", () =>
|
|||||||
disambiguateSlugs(pages);
|
disambiguateSlugs(pages);
|
||||||
expect(pages.map((p) => p.slug)).toEqual(before);
|
expect(pages.map((p) => p.slug)).toEqual(before);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("call-site wiring: the prepare/stage flow actually invokes disambiguateSlugs (source pin)", () => {
|
||||||
|
// The unit tests above prove the function works; nothing else proves the
|
||||||
|
// flow CALLS it — a refactor could drop the invocation and every test
|
||||||
|
// would stay green while #2724 regresses. Anchor narrowly: extract the
|
||||||
|
// preparePages function body (and, as an accepted alternate home, the
|
||||||
|
// main-flow stretch between the preparePages call and writeStaged) and
|
||||||
|
// require a disambiguateSlugs( invocation inside — cosmetic changes
|
||||||
|
// (argument rename, comment edits) don't trip this; moving the call out
|
||||||
|
// of the prepare→stage flow entirely does.
|
||||||
|
const src = readFileSync(
|
||||||
|
join(import.meta.dir, "..", "bin", "gstack-memory-ingest.ts"),
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
|
||||||
|
// preparePages body: from its declaration to the next top-level function.
|
||||||
|
const defStart = src.indexOf("function preparePages(");
|
||||||
|
expect(defStart).toBeGreaterThan(-1);
|
||||||
|
const afterDef = src.slice(defStart + "function preparePages(".length);
|
||||||
|
const endRel = afterDef.search(/\n(?:export )?(?:async )?function /);
|
||||||
|
const prepareBody = endRel === -1 ? afterDef : afterDef.slice(0, endRel);
|
||||||
|
|
||||||
|
// Alternate home: the stage flow between the preparePages call site and
|
||||||
|
// the writeStaged call that consumes its output.
|
||||||
|
const callSite = src.indexOf("= preparePages(");
|
||||||
|
const stageSite = callSite === -1 ? -1 : src.indexOf("writeStaged(", callSite);
|
||||||
|
const stageFlow =
|
||||||
|
callSite !== -1 && stageSite !== -1 ? src.slice(callSite, stageSite) : "";
|
||||||
|
|
||||||
|
const invokes = (text: string) =>
|
||||||
|
// An invocation, not the `function disambiguateSlugs(` definition.
|
||||||
|
/(?<!function )\bdisambiguateSlugs\(/.test(text);
|
||||||
|
|
||||||
|
expect(invokes(prepareBody) || invokes(stageFlow)).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user