plan

package
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 11, 2026 License: MIT Imports: 31 Imported by: 0

Documentation

Overview

Package plan enriches agent-generated implementation plans with SageOx team context. ox computes DETERMINISTIC badges locally (zero LLM tokens) and assembles a context bundle; the client agent does any inference. ox NEVER makes an LLM or network call in this path.

Architecture:

  • Detectors produce deterministic Annotations from local data (collision, prior-art, expert-routing). They are fail-open: missing/unreadable data returns (nil, nil), never an aborting error.
  • Retrievers assemble a context bundle ([]ContextItem) the client agent reasons over to author judgment badges (aligns/conflicts, expert perspective). Also fail-open.
  • Enrich() orchestrates registered detectors and retrievers, aggregating results into a Result with a deterministic SignalSummary.

Round 2 agents implement detectors/retrievers in collision.go, expert.go, priorart.go (and a context-bundle assembler) and register them via init().

Index

Constants

View Source
const (
	// NonTrivialMinFiles: a multi-file plan (>= 2 distinct files) is non-trivial.
	// Exported as the single source of truth: the plan-exit hook mirrors these
	// for wording and a drift test asserts the copies stay equal.
	NonTrivialMinFiles = 2
	// NonTrivialMinSteps: a ~5+ step plan is non-trivial, matching the prime
	// "~5+ steps" criterion. H2 sections are the step proxy.
	NonTrivialMinSteps = 5
)
View Source
const (
	SessionOutcomeActive  = "active"
	SessionOutcomeStopped = "stopped"
	SessionOutcomeAborted = "aborted"
)

Session-outcome values for Provenance.SessionOutcome. "" == unknown.

View Source
const SchemaVersion = "v1"

SchemaVersion is the on-disk schema version stamped into both annotations.json (Result.SchemaVersion) and meta.json (Meta.SchemaVersion) on write. These are long-lived ledger artifacts a future ox may need to migrate; an explicit version lets a reader detect and adapt to an older layout instead of guessing. Bump this when the serialized shape of Result or Meta changes incompatibly.

Variables

This section is empty.

Functions

func AppendResolution

func AppendResolution(planDir string, r Resolution, now time.Time) error

AppendResolution adds one agent disposition to the append log.

func FeedbackDigest

func FeedbackDigest(items []MergedItem) string

FeedbackDigest renders a compact, agent-readable summary from the merged view: open/addressed/verified/wontfix counts, then every OPEN actionable item with its anchor (the id to resolve), section, label, and note. Returns "" when there is no feedback.

func Load

func Load(gitRoot, slug string) (string, Result, PlanInfo, error)

Load reads a captured plan by slug. The slug matches either the meta.json slug or the directory's trailing slug segment (the YYYY-MM-DD- prefix is optional in the lookup). Returns the raw plan markdown, the stored Result, and the listing info.

func MutatePlanMeta

func MutatePlanMeta(ctx context.Context, planDir string, mutate func(*Meta) (*Meta, error)) error

MutatePlanMeta runs an exclusive read-modify-write under an advisory flock on a plan's meta.json — the direct mirror of lfs.MutateSessionMeta. Every write to meta.json AFTER the initial Save (status changes, session-outcome reconciliation) MUST go through this so a re-save and a concurrent session-stop/doctor write serialize at the filesystem level instead of clobbering each other.

The mutator receives the on-disk Meta (nil if the file is missing) and returns the Meta to write, or nil to leave the file untouched (an "only if exists" guard). Returning an error aborts the write.

func PlanHTMLPath

func PlanHTMLPath(dir string) (path string, ref lfs.FileRef, isPointer, exists bool)

PlanHTMLPath returns the absolute path to a captured plan's plan.html, the referenced FileRef when it is an LFS pointer, and whether the file exists. The view path uses this to decide between opening a plain HTML file and hydrating a pointer first.

func ReconcileSessionOutcome

func ReconcileSessionOutcome(gitRoot, slug, sessionID, outcome string) error

