loki-mode 11.4.2 → 11.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/SKILL.md +2 -2
  2. package/VERSION +1 -1
  3. package/autonomy/lib/chat.sh +6 -3
  4. package/autonomy/lib/engine10-push.sh +19 -1
  5. package/autonomy/lib/help-core.sh +1 -1
  6. package/autonomy/lib/proof-generator.py +23 -5
  7. package/autonomy/lib/proof-verify.py +607 -5
  8. package/autonomy/lib/run-summary.sh +17 -1
  9. package/autonomy/lib/setup-requirements.json +35 -0
  10. package/autonomy/loki +244 -237
  11. package/autonomy/notification-checker.py +1 -1
  12. package/autonomy/provider-offer.sh +95 -47
  13. package/autonomy/route.sh +38 -6
  14. package/autonomy/run.sh +42 -14
  15. package/autonomy/setup-wizard.sh +19 -10
  16. package/autonomy/verify.sh +40 -1
  17. package/completions/_loki +3 -1
  18. package/completions/loki.bash +9 -3
  19. package/dashboard/__init__.py +3 -10
  20. package/dashboard/control.py +5 -435
  21. package/docs/ARCHITECTURE.md +1 -2
  22. package/docs/CLI-CORE-VERBS.md +2 -6
  23. package/docs/CLI-REFERENCE.md +3 -1
  24. package/docs/COMPONENTS.md +0 -2
  25. package/docs/EVALUATING.md +27 -0
  26. package/docs/HARNESS-INTELLIGENCE-PLAN.md +20 -0
  27. package/docs/INSTALLATION.md +1 -1
  28. package/docs/KNOWN-ISSUES.md +10 -1
  29. package/docs/PROOF-PASSPORT.md +114 -0
  30. package/docs/alternative-installations.md +103 -0
  31. package/docs/authentication.md +19 -1
  32. package/docs/cost-controls.md +139 -0
  33. package/docs/dev/release-checklist.md +9 -8
  34. package/docs/enterprise/security.md +3 -234
  35. package/docs/environment-variables.md +36 -0
  36. package/docs/exit-codes.md +102 -2
  37. package/docs/spec-first-intent.md +29 -0
  38. package/docs/v10/ARCHITECT-CUTS.md +304 -0
  39. package/docs/v10/CLI-MODERN.md +2 -2
  40. package/docs/v10/DECISIONS.md +5 -5
  41. package/docs/v10/ENGINE.md +735 -267
  42. package/docs/v10/GUARDS.md +16 -14
  43. package/docs/v10/GUIDE.md +5 -7
  44. package/docs/v10/PROGRESS.md +12 -1
  45. package/docs/v11/MARKET-SLICES.md +1 -5
  46. package/integrations/openclaw/README.md +20 -0
  47. package/integrations/vibe-kanban.md +12 -50
  48. package/loki-ts/data/model-pricing.json +1 -0
  49. package/loki-ts/dist/loki.js +1156 -1103
  50. package/mcp/__init__.py +1 -1
  51. package/mcp/_sdk_loader.py +1 -1
  52. package/package.json +2 -2
  53. package/packages/control-plane/dist/server.js +2063 -1032
  54. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  55. package/plugins/loki-mode/README.md +41 -1
  56. package/plugins/loki-mode/commands/handoff.md +12 -0
  57. package/plugins/loki-mode/commands/loki-verify.md +9 -2
  58. package/plugins/loki-mode/commands/start.md +20 -0
  59. package/plugins/loki-mode/hooks/hooks.json +11 -0
  60. package/plugins/loki-mode/scripts/loki-stop-verify.sh +84 -0
  61. package/references/quality-control.md +1 -116
  62. package/skills/00-index.md +2 -2
  63. package/skills/artifacts.md +0 -22
  64. package/skills/production.md +2 -38
  65. package/web-app/dist/assets/{AdminPage-CYJ2BWRC.js → AdminPage-DhXdJNQk.js} +1 -1
  66. package/web-app/dist/assets/{Avatar-DdK4wynI.js → Avatar-ixVj76fM.js} +1 -1
  67. package/web-app/dist/assets/{Badge-CrX3MI7J.js → Badge-BGMSp22s.js} +1 -1
  68. package/web-app/dist/assets/{Button-709-wwGM.js → Button-pYJc32tG.js} +1 -1
  69. package/web-app/dist/assets/{CockpitPage-BapdTInO.js → CockpitPage-C1JBBouA.js} +1 -1
  70. package/web-app/dist/assets/{ComparePage-DT99pGvA.js → ComparePage-qD0NV4RK.js} +1 -1
  71. package/web-app/dist/assets/{ErrorBoundary-B0uJSys8.js → ErrorBoundary-C-yxO58j.js} +1 -1
  72. package/web-app/dist/assets/{EvidenceReceiptPanel-BtMFgElP.js → EvidenceReceiptPanel-ByPzuE1S.js} +1 -1
  73. package/web-app/dist/assets/{GitHubIssuesPanel-DjJuU941.js → GitHubIssuesPanel-BNv_2BEJ.js} +1 -1
  74. package/web-app/dist/assets/{GitHubPRsPanel-HXfEX1BC.js → GitHubPRsPanel-CuLZQ9CS.js} +1 -1
  75. package/web-app/dist/assets/{HomePage-Cqyves-S.js → HomePage-CS_FW8Je.js} +2 -2
  76. package/web-app/dist/assets/{LoginPage-jUbtC7_Y.js → LoginPage-DwiNf1Wm.js} +1 -1
  77. package/web-app/dist/assets/{MagicPage-DHRsqjgR.js → MagicPage-CmQybLPZ.js} +1 -1
  78. package/web-app/dist/assets/{MetricsPage-B-pED05R.js → MetricsPage-D9ERdpv8.js} +1 -1
  79. package/web-app/dist/assets/{NotFoundPage-CsbUs21R.js → NotFoundPage-D9ccgdbF.js} +1 -1
  80. package/web-app/dist/assets/{ProjectPage-BtlLEQwB.js → ProjectPage-B9Jslmgv.js} +3 -3
  81. package/web-app/dist/assets/{ProjectsPage-DaRbRjSp.js → ProjectsPage-DnU99Go2.js} +1 -1
  82. package/web-app/dist/assets/{SettingsPage-DALrtP3F.js → SettingsPage-DcTyx6wo.js} +1 -1
  83. package/web-app/dist/assets/{ShowcasePage-CoPHSNP0.js → ShowcasePage-jEY9ghSr.js} +1 -1
  84. package/web-app/dist/assets/{SystemSettingsPage-kOuSN50O.js → SystemSettingsPage-CSGltW-8.js} +1 -1
  85. package/web-app/dist/assets/{TeamsPage-DGWK0LMp.js → TeamsPage-gUuJXb9l.js} +1 -1
  86. package/web-app/dist/assets/{TemplatesPage-C-_10CwA.js → TemplatesPage-37Zhe--v.js} +1 -1
  87. package/web-app/dist/assets/{TerminalOutput-Cz_vPbXL.js → TerminalOutput-B09hRwaR.js} +1 -1
  88. package/web-app/dist/assets/{activity-icIsXJVa.js → activity-q_4MY9J0.js} +1 -1
  89. package/web-app/dist/assets/{bell-DbPR2_4r.js → bell-X8OC_PFP.js} +1 -1
  90. package/web-app/dist/assets/{bot-DvjP3Yh5.js → bot-C-N1PUOD.js} +1 -1
  91. package/web-app/dist/assets/{check-D1OBxHeU.js → check-B2c76Jmp.js} +1 -1
  92. package/web-app/dist/assets/{chevron-left-D09jDZ29.js → chevron-left-BF76YLNw.js} +1 -1
  93. package/web-app/dist/assets/{circle-alert-DyZMLOcC.js → circle-alert-AeFKBD8h.js} +1 -1
  94. package/web-app/dist/assets/{clock-CdpdJTId.js → clock-CiXRf-FH.js} +1 -1
  95. package/web-app/dist/assets/{cloud-CoLTlmoj.js → cloud-DKQ8SRJ5.js} +1 -1
  96. package/web-app/dist/assets/{code-xml-DrkE2ZZY.js → code-xml-DEb6jXWP.js} +1 -1
  97. package/web-app/dist/assets/{database-CMQQuM2R.js → database-6W7e-E9c.js} +1 -1
  98. package/web-app/dist/assets/{dollar-sign-CuioDaOG.js → dollar-sign-Ds2GOuyW.js} +1 -1
  99. package/web-app/dist/assets/{file-code-corner-Cx9j9K1S.js → file-code-corner-CZU0jg52.js} +1 -1
  100. package/web-app/dist/assets/{file-plus-CYD2zaaI.js → file-plus-DTVYnXCN.js} +1 -1
  101. package/web-app/dist/assets/{globe-6w-hnQkL.js → globe-0mUzKYDe.js} +1 -1
  102. package/web-app/dist/assets/{hammer-BpqtautL.js → hammer-BMHZg3YX.js} +1 -1
  103. package/web-app/dist/assets/{index-Dzs-9EgJ.js → index-BvOTvjax.js} +3 -3
  104. package/web-app/dist/assets/{layers-D5hd7jb7.js → layers-Df7TdcWB.js} +1 -1
  105. package/web-app/dist/assets/{loader-circle-C6Y9x_7H.js → loader-circle-kPsVfrSY.js} +1 -1
  106. package/web-app/dist/assets/{lock-D51L7rYh.js → lock-CXYcbEc5.js} +1 -1
  107. package/web-app/dist/assets/{package-BJF8hkP8.js → package-Cqhua1ID.js} +1 -1
  108. package/web-app/dist/assets/{plus-D5kUQ79n.js → plus-lseXhsn-.js} +1 -1
  109. package/web-app/dist/assets/{refresh-cw-BKdlCa7F.js → refresh-cw-OZriI6gD.js} +1 -1
  110. package/web-app/dist/assets/{rotate-ccw-_bd_YI1e.js → rotate-ccw-CKCfjPg8.js} +1 -1
  111. package/web-app/dist/assets/{scroll-text-DbkegIxF.js → scroll-text-DoVyyfKb.js} +1 -1
  112. package/web-app/dist/assets/{server-DAwqFIsq.js → server-DGy7TflW.js} +1 -1
  113. package/web-app/dist/assets/{shield-alert-BuR1tPHu.js → shield-alert-BCzRcMXi.js} +1 -1
  114. package/web-app/dist/assets/{thumbs-up-BF6p3mRd.js → thumbs-up-C_znJd5g.js} +1 -1
  115. package/web-app/dist/assets/{trash-2-BVpnkqqz.js → trash-2-BosCbeeA.js} +1 -1
  116. package/web-app/dist/assets/{trending-down-DpsDvRwW.js → trending-down-CkR5zDkV.js} +1 -1
  117. package/web-app/dist/assets/{trending-up-mFogTjt3.js → trending-up-DpUIfOH4.js} +1 -1
  118. package/web-app/dist/assets/{upload-4T8zpBsi.js → upload-CH9aKc8L.js} +1 -1
  119. package/web-app/dist/assets/{usePolling-DPXQv8WO.js → usePolling-pzxrHJeg.js} +1 -1
  120. package/web-app/dist/assets/{user-BYGIFaTm.js → user-B7OHa-VQ.js} +1 -1
  121. package/web-app/dist/index.html +1 -1
  122. package/dashboard/run.py +0 -63
  123. package/docs/openclaw-integration.md +0 -52
  124. package/docs/v10/HANDOFF-NEXT-WEEK.md +0 -40
  125. package/docs/v10/MORNING-BRIEF-1003.md +0 -25
  126. package/docs/v10/MORNING-BRIEF.md +0 -52
  127. package/docs/v10/TODO.md +0 -35
  128. package/mcp/resources.py +0 -351
  129. package/mcp/tools.py +0 -200
