Documentation
¶
Overview ¶
Category vocabulary (sty_b2315e17): controlled type list as embedded default + satelle.toml override. Values live in substrate/config/categories.toml (embedded data, not a Go literal). Enforcement phase is config-driven: off | warn | reject (default warn — upgrade never hard-breaks existing flows).
Package config is satelle's trimmed TOML configuration, ported from satellites' internal/cliconfig with the server/token/credstore surface dropped (the OSS tier is always local — no remote dispatch). What remains:
- Repo-root resolution: walk up from CWD for .satelle/satelle.toml.
- Defaults for every setting — a repo with no satelle.toml runs zero-config.
- A gitignored satelle.local.toml overlay beside the committed config.
- [substrate_roots]: per-kind authored-markdown dirs, which MAY live outside .satelle/ (the directory monitor watches whatever these point at).
- data_dir / db: where the per-repo sqlite database lives.
globalagents.go — the machine-wide agent PROFILE catalog at $SATELLE_HOME/agents.toml (sty_c7dfeedf / epic:agent-configuration-operability).
The catalog holds named, provider-neutral EXECUTION profiles so an operator working across several repositories updates a Claude/Grok/Codex/other definition once. It holds NO workflow policy: a profile cannot name a skill, a gate, a state, an applies_to, or anything else that decides PROCESS. That boundary is mechanical, not advisory — the loader accepts only the known binding keys and refuses everything else, naming policy keys specifically. Workflows stay repo substrate (see the satelle-repo-agnostic principle and the constitution's configuration-over-code rule).
A repo consumes a profile only by naming it: `profile = "<name>"` on a binding in .satelle/agents.toml, or — opt-in — through the catalog's [roles] defaults. There is no implicit same-name merge, so adding a profile called "reviewer" can never silently change a repo that never asked for it.
migrate.go — surgical format-migration for operator-owned agents.toml (epic:substrate-convergence order:7). harness= → command=; expand bare claude|grok presets to full templates; add missing role=. Comment-preserving line edits only — never a TOML marshal round-trip.
resolveagents.go — the deterministic precedence engine that folds the machine-wide profile catalog into a repo's agents layer (sty_c7dfeedf).
The ladder, highest first:
- repo — an inline value on the binding in .satelle/agents.toml
- profile — the catalog profile the binding EXPLICITLY names via profile= (and, transitively, whatever that profile extends)
- global-role — the catalog's [roles] default for the binding's role, and ONLY when the repo opted in with [defaults] use_global_roles
- embedded — the binary's safe fallback (DefaultExecutorCommand, DefaultReviewerCommand, DefaultReviewerTools), applied as today by the *Binding resolvers
The critical negative: tier 2 and tier 3 are reached only by an explicit reference. A catalog profile named "reviewer" and a repo [reviewer] that never mentions it DO NOT merge — the repo resolves byte-identically with and without the catalog present. Every convenience that would blur this is deliberately absent, because a machine-wide file that can silently re-point a pinned repo's reviewer is exactly the failure this design exists to prevent.
Resolution returns the same AgentsConfig shape everything downstream already consumes, plus a Provenance side-table, so no dispatch or gate code changes.
runtime.go — home-keyed runtime plane (epic:substrate-planes / sty_4660bbe1).
Authored substrate stays under <repo>/.satelle (ResolveDataDir). Runtime state — the per-repo DB, logs, backups, and the stories attachment cache — lives under ~/.satelle/<repo-key>/ (ResolveRuntimeDir). One repo-key maps to one isolated dir (never a flat multi-repo bag); see decision-local-db-placement and decision-substrate-planes-local-first.
Index ¶
- Constants
- Variables
- func ClearGlobalHostedServer() error
- func ConfigAreaLocation(cfg Config, repoRoot, area string) (location string, isDir bool)
- func CurrentInstanceID() string
- func DocumentFiles(cfg Config, repoRoot string) (Bundle, Scope, error)
- func EmbeddedCategories() []string
- func EmbeddedCategoriesErr() error
- func EnsureGrokFolderTrusted(repoRoot string) (changed bool, abs string, err error)
- func ExpandVars(s string, vars map[string]string) (string, error)
- func ExpandVarsInSettings(settings map[string]any, vars map[string]string) (map[string]any, error)
- func FileShared(scope Scope, frontmatter string) bool
- func FindDataDir(start string) (string, bool)
- func GlobalAgentsPath() string
- func GlobalConfigPath() string
- func GlobalDir() string
- func GrantsContextChannel(tools string) bool
- func GrokConfigPath() (string, error)
- func GrokFolderTrusted(content, abs string) bool
- func GrokTrustedFoldersPath() (string, error)
- func HasKey(content, section, key string) bool
- func HostedServerFor(gc GlobalConfig, repo Config) string
- func InstanceID(globalDir string) string
- func IsInLoopCommand(cmd string) bool
- func IsRuntimeKeyDir(name string) bool
- func LayerVars(global, repo map[string]string) map[string]string
- func ListStringValues(content, section, key string) []string
- func ListValueContains(content, section, key, val string) bool
- func LocalOnlyPath(p string) bool
- func MigrateAgents(content string) (out string, changes []string, err error)
- func ReadRepoPathMarker(runtimeDir string) string
- func RemoveKey(content, section, key string) string
- func RepoKey(repoRoot string) string
- func RepoRootFromConfigPath(configPath string) string
- func ResolveAgents(repo AgentsConfig, global GlobalAgentsConfig) (AgentsConfig, Provenance, error)
- func ResolveHostedServer(repo Config) string
- func ResolvedRole(section string, b AgentBinding) string
- func RoleInferred(b AgentBinding) bool
- func SafeCurrentInstanceID() string
- func SaveConfigValues(configPath string, edits []KeyEdit) error
- func SaveGlobal(gc GlobalConfig) error
- func SaveGlobalHostedServer(server string) error
- func ServerEndpointEnv() (endpoint string, disabled bool, set bool)
- func SettingDisplay(cfg Config, s Setting) string
- func SettingIDs() []string
- func ShellGrantToken(tools string) string
- func SourceGlobalRole(profile string) string
- func SourceProfile(profile string) string
- func StarterGlobalAgents(cli string) (string, error)
- func UpsertKey(content, section, key, value string) string
- func WriteRepoPathMarker(runtimeDir, repoRoot string) error
- type ActiveWorkspace
- type AgentBinding
- func (b AgentBinding) CommandTemplate() string
- func (b AgentBinding) InjectsPrinciples() bool
- func (b AgentBinding) IsACP() bool
- func (b AgentBinding) ResolvedInterface() string
- func (b AgentBinding) ResolvedPrinciples() string
- func (b AgentBinding) TimeoutDuration(def time.Duration) (time.Duration, error)
- type AgentConfig
- type AgentsConfig
- type AgentsDefaults
- type BackupConfig
- type Bundle
- type CategoriesConfig
- type Config
- func (c Config) CanonicaliseCategory(cat string) (string, error)
- func (c Config) CanonicaliseTags(tags []string) ([]string, error)
- func (c Config) CategoryEnforce() string
- func (c Config) CategoryWarning(cat string) string
- func (c Config) LegacyRuntimeNote(repoRoot string) string
- func (c Config) ResolveActiveWorkspace() ActiveWorkspace
- func (c Config) ResolveAuthoredDirs(repoRoot string) map[string]string
- func (c Config) ResolveCategoryVocabulary() []string
- func (c Config) ResolveConstitution(repoRoot string) string
- func (c Config) ResolveDB(repoRoot string) string
- func (c Config) ResolveDataDir(repoRoot string) string
- func (c Config) ResolveEditExemptPaths(repoRoot string) []string
- func (c Config) ResolveLogLevel() string
- func (c Config) ResolveLogsMaxFiles() int
- func (c Config) ResolveLogsMaxSizeBytes() int64
- func (c Config) ResolveRuntimeDB(repoRoot string) string
- func (c Config) ResolveRuntimeDir(repoRoot string) RuntimeResolution
- func (c Config) ResolveWebPort() int
- type ConfigFile
- type ConfigTier
- type EffectiveAgents
- type EmbeddedDefault
- type GateConfig
- type GlobalAgentsConfig
- type GlobalConfig
- type GlobalHostedConfig
- type HostedConfig
- type KeyEdit
- type Provenance
- type ReviewConfig
- type RuntimeResolution
- type Scope
- type ServerConfig
- type ServiceConfig
- type Setting
- type TagsConfig
- type UIConfig
- type WorkspaceConfig
Constants ¶
const ( AgentsConfigName = "agents.toml" ActorsConfigName = "actors.toml" )
AgentsConfigName is the per-repo agents-binding file, beside satelle.toml under the data dir (.satelle/agents.toml). ActorsConfigName is the now-removed legacy filename — it is no longer loaded (sty_7db2ed7d); `satelle reindex` warns a repo still carrying it so the rename is enforced rather than silently honoured.
const ( DefaultExecutorCommand = "in-loop" // DefaultReviewerCommand is the full claude command template (the canonical // seed for a [reviewer] that omits command). It carries the read-only // --disallowedTools denylist so the grant is a ceiling. A repo overrides it // with its own multi-token command template in agents.toml. Bare single-token // presets are no longer accepted on the agents.toml path. DefaultReviewerCommand = agentcli.DefaultClaudeCommand DefaultReviewerTools = "Read,Grep,Glob" )
Default agent grants — the BOOTSTRAP values a binding's empty fields resolve to: the executor drives in-loop (the agent itself); the reviewer runs as an isolated agent with a READ-ONLY tool grant (see the satelle-agent-model principle — the reviewer is limited to reviewing). They fill blanks INSIDE a loaded agents.toml; they are not a substitute for the file itself — an initialized repo without a loadable agents.toml refuses to run (the CLI bootstrap's requireAgents, sty_d0d6bb67).
const ( RoleReviewer = "reviewer" RoleAgent = "agent" )
Role values for AgentBinding.Role — the declared identity of a binding (sty_fc670c9b / epic:agent-invoke-unify). Role is orthogonal to the section name: judge-vs-perform is derived from role, not from the literal "reviewer".
const ( InterfaceCommand = "command" InterfaceACP = "acp" // Dispatch marker environment keys identify an isolated performing step to // harness hooks. They let the dispatched child use its authored tool grant // during the transition's in-flight window without opening that window to // the parent driving session. They are an honest-posture boundary, not a // sandbox: a process that bypasses or spoofs hooks is outside this contract. DispatchAgentEnv = "SATELLE_DISPATCH_AGENT" DispatchStepEnv = "SATELLE_DISPATCH_STEP" DispatchItemEnv = "SATELLE_DISPATCH_ITEM" )
Interface values for AgentBinding.Interface — how satelle runs the isolated worker subprocess (epic:agent-dispatch-transport). Orthogonal to role: command = full argv template (default; any CLI including Claude); acp = Agent Client Protocol over stdio (spawn line only; satelle is client).
const ( PrinciplesSession = "session" PrinciplesAll = "all" PrinciplesSystem = "system" PrinciplesProject = "project" PrinciplesNone = "none" )
Principles selector tokens for AgentBinding.Principles — which principles ride in an isolated agent's briefing (design sty_69fd4e20 §5).
const ( // DefaultDataDir is the per-repo home for satelle's data stores — // <repo>/.satelle. A relative data_dir resolves under the repo root. DefaultDataDir = ".satelle" // DefaultDBName is the sqlite database file inside data_dir. DefaultDBName = "satelle.db" // DefaultConstitutionName is the project constitution file at the data-dir // root (.satelle/constitution.md) — the order-zero doc injected every session // (epic:session-context). It lives OUTSIDE the authored-kind dirs, so it is // read directly, not indexed as a kind. DefaultConstitutionName = "constitution.md" // DefaultWebPort is the local web server's listen port. DefaultWebPort = 8787 // DefaultLogLevel is arbor's level when log_level is unset. DefaultLogLevel = "info" // DefaultLogsMaxSizeKB caps each flat evidence log (.satelle/logs/*.log) before // it rolls; DefaultLogsMaxFiles bounds how many rotations are kept. Daily // rolling is always on regardless of these (sty_a67e6e8c). DefaultLogsMaxSizeKB = 5120 // 5 MiB DefaultLogsMaxFiles = 7 // ConfigName / LocalConfigName are the committed config and its gitignored // per-user overlay, both under <repo>/.satelle/. ConfigName = "satelle.toml" LocalConfigName = "satelle.local.toml" )
Defaults applied when a key is unset. Every setting has one, so a repo with an empty (or absent) satelle.toml runs on defaults for all keys.
const ( // GlobalConfigName is the global config filename under the global dir. GlobalConfigName = "config.toml" // DefaultServiceAddr is the bind address for the background service. Unlike // the transient `serve` default (127.0.0.1), the service defaults to all // interfaces so it is reachable across the WSL↔Windows boundary in any // networking mode. Restrict it to 127.0.0.1 in config to keep it off the LAN. DefaultServiceAddr = "0.0.0.0" // EnvServerEndpoint overrides [server] endpoint discovery and push // (sty_5aa08259). Values: // // unset — use config / auto-bootstrap probe as usual // URL — use that base URL; no default-port probe // none|off|-|"" — disable discovery AND push (hermetic tests set this) // // It lives here, not in internal/cli, because the serve-side mirror // reconciler honours the same off-switch and cannot import cli // (sty_e6e467fe). EnvServerEndpoint = "SATELLE_SERVER_ENDPOINT" )
Global config lives under ~/.satelle/ — the one machine-wide touchpoint the spec reserves (the future workspace registry lives here too). It is kept in a file named config.toml, deliberately NOT satelle.toml, so the per-repo walk-up (which looks for .satelle/satelle.toml) can never mistake the global home for a repo root.
const ( // GlobalAgentsName is the profile catalog's filename under GlobalDir(). // It shares agents.toml's basename deliberately — same shape, different // scope — and can never be confused with a repo file: the per-repo walk-up // only ever looks under .satelle/. GlobalAgentsName = "agents.toml" // GlobalAgentsLabel is the stable, path-independent name used in validation // messages so an error reads the same on every machine (and in tests, where // SATELLE_HOME is a temp dir). GlobalAgentsLabel = "~/.satelle/agents.toml" )
const ( SourceRepo = "repo" SourceEmbedded = "embedded" )
Source labels recorded in Provenance. Profile and role sources carry the profile name, so a display surface names WHICH profile a field came from.
const ( // WorkspaceKindPersonal is the caller's own workspace (the default). WorkspaceKindPersonal = "personal" // WorkspaceKindTeam is a shared/team workspace multiple members sync to. WorkspaceKindTeam = "team" )
Workspace kind values — the two kinds the hosted server returns (a sync "shared" SCOPE is a tier on the sync.go ladder, NOT a workspace kind; keep the two enums distinct).
const DefaultAgentCLI = "claude"
DefaultAgentCLI is the agent CLI the reviewer/summariser shell out to when none is selected — claude, whose flag surface satelle's runner mirrors.
const GrokConfigName = "config.toml"
GrokConfigName is Grok's user-level config filename under ~/.grok. [compat.*] lives only here (not project-scoped) — see Grok docs on harness compatibility.
const GrokTrustedFoldersName = "trusted_folders.toml"
GrokTrustedFoldersName is Grok's folder-trust store under ~/.grok. Project hooks under <repo>/.grok/hooks/ load only when the folder is trusted here (same file /hooks-trust writes).
const OperatingPrinciple = "satelle-agent-goals"
OperatingPrinciple is the one tight operating principle — injected at session start (it ships carrying the principles:session residency marker) and guaranteed into every reviewer's context, so the agent (and the reviewer) is driven to the result. Residency is otherwise authored via the principles:session marker (see internal/cli hook + internal/agentstep): a principle is session because it is tagged, or on-demand (the default) because it is not — never auto-injected (sty_53a4233c). Its content is overridable by a repo (.satelle/principles).
const PersonalWorkspace = "personal"
PersonalWorkspace is the zero-config active workspace: every developer's own workspace. The default when [hosted] workspace is unset — nothing to configure for the common "sync to my own area" case. A login records nothing for it.
const RepoPathMarkerName = "repo.path"
RepoPathMarkerName is the file written into a runtime dir recording the absolute repo root that owns it (sty_c36c211f AC2 — forward resolution).
Variables ¶
var AuthoredKinds = []string{"documents", "workflows", "principles", "skills"}
AuthoredKinds are the markdown-source-of-truth artifact kinds the directory monitor watches and indexes. Each defaults to <repo>/.satelle/<kind> and is individually relocatable via [substrate_roots] (may be outside .satelle/).
var ConfigAreas = []string{"workflows", "principles", "skills", "constitution", "agents", "tasks", "settings"}
ConfigAreas are the .satelle areas eligible for the versioned config store (epic:scoped-sync, order:5): the authored kinds MINUS documents (which is its own sync kind), plus the project constitution, the agents layer, and task DEFINITIONS. The work-state areas (stories/ledger/executions) are order:7; documents is its own kind. Local-scope areas are skipped at push time — this list is the candidate set the walk resolves a tier for.
var ErrNotFound = errors.New("config: not found")
ErrNotFound signals no satelle.toml was found walking up from CWD. Callers fall back to the zero-value Config (zero-config still works).
var RuntimeKeyDirPattern = regexp.MustCompile(`^[^/]+-[0-9a-f]{8}$`)
RuntimeKeyDirPattern matches a home-keyed runtime directory name (`<sanitised-basename>-<sha256[:8]>`). Shared by CLI list and the integration host-surface guard (sty_c36c211f).
var Settings = []Setting{ {Key: "web_port", Label: "Web port", Help: "Applies on the next serve start.", Kind: kindInt}, {Key: "log_level", Label: "Log level", Help: "debug | info | warn | error", Kind: kindEnum, Enum: []string{"debug", "info", "warn", "error"}}, {Key: "data_dir", Label: "Data dir", Help: "Authored substrate home under the repo (default .satelle). Runtime state is home-keyed separately. Applies on the next serve start.", Kind: kindString}, {Key: "db", Label: "Database path", Help: "Override for the per-repo local ledger; default is ~/.satelle/<repo-key>/satelle.db (one project per DB). Applies on the next serve start.", Kind: kindString}, {Key: "substrate_roots", Label: "Substrate roots", Help: "Authored-kind → parent dir overrides.", Kind: kindMap}, {Key: "logs_max_size_kb", Label: "Log rotation size (KB)", Help: "Per evidence log before it rolls.", Kind: kindInt}, {Key: "logs_max_files", Label: "Log files kept", Help: "Rotations retained.", Kind: kindInt}, {Key: "stories_keep_closed", Label: "Keep closed stories (count)", Help: "0 = no count pruning.", Kind: kindInt}, {Key: "stories_keep_days", Label: "Keep closed stories (days)", Help: "0 = no age pruning.", Kind: kindInt}, {Section: "review", Key: "gate_create", Label: "Gate create", Help: "Run structure + create_review on story/task create (default on at init).", Kind: kindBool}, {Section: "gate", Key: "edit_exempt_paths", Label: "Edit-gate exempt paths", Help: "Path prefixes exempt from the engaged-story edit gate. Init seeds .satelle/ (authored substrate) and .gitignore (satelle-managed output).", Kind: kindList}, {Section: "gate", Key: "allow_outside_tree_edits", Label: "Allow outside-tree edits", Help: "Opt in to Bash/Edit mutations in another repo's working tree. Non-repo paths are never fenced. Default deny; only for a deliberate multi-repo install.", Kind: kindBool}, {Section: "hosted", Key: "project", Label: "Hosted project", Help: "Project slug this repo maps to (personal sync target).", Kind: kindString}, {Section: "hosted", Key: "workspace", Label: "Active workspace", Help: "Scoped-sync destination — personal default; a team-workspace name elects it.", Kind: kindString}, }
Settings is the ordered repo-key whitelist. The order and Label/Help strings are shared with the web settings page, so it renders identically.
var SyncAreas = buildSyncAreas()
SyncAreas is the canonical, de-duplicated list of .satelle area names a sync table may configure: the AuthoredKinds (documents/workflows/ principles/skills) plus the remaining epic-named areas — the project constitution, the agents layer, tasks, and the work-state areas (stories, ledger, executions). The reserved key syncAllKey ("all") is NOT a member — it only defaults areas that fall through unset (see ScopeFor).
Functions ¶
func ClearGlobalHostedServer ¶ added in v0.0.123
func ClearGlobalHostedServer() error
ClearGlobalHostedServer removes the machine-wide hosted-server binding (the "remove server" action), leaving the other sections intact. Separate from SaveGlobalHostedServer, which guards against an empty URL.
func ConfigAreaLocation ¶ added in v0.0.153
ConfigAreaLocation resolves a config or documents area's on-disk location. isDir reports whether location is a directory to walk (vs. a single file to read directly). It mirrors the cli syncAreaPath resolution but lives in config so the walk is unit-testable without the local store. documents is resolved here too so DocumentFiles can reuse the same path helper.
func CurrentInstanceID ¶ added in v0.0.292
func CurrentInstanceID() string
CurrentInstanceID is InstanceID(GlobalDir()).
func DocumentFiles ¶ added in v0.0.160
DocumentFiles walks the documents area under repoRoot with the same scope/ tier labeling as ConfigFiles (local skip; SharedTier marks publish eligibility only — sync destination remains personal). The returned Scope is the area's resolved scope so the CLI can print a clear "scope=local, skipping" message instead of an ambiguous empty list. Documents is its own sync kind (order:6) — not part of ConfigAreas. Paths matching LocalOnlyPath are withheld.
func EmbeddedCategories ¶ added in v0.0.328
func EmbeddedCategories() []string
EmbeddedCategories returns the default category list from the embedded substrate file. Parsed once. Empty on parse failure (tests assert it succeeds).
func EmbeddedCategoriesErr ¶ added in v0.0.328
func EmbeddedCategoriesErr() error
EmbeddedCategoriesErr is non-nil when the embedded file failed to load/parse. Exposed for tests; production call sites use the list (empty on failure).
func EnsureGrokFolderTrusted ¶ added in v0.0.167
EnsureGrokFolderTrusted records abs(repoRoot) as trusted in ~/.grok/trusted_folders.toml so Grok loads project hooks for that folder. Only that path is written; other folder entries are preserved. Idempotent: already-trusted leaves the file bytes unchanged (changed=false).
Returns changed, absolute path written, error.
func ExpandVars ¶ added in v0.0.129
ExpandVars replaces every ${NAME} in s with vars[NAME]. An unknown NAME is an error naming the missing var — never a silent empty, so a mistyped or absent key fails loudly rather than dispatching an agent with a blank credential. There is no escape syntax: a literal ${...} has no use case in an env value here.
func ExpandVarsInSettings ¶ added in v0.0.149
ExpandVarsInSettings walks settings (map/slice/string leaves, as decoded from TOML) and ${NAME}-expands every string leaf against vars via ExpandVars — the same fail-fast unknown-var error, so a binding's settings table (mirroring claude's settings.local.json shape verbatim) resolves its secrets identically to Env. Non-string leaves (bool, int64, float64) pass through unchanged.
func FileShared ¶ added in v0.0.144
FileShared reports whether a file's frontmatter marks it shared. frontmatter is the file's raw body (or just its leading `---`-fenced YAML block) — either works, since only the leading fence is scanned. The flag is only meaningful inside a personal-scope area (AC2): local and shared areas apply uniformly to every file they hold, so any other scope reports false without inspecting frontmatter at all.
func FindDataDir ¶ added in v0.0.372
resolvePath finds the committed config: an explicit path, then the SATELLE_CONFIG env, then walking up from CWD for .satelle/satelle.toml. Returns "" (no error) when none is found. FindDataDir walks up from start (empty → CWD) for a `.satelle` DIRECTORY and returns its path and whether one was found. This is satelle's one notion of "this repo is initialised", and it deliberately keys on the DIRECTORY rather than on `.satelle/satelle.toml`: zero-config is supported, so an initialised repo may legitimately carry agents.toml and no satelle.toml, and keying on the toml would classify it as ungoverned.
resolvePath above walks for the CONFIG file and answers a different question ("which config governs here"), so the two intentionally coexist.
func GlobalAgentsPath ¶ added in v0.0.365
func GlobalAgentsPath() string
GlobalAgentsPath returns the path to the machine-wide profile catalog.
func GlobalConfigPath ¶
func GlobalConfigPath() string
GlobalConfigPath returns the path to the global config file.
func GlobalDir ¶
func GlobalDir() string
GlobalDir returns the machine-wide satelle home (~/.satelle), honoring the SATELLE_HOME override (used by tests). Falls back to ".satelle-global" in CWD only if the home directory cannot be resolved.
Under `go test` (testing.Testing()), resolving without SATELLE_HOME panics instead of writing the developer's real ~/.satelle (sty_c36c211f). That is the enforcement seam for every package: tests must call testutil.IsolateHome (or set SATELLE_HOME themselves). We import testing for testing.Testing() — available since go 1.22; this module is go 1.26 — rather than an Args[0] heuristic, so the signal is accurate for both `go test` and `go test -c`.
func GrantsContextChannel ¶ added in v0.0.360
GrantsContextChannel reports whether a binding's tool grant gives a DISPATCHED agent a context channel — the pull-context contract (sty_47d31300). A dispatched performer starts with no conversation history and reconstructs its context by PULLING the story, its documents, and the ledger. Two channels satisfy it (sty_565a0202):
- satelle CLI via shell: `Bash(satelle…)`, broad `Bash`/`Bash(*)`, or `*`.
- Disk reads of story documents under the home-keyed runtime plane (~/.satelle/<repo-key>/stories/<id>/) via the grok-native `read_file` tool (used when headless Grok cannot enable run_terminal_command). The in-repo `.satelle/stories/` path is obsolete (sty_58fa970e).
A grant with neither channel leaves the agent silently context-starved, so dispatch refuses it loudly. Claude-only `Read` (without Bash) is intentionally NOT accepted: the Claude pull path is the satelle CLI, not a disk-first rubric.
REVIEWERS need no channel — satelle injects the attachments into the transition payload's docs array and reviewer bindings never reach dispatch. A shell grant on a reviewer is therefore unused capability, not a requirement.
This is the SINGLE owner of the rule (sty_87c0ef37): the runtime dispatch refusal and `satelle agent validate` both call it, so they cannot disagree about any grant string. It carries its own quote-stripping tokenizer rather than reusing splitList so that a quoted TOML token ("Bash(satelle:*)") is judged identically on both paths.
func GrokConfigPath ¶ added in v0.0.163
GrokConfigPath returns ~/.grok/config.toml. os.UserHomeDir honors HOME, so tests can t.Setenv("HOME", tmp) without a satelle-specific override.
func GrokFolderTrusted ¶ added in v0.0.167
GrokFolderTrusted reports whether content already marks abs as trusted=true.
func GrokTrustedFoldersPath ¶ added in v0.0.167
GrokTrustedFoldersPath returns ~/.grok/trusted_folders.toml (HOME-honoring).
func HasKey ¶ added in v0.0.217
HasKey reports whether content already assigns `key` inside `section` (empty section = root block before the first table header). Distinguishes "key absent from the file" from "key present with empty value" — Load cannot (sty_c73f8905 AC6 config reconciliation).
func HostedServerFor ¶ added in v0.0.121
func HostedServerFor(gc GlobalConfig, repo Config) string
HostedServerFor applies the precedence rule: the global hosted server wins; the repo's committed [hosted] server is the read-only fallback for a repo that predates the global binding. Both are normalized so a "https://h/" value can never mismatch the credential-store key "https://h". Returns "" when neither is set.
func InstanceID ¶ added in v0.0.292
InstanceID returns a stable short identity for a satelle home directory (GlobalDir). Serve exposes it on /healthz as X-Satelle-Instance so CLI auto-bootstrap only seeds a serve that belongs to the same home (sty_5aa08259 / epic:mirror-hygiene) — hermetic SATELLE_HOME runs cannot silently adopt the operator's live :8787 serve.
func IsInLoopCommand ¶ added in v0.0.360
IsInLoopCommand reports whether a binding command is the in-loop preset (single token "in-loop", case-insensitive). An in-loop binding is performed by the driving session and never dispatched as a child, so it needs no context channel and cannot produce an isolated verdict. Empty command is NOT in-loop: tests and bootstrap leave command blank and wire a runner directly.
func IsRuntimeKeyDir ¶ added in v0.0.268
IsRuntimeKeyDir reports whether name is a home-keyed runtime key segment.
func LayerVars ¶ added in v0.0.365
LayerVars returns the ${NAME} KV with the machine-wide catalog as the base and the repo's own [vars] (including its gitignored satelle.local.toml overlay) winning per key — the same per-key overlay shape the repo already uses for its own layers. Neither map is mutated.
func ListStringValues ¶ added in v0.0.272
ListStringValues returns the quoted string entries of a TOML array assignment for key inside section, in order. Returns nil when the key is absent or the RHS is not a bracketed list. Only double-quoted string elements are collected (the shape init seeds for edit_exempt_paths).
func ListValueContains ¶ added in v0.0.272
ListValueContains reports whether content's assignment of key inside section is a TOML string array that includes val (exact match after unquoting). False when the key/section is absent, the value is not a list of strings, or val is not among the quoted entries. Used by init analysis / migrate to detect a missing managed default entry without re-encoding the whole file.
func LocalOnlyPath ¶ added in v0.0.329
LocalOnlyPath reports whether a path is one that must NEVER be transmitted: any path segment carrying a `.local` dot-component (satelle.local.toml, notes.local.md, secrets.local/keys.md). Unconditional — it takes no Config, so no setting can disable or narrow it. The restore-side counterpart is subsync.ExcludedLocal (which delegates here for the .local case).
A bare segment named "local" (skills/local/x.md) is an ordinary name and is not withheld; the match requires a multi-component segment whose non-stem component equals "local" (case-insensitive).
func MigrateAgents ¶ added in v0.0.205
MigrateAgents rewrites format drift in an agents.toml body:
- harness = X → command = X (skipped when command= already present)
- bare command = "claude"|"grok" → full multi-token template
- missing role= → role = "reviewer"|"agent" via ResolvedRole
Returns the migrated content and a list of change notes. When nothing is stale, out equals content byte-for-byte and changes is empty (idempotent). A parse error returns err; callers must leave the on-disk file intact.
func ReadRepoPathMarker ¶ added in v0.0.268
ReadRepoPathMarker returns the recorded repo root for a runtime dir, or "" if absent.
func RemoveKey ¶ added in v0.0.330
RemoveKey drops a single `key = …` assignment inside section, leaving every other line (including comments) verbatim. A missing section or key is a no-op. Used by redactForTransmit to strip [hosted] project from a settings push (sty_ea18294f) without a full TOML re-encode.
func RepoKey ¶ added in v0.0.261
RepoKey returns a stable directory key for repoRoot under GlobalDir(). Multiple worktrees / cwd forms of the same repository hash identically.
Derivation:
- Prefer git identity via `git rev-parse --git-common-dir` (worktree-collapsing: a linked worktree returns the main repo's .git).
- Fall back to symlink-resolved absolute path for non-git trees.
The key is `<sanitised-basename>-<sha256[:8]>` so `ls ~/.satelle` stays legible while collisions stay improbable. Never reads os.Getwd(); the root is an argument.
Note: a tree that gains a .git later (init → git init) can flip identity. Callers that need continuity across that flip should use ResolveRuntimeDir, which prefers an already-existing runtime dir under either key.
func RepoRootFromConfigPath ¶
RepoRootFromConfigPath derives the repo root (the dir holding .satelle/) from a <repo>/.satelle/satelle.toml path. Empty path → "." (CWD).
func ResolveAgents ¶ added in v0.0.365
func ResolveAgents(repo AgentsConfig, global GlobalAgentsConfig) (AgentsConfig, Provenance, error)
ResolveAgents folds the catalog into the repo layer and returns the effective agents config with its per-field provenance. A missing referenced profile, a reference cycle, or a repo/profile role conflict is an error — resolution never half-applies a broken reference.
func ResolveHostedServer ¶ added in v0.0.121
ResolveHostedServer is the convenience resolver for callers that hold only the repo config: it loads the global config (a malformed global degrades to the repo fallback so a render or read command never fails on it) and applies the precedence rule. The login WRITE path calls LoadGlobal directly so it can still surface a malformed-global error.
func ResolvedRole ¶ added in v0.0.189
func ResolvedRole(section string, b AgentBinding) string
ResolvedRole returns the binding's effective role: the declared Role when set to reviewer|agent (case-insensitive), else inferred from the section name (section "reviewer" → reviewer, otherwise agent). section is the agents.toml table name ([reviewer], [planner], …).
func RoleInferred ¶ added in v0.0.189
func RoleInferred(b AgentBinding) bool
RoleInferred reports whether role was not declared and will be inferred from the section name — used by agent validate/show to warn the operator to declare it.
func SafeCurrentInstanceID ¶ added in v0.0.292
func SafeCurrentInstanceID() string
SafeCurrentInstanceID is CurrentInstanceID but returns "" under go test when SATELLE_HOME is unset — so NewMirror in unit tests does not panic on GlobalDir's isolation fence (sty_c36c211f / sty_5aa08259).
func SaveConfigValues ¶ added in v0.0.115
SaveConfigValues applies edits to the committed config at configPath, creating the file if absent. Each key is upserted in place; unrelated content is preserved. An empty configPath falls back to ./<DefaultDataDir>/<ConfigName>.
func SaveGlobal ¶
func SaveGlobal(gc GlobalConfig) error
SaveGlobal writes the global config to ~/.satelle/config.toml, creating the dir as needed. It renders a documented template (a fixed, satelle-managed shape) from the resolved values rather than re-encoding, so the file stays readable and self-explanatory.
func SaveGlobalHostedServer ¶ added in v0.0.121
SaveGlobalHostedServer records the machine-wide hosted-server URL in the global config, preserving the other sections (load-modify-save is correct here because SaveGlobal re-renders the whole managed template). It is the global analogue of the retired repo-file SaveHostedServer; `satelle login` calls it so one sign-in serves every repo. Tokens never pass through here — they go to the credential store.
func ServerEndpointEnv ¶ added in v0.0.368
ServerEndpointEnv inspects EnvServerEndpoint. disabled=true means discovery and push must not run. When disabled is false and endpoint is non-empty, callers should use it instead of config / probe.
func SettingDisplay ¶ added in v0.0.125
SettingDisplay returns the resolved value of a key as a display string (empty for an unset value; a bool renders "true"/"false"; a list/map renders one item per line). It is the single display entry point shared by the web page and the CLI.
func SettingIDs ¶ added in v0.0.125
func SettingIDs() []string
SettingIDs returns every settable repo key's FieldID, in schema order (for help and unknown-key errors).
func ShellGrantToken ¶ added in v0.0.360
ShellGrantToken returns the first token in a tool grant that confers shell access, or "" when none does. It is a REPORTING helper — it names which token a finding is about — and is deliberately not the channel decision, which GrantsContextChannel alone owns.
func SourceGlobalRole ¶ added in v0.0.365
SourceGlobalRole labels a field won by the catalog's opt-in [roles] default.
func SourceProfile ¶ added in v0.0.365
SourceProfile labels a field won by an explicitly referenced catalog profile.
func StarterGlobalAgents ¶ added in v0.0.365
StarterGlobalAgents renders a documented starter catalog derived from the machine's selected agent CLI (`~/.satelle/config.toml [agent] cli`) — the backward-compatible migration path for an installation that predates the catalog (AC6). It is CONTENT ONLY: no repo is touched, no repo value is read, and no secret is embedded. Secrets stay in [vars] on this machine and reach a profile only as a ${NAME} reference, expanded in memory at dispatch wiring.
The generated catalog changes nothing on its own: a repo picks it up only by writing `profile = "<name>"`.
func UpsertKey ¶ added in v0.0.115
UpsertKey sets `key = value` within section (empty section = the root block before the first table header), replacing an existing assignment in place, appending the key to an existing section, or appending a new [section]. Comments and unmodeled keys/tables are preserved.
func WriteRepoPathMarker ¶ added in v0.0.268
WriteRepoPathMarker records repoRoot into runtimeDir/repo.path so `satelle runtime list` can reverse-map keys. Best-effort: no-op when runtimeDir is empty or equals the repo's data dir (legacy layout).
Types ¶
type ActiveWorkspace ¶ added in v0.0.152
ActiveWorkspace is the workspace the sync engine routes personal+shared areas through. Name is the server workspace handle (the workspace name) the sync engine prefixes; Kind is personal (the default) or team.
func (ActiveWorkspace) IsPersonal ¶ added in v0.0.152
func (a ActiveWorkspace) IsPersonal() bool
IsPersonal reports whether the active workspace is the developer's own — the zero-config default, needing no binding.
type AgentBinding ¶ added in v0.0.12
type AgentBinding struct {
// Interface is "command" | "acp". Empty means command. Unknown values fail at load.
Interface string `toml:"interface"`
// Command is the agent's spawn/template string.
// command transport: multi-token full argv template with {system}/{tools}/
// {model}/{settings}/{payload} (each its own argv token). Bare single-token
// only "in-loop"; bare claude/grok/codex rejected by agentvalidate.
// acp transport: ACP stdio spawn only (e.g. "grok agent stdio") — no
// {system}/{payload} placeholders (those ride the protocol).
// Prefer over retired harness= (no runtime fallback; MigrateAgents rewrites).
Command string `toml:"command"`
// Harness is retired: still decoded for MigrateAgents; CommandTemplate ignores it.
Harness string `toml:"harness"`
Tools string `toml:"tools"`
Model string `toml:"model"`
// Role is "reviewer" | "agent". Empty means inferred from the section name
// (see ResolvedRole). The binary's only hard determination for a reviewer is
// the verdict contract; tool grant/model/command are user configuration.
Role string `toml:"role"`
// Principles selects which principles inject into the isolated briefing:
// "session" (default) | "all" | "system" | "project" | "none" | comma-list.
// Empty defaults to session. inject_principles is retired (MigrateAgents).
Principles string `toml:"principles"`
// Env sets environment variables on the dispatched agent's process (layered
// onto os.Environ, binding keys winning). Each value may reference the [vars]
// KV via ${NAME}, resolved at CLI wiring time (ResolveAgentEnvs) — how a step
// points at an alternate model backend, e.g. env = { ANTHROPIC_BASE_URL =
// "https://api.z.ai/api/anthropic", ANTHROPIC_AUTH_TOKEN = "${GLM_API_KEY}" }.
// The in-loop executor never execs a child, so its Env is inert (sty_001558ce).
Env map[string]string `toml:"env"`
// Timeout bounds ONE dispatch of this binding — a Go duration string (e.g.
// "45m"). Empty inherits the engine's default dispatch bound. A from-scratch
// code-writing worker needs a longer window than the 20m default (a real
// feature was SIGKILLed at exactly 20m — sty_b73c3236), so the bound is authored
// config, not a compiled constant (sty_446c38b7). Applies to a DISPATCHED named
// executor; reviewer/summariser gate invocations keep the engine's agent bound.
Timeout string `toml:"timeout"`
// InjectPrinciples is retired (MigrateAgents → principles=). Not used at runtime.
InjectPrinciples *bool `toml:"inject_principles"`
// Settings MIRRORS claude's settings.local.json schema (env, model, permissions)
// verbatim — no derivation, no satelle-specific shape. It is materialised into
// the {settings} placeholder: ${VAR}-resolved (ResolveAgentEnvs, fail-fast on an
// unknown var, same as Env), JSON-marshalled, and passed INLINE as
// `--settings <json>`. Because --settings is CLI-tier it OVERRIDES
// .claude/settings.local.json, so a binding that authors Settings becomes the
// authoritative provider/auth/permissions layer for that dispatch — e.g. moving
// [retrospective]'s GLM env under settings.env fixes its silent clobber by the
// repo's openrouter settings.local.json (that env block wins over a bare
// process-env overlay, but never over --settings). A binding with no Settings
// emits no --settings flag, exactly as an empty Model drops {model}.
Settings map[string]any `toml:"settings"`
// Effort is optional reasoning/thinking effort for the binding (sty_657f77b9):
// e.g. "low" | "medium" | "high". Empty means peer default. Substituted into
// {effort} on command templates (flag dropped when empty, like {model}) and
// applied on ACP via session/set_config_option (failure-tolerant).
Effort string `toml:"effort"`
// Secondary names another agents.toml binding to retry once when this
// binding's dispatch fails with a classified rate-limit/unavailable error
// (sty_5bf61f89). Empty inherits [defaults] secondary. Empty both = no failover.
Secondary string `toml:"secondary"`
// Profile names a profile in the MACHINE-WIDE catalog ($SATELLE_HOME/
// agents.toml) this binding builds on (sty_c7dfeedf). The reference is always
// EXPLICIT: a catalog profile that happens to share this section's name is
// never merged in on its own, so adding a profile can never silently change a
// repo. Repo values on this binding win over the referenced profile field by
// field; role is identity and must not disagree. A profile may itself set
// profile= to extend another, with cycles refused at load. See ResolveAgents.
Profile string `toml:"profile"`
}
AgentBinding binds one agent to a backend (how/where it runs) and its grant (the tool allowance, and an optional model). Empty fields take the defaults.
Role declares whether the binding is a reviewer (verdict contract) or an agent (performer). Principles declares which principles inject into the isolated briefing. InjectPrinciples is the DEPRECATED boolean alias for Principles (true→session, false→none); Principles wins when both are set.
Interface selects the dispatch transport (epic:agent-dispatch-transport): "command" (default) or "acp". Shared grant fields apply to both; spawn shape differs.
func (AgentBinding) CommandTemplate ¶ added in v0.0.180
func (b AgentBinding) CommandTemplate() string
CommandTemplate resolves the effective command template for this binding. Only `command` is read — the deprecated `harness` field is no longer a runtime fallback (breaking surface: run `satelle init` to MigrateAgents).
func (AgentBinding) InjectsPrinciples ¶ added in v0.0.18
func (b AgentBinding) InjectsPrinciples() bool
InjectsPrinciples reports whether this binding injects any principles (and the constitution order-zero block) into the isolated agent's context — true unless the resolved selector is none.
func (AgentBinding) IsACP ¶ added in v0.0.275
func (b AgentBinding) IsACP() bool
IsACP reports whether this binding uses the ACP transport.
func (AgentBinding) ResolvedInterface ¶ added in v0.0.275
func (b AgentBinding) ResolvedInterface() string
ResolvedInterface returns the effective dispatch transport: command (default) or acp. Unknown non-empty values are returned lowercased so LoadAgents / agentvalidate can reject them (epic:agent-dispatch-transport).
func (AgentBinding) ResolvedPrinciples ¶ added in v0.0.189
func (b AgentBinding) ResolvedPrinciples() string
ResolvedPrinciples returns the principles selector: Principles when set, else session. The deprecated inject_principles field is no longer a runtime fallback (breaking surface: run `satelle init` to MigrateAgents).
func (AgentBinding) TimeoutDuration ¶ added in v0.0.139
TimeoutDuration resolves this binding's dispatch bound: the parsed Timeout when set, else def. A malformed or non-positive Timeout is an error — LoadAgents validates it at load (validateTimeouts) so a dispatch never silently falls back on a typo (sty_446c38b7).
type AgentConfig ¶ added in v0.0.6
type AgentConfig struct {
// CLI is the agent CLI identifier (claude | codex). Empty resolves to
// DefaultAgentCLI.
CLI string `toml:"cli"`
}
AgentConfig selects the headless agent CLI the quality-management spine uses for isolated reviews/summaries. Set once at install (`satelle agent`).
func (AgentConfig) ResolveCLI ¶ added in v0.0.6
func (a AgentConfig) ResolveCLI() string
ResolveCLI returns the selected agent CLI, defaulting when unset.
type AgentsConfig ¶ added in v0.0.12
type AgentsConfig struct {
Defaults AgentsDefaults `toml:"defaults"`
Executor AgentBinding `toml:"executor"`
Reviewer AgentBinding `toml:"reviewer"`
Agents map[string]AgentBinding `toml:"agents"`
}
AgentsConfig is the on-disk shape at .satelle/agents.toml — the agents layer. Every field is optional; the *Binding resolvers supply today's defaults, so the zero value (and an absent file) is the current behaviour. Agents holds OPTIONAL named agents (beyond the executor/reviewer roles) declared as flat top-level [<name>] sections — consistent with [executor]/[reviewer] — or the legacy nested [agents.<name>] (still read for back-compat). A workflow node may allocate a step to one, and a named agent is ALWAYS isolated (see satelle-agent-model). LoadAgents does the classification; the toml tag here is retained only for the legacy nested form.
func LoadAgents ¶ added in v0.0.12
func LoadAgents(dataDir string) (AgentsConfig, error)
LoadAgents reads the agents layer from <dataDir>/agents.toml. The legacy actors.toml is no longer read (sty_7db2ed7d); an absent agents.toml yields the zero AgentsConfig — defaults via the *Binding resolvers — and a nil error. Absence is judged by the CALLER: the CLI bootstrap treats a missing file in an initialized repo as broken and refuses to run (requireAgents, sty_d0d6bb67); pre-init surfaces (nothing to load yet) keep the zero-config bootstrap.
func ResolveAgentEnvs ¶ added in v0.0.129
func ResolveAgentEnvs(a AgentsConfig, vars map[string]string) (AgentsConfig, error)
ResolveAgentEnvs returns a copy of the agents layer with every binding's Env AND Settings values ${NAME}-expanded against vars (executor, reviewer, and each named agent). Resolution happens ONCE, at CLI wiring time (fail-fast, the repo's established style): a broken ${VAR} in any binding refuses the command with an actionable message rather than sitting undetected until a dispatch. A binding with no Env/Settings is a no-op, so a zero-config repo stays zero-config. The error names the binding section and key but NEVER the value (values may be secrets — kept out of all evidence, logs, and errors).
func (AgentsConfig) ExecutorBinding ¶ added in v0.0.12
func (a AgentsConfig) ExecutorBinding() AgentBinding
ExecutorBinding resolves the executor agent's backend, defaulting to in-loop (the driving agent itself).
func (AgentsConfig) NamedBinding ¶ added in v0.0.12
func (a AgentsConfig) NamedBinding(name string) (AgentBinding, bool)
NamedBinding resolves an optional named agent declared as a flat top-level [<name>] section (or the legacy nested [agents.<name>]). ok is false when none is declared, so a workflow node that allocates a step to an absent agent degrades gracefully to the in-loop executor. A named agent is always isolated; an unset command defaults to the isolated claude preset.
func (AgentsConfig) ResolveSecondary ¶ added in v0.0.294
func (a AgentsConfig) ResolveSecondary(section string, b AgentBinding) (sec AgentBinding, name string, ok bool)
ResolveSecondary returns the fallback binding for section/b when secondary is configured (per-binding wins over [defaults] secondary). ok is false when unconfigured or the named binding is missing (sty_5bf61f89).
func (AgentsConfig) ReviewerBinding ¶ added in v0.0.12
func (a AgentsConfig) ReviewerBinding() AgentBinding
ReviewerBinding resolves the reviewer agent's backend and grant, defaulting to an isolated agent with the read-only tool grant. The grant travels with the binding, so the reviewer's read-only limit holds whatever the backend.
type AgentsDefaults ¶ added in v0.0.294
type AgentsDefaults struct {
// Secondary is the default fallback binding name for isolated agents when
// a binding omits secondary= and the primary hits rate-limit/unavailable.
Secondary string `toml:"secondary"`
// UseGlobalRoles opts this repo into the machine-wide catalog's [roles]
// defaults for bindings that name no profile= of their own (sty_c7dfeedf).
// It is off by default and must be written by hand: without it, tier 3 of the
// precedence ladder is skipped entirely and the catalog can only reach a
// binding that explicitly asks for it.
UseGlobalRoles bool `toml:"use_global_roles"`
}
AgentsDefaults is the optional [defaults] table in agents.toml (sty_5bf61f89).
type BackupConfig ¶ added in v0.0.230
type BackupConfig struct {
// LocalOnly means keep backups under .satelle/backups/ only and do not emit
// the "enable online backup" advisory. Prefer satelle.local.toml for a
// per-developer choice.
LocalOnly bool `toml:"local_only"`
// Hosted opts into pushing pre-images to the bound project's personal
// documents partition (path backups/<rel>). Default false: that partition
// is also listed by documents pull, and backups/ is a restore exclusion —
// auto-push used to permanently wedge sync (sty_84f14ace). Operators who
// set hosted = true re-introduce poison into the partition but, post-
// unwedge, pull only skips those paths rather than failing the whole sync.
Hosted bool `toml:"hosted"`
}
BackupConfig is the operator policy for pre-mutation substrate backups (sty_873a5380, sty_84f14ace). Local floor is always on; this tunes the advisory and the opt-in hosted documents push.
type Bundle ¶ added in v0.0.329
type Bundle struct {
Files []ConfigFile
Withheld []string // sorted, server-relative
}
Bundle is an assembled set of files destined for transmission, plus the server-relative paths withheld by LocalOnlyPath so a caller can REPORT them. Work-state push transmits DB rows (no on-disk filename), so it is out of scope for this exclusion by deliberate decision — not a gap.
func ConfigFiles ¶ added in v0.0.153
ConfigFiles walks the ConfigAreas under repoRoot, resolving each file's scope via ScopeFor. Local-scope areas contribute nothing. Non-local files are labeled PersonalTier or SharedTier for operator messaging / publish eligibility — both still sync to the personal workspace only (epic:sync-publish). A shared-scope area is SharedTier wholesale; a personal-scope area is PersonalTier per file, promoted to SharedTier when FileShared (frontmatter shared:true). Reserved generated views (index.md/log.md/README) are excluded — they are not authored. Paths matching LocalOnlyPath are withheld (listed in Bundle.Withheld, never in Files). Files are returned sorted by Path. A non-existent area on disk is benign (nothing to push yet). An explicitly invalid scope is a hard error.
type CategoriesConfig ¶ added in v0.0.328
type CategoriesConfig struct {
// Vocabulary, when non-empty, REPLACES the embedded default list entirely.
Vocabulary []string `toml:"vocabulary"`
// Extra appends to the resolved base (embedded or Vocabulary).
Extra []string `toml:"extra"`
// Enforce is off | warn | reject. Empty means warn.
Enforce string `toml:"enforce"`
}
CategoriesConfig is the optional [categories] table in satelle.toml.
[categories] enforce = "warn" # off | warn | reject (default warn) extra = ["my-type"] # ADD to the embedded default list # vocabulary = ["fix", "feature"] # REPLACE the embedded default list
type Config ¶
type Config struct {
// DataDir is the per-repo store home; empty means DefaultDataDir. A
// relative value resolves under the repo root, never the process CWD.
DataDir string `toml:"data_dir"`
// DB overrides the database path; empty means <data_dir>/satelle.db. A
// relative value resolves under the repo root.
DB string `toml:"db"`
// SubstrateRoots maps an authored kind to the parent dir holding it. UNSET
// for a kind means the default <data_dir>/<kind>. An absolute value lets a
// kind's source live anywhere on disk.
SubstrateRoots map[string]string `toml:"substrate_roots"`
// WebPort is the local web server port; zero means DefaultWebPort.
WebPort int `toml:"web_port"`
// LogLevel is arbor's level (debug|info|warn|error); empty means info.
LogLevel string `toml:"log_level"`
// LogsMaxSizeKB caps each flat evidence log under .satelle/logs before it rolls;
// zero means DefaultLogsMaxSizeKB. LogsMaxFiles bounds kept rotations; zero means
// DefaultLogsMaxFiles. Daily rolling is always on.
LogsMaxSizeKB int `toml:"logs_max_size_kb"`
LogsMaxFiles int `toml:"logs_max_files"`
// StoriesKeepClosed keeps the N most-recently-updated CLOSED-story attachment
// dirs under .satelle/stories; 0 (default) disables count-based pruning.
// StoriesKeepDays prunes a closed story's dir when the story's terminal update
// is older than N days; 0 (default) disables age-based pruning. The two
// compose — either triggers pruning. A NON-terminal story's dir is ALWAYS kept
// regardless of either setting. Pruning MOVES the dir to
// .satelle/backups/stories/ (never deletes in place). (sty_aba7200c)
StoriesKeepClosed int `toml:"stories_keep_closed"`
StoriesKeepDays int `toml:"stories_keep_days"`
// Review opts this repo into reviewer-gated create. satelle init seeds
// gate_create = true (misclassification is cheapest to catch at create —
// sty_83782ffb); set false to opt out. Absent key stays false so older
// configs without [review] do not silently change until re-init/edit.
Review ReviewConfig `toml:"review"`
// Gate tunes the PreToolUse edit gate (the `satelle hook gate` handler).
Gate GateConfig `toml:"gate"`
// Hosted records the hosted-server binding for `satelle login` — the server
// URL and the project slug. Both are committed (secret-free); the OAuth
// access/refresh TOKENS are NEVER stored here — they live in the user-level
// credential store outside the repo (see internal/hosted). (sty_2fc93374)
Hosted HostedConfig `toml:"hosted"`
// Server is the LOCAL push-fed UI server endpoint the CLI publishes mutation
// events to (epic:serve-split / sty_126228b2). Distinct from Hosted (the
// remote satelle-server tier). Unset = change publisher inert (no network).
Server ServerConfig `toml:"server"`
// Backup tunes pre-mutation substrate backups (sty_873a5380). Local copies
// under .satelle/backups/ are always written; LocalOnly suppresses the
// advisory about enabling online/personal backup when no hosted channel is
// configured.
Backup BackupConfig `toml:"backup"`
// Vars is the operator KV an agents.toml binding's env values substitute via
// ${NAME} (sty_001558ce) — how a dispatched step points at an alternate model
// backend (e.g. GLM's Anthropic-compatible endpoint) without a wrapper binary.
// NON-secret vars MAY live in the committed satelle.toml; SECRETS (API keys)
// go in the gitignored satelle.local.toml overlay, whose keys win per-key. The
// KV is file-only — never persisted to the DB and never included in a substrate
// push (satelle.local.toml is excluded), so secrets do not leave the machine.
Vars map[string]string `toml:"vars"`
// Sync maps a .satelle area name (see SyncAreas) to its scope on the
// local|personal|shared ladder (epic:scoped-sync). Unset means "local" —
// nothing syncs without explicit opt-in. A committed [sync] table sets team
// defaults; satelle.local.toml overrides per-area for a single developer, the
// same per-key overlay merge as Vars (sty_a2d2e057).
Sync map[string]string `toml:"sync"`
// Tags holds the optional controlled-vocabulary table for work-item tags
// (sty_034d843c / epic:surface-scoped-steps). Declared namespaces reject
// unknown values at story/task create and set; absent/empty means every tag
// is free-form. The vocabulary is REPO CONFIG — the binary never hardcodes
// which namespaces or values exist.
Tags TagsConfig `toml:"tags"`
// Categories holds the optional controlled category vocabulary (sty_b2315e17).
// Default list ships embedded (substrate/config/categories.toml); satelle.toml
// may extend (extra) or replace (vocabulary). Enforce is off|warn|reject
// (default warn). Values are never a Go literal.
Categories CategoriesConfig `toml:"categories"`
}
Config is the on-disk shape at .satelle/satelle.toml. Every field is optional; the Resolve* methods supply defaults so the zero value is valid.
func Load ¶
Load resolves and parses the config, applying the satelle.local.toml overlay. It returns the Config, the resolved committed-config path (for repo-root derivation), and any error. A missing config is ErrNotFound — callers may treat that as "use the zero-value Config" for zero-config operation.
func (Config) CanonicaliseCategory ¶ added in v0.0.328
CanonicaliseCategory validates cat against the resolved vocabulary and rewrites a match to the declared casing.
Rules:
- Empty cat: pass through (structure still requires non-empty when gated).
- EqualFold match: return the declared casing (every enforce mode).
- No match + enforce reject: named error listing allowed values.
- No match + warn/off: return cat unchanged (CLI surfaces warn separately).
func (Config) CanonicaliseTags ¶ added in v0.0.249
CanonicaliseTags validates tags against the configured vocabulary and rewrites matching tags to the casing declared in config.
Rules:
- No colon, or namespace not in Vocabulary → pass through unchanged.
- Namespace present: value must EqualFold-match an allowed value; emit namespace:declaredValue (canonical casing from config).
- No match: named error listing the allowed values (ParseScope-style).
- Empty/absent Vocabulary: return tags unchanged, nil error.
The word for any particular namespace (e.g. surface) never appears here — only the generic mechanism. Values and namespaces come from config alone.
func (Config) CategoryEnforce ¶ added in v0.0.328
CategoryEnforce returns the effective enforcement phase: off | warn | reject. Empty/unknown defaults to warn so an upgrade never silently hard-rejects.
func (Config) CategoryWarning ¶ added in v0.0.328
CategoryWarning returns a non-empty advisory when cat is unknown and enforce is warn; empty otherwise. Used by the CLI create/set path (stderr, exit 0).
func (Config) LegacyRuntimeNote ¶ added in v0.0.261
LegacyRuntimeNote is the one-line deprecation message when a repo still has its database under the authored data dir. Empty when not legacy.
func (Config) ResolveActiveWorkspace ¶ added in v0.0.152
func (c Config) ResolveActiveWorkspace() ActiveWorkspace
ResolveActiveWorkspace resolves the active workspace from [hosted] workspace. Unset, blank, or the personal sentinel resolves to the personal default — zero-config. Any other value is a team workspace the developer elected: an explicit value is never the personal default, because that needs no binding, so Kind is derivable (personal iff the value is the sentinel).
The satelle.local.toml overlay is already merged by Load, so a per-developer choice recorded there overrides a committed team default transparently.
func (Config) ResolveAuthoredDirs ¶
ResolveAuthoredDirs returns kind→absolute-dir for every AuthoredKind, with [substrate_roots] overrides applied over the <data_dir>/<kind> default. An override may be absolute, placing a kind's source anywhere on disk.
func (Config) ResolveCategoryVocabulary ¶ added in v0.0.328
ResolveCategoryVocabulary returns the effective allowed category values: Vocabulary (if set) else EmbeddedCategories, then Extra, deduped case-insensitively (first casing wins). Never a Go-hardcoded list.
func (Config) ResolveConstitution ¶ added in v0.0.36
ResolveConstitution returns the absolute path to the repo's project constitution — <data_dir>/constitution.md — the order-zero doc injected every session (epic:session-context). Read directly (it is not an indexed kind).
func (Config) ResolveDB ¶
ResolveDB returns the absolute sqlite database path. An explicit db wins; otherwise the home-keyed runtime plane (~/.satelle/<repo-key>/satelle.db), falling back to a still-unmigrated legacy <data_dir>/satelle.db when present (sty_4660bbe1 / epic:substrate-planes). See ResolveRuntimeDir.
func (Config) ResolveDataDir ¶
ResolveDataDir returns the absolute data dir for repoRoot.
func (Config) ResolveEditExemptPaths ¶ added in v0.0.104
ResolveEditExemptPaths returns the configured [gate] edit_exempt_paths as absolute prefixes under repoRoot. Blank entries are DROPPED (a blank prefix would classify every edit as exempt and silently disable the gate — see withinRoot's fail-open-toward-inside default), and an absolute entry passes through unchanged. Returns nil when nothing is configured, so the gate exempts nothing and every in-repo edit requires an engaged story (sty_8c3d345c).
func (Config) ResolveLogLevel ¶
ResolveLogLevel returns the log level, defaulting empty to info.
func (Config) ResolveLogsMaxFiles ¶ added in v0.0.40
ResolveLogsMaxFiles returns how many rotated flat-log files to keep per log.
func (Config) ResolveLogsMaxSizeBytes ¶ added in v0.0.40
ResolveLogsMaxSizeBytes returns the per-file size cap (bytes) for the flat evidence logs under .satelle/logs, before a file rolls.
func (Config) ResolveRuntimeDB ¶ added in v0.0.261
ResolveRuntimeDB returns the absolute path of the per-repo sqlite database under the resolved runtime dir. An explicit `db` config still wins via ResolveRuntimeDir.
func (Config) ResolveRuntimeDir ¶ added in v0.0.261
func (c Config) ResolveRuntimeDir(repoRoot string) RuntimeResolution
ResolveRuntimeDir returns where runtime state lives for repoRoot.
Policy (sty_4660bbe1):
- An explicit `db` override wins: runtime dir is the parent of that path.
- Prefer an existing home-keyed dir: first the current RepoKey (git-aware), then the path-only key (so a tree that was inited non-git and later gained a .git keeps its original ledger).
- Else, if a legacy <data_dir>/satelle.db exists, return the legacy dir with Legacy=true so open keeps working and callers can emit a deprecation note (no silent move — migrate is an explicit command).
- Else (fresh repo) return the home-keyed path under the current RepoKey.
data_dir overrides do NOT relocate runtime; they only relocate authored substrate.
func (Config) ResolveWebPort ¶
ResolveWebPort returns the web port, defaulting when unset.
type ConfigFile ¶ added in v0.0.153
type ConfigFile struct {
Area string // the config-area name (skills, constitution, agents, tasks, ...)
Path string // server-relative path under the workspace-config root
Tier ConfigTier // PersonalTier | SharedTier (label only; sync dest is personal)
Content []byte // verbatim file bytes
}
ConfigFile is one resolved authored-config file destined for the versioned store. Path is server-relative (forward slashes, no leading ".satelle/", e.g. "skills/my-skill.md", "agents.toml", "constitution.md", "tasks/tsk_x.md") — the key the server stores the file under, stable regardless of where a kind lives on disk via [substrate_roots].
type ConfigTier ¶ added in v0.0.153
type ConfigTier int
ConfigTier flags whether a file is eligible to be exposed via satelle publish (SharedTier) or stays purely personal (PersonalTier). It is NOT a sync push destination — satelle sync always writes to the personal workspace; Tier == SharedTier only triggers an operator note (epic:sync-publish). A LocalScope area never reaches tier resolution — it is skipped wholesale before any file is read, so there is no LocalTier.
const ( // PersonalTier is ordinary personal-scope content (sync destination: personal). PersonalTier ConfigTier = iota // pushes personal-only; the CLI emits a note pointing operators at satelle // publish for team catalog exposure (not dual-destination). SharedTier )
func (ConfigTier) String ¶ added in v0.0.153
func (t ConfigTier) String() string
String renders the tier as its push/deploy label.
type EffectiveAgents ¶ added in v0.0.365
type EffectiveAgents struct {
// Agents is the resolved layer — the same shape LoadAgents returns, so every
// downstream consumer (dispatch, gates, validate) is unchanged.
Agents AgentsConfig
// Provenance is the per-field source table for display and validation.
Provenance Provenance
// Vars is the catalog's [vars] layered UNDER the repo's, repo keys winning.
// Expansion still happens in memory at wiring time (ResolveAgentEnvs) — a
// catalog secret is never written into a repository.
Vars map[string]string
}
EffectiveAgents is the resolved agents layer plus what a caller needs to explain and expand it: where each field came from, and the layered ${VAR} KV.
func LoadEffectiveAgents ¶ added in v0.0.365
func LoadEffectiveAgents(dataDir string, repoVars map[string]string) (EffectiveAgents, error)
LoadEffectiveAgents is the SINGLE resolution site: read the repo layer, read the machine-wide catalog, and fold them by the documented precedence. Every surface that needs the effective agents layer calls this rather than merging on its own — two independently-merging call sites is the defect this exists to prevent. repoVars is the repo's [vars] KV (may be nil).
func ResolveEffectiveAgents ¶ added in v0.0.365
func ResolveEffectiveAgents(repo AgentsConfig, global GlobalAgentsConfig, repoVars map[string]string) (EffectiveAgents, error)
ResolveEffectiveAgents folds an already-loaded repo layer and catalog. Split from LoadEffectiveAgents so tests and multi-repo fixtures can resolve without disk, and so the precedence logic has no I/O in it.
type EmbeddedDefault ¶ added in v0.0.6
type EmbeddedDefault struct {
Kind string // workflows | principles | skills | tasks (the substrate subdir)
Name string // filename without the .md extension
Body string // raw markdown
}
EmbeddedDefault is one canonical default artifact carried in the binary.
func EmbeddedDefaults ¶ added in v0.0.6
func EmbeddedDefaults() []EmbeddedDefault
EmbeddedDefaults returns every embedded default artifact, across all kinds. These are the canonical SEED + reference: init materialises them onto disk, the doc index resolves them by name as a Get fallback (e.g. the gating baseline), and validate compares against them — but they are NOT overlaid into List/Count, so an embedded default is never enumerated as a project doc (sty_94da9ac9).
type GateConfig ¶ added in v0.0.104
type GateConfig struct {
EditExemptPaths []string `toml:"edit_exempt_paths"`
AllowOutsideTreeEdits bool `toml:"allow_outside_tree_edits"`
CommandAllow map[string][]string `toml:"command_allow"`
}
GateConfig tunes the PreToolUse edit gate and the single-story process rule. EditExemptPaths lists repo-relative (or absolute) path prefixes whose edits are exempt from the engaged-story gate. It is the SOLE exemption source: the binary does NOT special-case the data dir or any managed path (configuration over code, the constitution). `satelle init` seeds ".satelle/" and ".gitignore" here so authored substrate and satelle-managed .gitignore output (init/migrate write its managed block) stay editable without a release OOTB, but the operator owns the list — add a harness authoring dir (e.g. ".claude/", which holds authored skills, not product code) or drop either default to gate those paths too. Empty means everything in-repo requires an engaged story (sty_8c3d345c / sty_f115e6bf).
One performing story at a time is always enforced (sty_c7149f8a) — there is no allow_parallel opt-out (removed sty_a614a0ea).
AllowOutsideTreeEdits opts INTO Bash/Edit mutations whose targets resolve inside another git working tree (root differs from the session-home anchor; sty_a8454d10 / sty_aadd4d6c). Non-repo paths (temp, scratchpads) are never fenced. Default false = deny foreign-tree mutations (create stories in other repos stays allowed; progressing/mutating them from this session does not). true only for a deliberately multi-repo install — it relaxes containment only; the engaged-story rule is untouched.
CommandAllow is an OPT-IN step-scoped command policy (sty_c21490cc). Keys are git subcommands (e.g. "push", "commit"); values are story statuses that may run them while engaged. Empty/nil = no step restriction (today's engage-only commitgate). Example:
[gate.command_allow] push = ["release"]
Not a satelle default — the operator authors the policy per repo.
type GlobalAgentsConfig ¶ added in v0.0.365
type GlobalAgentsConfig struct {
// Vars is the machine-wide base of the ${NAME} KV. A repo's [vars] (and its
// gitignored satelle.local.toml overlay) win per key. Secrets belong here on
// the operator's machine — they are expanded in memory at wiring time and are
// never written into a repository.
Vars map[string]string
// Profiles are the named reusable execution profiles, keyed by profile name.
Profiles map[string]AgentBinding
// Roles maps a logical role (reviewer|agent) to a default profile name. It is
// consulted ONLY for a repo that opts in with [defaults] use_global_roles.
Roles map[string]string
}
GlobalAgentsConfig is the on-disk shape of the machine-wide catalog.
[vars] # base KV for ${NAME} in profile env/settings;
# a repo's own [vars] override per key.
[profiles.<name>] # one reusable execution profile (an AgentBinding)
[roles] # OPT-IN per-role defaults: reviewer = "<profile>"
Every section is optional; an absent file is the zero value and a nil error, so a machine that never creates one behaves exactly as before.
func LoadGlobalAgents ¶ added in v0.0.365
func LoadGlobalAgents() (GlobalAgentsConfig, error)
LoadGlobalAgents reads the machine-wide profile catalog. An absent file is the zero value and a nil error (zero-config machines are unchanged); a present but malformed or policy-carrying file is an error, so a broken catalog fails loud rather than silently resolving to embedded defaults.
func ParseGlobalAgents ¶ added in v0.0.365
func ParseGlobalAgents(content string) (GlobalAgentsConfig, error)
ParseGlobalAgents decodes and validates a catalog body. Exported so the migration path and tests can check content without touching disk.
type GlobalConfig ¶
type GlobalConfig struct {
Service ServiceConfig `toml:"service"`
Agent AgentConfig `toml:"agent"`
Workspace WorkspaceConfig `toml:"workspace"`
UI UIConfig `toml:"ui"`
Hosted GlobalHostedConfig `toml:"hosted"`
}
GlobalConfig is the machine-wide config at ~/.satelle/config.toml.
func LoadGlobal ¶
func LoadGlobal() (GlobalConfig, error)
LoadGlobal reads the global config, returning a zero-value GlobalConfig (which resolves to defaults) when the file is absent. A present-but-malformed file is an error.
type GlobalHostedConfig ¶ added in v0.0.121
type GlobalHostedConfig struct {
Server string `toml:"server"`
}
GlobalHostedConfig binds this MACHINE to one hosted satelle-server — the single account the operator signs in to, shared across every repo (sty_53ccf845). Like UIConfig.Theme it follows the operator rather than a repo, so one `satelle login` signs in everywhere. Secret-free: the OAuth tokens live in the per-user credential store outside any config (internal/hosted), never here.
func (GlobalHostedConfig) ResolveServer ¶ added in v0.0.121
func (h GlobalHostedConfig) ResolveServer() string
ResolveServer returns the global hosted-server URL, trimmed of whitespace and a trailing slash so it matches the credential-store key exactly. Empty when unset.
type HostedConfig ¶ added in v0.0.113
type HostedConfig struct {
// Server is the hosted-server base URL (e.g. https://hosted.satelle.dev).
Server string `toml:"server"`
// Project is the hosted project slug this repo maps to. Personal sync
// (config/documents/workstate) targets this project's collection on the
// server — not a shared dump across every project under the personal
// workspace (sty_0aa3df89). Written by `satelle project bind <slug>`.
Project string `toml:"project"`
// Workspace is the ACTIVE hosted workspace scoped sync routes personal+shared
// areas through (epic:scoped-sync) — the handle the future sync engine
// prefixes. Unset (or the personal sentinel) means the developer's OWN
// workspace, the zero-config default; any other value is a team-workspace
// NAME the developer elected. This is PER-DEVELOPER: `satelle login
// --workspace <name>` records the choice in the gitignored satelle.local.toml
// overlay, and a value committed HERE is a hand-authored TEAM DEFAULT the
// overlay overrides per-key. Resolve via ResolveActiveWorkspace.
Workspace string `toml:"workspace"`
}
HostedConfig binds a repo to a hosted satelle-server. Secret-free and committed in satelle.toml; the login tokens are stored per-user outside the repo (internal/hosted credential store), never here.
type KeyEdit ¶ added in v0.0.115
KeyEdit is one key to write. Section is the TOML table ("" = a root key, above the first table header). Value is the already-rendered TOML right-hand side (e.g. `"info"`, `8787`, `true`, `["a", "b"]`) — the caller owns typing/quoting.
type Provenance ¶ added in v0.0.365
Provenance records, per binding section, the source of every effective field. Keys are the TOML field names (command, tools, model, …) so a display surface can render "field value (source)" without re-deriving anything.
func (Provenance) Fields ¶ added in v0.0.365
func (p Provenance) Fields(section string) []string
Fields returns the sourced field names for a section, sorted — a stable iteration order for rendering and snapshot tests.
func (Provenance) Source ¶ added in v0.0.365
func (p Provenance) Source(section, field string) string
Source returns the recorded source for one field, or "" when the field was never set by any tier (no value, so nothing to attribute).
type ReviewConfig ¶ added in v0.0.6
type ReviewConfig struct {
// GateCreate runs the required-structure reviewer on story/task creation,
// pushing non-conforming drafts back instead of persisting them.
GateCreate bool `toml:"gate_create"`
}
ReviewConfig toggles the quality-management gates for a repo.
type RuntimeResolution ¶ added in v0.0.261
type RuntimeResolution struct {
Dir string // absolute path holding satelle.db, logs/, backups/, stories/
Legacy bool // true when Dir is still <data_dir> because migration has not run
}
RuntimeResolution is the result of ResolveRuntimeDir: the absolute runtime directory and whether it is the pre-migration legacy layout under data_dir.
type Scope ¶ added in v0.0.144
type Scope int
Scope is a position on the local -> personal -> shared sync ladder for a .satelle authored area.
func ParseScope ¶ added in v0.0.144
ParseScope parses a sync area value off the local|personal|shared ladder. An unrecognised value is a config error (satelle's fail-fast posture) rather than a silent fallback to local — a typo in an EXPLICITLY set area must not go unnoticed.
func ScopeFor ¶ added in v0.0.144
ScopeFor resolves a configured area's scope. Precedence: an explicit sync <area> value wins; else the blanket sync all fallthrough; else LocalScope. A value outside local|personal|shared is a config error, never a silent local — for the per-area key when it is set, and for all only when an area actually falls through to it (a bad all beside a valid override does not error the overridden area).
type ServerConfig ¶ added in v0.0.279
type ServerConfig struct {
// Endpoint is the base URL of the local UI server (e.g. http://127.0.0.1:8787).
// Empty means the change publisher is a no-op.
Endpoint string `toml:"endpoint"`
}
ServerConfig points the CLI change publisher at a local read-only UI server (sty_126228b2). Secret-free; mechanism only — the binary carries no server opinion beyond POSTing events when Endpoint is set.
type ServiceConfig ¶
type ServiceConfig struct {
// Port the service listens on; zero means DefaultWebPort.
Port int `toml:"port"`
// Addr the service binds; empty means DefaultServiceAddr (0.0.0.0).
Addr string `toml:"addr"`
// Repo is the repository the service serves (its working directory). Empty
// until set by `satelle service install`, which defaults it to the CWD.
Repo string `toml:"repo"`
}
ServiceConfig configures the background web service (`satelle service`).
func (ServiceConfig) ResolveAddr ¶
func (s ServiceConfig) ResolveAddr() string
ResolveAddr returns the service bind address, defaulting when unset.
func (ServiceConfig) ResolvePort ¶
func (s ServiceConfig) ResolvePort() int
ResolvePort returns the service port, defaulting when unset.
type Setting ¶ added in v0.0.125
type Setting struct {
Section string
Key string
Label string
Help string
Kind settingKind
Enum []string
}
Setting declares one committed-config key. Section "" is a root key (above the first table header). Enum holds the allowed values for kindEnum.
func SettingByID ¶ added in v0.0.125
SettingByID looks up a Setting by its FieldID ("review.gate_create") OR its bare Key ("gate_create") — every bare key in the schema is unique across sections, so the short form is unambiguous and accepted for convenience.
func (Setting) EncodeValue ¶ added in v0.0.125
EncodeValue validates a user-supplied value for this key and renders the TOML right-hand side for a KeyEdit (e.g. `"info"`, `8787`, `true`, `["a", "b"]`). Returns an error for an invalid value or a display-only key.
type TagsConfig ¶ added in v0.0.249
type TagsConfig struct {
// Vocabulary maps a tag namespace (without trailing colon) to the allowed
// values for that namespace. Empty map / unset = no controlled namespaces.
Vocabulary map[string][]string `toml:"vocabulary"`
}
TagsConfig holds the optional controlled-vocabulary table for work-item tags. Declared in satelle.toml as:
[tags.vocabulary] surface = ["ui", "cli"]
A tag whose namespace is listed must use a declared value (matched case-insensitively, stored in the casing declared here). Namespaces absent from the table stay free-form.
type UIConfig ¶ added in v0.0.6
type UIConfig struct {
Theme string `toml:"theme"` // "dark" | "light" (empty = light default)
}
UIConfig holds user-level UI preferences shared across every repo, so the light/dark choice follows the operator rather than a single browser origin.
type WorkspaceConfig ¶ added in v0.0.6
type WorkspaceConfig struct {
Repos []string `toml:"repos"`
}
WorkspaceConfig is the connected-repo registry the workspace view aggregates. Per-repo databases stay the source of truth; this is just the list of paths.
func (*WorkspaceConfig) AddRepo ¶ added in v0.0.6
func (w *WorkspaceConfig) AddRepo(path string) bool
AddRepo adds an absolute repo path to the registry, de-duplicated. Reports whether it was newly added.
func (*WorkspaceConfig) RemoveRepo ¶ added in v0.0.6
func (w *WorkspaceConfig) RemoveRepo(path string) bool
RemoveRepo drops a repo path from the registry. Reports whether it was present.