ReconcileSessionOutcome backfills the producing session's canonical id and final outcome onto a plan (by slug) at session-stop, where both are known. sessionID is the ses_<UUIDv7> minted at stop (skipped when ""); outcome is a SessionOutcome* constant. Single flocked write so it can't race a re-save.

func RecordPlanGenerated

func RecordPlanGenerated(res Result, saved bool)

RecordPlanGenerated emits a single-line, key=value structured metric for a completed `ox plan` enrichment. It is purely local observability — there is no server LLM in this path, so there is nothing to meter server-side. The counts let us see, in aggregate, how often plans fire collision / prior-art / expert signals and how much context the bundle carried, without recording any plan content.

saved reports whether the enriched plan was captured to the ledger. It is a separate boolean (not derived from res) because auto-save is gated on config and on a configured ledger, independent of the signal summary.

func RegisterDetector

func RegisterDetector(d Detector)

RegisterDetector adds a deterministic detector to the global registry. Call from an init() in the detector's file. Nil detectors are ignored.

func RegisterRetriever

func RegisterRetriever(r Retriever)

RegisterRetriever adds a context-bundle retriever to the global registry. Call from an init() in the retriever's file. Nil retrievers are ignored.

func RenderHTML

func RenderHTML(in Input, res Result) ([]byte, error)

RenderHTML renders a resolved plan + its enrichment Result into a single self-contained HTML document. Deterministic and network-free at render time (Mermaid loads from CDN only when the page is viewed).

func RenderHTMLOpts

func RenderHTMLOpts(in Input, res Result, opts RenderOptions) ([]byte, error)

RenderHTMLOpts is RenderHTML with optional render-time context (e.g. the slug for the review layer). RenderHTML delegates here with zero options.

func RenderViz

func RenderViz(pattern string, data []byte) (string, error)

RenderViz renders one parameterized pattern from its JSON data into an HTML fragment. Returns an error for an unknown pattern or malformed data so the command layer can show an actionable message.

func Save

func Save(gitRoot string, in Input, res Result, html []byte, meta Meta) (string, error)

Save writes a captured plan into the ledger under data/plans/<dated-slug>/. It writes plan.md (from in.Raw), annotations.json (res), and meta.json as plain git-tracked text. plan.html is written ONLY when html != nil: plain when small, as an LFS pointer when it exceeds htmlLFSThreshold. Save never renders HTML and never commits — it only materializes files in the working tree. Returns the absolute plan directory.

gitRoot is the producing project's git root; the ledger path is resolved from it via ProjectContext. Returns an error if no ledger is configured (the caller decides whether that is fatal — the porcelain path treats it as "nothing to save").

func SaveFeedback

func SaveFeedback(planDir string, set FeedbackSet, now time.Time) (string, error)

SaveFeedback writes a review round under <planDir>/feedback/. now controls the timestamp (tests stay deterministic). Returns the written path.

func SetSessionOutcome

func SetSessionOutcome(gitRoot, slug, outcome string) error

SetSessionOutcome reconciles the producing session's lifecycle onto the plan (by slug) under the meta flock — written only by session-stop / `ox doctor`, never by Save. Use the SessionOutcome* constants. No-op if no meta.json.

func SetStatus

func SetStatus(gitRoot, slug string, status PlanStatus) error

SetStatus updates a saved plan's lifecycle status (by slug) under the meta flock. No-op if the plan dir has no meta.json. Use the PlanStatus* constants.

func Slugify

func Slugify(topic string) string

Slugify derives a 2-4 word kebab-case slug from a topic/title. Lowercases, strips punctuation, and keeps the first 2-4 meaningful words. An empty or punctuation-only input yields "untitled-plan" so a directory name is always well-formed.

Types

type Annotation

type Annotation struct {
	Section   string    `json:"section,omitempty"`
	Kind      BadgeKind `json:"kind"`
	Type      BadgeType `json:"type"`
	Why       string    `json:"why"`
	SourceURL string    `json:"source_url,omitempty"`
	Expert    string    `json:"expert,omitempty"`
	Files     []string  `json:"files,omitempty"`
}

Annotation is a single badge attached to a plan section.