@@ -6,153 +6,9 @@
6
6
 
7
7
  Loki Mode's security model follows a defense-in-depth approach. All security features are opt-in via environment variables, with sensible defaults that prioritize safety. When enterprise auth is not configured, the system operates in local-only mode with no network exposure.
8
8
 
9
- ## Authentication
9
+ ## Authentication and Authorization
10
10
 
11
- ### Token Authentication
12
-
13
- The primary authentication method uses bearer tokens prefixed with `loki_`.
14
-
15
- **Configuration:**
16
-
17
- ```bash
18
- # Enable token auth for the dashboard/control plane API
19
- export LOKI_ENTERPRISE_AUTH=true
20
- ```
21
-
22
- **Token Lifecycle:**
23
-
24
- ```bash
25
- # Create a new API key
26
- curl -X POST http://localhost:57374/api/keys \
27
- -H "Authorization: Bearer $ADMIN_TOKEN" \
28
- -H "Content-Type: application/json" \
29
- -d '{"name": "ci-pipeline", "role": "operator"}'
30
-
31
- # Rotate a key with 24-hour grace period
32
- curl -X POST http://localhost:57374/api/keys/ci-pipeline/rotate \
33
- -H "Authorization: Bearer $ADMIN_TOKEN" \
34
- -H "Content-Type: application/json" \
35
- -d '{"grace_period_hours": 24}'
36
-
37
- # Revoke a key
38
- curl -X DELETE http://localhost:57374/api/keys/ci-pipeline \
39
- -H "Authorization: Bearer $ADMIN_TOKEN"
40
- ```
41
-
42
- **SDK Usage:**
43
-
44
- Python:
45
- ```python
46
- from loki_mode_sdk import AutonomiClient
47
-
48
- client = AutonomiClient(
49
- base_url="http://localhost:57374",
50
- token="loki_your_token_here"
51
- )
52
- ```
53
-
54
- TypeScript:
55
- ```typescript
56
- import { AutonomiClient } from 'loki-mode-sdk';
57
-
58
- const client = new AutonomiClient({
59
- baseUrl: 'http://localhost:57374',
60
- token: 'loki_your_token_here'
61
- });
62
- ```
63
-
64
- ### OIDC / SSO Integration
65
-
66
- For organizations using identity providers (Okta, Auth0, Azure AD), Loki Mode supports OIDC-based authentication.
67
-
68
- **Configuration:**
69
-
70
- ```bash
71
- export LOKI_OIDC_ISSUER="https://your-idp.okta.com/oauth2/default"
72
- export LOKI_OIDC_CLIENT_ID="your-client-id"
73
- # Optional: audience override (defaults to LOKI_OIDC_CLIENT_ID)
74
- export LOKI_OIDC_AUDIENCE="your-api-audience"
75
- ```
76
-
77
- The system validates OIDC bearer tokens (presented as `Authorization: Bearer <jwt>`)
78
- against the issuer's JWKS endpoint. When PyJWT + cryptography are installed, the
79
- JWT signature is cryptographically verified (RS256/RS384/RS512) along with the
80
- issuer, audience, and expiry. Without PyJWT, tokens are rejected unless
81
- `LOKI_OIDC_SKIP_SIGNATURE_VERIFY=true` is set explicitly (insecure, local testing
82
- only).
83
-
84
- **Claims-to-roles mapping:**
85
-
86
- On a valid token, role/group claims are mapped to the four built-in Loki roles
87
- (`admin`, `operator`, `viewer`, `auditor`). The resolved role's scopes are then
88
- enforced through the same scope hierarchy used for API tokens. OIDC users are
89
- NOT granted full access by default.
90
-
91
- Recognized claim sources (case-insensitive; values may be a string,
92
- space-separated string, or list):
93
-
94
- | Claim | Provider |
95
- |-------|----------|
96
- | value of `LOKI_OIDC_ROLES_CLAIM` (supports dotted paths, e.g. `realm_access.roles`) | configurable |
97
- | `roles` | generic |
98
- | `groups` | generic |
99
- | `realm_access.roles` | Keycloak |
100
- | `cognito:groups` | AWS Cognito |
101
-
102
- Only claim values that exactly match a built-in role name (`admin`, `operator`,
103
- `viewer`, `auditor`) grant that role. Arbitrary group names (common in `groups`
104
- or `cognito:groups`) do not map to a role and fall through to the default.
105
-
106
- When multiple recognized roles are present, the highest-privilege match wins
107
- (precedence: `admin` > `operator` > `auditor` > `viewer`).
108
-
109
- **Default role (when no recognized role claim is present):**
110
-
111
- ```bash
112
- # Defaults to the least-privileged role (viewer). Never admin.
113
- export LOKI_OIDC_DEFAULT_ROLE="viewer"
114
- ```
115
-
116
- If `LOKI_OIDC_DEFAULT_ROLE` is unset or set to an unrecognized value, it falls
117
- back to `viewer`. The default is never `admin` / full access.
118
-
119
- ## Authorization
120
-
121
- ### Role-Based Access Control
122
-
123
- Four built-in roles provide scoped access:
124
-
125
- | Role | Scope | Permissions |
126
- |------|-------|-------------|
127
- | `admin` | Full control | Create/delete tenants, manage API keys, configure policies, all operator/viewer/auditor permissions |
128
- | `operator` | Execution | Start/cancel runs, create/update projects and tasks, read all resources |
129
- | `viewer` | Read-only | Read projects, runs, tasks, status. No write or delete operations |
130
- | `auditor` | Audit access | Read audit logs, verify chain integrity, generate compliance reports. No execution permissions |
131
-
132
- **Scope Hierarchy:**
133
-
134
- ```
135
- admin > operator > viewer
136
- admin > auditor
137
- ```
138
-
139
- Admin inherits all permissions. Operator inherits viewer permissions. Auditor is a separate branch with read-only access to audit data.
140
-
141
- **API Key Scopes:**
142
-
143
- API keys are created with a role assignment:
144
-
145
- ```bash
146
- # Create a viewer key for monitoring dashboards
147
- curl -X POST http://localhost:57374/api/keys \
148
- -H "Authorization: Bearer $ADMIN_TOKEN" \
149
- -d '{"name": "grafana-reader", "role": "viewer"}'
150
-
151
- # Create an operator key for CI pipelines
152
- curl -X POST http://localhost:57374/api/keys \
153
- -H "Authorization: Bearer $ADMIN_TOKEN" \
154
- -d '{"name": "github-actions", "role": "operator"}'
155
- ```
11
+ Token auth (`LOKI_ENTERPRISE_AUTH`), key rotation with a 24-hour grace period, OIDC/SSO claim mapping and the default viewer role are documented in [authentication.md](../authentication.md). Roles (`admin`, `operator`, `viewer`, `auditor`), scope hierarchy and API key scopes are in [authorization.md](../authorization.md).
156
12
 
