package cockpit import ( "fmt" "sort" "strings" ) // advisoryBanner is the loud header every advisory artifact (Cursor, // AGENTS.md, GEMINI.md) carries so the operator can never mistake it for // harness-enforced config. It is a single package constant shared by the // renderers and the self-consistency parser, so the honesty notice can never // silently drift; fidelityBannerPresent asserts it in tests. const advisoryBanner = "**ADVISORY ONLY — NOT HARNESS-ENFORCED.** This file documents the tool policy " + "the AI should honor; the harness does not enforce it at runtime. eeco still refuses to emit any " + "playbook that declares a forbidden write-git verb — the advisory note describes only the harness side." // Heading texts the self-consistency parser keys on. The renderers and the // parser reference the same constants so a section can never be renamed in one // place and silently missed in the other. const ( headingForbidden = "Forbidden" // section listing the safety denylist headingAllowed = "Allowed (advisory)" // section listing the composed allowlist headingStep = "Step " // prefix of a per-step heading's text headingOutput = "Output" // closing output-format section ) // renderPlaybookBody appends the shared advisory body for one playbook under // the given heading depth marker (e.g. "##" for a per-file Cursor doc, "###" // for a section inside an aggregate AGENTS.md / GEMINI.md). The structure — // derived safety warning, a Forbidden block enumerating both the git-verb // denylist (as `git `) and the human Intent.Forbidden phrases, an // Allowed (advisory) list from composeAllowedTools, the numbered Steps, and // the Output section — is identical across advisory targets so one // self-consistency parser serves them all. func renderPlaybookBody(b *strings.Builder, p Playbook, depth string) { b.WriteString(deriveSafetyWarning(p.Intent)) b.WriteString("\n\n") fmt.Fprintf(b, "%s %s\n", depth, headingForbidden) for _, v := range p.Intent.forbiddenVerbs() { fmt.Fprintf(b, "- `git %s`\n", v) } for _, ph := range p.Intent.Forbidden { fmt.Fprintf(b, "- %s\n", ph) } b.WriteString("\n") fmt.Fprintf(b, "%s %s\n", depth, headingAllowed) for _, a := range composeAllowedTools(p) { fmt.Fprintf(b, "- %s\n", a) } b.WriteString("\n") for _, s := range p.Steps { fmt.Fprintf(b, "%s %s%d — %s\n", depth, headingStep, s.Index, s.Title) if body := strings.TrimRight(s.Body, "\n"); body != "" { b.WriteString(body) b.WriteString("\n") } if len(s.Runs) > 0 { b.WriteString("\n```\n") for _, run := range s.Runs { b.WriteString(run) b.WriteString("\n") } b.WriteString("```\n") } b.WriteString("\n") } if out := strings.TrimRight(p.OutputFormat, "\n"); out != "" { fmt.Fprintf(b, "%s %s\n", depth, headingOutput) b.WriteString(out) b.WriteString("\n") } } // renderAggregateMarkdown renders a whole playbook set as one advisory // Markdown document: an H1 title, the ADVISORY banner, an optional // target-specific header note, a fidelity report naming the set, then one // section per playbook. The set is sorted by Name so the output is // byte-deterministic regardless of input order (re-emit stays sha-idempotent). // Shared by the AGENTS.md and GEMINI.md renderers. func renderAggregateMarkdown(ps []Playbook, title, header string) []byte { set := append([]Playbook(nil), ps...) sort.Slice(set, func(i, j int) bool { return set[i].Name < set[j].Name }) var b strings.Builder fmt.Fprintf(&b, "# %s\n\n", title) b.WriteString(advisoryBanner) b.WriteString("\n\n") if h := strings.TrimSpace(header); h != "" { b.WriteString(h) b.WriteString("\n\n") } b.WriteString("## Fidelity report\n\n") b.WriteString("Enforcement: advisory (not harness-enforced). Playbooks in this file:\n\n") for _, p := range set { fmt.Fprintf(&b, "- %s\n", p.Name) } b.WriteString("\n") for _, p := range set { fmt.Fprintf(&b, "## %s\n\n", deriveTitle(p.Name)) if d := strings.TrimSpace(p.Description); d != "" { b.WriteString(d) b.WriteString("\n\n") } renderPlaybookBody(&b, p, "###") b.WriteString("\n") } return []byte(b.String()) }