Documentation
¶
Overview ¶
Package guide mounts the Hanzo Cloud /v1/guide/* surface: 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 (Step / 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
- func BindSignals(s Signals)
- func BrandCurriculums() []string
- func Mount(app cloud.Router, deps cloud.Deps) error
- func Shutdown() error
- func Validate(c Curriculum) error
- type ActionRecord
- type Blog
- type Blueprint
- type BlueprintStore
- func (s *BlueprintStore) Close() error
- func (s *BlueprintStore) LatestResolved(ctx context.Context, brand string) (doc []byte, version int, key string, ok bool, err error)
- func (s *BlueprintStore) ListVersions(ctx context.Context, brand string) ([]VersionMeta, error)
- func (s *BlueprintStore) SaveVersion(ctx context.Context, brand string, doc []byte, now int64) (int, error)
- func (s *BlueprintStore) SeedOrUpgrade(ctx context.Context, brand string, doc []byte, seedVersion int, now int64) (SeedAction, error)
- type Curriculum
- type Detector
- type Funnel
- type Principle
- type Section
- type SeedAction
- type SignalSet
- type Signals
- type Stage
- type State
- type StateRow
- type Step
- type Store
- func (s *Store) AddAction(ctx context.Context, a ActionRecord) error
- func (s *Store) ClearCurriculum(ctx context.Context) error
- func (s *Store) Close() error
- func (s *Store) GetCurriculum(ctx context.Context) ([]byte, bool, error)
- func (s *Store) HasSuccessfulAction(ctx context.Context, tool string) (bool, error)
- func (s *Store) ListActions(ctx context.Context, limit int) ([]ActionRecord, error)
- func (s *Store) ResetState(ctx context.Context, stepID string) error
- func (s *Store) SetCurriculum(ctx context.Context, doc []byte, now int64) error
- func (s *Store) SetState(ctx context.Context, stepID string, state State, source, note string, ...) error
- func (s *Store) States(ctx context.Context) (map[string]StateRow, error)
- type Strategy
- type Template
- type VersionMeta
Constants ¶
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 ¶
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 []Step `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 ¶
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 ¶
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 []Step `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 ¶
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 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 ¶
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 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 Step ¶
type Step 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"`
}
Step is one checklist item. 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).
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 ¶
ClearCurriculum removes the org override, reverting to the built-in default.
func (*Store) GetCurriculum ¶
GetCurriculum returns the org's custom curriculum doc, or (nil,false) when the org uses the built-in default.
func (*Store) HasSuccessfulAction ¶
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 ¶
ListActions returns the most-recent actions first, bounded by limit.
func (*Store) ResetState ¶
ResetState removes a step's row, returning it to the implicit todo default.
func (*Store) SetCurriculum ¶
SetCurriculum stores the org's custom curriculum doc (the raw validated source).
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).