157
13
  ## API Security
158
14
 
@@ -221,94 +77,7 @@ This is a deliberate fail-closed design. Invalid or missing signatures always re
221
77
 
222
78
  ## Audit Logging
223
79
 
224
- ### Hash-Chained Audit Logs
225
-
226
- Audit logs use SHA-256 hash chains for tamper evidence. Each entry's hash depends on the previous entry's hash, creating an immutable chain from the genesis record.
227
-
228
- **JavaScript Audit (Agent Actions):**
229
-
230
- ```
231
- .loki/audit/audit.jsonl
232
- ```
233
-
234
- Each entry:
235
- ```json
236
- {
237
- "seq": 42,
238
- "timestamp": "2026-02-21T10:00:00.000Z",
239
- "who": "agent-1",
240
- "what": "file_write",
241
- "where": "src/app.js",
242
- "why": "implement feature",
243
- "previousHash": "abc123...",
244
- "hash": "def456..."
245
- }
246
- ```
247
-
248
- **Python Audit (Dashboard API):**
249
-
250
- ```
251
- ~/.loki/dashboard/audit/audit-2026-02-21.jsonl
252
- ```
253
-
254
- Each entry:
255
- ```json
256
- {
257
- "timestamp": "2026-02-21T10:00:00.000Z",
258
- "action": "create",
259
- "resource_type": "project",
260
- "resource_id": "proj-1",
261
- "user_id": "admin",
262
- "success": true,
263
- "_integrity_hash": "abc123..."
264
- }
265
- ```
266
-
267
- ### Chain Verification
268
-
269
- JavaScript:
270
- ```javascript
271
- const audit = require('./src/audit');
272
- audit.init('/path/to/project');
273
- const result = audit.verifyChain();
274
- // { valid: true, entries: 1542, brokenAt: null, error: null }
275
- ```
276
-
277
- Python:
278
- ```python
279
- from dashboard.audit import verify_log_integrity
280
- result = verify_log_integrity("/path/to/audit-2026-02-21.jsonl")
281
- # {"valid": True, "entries_checked": 1542, "first_tampered_line": None}
282
- ```
283
-
284
- API:
285
- ```bash
286
- curl http://localhost:57374/api/audit/verify \
287
- -H "Authorization: Bearer $TOKEN"
288
- ```
289
-
290
- ### Syslog Forwarding
291
-
292
- Forward audit events to a centralized syslog server for SIEM integration:
293
-
294
- ```bash
295
- export LOKI_AUDIT_SYSLOG_HOST="syslog.company.com"
296
- export LOKI_AUDIT_SYSLOG_PORT="514" # default
297
- export LOKI_AUDIT_SYSLOG_PROTO="udp" # or "tcp"
298
- ```
299
-
300
- Security-relevant actions (`delete`, `kill`, `stop`, `login`, `logout`, `create_token`, `revoke_token`) are logged at `WARNING` level. All other actions are logged at `INFO` level.
301
-
302
- Syslog forwarding is fire-and-forget -- failures never block the main audit write path.
303
-
304
- ### Log Rotation
305
-
306
- ```bash
307
- export LOKI_AUDIT_MAX_SIZE_MB="10" # Rotate when file exceeds this size
308
- export LOKI_AUDIT_MAX_FILES="10" # Keep this many rotated files
309
- ```
310
-
311
- Date-based log files (`audit-YYYY-MM-DD.jsonl`) are automatically rotated when they exceed the size limit. Old files are removed when the count exceeds the maximum.
80
+ Hash-chained audit logs, chain verification, syslog forwarding (`LOKI_AUDIT_SYSLOG_*`) and rotation (`LOKI_AUDIT_MAX_SIZE_MB`, `LOKI_AUDIT_MAX_FILES`) are documented in [audit-logging.md](../audit-logging.md).
312
81
 