type BadgeKind

type BadgeKind string

BadgeKind distinguishes who produces an annotation: ox locally (deterministic, zero tokens) versus the client agent (judgment, reasoned from the bundle).

const (
	// BadgeDeterministic: ox computes the badge locally with zero LLM tokens.
	BadgeDeterministic BadgeKind = "deterministic"
	// BadgeJudgment: the client agent authors the badge from the context bundle.
	BadgeJudgment BadgeKind = "judgment"
)

type BadgeType

type BadgeType string

BadgeType is the specific signal an annotation carries.

const (
	// BadgeCollision: plan touches files in an open PR, hotspot, or recent murmur.
	BadgeCollision BadgeType = "collision"
	// BadgePriorArt: a teammate already did or planned this.
	BadgePriorArt BadgeType = "prior-art"
	// BadgeExpertRoute: who owns this area + their relevant work (deterministic).
	BadgeExpertRoute BadgeType = "expert-routing"
	// BadgeAligns: plan aligns with ADRs, decisions, conventions (judgment).
	BadgeAligns BadgeType = "aligns"
	// BadgeConflicts: plan conflicts with ADRs, decisions, conventions (judgment).
	BadgeConflicts BadgeType = "conflicts"
	// BadgeExpertPersp: synthesized expert stance, cited (judgment).
	BadgeExpertPersp BadgeType = "expert-perspective"
	// BadgeRigor: collaboration-rigor stance synthesized from CollabSignals —
	// how thoughtful the human↔agent path to this plan was (judgment). ox emits
	// the raw counts (CollabSignals); the agent/cloud authors this badge.
	BadgeRigor BadgeType = "rigor"
)

type BrandingFinding

type BrandingFinding = Finding

BrandingFinding is retained as an alias so existing callers/tests keep compiling; new code should use Finding.

func LintBranding

func LintBranding(html []byte, res Result) []BrandingFinding

LintBranding verifies a rendered plan HTML carries the conditional SageOx attribution the html-plan skill is spec'd to produce. The contract (extensions/claude/commands/ox-plan.md, "SageOx attribution — subtle, earned, conditional"):

  • EARNED: when the plan carried enrichment — any deterministic badges OR context-bundle items were present — the render MUST credit it: a footer line ("…enriched by SageOx") and, when there are deterministic badges, at least one anchored OX marker.
  • NO OVERCLAIM: an un-enriched plan (no badges, empty context) must NOT carry SageOx credit — there is nothing to credit.
  • SELF-CONTAINED: the OX marker's avatar must never be a live remote <img src>; it is data:-inlined or an inline-SVG monogram. Always checked.

Returns nil when the page satisfies the contract. Fail-open: callers warn, never block.

type CollabSignals

type CollabSignals struct {
	UserPrompts     int `json:"user_prompts"`     // distinct human turns before the plan
	AgentQuestions  int `json:"agent_questions"`  // AskUserQuestion / clarifying tool calls
	ToolCalls       int `json:"tool_calls"`       // exploration-depth proxy
	DurationSeconds int `json:"duration_seconds"` // first user prompt → plan finalized
}

CollabSignals are deterministic, locally-counted facts about the human↔agent collaboration that produced the plan — effort proxies, NOT a score. Scoring (a rigor judgment) is authored by the agent now / a cloud judge later, per ADR-021. Signal COUNTS (collisions/prior-art/expert-routes) deliberately live in annotations.json (Result.Signals), not here, to avoid duplication.

type ContextItem

type ContextItem struct {
	Kind    string  `json:"kind"` // murmur|session|decision|adr|commit|discussion
	Title   string  `json:"title"`
	Ref     string  `json:"ref"`
	Snippet string  `json:"snippet,omitempty"`
	Score   float64 `json:"score"`
	Author  string  `json:"author,omitempty"`
	When    string  `json:"when,omitempty"`
}

ContextItem is one ranked, pre-retrieved slice of ledger / team context / code the client agent reasons over to author judgment badges.

type Detector

type Detector interface {
	Name() string
	Detect(ctx context.Context, in Input, gitRoot string) ([]Annotation, error)
}

