Resource discovery
Find and manage resources and their loading rules.
Prompts, skills, and executable extensions share one filesystem
resolver. Resource-specific parsers own their schemas; the resolver owns the
cross-cutting local safety and precedence contract. Built-in auto, light,
and dark appearances are available through /theme without
theme files. Named theme files follow the separate bounded theme loader.
Locations and precedence#
| Kind | Global | Trusted project | Explicit option |
|---|---|---|---|
| Prompt | ~/.octet/prompts/*.{md,toml} |
.octet/prompts/*.{md,toml} |
--prompt-template <file-or-dir> |
| Skill | Ordered user roots | Ordered project roots | --skill-dir |
| Extension | ~/.octet/extensions/*/extension.toml |
.octet/extensions/*/extension.toml |
--extension-dir |
Roots are visited global, project, then explicit in option order. An explicit
Pi-compatible prompt source may be one .md/.toml file or a directory.
Later definitions with the same resource name win, and the shadowed path
remains in the diagnostic snapshot. Scans and result ordering are
deterministic. A valid package-manager install.json admits an installed
bundle's nested skills/ root; merely copying an unmanaged extension directory
does not. Bundle skills have lower precedence than ~/.octet/skills, remain
inactive until explicitly loaded, and disappear from the next discovery
snapshot after package removal.
Workspace resources are ignored until --workspace-trusted is present.
Explicit paths are an intentional user choice for that invocation. Executable
extensions add a second boundary: discovery and workspace trust still do not
launch code. Installed executable extensions remain disabled by default.
Full access (unsafe_host, the default) implicitly trusts the selected validated
source, so an explicitly enabled extension needs no extra trust flag. Trust and
enablement are separate; implicit trust never writes a grant to configuration.
Process startup still requires full access and the independent process gate.
--safe-mode removes implicit trust and blocks executable startup even when an
explicit grant exists. It does not sandbox extensions or allow approval to
bypass the unsafe_host floor. A project config cannot create a persistent
executable trust grant. Bare persistent trust names apply only to the global
extension directory; project and explicit sources require an exact absolute
name@.../extension.toml grant to persist trust. --trust-extension name grants
explicit trust only for that invocation, never enablement. These grants do not
transfer between sources or override safe-mode execution policy. The extension
directory name must match the manifest name, and source, compatibility, bundle,
and artifact validation remain mandatory.
If octet cannot resolve an absolute user home directory, global configuration and global resources are disabled with a diagnostic. It never falls back to the invocation directory and reclassifies project files as user-owned resources.
Skill roots#
Skill discovery uses this low-to-high precedence order:
- User:
~/.agents/skills, then~/.pi/agent/skills, then managed extension skills, then~/.octet/skills. - Trusted project:
.agents/skillsroots from the workspace through the invocation directory, then the invocation directory's.pi/skills, then the workspace's.octet/skills. These project roots require--workspace-trusted. - Explicit:
--skill-dirsources in command-line option order.
A project root that is also a user skill root is scanned only in the user tier.
For example, starting in your home directory keeps ~/.agents/skills and
~/.octet/skills user-installed, without an untrusted-project warning for those
same roots. This does not trust the workspace: distinct project roots, including
nested .agents/skills and the invocation's .pi/skills, remain gated. Root
symlinks are still rejected.
Later definitions replace earlier definitions of the same skill name; collisions
are recorded in discovery diagnostics. Discovery does not activate a skill.
The octet-native entrypoints remain ~/.octet/skills/*/SKILL.md,
.octet/skills/*/SKILL.md, and managed
~/.octet/extensions/*/skills/*/SKILL.md.
These lookup locations do not promise full Agent Skills/Pi parser compatibility or additional symlink support. Parser-specific shapes and symlink behavior for those additional roots require their exact source contract.
Skill catalog budgets#
Discovery accepts at most 32 KiB of YAML frontmatter per skill and has a 4096-entry per-root scan limit. Those are per-input limits, not global catalog limits. Across all roots, the retained discovery catalog additionally has these caps:
- 1024 UTF-8 bytes per description, including an ellipsis when shortened. Only the metadata excerpt is shortened; the source file and instruction body are unchanged. The full description allocation is not retained.
- 256 descriptors / 256 KiB of descriptor payload bytes, whichever is reached first. Payload counts text fields, encoded paths, and JSON-serialized arbitrary metadata, not allocator overhead. Admission follows deterministic root/candidate order. Later definitions still replace earlier ones of the same ID; if a larger replacement does not fit, its predecessor is removed rather than advertised as the winner.
- 64 KiB of rendered model-catalog text, including XML escaping, framing,
paths, and any cap notice. Rendering uses ID/path order and stops before the
first entry that does not fit. Names, location paths, XML entities, and closing
tags are never cut.
disable-model-invocationskills remain excluded.
Description caps and omitted counts appear in discovery diagnostics. Catalog
omission is not deactivation: winning source locations remain indexed, so a
known /skill:NAME or /skills load NAME can still load an omitted skill with
the usual trust, required-tool, symlink, and 256 KiB file limits. An omitted
header is parsed on demand; a changed skill ID requires rediscovery. Listing and
search use the bounded descriptors, not the complete source index. These limits
bound retained descriptors and model context, not total discovery work or RSS:
the lightweight source index and diagnostics still grow with discovered inputs.
Reads and diagnostics#
For octet-native resource roots, selected files, and directory entrypoints, the existing resolver contract requires regular, non-symlink filesystem objects. Parser reads use descriptor-bound no-follow opens and fixed byte limits:
| Kind | Maximum parser input |
|---|---|
| Prompt | 512 KiB |
| Skill entrypoint | 256 KiB |
| Extension manifest selected by the product resource resolver | 256 KiB |
Prompt expansion, skill resource reads, extension protocol messages, and
session files have their own narrower purpose-specific limits after discovery.
The lower-level ExtensionManifest::load API has a separate 64 KiB default;
product discovery reads the selected manifest through the 256 KiB resolver
bound and then calls ExtensionManifest::parse.
Invalid UTF-8, invalid names, inaccessible roots, rejected links, oversized
files, parser failures, and precedence decisions become inspectable
diagnostics. One broken customization does not prevent the core binary from
starting.
Automatic reload suppresses repeated resource/bootstrap and keybinding problems per checked component. A successful check clears that component's remembered problem, allowing a later recurrence to appear; skipped checks do not clear it. Explicit commands still report their diagnostics, and actual work losses are never suppressed as duplicate configuration warnings.
Reload#
Each discovery pass produces an immutable generation snapshot. Consumers build a complete replacement from the new snapshot and swap only after validation, so an in-flight prompt never observes half of a reload.
/skills reloadrefreshes the shared prompt/skill resource boundary./extensions reloadhandshakes replacement processes under the default full-access policy when the independent process gate permits startup; safe mode leaves executable extension processes stopped./reloadperforms full product resource discovery and rebuilds the active customization boundary.