313
82
  ## Data Residency
314
83
 
@@ -49,6 +49,7 @@ interact and why hitting one is a failure rather than a success.
49
49
  | `LOKI_ENGINE` | unset | `legacy` forces the previous loop; the `--engine` flag wins over it. |
50
50
  | `LOKI_SUMMARY_JSON` | unset | `1` prints the end-of-run summary as one JSON line on the previous loop. |
51
51
  | `LOKI_TIER` | `oss` | **Not a model setting.** The open-core licensing seam. Leave it unset. |
52
+ | `LOKI_REPO_BASELINE` | unset | `0` skips the intake repo profile and baseline test run (see docs/HARNESS-INTELLIGENCE-PLAN.md). |
52
53
  | `LOKI_ALLOW_CLAUDE_SIDECALLS` | unset | `1` (exactly) lets PRD enrichment, done recognition, the council voters, the USAGE.md refresh and the quickstart intent check prompt the `claude` CLI under a non-claude provider; `loki doctor --airgap` then reports that as required egress to Anthropic. |
53
54
 
54
55
  ### Picking a model without naming one
@@ -99,11 +100,46 @@ behaviour has a switch rather than a fork.
99
100
  | `LOKI_INJECT_FINDINGS` | on | Injects the previous iteration's reviewer findings, by severity and file:line. `0` makes the next iteration a blind retry. |
