OFTR-004: Setup, Sync, and Profile Creation Commands
Synced from
outfitter/docs/requirements/OFTR-004-sync-and-setup.md. The repository is the source of truth.
Amended (RFC #165 and #184, 2026-07-25): the
.agents-nativesetupandsynccommands are implemented. See OFTR-010 for onboarding. The profile-eraprofile create/profile listcommands (OFTR-004.3/.5) remain removed/deferred.
Overview
Section titled “Overview”Outfitter provides setup and maintenance commands that onboard a new user, synchronize remote .agents sources, and (in future) scaffold resources.
Requirements
Section titled “Requirements”OFTR-004.1: Setup Command
Section titled “OFTR-004.1: Setup Command”- Outfitter MUST provide a
setupcommand. - The
setupcommand MUST run the interactive.agentsonboarding flow defined in OFTR-010 (writedefault_agent/default_harnessand a starter agent), sharing one implementation with the implicit first-run entry fromrun.
OFTR-004.2: Sync Command
Section titled “OFTR-004.2: Sync Command”-
Outfitter MUST provide a
synccommand. -
The
synccommand MUST read and validate settings before synchronizing sources. -
The
synccommand MUST synchronize locally configuredremote_settingsfirst, reload the merged settings, and then synchronize every remotesourcesentry in the resulting settings. Localpath:sources MUST remain live and MUST NOT be copied into the cache. -
The
synccommand MUST store every URI or GitHub repository under<cache_directory>/repos/<encoded-uri-and-ref>/. The defaultcache_directoryis~/.agents/cache; the selected value MUST be shared by sync, remote-settings loading, layer discovery, and default-catalog bootstrap. -
The encoded URI cache path MUST support non-GitHub URIs.
-
The
synccommand MUST validate fetched remote settings against the settings schema and validate fetched.agentspayloads before making them active. -
The
synccommand MUST reportupdated,unchanged,skipped, orfailedfor each configured remote and MUST exit nonzero when any required remote fails or merged settings are invalid. -
Fetch and validation MUST occur in a temporary sibling directory. A successful checkout MUST be swapped into place atomically; any fetch, checkout, validation, or swap failure MUST preserve an existing valid cache. The first version of
syncMUST NOT require lockfiles or provenance. -
The
synccommand MUST redact credentials embedded in source URIs from status, errors, cache identifiers, and captured Git stdout/stderr. -
~/.agents/settings.ymlMUST be the source of truth for private GitHub profile catalog enablement, usingenterprise.private_catalogs: true. -
If
enterprise.private_catalogsis already true in~/.agents/settings.yml, setup and sync MUST NOT show private-catalog enterprise information or prompts. -
If setup or sync detects a confirmed-private GitHub catalog while the home setting is not enabled, interactive flows SHOULD ask whether to enable it and MUST include this prompt text:
Private GitHub profile catalog detected: OWNER/REPO.Private profile catalog support is covered by the Outfitter Enterprise license.Review code/enterprise/LICENSE or your enterprise agreement before enabling.Enable private profile catalogs in ~/.agents/settings.yml? [y/N] -
If the user accepts, setup or sync MUST write
enterprise.private_catalogs: trueto~/.agents/settings.ymland show:info: Enabled private profile catalogs in ~/.agents/settings.yml. -
If the user declines, setup or sync MUST skip that private catalog without changing settings and show:
info: Private profile catalog setup was skipped for OWNER/REPO; no settings were changed. -
Non-interactive setup and sync SHOULD skip confirmed-private GitHub catalogs without warning, error, or blocking public/unknown sources, and SHOULD show:
info: Private GitHub profile catalog detected: OWNER/REPO. Enable enterprise.private_catalogs in ~/.agents/settings.yml after reviewing code/enterprise/LICENSE or your enterprise agreement. -
GitHub privacy detection MUST only treat an HTTP 200 GitHub API response with JSON
private: trueas private. Public responses, unknown responses, HTTP 403/404, network failures, malformed responses, and non-GitHub sources MUST NOT warn, error, or block. -
Private catalog enablement MUST remain informational commercial governance and MUST NOT collect, echo, persist, synthesize, or validate provider credentials.
-
Resolution MUST report a configured remote source or remote-settings target whose cache is absent with actionable
outfitter syncguidance instead of silently dropping it. -
Amendment (2026-08-30):
outfitter runMUST verify declared remote settings and source caches before composition. The defaultrepairpolicy MUST reuse a manifest-valid clean cache without network access and MUST atomically repair a missing, dirty, mismatched, legacy, or unverifiable cache.lockedMUST additionally require every remote declaration to use a full 40-character commit pin.offlineMUST prohibit network access and fail unless every declared remote has a manifest-valid clean cache. An unavailable declared remote MUST fail before composition and MUST NOT be replaced by a lower-precedence or built-in profile. -
Settings MAY declare
source_cache.policyasrepair,locked, oroffline;repairis the default.outfitter run --source-cache-policyMUST override the configured policy for one run. -
Outfitter MUST store a versioned manifest under
<cache_directory>/source-state/containing the credential-redacted declared source identity, requested ref, resolved full commit, and cache key. -
Dirty cache checkouts MUST be quarantined rather than silently deleted. Concurrent startup repairs MUST serialize per source.
-
Workspace/global layers and explicit
path:sources remain live local authority. Startup cache verification MUST NOT reset, clean, fetch, or otherwise mutate those working copies. -
Outfitter MUST provide
outfitter sources [--json]with credential-redacted source, requested and resolved revision, origin, cache health, and precedence. JSON output MUST be stable and MUST NOT contain credentials.
OFTR-004.3: Create Profile Command
Section titled “OFTR-004.3: Create Profile Command”- Outfitter MUST provide a
profile createcommand. - The
profile createcommand MUST require a destination scope or destination path. - The
profile createcommand MUST require a profile name. - The
profile createcommand MUST create a placeholder profile folder with a validprofile.ymlfile. - The
profile createcommand SHOULD create conventional subfolders for common profile resources.
OFTR-004.4: Command Object Implementation
Section titled “OFTR-004.4: Command Object Implementation”- All CLI command entry points MUST execute command objects rather than duplicate implementation logic in parser callbacks.
- Command objects MUST accept typed input objects rather than reading directly from
process.argv. - Command objects SHOULD receive filesystem, settings, profile, and process dependencies through constructors or equivalent dependency injection.
- The
profile createparser entry point MUST execute the profile-creation command object.
OFTR-004.5: List Profiles Command
Section titled “OFTR-004.5: List Profiles Command”- Outfitter MUST provide a
profile listcommand. - The
profile listcommand MUST read and validate settings before listing profiles. - The
profile listcommand MUST list unique profile IDs from configured local and cached remote profile sources. - When multiple configured sources define the same profile ID, the listed profile metadata MUST come from the highest-precedence loaded definition.
OFTR-004.6: Transitive Catalog Sources
Section titled “OFTR-004.6: Transitive Catalog Sources”A remote catalog may declare its own sources in a settings file at its payload root
(settings.yml, or .agents/settings.yml when the payload root has no settings.yml). Those
declarations form the catalog’s dependencies; resolution and sync follow them transitively so a
pinned root catalog determines its dependency closure.
Transitive resolution is deliberately restricted to the narrowest safe subset of source references
while the remote-catalog trust and provenance model (issue #212) is defined: a transitive source
must be a github: shorthand pinned to an immutable ref. This keeps every transitive fetch sync
performs within the private-catalog gate and a known git transport, and prevents a declaration from
selecting an arbitrary git transport or escaping its fetched checkout. (First-party default-catalog
bootstrap is the one gate exemption — see OFTR-004.6.10.)
- Layer discovery MUST resolve
github:sourcesdeclared by a cached remote catalog’s own settings file and append them as layers, andsyncMUST fetch the same closure. - A transitive source MUST contribute only
.agentspayload content. Every other setting declared by a catalog’s settings file (default agent, default harness, cache directory, state persistence, custom settings, startup, enterprise,remote_settings) MUST NOT take effect through transitive resolution. - Every directly configured source MUST outrank every transitive source. Transitive layers MUST be ordered breadth-first by dependency depth, then by declaring-catalog order, then by declaration order within a catalog’s settings file.
- A transitive source MUST be a
github:shorthand pinned to an immutable ref (a full commit SHA or a version tag). Resolution and sync MUST skip a declared source that is auri:source, that carries apath:subpath, or whose ref is absent or not immutable, and report a warning naming the declaring catalog. - Resolution and sync MUST skip a local
path:source declared by a remote catalog and report a warning naming the declaring catalog. - A source already resolved — directly or by an earlier catalog — MUST NOT be resolved again; dependency cycles MUST terminate without error.
syncMUST fetch newly discovered transitive sources until no new sources remain, report each with kindtransitiveand the OFTR-004.2.7 status vocabulary, and gate each through the same private-catalog policy as directly configured sources.- A cached catalog settings file that is unreadable (not a regular file inside the checkout — for
example a
settings.ymlcommitted as a symlink to a directory or outside the checkout) or that fails schema validation MUST NOT fail resolution or sync; its declared sources MUST be skipped with a warning naming the declaring catalog. - Resolution MUST report a transitive source whose cache is absent with the same actionable
outfitter syncguidance as a directly configured source (OFTR-004.2.18). - Default-catalog bootstrap MUST fetch the pinned
github:closure the default catalog declares before setup offers or launches a profile, so a first run resolves a default profile whose skills a depended-on catalog supplies without a separateoutfitter sync. Bootstrap MUST accept a dependency that ships only skills (or any recognized.agentspayload) rather than requiring it to contain agents. A declared dependency that cannot be fetched MUST be reported by resolution as unsynchronized rather than failing setup, while the root catalog failing to fetch remains fatal. Because the default catalog is the first-party, pinned catalog Outfitter ships, bootstrap MAY fetch its declared closure without the interactive private-catalog gate thatoutfitter syncapplies; the gate remains a property ofsync, not of first-party bootstrap. - When
syncvalidates a single fetched source in isolation, an unresolved loadout skill or agent reference MUST NOT fail that source, because the referenced skill or agent may be supplied by a catalog the source declares as a transitive dependency. Structural validity of the source (schema, resource naming) MUST still be enforced. Whether every loadout skill/agent slug resolves is authoritatively enforced against the merged effective set byoutfitter validate, which MUST treat an unresolved loadout skill or agent reference as an error. (This concerns only skill and agent loadout slugs; an unknown MCP server reference remains a warning, unchanged.) Consistent with OFTR-005.3.4, the run-time composer surfaces an unresolved loadout reference as a non-fatal warning (fatal only underoutfitter run --strict);outfitter validateis the command that fails on it.
OFTR-004.7: Ambiguous Source Resolution
Section titled “OFTR-004.7: Ambiguous Source Resolution”Resolution precedence exists to compose layers, not to hide disagreement. When two declarations disagree about the same thing, the selected declaration must be visible.
- Resolution MUST detect when the same source repository is declared more than once with different refs across all declared configuration — every loaded settings scope plus the transitive declarations of effective sources — MUST report a warning naming each declaring layer and its ref, and MUST name the declaration that won.
- Resolution MUST detect when a settings scope’s
sourceslist replaces a lower-precedence scope’s list and drops a declared repository entirely, and MUST report a warning naming the dropped source, its declaring scope, and the replacing scope. - Resolution MUST detect when the same agent or skill slug is supplied by more than one source, MUST report a warning naming each supplying source, and MUST name the definition that won. A slug intentionally overridden by a higher-precedence layer is still reported; visibility, not prohibition, is the requirement.
- These warnings MUST be surfaced by diagnostic commands that resolve the effective set, including
sync,validate, andlist agents.run --strictMUST also surface them before it fails. A successful non-strictrunMUST suppress them so routine startup stays quiet. - Detection MUST NOT change which declaration wins; precedence rules are unchanged.
- Under strict mode, every command that resolves the effective set MUST report every detected ambiguity and then fail with a nonzero exit status. All three ambiguity classes above gate uniformly. A deliberate divergence under strict mode MUST be resolved by making the configuration unambiguous, not by suppressing the error.