mirror of
https://github.com/EveryInc/compound-engineering-plugin.git
synced 2026-10-10 01:49:05 +02:00
1210 lines
63 KiB
TypeScript
1210 lines
63 KiB
TypeScript
import { readFileSync } from "fs"
|
|
import path from "path"
|
|
import { describe, expect, test } from "bun:test"
|
|
|
|
function readRepoFile(relativePath: string): string {
|
|
return readFileSync(path.join(process.cwd(), relativePath), "utf8")
|
|
}
|
|
|
|
function sliceSection(doc: string, startMarker: string, endMarker: string): string {
|
|
const start = doc.indexOf(startMarker)
|
|
expect(start).toBeGreaterThan(-1)
|
|
const end = doc.indexOf(endMarker, start + startMarker.length)
|
|
expect(end).toBeGreaterThan(start)
|
|
return doc.slice(start, end)
|
|
}
|
|
|
|
const planSections = readRepoFile(
|
|
"skills/ce-plan/references/plan-sections.md",
|
|
)
|
|
const brainstormSections = readRepoFile(
|
|
"skills/ce-brainstorm/references/brainstorm-sections.md",
|
|
)
|
|
const planSkill = readRepoFile("skills/ce-plan/SKILL.md")
|
|
/**
|
|
* Phase blocks that used to sit in ce-plan's body now live in the references the
|
|
* body names as required reads at their point of use (#1412 restructure). Rules
|
|
* that must fire from the always-loaded window keep a `planSkill` pin below;
|
|
* invariants of the produced artifact are pinned against the corpus instead, so
|
|
* the guarantee follows the text rather than being deleted with it.
|
|
*/
|
|
const planIntake = readRepoFile("skills/ce-plan/references/intake.md")
|
|
const planStructure = readRepoFile("skills/ce-plan/references/structure.md")
|
|
const planFinalReview = readRepoFile("skills/ce-plan/references/final-review.md")
|
|
const planResume = readRepoFile("skills/ce-plan/references/resume.md")
|
|
const planHandoff = readRepoFile("skills/ce-plan/references/plan-handoff.md")
|
|
const planCorpus =
|
|
planSkill + planIntake + planStructure + planFinalReview + planResume
|
|
const brainstormSkill = readRepoFile("skills/ce-brainstorm/SKILL.md")
|
|
const brainstormPhase0 = readRepoFile("skills/ce-brainstorm/references/phase-0.md")
|
|
const brainstormPlanWrite = readRepoFile("skills/ce-brainstorm/references/plan-write.md")
|
|
const brainstormHandoff = readRepoFile(
|
|
"skills/ce-brainstorm/references/handoff.md",
|
|
)
|
|
const universalBrainstorming = readRepoFile(
|
|
"skills/ce-brainstorm/references/universal-brainstorming.md",
|
|
)
|
|
const ceWork = readRepoFile("skills/ce-work/SKILL.md")
|
|
// Plan-reading strategy and worker packaging are loaded from the references the body
|
|
// mandates at Phase 1; the plan-artifact invariants they carry are asserted over the
|
|
// whole ce-work unit rather than the always-loaded window.
|
|
const ceWorkIntake = readRepoFile("skills/ce-work/references/work-intake.md")
|
|
const ceWorkTriage = readRepoFile("skills/ce-work/references/input-triage.md")
|
|
const ceWorkReturn = readRepoFile("skills/ce-work/references/return-to-caller.md")
|
|
const ceWorkLoop = readRepoFile("skills/ce-work/references/implementation-loop.md")
|
|
const ceWorkStrategy = readRepoFile("skills/ce-work/references/execution-strategy.md")
|
|
const ceWorkDocs = readRepoFile("docs/guides/ce-work.md")
|
|
const ceWorkEngines = readRepoFile(
|
|
"skills/ce-work/references/execution-engines.md",
|
|
)
|
|
const crossModelExecution = readRepoFile(
|
|
"skills/ce-work/references/cross-model-execution.md",
|
|
)
|
|
const planMarkdownRendering = readRepoFile(
|
|
"skills/ce-plan/references/markdown-rendering.md",
|
|
)
|
|
const planHtmlRendering = readRepoFile(
|
|
"skills/ce-plan/references/html-rendering.md",
|
|
)
|
|
const planSynthesisSummary = readRepoFile(
|
|
"skills/ce-plan/references/synthesis-summary.md",
|
|
)
|
|
const brainstormSynthesisSummary = readRepoFile(
|
|
"skills/ce-brainstorm/references/synthesis-summary.md",
|
|
)
|
|
const planDeepeningWorkflow = readRepoFile(
|
|
"skills/ce-plan/references/deepening-workflow.md",
|
|
)
|
|
const lfg = readRepoFile("skills/lfg/SKILL.md")
|
|
const lfgNextWorkHandoff = readRepoFile(
|
|
"skills/lfg/references/next-work-handoff.md",
|
|
)
|
|
// lfg's body is the pipeline spine: each step's invocation string, its stop
|
|
// classes, and a required-read pointer. The step-scoped mechanics live in the
|
|
// reference each step names, so those invariants are asserted against the file
|
|
// that now owns them.
|
|
const lfgStageRouting = readRepoFile("skills/lfg/references/stage-routing.md")
|
|
const lfgPlanBrief = readRepoFile("skills/lfg/references/plan-brief.md")
|
|
const lfgWorkReturn = readRepoFile("skills/lfg/references/work-return.md")
|
|
const lfgReviewFollowup = readRepoFile("skills/lfg/references/review-followup.md")
|
|
const lfgCloseOut = readRepoFile("skills/lfg/references/shipping-tail.md")
|
|
const docReview = readRepoFile("skills/ce-doc-review/SKILL.md")
|
|
const docReviewTemplate = readRepoFile(
|
|
"skills/ce-doc-review/references/subagent-template.md",
|
|
)
|
|
// Reviewer payload construction (slices, provenance, settled decisions) moved into
|
|
// the dispatch reference the body mandates before Phase 2 dispatch.
|
|
const docReviewDispatch = readRepoFile("skills/ce-doc-review/references/dispatch.md")
|
|
// Document classification signals moved into the Phase 1 intake reference.
|
|
const docReviewIntake = readRepoFile("skills/ce-doc-review/references/document-intake.md")
|
|
const codeReview = readRepoFile("skills/ce-code-review/SKILL.md")
|
|
// Plan discovery, readiness classification, and requirement extraction moved into the
|
|
// reference the body's spine mandates before reviewer selection.
|
|
const codeReviewIntent = readRepoFile(
|
|
"skills/ce-code-review/references/intent-and-plan.md",
|
|
)
|
|
const codeReviewFinish = readRepoFile(
|
|
"skills/ce-code-review/references/finish-review.md",
|
|
)
|
|
const ceWorkShipping = readRepoFile(
|
|
"skills/ce-work/references/shipping-workflow.md",
|
|
)
|
|
const docReviewSynthesis = readRepoFile(
|
|
"skills/ce-doc-review/references/synthesis-and-presentation.md",
|
|
)
|
|
const simplifyCode = readRepoFile("skills/ce-simplify-code/SKILL.md")
|
|
const prDescriptionWriting = readRepoFile(
|
|
"skills/ce-commit-push-pr/references/pr-description-writing.md",
|
|
)
|
|
const proof = readRepoFile("skills/ce-proof/SKILL.md")
|
|
const ideate = readRepoFile("skills/ce-ideate/references/post-ideation-workflow.md")
|
|
const agents = readRepoFile("AGENTS.md")
|
|
|
|
describe("unified plan artifact contract", () => {
|
|
test("plan section contract defines unified metadata, readiness, and section ids", () => {
|
|
expect(planSections).toContain("artifact_contract: ce-unified-plan/v1")
|
|
expect(planSections).toContain("artifact_readiness")
|
|
expect(planSections).toContain("product_contract_source")
|
|
expect(planSections).toContain("requirements-only")
|
|
expect(planSections).toContain("implementation-ready")
|
|
expect(planSections).toContain("Do **not** use `artifact_readiness: approach-plan`")
|
|
expect(planSections).not.toMatch(/^\s+- `approach-plan`/m)
|
|
expect(planSections).toMatch(/active.*in_progress.*completed.*done/s)
|
|
expect(planSections).toMatch(/no `status` field|no .*status.*field/i)
|
|
|
|
for (const id of [
|
|
"goal-capsule",
|
|
"product-contract",
|
|
"product-requirements",
|
|
"planning-contract",
|
|
"implementation-units",
|
|
"verification-contract",
|
|
"definition-of-done",
|
|
]) {
|
|
expect(planSections).toContain(id)
|
|
}
|
|
// The launch prompt is skill-emitted and there is no Reader Index — neither is a doc section.
|
|
expect(planSections).not.toContain("goal-launch-block")
|
|
expect(planSections).not.toContain("reader-index")
|
|
})
|
|
|
|
test("brainstorm writes requirements-only unified plan skeletons under docs/plans", () => {
|
|
expect(brainstormSections).toContain("<root>/plans/YYYY-MM-DD-HHMM-<type>-<topic>-plan")
|
|
expect(brainstormSections).toContain("no daily sequence number")
|
|
expect(brainstormSections).toContain("artifact_readiness: requirements-only")
|
|
expect(brainstormSections).toContain("product_contract_source: ce-brainstorm")
|
|
// Requirements-only is slimmed for standalone readability: no Goal Launch
|
|
// Block and no Reader Index (the launch prompt is skill-emitted at handoff).
|
|
expect(brainstormSections).toMatch(/light and standalone-readable/i)
|
|
expect(brainstormSections).toContain("Do **not** emit a `## Goal Launch Block` or `## Reader Index`")
|
|
expect(brainstormSections).toMatch(/omits empty\s+`Planning Contract`/)
|
|
|
|
expect(brainstormSkill).toContain("<root>/plans/YYYY-MM-DD-HHMM-<type>-<topic>-plan")
|
|
expect(brainstormSkill).toContain("local wall-clock time at write")
|
|
expect(brainstormSkill).toContain("artifact_readiness: requirements-only")
|
|
expect(brainstormSkill).toContain("product_contract_source: ce-brainstorm")
|
|
expect(brainstormSkill).toContain("Do **not** emit a Goal Launch Block or Reader Index")
|
|
// 2026-08-18: the legacy-path rule (Phase 0.1) and the non-software carve-out
|
|
// (Phase 0.1b) moved into references/phase-0.md with the rest of Phase 0 when
|
|
// the body was restructured under the Codex 8000-byte prompt budget. Both are
|
|
// artifact-content invariants, so they are asserted against the file that owns
|
|
// them; the frontmatter fields and the path shape above stay pinned to the body
|
|
// because they are the cross-skill contract ce-plan enriches.
|
|
expect(brainstormPhase0).toContain("new `ce-brainstorm` outputs do not write there")
|
|
expect(brainstormPhase0).toContain("non-software route does **not** write `artifact_contract: ce-unified-plan/v1`")
|
|
|
|
expect(universalBrainstorming).toContain("outside the software unified-plan artifact contract")
|
|
expect(universalBrainstorming).toContain("Do not write `artifact_contract: ce-unified-plan/v1`")
|
|
expect(universalBrainstorming).toContain("let `ce-plan` choose the universal/knowledge-work artifact shape")
|
|
})
|
|
|
|
test("plan filenames use a local wall-clock time instead of daily sequences", () => {
|
|
expect(planCorpus).toContain("<root>/plans/YYYY-MM-DD-HHMM-<type>-<descriptive-name>-plan.md")
|
|
expect(planCorpus).toContain("do not scan for or allocate a daily sequence number")
|
|
expect(planCorpus).toContain("local wall-clock time at write")
|
|
expect(planCorpus).toContain("Reserve the candidate path atomically")
|
|
expect(planCorpus).toContain("preserve the existing artifact basename")
|
|
expect(planCorpus).not.toContain("YYYY-MM-DD-NNN")
|
|
// The hyphenated prefix keeps new artifacts sorting interleaved with legacy
|
|
// `YYYY-MM-DD-NNN` files; a hyphen-free prefix sorts them into a separate block.
|
|
expect(planCorpus).not.toContain("YYYYMMDDTHHMMSSZ")
|
|
})
|
|
|
|
test("brainstorm handoff passes the unified plan path to ce-plan", () => {
|
|
expect(brainstormHandoff).toContain("Pass the unified")
|
|
expect(brainstormHandoff).toContain("exact plan artifact path returned by the write step")
|
|
expect(brainstormHandoff).toContain("including any collision suffix")
|
|
expect(brainstormHandoff).toContain("Recommended next step: `ce-plan <plan artifact path>`")
|
|
// Recommended path is interactive planning; the autonomous slot is lfg
|
|
// (plan-first full ship), not a skip-planning /goal.
|
|
expect(brainstormHandoff).toContain("Create the implementation plan")
|
|
expect(brainstormHandoff).toContain("Ship it autonomously with `lfg`")
|
|
expect(brainstormHandoff).toMatch(/passing the unified plan artifact path as its\s+argument/i)
|
|
// The skip-planning /goal slot was removed in favor of plan-first lfg.
|
|
expect(brainstormHandoff).not.toContain("skip-planning slot")
|
|
// The lfg option must be gated on an artifact existing: a brief-alignment
|
|
// brainstorm can skip doc creation, and lfg's pipeline ce-plan step needs the
|
|
// artifact path (it cannot prompt) — without one there is nothing to enrich.
|
|
expect(brainstormHandoff).toMatch(/a unified plan artifact was created/i)
|
|
expect(brainstormHandoff).toMatch(/with no artifact.*nothing to enrich/i)
|
|
})
|
|
|
|
test("brainstorm self-reviews the written artifact before its handoff", () => {
|
|
expect(brainstormSkill).toContain("Ready for Planning Check")
|
|
for (const check of ["Complete", "Consistent", "Focused", "Usable by planning"]) {
|
|
expect(brainstormSections).toContain(`**${check}**`)
|
|
}
|
|
expect(brainstormSections).toMatch(/Fix a failed check in place.*preserves settled intent/s)
|
|
expect(brainstormSections).toContain("ask one targeted question")
|
|
expect(brainstormSections).toMatch(/choose or change product behavior or\s+scope/)
|
|
// 2026-08-22: the check is scoped to a written file; a Lightweight chat result enters Phase 4 with no check to run.
|
|
expect(brainstormSkill).toContain("do not declare it written or enter Phase 4 while any check fails")
|
|
})
|
|
|
|
test("brainstorm handoff explains that downstream work consumes the written artifact", () => {
|
|
expect(brainstormHandoff).toContain(
|
|
"Planning and shipping will use this artifact as the definition of what to build.",
|
|
)
|
|
})
|
|
|
|
test("ce-doc-review personas map unified-* document types to their base review lens", () => {
|
|
// Another-agent P2 (PR #972): persona prompts branch on `Document type:
|
|
// requirements` / `plan`, but the orchestrator may pass `unified-requirements`
|
|
// / `unified-plan` — so the persona's adaptation block silently never fires on
|
|
// a unified artifact. The shared subagent-template must map unified-* to base.
|
|
const subagentTemplate = readRepoFile(
|
|
"skills/ce-doc-review/references/subagent-template.md",
|
|
)
|
|
expect(subagentTemplate).toMatch(/apply the `requirements` branch for `unified-requirements`/i)
|
|
expect(subagentTemplate).toMatch(/the `plan` branch for `unified-plan`/i)
|
|
})
|
|
|
|
test("ce-plan enriches unified plans in place and preserves legacy inputs", () => {
|
|
expect(planCorpus).toContain("requirements-only unified plan")
|
|
expect(planCorpus).toMatch(/enrich(?:es|ing) (?:it|that same (?:artifact|file)) in place/)
|
|
expect(planCorpus).toContain("this run enriches that same file in place")
|
|
expect(planCorpus).toContain("Search `docs/brainstorms/`")
|
|
expect(planCorpus).toContain("create a new unified plan in `<root>/plans/`")
|
|
expect(planCorpus).toContain("product_contract_source: ce-plan-bootstrap")
|
|
expect(planFinalReview).toContain("artifact_readiness: implementation-ready")
|
|
expect(planCorpus).toContain("Definition of Done")
|
|
// The launch prompt is generated at handoff, never written into the doc.
|
|
expect(planCorpus).toContain("Do not write a launch prompt into the doc")
|
|
})
|
|
|
|
test("ce-work is readiness-aware before execution", () => {
|
|
expect(ceWork).toContain("plan readiness")
|
|
expect(ceWork).toContain("references/input-triage.md")
|
|
expect(ceWorkTriage).toContain("classify `artifact_readiness` before reading the body")
|
|
expect(ceWorkTriage).toContain("requirements-only` -> stop")
|
|
expect(ceWorkTriage).toContain("Any other readiness value")
|
|
expect(ceWorkIntake).toContain("Build a section map")
|
|
expect(ceWorkStrategy).toContain("Do not send \"read the whole plan\"")
|
|
expect(ceWorkTriage).toContain("mode:return-to-caller <plan-path>")
|
|
expect(ceWorkReturn).toContain("standalone_shipping_skipped: true")
|
|
expect(ceWorkTriage).not.toContain("artifact_readiness: approach-plan")
|
|
})
|
|
|
|
test("lfg delegates implementation to ce-work return-to-caller mode", () => {
|
|
// The dispatch strings and the /goal boundary fire from the body; the readiness
|
|
// values are applied by step 1's gate, whose first action is reading plan-brief.
|
|
expect(lfgPlanBrief).toContain("artifact_readiness: implementation-ready")
|
|
expect(lfgPlanBrief).toContain("execution: code")
|
|
expect(lfgPlanBrief).toContain("any unrecognized readiness value")
|
|
expect(lfg).toContain("readiness check in `references/plan-brief.md`")
|
|
expect(lfg).toContain("LFG never launches `/goal` directly")
|
|
expect(lfg).toContain("mode:return-to-caller <plan-path-from-step-1>")
|
|
expect(lfg).toContain("ce-code-review` skill with `mode:agent plan:<plan-path-from-step-1>`")
|
|
expect(lfgPlanBrief).not.toContain("artifact_readiness: approach-plan")
|
|
|
|
// The return contract itself is owned by the reference step 2 requires before
|
|
// accepting a return.
|
|
expect(lfgWorkReturn).toContain("standalone_shipping_skipped: true")
|
|
expect(lfgWorkReturn).toContain("verification_evidence")
|
|
expect(lfgWorkReturn).toContain("Do NOT decide the test strategy inside LFG")
|
|
expect(lfgWorkReturn).toContain("invoke `ce-work` one more time in recovery mode")
|
|
expect(lfgWorkReturn).toContain("implementation_run:<safe-id>")
|
|
expect(lfgWorkReturn).toContain("When `actual_route` is `native` and `run_id` is `null`")
|
|
expect(lfgWorkReturn).toContain("repeat the original ce-work invocation once without an `implementation_run:` carrier")
|
|
expect(lfgWorkReturn).toContain("A non-native return without a safe run id remains blocked")
|
|
expect(lfgWorkReturn).toContain("stop as blocked and report the missing fields")
|
|
})
|
|
|
|
test("lfg offers an opt-in fresh-session handoff for separately planned future work", () => {
|
|
// The closeout that gates the offer moved into the reference step 10 requires
|
|
// before it prints anything.
|
|
expect(lfg).toContain("references/shipping-tail.md")
|
|
expect(lfgCloseOut).toContain("semantic role `work-relationships`")
|
|
expect(lfgCloseOut).toContain("cautious legacy semantic fallback")
|
|
expect(lfgCloseOut).toContain("references/next-work-handoff.md")
|
|
expect(lfgCloseOut).toMatch(/older unmarked Product Contract.*area this plan owns.*future separately planned areas/s)
|
|
expect(lfgCloseOut).toContain("Do not match an exact visible heading")
|
|
expect(lfgCloseOut).toMatch(/do not .*invoke `ce-handoff` before the user explicitly accepts/i)
|
|
|
|
expect(lfgNextWorkHandoff).toContain("<!-- ce-section: work-relationships -->")
|
|
expect(lfgNextWorkHandoff).toContain('data-ce-section="work-relationships"')
|
|
expect(lfgNextWorkHandoff).toContain("The visible heading is not part of this protocol")
|
|
expect(lfgNextWorkHandoff).toMatch(/larger body of separately planned work/i)
|
|
expect(lfgNextWorkHandoff).toMatch(/already planned, completed, absorbed/i)
|
|
expect(lfgNextWorkHandoff).toMatch(/Do not choose by document\s+order/)
|
|
expect(lfgNextWorkHandoff).toContain("One justified winner")
|
|
expect(lfgNextWorkHandoff).toContain("Real tie")
|
|
expect(lfgNextWorkHandoff).toContain("No ready candidate")
|
|
expect(lfgNextWorkHandoff).toContain("Only after explicit acceptance")
|
|
expect(lfgNextWorkHandoff).toContain("LFG owns the recommendation")
|
|
|
|
for (const field of [
|
|
"Next-session objective",
|
|
"Recommended area",
|
|
"Why next",
|
|
"Authoritative prior plan",
|
|
"Relationship to completed work",
|
|
"Actual delivery state",
|
|
"Carry-forward decisions",
|
|
"Assumptions to revalidate",
|
|
"Other candidates not selected",
|
|
"Artifact boundary",
|
|
]) {
|
|
expect(lfgNextWorkHandoff).toContain(`**${field}:**`)
|
|
}
|
|
expect(lfgNextWorkHandoff).toMatch(/separate requirements-only unified plan/i)
|
|
expect(lfgNextWorkHandoff).toMatch(/do not extend or edit the prior plan/i)
|
|
})
|
|
|
|
test("lfg carries per-stage routing carriers at each stage seam", () => {
|
|
const carrier = lfgStageRouting
|
|
expect(sliceSection(lfg, "## Per-stage routing carriers", "1. **Read `references/plan-brief.md` first**")).toContain(
|
|
"semantic intent",
|
|
)
|
|
expect(carrier).toContain("semantic intent")
|
|
expect(carrier).toContain("not keyword or prompt-token matching")
|
|
expect(carrier).toContain("plain mention")
|
|
expect(carrier).toContain('"use Codex for implementation"')
|
|
expect(carrier).toContain('"only use Composer for implementation"')
|
|
expect(carrier).toContain("implementation_engine")
|
|
for (const field of ["mode", "target", "model", "source"]) {
|
|
expect(carrier).toContain(`\`${field}\``)
|
|
}
|
|
expect(carrier).toContain("exactly these four fields")
|
|
expect(carrier).toContain("Never pass")
|
|
expect(carrier).toContain("`ce-plan`")
|
|
expect(carrier).toContain("planning or review")
|
|
|
|
// Per-stage routing: planning routes to a plan_model carrier; an unscoped
|
|
// directive binds to implementation only and never broadens; the upfront
|
|
// disambiguation question is interactive-gated and headless runs never ask.
|
|
expect(carrier).toContain("plan_model:<alias>")
|
|
expect(carrier).toContain("Planning** routes to `ce-plan`")
|
|
expect(carrier).toContain("Scoped directive")
|
|
expect(carrier).toContain("Unscoped directive")
|
|
expect(carrier).toContain("implementation stage only")
|
|
expect(carrier).toContain("never broaden an unscoped directive")
|
|
expect(carrier).toMatch(/ask exactly \*\*one\*\* upfront question/i)
|
|
expect(carrier).toMatch(/disable-model-invocation.*headless run, never ask/is)
|
|
expect(carrier).toContain("default path is mandatory")
|
|
|
|
// Step 1 threads the plan_model carrier to ce-plan beside the sanitized request;
|
|
// the body names the carrier at the seam and the reference owns its exact form.
|
|
expect(sliceSection(lfg, "1. **Read `references/plan-brief.md` first**", "2. **Read `references/work-return.md` first**")).toContain(
|
|
"`plan_model:<alias>` carrier",
|
|
)
|
|
const step1 = carrier
|
|
expect(step1).toContain("prefix the `ce-plan` invocation with its `plan_model:<alias>` carrier")
|
|
expect(step1).toMatch(/never woven into it/i)
|
|
|
|
const step2 = carrier
|
|
expect(step2).toContain("mode:return-to-caller implementation_engine:<compact-json> <plan-path-from-step-1>")
|
|
expect(step2).toContain("mode:return-to-caller implementation_engine:<compact-json> implementation_run:<safe-id> <plan-path-from-step-1>")
|
|
expect(step2).toContain('implementation_engine:{"mode":"prefer","target":"codex","model":null,"source":"lfg-current-turn"}')
|
|
expect(step2).toContain("portable string envelope")
|
|
expect(step2).toContain("standing per-checkout configuration")
|
|
expect(carrier).toContain("Do not construct a carrier from standing configuration")
|
|
expect(lfgWorkReturn).toContain("same `implementation_engine:<compact-json>` carrier")
|
|
expect(lfgWorkReturn).toContain("same `run_id`")
|
|
})
|
|
|
|
test("lfg's route-aware return gate preserves its shipping tail", () => {
|
|
// Receipt fields belong to the reference; the prefer/require stop classes stay
|
|
// in the body, where they fire whether or not the reference was opened.
|
|
const step2 = lfgWorkReturn + sliceSection(lfg, "2. **Read `references/work-return.md` first**", "3. **Read `references/review-followup.md` now**")
|
|
for (const field of [
|
|
"implementation_engine_binding",
|
|
"requested_route",
|
|
"actual_route",
|
|
"requested_model",
|
|
"actual_model",
|
|
"fallback_reason",
|
|
"run_id",
|
|
"unit_receipts",
|
|
"plan_checkpoint",
|
|
"blockers",
|
|
"recovery_path",
|
|
]) {
|
|
expect(step2).toContain(`\`${field}\``)
|
|
}
|
|
expect(step2).toContain("`prefer`")
|
|
expect(step2).toContain("continue to step 3 exactly once")
|
|
expect(step2).toContain("prominently disclosing its requested-versus-actual route/model")
|
|
expect(step2).toContain("`require`")
|
|
// 2026-08-21: the require stop is decided from the return fields; ce-work's producer contract continues natively.
|
|
expect(step2).toContain("`actual_route` differs from `requested_route` stops the pipeline as blocked")
|
|
})
|
|
|
|
test("review and publishing skills understand unified artifacts", () => {
|
|
expect(docReview).toContain("unified-requirements")
|
|
expect(docReview).toContain("unified-plan")
|
|
expect(docReview).toContain("Product Contract only")
|
|
expect(docReview).toContain("HTML unified artifacts")
|
|
expect(docReviewDispatch).toContain("section slice")
|
|
expect(docReviewIntake).toContain("product_contract_source: ce-brainstorm")
|
|
expect(docReviewDispatch).toContain("product_contract_source:<value>")
|
|
expect(docReviewTemplate).toContain("product_contract_source:ce-brainstorm")
|
|
expect(docReviewTemplate).toContain("product_contract_source:ce-plan-bootstrap")
|
|
|
|
expect(codeReviewIntent).toContain("<root>/plans/*.{md,html}")
|
|
expect(codeReviewIntent).toContain("Product Contract` -> `### Requirements")
|
|
expect(codeReviewIntent).toContain("readiness before checking completeness")
|
|
expect(codeReviewIntent).toContain("must not trigger implementation-unit completeness findings")
|
|
|
|
expect(proof).toContain("Only publish markdown")
|
|
expect(proof).toContain("requirements-only")
|
|
})
|
|
|
|
test("docs and adjacent handoffs use the new convention", () => {
|
|
expect(ideate).toContain("requirements-only unified plan under `<root>/plans/`")
|
|
expect(agents).toContain("New `ce-brainstorm` outputs are requirements-only unified plans")
|
|
expect(agents).toContain("Historical `docs/brainstorms/*-requirements.*` files remain readable legacy inputs")
|
|
})
|
|
|
|
test("launch prompt is skill-emitted at handoff, not a baked doc section", () => {
|
|
// No Goal Launch Block in the artifact contract (anchor or section bullet).
|
|
expect(planSections).not.toContain("goal-launch-block")
|
|
expect(planSections).not.toContain("**Goal Launch Block**")
|
|
// The prompt is generated by the handoff from the plan's current content,
|
|
// thin, pointing to the plan's sections rather than copying them.
|
|
expect(planHandoff).toMatch(/generated here at handoff, never written into the doc/i)
|
|
expect(planHandoff).toMatch(/do \*\*not\*\* copy the plan's resolved decisions/i)
|
|
})
|
|
|
|
test("consuming skills carry a size-aware heading-scan algorithm, not full-doc-first", () => {
|
|
// The reader strategy lives in the skills, not in an in-doc Reader Index.
|
|
// plan-sections prescribes heading/anchor wayfinding for markdown AND HTML.
|
|
expect(planSections).toMatch(/consuming skills own the reading\s+algorithm/i)
|
|
expect(planSections).toMatch(/scan headings/i)
|
|
expect(planSections).toMatch(/scan the heading elements/i) // explicit HTML wayfinding
|
|
expect(planSections).toMatch(/do \*\*not\*\* load the entire artifact/i)
|
|
// Size-aware: a short plan can be read in full.
|
|
expect(planSections).toMatch(/can just be read in full/i)
|
|
// ce-work carries the same discipline, markdown + HTML, size-aware.
|
|
expect(ceWorkIntake).toContain("do **not** read the whole document first")
|
|
expect(ceWorkIntake).toMatch(/can be read in full/i)
|
|
expect(ceWorkIntake).toMatch(/in \*\*HTML\*\* scan the/i)
|
|
})
|
|
|
|
test("Verification Contract requires repo-specific commands, not generic run tests", () => {
|
|
expect(planSections).toContain("Repo-specific test commands and quality gates")
|
|
expect(planSections).toMatch(/repo-specific commands and quality gates/i)
|
|
expect(planSections).toMatch(/Avoid generic "run tests"/i)
|
|
expect(planMarkdownRendering).toMatch(/concrete repo commands such as `bun test` rather than generic "run tests"/i)
|
|
})
|
|
|
|
test("contract guides measurable exit thresholds and dead-code cleanup for long goal runs", () => {
|
|
// Reinforced by Kundel's /goal guide: optimization-shaped goals need a
|
|
// measurable exit threshold, and long autonomous runs must remove
|
|
// abandoned-attempt code before declaring done.
|
|
expect(planSections).toMatch(/optimization-shaped/i)
|
|
expect(planSections).toMatch(/measurable threshold|metric target/i)
|
|
expect(planSections).toContain("ce-optimize")
|
|
expect(planSections).toMatch(/abandoned-attempt code is removed|dead-end and experimental code/i)
|
|
expect(ceWorkEngines).toMatch(/dead-end or experimental code .* has been removed|experimental code from approaches that did not pan out/i)
|
|
})
|
|
|
|
test("conversion/pipeline override keeps one canonical discovery target", () => {
|
|
// Same-basename .md/.html siblings must not become competing latest plans.
|
|
expect(planCorpus).toContain("new canonical path")
|
|
expect(planCorpus).toMatch(/report old path and new canonical path/i)
|
|
expect(planHandoff).toContain("the plan exists as exactly one artifact")
|
|
})
|
|
|
|
test("ce-work Phase 0 parses the return-to-caller mode token before triage", () => {
|
|
// Codex #972 P1: lfg passes `mode:return-to-caller <plan-path>`; ce-work
|
|
// must strip the mode token, not treat the whole string as a bare prompt.
|
|
expect(ceWork).toMatch(/Before any other input decision, read `references\/input-triage\.md`/i)
|
|
expect(ceWorkTriage).toMatch(/begins with `mode:return-to-caller`/i)
|
|
// legacy alias still recognized so an old reference doesn't break.
|
|
expect(ceWorkTriage).toMatch(/legacy aliases `mode:caller-owned-tail`/i)
|
|
expect(ceWorkTriage).toMatch(/strip that token/i)
|
|
expect(ceWorkTriage).toMatch(/one compact JSON object prefixed exactly `implementation_engine:`/i)
|
|
expect(ceWorkTriage).toContain("after any mode token is stripped")
|
|
})
|
|
|
|
test("ce-work surfaces its caller-owned mode in discovery metadata and public docs", () => {
|
|
// Description states the orchestrator-only branch; the flag grammar lives in
|
|
// argument-hint and the body so the always-on catalog is not a procedure dump.
|
|
expect(ceWork).toMatch(/description:.*outer orchestrator needs implementation and local verification only, without the shipping tail/i)
|
|
expect(ceWork).toMatch(/argument-hint:.*mode:return-to-caller \[implementation_engine:<compact-json>\] \[implementation_run:<safe-id>\] <plan path> for outer orchestrators/i)
|
|
expect(ceWorkDocs).toContain("## Use Beneath an Outer Orchestrator")
|
|
expect(ceWorkDocs).toContain("standalone_shipping_skipped: true")
|
|
expect(ceWorkDocs).toMatch(/does not run the standalone shipping tail/i)
|
|
// Do not claim return-to-caller skips all simplification — Phase 2 Simplify as You Go still runs.
|
|
expect(ceWorkDocs).toMatch(/Mid-implementation "Simplify as You Go" still runs/i)
|
|
expect(ceWorkDocs).toMatch(/skips the standalone shipping tail \(final simplify, review, PR, CI\)/i)
|
|
})
|
|
|
|
test("ce-code-review discovery/extraction covers HTML and Product Contract requirements", () => {
|
|
// Codex #972 P2: discovery must scan .html and extraction must read
|
|
// Product Contract > Requirements, matching the completeness contract.
|
|
expect(codeReviewIntent).toContain("<root>/plans/*.{md,html}")
|
|
expect(codeReviewIntent).toMatch(/unified `Product Contract` -> `### Requirements`/)
|
|
expect(codeReviewIntent).toMatch(/requirements-only artifact[\s\S]{0,80}product intent only/i)
|
|
})
|
|
|
|
test("ce-plan 5.1.5 synthesis gate fires for unified-plan sources, not only legacy docs", () => {
|
|
// Codex #972 P2: new ce-brainstorm -> ce-plan <unified-plan> enrichment
|
|
// must still get the plan-time scoping-synthesis checkpoint.
|
|
expect(planCorpus).toMatch(/whenever Phase 0\.2 resolved an upstream Product Contract source/i)
|
|
expect(planCorpus).toMatch(/enrichment flow is brainstorm-sourced and MUST fire this gate/i)
|
|
expect(planCorpus).toMatch(/Skip Phase 0\.7 only in solo invocation|Skip Phase 5\.1\.5 only in solo invocation/i)
|
|
})
|
|
|
|
test("evaluator-complete launch prompt lives in the engine template, not the doc", () => {
|
|
// The goal prompt is also the completion criteria, so it must be
|
|
// self-contained — but it lives in the emitted template (ce-work's
|
|
// execution-engines.md), not as a baked plan-sections doc section.
|
|
expect(ceWorkEngines).toMatch(/Done when the transcript shows/i)
|
|
// The standalone /goal prompt is plan-agnostic and hardcodes no PR directive;
|
|
// instead it carries the PR-precedence line (plan strategy, repo/user override).
|
|
// (Structural no-PR lives only in return-to-caller mode, asserted separately below.)
|
|
expect(ceWorkEngines).toMatch(/plan-agnostic/i)
|
|
expect(ceWorkEngines).toMatch(/don't hardcode an open-a-PR/i)
|
|
expect(ceWorkEngines).toMatch(/Follow the plan's PR\/landing strategy if it defines one/i)
|
|
// plan-sections no longer prescribes a launch-prompt/Goal Launch Block section.
|
|
expect(planSections).not.toMatch(/evaluator-complete/i)
|
|
expect(planSections).not.toContain("Human standalone launch")
|
|
})
|
|
|
|
test("implementation-ready requires zero launch-blocking open questions", () => {
|
|
expect(planSections).toMatch(/no\s+launch-blocking open question remains/i)
|
|
expect(planSections).toMatch(/stays\s+`requirements-only`/i)
|
|
expect(planSections).toMatch(/blocker resolution \/\s*planning/i)
|
|
})
|
|
|
|
test("a requirements-only path is an enrichment input, not a Phase 0.1 resume target", () => {
|
|
// Codex P1 (PR #972): lfg hands ce-plan a requirements-only docs/plans/ path
|
|
// in disable-model-invocation pipeline mode; Phase 0.1's "confirm update-or-
|
|
// create" fires on any referenced docs/plans/ file BEFORE Phase 0.2's
|
|
// enrich-in-place rule, with no user to answer -> the hands-off flow strands.
|
|
// Phase 0.1 must carve requirements-only plans out of the resume prompt and
|
|
// auto-resolve the resume choice in pipeline mode.
|
|
const phaseStart = planResume.indexOf("#### 0.1 Resume Existing Plan Work")
|
|
expect(phaseStart).toBeGreaterThan(-1)
|
|
const resumeRegion = planResume.slice(phaseStart, phaseStart + 1600)
|
|
expect(/requirements-only unified plan is not a resume target/i.test(resumeRegion)).toBe(true)
|
|
expect(/do \*?\*?not\*?\*? fire the update-or-create confirm/i.test(resumeRegion)).toBe(true)
|
|
expect(/Fall through to Phase 0\.2/i.test(resumeRegion)).toBe(true)
|
|
expect(/pipeline mode the resume choice is made automatically|never prompted/i.test(resumeRegion)).toBe(true)
|
|
})
|
|
|
|
test("format conversion: a requirements-only artifact with an implementation-ready sibling is superseded", () => {
|
|
// Codex P2 (PR #972): a format conversion writes a new canonical .md and
|
|
// leaves the old .html with stale requirements-only metadata. Both discovery
|
|
// sites glob .md AND .html, so they could rediscover the stale sibling and
|
|
// re-enrich (ce-plan) or stop (ce-work) even though the sibling is ready.
|
|
// Both must skip a requirements-only artifact that has an implementation-ready
|
|
// same-basename sibling.
|
|
expect(planCorpus).toMatch(/Skip a superseded sibling/i)
|
|
expect(planCorpus).toMatch(/same-basename.*other format|<basename>\.md.*<basename>\.html/i)
|
|
expect(ceWorkTriage).toMatch(/Superseded sibling/i)
|
|
expect(ceWorkTriage).toMatch(/select the implementation-ready sibling and execute it rather than stopping/i)
|
|
})
|
|
|
|
test("large plans get a navigation-only Unit Index, gated to ~10+ units", () => {
|
|
expect(planSections).toMatch(/Unit Index \(large plans only/i)
|
|
expect(planSections).toMatch(/ten or more\s+units/i)
|
|
// navigation-only, not a content restatement (avoids the Reader-Index anti-pattern)
|
|
expect(planSections).toMatch(/navigation aid only/i)
|
|
expect(planSections).toMatch(/unit bodies\s+stay authoritative/i)
|
|
expect(planSections).toMatch(/files touched/i)
|
|
// gated: omitted on small plans so it isn't ceremony
|
|
expect(planSections).toMatch(/Omit it below ~?10 units/i)
|
|
})
|
|
|
|
test("ce-plan records a Product Contract preservation note on in-place enrichment", () => {
|
|
expect(planCorpus).toContain("Product Contract preservation")
|
|
expect(planCorpus).toMatch(/Product Contract unchanged|changed: .*R-IDs/)
|
|
})
|
|
|
|
test("execution engines define a Codex lane, progress-visibility, and compaction recovery", () => {
|
|
// Codex #972-review P1 #3 / P2 #9 / P2 #10
|
|
expect(ceWorkEngines).toContain("Codex specifically")
|
|
// Codex exposes a callable goal tool; the skill starts it and does NOT call update_goal.
|
|
expect(ceWorkEngines).toContain("create_goal")
|
|
expect(ceWorkEngines).toMatch(/skill does NOT call `update_goal`/i)
|
|
expect(ceWorkEngines).toMatch(/start goal-mode directly, with no copy-paste/i)
|
|
// Claude Code has no goal tools → copy-paste only.
|
|
expect(ceWorkEngines).toMatch(/Claude Code exposes no goal tools/i)
|
|
expect(ceWorkEngines).toContain("Progress visibility (independent of tail ownership)")
|
|
expect(ceWorkEngines).toMatch(/must not open any PR/i)
|
|
expect(ceWorkEngines).toMatch(/draft\*?\*? PR only/i)
|
|
expect(ceWorkEngines).toMatch(/re-open the plan and re-check/i)
|
|
expect(ceWorkEngines).toMatch(/compacted to a summary/i)
|
|
})
|
|
|
|
test("post-plan menu offers /goal prompt as a mutually-exclusive executor", () => {
|
|
expect(planHandoff).toContain("Run it as a `/goal`")
|
|
expect(planHandoff).toMatch(/`ce-work` does \*{0,2}not\*{0,2} also run/i)
|
|
expect(planHandoff).toContain("create_goal")
|
|
// The update_goal rule is a mechanic of starting the goal, and plan-handoff.md
|
|
// owns the objective and the start. It arrived with the goal lane in #972 with no
|
|
// recorded incident behind the duplicate body copy, and the body STOP-loads that
|
|
// reference before the menu renders — so pin the rule in its owner, and pin the
|
|
// kernel to require the owner immediately before the menu.
|
|
expect(planHandoff).toMatch(/do not call `update_goal`|the goal session marks its own completion/i)
|
|
expect(planSkill).toMatch(/Read `references\/plan-handoff\.md` immediately before Phase 5\.3\.8 and 5\.4/i)
|
|
// No authoring-file meta-references leak into runtime menu content.
|
|
expect(planHandoff).not.toContain("Per the AGENTS.md")
|
|
expect(planSkill).not.toContain("per the AGENTS.md narrow exception")
|
|
})
|
|
|
|
test("ce-work defines the execution-engine selection lane", () => {
|
|
expect(ceWork).toContain("Resolve the engine, then strategy")
|
|
expect(ceWork).toContain("references/execution-engines.md")
|
|
expect(ceWorkEngines).toContain("dynamic-workflow")
|
|
expect(ceWorkEngines).toMatch(/prompt-emission only|never invoked from inside this skill/i)
|
|
|
|
expect(ceWorkEngines).toContain("Probe host capability")
|
|
expect(ceWorkEngines).toContain("/goal Implement <plan-path>")
|
|
expect(ceWorkEngines).toContain("ultracode:")
|
|
expect(ceWorkEngines).toMatch(/Resume the correct tail/i)
|
|
expect(ceWorkEngines).toContain("standalone_shipping_skipped: true")
|
|
// No-PR is now structural (return-to-caller only); standalone defers to repo/user conventions.
|
|
expect(ceWorkEngines).toMatch(/must not open any PR/i)
|
|
})
|
|
})
|
|
|
|
describe("session-settled decision contract", () => {
|
|
test("plan-sections defines the session-settled annotation stem, closed two-class enum, and strip sanction", () => {
|
|
const annotation = sliceSection(
|
|
planSections,
|
|
"**Session-settled annotations on KTDs.**",
|
|
"**Group Requirements by concern",
|
|
)
|
|
expect(annotation).toContain("session-settled:")
|
|
expect(annotation).toContain("Exactly two classes:")
|
|
expect(annotation).toContain("`user-directed`")
|
|
expect(annotation).toContain("`user-approved`")
|
|
expect(annotation).toContain("review passes must not strip it")
|
|
// Enum closure: the contract admits no third provenance class.
|
|
expect(annotation).not.toContain("evidence-settled")
|
|
expect(annotation).not.toContain("agent-settled")
|
|
})
|
|
|
|
test("ce-plan loads settled-decisions.md, keeps the stem live in Phase 2, and emits the pipeline blocked token", () => {
|
|
expect(planCorpus).toContain("Read `references/settled-decisions.md`")
|
|
const phase2 = sliceSection(
|
|
planStructure,
|
|
"### Phase 2: Resolve Planning Questions",
|
|
"### Phase 3:",
|
|
)
|
|
expect(phase2).toContain("session-settled:")
|
|
expect(planCorpus).toContain("settled-decision-invalidated")
|
|
})
|
|
|
|
test("ce-brainstorm loads settled-decisions.md and annotates Key Decisions with the stem", () => {
|
|
expect(brainstormSkill).toContain("Read `references/settled-decisions.md`")
|
|
// The Phase 3 rendering rule moved into references/plan-write.md with the rest
|
|
// of Phase 3; the body still carries the load instruction above.
|
|
expect(brainstormPlanWrite).toContain(
|
|
"Key Decisions section carrying their `session-settled:` annotation",
|
|
)
|
|
})
|
|
|
|
test("lfg brief carries the four required fields, recognizes the blocked token, and retries the brief verbatim", () => {
|
|
// The brief's shape is owned by the reference step 1 requires before invoking
|
|
// ce-plan; the blocked-token stop and the verbatim retry stay in the body.
|
|
const bodyStep1 = sliceSection(
|
|
lfg,
|
|
"1. **Read `references/plan-brief.md` first**",
|
|
"2. **Read `references/work-return.md` first**",
|
|
)
|
|
const step1 = lfgPlanBrief
|
|
for (const field of [
|
|
"the decision",
|
|
"provenance class",
|
|
"rejected alternative",
|
|
"one-line reason",
|
|
]) {
|
|
expect(step1).toContain(field)
|
|
}
|
|
expect(step1).toContain("`user-directed`")
|
|
expect(bodyStep1).toContain("settled-decision-invalidated")
|
|
expect(bodyStep1).toContain("reusing the composed brief verbatim")
|
|
})
|
|
|
|
test("lfg threads settled_conflict findings through both step 4 and step 6", () => {
|
|
const step4 = sliceSection(
|
|
lfg,
|
|
"4. Invoke the `ce-code-review` skill",
|
|
"5. **Apply and persist review fixes**",
|
|
)
|
|
expect(step4).toContain("`settled_conflict`")
|
|
expect(lfgReviewFollowup).toContain("`settled_conflict`")
|
|
// Step 6's second trigger travels with the step-6 procedure in the reference
|
|
// lfg reads at step 3; the body keeps the step and its no-prompt rule.
|
|
const step6 = sliceSection(
|
|
lfg,
|
|
"6. **Autonomous residual handoff**",
|
|
"7. Invoke the `ce-test-browser` skill",
|
|
)
|
|
// The step-6 trigger set is back in the body: the skip must not fire on
|
|
// "Actionable findings: none." while a divergent entry is still undurable.
|
|
expect(step6).toContain("`settled_conflict`")
|
|
expect(step6).toContain("`settled_decision_conflicts`")
|
|
expect(step6).toMatch(/Skip only when none of the three exists/)
|
|
expect(lfgReviewFollowup).toMatch(/Two further triggers[\s\S]{0,300}`settled_conflict`/)
|
|
})
|
|
|
|
test("ce-work envelope reports settled conflicts; shipping tail treats invalidation as a blocker", () => {
|
|
expect(ceWorkReturn).toContain("`settled_decision_conflicts`")
|
|
expect(ceWorkShipping).toContain("never auto-accepted as a residual")
|
|
})
|
|
|
|
test("ce-code-review routes settlement conflicts advisory+human, never demotes defects, and keeps stamps report-only in 5c", () => {
|
|
const stage5 = sliceSection(
|
|
codeReviewFinish,
|
|
"### Stage 5: Merge findings",
|
|
"### Stage 5b",
|
|
)
|
|
expect(stage5).toContain("stamp `settled_conflict`")
|
|
expect(stage5).toContain("route it advisory/human")
|
|
// Negative boundary: defects inside a settled approach are not demoted.
|
|
expect(stage5).toContain("Do not demote a real defect")
|
|
const stage5c = sliceSection(
|
|
codeReviewFinish,
|
|
"### Stage 5c: Act on findings",
|
|
"### Stage 6",
|
|
)
|
|
expect(stage5c).toContain("`settled_conflict`-stamped")
|
|
expect(stage5c).toContain("stay report-only")
|
|
})
|
|
|
|
test("PR description Step C carries the session-settled provenance element", () => {
|
|
const stepC = sliceSection(
|
|
prDescriptionWriting,
|
|
"## Step C: Assemble the body",
|
|
"## Step D",
|
|
)
|
|
expect(stepC).toContain("**Session-settled provenance:**")
|
|
expect(stepC).toContain("session-settled:")
|
|
})
|
|
|
|
test("ce-doc-review threads settled KTDs through the {settled_ktds} slot and protects the annotation", () => {
|
|
expect(docReviewDispatch).toContain("| `{settled_ktds}` |")
|
|
expect(docReviewTemplate).toContain("Settled decisions: {settled_ktds}")
|
|
expect(docReviewSynthesis).toContain(
|
|
"must never remove or reword a `session-settled:` annotation",
|
|
)
|
|
})
|
|
|
|
test("ce-simplify-code honors session-settled structure pins", () => {
|
|
expect(simplifyCode).toContain("structure-pin constraint")
|
|
expect(simplifyCode).toContain("session-settled:")
|
|
})
|
|
|
|
test("rendering, synthesis, and deepening references carry the settled-decision contract", () => {
|
|
expect(brainstormSections).toContain("session-settled:")
|
|
expect(planMarkdownRendering).toContain("session-settled:")
|
|
expect(planHtmlRendering).toContain("session-settled:")
|
|
expect(planSynthesisSummary).toContain("Carrying forward:")
|
|
expect(brainstormSynthesisSummary).toContain("Carrying forward:")
|
|
expect(planDeepeningWorkflow).toContain(
|
|
"never removes the annotation or inverts the decision",
|
|
)
|
|
})
|
|
})
|
|
|
|
describe("cross-layer ownership contract", () => {
|
|
test("both section contracts carry the one-owner rule and the duplication named-test clause", () => {
|
|
for (const doc of [planSections, brainstormSections]) {
|
|
expect(doc).toMatch(/One owner per rule; cite, don't restate\./)
|
|
expect(doc).toMatch(/Unlinked sibling\s+restatement/)
|
|
expect(doc).toMatch(/Bind external authorities; don't summarize them\./)
|
|
expect(doc).toMatch(/a rule stated in full\s+in more than one section/)
|
|
}
|
|
})
|
|
|
|
test("plan-sections registers KTD-IDs with the R/U grammar and typed authority order", () => {
|
|
expect(planSections).toContain("KTD-IDs (Key Technical Decisions")
|
|
expect(planSections).toContain("`KTD1.`")
|
|
expect(planSections).toContain("KTD-IDs on implementation-ready plans")
|
|
expect(planSections).toContain("no mass renumbering")
|
|
// Typed authority: R wins product behavior; KTD wins mechanism; units override neither.
|
|
expect(planSections).toMatch(/R wins on product\s+behavior/)
|
|
expect(planSections).toMatch(/KTD wins on implementation mechanism/)
|
|
expect(planSections).toMatch(/a unit overrides neither/)
|
|
// No mirror KTDs.
|
|
expect(planSections).toMatch(/Do \*\*not\*\* create a KTD that merely mirrors/)
|
|
expect(planMarkdownRendering).toContain("`KTD<N>.` plain prefix")
|
|
})
|
|
|
|
test("brainstorm Key Decisions are a provenance index with Governs links, not a second statement", () => {
|
|
expect(brainstormSections).toMatch(/provenance index entry/)
|
|
expect(brainstormSections).toContain("`Governs R5, R7`")
|
|
expect(brainstormSections).toMatch(
|
|
/must not create a KTD that merely mirrors\s+the\s+product decision/,
|
|
)
|
|
expect(brainstormSections).not.toContain(
|
|
"inherits these labels into plan KTDs",
|
|
)
|
|
})
|
|
|
|
test("synthesis routes settled product decisions and planning decisions to their distinct owners", () => {
|
|
expect(planSynthesisSummary).toMatch(
|
|
/settled product decisions.*labeled Product Contract Key Decisions.*exact `Governs R…` links/s,
|
|
)
|
|
expect(planSynthesisSummary).toMatch(
|
|
/settled planning(?:\/how)? decisions.*labeled Key Technical Decisions/s,
|
|
)
|
|
})
|
|
|
|
test("ce-plan preservation protects meaning + IDs and sanctions restructuring with its own note class", () => {
|
|
expect(planCorpus).toContain("Meaning-preserving restructuring is sanctioned")
|
|
expect(planCorpus).toContain("restructured, no scope change")
|
|
expect(planCorpus).toContain(
|
|
"Preserve Product Contract meaning and stable IDs under Phase 0.3 step 3",
|
|
)
|
|
expect(planCorpus).not.toContain("Preserve Product Contract IDs and content")
|
|
expect(planCorpus).toContain(
|
|
"re-point every affected `Governs R…`, `Covers R…`, and inline `per R…` citation",
|
|
)
|
|
expect(planCorpus).toContain(
|
|
"no pre-restructure catch-all link silently excludes a split-out requirement",
|
|
)
|
|
expect(planCorpus).toContain("do **not** mirror it into a KTD")
|
|
// Unit Approach owns only unit-local content.
|
|
expect(planCorpus).toContain("Unit-local content only")
|
|
// Settlement channel: KTD<N> for planning decisions, governed Rs for product decisions.
|
|
expect(planCorpus).toContain("reverse-resolved through its `Governs R…` links")
|
|
})
|
|
|
|
test("ce-work packets reverse-resolve Product Key Decisions so settlement labels survive bounded reads", () => {
|
|
expect(ceWorkStrategy).toContain("`Governs R…` links name the unit's cited R-IDs")
|
|
expect(ceWorkLoop).toContain("A KTD or Product Contract Key Decision carrying")
|
|
})
|
|
|
|
test("every executor handoff reverse-resolves labeled Product Contract Key Decisions", () => {
|
|
// The invariant is #1234's: an objective or unit packet handed to an executor must
|
|
// reverse-resolve the labeled Key Decisions rather than copy requirements across
|
|
// layers. It belongs to whoever composes that text. ce-plan's SKILL.md menu bullet
|
|
// no longer composes an objective — it defers to plan-handoff.md, which the body
|
|
// STOP-loads before the menu renders — so the pin follows the composition, and the
|
|
// body is pinned to defer (see the /goal menu test above).
|
|
const handoffs = [
|
|
sliceSection(
|
|
planHandoff,
|
|
"- **Run it as a `/goal`**",
|
|
"- **Decide on the review's open items**",
|
|
),
|
|
sliceSection(
|
|
ceWorkEngines,
|
|
"Copyable goal-mode prompt",
|
|
"Copyable dynamic-workflow prompt",
|
|
),
|
|
sliceSection(
|
|
crossModelExecution,
|
|
"3. **Prepare one bounded unit packet.**",
|
|
"4. **Start one fixed author.**",
|
|
),
|
|
]
|
|
|
|
for (const handoff of handoffs) {
|
|
expect(handoff).toMatch(
|
|
/Product Contract Key Decision.*exact `Governs R…` links.*cited R-IDs/s,
|
|
)
|
|
}
|
|
})
|
|
|
|
test("deepening strengthens at the owning entry and never restates owned rules into siblings", () => {
|
|
expect(planDeepeningWorkflow).toContain("Strengthen at the owning entry.")
|
|
expect(planDeepeningWorkflow).toContain(
|
|
"Restate a rule a cited R or KTD already owns into a sibling section",
|
|
)
|
|
})
|
|
})
|
|
|
|
describe("Product Contract section catalog and routing destinations", () => {
|
|
// ce-plan's include-when-material catalog specified only implementation-facing
|
|
// sections; its product-shape sections had no firing/skip rule at all. Measured
|
|
// 2026-08-12 across 34 pre-existing unified plans: Success Criteria appeared in
|
|
// 8, Key Decisions in 19. These pins keep the rule present in each of the five
|
|
// routing statements plus the catalog, since a fix in one leaves the others as
|
|
// stale sources of truth.
|
|
|
|
function entryBlock(doc: string, name: string): string {
|
|
const marker = `- **${name}** —`
|
|
const start = doc.indexOf(marker)
|
|
expect(start, `plan-sections.md must carry a '${name}' catalog entry.`).toBeGreaterThan(-1)
|
|
const end = doc.indexOf("\n\n- **", start)
|
|
return doc.slice(start, end > start ? end : doc.length)
|
|
}
|
|
|
|
// Typed once: both the routing-statement sweep and the session-settled test
|
|
// slice this same region, and drifting markers would fail one for the wrong
|
|
// reason.
|
|
const planInteractiveTable = sliceSection(
|
|
planSynthesisSummary,
|
|
"| Internal-draft element | Where it goes in the unified plan |",
|
|
"No italic capture-context note",
|
|
)
|
|
|
|
const PRODUCT_SECTIONS = [
|
|
"Problem Frame",
|
|
"Key Decisions",
|
|
"Success Criteria",
|
|
"Actors",
|
|
"Key Flows",
|
|
]
|
|
|
|
// Problem Frame is unconditional in the hard floor ("Contains Summary,
|
|
// Problem Frame, Requirements"), so it must NOT carry a skip test — an
|
|
// earlier revision shipped one and let an implementation-ready plan omit a
|
|
// mandatory section.
|
|
const SKIPPABLE_SECTIONS = PRODUCT_SECTIONS.filter((s) => s !== "Problem Frame")
|
|
|
|
test("plan-sections.md catalogs every Product Contract section with a skip test", () => {
|
|
const catalog = sliceSection(
|
|
planSections,
|
|
"## Include when material",
|
|
"## Agent agency",
|
|
)
|
|
for (const name of PRODUCT_SECTIONS) {
|
|
entryBlock(catalog, name)
|
|
}
|
|
for (const name of SKIPPABLE_SECTIONS) {
|
|
const block = entryBlock(catalog, name)
|
|
expect(
|
|
/\bskip\b/i.test(block),
|
|
`The '${name}' catalog entry needs a skip test. A firing rule with no skip rule fires on everything, which the include-when-material doctrine treats as broken.`,
|
|
).toBe(true)
|
|
}
|
|
// Enriching a legacy requirements doc inherits decisions made during
|
|
// brainstorming, and Phase 0.3 requires carrying them forward — a
|
|
// planning-only firing rule would silently drop them.
|
|
expect(
|
|
/inherited from an upstream Product Contract/.test(entryBlock(catalog, "Key Decisions")),
|
|
"The Key Decisions firing rule must cover decisions inherited from an upstream Product Contract, not only choices made during planning.",
|
|
).toBe(true)
|
|
|
|
expect(
|
|
/\bskip\b/i.test(entryBlock(catalog, "Problem Frame")),
|
|
"The Problem Frame entry must NOT carry a skip test — the hard floor contains it unconditionally, so a skip rule would let an implementation-ready plan omit a mandatory section.",
|
|
).toBe(false)
|
|
})
|
|
|
|
test("the hard-floor enumeration still names its Product Contract subsections", () => {
|
|
// U1 deliberately left :171-175 untouched. Guard against a later edit that
|
|
// "deduplicates" the floor against the new catalog entries.
|
|
// The floor is one wrapped sentence, so collapse whitespace before matching
|
|
// ("Summary, Problem\n Frame, Requirements ...").
|
|
const floor = sliceSection(
|
|
planSections,
|
|
"- **Product Contract** — product scope and behavior.",
|
|
"- **Planning Contract**",
|
|
).replace(/\s+/g, " ")
|
|
for (const name of ["Summary", "Problem Frame", "Success Criteria", "Scope Boundaries"]) {
|
|
expect(
|
|
floor.includes(name),
|
|
`The Product Contract hard floor must still name ${name}; the catalog entry supplements the floor rather than replacing it.`,
|
|
).toBe(true)
|
|
}
|
|
})
|
|
|
|
test("all five routing statements name a Success Criteria destination", () => {
|
|
const statements: Array<[string, string]> = [
|
|
[
|
|
"ce-plan prose restatement",
|
|
sliceSection(planSynthesisSummary, "**Three-bucket structure is the internal draft", "## Stage 1"),
|
|
],
|
|
[
|
|
"ce-plan headless list",
|
|
sliceSection(planSynthesisSummary, "Route internal-draft content with mode-aware shape", "The `### Assumptions` section appears"),
|
|
],
|
|
["ce-plan interactive table", planInteractiveTable],
|
|
[
|
|
"ce-brainstorm prose restatement",
|
|
sliceSection(brainstormSynthesisSummary, "**Three-bucket structure is the internal draft", "This content is loaded"),
|
|
],
|
|
[
|
|
"ce-brainstorm routing table",
|
|
sliceSection(brainstormSynthesisSummary, "| Internal-draft element | Where it goes in the doc |", "The chat-time Trade-offs section"),
|
|
],
|
|
]
|
|
|
|
for (const [label, statement] of statements) {
|
|
expect(
|
|
statement.includes("Success Criteria"),
|
|
`The ${label} must name a Success Criteria destination. A section the synthesis drafts with no destination in one statement drifts back out when a later edit reconciles the statements against each other.`,
|
|
).toBe(true)
|
|
}
|
|
})
|
|
|
|
test("ce-plan's interactive table routes session-settled product decisions to Key Decisions", () => {
|
|
// The headless list already carried this clause; the interactive table --
|
|
// the most-used path -- had four rows and no session-settled row.
|
|
expect(
|
|
/Session-settled product decisions.*### Key Decisions/s.test(planInteractiveTable),
|
|
"The interactive routing table must route session-settled product decisions to Product Contract `### Key Decisions`.",
|
|
).toBe(true)
|
|
})
|
|
|
|
test("no route lets an implementation-ready plan drop Problem Frame", () => {
|
|
// Three separate review findings on PR #1359 came from the same shape: a
|
|
// rule elsewhere in the file quietly permitting the omission of a
|
|
// hard-floor section. Pin the remaining escape.
|
|
const agency = sliceSection(planSections, "## Agent agency", "## Prose economy")
|
|
expect(
|
|
/Problem Frame merges into Summary/.test(agency),
|
|
"The agency list is expected to still mention the Problem Frame merge; if it was deleted outright, drop this guard rather than letting it pass vacuously.",
|
|
).toBe(true)
|
|
// Require the blocking sentence itself, not an alternative: an OR here let
|
|
// the clause that actually prevents the omission be deleted while the pin
|
|
// still passed.
|
|
expect(
|
|
/`ce-unified-plan\/v1` artifact keeps both\s+headings/s.test(agency),
|
|
"The Problem Frame merge escape must be scoped away from every ce-unified-plan/v1 artifact, or it removes a hard-floor heading downstream consumers anchor on.",
|
|
).toBe(true)
|
|
})
|
|
|
|
test("the headless Success Criteria destination takes Stated signals only", () => {
|
|
// The unconfirmed paths (headless, SKIP_SCOPING_CONFIRM) never validate an
|
|
// Inferred bet, so routing an inferred success signal into an unlabeled
|
|
// Product Contract section contradicts the `### Assumptions` firewall.
|
|
// Two independent reviewers caught this collision on PR #1359.
|
|
const headless = sliceSection(
|
|
planSynthesisSummary,
|
|
"Route internal-draft content with mode-aware shape",
|
|
"The `### Assumptions` section appears",
|
|
)
|
|
expect(
|
|
/Success signals — Stated only/.test(headless),
|
|
"The headless routing list must restrict the Success Criteria destination to Stated signals; an inferred one belongs in `### Assumptions`.",
|
|
).toBe(true)
|
|
expect(
|
|
/Success signals.*\(Stated or Inferred\)/.test(headless),
|
|
"The headless Success Criteria destination must not offer Inferred content — that reopens the firewall contradiction.",
|
|
).toBe(false)
|
|
|
|
// The interactive table also governs SKIP_SCOPING_CONFIRM runs (its own
|
|
// Inferred row says so), so an unqualified "(Stated or Inferred)" there
|
|
// reopens the same contradiction one section down.
|
|
expect(
|
|
/Success signals \(Stated or Inferred\)/.test(planInteractiveTable),
|
|
"The interactive routing table must qualify its Success Criteria row: it also governs SKIP_SCOPING_CONFIRM runs, where an inferred signal belongs in `### Assumptions`.",
|
|
).toBe(false)
|
|
expect(
|
|
/confirmed interactive run/.test(planInteractiveTable),
|
|
"The interactive table's Success Criteria row must name the confirmed-interactive condition for Inferred signals.",
|
|
).toBe(true)
|
|
})
|
|
|
|
test("the silent-dissolve rule names which Inferred items are exempt", () => {
|
|
expect(planSynthesisSummary).toMatch(
|
|
/exempt from that silent dissolve.*success criteria extrapolated from intent.*scope boundaries the user never explicitly named/s,
|
|
)
|
|
})
|
|
|
|
test("ce-plan's bootstrap carries an exit condition with both escapes", () => {
|
|
const bootstrap = sliceSection(
|
|
planIntake,
|
|
"The planning bootstrap should establish:",
|
|
"#### 0.5",
|
|
)
|
|
expect(
|
|
bootstrap.includes("**Exit condition:**"),
|
|
"The Phase 0.4 bootstrap must carry an exit condition; without one it lists what to establish and can proceed having established none of it.",
|
|
).toBe(true)
|
|
expect(
|
|
bootstrap.includes("recorded as assumptions"),
|
|
"The exit condition must be satisfiable by recording an assumption, or it becomes a blocking question in headless mode.",
|
|
).toBe(true)
|
|
expect(
|
|
bootstrap.includes("explicitly wants to proceed"),
|
|
"The exit condition must carry the explicit-user-proceed escape, matching ce-brainstorm's Phase 1.3 gate.",
|
|
).toBe(true)
|
|
})
|
|
|
|
test("Success Metrics and Success Criteria are distinguished, not collided", () => {
|
|
// Both names exist in ce-plan, so the boundary between them must be stated
|
|
// once and be exclusive — an overlapping definition let the same p95 target
|
|
// belong to either section.
|
|
expect(
|
|
/Success Metrics.*`### Success Criteria`/s.test(planStructure),
|
|
"ce-plan names both `Success Metrics` (deep-plan extension) and `Success Criteria` (Product Contract subsection); the relationship must be stated once so a future author does not merge them.",
|
|
).toBe(true)
|
|
expect(
|
|
/never appears here as well|only what Success Criteria does not already state/.test(planStructure),
|
|
"The Success Metrics definition must claim an exclusive boundary, or a product-outcome threshold lands in both sections.",
|
|
).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe("Goal Capsule objective is outcome-shaped (issue #1423)", () => {
|
|
const coherence = readRepoFile(
|
|
"skills/ce-doc-review/references/personas/coherence-reviewer.md",
|
|
)
|
|
|
|
test("both section contracts define Objective as an outcome and give the mechanism a Means slot", () => {
|
|
for (const [doc, marker] of [
|
|
[planSections, "**Goal Capsule**"],
|
|
[brainstormSections, "`## Goal Capsule`"],
|
|
] as const) {
|
|
const capsule = sliceSection(doc, marker, "\n- ")
|
|
expect(capsule).toMatch(/\bMeans\b/)
|
|
expect(capsule).toMatch(/different\s+(implementation|mechanism)/i)
|
|
}
|
|
})
|
|
|
|
// 2026-08-24: an Objective can be outcome-shaped and still sit at the
|
|
// altitude of the component being changed ("X no longer occupies Y's
|
|
// wall-clock"), which passes the different-implementation test alone. Both
|
|
// contracts must also anchor the outcome to who it is felt by.
|
|
test("both section contracts state the objective's altitude, not only its outcome shape", () => {
|
|
for (const [doc, marker] of [
|
|
[planSections, "**Goal Capsule**"],
|
|
[brainstormSections, "`## Goal Capsule`"],
|
|
] as const) {
|
|
const capsule = sliceSection(doc, marker, "\n- ")
|
|
expect(capsule).toMatch(/users\s+or\s+operators/i)
|
|
expect(capsule).toMatch(/outside\s+the\s+component\s+being\s+changed/i)
|
|
expect(capsule).toMatch(/internals/i)
|
|
}
|
|
})
|
|
|
|
test("Success Criteria skip does not fire on approach-shaped requirements", () => {
|
|
const sc = sliceSection(planSections, "- **Success Criteria**", "\n- **")
|
|
expect(sc).toMatch(/approach rather than an outcome/)
|
|
})
|
|
|
|
test("bootstrap exit requires an outcome-shaped problem frame", () => {
|
|
const exit = sliceSection(planIntake, "**Exit condition:** Exit the bootstrap", "\n\n")
|
|
expect(exit).toMatch(/Means/)
|
|
})
|
|
|
|
// 2026-08-24: the pin was the phrase "mechanism-only Objective", which named
|
|
// only the no-outcome shape. The reviewer now covers the outcome-shaped
|
|
// Objective stated at the changed component's altitude too, so the pin
|
|
// asserts the condition and its leniency guard rather than the old label.
|
|
test("coherence reviewer flags an Objective that cannot outlive its mechanism", () => {
|
|
expect(coherence).toMatch(/Goal Capsule Objective/)
|
|
expect(coherence).toMatch(/component being changed/i)
|
|
expect(coherence).toMatch(/outside the changed component/i)
|
|
expect(coherence).toMatch(/Means line/)
|
|
// Without the mixed-clause rule, one acceptable clause exonerates the
|
|
// whole Objective and neither Claude nor Codex flagged the three-clause
|
|
// capsule this reviewer exists to catch.
|
|
expect(coherence).toMatch(/other clauses of the same Objective/i)
|
|
})
|
|
|
|
// 2026-08-31: an Objective can pass the who-can-check / different-implementation
|
|
// tests and still not be holdable as a goal without later sections. Both
|
|
// section contracts and the coherence reviewer must name that altitude
|
|
// separately from the component-internals one, and must not bury it after
|
|
// the "however brief" exemption.
|
|
test("both section contracts require an Objective a reader can hold without the rest of the plan", () => {
|
|
for (const [doc, marker] of [
|
|
[planSections, "**Goal Capsule**"],
|
|
[brainstormSections, "`## Goal Capsule`"],
|
|
] as const) {
|
|
const capsule = sliceSection(doc, marker, "\n- ")
|
|
expect(capsule).toMatch(/rest of the plan/i)
|
|
expect(capsule).toMatch(/owning R-IDs/i)
|
|
}
|
|
})
|
|
|
|
test("coherence reviewer flags an Objective a reader cannot hold as the goal", () => {
|
|
expect(coherence).toMatch(/cannot hold as the goal/i)
|
|
expect(coherence).toMatch(/however brief.*does not apply here/i)
|
|
})
|
|
})
|
|
|
|
// 2026-08-22: Lightweight brainstorms end in chat unless a file is earned, and
|
|
// ce-plan's no-plan rule moved from a Phase 5 reference to the intake gate.
|
|
describe("right-sized brainstorm and plan outputs", () => {
|
|
test("ce-brainstorm decides Lightweight before its first reference read and ends Lightweight in chat", () => {
|
|
expect(brainstormSkill).toContain("**Lightweight work ends in chat.**")
|
|
expect(brainstormSkill).toMatch(/ends in a chat paragraph with no file/)
|
|
expect(brainstormPhase0).toContain("**Lightweight ends in chat.**")
|
|
expect(brainstormSkill).toMatch(/When a file is written on the brainstorm path the artifact contract does not change/)
|
|
})
|
|
|
|
test("brainstorm-sections states the file-earning condition, not a doc-by-default trigger", () => {
|
|
expect(brainstormSections).toMatch(/A brainstorm ends in chat unless a file is earned/)
|
|
expect(brainstormSections).not.toContain("The trigger for creating a doc is")
|
|
})
|
|
|
|
test("plan-sections no longer biases toward writing a plan; the intake gate decides", () => {
|
|
expect(planSections).not.toContain("Bias toward producing a plan")
|
|
expect(planSections).toMatch(/Output Contract gate decides this at intake/)
|
|
})
|
|
})
|