100
101
  | `LOKI_ITERATION_GRACE` | on | When the agent reports done and no gate is failing, grants ONE extra iteration past the cap to land the work. Granted at most once per run. `0` restores a pure counter. |
101
102
  | `LOKI_SCOPED_CHANGE` | auto | Forces the scoped-change profile on (`1`) or off (`0`). Auto-detected from an issue-sourced spec on a repository with real history. Never disables a trust gate; it skips phases irrelevant to a scoped fix. |
103
+ | `LOKI_RISK_TIER` | computed | Raises the verification tier (`low`, `medium`, `high`) above the one computed from the diff. Lowering is ignored unless `LOKI_RISK_OVERRIDE_REASON` is also set. |
104
+ | `LOKI_RISK_OVERRIDE_REASON` | unset | Recorded reason that lets `LOKI_RISK_TIER` lower the computed tier. Stored in the risk record and shown in the run summary. |
102
105
 
103
106
  `LOKI_INJECT_FINDINGS=0` is the one to think twice about. Without it the model
104
107
  is told to fix problems without being told which, so it re-derives them from
105
108
  scratch every iteration -- the slowest and least accurate way to converge.
106
109
 
110
+ ### Verification depth (risk tier)
111
+
112
+ The v10 verify stage classifies risk on the diff (the artifact), never on the
113
+ prompt, and records the result in `.loki/state/risk-tier.json`. The run
114
+ summary prints its one-line `summary` (also `summary.risk` under `--json`; in
115
+ default output only while the 8-line cap still holds). A record older than the
116
+ run start is ignored, and a classification failure never fails verify.
117
+ `loki review --risk` (`autonomy/lib/review_risk.py`) is a separate 0-100
118
+ advisory score and is unchanged.
119
+
120
+ - high: a sensitive path (auth, crypto, payments, migrations or `.sql`, CI config, lockfiles, infra, `.env`), a deleted test file, public API surface (openapi/swagger, `.proto`, `.d.ts`, `api/`, `routes/`, `public/`, index files), 15+ files, or 500+ changed lines.
121
+ - medium: an existing test or test config modified, 4+ files, or 100+ changed lines.
122
+ - low: everything else. Test files are never counted as sensitive.
123
+
124
+ | Tier | Selected checks | Recorded as not run |
125
+ |---|---|---|
126
+ | low | targeted tests, lint of changed files | full test command, reviewer, council, security scan (skipped by design) |
127
+ | medium | low plus full test command, one reviewer | one reviewer (and the full test command when no runner was detected) |
128
+ | high | medium plus full suite, council, security scan | council, security scan (and the full test command and full suite when no runner was detected) |
129
+
130
+ Targeted tests and lint always run. At medium and high, verify also runs the
131
+ detected full test command (the `deep.ts` per-runner table); a full-test
132
+ failure counts like any failing check, and a `not_run` result is added to
133
+ `not_proven`. Reviewer, council and security scan are not run by fast verify at
134
+ any tier, so they stay recorded as not run; nothing marks a skipped check as
135
+ passing. Record shape: `{version, tier, computed, reasons[], stats{files, lines,
136
+ sensitive[], tests_modified[], tests_deleted[], api[]}, checks_selected[],
137
+ checks_deferred[], override?, override_ignored?, summary}`.
138
+
139
+ Known gaps: the bash `run.sh` council triggers and the outcome record do not
140
+ yet use this tier; public API detection is path-based, not a semantic diff of
141
+ exports; a full-test failure that also fails on the base is not subtracted.
142
+
107
143
  ## Timeouts
