feat(doc-review, learnings-researcher): tiers, chain grouping, rewrite (#601)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -353,3 +353,278 @@ describe("ce-plan review contract", () => {
|
||||
expect(content).not.toContain("**Options for Standard or Lightweight plans:**")
|
||||
})
|
||||
})
|
||||
|
||||
describe("ce-doc-review contract", () => {
|
||||
test("findings-schema autofix_class enum uses ce-code-review-aligned tier names", async () => {
|
||||
const schema = JSON.parse(
|
||||
await readRepoFile("plugins/compound-engineering/skills/ce-doc-review/references/findings-schema.json")
|
||||
)
|
||||
const enumValues = schema.properties.findings.items.properties.autofix_class.enum
|
||||
|
||||
// Three-tier system aligned with ce-code-review's first three tier names
|
||||
expect(enumValues).toEqual(["safe_auto", "gated_auto", "manual"])
|
||||
|
||||
// No advisory tier — advisory-style findings surface as an FYI subsection at presentation layer
|
||||
expect(enumValues).not.toContain("advisory")
|
||||
|
||||
// Old tier names must be gone after the rename
|
||||
expect(enumValues).not.toContain("auto")
|
||||
expect(enumValues).not.toContain("present")
|
||||
})
|
||||
|
||||
test("subagent template carries framing guidance and strawman rule", async () => {
|
||||
const template = await readRepoFile(
|
||||
"plugins/compound-engineering/skills/ce-doc-review/references/subagent-template.md"
|
||||
)
|
||||
|
||||
// Framing guidance block present
|
||||
expect(template).toContain("observable consequence")
|
||||
expect(template).toContain("2-4 sentences")
|
||||
|
||||
// Strawman-aware classification rule
|
||||
expect(template).toContain("Strawman-aware classification rule")
|
||||
expect(template).toContain("is NOT a real alternative")
|
||||
|
||||
// Strawman safeguard on safe_auto
|
||||
expect(template).toContain("Strawman safeguard")
|
||||
|
||||
// Persona exclusion of Open Questions section (prevents round-2 feedback loop)
|
||||
expect(template).toContain("Exclude prior-round deferred entries")
|
||||
expect(template).toContain("Deferred / Open Questions")
|
||||
|
||||
// Decision primer slot and rules
|
||||
expect(template).toContain("{decision_primer}")
|
||||
expect(template).toContain("<decision-primer-rules>")
|
||||
})
|
||||
|
||||
test("synthesis pipeline routes three tiers with per-severity gates and FYI subsection", async () => {
|
||||
const synthesis = await readRepoFile(
|
||||
"plugins/compound-engineering/skills/ce-doc-review/references/synthesis-and-presentation.md"
|
||||
)
|
||||
|
||||
// Per-severity confidence gate with the specific thresholds
|
||||
expect(synthesis).toContain("Per-Severity")
|
||||
expect(synthesis).toMatch(/P0\s*\|\s*0\.50/)
|
||||
expect(synthesis).toMatch(/P1\s*\|\s*0\.60/)
|
||||
expect(synthesis).toMatch(/P2\s*\|\s*0\.65/)
|
||||
expect(synthesis).toMatch(/P3\s*\|\s*0\.75/)
|
||||
|
||||
// FYI floor at 0.40 for low-confidence manual findings
|
||||
expect(synthesis).toContain("0.40")
|
||||
expect(synthesis).toContain("FYI floor")
|
||||
|
||||
// Three-tier routing table present
|
||||
expect(synthesis).toContain("`safe_auto`")
|
||||
expect(synthesis).toContain("`gated_auto`")
|
||||
expect(synthesis).toContain("`manual`")
|
||||
|
||||
// Cross-persona agreement boost (replaces residual-concern promotion)
|
||||
expect(synthesis).toContain("Cross-Persona Agreement Boost")
|
||||
expect(synthesis).toContain("+0.10")
|
||||
|
||||
// R29 and R30 round-2 rules
|
||||
expect(synthesis).toContain("R29 Rejected-Finding Suppression")
|
||||
expect(synthesis).toContain("R30 Fix-Landed Matching Predicate")
|
||||
})
|
||||
|
||||
test("headless envelope surfaces new tiers distinctly", async () => {
|
||||
const synthesis = await readRepoFile(
|
||||
"plugins/compound-engineering/skills/ce-doc-review/references/synthesis-and-presentation.md"
|
||||
)
|
||||
|
||||
// Bucket headers for the new tiers appear in the headless envelope template.
|
||||
// User-facing vocabulary: fixes / Proposed fixes / Decisions / FYI observations
|
||||
// maps to the safe_auto / gated_auto / manual / FYI internal enum values.
|
||||
expect(synthesis).toContain("Applied N fixes")
|
||||
expect(synthesis).toContain("Proposed fixes")
|
||||
expect(synthesis).toContain("Decisions")
|
||||
expect(synthesis).toContain("FYI observations")
|
||||
|
||||
// Terminal signal preserved for programmatic callers
|
||||
expect(synthesis).toContain("Review complete")
|
||||
})
|
||||
|
||||
test("terminal question is three-option by default with label adaptation", async () => {
|
||||
const synthesis = await readRepoFile(
|
||||
"plugins/compound-engineering/skills/ce-doc-review/references/synthesis-and-presentation.md"
|
||||
)
|
||||
|
||||
// Three options when fixes are queued
|
||||
expect(synthesis).toContain("Apply decisions and proceed to <next stage>")
|
||||
expect(synthesis).toContain("Apply decisions and re-review")
|
||||
expect(synthesis).toContain("Exit without further action")
|
||||
|
||||
// Two options in the zero-actionable case with the adapted label
|
||||
expect(synthesis).toContain("fixes_applied_count == 0")
|
||||
expect(synthesis).toContain("zero-actionable case")
|
||||
|
||||
// Next-stage substitution rules documented
|
||||
expect(synthesis).toContain("Requirements document")
|
||||
expect(synthesis).toContain("Plan document")
|
||||
expect(synthesis).toContain("ce-plan")
|
||||
expect(synthesis).toContain("ce-work")
|
||||
})
|
||||
|
||||
test("SKILL.md has Interactive mode rules with AskUserQuestion pre-load", async () => {
|
||||
const content = await readRepoFile(
|
||||
"plugins/compound-engineering/skills/ce-doc-review/SKILL.md"
|
||||
)
|
||||
|
||||
// Interactive mode rules section at top
|
||||
expect(content).toContain("## Interactive mode rules")
|
||||
expect(content).toContain("AskUserQuestion")
|
||||
expect(content).toContain("ToolSearch")
|
||||
expect(content).toContain("numbered-list fallback")
|
||||
|
||||
// Decision primer variable in the dispatch table
|
||||
expect(content).toContain("{decision_primer}")
|
||||
expect(content).toContain("<prior-decisions>")
|
||||
|
||||
// References loaded lazily via backtick paths for walk-through and bulk-preview
|
||||
expect(content).toContain("`references/walkthrough.md`")
|
||||
expect(content).toContain("`references/bulk-preview.md`")
|
||||
})
|
||||
|
||||
test("walkthrough and bulk-preview reference files exist with required mechanics", async () => {
|
||||
const walkthrough = await readRepoFile(
|
||||
"plugins/compound-engineering/skills/ce-doc-review/references/walkthrough.md"
|
||||
)
|
||||
const bulkPreview = await readRepoFile(
|
||||
"plugins/compound-engineering/skills/ce-doc-review/references/bulk-preview.md"
|
||||
)
|
||||
|
||||
// Routing question distinguishing words present (front-loaded per AGENTS.md Interactive Question Tool Design)
|
||||
expect(walkthrough).toContain("Review each finding one by one")
|
||||
expect(walkthrough).toContain("LFG")
|
||||
expect(walkthrough).toContain("Append findings to the doc's Open Questions section")
|
||||
expect(walkthrough).toContain("Report only")
|
||||
|
||||
// Four per-finding options
|
||||
expect(walkthrough).toContain("Apply the proposed fix")
|
||||
expect(walkthrough).toContain("Defer — append to the doc's Open Questions section")
|
||||
expect(walkthrough).toContain("Skip — don't apply, don't append")
|
||||
expect(walkthrough).toContain("LFG the rest")
|
||||
|
||||
// Recommended marker mandatory
|
||||
expect(walkthrough).toContain("(recommended)")
|
||||
|
||||
// No advisory variant (advisory is a presentation-layer concept, not a walkthrough option)
|
||||
expect(walkthrough).not.toContain("Acknowledge — mark as reviewed")
|
||||
|
||||
// No tracker-detection machinery (ce-doc-review has no external tracker)
|
||||
expect(walkthrough).not.toContain("named_sink_available")
|
||||
expect(walkthrough).not.toContain("any_sink_available")
|
||||
expect(walkthrough).not.toContain("[TRACKER]")
|
||||
|
||||
// Bulk preview has Proceed/Cancel options and the four bucket labels
|
||||
expect(bulkPreview).toContain("Proceed")
|
||||
expect(bulkPreview).toContain("Cancel")
|
||||
expect(bulkPreview).toContain("Applying (N):")
|
||||
expect(bulkPreview).toContain("Appending to Open Questions (N):")
|
||||
expect(bulkPreview).toContain("Skipping (N):")
|
||||
|
||||
// No Acknowledge bucket in bulk preview either
|
||||
expect(bulkPreview).not.toContain("Acknowledging (N):")
|
||||
})
|
||||
|
||||
test("open-questions-defer reference implements append mechanic with failure path", async () => {
|
||||
const defer = await readRepoFile(
|
||||
"plugins/compound-engineering/skills/ce-doc-review/references/open-questions-defer.md"
|
||||
)
|
||||
|
||||
// Append mechanic steps
|
||||
expect(defer).toContain("## Deferred / Open Questions")
|
||||
expect(defer).toContain("### From YYYY-MM-DD review")
|
||||
|
||||
// Entry format includes required fields but excludes suggested_fix and evidence
|
||||
expect(defer).toContain("{title}")
|
||||
expect(defer).toContain("{severity}")
|
||||
expect(defer).toContain("{reviewer}")
|
||||
expect(defer).toContain("{confidence}")
|
||||
expect(defer).toContain("{why_it_matters}")
|
||||
|
||||
// Failure-path sub-question with three options
|
||||
expect(defer).toContain("Retry")
|
||||
expect(defer).toContain("Record the deferral in the completion report only")
|
||||
expect(defer).toContain("Convert this finding to Skip")
|
||||
|
||||
// No tracker-detection logic (this is the in-doc defer path, not tracker-defer)
|
||||
expect(defer).not.toContain("named_sink_available")
|
||||
expect(defer).not.toContain("[TRACKER]")
|
||||
})
|
||||
})
|
||||
|
||||
describe("ce-compound frontmatter schema expansion contract", () => {
|
||||
test("problem_type enum includes the four new knowledge-track values", async () => {
|
||||
const schema = await readRepoFile(
|
||||
"plugins/compound-engineering/skills/ce-compound/references/schema.yaml"
|
||||
)
|
||||
|
||||
// Four new knowledge-track values present in the enum
|
||||
expect(schema).toContain("architecture_pattern")
|
||||
expect(schema).toContain("design_pattern")
|
||||
expect(schema).toContain("tooling_decision")
|
||||
expect(schema).toContain("convention")
|
||||
|
||||
// best_practice remains valid as fallback
|
||||
expect(schema).toContain("best_practice")
|
||||
})
|
||||
|
||||
test("ce-compound-refresh schema stays in sync with canonical ce-compound schema", async () => {
|
||||
const canonical = await readRepoFile(
|
||||
"plugins/compound-engineering/skills/ce-compound/references/schema.yaml"
|
||||
)
|
||||
const refresh = await readRepoFile(
|
||||
"plugins/compound-engineering/skills/ce-compound-refresh/references/schema.yaml"
|
||||
)
|
||||
|
||||
// Duplicate schemas must be identical (kept in sync intentionally per AGENTS.md)
|
||||
expect(refresh).toEqual(canonical)
|
||||
})
|
||||
|
||||
test("yaml-schema.md documents category mappings for the four new values", async () => {
|
||||
const mapping = await readRepoFile(
|
||||
"plugins/compound-engineering/skills/ce-compound/references/yaml-schema.md"
|
||||
)
|
||||
|
||||
expect(mapping).toContain("architecture_pattern` -> `docs/solutions/architecture-patterns/")
|
||||
expect(mapping).toContain("design_pattern` -> `docs/solutions/design-patterns/")
|
||||
expect(mapping).toContain("tooling_decision` -> `docs/solutions/tooling-decisions/")
|
||||
expect(mapping).toContain("convention` -> `docs/solutions/conventions/")
|
||||
})
|
||||
})
|
||||
|
||||
describe("ce-learnings-researcher domain-agnostic contract", () => {
|
||||
test("agent prompt frames as domain-agnostic not bug-focused", async () => {
|
||||
const agent = await readRepoFile(
|
||||
"plugins/compound-engineering/agents/research/ce-learnings-researcher.agent.md"
|
||||
)
|
||||
|
||||
// Domain-agnostic identity framing
|
||||
expect(agent).toContain("domain-agnostic institutional knowledge researcher")
|
||||
|
||||
// Multiple learning shapes named as first-class
|
||||
expect(agent).toContain("Architecture patterns")
|
||||
expect(agent).toContain("Design patterns")
|
||||
expect(agent).toContain("Tooling decisions")
|
||||
expect(agent).toContain("Conventions")
|
||||
|
||||
// Structured <work-context> input accepted
|
||||
expect(agent).toContain("<work-context>")
|
||||
expect(agent).toContain("Activity:")
|
||||
expect(agent).toContain("Concepts:")
|
||||
expect(agent).toContain("Decisions:")
|
||||
expect(agent).toContain("Domains:")
|
||||
|
||||
// Dynamic subdirectory probe replaces hardcoded category table
|
||||
expect(agent).toContain("Probe")
|
||||
expect(agent).toContain("discover which subdirectories actually exist")
|
||||
|
||||
// Critical-patterns.md read is conditional, not assumed
|
||||
expect(agent).toMatch(/critical-patterns.md.*exists/i)
|
||||
|
||||
// Integration Points list no longer includes ce-doc-review (agent is ce-plan-owned)
|
||||
const integration = agent.substring(agent.indexOf("Integration Points"))
|
||||
expect(integration).not.toContain("ce-doc-review")
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user