mirror of
https://github.com/EveryInc/compound-engineering-plugin.git
synced 2026-09-19 01:09:55 +08:00
232 lines
9.7 KiB
TypeScript
232 lines
9.7 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import { spawnSync } from "node:child_process"
|
|
import fs from "node:fs"
|
|
import path from "node:path"
|
|
import {
|
|
ISSUE_1482_BASE_REF,
|
|
POST_SWEEP_REF,
|
|
PRE_SWEEP_REF,
|
|
SCENARIOS,
|
|
WAVE1,
|
|
scenarioHasDecisionGrade,
|
|
} from "./catalog"
|
|
import { REPO_ROOT, WORKTREE_REF } from "./extract"
|
|
|
|
const skillsDir = path.join(REPO_ROOT, "skills")
|
|
|
|
function shippedSkills(): string[] {
|
|
return fs
|
|
.readdirSync(skillsDir)
|
|
.filter((name) => fs.existsSync(path.join(skillsDir, name, "SKILL.md")))
|
|
.sort()
|
|
}
|
|
|
|
function gitPathExists(ref: string, gitPath: string): boolean {
|
|
if (ref === WORKTREE_REF) return fs.existsSync(path.join(REPO_ROOT, gitPath))
|
|
return spawnSync("git", ["cat-file", "-e", `${ref}:${gitPath}`], { cwd: REPO_ROOT }).status === 0
|
|
}
|
|
|
|
function gitShowExists(ref: string, skill: string): boolean {
|
|
return gitPathExists(ref, `skills/${skill}/SKILL.md`)
|
|
}
|
|
|
|
describe("skill-eval-cell catalog", () => {
|
|
test("every scenario grades a decision or an artifact", () => {
|
|
expect(SCENARIOS.filter((s) => !scenarioHasDecisionGrade(s)).map((s) => s.id)).toEqual([])
|
|
})
|
|
|
|
test("catalog skills are a subset of shipped skills", () => {
|
|
const shipped = new Set(shippedSkills())
|
|
expect(SCENARIOS.map((s) => s.skill).filter((name) => !shipped.has(name))).toEqual([])
|
|
})
|
|
|
|
test("scenario ids are unique", () => {
|
|
const ids = SCENARIOS.map((s) => s.id)
|
|
expect(new Set(ids).size).toBe(ids.length)
|
|
})
|
|
|
|
test("issue #1482 scenarios use the exact pre-change baseline", () => {
|
|
const issue1482 = new Set([
|
|
"ce-plan/no-implement",
|
|
"ce-work/requirements-only-stops",
|
|
"ce-work/return-to-caller-no-pr",
|
|
"lfg/plan-first",
|
|
])
|
|
|
|
expect(
|
|
SCENARIOS.filter((scenario) => issue1482.has(scenario.id)).map((scenario) => [
|
|
scenario.id,
|
|
scenario.baseline_ref,
|
|
]),
|
|
).toEqual(
|
|
[...issue1482].map((id) => [id, ISSUE_1482_BASE_REF]),
|
|
)
|
|
expect(SCENARIOS.find((scenario) => scenario.id === "ce-code-review/report-only-default")?.baseline_ref).toBeUndefined()
|
|
})
|
|
|
|
test("WAVE1 ids exist in the catalog", () => {
|
|
const ids = new Set(SCENARIOS.map((s) => s.id))
|
|
expect(WAVE1.filter((id) => !ids.has(id))).toEqual([])
|
|
})
|
|
|
|
test("every scenario skill exists at PRE_SWEEP_REF and POST_SWEEP_REF", () => {
|
|
const missing: string[] = []
|
|
for (const scenario of SCENARIOS) {
|
|
if (!gitShowExists(PRE_SWEEP_REF, scenario.skill)) {
|
|
missing.push(`${scenario.skill} missing at ${PRE_SWEEP_REF}`)
|
|
}
|
|
if (!gitShowExists(POST_SWEEP_REF, scenario.skill)) {
|
|
missing.push(`${scenario.skill} missing at ${POST_SWEEP_REF}`)
|
|
}
|
|
}
|
|
expect(missing).toEqual([])
|
|
})
|
|
|
|
test("fixture paths exist and tasks are non-empty", () => {
|
|
const bad: string[] = []
|
|
for (const s of SCENARIOS) {
|
|
if (!s.task.trim()) bad.push(`${s.id}: empty task`)
|
|
if (s.fixture && !fs.existsSync(path.join(REPO_ROOT, s.fixture))) {
|
|
bad.push(`${s.id}: missing fixture ${s.fixture}`)
|
|
}
|
|
}
|
|
expect(bad).toEqual([])
|
|
})
|
|
|
|
test("workspace_read paths exist in the scenario fixture", () => {
|
|
const missing: string[] = []
|
|
for (const s of SCENARIOS) {
|
|
if (!s.grade.workspace_read?.length) continue
|
|
if (!s.fixture) {
|
|
missing.push(`${s.id}: workspace_read without a fixture`)
|
|
continue
|
|
}
|
|
for (const rel of s.grade.workspace_read) {
|
|
if (!fs.existsSync(path.join(REPO_ROOT, s.fixture, rel))) {
|
|
missing.push(`${s.id}: ${rel} missing under ${s.fixture}`)
|
|
}
|
|
}
|
|
}
|
|
expect(missing).toEqual([])
|
|
})
|
|
|
|
test("files_read_post pointers exist on the arm that grades them", () => {
|
|
const missing: string[] = []
|
|
for (const s of SCENARIOS) {
|
|
for (const rel of s.grade.files_read_post ?? []) {
|
|
const refs = [POST_SWEEP_REF, ...(s.preview_ref ? [s.preview_ref] : [])]
|
|
for (const ref of refs) {
|
|
if (!gitPathExists(ref, `skills/${s.skill}/${rel}`)) {
|
|
missing.push(`${s.id}: ${rel} missing at ${ref}`)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
expect(missing).toEqual([])
|
|
})
|
|
|
|
test("required-read files are only the load-bearing ones", () => {
|
|
const listed = SCENARIOS.flatMap((s) =>
|
|
(s.grade.files_read_post ?? []).map((rel) => `${s.id}:${rel}`),
|
|
).sort()
|
|
expect(listed).toEqual(
|
|
[
|
|
"ce-babysit-pr/check-only-answer-reactivates-source:references/tick.md",
|
|
"ce-babysit-pr/behind-reads-branch-currency:references/branch-currency.md",
|
|
"ce-babysit-pr/pipeline-returns-canonical-human-decision:references/pipeline.md",
|
|
"ce-babysit-pr/pipeline-returns-canonical-human-decision:references/report.md",
|
|
"ce-brainstorm/lightweight-ends-in-chat:references/phase-0.md",
|
|
"ce-brainstorm/lookup-not-ask:references/interaction-rules.md",
|
|
"ce-brainstorm/standard-scope-routes-to-file:references/phase-0.md",
|
|
"ce-brainstorm/verdict-routes-to-pov:references/phase-0.md",
|
|
"ce-brainstorm/write-plan-reads-plan-write:references/plan-write.md",
|
|
"ce-commit-push-pr/description-only-no-commit:references/pr-description-writing.md",
|
|
"ce-commit-push-pr/babysit-off-preserves-human-decision:references/apply-and-handoff.md",
|
|
"ce-debug/pipeline-convergent-fix:references/pipeline-mode.md",
|
|
"ce-doc-review/routine-fix-no-product-lens:references/persona-selection.md",
|
|
"ce-doc-review/settled-origin-no-product-lens:references/persona-selection.md",
|
|
"ce-doc-review/staked-position-keeps-product-lens:references/persona-selection.md",
|
|
"ce-doc-review/strategic-weight-keeps-product-lens:references/persona-selection.md",
|
|
"ce-debug/pipeline-divergent-defer:references/pipeline-mode.md",
|
|
"ce-handoff/resume-asks-does-not-act:references/resume.md",
|
|
"ce-ideate/unidentified-subject-reads-scope-gates:references/scope-gates.md",
|
|
"ce-plan/chat-brief-small-no-file:references/output-contracts.md",
|
|
"ce-plan/config-model-reaches-authoring-gate:references/reasoning-elevation.md",
|
|
"ce-plan/direct-trivial-stays-in-chat:references/output-contracts.md",
|
|
"ce-plan/no-implement:references/output-mode.md",
|
|
"ce-plan/no-implement:references/resume.md",
|
|
"ce-plan/objective-above-the-changed-component:references/plan-sections.md",
|
|
"ce-plan/objective-holdable-without-the-rest-of-the-plan:references/plan-sections.md",
|
|
"ce-polish/https-server-uses-actual-url:references/run.md",
|
|
"ce-polish/start-server-reads-run:references/run.md",
|
|
"ce-pov/oracle-dispatches-peers:references/cross-model-panel.md",
|
|
"ce-pov/stay-read-only:references/method.md",
|
|
"ce-riffrec-feedback-analysis/quick-notes:references/analyzer.md",
|
|
"ce-riffrec-feedback-analysis/quick-notes:references/quick-bug-report.md",
|
|
"ce-riffrec-feedback-analysis/setup-before-recording:references/install-riffrec.md",
|
|
"ce-resolve-pr-feedback/pipeline-no-merge:references/pipeline-mode.md",
|
|
"ce-resolve-pr-feedback/pipeline-returns-complete-human-decision:references/evaluation-rubric.md",
|
|
"ce-resolve-pr-feedback/pipeline-returns-complete-human-decision:references/pipeline-mode.md",
|
|
"ce-test-xcode/missing-mcp-stops:references/setup-and-build.md",
|
|
"ce-test-xcode/swiftui-inline-link-fallback:references/test-and-report.md",
|
|
"ce-work/behavior-fix-routes-to-review:references/input-triage.md",
|
|
"ce-work/requirements-only-stops:references/input-triage.md",
|
|
"ce-work/return-to-caller-no-pr:references/input-triage.md",
|
|
"ce-work/return-to-caller-no-pr:references/return-to-caller.md",
|
|
"lfg/plan-first:references/plan-brief.md",
|
|
].sort(),
|
|
)
|
|
})
|
|
|
|
test("the 8KB sweep has no in-progress skills left", () => {
|
|
expect(SCENARIOS.filter((s) => s.cohort === "in-progress").map((s) => s.id)).toEqual([])
|
|
})
|
|
|
|
test("feature-only decision rows are explicitly post-only", () => {
|
|
expect(SCENARIOS.filter((s) => s.post_only).map((s) => s.id).sort()).toEqual([
|
|
"ce-babysit-pr/check-only-answer-reactivates-source",
|
|
"ce-babysit-pr/pipeline-returns-canonical-human-decision",
|
|
"ce-commit-push-pr/babysit-off-preserves-human-decision",
|
|
"ce-compound-refresh/guidance-survives-implementation-conflict",
|
|
"ce-debug/pipeline-divergent-defer",
|
|
"ce-plan/config-model-reaches-authoring-gate",
|
|
"ce-resolve-pr-feedback/pipeline-returns-complete-human-decision",
|
|
])
|
|
})
|
|
|
|
test("the post arm resolves the working tree, not a commit", () => {
|
|
expect(POST_SWEEP_REF).toBe(WORKTREE_REF)
|
|
})
|
|
|
|
test("a read-only restraint row also carries a positive probe", () => {
|
|
// Under read_only the forbidden mutation is impossible, so must_exclude alone
|
|
// can never fail. Something that observes the stated decision has to be present.
|
|
const vacuous = SCENARIOS.filter((s) => {
|
|
if (!s.read_only || !s.grade.must_exclude?.length) return false
|
|
return (
|
|
!s.grade.must_include?.length &&
|
|
!s.grade.files_read_post?.length &&
|
|
!s.grade.workspace_read?.length
|
|
)
|
|
}).map((s) => s.id)
|
|
expect(vacuous).toEqual([])
|
|
})
|
|
|
|
test("preview refs are only on in-progress skills and resolve when set", () => {
|
|
const bad: string[] = []
|
|
const resolved = new Map<string, boolean>()
|
|
for (const s of SCENARIOS) {
|
|
if (s.preview_ref && s.cohort !== "in-progress") bad.push(`${s.id}: preview_ref on ${s.cohort}`)
|
|
if (!s.preview_ref) continue
|
|
let ok = resolved.get(s.preview_ref)
|
|
if (ok === undefined) {
|
|
const r = spawnSync("git", ["rev-parse", "--verify", s.preview_ref], { cwd: REPO_ROOT })
|
|
ok = r.status === 0
|
|
resolved.set(s.preview_ref, ok)
|
|
}
|
|
if (!ok) bad.push(`${s.id}: preview_ref ${s.preview_ref} does not resolve`)
|
|
}
|
|
expect(bad).toEqual([])
|
|
})
|
|
})
|