mirror of
https://github.com/garrytan/gstack.git
synced 2026-08-29 09:20:39 +02:00
fix(test): adversarial findings — stable root key, symlink-alias dedupe, fixture-shape guard
Adversarial review (Claude subagent) verified the fixture's root-skill key was
the capture machine's checkout dirname: any non-gstack-named clone (every
Conductor worktree) failed the free suite, and the documented re-run-the-capture
recovery baked the local dirname into the committed fixture — silent corruption
through the tool's own protocol. The root skill is now pinned to ROOT_SKILL_KEY
('gstack', its frontmatter name). Symlink aliases are realpath-deduped (census
precedent): connect-chrome no longer gets its own ceiling, so Windows checkouts
that materialize the symlink as a plain file can't fail the stale-ceiling
set-equality test. New guards: fixture-shape validation (a string alwaysOnTotal
can no longer silently disable the ceiling), a mutation pin that the filter
shrinks the always-on ledger vs the raw bill, an alwaysOnTotal violation test
(the branch was load-bearing with only under-budget coverage), and an atomic
temp+rename fixture write. Fixture regenerated: 59 ceilings, alwaysOnTotal 6344.
Deferred with a TODO: anchoring transformFrontmatter's denylist strip to the
frontmatter block (latent, zero live collisions, pre-existing path).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
d80d3c3ed7
commit
c11e44b93a
@@ -27,7 +27,7 @@
|
||||
import { describe, test, expect } from 'bun:test';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { checkBudget } from '../lib/context-bill';
|
||||
import { buildBill, checkBudget } from '../lib/context-bill';
|
||||
import {
|
||||
buildRatchetBill,
|
||||
captureContextBudget,
|
||||
@@ -48,6 +48,28 @@ const budget: ContextBudget = JSON.parse(fs.readFileSync(BUDGET_FIXTURE_PATH, 'u
|
||||
const bill = buildRatchetBill();
|
||||
|
||||
describe('context-budget ratchet', () => {
|
||||
// checkBudget only enforces alwaysOnTotal when it is typeof number — a
|
||||
// string or missing value from a hand edit or bad merge would silently
|
||||
// turn the always-on ceiling OFF while every test stays green. Validate
|
||||
// the fixture shape so the guard cannot be disabled by a typo.
|
||||
test('fixture shape is valid (a malformed fixture must not silently disable ceilings)', () => {
|
||||
expect(typeof budget.alwaysOnTotal).toBe('number');
|
||||
expect(Number.isFinite(budget.alwaysOnTotal)).toBe(true);
|
||||
const bad = Object.entries(budget.eagerPerInvocation).filter(
|
||||
([, v]) => typeof v !== 'number' || !Number.isFinite(v),
|
||||
);
|
||||
expect(bad, `Non-numeric ceilings: ${bad.map(([k]) => k).join(', ')}. Re-run the capture.`).toEqual([]);
|
||||
});
|
||||
|
||||
// Mutation pin: the fixture-skill filter must actually shrink the
|
||||
// always-on sum vs the raw bill (deleting the totals recompute would leak
|
||||
// fixture tokens under the headroom and never fail a ceiling).
|
||||
test('filtering fixture skills shrinks the always-on ledger vs the raw bill', () => {
|
||||
const raw = buildBill(path.join(import.meta.dir, '..'));
|
||||
expect(bill.skills.some((s) => s.name.startsWith('test/'))).toBe(false);
|
||||
expect(bill.totals.skillCount).toBeLessThan(raw.totals.skillCount);
|
||||
expect(bill.totals.alwaysOnTokens).toBeLessThan(raw.totals.alwaysOnTokens);
|
||||
});
|
||||
test('always-on + eager ledgers stay under the fixture ceilings', () => {
|
||||
// actual === null means "fixture names a skill missing from the tree" —
|
||||
// the dedicated stale-fixture test below owns that case with a clearer
|
||||
|
||||
Reference in New Issue
Block a user