specgate/docs v0.20.0

The agent contract#

How a machine drives this CLI. The rules are fixed by ADR-0017; this document is generated from the source so it cannot drift from it.

The rules#

  1. One JSON document per invocation. In --json mode stdout carries exactly one JSON document. Prose, progress and warnings go to stderr, so cmd --json 2>/dev/null | jq . always parses.

  2. One diagnostic envelope, everywhere:

    json
    {
      "severity": "error" | "warning" | "info",
      "code": "snake_case_stable_string",
      "message": "human sentence",
      "target": "the thing at fault (optional)",
      "fix": "one actionable sentence or command (optional)",
      "file": "relative/path.md (optional)",
      "line": 42
    }

    Branch on code, never on message. message is prose and may be reworded; code is the published surface.

  3. Failure carries the command's null-shape. A failure prints { ...nullShape, "status": [diagnostic] } and exits 1 — never bare stderr, never a partial document. A consumer never has to tell "it failed" apart from "it printed nothing".

  4. camelCase everywhere in command output. (The on-disk formats are not command output: pack.yaml keeps schema_version and .specops.lock keeps pack_id.)

  5. --json and --format json are the same flag. The second spelling predates the contract and keeps working.

Exit codes#

SituationExit
Success, including advisory warnings0
Command failure — one JSON document with status1
Gate failure (validate, doctor, change validate)1
Usage error — unknown flag, missing or malformed argument2
Required script missing (a broken installation)3
Cancelled by the user130

A gate command exits 1 when it finds something, which is not the same as failing to run. The status array tells the two apart: a gate that could not run reports an error about itself.

Commands#

CommandDocument keyGate
specgate doctor --jsondoctoryes
specgate status --jsonstatusno
specgate alm pull --jsonpulledno
specgate check [dir] --jsoncheckyes
specgate draft --jsondraftyes
specgate verify --jsonverifyyes
specgate pet --jsonpetno
specgate matrix --jsonmatrixyes
specgate validate <dir> --jsonvalidationyes
specgate plan --jsonplanno
specgate report --jsonreportno
specgate done <REQ> --jsonrequirementno
specgate new <title> --jsonrequirementno
specgate change list --jsonchangesno
specgate change show <id> --jsonchangeno
specgate change status --jsonartifactsno
specgate change validate <id> --jsonchangeyes
specgate change archive <id> --jsonarchiveyes
specgate change instructions <artifact> --jsoninstructionsno
specgate change author <id> --jsonchangeyes
specgate specops diff --jsonchangesno
specgate harness run --format jsonresultsno
specgate harness report --jsonreportno

The document key is what the payload carries on success and what is set to null on failure. Every document also carries status: an array of diagnostics, empty when there is nothing to report.

Diagnostic codes#

Harvested from the source. A trailing * marks a family whose suffix is computed — strict_tdd_* covers strict_tdd_1 through strict_tdd_3.

