mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-02 17:40:02 +02:00
test: delete test-infrastructure dead code (G)
- exit-propagation drives the runner's real strict verdict (BunTestOutputClassifier + strictTestExitCode); delete the unused shardRunLooksTruncated predicate. - delete skill-coverage-matrix registry + its gate (nothing reads it; the floor already iterates skillCensus()). - delete touchfiles-facade export-parity tests (Bun fails missing imports at link time) and the duplicated E2E_TIERS tier-value test. - delete brain-cache-spec TRANSPORT_DEFAULT_POLICY, SKILL_RUN_RETENTION_DAYS and the now-unused BrainTrustPolicy type with their literal tests. AUTOPLAN_PREFLIGHT_BUDGET_BYTES stays: skill-preflight-budget enforces it against real resolver output. - delete audit-compliance's JSDoc-comment grep.
This commit is contained in:
1 parent
65bfb0ce49
commit
6dc624eda6
10 files changed
+25
-460
No files matched your search
@@ -162,12 +162,6 @@ export const SKILL_CALIBRATION_WEIGHTS: Record<string, number> = {
|
||||
*/
|
||||
export const CACHE_REFRESH_LOCK_TIMEOUT_MS = 5 * 60_000;
|
||||
|
||||
/**
|
||||
* Retention policy: gstack/skill-run pages auto-archive after this many days.
|
||||
* Calibration takes (kind=bet) NEVER archive (long-term scorecard needs them).
|
||||
*/
|
||||
export const SKILL_RUN_RETENTION_DAYS = 90;
|
||||
|
||||
/**
|
||||
* Schema pack identity. Bumped when adding/removing/renaming page types.
|
||||
* On mismatch with the version recorded in _meta.json, the cache layer
|
||||
@@ -176,26 +170,6 @@ export const SKILL_RUN_RETENTION_DAYS = 90;
|
||||
export const GSTACK_SCHEMA_PACK_NAME = 'gstack-core';
|
||||
export const GSTACK_SCHEMA_PACK_VERSION = '1.0.0';
|
||||
|
||||
/**
|
||||
* Trust policy values. Drives auto-push of artifacts, calibration write-back
|
||||
* eligibility, and user-namespacing strategy.
|
||||
*/
|
||||
export type BrainTrustPolicy = 'personal' | 'shared' | 'unset';
|
||||
|
||||
/**
|
||||
* Per-transport default policy. Local engines auto-set to personal (single-tenant
|
||||
* by construction). Remote endpoints are inferred based on sources_list shape:
|
||||
* exactly one source + whoami matches → personal default; multiple sources or
|
||||
* federation → ask the policy question.
|
||||
*/
|
||||
export const TRANSPORT_DEFAULT_POLICY: Record<string, BrainTrustPolicy | 'infer'> = {
|
||||
'local-pglite': 'personal',
|
||||
'local-stdio': 'personal',
|
||||
'remote-http-single-tenant': 'personal',
|
||||
'remote-http-ambiguous': 'unset',
|
||||
unknown: 'unset',
|
||||
};
|
||||
|
||||
/**
|
||||
* User-slug fallback chain (D4 A3 defensive default). Resolved once per endpoint
|
||||
* and persisted via `gstack-config set user_slug_at_<endpoint-hash> <slug>`.
|
||||
|
||||
@@ -930,23 +930,6 @@ function formatShardSummary(shards: string[][]): string[] {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* True when a shard's output shows the run ended WITHOUT bun's final summary
|
||||
* ("Ran N tests across ..."). A process.exit() fired mid-suite skips the
|
||||
* summary AND hands back whatever code the caller passed — historically 0,
|
||||
* which made a truncated shard indistinguishable from a green one. Exit code
|
||||
* alone is therefore not evidence of completion; the summary line is.
|
||||
*
|
||||
* The runner itself now enforces this (and more) through
|
||||
* scripts/test-strict-output.ts inside runFreeShard; this predicate remains
|
||||
* the minimal documented primitive that test/exit-propagation.test.ts drives
|
||||
* with genuine truncated and genuine complete bun runs.
|
||||
*/
|
||||
export function shardRunLooksTruncated(status: number | null, output: string): boolean {
|
||||
if (status !== 0) return false; // already failing — not the silent case
|
||||
return !/Ran \d+ tests? across \d+ files?/.test(output);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Output contract: console filtering + per-file failure attribution.
|
||||
//
|
||||
|
||||
@@ -118,12 +118,6 @@ describe('Audit compliance', () => {
|
||||
});
|
||||
|
||||
// Fix 5: Data flow documentation in review.ts
|
||||
test('review.ts has data flow documentation', () => {
|
||||
const review = readFileSync(join(ROOT, 'scripts/resolvers/review.ts'), 'utf-8');
|
||||
expect(review).toContain('Data sent');
|
||||
expect(review).toContain('Data NOT sent');
|
||||
});
|
||||
|
||||
// Round 2 Fix 3: Extension sender validation + message type allowlist
|
||||
test('extension background.js validates message sender', () => {
|
||||
const bg = readFileSync(join(ROOT, 'extension/background.js'), 'utf-8');
|
||||
|
||||
@@ -22,12 +22,10 @@ import {
|
||||
AUTOPLAN_PREFLIGHT_BUDGET_BYTES,
|
||||
SALIENCE_DEFAULT_ALLOWLIST,
|
||||
SKILL_CALIBRATION_WEIGHTS,
|
||||
TRANSPORT_DEFAULT_POLICY,
|
||||
USER_SLUG_RESOLUTION_ORDER,
|
||||
GSTACK_SCHEMA_PACK_NAME,
|
||||
GSTACK_SCHEMA_PACK_VERSION,
|
||||
CACHE_REFRESH_LOCK_TIMEOUT_MS,
|
||||
SKILL_RUN_RETENTION_DAYS,
|
||||
getCacheFile,
|
||||
getSkillSubset,
|
||||
getSkillBudget,
|
||||
@@ -111,18 +109,6 @@ describe('brain-cache-spec internal consistency', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('transport policy defaults exist for all transport modes', () => {
|
||||
const required = ['local-pglite', 'local-stdio', 'remote-http-single-tenant', 'remote-http-ambiguous'];
|
||||
for (const transport of required) {
|
||||
expect(TRANSPORT_DEFAULT_POLICY[transport]).toBeDefined();
|
||||
}
|
||||
// Local transports must default personal (D4 / Phase 1.5 default rule)
|
||||
expect(TRANSPORT_DEFAULT_POLICY['local-pglite']).toBe('personal');
|
||||
expect(TRANSPORT_DEFAULT_POLICY['local-stdio']).toBe('personal');
|
||||
// Ambiguous remote MUST require explicit ask (never silent default)
|
||||
expect(TRANSPORT_DEFAULT_POLICY['remote-http-ambiguous']).toBe('unset');
|
||||
});
|
||||
|
||||
test('user-slug resolution chain has 4 deterministic fallbacks ending in non-empty', () => {
|
||||
expect(USER_SLUG_RESOLUTION_ORDER.length).toBe(4);
|
||||
expect(USER_SLUG_RESOLUTION_ORDER[USER_SLUG_RESOLUTION_ORDER.length - 1]).toBe('anonymous_hostname_sha8');
|
||||
@@ -137,10 +123,6 @@ describe('brain-cache-spec internal consistency', () => {
|
||||
expect(CACHE_REFRESH_LOCK_TIMEOUT_MS).toBe(5 * 60_000);
|
||||
});
|
||||
|
||||
test('skill-run retention is 90 days per D10 lifecycle policy', () => {
|
||||
expect(SKILL_RUN_RETENTION_DAYS).toBe(90);
|
||||
});
|
||||
|
||||
test('invalidation graph: every "skill-run-write" target also depends on it', () => {
|
||||
// recent-decisions invalidates on skill-run-write — verify the contract holds
|
||||
const targets = getInvalidationTargets('skill-run-write');
|
||||
|
||||
@@ -3,7 +3,7 @@ import { spawnSync } from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { shardRunLooksTruncated } from '../scripts/test-free-shards';
|
||||
import { BunTestOutputClassifier, strictTestExitCode } from '../scripts/test-strict-output';
|
||||
|
||||
// Fault-injection companion to test/no-suicide-exit.test.ts.
|
||||
//
|
||||
@@ -11,9 +11,10 @@ import { shardRunLooksTruncated } from '../scripts/test-free-shards';
|
||||
// process.exit. This file proves, with real bun output, WHY that guard and
|
||||
// the sharded runner's summary check both exist: `bun test` itself exits 0
|
||||
// when a mid-suite process.exit(0) fires — the truncated run is
|
||||
// indistinguishable from a green one by exit code alone. The sharded
|
||||
// runner's shardRunLooksTruncated() predicate is the detection layer; these
|
||||
// tests drive it with genuine truncated and genuine complete runs.
|
||||
// indistinguishable from a green one by exit code alone. The runner's strict
|
||||
// verdict (BunTestOutputClassifier + strictTestExitCode, used by runFreeShard)
|
||||
// is the detection layer; these tests drive it with genuine truncated and
|
||||
// genuine complete runs.
|
||||
|
||||
function runBunTest(dir: string) {
|
||||
return spawnSync('bun', ['test', '.'], {
|
||||
@@ -24,6 +25,13 @@ function runBunTest(dir: string) {
|
||||
});
|
||||
}
|
||||
|
||||
function strictVerdict(r: ReturnType<typeof runBunTest>, expectedFiles: number): number {
|
||||
const classifier = new BunTestOutputClassifier();
|
||||
classifier.write(r.stdout ?? '', 'stdout');
|
||||
classifier.write(r.stderr ?? '', 'stderr');
|
||||
return strictTestExitCode(r.status ?? 1, classifier.end(), expectedFiles);
|
||||
}
|
||||
|
||||
function withFixtureDir(files: Record<string, string>, fn: (dir: string) => void) {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'exit-prop-'));
|
||||
try {
|
||||
@@ -46,16 +54,15 @@ const FAILING_FIXTURE = fs.readFileSync(path.join(FIXTURES, 'failing.txt'), 'utf
|
||||
const PASSING_FIXTURE = fs.readFileSync(path.join(FIXTURES, 'passing.txt'), 'utf8');
|
||||
|
||||
describe('exit-code propagation (fault injection)', () => {
|
||||
test('a mid-suite process.exit(0) yields exit 0 with NO summary — and the shard predicate catches it', () => {
|
||||
test('a mid-suite process.exit(0) yields exit 0 with NO summary — and the strict verdict fails it', () => {
|
||||
withFixtureDir(
|
||||
{ 'a-suicide.test.ts': SUICIDE_FIXTURE, 'b-failing.test.ts': FAILING_FIXTURE },
|
||||
(dir) => {
|
||||
const r = runBunTest(dir);
|
||||
const combined = `${r.stdout ?? ''}${r.stderr ?? ''}`;
|
||||
if (r.status === 0) {
|
||||
// The dangerous shape: green exit, truncated run. The predicate
|
||||
// MUST flag it — this is the assertion that guards the suite.
|
||||
expect(shardRunLooksTruncated(r.status, combined)).toBe(true);
|
||||
// The dangerous shape: green exit, truncated run. The strict
|
||||
// verdict MUST fail it — this is the assertion that guards the suite.
|
||||
expect(strictVerdict(r, 2)).not.toBe(0);
|
||||
} else {
|
||||
// If a future bun version starts propagating the failure itself,
|
||||
// even better — nothing to detect. Either way, never green+silent.
|
||||
@@ -68,18 +75,16 @@ describe('exit-code propagation (fault injection)', () => {
|
||||
test('a complete green run is NOT flagged as truncated', () => {
|
||||
withFixtureDir({ 'ok.test.ts': PASSING_FIXTURE }, (dir) => {
|
||||
const r = runBunTest(dir);
|
||||
const combined = `${r.stdout ?? ''}${r.stderr ?? ''}`;
|
||||
expect(r.status).toBe(0);
|
||||
expect(shardRunLooksTruncated(r.status, combined)).toBe(false);
|
||||
expect(strictVerdict(r, 1)).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('a plain failing run propagates nonzero and is not the silent case', () => {
|
||||
test('a plain failing run propagates nonzero through the strict verdict', () => {
|
||||
withFixtureDir({ 'fail.test.ts': FAILING_FIXTURE }, (dir) => {
|
||||
const r = runBunTest(dir);
|
||||
const combined = `${r.stdout ?? ''}${r.stderr ?? ''}`;
|
||||
expect(r.status).not.toBe(0);
|
||||
expect(shardRunLooksTruncated(r.status, combined)).toBe(false);
|
||||
expect(strictVerdict(r, 1)).not.toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,7 @@
|
||||
* Asserts the schema pack is well-formed and matches the v1.48 plan:
|
||||
* - Exactly 8 page types (7 entities + 1 take)
|
||||
* - Frontmatter shape is internally consistent
|
||||
* - Retention policies match SKILL_RUN_RETENTION_DAYS spec
|
||||
* - Retention policies (skill-run pages archive after 90 days)
|
||||
* - Link verbs only reference declared verbs
|
||||
* - JSON payload shape is acceptable to mcp__gbrain__schema_apply_mutations
|
||||
*
|
||||
|
||||
@@ -8,17 +8,14 @@
|
||||
* frontmatter regressions, missing generated header, empty/trivial bodies,
|
||||
* and dangling SKILL.md.tmpl-without-SKILL.md mismatches.
|
||||
*
|
||||
* Pairs with test/skill-coverage-matrix.ts (the registry) and
|
||||
* test/parity-suite.test.ts (the content-invariant suite). Together,
|
||||
* v1.45.0.0 ships with: floor (this file) + matrix (registry CI gate)
|
||||
* + invariants (content per skill family) + size budget. That's the
|
||||
* eval-first foundation the v2.0.0.0 sections/ work builds on.
|
||||
* Pairs with test/parity-suite.test.ts (the content-invariant suite).
|
||||
* The floor iterates every authored skill from skillCensus(), so a new
|
||||
* skill is covered without registering it anywhere.
|
||||
*/
|
||||
|
||||
import { describe, test, expect } from 'bun:test';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { SKILL_COVERAGE } from './skill-coverage-matrix';
|
||||
import { skillCensus } from './helpers/skill-census';
|
||||
|
||||
const REPO_ROOT = path.resolve(import.meta.dir, '..');
|
||||
@@ -32,30 +29,9 @@ function readSkillMd(skill: string): string | null {
|
||||
}
|
||||
}
|
||||
|
||||
// Registry-completeness assertions ("every skill on disk is registered",
|
||||
// "every entry has a gate test") live in test/skill-coverage-matrix.test.ts —
|
||||
// they were duplicated here with a DIFFERENT hand-rolled directory walk, which
|
||||
// is the divergence class test/helpers/skill-census.ts exists to kill. This
|
||||
// file owns the per-skill structural compliance checks only.
|
||||
|
||||
describe('skill-coverage-floor: every skill passes structural compliance', () => {
|
||||
const skills = skillCensus(REPO_ROOT).authoredSkills;
|
||||
|
||||
test('every gate-tier test path referenced in registry exists on disk', () => {
|
||||
const missing: string[] = [];
|
||||
for (const [skill, coverage] of Object.entries(SKILL_COVERAGE)) {
|
||||
for (const testPath of [...coverage.gate, ...coverage.periodic]) {
|
||||
const fullPath = path.join(REPO_ROOT, testPath);
|
||||
if (!fs.existsSync(fullPath)) {
|
||||
missing.push(`${skill} → ${testPath}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (missing.length > 0) {
|
||||
throw new Error(`Registry references missing test files:\n ${missing.join('\n ')}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Per-skill structural compliance (file IO only, no LLM)
|
||||
for (const skill of skills) {
|
||||
describe(`skill: ${skill}`, () => {
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/**
|
||||
* Skill coverage matrix CI gate (v1.45.0.0 T1).
|
||||
*
|
||||
* Asserts every skill on disk has an entry in SKILL_COVERAGE with at
|
||||
* least one gate-tier test. The detailed per-skill structural checks
|
||||
* live in test/skill-coverage-floor.test.ts; this file is the matrix-
|
||||
* level gate that surfaces "skill added but eval not registered" cleanly.
|
||||
*/
|
||||
|
||||
import { describe, test, expect } from 'bun:test';
|
||||
import * as path from 'path';
|
||||
import { SKILL_COVERAGE, type SkillCoverage } from './skill-coverage-matrix';
|
||||
import { skillCensus } from './helpers/skill-census';
|
||||
|
||||
const REPO_ROOT = path.resolve(import.meta.dir, '..');
|
||||
|
||||
// Canonical walk (skill-census.ts). This file and skill-coverage-floor
|
||||
// previously hand-rolled two DIFFERENT walks (one skipped node_modules/docs/
|
||||
// test, one didn't) — exactly the divergence class the census exists to kill.
|
||||
function discoverSkills(): string[] {
|
||||
return skillCensus(REPO_ROOT).authoredSkills;
|
||||
}
|
||||
|
||||
describe('skill coverage matrix', () => {
|
||||
test('SKILL_COVERAGE is exported and non-empty', () => {
|
||||
expect(typeof SKILL_COVERAGE).toBe('object');
|
||||
expect(Object.keys(SKILL_COVERAGE).length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('every entry has the right shape', () => {
|
||||
const missingGate: string[] = [];
|
||||
for (const [skill, coverage] of Object.entries(SKILL_COVERAGE)) {
|
||||
expect(Array.isArray(coverage.gate)).toBe(true);
|
||||
expect(Array.isArray(coverage.periodic)).toBe(true);
|
||||
if (!coverage.gate || coverage.gate.length === 0) missingGate.push(skill);
|
||||
for (const p of [...coverage.gate, ...coverage.periodic]) {
|
||||
expect(typeof p).toBe('string');
|
||||
expect(p.startsWith('test/')).toBe(true);
|
||||
expect(p.endsWith('.test.ts')).toBe(true);
|
||||
}
|
||||
}
|
||||
if (missingGate.length > 0) {
|
||||
throw new Error(
|
||||
`Skills with no gate-tier eval: ${missingGate.join(', ')}. ` +
|
||||
`Eval-first foundation requires at least one CI-blocking check per skill.`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('every skill on disk has a registry entry', () => {
|
||||
const skills = discoverSkills();
|
||||
const missing: string[] = [];
|
||||
for (const s of skills) {
|
||||
if (!SKILL_COVERAGE[s]) missing.push(s);
|
||||
}
|
||||
if (missing.length > 0) {
|
||||
throw new Error(
|
||||
`Skills on disk missing from SKILL_COVERAGE: ${missing.join(', ')}. ` +
|
||||
`Add an entry to test/skill-coverage-matrix.ts with at least ` +
|
||||
`'test/skill-coverage-floor.test.ts' in gate[].`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('no registry entry references a skill that does not exist on disk', () => {
|
||||
const skills = new Set(discoverSkills());
|
||||
const orphans: string[] = [];
|
||||
for (const skill of Object.keys(SKILL_COVERAGE)) {
|
||||
if (!skills.has(skill)) orphans.push(skill);
|
||||
}
|
||||
if (orphans.length > 0) {
|
||||
throw new Error(
|
||||
`Registry references skills not on disk: ${orphans.join(', ')}. ` +
|
||||
`Remove from SKILL_COVERAGE or restore the skill directory.`,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,226 +0,0 @@
|
||||
/**
|
||||
* Skill coverage matrix (v1.45.0.0 T1, cathedral Phase 0).
|
||||
*
|
||||
* Single source of truth mapping each gstack skill to its E2E test files.
|
||||
* The CI gate at test/skill-coverage-matrix.test.ts fails if a skill has
|
||||
* no gate-tier entry, ensuring the eval-first foundation holds: every
|
||||
* skill has at least one CI-blocking check that asserts must-have
|
||||
* behavior.
|
||||
*
|
||||
* Two tiers per entry:
|
||||
* gate CI-blocking, runs on every PR, target <$0.50/test or free.
|
||||
* periodic Weekly cron, deeper coverage, can cost ~$1-$3/test.
|
||||
*
|
||||
* The 'floor' entry refers to test/skill-coverage-floor.test.ts —
|
||||
* a structural-compliance smoke test that covers every skill with
|
||||
* file-IO checks (free, no LLM cost). When a skill has only 'floor'
|
||||
* coverage, that's the eval-first minimum; future work can layer
|
||||
* behavioral checks on top.
|
||||
*/
|
||||
|
||||
export interface SkillCoverage {
|
||||
/** Gate-tier test file paths (relative to repo root). At least one required per skill. */
|
||||
gate: string[];
|
||||
/** Periodic-tier test file paths. Optional but recommended. */
|
||||
periodic: string[];
|
||||
/** Brief note on why this coverage is the right shape for this skill. */
|
||||
rationale?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-skill coverage. Keys MUST match the top-level skill directory name.
|
||||
* The CI test asserts every skill in the repo has an entry here AND that
|
||||
* gate[] is non-empty.
|
||||
*
|
||||
* Adding a new skill: add an entry here AND either reference an existing
|
||||
* test that covers it OR add 'test/skill-coverage-floor.test.ts' as the
|
||||
* minimum gate-tier check.
|
||||
*/
|
||||
export const SKILL_COVERAGE: Record<string, SkillCoverage> = {
|
||||
// ─── Core loop ──────────────────────────────────────────────
|
||||
ship: {
|
||||
gate: ['test/skill-e2e-ship-idempotency.test.ts', 'test/skill-coverage-floor.test.ts'],
|
||||
periodic: ['test/skill-e2e-workflow.test.ts'],
|
||||
},
|
||||
review: {
|
||||
gate: ['test/skill-e2e-review.test.ts', 'test/skill-e2e-shared-libs.test.ts', 'test/skill-e2e-shared-libs-paths.test.ts', 'test/shared-libs-evidence.test.ts', 'test/shared-libs-rendering.test.ts', 'test/skill-coverage-floor.test.ts'],
|
||||
periodic: ['test/skill-e2e-review-army.test.ts', 'test/regression-1539-review-self-verify.test.ts'],
|
||||
},
|
||||
'deslop-shared-libs': {
|
||||
gate: ['test/shared-libs-rendering.test.ts', 'test/skill-e2e-shared-libs.test.ts', 'test/skill-coverage-floor.test.ts'],
|
||||
periodic: ['test/skill-e2e-shared-libs-periodic.test.ts', 'test/codex-e2e-shared-libs.test.ts'],
|
||||
rationale: 'Free host/discovery checks; native gate traces enforce read-only source access and the actual review advisory lifecycle. Periodic evaluates opportunity and PR coverage judgment.',
|
||||
},
|
||||
qa: {
|
||||
gate: ['test/skill-e2e-qa-workflow.test.ts', 'test/skill-coverage-floor.test.ts'],
|
||||
periodic: ['test/skill-e2e-qa-workflow.test.ts', 'test/skill-e2e-qa-bugs.test.ts', 'test/skill-e2e-aside.test.ts'],
|
||||
rationale: 'qa-quick / qa-only-no-fix / qa-bootstrap are gate: the skill drives Aside when it is live and the gstack browse binary otherwise, so CI runs the fallback path. The planted-bug benchmarks, the fix loop and the live-Aside run (aside-qa-quick) are periodic.',
|
||||
},
|
||||
'qa-only': {
|
||||
gate: ['test/skill-coverage-floor.test.ts'],
|
||||
periodic: [],
|
||||
rationale: 'qa-only is qa with --report-only; behavior tested via /qa coverage.',
|
||||
},
|
||||
investigate: {
|
||||
gate: ['test/skill-coverage-floor.test.ts'],
|
||||
periodic: [],
|
||||
},
|
||||
browse: {
|
||||
gate: ['test/skill-e2e-bws.test.ts', 'test/skill-coverage-floor.test.ts'],
|
||||
periodic: ['test/skill-e2e-aside.test.ts'],
|
||||
rationale: '/browse drives the Aside browser first (the live E2E aside-browse-basic / aside-browse-flow needs a running Aside, so it is periodic) and the gstack browse binary as fallback (browse-basic / browse-snapshot exercise it, gate; the binary has its own integration suite under browse/test/). Local-HTML rendering (lib/aside-render.ts, bin/gstack-render.ts) is a library, covered by test/aside-render.test.ts.',
|
||||
},
|
||||
spec: {
|
||||
gate: [
|
||||
'test/spec-template-invariants.test.ts',
|
||||
'test/spec-template-sync.test.ts',
|
||||
'test/skill-coverage-floor.test.ts',
|
||||
],
|
||||
periodic: [
|
||||
'test/skill-e2e-spec-execute.test.ts',
|
||||
'test/skill-llm-eval-spec.test.ts',
|
||||
],
|
||||
rationale: '37 deterministic invariants pin Phase 1/3 gating, --execute race/security hardening, quality-gate redaction, archive contract, plan-mode-aware Phase 5. Periodic adds full PTY pipeline + LLM-judge.',
|
||||
},
|
||||
|
||||
// ─── Plan triad ─────────────────────────────────────────────
|
||||
'plan-ceo-review': {
|
||||
gate: [
|
||||
'test/skill-e2e-plan-ceo-finding-floor.test.ts',
|
||||
'test/skill-e2e-plan-ceo-plan-mode.test.ts',
|
||||
'test/skill-coverage-floor.test.ts',
|
||||
],
|
||||
periodic: [
|
||||
'test/skill-e2e-plan-ceo-finding-count.test.ts',
|
||||
'test/skill-e2e-plan-ceo-mode-routing.test.ts',
|
||||
],
|
||||
},
|
||||
'plan-eng-review': {
|
||||
gate: [
|
||||
'test/shared-libs-rendering.test.ts',
|
||||
'test/skill-e2e-plan-eng-finding-floor.test.ts',
|
||||
'test/skill-e2e-plan-eng-plan-mode.test.ts',
|
||||
'test/skill-coverage-floor.test.ts',
|
||||
],
|
||||
periodic: [
|
||||
'test/skill-e2e-shared-libs-periodic.test.ts',
|
||||
'test/skill-e2e-plan-eng-finding-count.test.ts',
|
||||
'test/skill-e2e-plan-eng-multi-finding-batching.test.ts',
|
||||
],
|
||||
},
|
||||
'plan-design-review': {
|
||||
gate: [
|
||||
'test/skill-e2e-plan-design-finding-floor.test.ts',
|
||||
'test/skill-e2e-plan-design-plan-mode.test.ts',
|
||||
'test/skill-e2e-plan-design-with-ui.test.ts',
|
||||
'test/skill-coverage-floor.test.ts',
|
||||
],
|
||||
periodic: ['test/skill-e2e-plan-design-finding-count.test.ts'],
|
||||
},
|
||||
'plan-devex-review': {
|
||||
gate: [
|
||||
'test/skill-e2e-plan-devex-finding-floor.test.ts',
|
||||
'test/skill-e2e-plan-devex-plan-mode.test.ts',
|
||||
'test/skill-coverage-floor.test.ts',
|
||||
],
|
||||
periodic: ['test/skill-e2e-plan-devex-finding-count.test.ts'],
|
||||
},
|
||||
autoplan: {
|
||||
gate: ['test/skill-coverage-floor.test.ts'],
|
||||
periodic: ['test/skill-e2e-autoplan-chain.test.ts', 'test/skill-e2e-autoplan-dual-voice.test.ts'],
|
||||
},
|
||||
'office-hours': {
|
||||
gate: ['test/skill-e2e-office-hours.test.ts', 'test/skill-coverage-floor.test.ts'],
|
||||
periodic: ['test/skill-e2e-office-hours-auto-mode.test.ts', 'test/skill-e2e-office-hours-phase4.test.ts'],
|
||||
},
|
||||
|
||||
// ─── Polish + design ────────────────────────────────────────
|
||||
'design-review': {
|
||||
gate: ['test/skill-coverage-floor.test.ts'],
|
||||
periodic: ['test/skill-e2e-design.test.ts'],
|
||||
rationale: 'design-review-fix drives the Aside browser (periodic; skips without one).',
|
||||
},
|
||||
'design-consultation': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
'design-shotgun': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
'design-html': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
diagram: {
|
||||
gate: ['test/skill-e2e-diagram.test.ts', 'test/skill-coverage-floor.test.ts'],
|
||||
periodic: ['test/skill-e2e-diagram.test.ts'],
|
||||
rationale: 'Triplet contract is gate-tier deterministic (gstack-render drives Aside when live, the browse daemon otherwise, so CI runs it); authoring-quality judge is periodic (E2E_TIERS: diagram-triplet/diagram-authoring-quality). The renderer itself is pinned free by test/aside-render.test.ts.',
|
||||
},
|
||||
cso: {
|
||||
gate: ['test/skill-e2e-cso.test.ts', 'test/cso-preserved.test.ts', 'test/skill-coverage-floor.test.ts'],
|
||||
periodic: [],
|
||||
rationale: 'cso-preserved.test.ts pins must-not-strip security guidance phrases.',
|
||||
},
|
||||
'document-release': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
'document-generate': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
|
||||
// ─── Ops + integrations ─────────────────────────────────────
|
||||
'land-and-deploy': { gate: ['test/skill-e2e-deploy.test.ts', 'test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
canary: {
|
||||
gate: ['test/skill-e2e-deploy.test.ts', 'test/skill-coverage-floor.test.ts'],
|
||||
periodic: ['test/skill-e2e-aside.test.ts'],
|
||||
rationale: 'canary-workflow (gate) runs the skill in simulation without a browser; aside-canary-quick drives Aside live (periodic).',
|
||||
},
|
||||
benchmark: {
|
||||
gate: ['test/skill-e2e-deploy.test.ts', 'test/skill-e2e-benchmark-providers.test.ts', 'test/skill-coverage-floor.test.ts'],
|
||||
periodic: [],
|
||||
rationale: 'benchmark-workflow (gate) runs the skill in simulation without a browser.',
|
||||
},
|
||||
'benchmark-models': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
codex: { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
retro: {
|
||||
gate: ['test/skill-coverage-floor.test.ts'],
|
||||
periodic: ['test/regression-1624-retro-stale-base.test.ts'],
|
||||
},
|
||||
'gstack-upgrade': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
'context-save': { gate: ['test/skill-e2e-context-skills.test.ts', 'test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
'context-restore': { gate: ['test/skill-e2e-context-skills.test.ts', 'test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
'setup-deploy': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
'setup-browser-cookies': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
'setup-gbrain': {
|
||||
gate: [
|
||||
'test/skill-e2e-setup-gbrain-bad-token.test.ts',
|
||||
'test/skill-e2e-setup-gbrain-path4-local-pglite.test.ts',
|
||||
'test/skill-e2e-setup-gbrain-remote.test.ts',
|
||||
'test/skill-coverage-floor.test.ts',
|
||||
],
|
||||
periodic: [],
|
||||
},
|
||||
'sync-gbrain': {
|
||||
gate: ['test/skill-coverage-floor.test.ts'],
|
||||
periodic: ['test/regression-1611-gbrain-sync-resume.test.ts'],
|
||||
},
|
||||
'open-gstack-browser': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
'pair-agent': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
scrape: {
|
||||
gate: ['test/skill-coverage-floor.test.ts'],
|
||||
periodic: ['test/skill-e2e-skillify.test.ts', 'test/skill-e2e-aside.test.ts'],
|
||||
rationale: '/scrape is Aside-first: aside-scrape-json drives Aside live and checks the JSON-only output discipline (periodic; skips without Aside). scrape-match-path / scrape-prototype-path assert the browser-skills `$B skill list` / `skill run` flow, which the Aside-first template no longer prescribes in its fallback — periodic until the fallback carries it again.',
|
||||
},
|
||||
skillify: { gate: ['test/skill-e2e-skillify.test.ts', 'test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
learn: { gate: ['test/skill-e2e-learnings.test.ts', 'test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
'plan-tune': { gate: ['test/skill-e2e-plan-tune.test.ts', 'test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
|
||||
// ─── iOS family ─────────────────────────────────────────────
|
||||
'ios-qa': { gate: ['test/skill-e2e-ios.test.ts', 'test/skill-coverage-floor.test.ts'], periodic: ['test/skill-e2e-ios-device.test.ts', 'test/skill-e2e-ios-swift-build.test.ts'] },
|
||||
'ios-fix': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
'ios-clean': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
'ios-sync': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
'ios-design-review': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
|
||||
// ─── Safety / housekeeping ──────────────────────────────────
|
||||
careful: { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
freeze: { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
unfreeze: { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
guard: { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
'landing-report': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
health: { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
'make-pdf': {
|
||||
gate: ['test/skill-coverage-floor.test.ts'],
|
||||
periodic: [],
|
||||
rationale: 'make-pdf is a binary with its own free suite under make-pdf/test/ (print pipeline via lib/aside-render.ts); the skill doc is structure-checked by the floor.',
|
||||
},
|
||||
'devex-review': { gate: ['test/skill-coverage-floor.test.ts'], periodic: [] },
|
||||
};
|
||||
@@ -5,9 +5,8 @@
|
||||
* (a) touchfiles-data.ts stays LITERALS ONLY — no imports/requires, no call
|
||||
* expressions, no spreads, no template literals. Map-diff selection
|
||||
* evaluates old git versions of that file standalone; any logic breaks it.
|
||||
* (b) the ./helpers/touchfiles facade re-exports EVERY export of both halves
|
||||
* by identity (===), so existing import sites see the same objects.
|
||||
* (c) the data file's exports are importable and non-empty.
|
||||
* (b) the data file's exports are importable and non-empty. A missing facade
|
||||
* re-export needs no test: Bun fails every importer at link time.
|
||||
*/
|
||||
|
||||
import { describe, test, expect } from 'bun:test';
|
||||
@@ -15,8 +14,6 @@ import { readFileSync } from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import * as data from './helpers/touchfiles-data';
|
||||
import * as logic from './helpers/test-selection';
|
||||
import * as facade from './helpers/touchfiles';
|
||||
|
||||
const DATA_PATH = path.join(import.meta.dir, 'helpers', 'touchfiles-data.ts');
|
||||
|
||||
@@ -124,40 +121,6 @@ describe('touchfiles-data.ts literal-only tripwire', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('facade export parity', () => {
|
||||
test('every touchfiles-data export is re-exported by identity', () => {
|
||||
const dataExports = Object.keys(data);
|
||||
expect(dataExports.length).toBeGreaterThan(0);
|
||||
for (const name of dataExports) {
|
||||
expect(
|
||||
(facade as Record<string, unknown>)[name],
|
||||
`facade must re-export '${name}' from touchfiles-data by identity`,
|
||||
).toBe((data as Record<string, unknown>)[name] as never);
|
||||
}
|
||||
});
|
||||
|
||||
test('every test-selection export is re-exported by identity', () => {
|
||||
const logicExports = Object.keys(logic);
|
||||
expect(logicExports.length).toBeGreaterThan(0);
|
||||
for (const name of logicExports) {
|
||||
expect(
|
||||
(facade as Record<string, unknown>)[name],
|
||||
`facade must re-export '${name}' from test-selection by identity`,
|
||||
).toBe((logic as Record<string, unknown>)[name] as never);
|
||||
}
|
||||
});
|
||||
|
||||
test('facade exports exactly the union of both halves', () => {
|
||||
const union = new Set([...Object.keys(data), ...Object.keys(logic)]);
|
||||
expect(new Set(Object.keys(facade))).toEqual(union);
|
||||
});
|
||||
|
||||
test('no export name collisions between data and logic', () => {
|
||||
const overlap = Object.keys(data).filter((k) => k in logic);
|
||||
expect(overlap).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('touchfiles-data exports are importable and non-empty', () => {
|
||||
test('E2E_TOUCHFILES has entries with non-empty pattern lists', () => {
|
||||
const keys = Object.keys(data.E2E_TOUCHFILES);
|
||||
@@ -167,14 +130,6 @@ describe('touchfiles-data exports are importable and non-empty', () => {
|
||||
}
|
||||
});
|
||||
|
||||
test('E2E_TIERS has entries with valid tier values', () => {
|
||||
const entries = Object.entries(data.E2E_TIERS);
|
||||
expect(entries.length).toBeGreaterThan(0);
|
||||
for (const [key, tier] of entries) {
|
||||
expect(['gate', 'periodic'], `E2E_TIERS['${key}'] has invalid tier`).toContain(tier);
|
||||
}
|
||||
});
|
||||
|
||||
test('LLM_JUDGE_TOUCHFILES has entries with non-empty pattern lists', () => {
|
||||
const keys = Object.keys(data.LLM_JUDGE_TOUCHFILES);
|
||||
expect(keys.length).toBeGreaterThan(0);
|
||||
|
||||
Reference in new issue
Block a user