From 43e18af4ad7d2cb15961c8c9df57b985323014e7 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Mon, 25 May 2026 20:40:28 -0700 Subject: [PATCH] fix(catalog): deterministic proactive-suggestions.json (no per-run timestamp) Original implementation wrote a generated_at timestamp on every gen-skill-docs run. That made CI dry-run freshness checks flap because the file changed on every regeneration even when the actual content (skill descriptions, routing prose, voice triggers) was unchanged. Two fixes: 1. Drop the generated_at field. The file is purely a content registry now. 2. Only write the file when serialized content actually differs from disk. Reproducible test: bun run gen:skill-docs twice in a row now leaves scripts/proactive-suggestions.json unchanged on the second run. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/gen-skill-docs.ts | 15 +++++++++++++-- scripts/proactive-suggestions.json | 1 - 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/scripts/gen-skill-docs.ts b/scripts/gen-skill-docs.ts index bde328a1a..d928cd591 100644 --- a/scripts/gen-skill-docs.ts +++ b/scripts/gen-skill-docs.ts @@ -840,16 +840,27 @@ The orchestrator will persist the plan link to its own memory/knowledge store. // T4 catalog trim: write aggregated proactive-suggestions.json (Claude only). // The JSON registry lets agents pull voice triggers / routing prose for any // skill on demand instead of paying for it always-loaded in the catalog. + // + // No timestamp field — keeps the file content-deterministic across runs so + // CI dry-run freshness checks don't flap on regen. If a per-run timestamp + // is ever needed for debugging, write it to a separate `.gen-stamp` file. if (currentHost === 'claude' && CATALOG_MODE === 'trim' && Object.keys(proactiveAggregate).length > 0 && !DRY_RUN) { const proactivePath = path.join(ROOT, 'scripts', 'proactive-suggestions.json'); const payload = { $schema: 'https://gstack.dev/schemas/proactive-suggestions.json', - generated_at: new Date().toISOString(), catalog_mode: 'trim', note: 'Routing / voice-trigger prose extracted from SKILL.md frontmatter descriptions during catalog trim. Loaded on demand when routing guidance is needed.', skills: proactiveAggregate, }; - fs.writeFileSync(proactivePath, JSON.stringify(payload, null, 2) + '\n'); + const serialized = JSON.stringify(payload, null, 2) + '\n'; + // Only write if content actually changed — prevents needless touches that + // would flap CI freshness checks. Read existing file, compare, skip write + // when identical. + let existing = ''; + try { existing = fs.readFileSync(proactivePath, 'utf-8'); } catch { /* first run */ } + if (existing !== serialized) { + fs.writeFileSync(proactivePath, serialized); + } } // Print token budget summary diff --git a/scripts/proactive-suggestions.json b/scripts/proactive-suggestions.json index eacf6e9fd..97caec81b 100644 --- a/scripts/proactive-suggestions.json +++ b/scripts/proactive-suggestions.json @@ -1,6 +1,5 @@ { "$schema": "https://gstack.dev/schemas/proactive-suggestions.json", - "generated_at": "2026-05-26T03:40:01.996Z", "catalog_mode": "trim", "note": "Routing / voice-trigger prose extracted from SKILL.md frontmatter descriptions during catalog trim. Loaded on demand when routing guidance is needed.", "skills": {