CodeEmitted by
events_header_missingscripts/validate_specs.ts
feature_not_in_matrixscripts/validate_specs.ts
missing_required_filescripts/validate_specs.ts
no_featuresscripts/validate_specs.ts
requirement_cyclescripts/validate_specs.ts
self_dependencyscripts/validate_specs.ts
strict_tdd_1scripts/validate_specs.ts
strict_tdd_2scripts/validate_specs.ts
strict_tdd_3scripts/validate_specs.ts
traceability_unrecognizedscripts/validate_specs.ts
unknown_dependencyscripts/validate_specs.ts
use_cases_header_missingscripts/validate_specs.ts
archive_change_name_requiredscripts/cli/commands/change/ChangeCommand.ts
archive_draft_not_readyscripts/cli/commands/change/ChangeCommand.ts
archive_spec_update_failedscripts/cli/commands/change/ChangeCommand.ts
author_agent_failedscripts/cli/commands/change/ChangeCommand.ts
author_agent_unsetscripts/cli/commands/change/ChangeCommand.ts
author_needs_gitscripts/cli/commands/change/ChangeCommand.ts
author_out_of_scopescripts/cli/commands/change/ChangeCommand.ts
author_tree_dirtyscripts/cli/commands/change/ChangeCommand.ts
change_errorscripts/cli/commands/change/ChangeCommand.ts
change_existsscripts/cli/commands/change/ChangeCommand.ts
change_id_requiredscripts/cli/commands/change/ChangeCommand.ts
change_not_foundscripts/cli/commands/change/ChangeCommand.ts
change_requiredscripts/cli/commands/change/ChangeCommand.ts
conflicting_seed_flagsscripts/cli/commands/change/ChangeCommand.ts
invalid_change_idscripts/cli/commands/change/ChangeCommand.ts
invalid_value_drift_specscripts/cli/commands/change/ChangeCommand.ts
value_drift_already_matchesscripts/cli/commands/change/ChangeCommand.ts
value_drift_id_not_declaredscripts/cli/commands/change/ChangeCommand.ts
value_drift_no_code_valuescripts/cli/commands/change/ChangeCommand.ts
value_drift_requirement_not_foundscripts/cli/commands/change/ChangeCommand.ts
missing_proposalpackages/core/src/application/ValidateChangeUseCase.ts
no_deltaspackages/core/src/application/ValidateChangeUseCase.ts
delta_emptypackages/core/src/domain/DeltaSpec.ts
delta_unknown_sectionpackages/core/src/domain/DeltaSpec.ts
duplicate_requirementpackages/core/src/domain/DeltaSpec.ts
invalid_requirement_idpackages/core/src/domain/DeltaSpec.ts
no_rfc2119_keywordpackages/core/src/domain/DeltaSpec.ts
requirement_already_existspackages/core/src/domain/DeltaSpec.ts
requirement_without_scenariopackages/core/src/domain/DeltaSpec.ts
scenario_not_gherkinpackages/core/src/domain/DeltaSpec.ts
scenario_steps_unwrittenpackages/core/src/domain/DeltaSpec.ts
scenario_without_stepspackages/core/src/domain/DeltaSpec.ts
unknown_requirementpackages/core/src/domain/DeltaSpec.ts
archive_capability_retiredpackages/core/src/application/ArchiveChangeUseCase.ts
archive_change_not_foundpackages/core/src/application/ArchiveChangeUseCase.ts
archive_change_symlinkpackages/core/src/application/ArchiveChangeUseCase.ts
archive_feature_existspackages/core/src/application/ArchiveChangeUseCase.ts
archive_feature_regeneratedpackages/core/src/application/ArchiveChangeUseCase.ts
archive_feature_stalepackages/core/src/application/ArchiveChangeUseCase.ts
archive_no_deltaspackages/core/src/application/ArchiveChangeUseCase.ts
archive_retire_not_declaredpackages/core/src/application/ArchiveChangeUseCase.ts
archive_target_existspackages/core/src/application/ArchiveChangeUseCase.ts
archive_tasks_incompletepackages/core/src/application/ArchiveChangeUseCase.ts
traceability_upgradedpackages/core/src/application/ArchiveChangeUseCase.ts
artifact_requiredscripts/change/instructions.ts
artifact_unknownscripts/change/instructions.ts
change_not_foundscripts/change/instructions.ts
project_not_foundscripts/change/instructions.ts
ambiguous_packscripts/cli/commands/specops/ContributeCommand.ts
change_has_no_deltasscripts/cli/commands/specops/ContributeCommand.ts
change_not_foundscripts/cli/commands/specops/ContributeCommand.ts
change_requiredscripts/cli/commands/specops/ContributeCommand.ts
contribute_not_pushedscripts/cli/commands/specops/ContributeCommand.ts
contribute_stage_failedscripts/cli/commands/specops/ContributeCommand.ts
no_lockfilescripts/cli/commands/specops/ContributeCommand.ts
nothing_to_contributescripts/cli/commands/specops/ContributeCommand.ts
pack_has_no_reposcripts/cli/commands/specops/ContributeCommand.ts
pack_not_foundscripts/cli/commands/specops/ContributeCommand.ts

Worked example#

bash
specgate change instructions specs --json 2>/dev/null | jq -r '.instructions.rules[]'

An agent that reads fix self-corrects; one that reads only message retries the same mistake. That is why a check without a fix is treated as unfinished.