guide

package
v1.801.475 Latest Latest
Warning

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

Go to latest
Published: Aug 5, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Overview

Package guide is a step-by-step checklist that gets your business running on AI.

The Business AI Guide: an interactive launch checklist every org completes on-site.

It is three orthogonal things composed:

  • a CHECKLIST ENGINE over a machine-readable curriculum (steps with id, section, title, detail, tool, dependencies). Per-org progress tracks a state per step (todo|in_progress|done|skipped); next-step logic honours dependencies; a step whose done-criterion maps to a real signal auto-marks done when that signal is present (auto-detect).
  • a BUSINESS AI AGENT: for a step bound to an MCP tool, "do it for me" drafts the content with the embedded AI (deps.AI) and executes the tool through the per-principal MCP plane (automations.InvokeTool) AS THE CALLER — so the action can never exceed the caller's own authorization and is metered + audited like any MCP call.
  • a BLUEPRINT: the full Guide™ playbook (sections, steps, strategies, templates, each carrying an admin `enabled` lever). It is seeded from the embedded fixture into a DB table, then authored LIVE by a SuperAdmin on admin.hanzo.ai; at runtime the DB is authoritative. See blueprint.go for the schema + projection, blueprint_store.go for the seeded/versioned store, admin.go for the CRUD plane.

This file is the pure ENGINE: the checklist schema (JourneyStep / Curriculum), validation, and the dependency/next-step/auto-detect logic — all free functions over plain data, no I/O, so they are exhaustively unit-testable. The engine ALWAYS runs on a Curriculum of ENABLED steps (the projection Blueprint.Curriculum() drops disabled sections/steps and filters their edges), so no enable-logic braids into the engine. Parsing/validation of the full blueprint live in blueprint.go; storage in store.go (per-org) + blueprint_store.go (shared); detectors in detect.go; the agent in agent.go; the HTTP surface in guide.go.

Index

Constants

View Source
const (
	SignalAnalytics      = "analytics"       // the analytics warehouse is emitting for the org
	SignalDeployed       = "deployed"        // the org has a live deployment
	SignalRevenue        = "revenue"         // the org has recorded revenue > 0
	SignalCustomers      = "customers"       // the org's own record count crossed the data-volume threshold
	SignalFunnelVisitors = "funnel:visitors" // the funnel has visitors
	SignalFunnelSignups  = "funnel:signups"  // the funnel has signups
	SignalFunnelOrders   = "funnel:orders"   // the funnel has orders
)

Growth signal names — the observed vocabulary. The fixed (no-parameter) signals are constants; the parameterized families (module:<name>, connected:<provider>, funnel:<stage>, customers:>=<N>) are a KIND plus a param. These strings are the ONE contract the profile emits, the classifier reads, and a curriculum step may name as its done-signal.

Variables

This section is empty.

Functions

func BindSignals

func BindSignals(s Signals)

BindSignals installs the cross-subsystem reads. The composition root (apps/wire_seams.go) calls it once at init, before guide.Mount. Last write wins; guide never mutates it after mount.

func BrandCurriculums

func BrandCurriculums() []string

BrandCurriculums returns the sorted brand names that ship a default blueprint — introspection for a catalog / link-guard test.

func Mount

func Mount(app cloud.Router, deps cloud.Deps) error

