Help us shape the future of Go! Take the 2026 Go Developer Survey

planview

package
v0.22.0 Latest Latest
Warning

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

Go to latest
Published: Oct 21, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

Documentation

Overview

Package planview embeds bide view's flow-config page in a program, with the program's plan.Registry, so the page checks a config the way plan.Load does: blocks, predicates, merges and types, as well as structure, and shows the digest a run of the flow would journal. The bide view command cannot import a program's Go types, so its -config page is structural; this package is the full check. Nothing runs, and the page is read-only.

Example

A program checks a flow config against its own Registry: the type mismatch on the edge from classify to done is found only with the Registry (bide view -config checks structure only). planview.Handler serves the same check as the config page.

package main

import (
	"context"
	"os"

	"github.com/bide-ai/bide/plan"
	"github.com/bide-ai/bide/plan/planview"
)

type Order struct{ Amount int }
type Receipt struct{ Status string }

func main() {
	reg := plan.NewRegistry()
	_ = reg.RegisterStep("classify", func(_ context.Context, o Order) (Order, error) { return o, nil })
	_ = reg.RegisterStep("done", func(_ context.Context, s string) (Receipt, error) { return Receipt{Status: s}, nil })
	config := []byte(`{"version": 1, "flow": "orders",
	  "nodes": [{"name": "classify", "block": "classify"}, {"name": "done", "block": "done"}],
	  "wiring": [{"edge": ["classify", "done"]}]}`)

	if planview.Check(os.Stdout, "orders.json", config, nil) {
		return
	}
	planview.Check(os.Stdout, "orders.json", config, reg)
}
Output:
orders.json:classify->done: edge "classify" -> "done" connects planview_test.Order to string (types must match exactly)

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Check

func Check(w io.Writer, name string, data []byte, reg *plan.Registry) bool

Check writes the config's problems to w as "name:node: message" lines ("name:from->to: message" for an edge or arm, "name: message" for the config as a whole), warnings marked "warning:", then a line counting the verdicts on the config's assert lines when it has any, and reports whether any problem is an error (a violated, undecided or invalid assertion is one): the bide view -check output, with reg's checks added. See plan.InspectConfig for the problems and the assertions.

func CheckDiff added in v0.15.0

func CheckDiff(w io.Writer, old, newer Version) (weakened bool, err error)

CheckDiff writes the diff of newer from old to w as bide check -diff -check prints it: one line per change, a line per weakened approval gate, and a summary line with the counts and the digest change. It reports whether an approval gate was removed or weakened (a set of approvals the new gate accepts would not have passed the old one), and fails only when a version cannot be read as a flow config.

Example

A CI step compares a flow config with the version on the main branch and fails when an approval gate was weakened. With each version's Registry (Version.Registry) the summary line also gives the digest each version's runs journal.

package main

import (
	"fmt"
	"os"

	"github.com/bide-ai/bide/plan/planview"
)