108
144
 
109
145
  | Variable | Default | Effect |
@@ -24,7 +24,9 @@ is the part most likely to surprise a script author.
24
24
 
25
25
  Usage errors are distinct from run outcomes: `loki start` with an unknown
26
26
  option exits 64, and `loki quick` with no task description exits 64 (they were
27
- 1 and 2). A missing provider CLI still exits 2 before any spend. There is no
27
+ 1 and 2). A missing provider CLI still exits 2 before any spend, and so does a
28
+ working directory that is not a git repository (`engine10: not inside a git
29
+ repository`). A PRD path that does not exist also exits 2 (`PRD file not found`). There is no
28
30
  finer run-outcome signal. A gate that needs to tell "failed the quality gate"
29
31
  from "crashed" must opt into the durable contract below.
30
32
 
@@ -59,7 +61,8 @@ identically; a parity test asserts they agree status for status.
59
61
  |---|---|
60
62
  | 0 | The run completed and its diff did not weaken the tests |
61
63
  | 3 | The diff weakened the tests (a skip marker added in a test file, a test runner config changed, or an existing test file deleted or renamed). The receipt headline is NOT VERIFIED and names what was weakened, for example `NOT VERIFIED (tests weakened: skip added in sum.test.js)` |
62
- | 64 | Usage error: no task description |
64
+ | 2 | Not a git repository (engine10 preflight, before any spend), or a missing provider CLI |
65
+ | 64 | Usage error: no task description, or an unknown leading `--option` (from 11.5.0; before that it was taken as task text and started a paid run) |
63
66
  | other nonzero | The run itself failed; this code is passed through unchanged |
64
67
 
65
68
  `loki quick` has no `--json` flag; its structured result is `loki why --json`, schema: `schemas/why-result.schema.json` (`state.lastExitCode` carries the ladder above).
@@ -363,3 +366,100 @@ One provider pass, no loop.
363
366
  | 0 | Edit done (or nothing to change, or `--dry-run` plan printed) |
364
367
  | 1 | Provider pass failed, or `--verify` failed (nothing is committed) |
365
368
  | 2 | Usage or setup error (no change text, not a git repo, claude or python3 missing, unsupported provider) |
