loki-mode

Workspaces: one issue across several repos

On by default. Set LOKI_WORKSPACES=0 to disable. Design: docs/v10/D51-PHASE-B.md.

Define a group of repos once in loki.yaml:

workspaces:
  shop:
    repos:
      - {repo: acme/api, path: ~/src/api, setup: "npm ci"}
      - {repo: acme/web, path: ~/src/web, after: [acme/api]}
    integration: {command: "make e2e", timeout_s: 900, per_stage: false}
    concurrency: 2
    plan: false

The workspaces: block is validated and fails closed: a misspelled key (for example afer) or a wrong type exits 2 and names the key.

Repos with no pending after run in parallel, at most concurrency at a time (default 2; set LOKI_WORKSPACE_CONCURRENCY to change it).

Then:

loki workspace list
loki workspace show shop
loki workspace run shop acme/api#12
loki start --workspace shop "<brief>"   # same runner, from the primary command
loki workspace infer shop        # print suggested after: edges
loki workspace status            # every recorded run, one row per repo
loki workspace status <run-id>

Verify a workspace run

After loki workspace run <name> <ref> finishes, check the evidence rather than trusting the exit code alone:

  1. Exit code: 0 means every repo succeeded and integration did not fail; 3 means only budget stops; 1 is any other failure.
  2. loki workspace status prints one row per repo and the integration status for each recorded run.
  3. .loki/workspaces/<name>/<run-id>/integration.json holds status, the head SHA per repo and timing.
  4. python3 autonomy/lib/workspace_metrics.py <run-dir> --attention-min <minutes> prints prs, prs_per_hour and prs_per_attention_minute; any value it cannot measure prints unmeasured.
  5. Each repo PR for the run branch carries exactly one integration comment.
  6. loki verify <run-dir>/composite.json re-checks the composite offline (add --pubkey FILE on another machine). It recomputes the composite hash, re-verifies every child receipt from disk and checks the signature. It prints the integration result and every repo head SHA it attests. It is TAMPERED for a tampered or swapped child, a child head that is not the integrated head, a failed or interrupted integration status or nonzero exit code, an edited integration section, or an integration.log that no longer matches its recorded hash. It is UNCHECKED for an unsigned child under a signed composite, and for a legacy composite that predates the integration section and has no integrated head to compare. Deployed-SHA binding is partial: loki deploy --execute ships a signed .well-known/loki-provenance.json (head_sha, tree_sha, receipt_sha256, run_id, built_at) in the static root of a single-repo nextjs, Vite or built-static project (public/, dist/ or build/) and, when the provider prints a URL, fetches it back through loki verify --url --expect-sha. The deploy record’s live_binding is VERIFIED only when the served doc is signed and names the anchor SHA; a stale or forged doc is FAILED (FAILED_HEALTH, exit 4), an unreachable one is unchecked, and docker, python and node projects (no static root) are unsupported. A mismatch is retried inside the health attempts and interval budget before it is FAILED, so a URL still serving the previous build for a few seconds is not rolled back at once. A live doc that is unsigned or signed with an unknown key is unchecked, not FAILED, so a stale non-loki build serving such a doc passes silently. Under --allow-unanchored the anchor is UNPROVEN, so no doc is published and live_binding is unanchored. The deploy appends **/.well-known/loki-provenance.json to .git/info/exclude and leaves the file in the working tree. Every loki deploy --execute first removes that file from public/, dist/ and build/, so only a doc written by the current deploy ships; an unanchored, unsupported or unsigned deploy ships none. A provider run by hand outside loki deploy --execute uploads whatever doc the last loki deploy left, which names that deploy’s SHA, not the new build. If the provider rebuilds remotely from a gitignored root, the doc is lost and the result is unchecked. None of this binds a composite: the composite attests what was integrated and tested, not what is running anywhere, and no workspace deploy writes or probes a provenance doc. Exit codes are the single-receipt ones: 0 VERIFIED, 1 TAMPERED, 2 UNCHECKED, 3 UNSIGNED. The composite never reads higher than its weakest child: one PARTIAL child caps it at PARTIAL.
  7. A run that could not seal prints composite not sealed: <reason> (for example a repo with no receipt). Seal by hand with loki verify --seal-composite <run-dir>; it refuses unless every child has a receipt.

Not measured: no scale test exists for large repo counts. Claim only what you have run.

tests/workspace/99-e2e.sh runs this whole check through both autonomy/loki and bin/loki with a stub launcher and a stub gh.