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
- Variables
- func ImportLocalSyncPull(storePath, remoteID, nextCursor string, commits []contract.LocalCommit, ...) error
- func ListenAddrFromEndpoint(endpoint, fallback string) string
- func LocalRuntimeConfigFromBindings(bindings []channel.ChannelBinding, catalog map[string]capability.Capability) (runtime.RuntimeConfig, error)
- func OpenLocalRuntime(storePath string, loaded channel.LoadedBindings, loops []string, ...) (*runtime.Runtime, error)
- func OpenSyncImportRuntime(storePath string, refs []contract.ResourceRef, ...) (*runtime.Runtime, error)
- func RemoteWorkspaceStatus(projectRoot string) string
- func ReobserveCandidate(rt *runtime.Runtime, operator contract.ActorID, escalation InboxRow) error
- func ResolveLocalStorePath(root, storePath string) string
- func ResolveProjectPath(root, path string) string
- func RunLocalHTTPServerWithBindings(ctx context.Context, addr, storePath string, loaded channel.LoadedBindings, ...) error
- func RunSyncWorker(ctx context.Context, rt *runtime.Runtime, opts SyncWorkerOptions, ...)
- func SyncImportCatalog(projectRoot string, errw io.Writer) map[string]capability.Capability
- func SyncImportRuntimeConfig(refs []contract.ResourceRef, catalog map[string]capability.Capability) runtime.RuntimeConfig
- func ValidateListenAddr(addr string, allowNonLoopback bool) error
- type AgentRow
- type AssignmentRow
- type CapabilityInfo
- type FieldPage
- type GoalPage
- type Harness
- func (h *Harness) LoopAdd(srcDir string) (string, error)
- func (h *Harness) LoopCapabilities() ([]CapabilityInfo, error)
- func (h *Harness) LoopProject(ctx context.Context, out, errw io.Writer, action, projectRoot, host string, ...) error
- func (h *Harness) LoopSchema(kind string) (CapabilityInfo, error)
- func (h *Harness) LoopValidate() ([]string, error)
- func (h *Harness) Refresh(ctx context.Context, out, errw io.Writer, projectRoot, host string, ...) ([]string, error)
- func (h *Harness) RenderObserveSkill() (string, error)
- func (h *Harness) Setup(ctx context.Context, out, errw io.Writer, opts SetupOptions) (SetupResult, error)
- func (h *Harness) SetupStatus(projectRoot, principal string) ([]string, error)
- func (h *Harness) SetupUninstall(ctx context.Context, out, errw io.Writer, opts SetupOptions) error
- type InboxPage
- type InboxRow
- type LedgerPage
- type LedgerRow
- type LocalBoot
- type LocalConfig
- type ServeOptions
- type SetupOptions
- type SetupResult
- type SyncWorkerOptions
- type TowerView
Constants ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
ResolveLocalStorePath resolves the effective store path for a --store override ("" = the project-default store under root).
func ResolveProjectPath ¶
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 ¶
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 AssignmentRow ¶
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 (*Harness) LoopAdd ¶
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 ¶
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 ¶
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 ¶
SetupStatus reports the public setup state without exposing local transport details. Debug/internal commands can inspect binding files directly.
func (*Harness) SetupUninstall ¶
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 ¶
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 ¶
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).