369
+
370
+ ## `loki run` (deprecated alias of `loki start <issue-ref>`)
371
+
372
+ Not changed. `loki run` with an unknown option exits 1 (it predates the 64 usage
373
+ contract and keeps its code); with no issue reference it exits 2. New scripts
374
+ should call `loki start` instead. `loki route` input errors (a missing PRD file,
375
+ or `--engine engine10` combined with a flag engine10 cannot honor) exit 2.
376
+
377
+ ## Contract cases
378
+
379
+ Each row is executed against the real CLI by `tests/test-exit-codes-documented.sh`,
380
+ which fails if an observed code differs from the Exit column. Every case stops
381
+ before any provider spend. An empty diff is a distinct code only for `loki verify`
382
+ (3). `loki edit` treats it as "nothing to change" (exit 0, see above); `loki quick`,
383
+ `loki start` and `loki run` have no empty-diff exit code, and none is asserted.
384
+
385
+ | Case | Command and setup | Exit | Meaning |
386
+ |---|---|---|---|
387
+ | `verify-empty-diff` | `loki verify` in a repo with no diff vs base | 3 | Verifier error: nothing to check is never a pass |
388
+ | `verify-not-git` | `loki verify` outside a git repository | 2 | Could not check |
389
+ | `verify-unknown-flag` | `loki verify --no-such-flag` | 64 | Usage error |
390
+ | `run-unknown-flag` | `loki run --no-such-flag` | 1 | Legacy alias keeps its pre-64 code |
391
+ | `run-no-issue-ref` | `loki run` with no argument | 2 | Issue reference required |
392
+ | `quick-unknown-flag` | `loki quick --no-such-flag` | 64 | Usage error (from 11.5.0) |
393
+ | `quick-no-task` | `loki quick` with no argument | 64 | Usage error |
394
+ | `quick-not-git` | `loki quick fix the bug now` outside a git repository | 2 | engine10 preflight, before spend |
395
+ | `edit-unknown-flag` | `loki edit --no-such-flag` | 2 | Usage error |
396
+ | `edit-no-change` | `loki edit` with no change text | 2 | Usage error |
397
+ | `edit-not-git` | `loki edit "change x"` outside a git repository | 2 | Setup error |
398
+ | `start-unknown-flag` | `loki start --no-such-flag` | 64 | Usage error |
399
+ | `start-missing-prd` | `loki start missing-prd.md` | 2 | PRD file not found |
400
+ | `start-not-git` | `loki start fix the bug now` outside a git repository | 2 | engine10 preflight, before spend |
401
+
402
+ Migration note (11.5.0): `loki quick --some-typo` used to run a paid task whose text
403
+ was the flag. It now exits 64 before any spend. Task text that starts with `--` must
404
+ be rephrased or put after other words.
405
+
406
+ ## Routing contract
407
+
408
+ Authoritative contract for which engine and adapter `loki start` and `loki quick` use for each input form. Inspect any call without running it:
409
+
410
+ ```sh
411
+ loki route start <args> --json
412
+ loki route quick <args> --json
413
+ loki --explain-route <flag-first args>
414
+ ```
415
+
416
+ The JSON carries `engine`, `adapter`, `provider`, `model`, `input_type`, `input`, `reason` and `capability_differences`. `capability_differences` is an empty list only for the full Claude Code path on the Loki 10 engine. Any compatibility route (the legacy bash loop, or a provider other than Claude) lists at least one difference.
417
+
418
+ ### Rules
419
+
420
+ - Argument order never changes the route: flags first or last, `--prd FILE` or a positional file, `--provider X` or `LOKI_PROVIDER=X`, `--engine X` or `LOKI_ENGINE=X` all resolve identically (the flag wins over the env var).
421
+ - Default engine is `engine10` for a PRD file (`.md`, `.json`, `.yaml`), an issue reference (`owner/repo#N` or an issue URL), a multi-word task and a one-word brief.
422
+ - The legacy engine runs only on `--engine legacy` or `LOKI_ENGINE=legacy`, for one call when a flag engine10 does not support is present (a notice is printed), for a provider with no engine10 invoker, or for a PRD larger than 100000 bytes.
423
+ - `--engine engine10` with an unsupported flag is refused (exit 2) instead of rerouted.
424
+ - A missing PRD file path is a usage error (exit 2).
425
+ - Adapter is `<engine>:<provider>`, with the legacy engine written `legacy-bash:<provider>`.
426
+ - Claude Code is the primary provider. The `loki chat` surface does not use this router; it is a separate interactive entry point.
427
+
428
+ ### Invocation table
429
+
430
+ The table below is rendered from live `loki route --json` output by `tests/test-engine10-dispatch.sh`. The test fails if it drifts; regenerate with `bash tests/test-engine10-dispatch.sh --update-doc`.
431
+
432
+ <!-- route-table:begin (generated by tests/test-engine10-dispatch.sh --update-doc) -->
433
+ | Invocation (after `loki route`, unless it starts with `loki` or an env var) | Engine | Adapter | Capability differences | Reason |
434
+ |---|---|---|---|---|
435
+ | `start p.md` | engine10 | engine10:claude | none | default: prd input runs on engine10 regardless of flag order |
436
+ | `start p.md --provider claude` | engine10 | engine10:claude | none | default: prd input runs on engine10 regardless of flag order |
437
+ | `start --provider claude p.md` | engine10 | engine10:claude | none | default: prd input runs on engine10 regardless of flag order |
438
+ | `start --prd p.md` | engine10 | engine10:claude | none | default: prd input runs on engine10 regardless of flag order |
439
+ | `start p.json` | engine10 | engine10:claude | none | default: prd input runs on engine10 regardless of flag order |
440
+ | `start p.yaml` | engine10 | engine10:claude | none | default: prd input runs on engine10 regardless of flag order |
441
+ | `start "fix the bug"` | engine10 | engine10:claude | none | default: task input runs on engine10 regardless of flag order |
442
+ | `start fix the bug` | engine10 | engine10:claude | none | default: task input runs on engine10 regardless of flag order |
443
+ | `loki --explain-route --json "fix the bug"` | engine10 | engine10:claude | none | default: task input runs on engine10 regardless of flag order |
444
+ | `quick "fix the bug"` | engine10 | engine10:claude | none | default: task input runs on engine10 regardless of flag order |
445
+ | `quick fix the bug` | engine10 | engine10:claude | none | default: task input runs on engine10 regardless of flag order |
446
+ | `start owner/repo#5` | engine10 | engine10:claude | none | default: issue input runs on engine10 regardless of flag order |
447
+ | `start owner/repo#5 --provider claude` | engine10 | engine10:claude | none | default: issue input runs on engine10 regardless of flag order |
448
+ | `start https://github.com/owner/repo/issues/5` | engine10 | engine10:claude | none | default: issue input runs on engine10 regardless of flag order |
449
+ | `start hello` | engine10 | engine10:claude | none | default: brief input runs on engine10 regardless of flag order |
450
+ | `start p.md --provider codex` | engine10 | engine10:codex | 1 listed | default: prd input runs on engine10 regardless of flag order |
451
+ | `start --provider codex p.md` | engine10 | engine10:codex | 1 listed | default: prd input runs on engine10 regardless of flag order |
452
+ | `LOKI_PROVIDER=codex start p.md` | engine10 | engine10:codex | 1 listed | default: prd input runs on engine10 regardless of flag order |
453
+ | `start p.md --provider cline` | engine10 | engine10:cline | 1 listed | default: prd input runs on engine10 regardless of flag order |
454
+ | `start p.md --provider aider` | engine10 | engine10:aider | 1 listed | default: prd input runs on engine10 regardless of flag order |
455
+ | `start p.md --provider opencode` | engine10 | engine10:opencode | 1 listed | default: prd input runs on engine10 regardless of flag order |
456
+ | `start p.md --provider gemini` | legacy | legacy-bash:gemini | 2 listed | provider gemini has no engine10 invoker (use claude, codex, cline, aider or opencode) |
457
+ | `start p.md --engine legacy` | legacy | legacy-bash:claude | 1 listed | explicit: --engine legacy or LOKI_ENGINE=legacy |
458
+ | `start --engine legacy p.md` | legacy | legacy-bash:claude | 1 listed | explicit: --engine legacy or LOKI_ENGINE=legacy |
459
+ | `LOKI_ENGINE=legacy start p.md` | legacy | legacy-bash:claude | 1 listed | explicit: --engine legacy or LOKI_ENGINE=legacy |
460
+ | `start p.md --engine engine10` | engine10 | engine10:claude | none | explicit: --engine engine10 |
461
+ | `LOKI_ENGINE=engine10 start p.md` | engine10 | engine10:claude | none | explicit: --engine engine10 |
462
+ | `start p.md --parallel` | legacy | legacy-bash:claude | 2 listed | flag --parallel is not supported by engine10 |
463
+ | `start --parallel p.md` | legacy | legacy-bash:claude | 2 listed | flag --parallel is not supported by engine10 |
464
+ | `start big.md` | legacy | legacy-bash:claude | 1 listed | PRD is larger than 100000 bytes (engine10 takes the task as one argument) |
465
+ <!-- route-table:end -->
@@ -26,3 +26,32 @@ A spec that does not parse exits 2 before any run state is created, naming the f
26
26
  ## Unchanged without the flags
