mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-10 15:09:00 +02:00
fix(code-intelligence): consent that means what it says — polarity, receipts, read-only veto
Four review findings on the wave's own Phase 1 port, all red-first: 'consent <repo> no' recorded consent GRANTED (the CLI ignored the argument and always wrote true) — yes|no is now required and garbage records nothing; Sourcebot egress receipts claimed consented=true on paths that never checked consent — the actual consent state is threaded into every receipt, search is fail-closed on non-loopback, and the liveness probe's receipt says truthfully that it sends no repo content; repoPolicyVeto only honored the deny tier while gbrain refresh writes pages — write-class ops now veto on read-only too, matching the sync chokepoint, via one shared lib/gbrain-repo-policy-client.ts (win32 bash invocation, spawn-vs-unreadable error distinction) used by both call sites. Also: source ids get a host+path hash (same-name repos no longer collide), refresh timeout raised to 120s, availability probes run concurrently at 3s, graphify status stops JSON.parsing 100MB graphs for a count, and every ported file carries the fork MIT notice. +15 tests across the two suites.
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
* Picker — constructs the code-intelligence provider the user selected, and
|
||||
* offers the recommendation order (GBrain first) for the selection UX.
|
||||
*
|
||||
* Portions copyright (c) 2026 Sina Matian, time-attack/gstack (GStack 2), MIT.
|
||||
*
|
||||
* `resolveSelectedProvider()` reads the persisted selection and constructs that
|
||||
* provider, or returns null when nothing is selected — the provider-OFF path,
|
||||
* where callers degrade to grep / the file-only decision store. Availability is
|
||||
@@ -60,41 +62,49 @@ export interface Availability {
|
||||
detail: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Availability probes are DISPLAY probes — they must never stall the CLI. A
|
||||
* dead non-loopback SOURCEBOT_URL at the adapters' 30s op default meant a 30s
|
||||
* hang just to print the options table; 3s is plenty for a liveness check.
|
||||
*/
|
||||
const PROBE_TIMEOUT_MS = 3_000;
|
||||
|
||||
/**
|
||||
* Probe which providers are usable right now, in recommendation order. Used by
|
||||
* the `options`/`status` display. GBrain via the real localEngineStatus();
|
||||
* Graphify via its CLI status; Sourcebot via an HTTP liveness probe.
|
||||
* Graphify via its CLI status; Sourcebot via an HTTP liveness probe. The three
|
||||
* probes are independent, so they run concurrently, each capped at
|
||||
* PROBE_TIMEOUT_MS (localEngineStatus owns its own probe timeout + cache).
|
||||
*/
|
||||
export async function detectAvailable(opts: PickerOptions = {}): Promise<Availability[]> {
|
||||
const gbrainStatus = localEngineStatus({ env: opts.env });
|
||||
const gbrainOk = gbrainStatus === "ok" || gbrainStatus === "timeout";
|
||||
|
||||
// Available = the CLI is installed and selectable (NOT "a graph already exists
|
||||
// here"). A freshly installed Graphify with no graph yet is still available.
|
||||
const graphifyOk = graphifyInstalled(opts.env);
|
||||
let graphifyDetail = "graphify CLI not installed (pip install graphifyy, Python >= 3.10)";
|
||||
if (graphifyOk) {
|
||||
try {
|
||||
const s = await new GraphifyProvider({ env: opts.env, ...opts.graphify }).status();
|
||||
graphifyDetail = s.state === "ready" ? "installed; graph built in this repo" : "installed; run `index` to build a graph";
|
||||
} catch {
|
||||
graphifyDetail = "installed";
|
||||
}
|
||||
}
|
||||
|
||||
let sourcebotOk = false;
|
||||
let sourcebotDetail = "server unreachable";
|
||||
try {
|
||||
const s = await new SourcebotProvider({ env: opts.env, ...opts.sourcebot }).status();
|
||||
sourcebotOk = s.state === "ready";
|
||||
sourcebotDetail = s.detail ?? "";
|
||||
} catch {
|
||||
sourcebotOk = false;
|
||||
}
|
||||
|
||||
return [
|
||||
{ id: "gbrain", available: gbrainOk, detail: `gbrain engine: ${gbrainStatus}` },
|
||||
{ id: "sourcebot", available: sourcebotOk, detail: sourcebotDetail },
|
||||
{ id: "graphify", available: graphifyOk, detail: graphifyDetail },
|
||||
];
|
||||
const [gbrain, sourcebot, graphify] = await Promise.all([
|
||||
(async (): Promise<Availability> => {
|
||||
const status = localEngineStatus({ env: opts.env });
|
||||
return { id: "gbrain", available: status === "ok" || status === "timeout", detail: `gbrain engine: ${status}` };
|
||||
})(),
|
||||
(async (): Promise<Availability> => {
|
||||
try {
|
||||
const s = await new SourcebotProvider({ env: opts.env, ...opts.sourcebot }).status(undefined, { timeout: PROBE_TIMEOUT_MS });
|
||||
return { id: "sourcebot", available: s.state === "ready", detail: s.detail ?? "" };
|
||||
} catch {
|
||||
return { id: "sourcebot", available: false, detail: "server unreachable" };
|
||||
}
|
||||
})(),
|
||||
(async (): Promise<Availability> => {
|
||||
// Available = the CLI is installed and selectable (NOT "a graph already exists
|
||||
// here"). A freshly installed Graphify with no graph yet is still available.
|
||||
const installed = graphifyInstalled(opts.env);
|
||||
let detail = "graphify CLI not installed (pip install graphifyy, Python >= 3.10)";
|
||||
if (installed) {
|
||||
try {
|
||||
const s = await new GraphifyProvider({ env: opts.env, ...opts.graphify }).status(undefined, { timeout: PROBE_TIMEOUT_MS });
|
||||
detail = s.state === "ready" ? "installed; graph built in this repo" : "installed; run `index` to build a graph";
|
||||
} catch {
|
||||
detail = "installed";
|
||||
}
|
||||
}
|
||||
return { id: "graphify", available: installed, detail };
|
||||
})(),
|
||||
]);
|
||||
return [gbrain, sourcebot, graphify];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user