Reference

Machine Readable CLI Contracts

Public contract reference for gitrole machine readable CLI output, including status --short, doctor --json, and resolve --json for scripts and automation.

Scripts and agents break when they guess which field is the summary, or when they paraphrase overall as "ok". This page is the contract for structured diagnostic output and the local commit check’s exit codes. Field names, values, and exit codes below are the ones the CLI writes.

Quick start

For a commit or a push check, run:

gitrole status --short

Read overall by name. It is the eighth field. policy is the seventh, so a parser that still treats field seven as the summary is reading policy. overall=aligned exits 0. overall=warning exits 2 and still prints the line. Exit 1 is a failure: the error is on stderr and stdout is empty. If you only need to stop on a warning, you can stop here.

A local role, SSH auth matched, no .gitrole file:

role=work scope=local override=true commit=ok remote=ok auth=ok policy=na overall=aligned
result=$(gitrole status --short)
overall=$(echo "$result" | grep -o 'overall=[^ ]*' | cut -d= -f2)

if [ "$overall" != "aligned" ]; then
  echo "repo is not aligned, stopping"
  exit 1
fi

Shell prompts call gitrole status --short --offline instead, so they don't open SSH on every redraw. --offline does not emit auth=ok. Setup and the segment glyphs are in Show gitrole in your shell prompt.

gitrole check commit
Local-only saved identity and policy guard. Automate against its exit code; success is quiet.
gitrole status --short
One line. Order is role scope override commit remote auth policy overall.
gitrole doctor --json
Full diagnosis as JSON. HTTPS auth is info only when a pin allows the role and that role has a githubUser. This does not verify HTTPS credentials. Otherwise it is warn.
gitrole resolve --json
The .gitrole file as JSON.
Role name format
Saved role names, and the reserved role=no-role sentinel.

gitrole check commit

This command checks local commit identity and repository policy. It accepts no operands, --offline, or JSON mode. It does not resolve push destinations, invoke SSH, check authentication, or write roles, policy, or Git configuration. It can pass without a remote.

Exit Meaning stdout stderr
0 Complete saved identity and applicable policy match Empty Empty
2 Identity/policy mismatch or unsuitable repository context Empty Human-readable explanation
1 Required read, parse, Git operation, or usage failure Empty Human-readable explanation

The command and exit codes are the automation contract; diagnostic wording is not. A required read or parse failure takes precedence over an observed mismatch. No successful result means a commit occurred.

The complete effective author name and email must match the first saved role with that identity, in store order. The committer must equal that same complete identity. No pin is required, but no saved match is exit 2, including a missing or empty role store; the check never creates a store. A matching legacy reserved no-role profile is refused. Duplicate identities do not select a later profile to satisfy policy.

With a valid .gitrole, both default and allowed evaluations pass. notAllowed refuses. An allowed non-default role may pass without a saved default profile. Missing policy is optional; malformed role data anywhere in the store or malformed policy is exit 1. Existing tolerant parsing of legacy blank identities and filtered allowed-list elements remains unchanged; unrelated unusable identities do not veto a valid match. Unreadable files and dangling symlinks are failures, not absence; valid file symlinks may be read.

Mixed configured identity sources refuse. An unborn branch requires a local or worktree override; matching global-only identity can pass in an established repository. A valid unborn branch differs from damaged HEAD, index, config, or unexpected Git results, which fail with 1. Outside a worktree and bare repositories return 2. The check reads the invocation's effective identity, including Git-prepared author values in a hook; a standalone run cannot predict later author arguments.

The optional packaged pre-commit wrapper delegates to this command. It preserves 1/2, normalizes unexpected failures to 1, and appends a fixed bypass disclosure on failure. Missing or incompatible CLI and missing Node never count as a pass. The wrapper is quiet on success. See local hook setup and migration. Existing status fields/exits and the packaged agent skill's strict gate remain unchanged.

gitrole status --short

One line answers whether this repo is aligned to commit or push. --offline is the same line with the live SSH probe skipped.

Signature

gitrole status --short
gitrole status --short --offline

How to read the line

Exactly one line. Eight key=value fields, in this order, separated by single spaces. A pin is the repo's .gitrole file (what gitrole pin writes). It names defaultRole and allowedRoles. See Pin a repo to one role.

No .gitrole file, so policy=na, and SSH auth matched:

role=work scope=local override=true commit=ok remote=ok auth=ok policy=na overall=aligned

HTTPS-only push destination whose pin allows the effective role, and that role has a githubUser. auth=na and overall=aligned together. Exit 0. auth=na here means SSH verification doesn't apply, not that a probe passed:

role=work scope=local override=true commit=ok remote=ok auth=na policy=ok overall=aligned

HTTPS-only push destination with no pin. Since 0.8.0 this is auth=warn and overall=warning, exit 2. gitrole can't tell which GitHub user the push will use:

role=work scope=local override=true commit=ok remote=ok auth=warn policy=na overall=warning

HTTPS-only push destination whose pin doesn't allow the effective role. auth=warn and policy=warn. Exit 2:

role=personal scope=local override=true commit=ok remote=ok auth=warn policy=warn overall=warning

Effective role is outside allowedRoles. policy=warn and overall=warning. Exit 2:

role=client-acme scope=local override=true commit=ok remote=ok auth=ok policy=warn overall=warning

Default push observation

A literal . selected by branch pushRemote, remote.pushDefault, or branch remote is resolved by Git before it is classified as local. If insteadOf or pushInsteadOf rewrites it, status and doctor inspect the rewritten endpoint. An unchanged direct . keeps its local-destination behavior; a configured remote named . uses its configured push URLs.

remote and auth cover the destination of a plain git push, not the fetch origin. Selection follows branch pushRemote, remote.pushDefault, branch remote, sole remote, then origin. A selected value that is not a configured remote is a URL or path, including an scp-style SSH URL, an HTTPS URL, and a relative or absolute local path. Named remotes are resolved with git remote get-url --push --all. Other destinations are resolved by Git in an isolated temporary repository using only the push URL aliases from the original effective configuration. This preserves conditional include behavior without changing the observed repository's configuration. Legacy .git/remotes and .git/branches destinations remain explicitly unverified. insteadOf and pushInsteadOf apply, and the longest match wins. An explicit pushurl is not rewritten by pushInsteadOf; insteadOf still applies to it. A pushInsteadOf rule that matches only some fetch URLs leaves the others out of the push. Every resolved push URL is checked; an unverified endpoint warns. Explicit remote-helper destinations such as ext::command or helper::address are unsupported and warn online and offline; inspection does not execute their helpers. Mixed SSH/HTTPS warns online; offline retains HTTPS pin checks and invokes no SSH.

Online standard OpenSSH inspection includes URL user/port and receive-pack context. Custom Git SSH commands, alternate diagnostic binaries, incomplete configuration, context differences or interactive authentication remain unverified. Inspection may execute configured Match exec commands or DNS lookups. These checks do not prove branch/refspec readiness, remote permission or push success, and do not predict future explicit push arguments.

--offline

--offline skips the live SSH githubUser probe. Field names and order stay the same. auth=na on SSH means the probe was skipped. That na doesn't by itself set overall=warning. --offline does not emit auth=ok.

Local checks still run: commit identity, GIT_AUTHOR_* and GIT_COMMITTER_* overrides, a fresh repo with no local role, remote host compared with the saved githubHost, and .gitrole policy. HTTPS auth is the same with or without --offline, because it uses the repo pin and the saved role's githubUser, not the network.

SSH remote, probe skipped, local checks clean. Exit 0:

role=work scope=local override=true commit=ok remote=ok auth=na policy=na overall=aligned

A live gitrole status --short on that same SSH remote probes GitHub and can report auth=ok or auth=warn instead. The prompt helper doesn't do that. A line that still says auth=ok didn't come from --offline.

Fields

FieldWhat it tells youValues
roleSaved role that matches the current commit identityrole name, or no-role
scopeUnderlying configured author scope, retained under environment overridesglobal, local, system, worktree, command, git, mixed, unset
overrideWhether a repo-local Git config is activetrue, false
commitCommit identity checkok, warn, na
remoteAll default push destinations against role host expectationsok, warn, na
authEvery supported SSH push account, or HTTPS-only pin checks; mixed online destinations warnok, warn, na
policy.gitrole against the effective roleok, warn, na
overallSummaryaligned, warning
Fieldna when
remoteNot inside a Git repo
authNot inside a Git repo, HTTPS whose pin allows the active role and that role has a githubUser, or --offline when the live SSH probe is skipped
policyNo .gitrole file

How na rolls into overall

na means that check doesn't apply. It doesn't by itself set overall=warning or exit 2. policy=na when there is no .gitrole file can sit next to overall=aligned when nothing is warn.

What is trueoverallExit
commit, remote, auth, or policy is warnwarning2
Working directory is outside a Git repowarning2
Those checks are only ok or na, inside a Git repoaligned0

Only warn on those checks, or being outside a Git repo, drives overall=warning. auth on SSH is ok or warn from the githubUser probe. --offline doesn't run that probe, so SSH auth is na. On HTTPS, auth=na only when a repo pin allows the active role and that role has a githubUser. No pin, or a pin that doesn't allow the active role, is auth=warn and exit 2.

policy=ok when .gitrole allows the effective role: that role is defaultRole, or it is listed in allowedRoles. policy=warn when the evaluation is notAllowed.

Outside a Git repo, with a global name and email that match no saved role, one run printed this line and exited 2. remote=na and auth=na because there is no work tree. commit=warn because nothing saved matches. Your role and commit follow the identity that is actually configured. overall is still warning outside a repo:

role=no-role scope=global override=false commit=warn remote=na auth=na policy=na overall=warning

Exit codes

CodeMeaning
0overall=aligned. The line was written to stdout. An HTTPS repo reaches this only when the pin allows the active role, that role has a githubUser, and no other field is warn.
2overall=warning. The line was written to stdout.
1Failure. Error on stderr. Stdout empty. No line.

When it fails

Exit 1 writes the error to stderr and doesn't print the line. A missing .gitrole file still prints the line, with policy=na.

ConditionResult
Saved role data contains a name outside the role name formatexit 1, empty stdout, stderr error: saved role data is invalid; fix or recreate the roles file
Saved role is already named no-rolenot this failure; the line is still printed. gitrole doctor warns and suggests a rename
.gitrole is invalid JSON or fails schema validationexit 1, stderr, empty stdout
Invalid .gitrole defaultRole or allowedRoles nameexit 1, empty stdout. Policy is loaded before the line is written. See Role name format.
Another operational failure before the line is writtenexit 1, stderr, empty stdout

What's stable

Field names, this order, and the value vocabularies above are the contract:

role scope override commit remote auth policy overall

policy is the seventh field. overall is the eighth. Read them by name. Since 0.7.6 the line has eight fields. A parser that treated the seventh field as overall is reading policy. Changing a name, the order, or a vocabulary is a breaking change.

gitrole doctor --json

Use this when the one-line check isn't enough and you need commit identity, repo context, SSH auth, and .gitrole policy as JSON. Don't parse checks[].message. Read overall, commitIdentity, and checks[].status.

Signature

gitrole doctor --json

Example

This illustrative subset shows the single-endpoint JSON shape; additive push/fetch metadata is described below. The saved role work matches the repo-local name and email, origin is git@github.com-work:acme/service.git, the SSH probe returned acme-dev, and there is no .gitrole file, so repoPolicy is omitted. repository.topLevelPath is that repo's absolute path. Yours will differ. Exit 0.

{
  "role": {
    "name": "work",
    "fullName": "Alex Developer",
    "email": "alex@work.example",
    "sshKeyPath": "~/.ssh/id_work",
    "githubUser": "acme-dev",
    "githubHost": "github.com-work"
  },
  "overall": "aligned",
  "commitIdentity": {
    "fullName": {
      "value": "Alex Developer",
      "source": "local"
    },
    "email": {
      "value": "alex@work.example",
      "source": "local"
    }
  },
  "configuredIdentity": {
    "local": {
      "fullName": "Alex Developer",
      "email": "alex@work.example"
    },
    "global": {
      "fullName": "Example Global Identity",
      "email": "global@example.test"
    }
  },
  "scope": {
    "effective": "local",
    "hasLocalOverride": true
  },
  "repository": {
    "isInsideWorkTree": true,
    "hasCommits": true,
    "topLevelPath": "/tmp/gitrole-doc-sample/service",
    "currentBranch": "main",
    "remote": {
      "name": "origin",
      "url": "git@github.com-work:acme/service.git",
      "protocol": "ssh",
      "host": "github.com-work",
      "owner": "acme",
      "repository": "service"
    }
  },
  "sshAuth": {
    "ok": true,
    "host": "github.com-work",
    "githubUser": "acme-dev"
  },
  "checks": [
    {
      "status": "ok",
      "label": "role",
      "message": "commit identity matches saved role work"
    },
    {
      "status": "info",
      "label": "remote",
      "message": "default push remote origin uses ssh at git@github.com-work:acme/service.git"
    },
    {
      "status": "ok",
      "label": "scope",
      "message": "selected role work is applied via local config"
    },
    {
      "status": "ok",
      "label": "commit",
      "message": "effective commit identity matches selected role work"
    },
    {
      "status": "ok",
      "label": "host",
      "message": "remote host matches role githubHost github.com-work"
    },
    {
      "status": "ok",
      "label": "auth",
      "message": "SSH auth matches role githubUser acme-dev"
    }
  ]
}

HTTPS auth is info, with message push destination uses HTTPS; SSH auth verification does not apply, only when a repo pin allows the active role and that role has a githubUser. No pin, or a pin that doesn't allow the active role, is warn and exit 2. sshAuth is omitted when no SSH probe runs.

Offline diagnosis

gitrole doctor --offline --json
gitrole doctor --json --offline

Both flag orders return the same DoctorResult shape. Offline mode adds no keys and invokes no SSH commands, including configuration inspection (ssh -G) or an authentication test. Top-level and per-target sshAuth are omitted. A saved role.githubUser is still an expectation, never an observed account.

For each SSH target, the authentication check is info with a message that authentication was skipped. Skipping alone causes no warning. Exit 0 and overall=aligned mean only that local checks have no warnings; neither verifies authentication, repository permission or a future push. A local warn still selects overall=warning and exit 2; usage and operational errors remain exit 1.

Role and effective author/committer checks, fresh-repository warnings, policy, default push resolution and every target's host checks remain active. HTTPS pin checks also remain active for pure and mixed destinations: an allowing repo pin plus a saved githubUser yields info; absent or mismatched pins warn. Offline mode omits the online mixed-authentication blanket warning. Unsupported transports remain local destination warnings, not failed SSH observations.

No authentication history is read or saved. Existing role-store behavior remains: an absent roles file may be initialized. Default online doctor, status, prompt and auth test are unchanged; online SSH inspection may run configured commands, use the network or change SSH state.

Fields

Top-level fields

FieldWhat it tells you
roleSaved role that matches the current commit identity. Omitted if no role matches.
overallaligned or warning
commitIdentityEffective name and email, plus where each comes from: local, global, system, worktree, command, git, env, or unset
configuredIdentityRaw local and global Git config values. This is not the commit identity when an env var overrides it.
scopeAggregate view of underlying configured author scope
repositoryRepo context, branch, and parsed remote info
sshAuthSSH probe result. Omitted if no SSH probe was run, including HTTPS-only push destinations.
repoPolicy.gitrole policy evaluation. Omitted if no policy file exists.
checksOrdered list of individual check results

commitIdentity

FieldMeaningValues
fullName.valueEffective commit author namestring, or omitted when unset
fullName.sourceWhere the effective name came fromlocal, global, system, worktree, command, git, env, unset
email.valueEffective commit author emailstring, or omitted when unset
email.sourceWhere the effective email came fromlocal, global, system, worktree, command, git, env, unset

configuredIdentity

FieldMeaning
configuredIdentity.local.fullNameRaw repo-local user.name, if present
configuredIdentity.local.emailRaw repo-local user.email, if present
configuredIdentity.global.fullNameRaw global user.name, if present
configuredIdentity.global.emailRaw global user.email, if present

scope

FieldMeaningValues
effectiveUnderlying configured author scope, including when effective values come from the environmentlocal, global, system, worktree, command, git, mixed, unset
hasLocalOverrideWhether underlying author configuration includes a local or worktree fieldtrue, false

repository

FieldMeaning
isInsideWorkTreeWhether the current working directory is inside a Git work tree
hasCommitsWhether HEAD exists. Omitted outside a Git repo.
topLevelPathAbsolute path to the repo root. Omitted outside a Git repo.
currentBranchCurrent branch name, when available
upstreamBranchConfigured upstream branch, when available
fetchRemoteParsed fetch origin, independent of push qualification.
pushSelected remoteName, optional resolution message and all targets. Each target contains parsed remote, optional sshAuth and unverified message. Singular top-level sshAuth is populated only for one endpoint.
remoteFirst effective default push endpoint. Omitted when no destination can be resolved.

repository.remote

FieldMeaningValues
nameRemote nameselected default push remote
urlRaw remote URLstring
protocolParsed remote protocolssh, https, unknown
hostParsed remote hoststring when parseable
userExplicit SSH URL user, when presentstring
portExplicit supported SSH URL port, when presentinteger 1–65535
pathSSH repository path as interpreted by Gitstring
ownerParsed repository owner or orgstring when parseable
repositoryParsed repository namestring when parseable

sshAuth

FieldMeaning
okWhether the SSH probe succeeded
hostSSH host alias or hostname that was probed
githubUserGitHub user resolved from the SSH probe, when available
messageProbe detail when no GitHub user could be resolved

repoPolicy

FieldMeaningValues
versionPolicy schema versioncurrently 1
defaultRolePreferred role for this reporole name
allowedRolesRoles allowed by .gitrolearray of role names
effectiveRoleActive matched role used for evaluationrole name, or omitted
statusPolicy evaluation resultdefault, allowed, notAllowed

status default and allowed are policy=ok on status --short. notAllowed is policy=warn.

checks

Each entry has status, label, and message. info doesn't set overall to warning and doesn't cause exit 2. A warn check does.

FieldMeaningValues
statusPer-check resultok, warn, info
labelDiagnostic category stringshort string such as role, remote, or auth. Not a closed vocabulary.
messageHuman-readable explanationstring. Don't parse this.

Doctor may append at most one {status: "info", label: "hook", message: "..."} entry for a legacy pre-commit hook or an unknown inspection result. It reads Git's effective hook path without executing or modifying the hook. This uses the existing check shape and status vocabulary, adds no top-level field, and does not change overall or exits. Exact legacy bytes, inactive files, symlink targets, and uncertain text matches receive explanatory wording; do not parse that wording. No finding is not proof of protection or successful migration. See inspection limits and manual review.

When a pin allows the active role and that role has a githubUser, the HTTPS auth entry is info:

{
  "status": "info",
  "label": "auth",
  "message": "push destination uses HTTPS; SSH auth verification does not apply"
}

No pin is warn. With no githubUser on the active role the message is push destination uses HTTPS and no identity pin is configured:

{
  "status": "warn",
  "label": "auth",
  "message": "push destination uses HTTPS and no identity pin is configured"
}

With a githubUser but no allowing .gitrole, the message is push destination uses HTTPS and no repo pin is configured.

A pin that doesn't allow the active role is warn. When both GitHub users are known and differ:

{
  "status": "warn",
  "label": "auth",
  "message": "push destination uses HTTPS; github user thisyearearth does not match pin alex-dev"
}

Otherwise the message is push destination uses HTTPS; active identity does not match pinned role <defaultRole>.

Exit 0 with overall aligned on HTTPS requires the pin to allow the active role, that role to have a githubUser, and no other warn check.

Exit codes

CodeMeaning
0Diagnosis complete, no warn check. JSON on stdout.
2Diagnosis complete, at least one warn check. JSON on stdout.
1Failure. Error on stderr. No JSON.

An HTTPS auth check selects exit 2 when it is warn (no pin, or a pin that doesn't allow the active role). It stays info, and doesn't by itself select exit 2, only when the pin allows the active role and that role has a githubUser.

When it fails

Exit 1 writes the error to stderr and doesn't print JSON. A missing .gitrole file still returns JSON. repoPolicy is omitted.

ConditionResult
Saved role data contains a name outside the role name formatexit 1, stderr, no JSON
Saved role is already named no-rolenot this failure. JSON is printed, overall is warning, and a warn check suggests a rename
.gitrole is invalid JSON or fails schema validationexit 1, stderr, no JSON
Invalid .gitrole defaultRole or allowedRoles nameexit 1, stderr, no JSON. See Role name format.
Another operational failure before JSON is writtenexit 1, stderr, no JSON

What's stable

The top-level field names are the contract. The meaning of overall, scope, the presence of checks, and the checks[].status vocabulary (ok, warn, info) are stable. Key order is not. checks[].message is descriptive text, not an automation surface. Adding new fields is not a breaking change. Removing or renaming documented top-level fields is.

SurfaceSafe to automate against
overallyes
commitIdentityyes
configuredIdentityyes
scopeyes
repositoryyes, but prefer presence and absence and documented fields over incidental details
sshAuthyes
repoPolicyyes
checksyes, as an ordered list of results
SurfaceGuidance
checks[].messagehuman-readable text. Don't parse this.
checks[].labeldiagnostic category string. Useful for display, not a closed vocabulary.
repository.currentBranchuseful context, not the primary contract surface
repository.topLevelPathuseful context, not the primary contract surface

Nested fields above are documented for meaning and current shape. Additive changes may happen over time.

gitrole resolve --json

Use this when you need the .gitrole policy and you don't need a diagnosis. status and doctor still run when the file is absent. This command doesn't.

Signature

gitrole resolve --json

Example

gitrole resolve --json with defaultRole work and allowedRoles work and maintainer-personal. Exit 0:

{
  "version": 1,
  "defaultRole": "work",
  "allowedRoles": [
    "work",
    "maintainer-personal"
  ]
}

Fields

FieldWhat it tells you
versionPolicy schema version. Currently always 1.
defaultRoleThe preferred role for this repo
allowedRolesRoles that are valid here. defaultRole is always included.

Exit codes

CodeMeaning
0Policy resolved. JSON on stdout.
1Failure. Error on stderr. No JSON.

When it fails

resolve --json doesn't emit empty success output when .gitrole is missing. It exits 1, writes the error to stderr, and prints no JSON. That is different from status and doctor, which still run without a policy file.

ConditionResult
Not inside a Git repoexit 1, stderr message, no JSON
No .gitrole file existsexit 1, stderr message, no JSON
.gitrole is invalid JSON or fails schema validationexit 1, stderr message, no JSON
invalid defaultRole or allowedRoles nameexit 1, stderr message, no JSON

What's stable

The field names version, defaultRole, and allowedRoles are the contract. version is 1. Key order is not.

Role name format

Saved role names use this format so machine-readable values such as role= stay unambiguous. The same rules apply to .gitrole defaultRole and every allowedRoles entry.

RuleAllowed
letterslowercase a-z only
digits0-9
separators-, _
disallowedspaces, slashes, uppercase, and other punctuation
ExampleValid
workyes
personalyes
client-acmeyes
agent_botyes
client acmeno
Workno
my@roleno
no-roleno. Reserved sentinel.

Creating a role with an invalid name prints:

error: invalid role name "client acme"; use lowercase letters, numbers, "-" or "_"

no-role matches the shape above, and it is still reserved. status --short writes role=no-role when no saved role matches. gitrole add no-role and gitrole import current --name no-role exit 1, write this to stderr, and print nothing on stdout:

error: role name "no-role" is reserved for the status and prompt sentinel when no saved role matches; choose a different name

A saved role that already uses that name isn't deleted or renamed. gitrole doctor warns and suggests adding the identity under a new name, then gitrole remove no-role. gitrole status still prints its line. The prompt segment still chooses ✓ or ⚠ from the status fields only, so a hand-built aligned line with role=no-role stays gitrole:no-role ✓.

If saved role data already contains a name outside this format, commands that load saved roles fail closed: exit 1, error on stderr, nothing on stdout. That includes gitrole status --short and gitrole doctor --json. The reserved name no-role is not that failure.

An invalid policy name is fail-closed for the same reason: a bad name would otherwise show up as a role= value scripts can't trust. These commands exit 1, write the error to stderr, and write nothing to stdout:

  • gitrole resolve
  • gitrole resolve --json
  • gitrole status
  • gitrole status --short
  • gitrole doctor
  • gitrole doctor --json

gitrole status --short loads the policy before the one-line contract is written. The field names and order for a valid policy are unchanged.

A defaultRole of client acme produces this stderr line and no stdout:

error: repo policy file .gitrole is invalid: defaultRole invalid role name "client acme"; use lowercase letters, numbers, "-" or "_"

An invalid allowed role is reported on that field. With defaultRole set to work and Client in allowedRoles:

error: repo policy file .gitrole is invalid: allowedRoles invalid role name "Client"; use lowercase letters, numbers, "-" or "_"

A missing .gitrole file is not this failure for status or doctor. Those commands still run without repo policy when the file is absent. resolve and resolve --json still exit 1 when the file is missing. Valid names such as client-acme and agent_bot still succeed.

What this page doesn't cover

These commands check. They don't switch roles, rewrite remotes, or install hooks. Human-readable gitrole status and gitrole doctor text isn't a parse contract. The prompt segment glyphs are in Show gitrole in your shell prompt.

Effective ordinary-commit identity

Gitrole asks Git for GIT_AUTHOR_IDENT and GIT_COMMITTER_IDENT. Included configuration, author/committer-specific settings, worktree/system/command configuration and environment overrides participate in Git’s precedence. Role matching, current and import current use the effective author. A differing or unavailable committer warns rather than proving alignment. doctor --json additionally reports committerIdentity with the same name/email value/source shape as commitIdentity.

Config includes retain their reported Git scope. git identifies Git-generated fallback values; env identifies environment input. Aggregate scope reports the underlying author configuration; environment overrides retain that convention. hasLocalOverride includes local and worktree configuration. The source and scope vocabularies are expanded: strict consumers accepting only the older values must update. The eight short fields, their order and exits remain unchanged.

This is a snapshot for an ordinary commit in the checked context. A later git commit --author argument or changed config/environment can change identity after the check; Gitrole cannot predict it.

SSH diagnostic message strings are explanatory prose, not reason codes. Human output may summarize additional differences and point to gitrole doctor --json, whose existing message fields retain the full explanation. The human expected label identifies the saved account; JSON keys and structural values are unchanged. Short status retains the same eight fields and exit codes.