27
27
 
28
28
  Without `--spec` or `--spec-first`, `loki plan` stays on its existing route, `loki start` behaves as before, and the plan and implement briefs are byte-identical (pinned by golden fixtures in `loki-ts/tests/engine10/fixtures`).
29
+
30
+ ## Requirement-to-check map (REQTRACE)
31
+
32
+ File: `.loki/state/requirements-map.json`. Written after verification, before the run summary. Read-only view: `loki plan --requirements` (add `--json` for the raw file). Code: `loki-ts/src/features/contract.ts`; tests: `loki-ts/tests/features/contract.test.ts`.
33
+
34
+ ```json
35
+ {"version":1,"revision":"<git sha at write time, or \"unknown\">","requirements":[
36
+ {"id":"R1","text":"...","checks":["<check id>"],"status":"covered","method":"explicit"}
37
+ ]}
38
+ ```
39
+
40
+ - `status` is `covered` only when at least one check that actually ran and passed maps to the requirement. `checks` lists only those passing checks. A failed, flaky, not_run or pending check never covers.
41
+ - `method` is `explicit` when the requirement carries its own link to checks, `heuristic` otherwise. An uncovered requirement with no link is `heuristic` with `checks: []`.
42
+ - Ids `R1..Rn` are assigned in source order. At most 40 requirements are taken from PRD text.
43
+ - The file is never written when there is no requirement source, and a write failure never fails a run.
44
+
45
+ Requirements and checks come from existing extractors, with no second parser:
46
+
47
+ | Run | Requirements | Checks that ran | Link |
48
+ |---|---|---|---|
49
+ | Bun runner (`loki start`, default) | `.loki/checklist/checklist.json` items | that item's `verification[]` entries, id `<item id>:<type>:<n>`; `passed === true` counts | explicit, by construction |
50
+ | engine10 with `--spec` | acceptance criteria of the spec snapshot (`spec_file.ts` parseSpec) | `test.result` events of the run, id = check name (`runner:path`) | explicit if the criterion ends with `[check: id1, id2]`, else heuristic |
51
+ | engine10 text or PRD task | units from `autonomy/lib/requirements_contract.py` `extract_bound_requirement_units`; needs 2 or more units | same `test.result` events | heuristic |
52
+
53
+ Heuristic rule: a passing check matches when it shares at least 2 significant words (4 or more letters, minus a small stop list; all of them when the requirement has only one) with the check id plus command. It is a hint, not proof, which is why `method` is recorded.
54
+
55
+ Surfaces: the run summary next-step line reads `N requirement(s) uncovered, see: loki plan --requirements` when `N > 0`, inside the 8-line cap (engine10 `withNextLine` / `formatRunSummary`, and `autonomy/lib/run-summary.sh` for the bash summary). The JSON summary adds `requirements_uncovered` only when `N > 0`.
56
+
57
+ Known gaps: the legacy bash engine (`autonomy/run.sh` without Bun) does not write the map; its summary only reads one if present. Heuristic matching is word overlap only and can both miss and over-claim, so use explicit `[check: id]` links for requirements that matter.