Go 82 lines
// Package cockpit is eeco's neutral, harness-independent model of an AI
// cockpit and the renderers that translate it into harness-specific
// config. A Playbook is a single unit of AI procedure — what it does, the
// structured safety contract it must never violate, the capabilities it is
// allowed, the ordered steps, and the output it produces. The model is
// brand-free; a Renderer (one per harness) is the only thing that knows a
// target's file layout and permission spelling.
//
// The single product-defining guarantee lives here, not in the renderer:
// the safety invariant (ScanAllowlistForWriteGitVerbs, gate.go) is derived
// from the structured Intent, so an emitted artifact can never grant a
// write-capable git verb a Playbook declares forbidden. Generation refuses
// rather than silently drop a forbidden verb.
//
// Dependency direction is fixed to avoid an import cycle: cockpit owns the
// model + renderer + gate + ledger + orchestration; internal/playbooks
// imports cockpit for the Playbook type; cmd/eeco wires the two
// (playbooks.Get -> cockpit.Generate). cockpit never imports playbooks.
package cockpit
// Playbook is the neutral, harness-independent description of one AI
// procedure. The same Playbook renders to any harness target; only a
// Renderer knows a target's file layout and permission spelling.
type Playbook struct {
Name string `json:"name"` // skill dir + frontmatter name
Description string `json:"description"` // single-line: when + what + safety promise
Intent Intent `json:"intent"` // structured safety contract
Capabilities []Capability `json:"capabilities"` // ordered allowlist source
Steps []Step `json:"steps"` // ordered procedure (Step 0..N)
OutputFormat string `json:"output_format"` // closing body section
Params []Param `json:"params,omitempty"` // optional parameterization
MapsToWorkflow string `json:"maps_to_workflow,omitempty"` // deterministic backing (metadata only in C1)
}
// Intent is the structured safety contract. Forbidden seeds the prose
// warning the renderer derives; ForbiddenGitVerbs is the gate's denylist
// (falling back to defaultForbiddenGitVerbs when empty). The renderer
// derives both the allowlist and the warning from this — neither is ever
// hand-written into the body data.
type Intent struct {
Guarantee string `json:"guarantee"` // positive promise (prose seed)
Forbidden []string `json:"forbidden"` // human phrases, e.g. "git commit", "touch any tracked file"
ForbiddenGitVerbs []string `json:"forbidden_git_verbs,omitempty"` // git write/mutate denylist (gate input)
}
// forbiddenVerbs returns the git write/mutate denylist the gate scans
// against: the Playbook's own list when set, otherwise the package default.
func (in Intent) forbiddenVerbs() []string {
if len(in.ForbiddenGitVerbs) > 0 {
return in.ForbiddenGitVerbs
}
return defaultForbiddenGitVerbs
}
// Capability is one entry of the allowlist source. Kind is "tool" (a named
// harness tool) or "bash" (a shell command head with an optional arg
// scope). The renderer walks Capabilities in declared order — the JSON is
// the single source of truth, so there is no silent reorder or dedupe.
type Capability struct {
Kind string `json:"kind"` // "tool" | "bash"
Name string `json:"name,omitempty"` // tool name when kind="tool"
Verb string `json:"verb,omitempty"` // command head when kind="bash" ("git status", "date")
Scope string `json:"scope,omitempty"` // arg glob when kind="bash"; default "*"
}
// Step is one ordered instruction. Runs lists optional read-only commands
// shown in a fenced block under the step body.
type Step struct {
Index int `json:"index"`
Title string `json:"title"`
Body string `json:"body"`
Runs []string `json:"runs,omitempty"`
}
// Param is one optional parameter the procedure accepts. Reserved for the
// parameterized-general playbooks (C2); unused by the C1 handover source.
type Param struct {
Name string `json:"name"`
Description string `json:"description"`
Default string `json:"default,omitempty"`
}