Detector produces deterministic annotations from local data. MUST be fail-open: on missing/unreadable data return (nil, nil), never an error that aborts enrichment.

type DiagramHint

type DiagramHint struct {
	Section       string      `json:"section"`        // H2 heading the hint applies to
	SuggestedType DiagramKind `json:"suggested_type"` // the diagram form that fits
	Reason        string      `json:"reason"`         // what structure was detected, in one clause
}

DiagramHint is a deterministic, per-section suggestion of which diagram form best captures the structure ox detected in that section. Rendering an HTML plan is now deterministic and free, so the only remaining lever on diagram QUALITY is the Mermaid the agent authors into the plan markdown — these hints point any agent (Claude, Codex, Gemini, …) at the right diagram for THIS plan, per section, instead of defaulting every section to a flowchart. Computed locally with zero LLM/network calls, same lane as the badge detectors.

type DiagramKind

type DiagramKind string

DiagramKind is a suggested diagram form for a plan section. The values are the literal Mermaid diagram keyword (or "swimlane-timeline" for the hand-built CSS timeline) so the agent can paste the suggestion straight into a fenced block.

const (
	DiagramSequence  DiagramKind = "sequenceDiagram"   // ordered call/response path
	DiagramState     DiagramKind = "stateDiagram-v2"   // states + time-bounded transitions
	DiagramSwimlane  DiagramKind = "swimlane-timeline" // phased/parallel work (CSS, not Mermaid)
	DiagramTopology  DiagramKind = "flowchart-LR"      // dependency/topology graph
	DiagramFlowchart DiagramKind = "flowchart-TB"      // branching procedure (hero default)
)

type FeedbackItem

type FeedbackItem struct {
	Anchor  string         `json:"anchor"`            // stable content-hash id, e.g. "h3f9a1c2"
	Section string         `json:"section,omitempty"` // section heading the element sits under
	Label   string         `json:"label"`             // short text of the element
	Status  FeedbackStatus `json:"status"`            // approve | request-change | flag | comment
	Note    string         `json:"note,omitempty"`    // the reviewer's comment
}

FeedbackItem is one anchored review mark. Anchor is a CONTENT hash of the element (section heading + element text), computed page-side, so it survives a re-render and only disappears when the agent rewrites that text — which is itself the signal the item was addressed. Anchor doubles as the item id used by `ox plan feedback resolve`.

type FeedbackSet

type FeedbackSet struct {
	Slug      string         `json:"slug"`
	Reviewer  string         `json:"reviewer,omitempty"`
	CreatedAt time.Time      `json:"created_at"`
	Items     []FeedbackItem `json:"items"`
}

FeedbackSet is one review round (one submit from the page).

func LoadAllFeedback

func LoadAllFeedback(planDir string) ([]FeedbackSet, error)

LoadAllFeedback reads every review round under a plan dir, oldest first. A missing feedback/ dir is not an error. resolutions.json is skipped (it is not a round).

func ParseFeedback

func ParseFeedback(raw []byte) (FeedbackSet, error)

ParseFeedback decodes and validates a review-round JSON (the page submit/export). Fail-loud on malformed input, an unknown status, or an unsafe slug.

type FeedbackStatus

type FeedbackStatus string

FeedbackStatus is the reviewer's verdict on one anchored element.

const (
	FeedbackApprove       FeedbackStatus = "approve"
	FeedbackRequestChange FeedbackStatus = "request-change"
	FeedbackFlag          FeedbackStatus = "flag"
	FeedbackComment       FeedbackStatus = "comment"
)

type Finding

type Finding struct {
	Rule    string // stable id, e.g. "branding.footer-credit" / "mermaid.arrow-in-label"
	Message string // human-readable, actionable
}

Finding is one advisory lint result on a rendered plan HTML — attribution (branding.*) or diagram (mermaid.*). All findings are warn-level: linting NEVER blocks a render or a save (fail-open agent UX). A non-empty slice means the render did not honor the html-plan contract or carries a diagram that will not render.

func LintMermaid

