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

app

package
v1.1.0 Latest Latest
Warning

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

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

Documentation

Overview

Package app is the small facade used by mnemon-harness product commands.

It keeps setup/status/validate command code out of declaration and host projection internals without reintroducing the older lifecycle command model.

localboot.go is the LOCAL boot face shared by the TWO local-trust-domain mains: `mnemon-harness local run` and the mnemond local governance daemon (P1 D13). Both resolve the same setup-written config, the same store path, the same endpoint-derived listen address, and the same T1 loopback floor — sunk here (pure move from cmd/mnemon-harness/local.go, behavior unchanged) so the daemon is a true alias of `local run`, never a drifting fork.

Index

Constants

View Source
const LocalNotSetupMessage = "Local Mnemon is not set up.\nRun: mnemon-harness setup --host codex --loop memory --loop skill"

LocalNotSetupMessage is the product remediation for a boot without setup artifacts.

Variables

View Source
var ErrLocalNotSetup = errors.New(LocalNotSetupMessage)

ErrLocalNotSetup is returned when no Local Mnemon config exists under the project root.

Functions

func ImportLocalSyncPull

func ImportLocalSyncPull(storePath, remoteID, nextCursor string, commits []contract.LocalCommit, catalog map[string]capability.Capability) error

ImportLocalSyncPull re-enters pulled remote commits through Event Intake (the import runtime), then advances the durable pull cursor. It drives Ingest/Tick, so it stays on the app side of the boundary (above remotesync's pure store helpers) — never bypassing the kernel. It is the OFFLINE path: it boots its own import runtime by path, so it must never run inside a serving process (the in-process worker drives importPulledCommits over the LIVE runtime instead — flock, v1.1 #2).

func ListenAddrFromEndpoint

func ListenAddrFromEndpoint(endpoint, fallback string) string

ListenAddrFromEndpoint derives the listen address from the setup-written channel endpoint (e.g. "http://127.0.0.1:9001" -> "127.0.0.1:9001"), so a bare `local run` listens where setup pointed the hooks/bindings. An empty/unparsable endpoint falls back to fallback.

func LocalRuntimeConfigFromBindings

func LocalRuntimeConfigFromBindings(bindings []channel.ChannelBinding, catalog map[string]capability.Capability) (runtime.RuntimeConfig, error)

LocalRuntimeConfigFromBindings derives Local Mnemon's policy from the installed Agent Integration bindings alone (enablement = binding scope kinds ∩ catalog; nil = Builtins). It is the bindings-only convenience over the same select-only assembly OpenLocalRuntime uses.

func OpenLocalRuntime

func OpenLocalRuntime(storePath string, loaded channel.LoadedBindings, loops []string, catalog map[string]capability.Capability) (*runtime.Runtime, error)

OpenLocalRuntime boots Local Mnemon over the select-only assembler: loops (from the setup-written localConfig) enable capabilities; bindings stay the source of truth for observe/pull/status scope. An empty loops list (the hidden `local run --bindings` path, which has no localConfig) derives enablement from the binding scope kinds ∩ catalog. catalog selects the capability universe (nil = capability.EmbeddedCatalog()); the serve path passes the boot-resolved external-merged catalog. The assembled policy is then merged with the sync-import half (withSyncImport), so the SERVING runtime can import pulled commits in-process (v1.1 #2) without a second runtime boot.

func OpenSyncImportRuntime

func OpenSyncImportRuntime(storePath string, refs []contract.ResourceRef, catalog map[string]capability.Capability) (*runtime.Runtime, error)

func RemoteWorkspaceStatus

func RemoteWorkspaceStatus(projectRoot string) string

RemoteWorkspaceStatus renders the one-line Remote Workspace banner both local mains (and the status command) print: "not connected" or "connected <remote id>" from remotes.json.

func ReobserveCandidate

func ReobserveCandidate(rt *runtime.Runtime, operator contract.ActorID, escalation InboxRow) error

ReobserveCandidate is the Tower's ONLY write action (P6b): it resolves an INBOX escalation by RE-OBSERVING the underlying candidate as the operator (a control-agent principal) — the action that clears a high-risk operator-gate denial (RiskOperatorGate exempts the control-agent). It is NOT "approve a proposal": no such kernel verb exists, and the wire rejects *.proposed/*.diagnostic. The original observed candidate is recovered from the durable event log by the escalation's CausedBy id and re-emitted through the SAME Ingest path under the operator (so the operator's binding governs it, G9). It fails loud if CausedBy does not name an OBSERVED candidate — the Tower never even attempts to ingest a trusted internal event. Idempotent per escalation (the re-observe ExternalID derives from CausedBy, so re-observing the same escalation twice dedups).

func ResolveLocalStorePath

func ResolveLocalStorePath(root, storePath string) string

ResolveLocalStorePath resolves the effective store path for a --store override ("" = the project-default store under root).

func ResolveProjectPath

func ResolveProjectPath(root, path string) string

ResolveProjectPath resolves path against the project root (absolute paths pass through cleaned).

func RunLocalHTTPServerWithBindings

func RunLocalHTTPServerWithBindings(ctx context.Context, addr, storePath string, loaded channel.LoadedBindings, opts ServeOptions, out io.Writer) error

RunLocalHTTPServerWithBindings serves Local Mnemon from a binding manifest. It is the product boot path used by `mnemon-harness local run`. When opts.Hosts is non-empty it co-hosts the Background Driver (plan 3.4): one goroutine in the SAME process — never a second store opener — driving Tick + DrainOutbox and re-projecting each recorded host's managed definition files when an invalidation drained. A driver error stops the driver (logged to stderr); the hot path serves on.

func RunSyncWorker

func RunSyncWorker(ctx context.Context, rt *runtime.Runtime, opts SyncWorkerOptions, errw io.Writer)

RunSyncWorker loops one sync pass on its own cadence until ctx cancels. Every pass error is logged to errw and SWALLOWED — an unreachable remote degrades sync, never the serve path (I13: the local loop stays fully functional offline; the bounded client keeps each pass finite).

func SyncImportCatalog

func SyncImportCatalog(projectRoot string, errw io.Writer) map[string]capability.Capability

SyncImportCatalog resolves the capability catalog the OFFLINE `sync pull` verb derives its import rules from (descriptor-derived, PD6): the embedded first-party catalog plus every external package under <projectRoot>/.mnemon/loops, so a remote commit of an external importable kind imports the same way the in-process worker imports it. Unlike serve boot, the manual pull verb degrades to the embedded catalog (with a stderr warning) when an external package is unreadable — a corrupt loop must not block importing first-party memory/skill commits.

func SyncImportRuntimeConfig

func SyncImportRuntimeConfig(refs []contract.ResourceRef, catalog map[string]capability.Capability) runtime.RuntimeConfig

SyncImportRuntimeConfig is the sync-import policy, fully descriptor-derived (PD6): one import rule per importable capability (each selecting its declared closed-set merge strategy), kernel authority for exactly the importable kinds, and a guard registering each importable kind's required header onto the governance base. The skipped-kind deny rule (v1.1 #4) keeps any OTHER pulled kind a durable diagnostic instead of a silent drop — the same rule set withSyncImport merges into the serving runtime, so the offline and in-process import paths share one policy. catalog selects the importable universe (nil = embedded first-party).

func ValidateListenAddr

func ValidateListenAddr(addr string, allowNonLoopback bool) error

ValidateListenAddr fail-closes a non-loopback listen address unless explicitly allowed: the local control plane is a same-machine governance boundary (T1) — binding 0.0.0.0 or a LAN address silently exposes the channel beyond it.

Types

type AgentRow

type AgentRow struct {
	Principal contract.ActorID
	Kind      contract.ActorKind
}

AgentRow is one bound principal on the field.

type AssignmentRow

type AssignmentRow struct {
	Scope    string
	Assignee string
	TTL      string
}

AssignmentRow is one live assignment (who's doing what, with its lease TTL).

type CapabilityInfo

type CapabilityInfo struct {
	Name         string   `json:"name"`
	Kind         string   `json:"kind"`
	ObservedType string   `json:"observed_type"`
	ProposedType string   `json:"proposed_type"`
	ItemsField   string   `json:"items_field"`
	Required     []string `json:"required"`
	Importable   bool     `json:"importable"`
	Merge        string   `json:"merge,omitempty"`
	Source       string   `json:"source"` // "embedded" (first-party) | "external" (.mnemon/loops package)
}

CapabilityInfo is the read-only view of a resolved capability — the discoverability answer to "what kinds can the agents work with and what does each expect" (P2). It is a projection of the descriptor (capability.Capability), never the runtime's internal rule state: the runtime is capability-free by design (PD6c), so this query resolves the project catalog from disk rather than coupling the kernel to capability shapes.

type FieldPage

type FieldPage struct {
	Agents      []AgentRow
	Assignments []AssignmentRow
	Diagnostics int // open escalations (the INBOX count, surfaced on FIELD too)
}

FieldPage answers "场上谁在干什么": the agents on the field (enumerated from the BindingSet — the only existing "who's on the field" source), the live assignments (scope/assignee/lease TTL), and the open-escalation count. There is deliberately NO "● active / ○ idle" liveness or "evt/h" rate: the data model has no heartbeat/last-seen concept, and inventing one would be a new kernel concept (T1 veto, folded from the adversarial review).

type GoalPage

type GoalPage struct {
	Statements []string // project_intent items' statements
	Progress   []string // progress_digest items' summaries
}

GoalPage answers "目标怎么样了": the project_intent statements (the goal) and the progress_digest summaries. "readiness" is shown as the ACTUAL progress entries — a fabricated percentage would need a KR data model that does not exist, and inventing one would be a new kernel concept (T1 veto).

type Harness

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

Harness is the facade handle. It carries the project root and constructs inner stores per operation, mirroring the original per-command behavior.

func New

func New(root string) *Harness

New returns a facade bound to the given project root ("." for the cwd).

func (*Harness) LoopAdd

func (h *Harness) LoopAdd(srcDir string) (string, error)

LoopAdd registers an external capability package from srcDir into the project's external loop root (<root>/.mnemon/loops/<name>). It is the "write a directory -> register it" front door (P2 minimal onboarding): the author writes a package dir, `loop add` places it under the canonical name and validates it through the SAME fail-closed boot resolution `local run` uses (capability.ResolveCatalog — symlink screen + LoadExternal + four-axis shadowing merge). A package that would refuse boot is rejected here and the copy is rolled back, so a half-added package never lingers. The canonical name is the spec's `name` (the external loader requires the directory name to equal it); an existing target is NOT overwritten (remove it first to replace). Returns the registered name.

func (*Harness) LoopCapabilities

func (h *Harness) LoopCapabilities() ([]CapabilityInfo, error)

LoopCapabilities resolves the project catalog (embedded first-party + every external package under .mnemon/loops, via the SAME fail-closed boot resolution) and returns one CapabilityInfo per kind, sorted by kind. It is a LOCAL read — no running server is contacted; the catalog is a disk fact.

func (*Harness) LoopProject

func (h *Harness) LoopProject(ctx context.Context, out, errw io.Writer, action, projectRoot, host string, loops, hostArgs []string) error

LoopProject runs the product projector action against a supported host runtime, streaming host output to out/errw.

func (*Harness) LoopSchema

func (h *Harness) LoopSchema(kind string) (CapabilityInfo, error)

LoopSchema returns the CapabilityInfo for one resource kind (the `control schema --type T` answer), resolved from the same project catalog. An unknown kind is an error (fail-closed — never an empty success that reads as "no required fields").

func (*Harness) LoopValidate

func (h *Harness) LoopValidate() ([]string, error)

LoopValidate validates the embedded harness loop/host/binding manifests unconditionally, then — when root names an external tree carrying its own loops/hosts/bindings — validates that too (the union). A root with no harness assets (the common case, including the repo root after the assets moved under internal/assets) contributes nothing, so the validation passes.

func (*Harness) Refresh

func (h *Harness) Refresh(ctx context.Context, out, errw io.Writer, projectRoot, host string, loops, hostArgs []string) ([]string, error)

Refresh re-projects the managed definition files (GUIDE, hooks, skill defs) for a host loop under the no-clobber policy: a definition file the user has edited is preserved and reported, never overwritten. It does NOT touch the channel (bindings, token, config) — only the Agent Workspace projection. It returns the display paths it preserved.

func (*Harness) RenderObserveSkill

func (h *Harness) RenderObserveSkill() (string, error)

RenderObserveSkill generates the mnemon-observe skill (decision F: a directory-level generated skill). The judgment half is hand-written (observeSkillJudgment); the mechanism half — which kinds this project enables and the event type to observe for each — is RENDERED from the resolved catalog, so the skill never drifts from the live capability set and never hardcodes per-kind fields (it points the agent at `loop schema` for those). It is the generic counterpart to per-loop skills: one skill teaches recording an observation for ANY kind.

func (*Harness) Setup

func (h *Harness) Setup(ctx context.Context, out, errw io.Writer, opts SetupOptions) (SetupResult, error)

Setup projects the selected loops into the host and writes the Local Mnemon channel artifacts. On DryRun it prints every projection + channel change without writing.

func (*Harness) SetupStatus

func (h *Harness) SetupStatus(projectRoot, principal string) ([]string, error)

SetupStatus reports the public setup state without exposing local transport details. Debug/internal commands can inspect binding files directly.

func (*Harness) SetupUninstall

func (h *Harness) SetupUninstall(ctx context.Context, out, errw io.Writer, opts SetupOptions) error

SetupUninstall reverses setup: it removes projected loop assets and the principal's channel binding + token file while preserving sibling bindings.

type InboxPage

type InboxPage struct {
	Escalations []InboxRow
}

InboxPage answers "什么需要我": the open escalations — high-risk/denied candidates surfaced as diagnostics. The operator acts by RE-OBSERVING the underlying candidate as a control-agent (P6b), not by "approving a proposal" (no such kernel verb). CausedBy links each escalation to its triggering candidate event.

type InboxRow

type InboxRow struct {
	Domain   string // the kind domain (e.g. "loopdef", "assignment")
	Actor    contract.ActorID
	Stage    string
	Reason   string
	CausedBy string // the triggering candidate event ID (the re-observation target, P6b)
}

InboxRow is one escalation (a durable diagnostic) awaiting operator attention.

type LedgerPage

type LedgerPage struct {
	Decisions []LedgerRow
}

LedgerPage answers "什么已经定了": the accepted decisions, newest last (append order).

type LedgerRow

type LedgerRow struct {
	DecisionID string
	Actor      contract.ActorID
	AppliedAt  string
	Refs       []contract.ResourceRef
}

LedgerRow is one accepted decision with its attribution (the proposer + what it changed).

type LocalBoot

type LocalBoot struct {
	Configured bool
	StorePath  string
	Loaded     channel.LoadedBindings
	Config     LocalConfig
}

LocalBoot is the resolved boot state both local mains serve from.

func ResolveLocalBoot

func ResolveLocalBoot(root, storePath, bindingsPath string) (LocalBoot, error)

ResolveLocalBoot resolves the boot state from the cleaned project root plus the two operator overrides: storePath (the --store flag; "" = config/default discovery) and bindingsPath (the hidden --bindings flag; "" = setup-config-driven discovery).

type LocalConfig

type LocalConfig struct {
	SchemaVersion int                 `json:"schema_version"`
	Mode          string              `json:"mode"`
	Endpoint      string              `json:"endpoint"`
	Principal     string              `json:"principal"`
	Loops         []string            `json:"loops"`
	Hosts         map[string][]string `json:"hosts"`       // per-host projected loops; absent on old installs (no background re-projection)
	MirrorMode    string              `json:"mirror_mode"` // "manual" | "prime-refresh"; absent defaults to prime-refresh
	BindingFile   string              `json:"binding_file"`
	StorePath     string              `json:"store_path"`
}

LocalConfig mirrors the setup-written .mnemon/harness/local/config.json document.

func ReadLocalConfig

func ReadLocalConfig(root string) (LocalConfig, error)

ReadLocalConfig reads + validates the setup-written Local Mnemon config under root.

type ServeOptions

type ServeOptions struct {
	Loops          []string
	Hosts          map[string][]string
	ProjectRoot    string
	MirrorMode     string // "manual" | "prime-refresh" (driver-side mirror regeneration gate)
	IgnoreExternal bool   // boot the embedded-only catalog, naming each ignored external package on stderr
	// AllowInsecureRemote is the sync worker's T2 downgrade override (v1.1 #3): permit a plaintext
	// non-loopback remote endpoint. Default false — fail closed.
	AllowInsecureRemote bool
	SyncInterval        time.Duration // sync worker cadence; <= 0 = default (30s)
}

ServeOptions carries the boot-config state the serve path needs beyond bindings: capability enablement (Loops), the per-host projected loops (Hosts — the background driver's re-projection authority), and the project root the host surfaces live under.

type SetupOptions

type SetupOptions struct {
	Host          string   // host runtime id, e.g. "codex"
	Loops         []string // loops to project, e.g. ["memory"]
	ControlURL    string   // channel endpoint, e.g. "http://127.0.0.1:8787"
	Principal     string   // authenticated principal, e.g. "codex@project"
	ActorKind     string   // "host-agent" (default) or "control-agent"
	UseToken      bool     // generate + reference a bearer token file (vs trusted-header auth)
	TokenExplicit bool     // true when the caller explicitly set UseToken
	ProjectRoot   string   // host projection working dir (defaults to the facade root)
	DryRun        bool     // print all projection + channel changes without writing
}

SetupOptions configures the `mnemon-harness setup` front door: project a loop into a host runtime AND wire the channel (binding entry + optional token + runtime env), so a host agent reaches the governed control plane through one channel.

type SetupResult

type SetupResult struct {
	BindingFile string
	TokenFile   string
	EnvFile     string
	ConfigFile  string
	Changes     []string
}

SetupResult records the channel artifact paths setup wrote (or would write, on dry-run).

type SyncWorkerOptions

type SyncWorkerOptions struct {
	ProjectRoot         string
	Interval            time.Duration // <= 0 defaults to defaultSyncWorkerInterval
	Timeout             time.Duration // per-call transport bound; <= 0 defaults to channel.DefaultSyncTimeout
	AllowInsecureRemote bool          // explicit T2 downgrade override (v1.1 #3)
	// Catalog is the boot-resolved capability catalog the pull import derives its kind→observation
	// mapping from (descriptor-derived, PD6). nil falls back to the embedded first-party catalog.
	Catalog map[string]capability.Capability
}

SyncWorkerOptions configures the worker. The zero value is safe: default cadence and transport timeout, fail-closed transport security.

type TowerView

type TowerView struct {
	Goal   GoalPage
	Field  FieldPage
	Inbox  InboxPage
	Ledger LedgerPage
}

TowerView is the read-only, operator-wide projection that backs the Agent Control Tower's four pages (P6). The app-layer facade assembles it from the *Runtime's read surfaces; the ui package renders a TowerView and never touches the store/kernel (the ui↛store law). Zero new kernel concepts — every field maps to an existing protocol object (§3.1). READ-ONLY: building a view never writes or Ticks.

func BuildTowerView

func BuildTowerView(rt *runtime.Runtime, bindings []channel.ChannelBinding) (TowerView, error)

BuildTowerView assembles the read-only Tower projection from the runtime + the BindingSet. It performs only resource reads, the read-only DecisionLedger, and an event-log scan — never a write or a Tick (G10/T5). The bindings supply the FIELD "who's on the field" enumeration (the only existing source); the ui package renders the result and never touches the store (ui↛store).

Jump to

Keyboard shortcuts

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