Mount wires /v1/guide/* onto app. Complex flavour (a package global for Shutdown + a per-org OrgStore), so it constructs the Service value directly.

func Shutdown

func Shutdown() error

Shutdown closes every cached per-org store and the shared blueprint store. Idempotent.

func Validate

func Validate(c Curriculum) error

Validate enforces the schema invariants the engine relies on: at least one step; unique non-empty ids; dependencies that reference existing ids (no self-edge); a bounded shape; and — the load-bearing one — an ACYCLIC dependency graph, so next-step selection always terminates and can never deadlock on a cycle. Blueprint validation (blueprint.go) reuses this over BOTH the authored step graph and the projected ENABLED journey.

Types

type ActionRecord

type ActionRecord struct {
	ID        string `json:"id"`
	StepID    string `json:"stepId"`
	Tool      string `json:"tool"`
	Args      string `json:"args,omitempty"`
	Result    string `json:"result,omitempty"`
	OK        bool   `json:"ok"`
	Err       string `json:"err,omitempty"`
	CreatedAt int64  `json:"createdAt"`
}

ActionRecord is one Business AI tool execution — the audit-visible ledger row.

type Blog

type Blog struct {
	Slug      string `json:"slug,omitempty"`
	Title     string `json:"title,omitempty"`
	Why       string `json:"why,omitempty"`
	How       string `json:"how,omitempty"`
	CaseStudy string `json:"caseStudy,omitempty"`
}

Blog is a tactic's long-form home: why it works (the principle in prose), how to run it, and a worked caseStudy. slug/title name the post. All fields are optional so a tactic without a blog omits it entirely (Strategy.Blog is nil).

type Blueprint

type Blueprint struct {
	Version    string        `json:"version"`
	Brand      string        `json:"brand,omitempty"`
	Title      string        `json:"title,omitempty"`
	Enabled    *bool         `json:"enabled,omitempty"`
	Principles []Principle   `json:"principles,omitempty"` // the 64-principle spine (Zen of Hanzo archetypes)
	Sections   []Section     `json:"sections,omitempty"`
	Steps      []JourneyStep `json:"steps"`
	Strategies []Strategy    `json:"strategies,omitempty"`
	Templates  []Template    `json:"templates,omitempty"`
}

Blueprint is the full authored playbook. `brand` is the white-label key (""==the global default); `enabled` at the root is the whole-blueprint lever (a disabled blueprint is skipped by resolution, falling through to the next tier). The four arrays are the content. The engine runs on Curriculum() — the enabled projection.

func Parse

func Parse(raw []byte) (Blueprint, error)

Parse decodes a blueprint from YAML or JSON (sigs.k8s.io/yaml accepts both) and validates it. A parse or validation failure returns an error; the caller keeps the previous blueprint (fail-closed — a bad PUT or seed never corrupts the active one).

func (Blueprint) Curriculum

func (b Blueprint) Curriculum() Curriculum

Curriculum projects the blueprint onto the engine's Curriculum: the ENABLED journey. The precedence rule (reconciliation c): a DISABLED section drops ALL its steps; a DISABLED step drops out; and every surviving step's dependencies are filtered to enabled steps only — a dependency on a disabled/absent step is DROPPED, not dangling (the disabled step is treated as resolved, so it never blocks). The result is acyclic and dangling-free by construction, so the engine runs it unchanged.

func (Blueprint) Validate

func (b Blueprint) Validate() error

Validate enforces the blueprint invariants, fail-closed:

  • the AUTHORED step graph is a valid engine curriculum (>=1 step, unique ids, no self/dangling deps, acyclic, bounded) — reuses Validate(Curriculum) over ALL steps, so a dep may reference a disabled step (it exists) but never a missing one;
  • principles have unique non-empty slugs and a bounded count;
  • sections/strategies/templates have unique non-empty ids, bounded counts, and the required content fields; every step's section (if named) refers to a real section, and every strategy's `principle` (if named) files under a real spine principle — referential integrity, so a dangling principle ref is caught fail-closed;
  • the projected ENABLED journey is ALSO a valid engine curriculum — DAG-acyclic + no-dangling over the enabled steps (the explicit requirement). Because the projection filters edges to enabled steps, this can never dangle; the check makes the guarantee real and would catch a projection regression. An all-disabled blueprint projects to an empty journey, which is allowed (resolution skips it).

type BlueprintStore

type BlueprintStore struct {
	// contains filtered or unexported fields
}

BlueprintStore persists brand blueprints as versioned canonical-JSON docs.

func (*BlueprintStore) Close

func (s *BlueprintStore) Close() error

Close closes the underlying database. Idempotent-safe via sql.DB.

func (*BlueprintStore) LatestResolved

func (s *BlueprintStore) LatestResolved(ctx context.Context, brand string) (doc []byte, version int, key string, ok bool, err error)

LatestResolved returns the latest blueprint doc for a deployment's brand, falling back to the base ("") blueprint, and reports WHICH brand key it resolved. Reads and admin writes both key off this SAME resolution, so a deployment authors exactly the row it serves (a brand with its own seeded row edits that row; a brand with none — e.g. "hanzo", which shares the base — edits ""). ok=false only when the DB carries nothing (pre-seed / unreachable), where the caller falls through to the fixture.

func (*BlueprintStore) ListVersions

func (s *BlueprintStore) ListVersions(ctx context.Context, brand string) ([]VersionMeta, error)

ListVersions returns brand's versions, newest first — the PITR/audit trail.

func (*BlueprintStore) SaveVersion

func (s *BlueprintStore) SaveVersion(ctx context.Context, brand string, doc []byte, now int64) (int, error)

SaveVersion appends a new admin version for brand (max version + 1) and returns it. This is the author write — every edit is a new immutable version (point-in-time recovery), stamped source="admin" so SeedOrUpgrade will never clobber it. seed_version is 0 on an admin row (the field is meaningful only for the seed).

func (*BlueprintStore) SeedOrUpgrade

func (s *BlueprintStore) SeedOrUpgrade(ctx context.Context, brand string, doc []byte, seedVersion int, now int64) (SeedAction, error)

SeedOrUpgrade is the VERSION-AWARE seed — the ONE way the embedded fixture reaches the DB. Given the embedded doc + its monotonic seedVersion, it:

  • NEVER touches a brand that carries an admin edit (source="admin") — the hard invariant: any admin edit at any version survives forever (returns SeedNone);
  • seeds version 1 (source="seed", stamped seedVersion) when the brand has no row;
  • UPGRADES the existing UNEDITED seed in place (same version 1) when the embedded seedVersion is strictly newer than the stored one — so a deployment seeded with an older corpus picks up new defaults on redeploy;
  • otherwise is a no-op (the seed is already at this generation).

Idempotent: re-running with the same seedVersion over an already-current seed is SeedNone.

type Curriculum

type Curriculum struct {
	Version string        `json:"version"`
	Title   string        `json:"title,omitempty"`
	Steps   []JourneyStep `json:"steps"`
}

Curriculum is the ENGINE's view: an ordered set of steps plus metadata. It only ever holds ENABLED steps — it is the projection of a Blueprint (Blueprint.Curriculum()), so the next-step/gating logic never has to reason about enablement. Order is authoring order and is the tiebreak the next-step logic walks.

func (Curriculum) Available

func (c Curriculum) Available(states map[string]State, id string) bool

Available reports whether every dependency of id is satisfied.

func (Curriculum) BlockedBy

func (c Curriculum) BlockedBy(states map[string]State, id string) []string

BlockedBy returns the step's dependencies that are NOT yet satisfied (not done/skipped). Empty slice means the step is available.

func (Curriculum) Counts

func (c Curriculum) Counts(states map[string]State) (done, total, percent int)

Counts returns how many steps are done (skipped counts as resolved) and the total, plus an integer percent complete (0..100).

func (Curriculum) Next

func (c Curriculum) Next(states map[string]State) string

Next returns the id of the step the org should tackle next: the FIRST step in authoring order that is neither done nor skipped and whose dependencies are all satisfied. It returns "" when nothing is actionable (all steps terminal, or the only remaining steps are blocked — which, on an acyclic graph, means their blockers are themselves the actionable next steps and get picked first).

type Detector

type Detector func(ctx context.Context, org string, step JourneyStep) (bool, error)

Detector reports whether a step's done-criterion is satisfied by the org's real state. It is the auto-detect seam: a step names a Signal, the engine looks the detector up by that name and runs it. A detector MUST be honest — a data source it cannot reach returns an error (treated as "not present"), never a spurious true.

type Funnel

type Funnel struct {
	Available  bool    `json:"available"`
	WindowDays int     `json:"windowDays"`
	Pageviews  int64   `json:"pageviews"`
	Visitors   int64   `json:"visitors"`
	Signups    int64   `json:"signups"`
	Orders     int64   `json:"orders"`
	Revenue    float64 `json:"revenue"`
}

Funnel is the org's top-of-funnel over the trailing window: traffic → signups → orders, with revenue. Available is false when the warehouse is unreachable or the org has emitted nothing (honest-empty), so the caller renders "turn on analytics" rather than a misleading row of zeros.

type JourneyStep added in v1.801.350

type JourneyStep struct {
	ID      string `json:"id"`
	Section string `json:"section,omitempty"` // the phase (section id) this step groups under
	Title   string `json:"title"`
	Detail  string `json:"detail,omitempty"` // the prose/juncture — what the Guide asks/explains here

	// Dependencies are step ids that must be done/skipped before this step is
	// available. The wire key is `deps` (the blueprint contract); the Go field keeps
	// its descriptive name.
	Dependencies []string `json:"deps,omitempty"`

	// Enabled is the admin on/off lever. A NIL pointer reads as ENABLED (absence ==
	// on): a legacy/org curriculum that omits the field keeps every step, and only an
	// explicit `enabled: false` (an admin disable) drops a step from the journey. See
	// on() in blueprint.go and the Blueprint.Curriculum() projection.
	Enabled *bool `json:"enabled,omitempty"`

	// Signal, when set, names a machine detector (detect.go). When the detector
	// reports the org's real state present, the step auto-marks done.
	Signal string `json:"signal,omitempty"`

	// Tool, when set, is the MCP tool the Business AI runs for "do it for me". Args
	// are its default arguments; Draft is an optional AI prompt whose output fills the
	// DraftInto arg (default "brief").
	Tool      string         `json:"tool,omitempty"`
	Args      map[string]any `json:"args,omitempty"`
	Draft     string         `json:"draft,omitempty"`
	DraftInto string         `json:"draftInto,omitempty"`
}

JourneyStep is one checklist item — one quest of the launch journey. The struct tags are JSON, and sigs.k8s.io/yaml decodes YAML through them — so one tag set is the ONE contract for both the embedded YAML blueprint and a JSON PUT body (DRY: no parallel yaml tags).

The qualifier is load-bearing, not decoration: the type NAME is the schema name in the published document, and that document is ONE flat namespace shared by every app in the fleet. Plain `Step` is already claimed there by apps/marketing (a step of a drip sequence — a different shape entirely), and openapi.Weave refuses one name with two shapes because a generated SDK would bind whichever it read last. Do not "simplify" this back to Step; that is the collision, not a tidier name. The wire is unaffected either way — the JSON keys live on the fields.

type Principle

type Principle struct {
	N         int    `json:"n"`                   // 1..64, the hexagram number + canonical order
	Hexagram  string `json:"hexagram,omitempty"`  // the I-Ching hexagram (pinyin + gloss)
	Slug      string `json:"slug"`                // stable identifier a tactic files under
	Name      string `json:"name,omitempty"`      // the principle's short name
	Principle string `json:"principle,omitempty"` // the actionable growth law
	Change    string `json:"change,omitempty"`    // the Book of Changes reading
	SunTzu    string `json:"sunTzu,omitempty"`    // the Art of War teaching
	Domain    string `json:"domain,omitempty"`    // the growth / go-to-market domain it governs
}

Principle is one of the 64 archetypes of the Zen of Hanzo spine — the fixed backbone a tactic is filed under (Strategy.Principle == Principle.Slug). It weds an I-Ching hexagram to a Sun Tzu teaching and a modern growth domain. The spine is authored DATA (admin.hanzo.ai can see + organize by it), NOT engine logic: nothing in the checklist engine depends on it; it exists so the corpus has a stable, first-principles home.

type Section

type Section struct {
	ID      string `json:"id"`
	Title   string `json:"title"`
	Detail  string `json:"detail,omitempty"`
	Order   int    `json:"order,omitempty"`
	Enabled *bool  `json:"enabled,omitempty"`
}

Section is one ordered phase of the journey. Steps group under a section by id; a DISABLED section drops all of its steps from the journey (the precedence rule).

type SeedAction

type SeedAction string

SeedAction is the outcome of a SeedOrUpgrade call (for logging / test assertions).

const (
	SeedNone     SeedAction = "unchanged" // admin-edited, or seed already current
	SeedInserted SeedAction = "seeded"    // no row existed → fresh seed at version 1
	SeedUpgraded SeedAction = "upgraded"  // unedited seed replaced with a newer generation
)

type SignalSet

type SignalSet map[string]bool

SignalSet is the observed growth facts about ONE org: signal name → present. It is a set of BOOLEANS / thresholds — never a secret, never another org's data. The profile emits it and classifyStage folds it into a Stage.

type Signals

type Signals struct {
	// ModuleInstalled reports whether org has installed the named framework module
	// (framework.ModuleInstalled) — e.g. "cms", "erp".
	ModuleInstalled func(ctx context.Context, org, module string) bool
	// ConnectorPresent reports whether org has connected the named integration
	// provider — a BOOLEAN "is connected" (integrations.Connected), NEVER the token.
	ConnectorPresent func(ctx context.Context, org, provider string) bool
	// HasDeployment reports whether org has a live deployment.
	HasDeployment func(ctx context.Context, org string) (bool, error)
	// RevenueCents is org's OWN recorded revenue-of-record in minor units. The
	// number stays in the org's own profile; the signal it satisfies is revenue > 0.
	RevenueCents func(ctx context.Context, org string) (int64, error)
	// RecordCount is a count of org's OWN business records — the data-volume signal.
	RecordCount func(ctx context.Context, org string) (int64, error)
}

Signals are the org-scoped, cross-subsystem reads the growth observe layer composes. Every func takes the caller's VALIDATED org and reads ONLY that org's state; a signal is a boolean / a threshold / the org's OWN number — never a secret and never another org's data.

type Stage

type Stage string

Stage is an org's observed growth stage — a first-principles ladder from nothing-observed to money-flowing. It is a pure function of the SignalSet.

const (
	StageFormed    Stage = "formed"    // the org exists; nothing else is observed yet
	StageLaunched  Stage = "launched"  // a live presence: a deployment is up or analytics is emitting
	StageActivated Stage = "activated" // real engagement: people are signing up or a book of records exists
	StageScaling   Stage = "scaling"   // money of record: revenue is recorded or orders are completing
)

type State

type State string

State is a step's per-org lifecycle state.

const (
	StateTodo       State = "todo"
	StateInProgress State = "in_progress"
	StateDone       State = "done"
	StateSkipped    State = "skipped"
)

type StateRow

type StateRow struct {
	State     State  `json:"state"`
	Source    string `json:"source,omitempty"`
	Note      string `json:"note,omitempty"`
	UpdatedAt int64  `json:"updatedAt,omitempty"`
}

StateRow is a step's persisted progress.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is one org's guide database. Isolation is PHYSICAL: cloud.OrgStore opens a distinct file per org ({DataDir}/orgs/{org}/guide.db), so there is no org column and no cross-org query is expressible. It holds three tables:

  • progress: one row per step whose state has diverged from the todo default.
  • actions: the Business AI action ledger — every "do it for me" tool call, its args, and its outcome. It is both the audit-visible record and the backing state for the "acted" auto-detect signal.
  • curriculum: at most one row — the org's custom curriculum override (raw doc).

The caller (guide.Mount) opens it through cloud.NewOrgStore; openStore below is the per-org open func. cloud.OrgDB has already applied the WAL/busy pragmas and single-writer bound, so openStore only migrates.

func (*Store) AddAction

func (s *Store) AddAction(ctx context.Context, a ActionRecord) error

AddAction appends an action-ledger row.

func (*Store) ClearCurriculum

func (s *Store) ClearCurriculum(ctx context.Context) error

ClearCurriculum removes the org override, reverting to the built-in default.

func (*Store) Close

func (s *Store) Close() error

Close closes the underlying database. Idempotent-safe via sql.DB.

func (*Store) GetCurriculum

func (s *Store) GetCurriculum(ctx context.Context) ([]byte, bool, error)

GetCurriculum returns the org's custom curriculum doc, or (nil,false) when the org uses the built-in default.

func (*Store) HasSuccessfulAction

func (s *Store) HasSuccessfulAction(ctx context.Context, tool string) (bool, error)

HasSuccessfulAction reports whether a prior "do it for me" call on tool succeeded — the backing predicate for the "acted" auto-detect signal. A successful call means the real, audited effect (e.g. a Content draft) landed in the sibling subsystem, so the step's done-criterion is met.

func (*Store) ListActions

func (s *Store) ListActions(ctx context.Context, limit int) ([]ActionRecord, error)

ListActions returns the most-recent actions first, bounded by limit.

func (*Store) ResetState

func (s *Store) ResetState(ctx context.Context, stepID string) error

ResetState removes a step's row, returning it to the implicit todo default.

func (*Store) SetCurriculum

func (s *Store) SetCurriculum(ctx context.Context, doc []byte, now int64) error

SetCurriculum stores the org's custom curriculum doc (the raw validated source).

func (*Store) SetState

func (s *Store) SetState(ctx context.Context, stepID string, state State, source, note string, now int64) error

SetState upserts a step's state with its source ("manual"|"agent"|"auto") and an optional note.

func (*Store) States

func (s *Store) States(ctx context.Context) (map[string]StateRow, error)

States returns every recorded progress row keyed by step id. Steps with no row are implicitly todo (the engine's stateOf default) — absence is the todo state, so a fresh org needs no seeding.

type Strategy

type Strategy struct {
	ID        string   `json:"id"`
	Principle string   `json:"principle,omitempty"` // the spine slug this tactic files under
	Category  string   `json:"category"`
	Workload  string   `json:"workload,omitempty"`
	Action    string   `json:"action"`
	Tags      []string `json:"tags,omitempty"`
	Source    string   `json:"source,omitempty"` // provenance / attribution
	Era       string   `json:"era,omitempty"`    // modern | heritage
	Blog      *Blog    `json:"blog,omitempty"`   // long-form explainer (nil for un-blogged tactics)
	Enabled   *bool    `json:"enabled,omitempty"`
}

Strategy is one tactic in the recommendation corpus (strategies.go filters it by category/workload and by the org's observed profile via the tag join). `tags` are signal predicates — `stage:<research|launched|activated|scaling>` and `has:<capability>` — that the corpus join maps onto the observe layer's vocabulary. `principle` files the tactic under one of the 64 spine archetypes (Blueprint.Principles); `era` is `modern` (an AI-era tactic) or `heritage` (a classical one); `source` is its provenance; `blog` is its long-form explainer. See the Zen of Hanzo corpus README.

type Template

type Template struct {
	ID      string `json:"id"`
	Title   string `json:"title"`
	Body    string `json:"body"`
	Enabled *bool  `json:"enabled,omitempty"`
}

Template is a reusable prompt/snippet a step references. `body` may carry {placeholder} tokens for client-specific bits ({client_name}, {domain}, {product}).

type VersionMeta

type VersionMeta struct {
	Brand     string `json:"brand"`
	Version   int    `json:"version"`
	UpdatedAt int64  `json:"updatedAt"`
}

VersionMeta is one stored version's metadata (audit / PITR listing — never the full doc, which the GET returns for the active version).

Jump to

Keyboard shortcuts

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