func LintMermaid(htmlBytes []byte) []Finding

LintMermaid extracts every Mermaid diagram from a rendered/saved plan HTML and returns one Finding per high-confidence problem. Fail-open: no diagrams (or none broken) returns nil.

func LintMermaidMarkdown

func LintMermaidMarkdown(raw string) []Finding

LintMermaidMarkdown is the same check over raw plan markdown (```mermaid fences), for the render-time path where the source is in hand before the page is built.

func LintRender

func LintRender(htmlBytes []byte, res Result) []Finding

LintRender runs the full advisory contract over a rendered plan HTML: SageOx attribution (LintBranding) plus diagram validity (LintMermaid). It is the single entrypoint `ox plan lint` / `ox plan save` call. Fail-open: an empty page returns nil.

type Input

type Input struct {
	Path     string
	Raw      string
	Sections []Section
}

Input is a resolved plan: its source path (if any), raw markdown, and parsed sections.

func Parse

func Parse(raw string) Input

Parse splits markdown into Sections on "## " H2 headings and extracts the file references cited in each section. Content before the first H2 becomes a preamble Section with an empty Heading (only emitted when it has content).

func Resolve

func Resolve(file string, stdin io.Reader) (Input, error)

Resolve reads a plan from --file if set, otherwise from a piped stdin, and parses it into an Input. When neither is provided it best-effort auto-discovers the active plan-mode file: the newest *.md under ~/.claude/plans/ (the dir Claude Code plan-mode writes to). Precedence is --file > piped stdin > auto-discovery. An empty/unfound source yields an Input with empty Raw and no sections (the enrich path must stay fail-open on empty input); the caller is expected to surface a clear "no plan found" message rather than enrich nothing.

type MergedItem

type MergedItem struct {
	FeedbackItem
	RaisedAt   time.Time
	Resolution *Resolution
	Open       bool
}

MergedItem is a review item joined with its latest resolution and a computed open/closed state. Open = no resolution, or the item was re-raised after the last resolution (CreatedAt newer than the resolution's At).

func AssembleReview

func AssembleReview(planDir string) ([]MergedItem, error)

AssembleReview joins every review item (latest mark per anchor across rounds) with its latest resolution, computing open/closed. This is the single source the digest and the render read. An item is OPEN when it has no resolution, or when it was re-raised after the latest resolution (supporting the verify loop).

type Meta

type Meta struct {
	// SchemaVersion stamps the meta.json shape (set to SchemaVersion on write)
	// so a future reader can detect and migrate an older layout.
	SchemaVersion  string    `json:"schema_version,omitempty"`
	Topic          string    `json:"topic"`
	Slug           string    `json:"slug"`
	Authors        []string  `json:"authors,omitempty"`
	CreatedAt      time.Time `json:"created_at"`
	SourcePlanPath string    `json:"source_plan_path,omitempty"`

	// Status is the plan's lifecycle. Missing == "draft" for legacy plans.
	Status PlanStatus `json:"status,omitempty"`
	// Provenance links the plan to its producing session/agent/repo (forward).
	Provenance *Provenance `json:"provenance,omitempty"`
	// Collaboration holds the deterministic collaboration-effort counts.
	Collaboration *CollabSignals `json:"collaboration,omitempty"`
}

Meta is the git-tracked descriptor written as meta.json alongside a captured plan. It carries the searchable, hydration-free facts about the plan: who authored it, when, where it came from, which session/agent produced it, and how thoughtful the collaboration was.

func ReadPlanMeta

func ReadPlanMeta(gitRoot, slug string) (Meta, error)

ReadPlanMeta returns the stored Meta for a saved plan (by slug), including provenance, collaboration signals, and status. Used by the view path to surface the link without re-deriving it.

type PlanInfo

type PlanInfo struct {
	Slug      string
	Topic     string
	Dir       string
	CreatedAt time.Time
	Authors   []string
	HasHTML   bool
}

PlanInfo is the listing-level view of a captured plan, assembled from meta.json. Dir is the absolute path to the plan folder.

func List

func List(gitRoot string) ([]PlanInfo, error)

List enumerates captured plans under <ledger>/data/plans/, parsing each meta.json. Fail-open: an unconfigured or missing ledger yields an empty slice (not an error), matching the detectors' fail-open contract. Results are sorted newest-first by CreatedAt.

type PlanStatus

type PlanStatus string

PlanStatus is the plan's own lifecycle, independent of the producing session. A plan is worth keeping even if never built — it is a decision record and prior-art seed — so status lets UI/search weight rather than discard. v1: a plain writable field; no CLI auto-detection of "implemented" (that correlation is inference and belongs in the cloud judge per ADR-021).

const (
	PlanStatusDraft       PlanStatus = "draft"
	PlanStatusApproved    PlanStatus = "approved" // reviewer signed off via the review loop
	PlanStatusImplemented PlanStatus = "implemented"
	PlanStatusAbandoned   PlanStatus = "abandoned"
	PlanStatusSuperseded  PlanStatus = "superseded"
)

type Provenance

type Provenance struct {
	// Join keys (may dangle if the session was aborted / never uploaded).
	//
	// Two-phase population, because the canonical ses_ SessionID is minted
	// fresh at session-STOP and is NOT knowable mid-recording:
	//   - SessionName is the durable identifier available at plan-save time
	//     (the recording's folder name, what `ox session view <name>` resolves).
	//     It is the primary join key and is always set when a recording is live.
	//   - SessionID (ses_<UUIDv7>) is BACKFILLED at session-stop, in the same
	//     reconciliation that sets SessionOutcome=stopped — we have the real id
	//     and the produced-plan slugs in hand there. Empty for aborted sessions
	//     (no stop) and for plans saved outside a recording.
	SessionName string `json:"session_name,omitempty"`
	SessionID   string `json:"session_id,omitempty"` // ses_<UUIDv7>, backfilled at stop
	AgentID     string `json:"agent_id,omitempty"`   // Ox#### stable agent instance
	RepoID      string `json:"repo_id,omitempty"`

	// Denormalized snapshot — renders without the session present.
	AgentType  string `json:"agent_type,omitempty"` // claude-code, codex, ...
	Model      string `json:"model,omitempty"`
	AuthorName string `json:"author_name,omitempty"` // privacy-safe display name at save time

	// SessionOutcome is RECONCILED SYSTEM STATE, not authored provenance:
	// "" (unknown) | "active" | "stopped" | "aborted". Written only by
	// session-stop / `ox doctor` through MutatePlanMeta, never by Save.
	SessionOutcome string `json:"session_outcome,omitempty"`
}

Provenance ties a saved plan back to the session/agent/repo that produced it. It is DENORMALIZED on purpose: the join keys (SessionID/AgentID/RepoID) are the precise link, but a session can be aborted, never uploaded, or GC'd, so the snapshot fields (AgentType/Model/AuthorName) let a plan render fully without the session present. Duplication is the feature, not a smell.

type RenderOptions

type RenderOptions struct {
	Slug string
	// Review is the merged review state (rounds + resolutions) for this plan, so
	// the render can show each item's open/addressed state inline and in a
	// summary. Empty for a plan with no review yet.
	Review []MergedItem
	// ReviewEndpoint + ReviewToken are set ONLY when the page is served by the
	// ephemeral `ox plan review` server: the page POSTs marks to the endpoint
	// with the token. Empty for a static file:// render (clipboard fallback).
	ReviewEndpoint string
	ReviewToken    string
}

RenderOptions carries optional render-time context that isn't part of the enrichment Result.

type Resolution

type Resolution struct {
	Anchor string          `json:"anchor"`           // the item it resolves
	State  ResolutionState `json:"state"`            // addressed | wontfix | verified
	Commit string          `json:"commit,omitempty"` // commit SHA that made the change
	Note   string          `json:"note,omitempty"`   // what the agent did / why wontfix
	At     time.Time       `json:"at"`
}

Resolution is one agent disposition of a review item, append-logged.

func LoadResolutions

func LoadResolutions(planDir string) ([]Resolution, error)

LoadResolutions reads the append log (latest entries last). Missing is empty.

type ResolutionState

type ResolutionState string

ResolutionState is the agent's disposition of a review item.

const (
	ResolutionAddressed ResolutionState = "addressed" // agent made the change
	ResolutionWontfix   ResolutionState = "wontfix"   // agent declined, with reason
	ResolutionVerified  ResolutionState = "verified"  // human confirmed the fix
)

type Result

type Result struct {
	// SchemaVersion stamps the serialized annotations.json shape (set to
	// SchemaVersion on write) so a future reader can detect an older layout.
	SchemaVersion string        `json:"schema_version,omitempty"`
	Annotations   []Annotation  `json:"annotations"`
	Context       []ContextItem `json:"context"`
	Signals       SignalSummary `json:"signals"`
	// DiagramHints are deterministic per-section diagram suggestions (which
	// Mermaid/timeline form fits the structure ox detected). Empty when no
	// section had strong enough structure to suggest one.
	DiagramHints []DiagramHint `json:"diagram_hints,omitempty"`
	// Guidance is a concise, cross-agent authoring contract for rendering a
	// fantastic HTML plan (decision-first, ten-minute reader, diagrams over
	// prose). It folds in the DiagramHints so the agent gets plan-specific
	// direction, not a generic spec. Empty for a trivial/empty plan.
	Guidance string `json:"guidance,omitempty"`
}

Result is the full output of Enrich: deterministic annotations, the context bundle, and the signal summary.

func Enrich

func Enrich(ctx context.Context, in Input, gitRoot string) Result

Enrich runs every registered detector and retriever against the plan, FAIL-OPEN: a panic or error in any one detector/retriever is logged and skipped, never aborting the others. It aggregates the annotations and context items, computes a deterministic SignalSummary, and returns a sorted, deduped Result.

ox makes NO network or LLM call here — detectors and retrievers read only local data. Round 2 owns their implementations.

type Retriever

type Retriever interface {
	Name() string
	Retrieve(ctx context.Context, in Input, gitRoot string) ([]ContextItem, error)
}

Retriever produces context-bundle items. Also fail-open.

type Section

type Section struct {
	Heading string
	Body    string
	Files   []string
}

Section is one H2-delimited block of a plan, with any file references it cites.

type SignalSummary

type SignalSummary struct {
	Collisions   int  `json:"collisions"`
	PriorArt     int  `json:"prior_art"`
	ExpertRoutes int  `json:"expert_routes"`
	Material     bool `json:"material"`
	Files        int  `json:"files"`
	Steps        int  `json:"steps"`
	NonTrivial   bool `json:"non_trivial"`
}

SignalSummary is the deterministic rollup of which signals fired.

Material is the TEAM-CONTEXT axis: true when the plan warrants surfacing a nudge because team context had something to say (any collision OR expert-route OR at least one strong prior-art hit).

NonTrivial is the STRUCTURAL axis, independent of team context: true when the plan is substantial enough to warrant an enriched HTML render for human review even on greenfield work where zero team-context signals fire — multi-file (Files >= 2) OR many-step (Steps >= 5). Files counts distinct file references cited across all sections; Steps counts H2 sections (excluding the preamble). These mirror the prime non-triviality criteria; hotspot/open-PR is already covered by Material, and "architectural" is left to agent judgment.

type VizPattern

type VizPattern struct {
	ID    string `json:"id"`              // stable slug, e.g. "sparkline"
	Use   string `json:"use"`             // when to reach for it
	Why   string `json:"why"`             // the cognitive payoff
	Param string `json:"param,omitempty"` // data-shape hint when `ox plan viz render <id> --data` is supported
	Body  string `json:"body"`            // copy-paste snippet(s) + any notes
}

VizPattern is one catalog entry.

func VizCatalog

func VizCatalog() []VizPattern

VizCatalog parses and returns every visualization pattern, in document order.

func VizPatternByID

func VizPatternByID(id string) (VizPattern, bool)

VizPatternByID returns the pattern with the given id (case-insensitive), or ok=false when none matches.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL