mirror of
https://github.com/EveryInc/compound-engineering-plugin.git
synced 2026-10-04 21:57:39 +02:00
341 lines
16 KiB
TypeScript
341 lines
16 KiB
TypeScript
import { readFileSync } from "fs"
|
|
import path from "path"
|
|
import { describe, expect, test } from "bun:test"
|
|
import { load as parseYaml } from "js-yaml"
|
|
|
|
const SKILL_PATH = path.join(
|
|
process.cwd(),
|
|
"skills/ce-plan/SKILL.md",
|
|
)
|
|
const SKILL_BODY = readFileSync(SKILL_PATH, "utf8")
|
|
|
|
const OUTPUT_MODE_BODY = readFileSync(
|
|
path.join(process.cwd(), "skills/ce-plan/references/output-mode.md"),
|
|
"utf8",
|
|
)
|
|
|
|
const FINAL_REVIEW_BODY = readFileSync(
|
|
path.join(process.cwd(), "skills/ce-plan/references/final-review.md"),
|
|
"utf8",
|
|
)
|
|
|
|
const RESUME_BODY = readFileSync(
|
|
path.join(process.cwd(), "skills/ce-plan/references/resume.md"),
|
|
"utf8",
|
|
)
|
|
|
|
const HANDOFF_BODY = readFileSync(
|
|
path.join(process.cwd(), "skills/ce-plan/references/plan-handoff.md"),
|
|
"utf8",
|
|
)
|
|
|
|
const HTML_RENDERING_PATH = path.join(
|
|
process.cwd(),
|
|
"skills/ce-plan/references/html-rendering.md",
|
|
)
|
|
|
|
const PLAN_SECTIONS_PATH = path.join(
|
|
process.cwd(),
|
|
"skills/ce-plan/references/plan-sections.md",
|
|
)
|
|
|
|
// Regression guard for the `output:html` / `output:md` argument on ce-plan.
|
|
// Under exclusive output mode, the plan is written as EITHER markdown OR
|
|
// HTML — never both. The skill body must carry the load-bearing surface:
|
|
// the argument-hint advertises the flag, the kernel requires the output owner
|
|
// before phase interpretation, and that owner preserves the precedence and
|
|
// pipeline override that automated downstream consumers rely on.
|
|
describe("ce-plan output:html mode", () => {
|
|
test("argument-hint advertises output:html", () => {
|
|
// argument-hint is in the frontmatter. Extract and parse to confirm
|
|
// the token is visible to humans discovering the flag, not just buried
|
|
// in skill prose.
|
|
const frontmatterMatch = SKILL_BODY.match(/^---\n([\s\S]*?)\n---/)
|
|
expect(frontmatterMatch).not.toBeNull()
|
|
const frontmatter = parseYaml(frontmatterMatch![1]) as Record<string, unknown>
|
|
const hint = frontmatter["argument-hint"]
|
|
expect(
|
|
typeof hint === "string" && hint.includes("output:html"),
|
|
`ce-plan argument-hint must mention 'output:html' so humans discover the flag. Current value: ${JSON.stringify(hint)}`,
|
|
).toBe(true)
|
|
})
|
|
|
|
test("SKILL.md requires the output owner before phase interpretation", () => {
|
|
expect(
|
|
/Read `references\/output-mode\.md` before interpreting any phase/i.test(SKILL_BODY),
|
|
"SKILL.md must require output-mode.md before any phase can interpret its inputs.",
|
|
).toBe(true)
|
|
|
|
const phaseStart = OUTPUT_MODE_BODY.indexOf("## 0.0")
|
|
expect(
|
|
phaseStart,
|
|
"output-mode.md no longer contains the Phase 0.0 owner anchor.",
|
|
).toBeGreaterThan(-1)
|
|
const phaseRegion = OUTPUT_MODE_BODY.slice(phaseStart)
|
|
|
|
expect(
|
|
/output:/.test(phaseRegion),
|
|
"Phase 0.0 must name the `output:` argument prefix.",
|
|
).toBe(true)
|
|
expect(
|
|
/plan_output/.test(phaseRegion),
|
|
"Phase 0.0 must name the `plan_output` config key.",
|
|
).toBe(true)
|
|
expect(
|
|
/pipeline|disable-model-invocation/i.test(phaseRegion),
|
|
"Phase 0.0 must describe the pipeline-mode override that forces markdown.",
|
|
).toBe(true)
|
|
expect(
|
|
/literal[\s-]prefix|literal prefix/i.test(phaseRegion),
|
|
"Phase 0.0 must state the literal-prefix token-parsing convention so `feat:`/`fix:`/`chore:` in feature descriptions pass through verbatim.",
|
|
).toBe(true)
|
|
// A user-stated/remembered format preference must override the config file
|
|
// (the config is the persisted fallback, not the top signal), and the skill
|
|
// must NOT be told to open instruction files to find it.
|
|
expect(
|
|
/user-stated preference/i.test(phaseRegion),
|
|
"Phase 0.0 must include a user-stated-preference tier above config.",
|
|
).toBe(true)
|
|
expect(
|
|
/overrides\*?\*? the config|more current than the rarely-edited config/i.test(phaseRegion),
|
|
"The user-stated preference must be stated to override the config file.",
|
|
).toBe(true)
|
|
expect(
|
|
/do not open or search instruction files|already (present )?in your context/i.test(phaseRegion),
|
|
"The stated-preference tier must act on context only, not instruct reading instruction files.",
|
|
).toBe(true)
|
|
// The in-prompt format trigger must be harness-neutral — reason over the
|
|
// user's prompt, NOT a Claude-only $ARGUMENTS token (Cursor uses $1/$2; Kiro
|
|
// drops $ARGUMENTS) — and a format named as subject matter must not be
|
|
// mistaken for a doc-format request.
|
|
expect(
|
|
/reason over the user's prompt/i.test(phaseRegion),
|
|
"Phase 0.0 step 1 must reason over the user's prompt (harness-neutral), not a $ARGUMENTS token.",
|
|
).toBe(true)
|
|
expect(
|
|
/subject matter|not a doc-format request/i.test(phaseRegion),
|
|
"Phase 0.0 step 1 must guard against treating a format named as subject matter as a doc-format request.",
|
|
).toBe(true)
|
|
const stepOne = phaseRegion.slice(
|
|
phaseRegion.indexOf("In-prompt request"),
|
|
phaseRegion.indexOf("User-stated preference"),
|
|
)
|
|
expect(
|
|
stepOne.includes("$ARGUMENTS"),
|
|
"The output-format trigger must not depend on the Claude-only $ARGUMENTS token.",
|
|
).toBe(false)
|
|
})
|
|
|
|
test("token-parsing convention names both mode: and output: as flag prefixes", () => {
|
|
// The convention is shared across `mode:`, `output:`, and any future
|
|
// flag-token. Both names must appear together in the parsing prose so a
|
|
// future implementer doesn't generalize to "any <word>:<word> token" and
|
|
// accidentally consume conventional commit prefixes.
|
|
const phaseStart = OUTPUT_MODE_BODY.indexOf("## 0.0")
|
|
const phaseRegion = OUTPUT_MODE_BODY.slice(phaseStart)
|
|
expect(
|
|
/mode:/.test(phaseRegion) && /output:/.test(phaseRegion),
|
|
"Phase 0.0 token-parsing convention must name both `mode:` and `output:` as literal-prefix flags so the rule generalizes correctly.",
|
|
).toBe(true)
|
|
})
|
|
|
|
test("no-artifact routes do not acquire a repository-backed format dependency", () => {
|
|
const phaseStart = OUTPUT_MODE_BODY.indexOf("## 0.0")
|
|
const phaseRegion = OUTPUT_MODE_BODY.slice(phaseStart)
|
|
|
|
expect(phaseRegion).toContain("keep config/default resolution pending until an artifact-producing route is known")
|
|
expect(phaseRegion).toContain("A terminal no-artifact route never probes config")
|
|
expect(phaseRegion).toContain("settle it before selecting a renderer or composing the artifact path")
|
|
})
|
|
|
|
test("software routing completes deferred output resolution before Phase 0.2", () => {
|
|
const domainRoute = RESUME_BODY.slice(RESUME_BODY.indexOf("#### 0.1b Classify Task Domain"))
|
|
|
|
expect(domainRoute).toContain("the artifact-producing route is now known")
|
|
expect(domainRoute).toContain("Re-read `references/output-mode.md`")
|
|
expect(domainRoute).toMatch(/settle any pending config\/default resolution before selecting the renderer or continuing to Phase 0\.2/i)
|
|
})
|
|
|
|
test("the kernel is the sole model-elevation dispatcher", () => {
|
|
expect(SKILL_BODY).toContain("Immediately before authoring, read `references/reasoning-elevation.md`")
|
|
expect(FINAL_REVIEW_BODY).toContain("Return to the kernel for its model-elevation boundary")
|
|
expect(FINAL_REVIEW_BODY).toContain("does not dispatch the authoring route itself")
|
|
expect(FINAL_REVIEW_BODY).not.toContain("load `references/reasoning-elevation.md`")
|
|
})
|
|
|
|
test("config matching rule ignores commented YAML lines (active-key principle)", () => {
|
|
// Codex review (2026-05-13, thread PRRT_kwDOP_gZVc6B6OgB) flagged that the
|
|
// prior phrasing — "contains `plan_output: md|html`" — would match the
|
|
// commented examples shipped in the config template (`# plan_output: html`),
|
|
// silently forcing every user into HTML mode. The fix is principle-level:
|
|
// require an ACTIVE (non-commented) key, and name the failure mode so a
|
|
// future maintainer doesn't loosen it back. We check the principle is
|
|
// present, not a specific phrasing.
|
|
const phaseStart = OUTPUT_MODE_BODY.indexOf("## 0.0")
|
|
const phaseRegion = OUTPUT_MODE_BODY.slice(phaseStart)
|
|
expect(
|
|
/active.*non-commented|non-commented.*key|lines starting with `#`.*comments|ignore commented/i.test(phaseRegion),
|
|
"Phase 0.0 config matching must require an ACTIVE (non-commented) `plan_output:` key, not a raw-text 'contains' match. Without this, the shipped config template's commented examples would silently force HTML mode.",
|
|
).toBe(true)
|
|
// The rationale-citation pin that used to sit here required the always-loaded
|
|
// body to spell out the shipped template's `# plan_output: html` example. The
|
|
// invariant it was protecting is the active-key rule asserted just above, which
|
|
// the shared ce-config-layers block a few lines below Phase 0.0 also states
|
|
// ("Win with the first active (non-commented) value"). Pin the condition, not
|
|
// the worked example: the body must say a commented template line is not a
|
|
// setting, in whatever words.
|
|
expect(
|
|
/(commented|template)[^.\n]{0,80}(are not settings|is not a setting|not an active setting)|(not settings|not an? (active )?setting)[^.\n]{0,80}(commented|template)/i.test(
|
|
phaseRegion,
|
|
),
|
|
"Phase 0.0 must say that a commented `plan_output:` line is not an active setting — otherwise the shipped template's examples silently force HTML mode.",
|
|
).toBe(true)
|
|
expect(
|
|
/ordinary-key|next layer|config\.local\.yaml then `config\.yaml`/i.test(phaseRegion),
|
|
"Phase 0.0 config step must cascade local then tracked before the skill default.",
|
|
).toBe(true)
|
|
})
|
|
|
|
test("unknown-value fallback note reflects final resolved mode, not a hardcoded md", () => {
|
|
// Codex review (2026-05-13, thread PRRT_kwDOP_gZVc6B-LIW) flagged that
|
|
// hardcoding "defaulting to md" in the unknown-value note is wrong when
|
|
// step 2 (config) or step 4 (pipeline override) resolves to a different
|
|
// value. The note must reflect the actual final value, not anticipate one.
|
|
const phaseStart = OUTPUT_MODE_BODY.indexOf("## 0.0")
|
|
const phaseRegion = OUTPUT_MODE_BODY.slice(phaseStart)
|
|
expect(
|
|
/using <resolved_format>|reflect.*final.*mode|after final resolution|after steps 2-4|Do not hardcode `md`/i.test(phaseRegion),
|
|
"Phase 0.0's unknown-value note must reflect the actual resolved OUTPUT_FORMAT after all precedence steps, not a hardcoded 'defaulting to md' that misleads users when config has set HTML.",
|
|
).toBe(true)
|
|
})
|
|
|
|
test("Phase 5.2 sends both output formats through ce-doc-review", () => {
|
|
const phase52Start = FINAL_REVIEW_BODY.indexOf("#### 5.2 Write Plan File")
|
|
expect(phase52Start).toBeGreaterThan(-1)
|
|
const phase52Region = FINAL_REVIEW_BODY.slice(phase52Start, phase52Start + 2000)
|
|
expect(
|
|
/both formats|markdown and HTML|native format/i.test(phase52Region),
|
|
"Phase 5.2 must state that markdown and HTML both continue through ce-doc-review in their native format.",
|
|
).toBe(true)
|
|
})
|
|
|
|
test("Phase 0.0 points at format-rendering refs based on resolved value", () => {
|
|
const phaseStart = OUTPUT_MODE_BODY.indexOf("## 0.0")
|
|
const phaseRegion = OUTPUT_MODE_BODY.slice(phaseStart)
|
|
expect(
|
|
/references\/markdown-rendering\.md|markdown-rendering\.md/i.test(phaseRegion),
|
|
"Phase 0.0 must point at markdown-rendering.md for md output mode.",
|
|
).toBe(true)
|
|
expect(
|
|
/references\/html-rendering\.md|html-rendering\.md/i.test(phaseRegion),
|
|
"Phase 0.0 must point at html-rendering.md for html output mode.",
|
|
).toBe(true)
|
|
})
|
|
|
|
test("post-generation menu offers prototype and browser for HTML, not Proof", () => {
|
|
const phaseStart = HANDOFF_BODY.indexOf("## 5.4 Post-Generation Options")
|
|
expect(phaseStart).toBeGreaterThan(-1)
|
|
const phaseRegion = HANDOFF_BODY.slice(phaseStart)
|
|
|
|
expect(
|
|
/Open in browser/.test(phaseRegion),
|
|
"plan-handoff.md Phase 5.4 menu must include 'Open in browser' for HTML mode.",
|
|
).toBe(true)
|
|
expect(
|
|
/Prototype a remaining feel-question/.test(phaseRegion),
|
|
"plan-handoff.md Phase 5.4 menu must include the prototype offer.",
|
|
).toBe(true)
|
|
expect(
|
|
/Publish to Proof/.test(phaseRegion),
|
|
"software plan Phase 5.4 must omit Share to Proof.",
|
|
).toBe(false)
|
|
expect(
|
|
/OUTPUT_FORMAT=html/i.test(phaseRegion),
|
|
"plan-handoff.md must state HTML-only browser rendering.",
|
|
).toBe(true)
|
|
})
|
|
|
|
test("no sibling logic — exclusive output mode is documented", () => {
|
|
// Defends against drift back to the old sibling model. The skill must
|
|
// state exclusivity ("md OR html, never both") so a future maintainer
|
|
// doesn't re-introduce sibling generation.
|
|
expect(
|
|
/exclusive|md OR html|markdown OR HTML|never both/i.test(OUTPUT_MODE_BODY),
|
|
"output-mode.md must state that output mode is exclusive — markdown OR HTML, never both.",
|
|
).toBe(true)
|
|
// OUTPUT_FORMAT_SOURCE was used by the sibling tracking; it should not
|
|
// re-appear.
|
|
expect(
|
|
/OUTPUT_FORMAT_SOURCE/.test(SKILL_BODY + OUTPUT_MODE_BODY + HANDOFF_BODY),
|
|
"The ce-plan kernel and output/handoff owners must not reference OUTPUT_FORMAT_SOURCE.",
|
|
).toBe(false)
|
|
})
|
|
|
|
test("plan-sections.md enumerates the required plan metadata fields by name", () => {
|
|
// PR #826 split the prescriptive plan-template.md into a section contract
|
|
// (plan-sections.md) + format-rendering refs. markdown-rendering.md now
|
|
// says "Per-skill frontmatter fields are defined in each skill's section
|
|
// contract" — so plan-sections.md MUST actually list them or downstream
|
|
// tooling that keys on these field names (deepening's
|
|
// `deepened: YYYY-MM-DD`, the `origin:` brainstorm traceback) breaks
|
|
// silently when agents compose plans from the new refs.
|
|
const body = readFileSync(PLAN_SECTIONS_PATH, "utf8")
|
|
|
|
// Required field names that downstream consumers depend on.
|
|
for (const field of ["title", "type", "date"]) {
|
|
expect(
|
|
new RegExp(`\\b${field}\\b`).test(body),
|
|
`plan-sections.md must name the required '${field}' metadata field — downstream tooling keys on it.`,
|
|
).toBe(true)
|
|
}
|
|
|
|
// Optional but well-known fields whose names are load-bearing for
|
|
// resume/traceback flows.
|
|
for (const field of ["origin", "deepened"]) {
|
|
expect(
|
|
new RegExp(`\\b${field}\\b`).test(body),
|
|
`plan-sections.md must name the optional '${field}' metadata field — its presence and exact name are load-bearing for downstream flows.`,
|
|
).toBe(true)
|
|
}
|
|
|
|
// Plans carry NO status field — the active → completed lifecycle was
|
|
// removed (ce-work no longer mutates the plan; completion is derived from
|
|
// git). The contract must say so explicitly so an agent reading it does
|
|
// not reintroduce a status field.
|
|
expect(
|
|
/no .{0,3}status.{0,3} field|carry .{0,6}no .{0,12}status/i.test(body),
|
|
"plan-sections.md must state plans carry NO status field.",
|
|
).toBe(true)
|
|
|
|
// The field-name rules below had no mechanical guard, and a real artifact
|
|
// shipped with `created:` instead of `date:` and a `feat:` prefix in the
|
|
// title. These are greppable contract text, so pin them here rather than
|
|
// scanning docs/plans/ (which legacy artifacts would fail).
|
|
expect(
|
|
body.includes("`date` to `created`"),
|
|
"plan-sections.md must name `date` -> `created` as a breaking rename — an artifact shipped with `created:` and downstream consumers key on `date`.",
|
|
).toBe(true)
|
|
expect(
|
|
body.includes("` - Plan` suffix"),
|
|
"plan-sections.md must require the ` - Plan` title suffix — artifacts have shipped without it.",
|
|
).toBe(true)
|
|
expect(
|
|
/conventional-commit prefix/i.test(body),
|
|
"plan-sections.md must prohibit a conventional-commit prefix in `title` — the `type` field carries that classification.",
|
|
).toBe(true)
|
|
})
|
|
|
|
test("html-rendering.md reference exists and is loadable", () => {
|
|
const body = readFileSync(HTML_RENDERING_PATH, "utf8")
|
|
expect(body.length).toBeGreaterThan(0)
|
|
// Spot-check that the major sections we promise the agent are present.
|
|
expect(/Hard invariants/i.test(body)).toBe(true)
|
|
expect(/Precedence stack/i.test(body)).toBe(true)
|
|
expect(/Active-recall/i.test(body)).toBe(true)
|
|
expect(/Format principles/i.test(body)).toBe(true)
|
|
expect(/Affordance idioms/i.test(body)).toBe(true)
|
|
expect(/Agent-consumability rules/i.test(body)).toBe(true)
|
|
expect(/Post-compose audit/i.test(body)).toBe(true)
|
|
})
|
|
})
|