func main() {
	old := []byte(`{"version": 1, "flow": "orders",
	  "nodes": [{"name": "classify", "block": "classify"},
	            {"name": "pay", "block": "pay", "approval": {"need": 2, "approvers": ["ops", "finance"]}}],
	  "wiring": [{"edge": ["classify", "pay"]}]}`)
	newer := []byte(`{"version": 1, "flow": "orders",
	  "nodes": [{"name": "classify", "block": "classify"},
	            {"name": "pay", "block": "pay", "approval": {"need": 1, "approvers": ["ops", "finance"]}}],
	  "wiring": [{"edge": ["classify", "pay"]}]}`)

	weakened, err := planview.CheckDiff(os.Stdout, planview.Version{Name: "main:orders.json", Data: old}, planview.Version{Name: "orders.json", Data: newer})
	if err == nil && weakened {
		fmt.Println("fail the build")
	}
}
Output:
orders.json: changed node pay: approval 2 of 2 -> 1 of 2 (weakened)
orders.json: approval policy weakened: pay: approval 2 of 2 -> 1 of 2
orders.json: 1 change from main:orders.json (nodes: 0 added, 0 removed, 1 changed; edges: 0 added, 0 removed, 0 changed); digest not computed: the check is structural (the digest commits to the blocks' Go types; see plan/planview)
fail the build

func DiffHandler added in v0.15.0

func DiffHandler(old, newer Version, opts ...Option) http.Handler

DiffHandler serves the diff page bide check -diff shows, with each version's checks: newer compared with old. The page draws the newer flow with added nodes and edges in green, removed ones as dashed red ghosts and changed nodes in amber, calls out an approval gate whose policy was weakened, and, with both Registries, shows the digest each version's runs journal. Its view is JSON at /diff.json. It answers only GET and HEAD requests addressed to a loopback host. The options are Handler's, and DiffHandler panics on a set of themes that cannot be served as Handler does.

func Handler

func Handler(name string, data []byte, reg *plan.Registry, opts ...Option) http.Handler

Handler serves the config page for the flow config data (JSON), named name in messages, checked against reg (nil: structural only, as the bide view command checks it): the page at / and its view as JSON at /config.json. It answers only GET and HEAD requests addressed to a loopback host. The options choose the themes (WithThemes, WithDefaultTheme, WithThemeLock); Handler panics on a set that cannot be served, as http.ServeMux.Handle panics on a bad pattern.

func Listen

func Listen(addr string) (net.Listener, error)

Listen listens on addr, which must name a loopback IP or localhost (any other host is refused), for serving Handler: http.Serve(ln, planview.Handler(...)).

Types

type Option added in v0.22.0

type Option func(*options)

Option configures Handler and DiffHandler.

func WithDefaultTheme added in v0.22.0

func WithDefaultTheme(name string) Option

WithDefaultTheme sets the theme a reader starts in, by name: a shipped theme or one added with WithThemes. Without it the reader's system setting picks light or dark. A reader who asks their system for more contrast or forced colors gets those, over the default. Handler and DiffHandler panic when the name is not a theme in the set or differs from the name given to WithThemeLock.

func WithThemeLock added in v0.22.0

func WithThemeLock(name string) Option

WithThemeLock makes the named theme the only one the page has: no switch, no remembered choice, and the reader's system setting no longer picks light or dark. A reader who asks their system for more contrast or forced colors still gets those, over the lock. Handler and DiffHandler panic when the name is not a theme in the set or differs from the name given to WithDefaultTheme.

Example

A program that serves the config page in its own look passes a theme that passed the checks, and may make it the only theme the page has. The page's stylesheet is generated from the checked values; the host cannot pass CSS.

package main

import (
	"fmt"
	"net/http/httptest"
	"strings"

	"github.com/bide-ai/bide/plan/planview"
	"github.com/bide-ai/bide/theme"
)

func main() {
	th, _, err := theme.Parse([]byte(`{"bide_theme": 1, "name": "acme-night", "base": "dark",
	  "surface": {"bg": "#0e1116", "accent": "#79b8ff"}}`))
	if err != nil {
		fmt.Println(err)
		return
	}
	config := []byte(`{"version": 1, "flow": "orders", "nodes": [{"name": "a", "block": "a"}], "wiring": []}`)
	h := planview.Handler("orders.json", config, nil, planview.WithThemes(th), planview.WithThemeLock("acme-night"))

	r := httptest.NewRequest("GET", "http://127.0.0.1/assets/style.css", nil)
	r.Host = "127.0.0.1"
	w := httptest.NewRecorder()
	h.ServeHTTP(w, r)
	fmt.Println(strings.Contains(w.Body.String(), `--bide-lock: "acme-night";`))
}
Output:
true

func WithThemes added in v0.22.0

func WithThemes(themes ...*theme.Theme) Option

WithThemes adds custom themes, each from theme.Parse or theme.Load (so each passed the checks in docs/design/themes.md), beside the shipped light, dark and high-contrast that the page's switch cycles through. A host passes values, never CSS: the page's stylesheet is generated from the checked values alone. Handler and DiffHandler panic when a theme is nil or not from Parse or Load, or when two themes (a shipped one included) have one name.

type Version added in v0.15.0

type Version struct {
	Name     string
	Data     []byte
	Registry *plan.Registry
}

Version is one version of a flow config: its name in messages, its JSON, and the Registry it loads against (nil: the structural check only). Load refuses a block registered and never used, so each version takes the Registry of the program as of that version; one Registry for two versions that use different blocks leaves a version with an error and no digest.

Jump to

Keyboard shortcuts

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