Sirius Forester GitHub ↗
δ Contracts

Contracts

The concrete interfaces where Sirius meets its parents — the ledger schema, the sirius --json shapes, the config file, and the ground-truth CLI deltas.

This is the reference surface: the ledger schema, the sirius --json output shapes, the policy config, and the exact parent-CLI calls Sirius makes. It mirrors the repo’s CONTRACTS.md, which stays the source of truth for contributors.

The ledger

.sirius/sirius.db is Sirius’s only write target (SQLite, WAL mode), created by sirius init, read-only to the console. Five tables:

-- schema_version = 1
CREATE TABLE meta (
  key   TEXT PRIMARY KEY,
  value TEXT NOT NULL
);  -- rows: schema_version, created_at, sirius_version

CREATE TABLE workers (
  id           TEXT PRIMARY KEY,          -- 'sirius/oak'
  created_at   TEXT NOT NULL,             -- ISO-8601 UTC
  last_seen_at TEXT,
  status       TEXT NOT NULL DEFAULT 'idle' -- idle|working|blocked|stopped
);

CREATE TABLE iterations (
  id            INTEGER PRIMARY KEY AUTOINCREMENT,
  worker_id     TEXT NOT NULL REFERENCES workers(id),
  issue_ref     TEXT,                     -- 'AMT-7'
  entities      TEXT,                     -- JSON array of hayven entity ids
  started_at    TEXT NOT NULL,
  ended_at      TEXT,
  outcome       TEXT,                     -- completed|released|deadend|gate_failed|error
  gate_result   TEXT,                     -- pass|fail|skipped|null
  oracle_verdicts TEXT,                   -- JSON array (per-entity)
  tokens        INTEGER,
  duration_ms   INTEGER,
  receipt_id    INTEGER REFERENCES receipts(id)
);

CREATE TABLE receipts (
  id            INTEGER PRIMARY KEY AUTOINCREMENT,
  kind          TEXT NOT NULL,            -- 'issue' | 'decision'
  ref           TEXT NOT NULL,            -- 'AMT-7' or 'D-3'
  symbols       TEXT NOT NULL,            -- JSON array of entity ids stamped
  forward_ok    INTEGER NOT NULL DEFAULT 0, -- amt comment landed (0/1)
  reverse_ok    INTEGER NOT NULL DEFAULT 0, -- hayven remember landed (0/1)
  created_at    TEXT NOT NULL,
  worker_id     TEXT
);

CREATE TABLE policy_events (
  id           INTEGER PRIMARY KEY AUTOINCREMENT,
  iteration_id INTEGER REFERENCES iterations(id),
  kind         TEXT NOT NULL,            -- claim_order|backoff_409|oracle_202|gate_tier|retry_budget|concurrency
  detail       TEXT,                     -- JSON
  created_at   TEXT NOT NULL
);

The sirius --json shapes

Every mutating command accepts --json and prints exactly one JSON object to stdout (logs go to stderr). Exit codes: 0 ok, 1 failure, 2 usage, 3 blocked.

sirius init          -> {"ok":true,"ledger":".sirius/sirius.db","schema_version":1}
sirius doctor --json -> {"ok":bool,"checks":[{"name":str,"pass":bool,"detail":str}, ...]}

sirius link AMT-7 --symbols a,b,c [--changed] --json
  -> {"ok":true,"receipt_id":12,"kind":"issue","ref":"AMT-7",
      "symbols":["a","b","c"],"forward_ok":true,"reverse_ok":true}

sirius why <symbol> --json -> {"symbol":str,"issues":[{"ref","title"}],"decisions":[{"ref","summary"}]}
sirius why AMT-7 --json    -> {"ref":"AMT-7","symbols":[str],"decisions":[str]}

sirius gate AMT-7 [--tier safe] [--target-status in_review] [--range <git-range>] --json
  -> {"ok":bool,"issue":"AMT-7","tier":"safe","gate":"pass|fail",
      "plan":"subset(n)|full-suite|blocked|pass-with-warning|unconfigured",
      "ran_tests":bool,"advanced_to":"in_review"|null,
      "tests_selected":int,"comment_filed":bool}

sirius run --workers N --agent-cmd "<cmd>" [--from todo] --json
  # streams NDJSON, one iteration event per line:
  -> {"event":"iteration","worker":"sirius/oak","issue":"AMT-7","phase":"claim|map|lock|brief|work|gate|receipt|release", ...}

Policy config

.sirius/config.jsonsirius init writes these defaults; an absent file means the same defaults. The console displays it read-only.

{
  "claim_order_enforced": true,
  "backoff_409": { "strategy": "release_and_comment", "base_ms": 500, "max_ms": 8000 },
  "oracle_202": "back-off",
  "gate_tier": "safe",
  "target_status": "in_review",
  "retry_budget": 3,
  "worker_concurrency": 3,
  "claim_mode": "adaptive"
}

Parent CLIs

Sirius never writes the parents’ databases. All Ametrite writes go through amt … --json; all Hayvenhurst reads and writes go through the hayven CLI or http://localhost:7777. A few ground-truth details, verified against the installed amt 0.1.0 and hayven 0.0.5:

  • --json is a global flag for amt (amt --json claim …), emitted before the subcommand.
  • amt claim returns the full issue object on success (Sirius keys off the presence of id); on no-work it returns {"claimed":false,"retry_after":N,…}.
  • Ametrite’s schema version lives in its meta table, not PRAGMA user_version; a fresh workspace reads v4, and sirius doctor checks >= 3 rather than an exact match.
  • hayven affected-tests is a selector, not a runner. Its exit code means “selection computed,” never “the tests pass.” Sirius owns running the tests: it trusts a narrow selection only when the command succeeded, roots > 0, the note raises no stale/under-report flag, and no global-impact file (Cargo.toml, package.json, .github/, …) changed — otherwise it falls back to the full suite, then takes the verdict from the test runner’s own exit code.
  • The daemon on :7777 is single-project-bound. A call against a workspace whose daemon isn’t the one on :7777 fails; when the workspace mismatches, forward stamping (amt) still lands while reverse stamping (hayven remember) reports reverse_ok:false.