mirror of https://github.com/garrytan/gstack.git
fix(browse): lazy GSTACK_HOME resolution in domain-skills
Module-level constants (GLOBAL_FILE, derived path) were evaluated at module-load and cached. When E2E and unit tests run in the same Bun test pass and set GSTACK_HOME differently, the second test sees the first test's path. Switch to lazy gstackHome() / globalFile() / projectFile() helpers so process.env mutations take effect. Mirrors the pattern already used in telemetry.ts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
bee6ceb4f3
commit
cc90f6761d
|
|
@ -62,11 +62,16 @@ export interface DomainSkillRow {
|
||||||
|
|
||||||
const PROMOTE_THRESHOLD = 3;
|
const PROMOTE_THRESHOLD = 3;
|
||||||
|
|
||||||
const GSTACK_HOME = process.env.GSTACK_HOME || path.join(os.homedir(), '.gstack');
|
function gstackHome(): string {
|
||||||
const GLOBAL_FILE = path.join(GSTACK_HOME, 'global-domain-skills.jsonl');
|
return process.env.GSTACK_HOME || path.join(os.homedir(), '.gstack');
|
||||||
|
}
|
||||||
|
|
||||||
|
function globalFile(): string {
|
||||||
|
return path.join(gstackHome(), 'global-domain-skills.jsonl');
|
||||||
|
}
|
||||||
|
|
||||||
function projectFile(slug: string): string {
|
function projectFile(slug: string): string {
|
||||||
return path.join(GSTACK_HOME, 'projects', slug, 'learnings.jsonl');
|
return path.join(gstackHome(), 'projects', slug, 'learnings.jsonl');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Hostname normalization (T3) ──────────────────────────────
|
// ─── Hostname normalization (T3) ──────────────────────────────
|
||||||
|
|
@ -222,7 +227,7 @@ export async function readSkill(host: string, projectSlug: string): Promise<Read
|
||||||
return { row: projectHit, source: 'project' };
|
return { row: projectHit, source: 'project' };
|
||||||
}
|
}
|
||||||
// Global layer fallback
|
// Global layer fallback
|
||||||
const globalRows = await readRows(GLOBAL_FILE);
|
const globalRows = await readRows(globalFile());
|
||||||
const globalLatest = resolveLatest(globalRows);
|
const globalLatest = resolveLatest(globalRows);
|
||||||
const globalHit = globalLatest.get(`global::${normalized}`);
|
const globalHit = globalLatest.get(`global::${normalized}`);
|
||||||
if (globalHit && globalHit.state === 'global') {
|
if (globalHit && globalHit.state === 'global') {
|
||||||
|
|
@ -346,7 +351,7 @@ export async function promoteToGlobal(host: string, projectSlug: string): Promis
|
||||||
flag_count: 0,
|
flag_count: 0,
|
||||||
updated_ts: now,
|
updated_ts: now,
|
||||||
};
|
};
|
||||||
await appendRow(GLOBAL_FILE, globalRow);
|
await appendRow(globalFile(), globalRow);
|
||||||
return globalRow;
|
return globalRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -356,7 +361,7 @@ export async function promoteToGlobal(host: string, projectSlug: string): Promis
|
||||||
*/
|
*/
|
||||||
export async function rollbackSkill(host: string, projectSlug: string, scope: SkillScope = 'project'): Promise<DomainSkillRow> {
|
export async function rollbackSkill(host: string, projectSlug: string, scope: SkillScope = 'project'): Promise<DomainSkillRow> {
|
||||||
const normalized = normalizeHost(host);
|
const normalized = normalizeHost(host);
|
||||||
const file = scope === 'project' ? projectFile(projectSlug) : GLOBAL_FILE;
|
const file = scope === 'project' ? projectFile(projectSlug) : globalFile();
|
||||||
const rows = await readRows(file);
|
const rows = await readRows(file);
|
||||||
const matching = rows.filter((r) => r.host === normalized && r.scope === scope && !r.tombstone);
|
const matching = rows.filter((r) => r.host === normalized && r.scope === scope && !r.tombstone);
|
||||||
if (matching.length < 2) {
|
if (matching.length < 2) {
|
||||||
|
|
@ -384,7 +389,7 @@ export async function rollbackSkill(host: string, projectSlug: string, scope: Sk
|
||||||
*/
|
*/
|
||||||
export async function listSkills(projectSlug: string): Promise<{ project: DomainSkillRow[]; global: DomainSkillRow[] }> {
|
export async function listSkills(projectSlug: string): Promise<{ project: DomainSkillRow[]; global: DomainSkillRow[] }> {
|
||||||
const projectRows = await readRows(projectFile(projectSlug));
|
const projectRows = await readRows(projectFile(projectSlug));
|
||||||
const globalRows = await readRows(GLOBAL_FILE);
|
const globalRows = await readRows(globalFile());
|
||||||
const projectLatest = Array.from(resolveLatest(projectRows).values());
|
const projectLatest = Array.from(resolveLatest(projectRows).values());
|
||||||
const globalLatest = Array.from(resolveLatest(globalRows).values()).filter((r) => r.state === 'global');
|
const globalLatest = Array.from(resolveLatest(globalRows).values()).filter((r) => r.state === 'global');
|
||||||
return { project: projectLatest, global: globalLatest };
|
return { project: projectLatest, global: globalLatest };
|
||||||
|
|
@ -395,7 +400,7 @@ export async function listSkills(projectSlug: string): Promise<{ project: Domain
|
||||||
*/
|
*/
|
||||||
export async function deleteSkill(host: string, projectSlug: string, scope: SkillScope = 'project'): Promise<void> {
|
export async function deleteSkill(host: string, projectSlug: string, scope: SkillScope = 'project'): Promise<void> {
|
||||||
const normalized = normalizeHost(host);
|
const normalized = normalizeHost(host);
|
||||||
const file = scope === 'project' ? projectFile(projectSlug) : GLOBAL_FILE;
|
const file = scope === 'project' ? projectFile(projectSlug) : globalFile();
|
||||||
const rows = await readRows(file);
|
const rows = await readRows(file);
|
||||||
const latest = resolveLatest(rows);
|
const latest = resolveLatest(rows);
|
||||||
const current = latest.get(`${scope}::${normalized}`);
|
const current = latest.get(`${scope}::${normalized}`);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue