Skip to content

Stand Operations

The stand is a singleton live environment shared by the agent fleet. Agents coordinate access through a lease-backed state file, and coordd prepares the active lease by running an Ansible YAML profile.

A stand can be local (STAND_HOST=localhost) or remote (STAND_HOST as an SSH config alias or comma-separated aliases). The profile decides what "deploy" means for the project: a smoke probe, an rsync install, a Docker Compose restart, a Vite dev server, or any other Ansible-native deployment sequence. greatminds owns the lease/state/evidence protocol; the project owns the playbook content.

Project Environment

Put machine-local stand values in .greatminds/PROJECT.env:

STAND_HOST=localhost
STAND_USER=violet

PROJECT.env is sourced before agents start and passed to stand profiles as Ansible extra vars. In a profile, STAND_HOST is readable as {{ STAND_HOST }}, STAND_USER as {{ STAND_USER }}, and any custom key the project adds is available the same way.

For a remote stand:

STAND_HOST=app-stand
STAND_USER=deploy

app-stand should resolve through the OS user's SSH config, known hosts, and keys. Multi-node stands can use a comma list such as STAND_HOST=web-a,web-b.

Ansible Profiles

The project profile registry lives at:

coordination/stand-profiles.yaml

Stand playbooks live under:

coordination/stand-profiles/

The lease --profile value selects a registry key, not a raw filename. The registry maps that key to a YAML playbook file:

profiles:
  smoke-only:
    file: smoke-only.yaml
    purpose: Fast reachability and readiness check.
    environment: stand
    used_for: [deploy_only, warmup, quick_readiness]
    default_for: [verify_only]

For example, --profile smoke-only records active_lease.profile: smoke-only and runs the registry entry's file, usually coordination/stand-profiles/smoke-only.yaml. The same profile name can point at a differently named file, such as file: smoke.yaml, when a project wants that convention.

used_for is the capability list for the profile. default_for assigns common role intents such as feature_test, explorer, reviewer, live_developer, production_deploy, and production_review to a profile. The vocabulary is defined in the packaged schema copied to .greatminds/schema.yaml under stand_profile_registry. Each default_for token can belong to only one profile, so automated role selection is unambiguous.

Use the registry commands after edits:

greatminds stand profiles list
greatminds stand profiles doctor

coordd runs the profile with Ansible and injects:

  • every key from .greatminds/PROJECT.env;
  • the active lease id as lease_id;
  • the task id as task_id;
  • the isolated task worktree path as worktree.

A minimal smoke profile:

---
- name: register stand node
  hosts: localhost
  gather_facts: false
  tasks:
    - name: add configured stand host
      ansible.builtin.add_host:
        name: "{{ STAND_HOST | default('localhost') }}"
        groups: stand_nodes
        ansible_connection: >-
          {{ 'local' if (STAND_HOST | default('localhost')) == 'localhost'
             else 'ssh' }}

- name: smoke stand
  hosts: stand_nodes
  gather_facts: false
  tasks:
    - name: remote shell works
      ansible.builtin.command: /bin/true
      changed_when: false

For a deploy profile, add tasks that copy or build from {{ worktree }} and then run readiness probes. The shipped full-deploy.yaml, vite-dev.yaml, and smoke-only.yaml files are reference profiles, not hidden deployment logic. Register additional project profiles in coordination/stand-profiles.yaml.

For a production deployment or post-deploy review, create a profile with:

profiles:
  production:
    file: production.yaml
    purpose: Production deployment and post-deploy verification.
    environment: production
    requires_explicit_user_approval: true
    allowed_roles: [ARCHITECT-REVIEWER, MAINTAINER]
    used_for: [production_deploy, production_post_deploy_review]
    default_for: [production_deploy, production_review]

The CLI enforces allowed_roles and requires an explicit approval marker:

greatminds stand lease \
  --task <task-id> \
  --profile production \
  --profile-approval USER_APPROVED

State File

The source of truth is:

.greatminds/.stand/state.yaml

greatminds stand status reads that file and prints the current state, active lease, pending FIFO queue, and recent transition history. Use the CLI output as the source of truth; do not inspect or edit state.yaml directly.

The stand has four states:

State Meaning
free No active lease. The next lease can be granted immediately.
preparing coordd is deploying or restarting the stand for the active lease.
ready The stand is ready for the lease holder to run probes.
down Stand operation is paused because deploy or infrastructure recovery is needed.

Each active lease carries a lease_id, task id, worktree path, profile name, profile file, holder role, grant timestamp, optional ready timestamp, and TTL. Pending leases wait in FIFO order in the same state file.

Lease Flow

The role that needs a live stand requests a lease:

greatminds stand lease --task <task-id> --worktree <path> --profile <profile>

--worktree must point at the task's isolated worktree under <project>/.worktrees/, not the main fleet checkout. The usual form is:

greatminds stand lease \
  --task <task-id> \
  --worktree "$(greatminds worktree path <task-id>)" \
  --profile full-deploy

If the stand is free, the lease becomes active and the state moves to preparing. If another lease is active, the new lease is appended to the FIFO queue. The command prints the lease_id; the requester must keep that token.

coordd watches .greatminds/.stand/state.yaml and runs the active lease profile file selected by active_lease.profile. Operators can inspect progress with:

greatminds stand status

When state is preparing, coordd deploys from the lease worktree using the lease profile. On success it records a deploy marker and transitions the stand to ready, equivalent to:

greatminds stand ready --lease-id <lease-id>

That moves the state to ready and files an inbox-info message to the holder: stand lease <lease-id> ready; task=<task-id>. The ready transition is valid only after the configured stand profile has run and left its deploy marker.

The holder, usually TESTER or EXPLORER, then runs its own probes against the prepared stand and releases the lease:

greatminds stand release --lease-id <lease-id> --result pass|fail|partial

Only the lease holder can release an active lease. Releasing moves the stand back to free; coordd promotes the next queued lease on a later tick.

TTL And Recovery

Leases have a default TTL from the packaged schema copied to .greatminds/schema.yaml (stand.resource.lease), currently four hours, with a warning window before automatic release. The TTL is a safety valve for abandoned leases; it is not a substitute for explicitly releasing a lease after probes finish.

If deployment fails or the stand has an infrastructure incident, the operator or maintainer runs:

greatminds stand down --reason "<operational reason>"

down pauses queue processing. After recovery:

greatminds stand up --reason "<recovery note>"

That returns the stand to free so queued work can resume.

Evidence Boundary

The coordd deploy step only proves infrastructure readiness: process status, health endpoint availability, remote reachability, GPU availability when requested, and equivalent bring-up checks. It does not run product acceptance tests.

Functional verification belongs to the lease holder. For stand-required tasks, TESTER records the lease evidence in the product task's tests block, including stand_evidence.lease_id, result, commit, worktree fingerprint, and its own functional_probes plus stand_evidence.tester_observations. greatminds gate-check <task-id> reads that tests-block evidence first.

Use the lease commands for all stand operations. Stand readiness belongs in .greatminds/.stand/state.yaml, and product validation evidence belongs in the task's tests block.