triss-coworker 0.35.0 → 0.37.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 (74) hide show
  1. package/CHANGELOG.md +224 -0
  2. package/README.md +72 -5
  3. package/bin/triss.js +94 -12
  4. package/docs/coder-one-shot-provider-plan.md +14 -0
  5. package/docs/configuration.md +18 -4
  6. package/docs/glm-clients.md +17 -16
  7. package/docs/mcp.md +22 -4
  8. package/docs/opencode2-engine-plan.md +1280 -0
  9. package/docs/opencode2.md +110 -0
  10. package/docs/reliable-delegation-contract-plan.md +37 -8
  11. package/docs/reliable-delegation-release-a.md +263 -0
  12. package/docs/usage-accounting.md +3 -3
  13. package/package.json +2 -2
  14. package/src/coder-credential-proxy.js +443 -0
  15. package/src/coder-engines/crush.js +10 -1
  16. package/src/coder-engines/opencode2.js +414 -0
  17. package/src/coder-git-mediator.js +226 -0
  18. package/src/coder-lease.js +240 -0
  19. package/src/coder-lock.js +5 -1
  20. package/src/coder-models.js +231 -44
  21. package/src/coder-orchestration.js +211 -0
  22. package/src/coder-process-supervisor.js +159 -0
  23. package/src/coder-result-owner-adapter.js +108 -0
  24. package/src/coder-result-quarantine.js +358 -0
  25. package/src/coder-result-registry-codec.js +231 -0
  26. package/src/coder-result-transitions.js +297 -0
  27. package/src/coder-result.js +542 -0
  28. package/src/coder-run-state.js +98 -0
  29. package/src/coder-sandbox.js +270 -0
  30. package/src/coder-session-inventory-codec.js +275 -0
  31. package/src/coder-session-owner-adapter.js +177 -0
  32. package/src/coder-session-store.js +327 -0
  33. package/src/coder-session-transitions.js +296 -0
  34. package/src/coder-state-backup.js +384 -0
  35. package/src/coder-state.js +346 -0
  36. package/src/coder-write-quota.js +171 -0
  37. package/src/commands/ask.js +2 -10
  38. package/src/commands/coder-models.js +32 -0
  39. package/src/commands/coder-state-backup.js +80 -0
  40. package/src/commands/coder.js +2009 -111
  41. package/src/commands/exec.js +8 -1
  42. package/src/commands/review.js +310 -54
  43. package/src/commands/status.js +14 -0
  44. package/src/config.js +103 -1
  45. package/src/fixed-kernel-lock.js +243 -0
  46. package/src/integrations/_contract.js +55 -16
  47. package/src/managed-root.js +253 -0
  48. package/src/mcp/handlers.js +95 -3
  49. package/src/mcp/review-core.js +310 -36
  50. package/src/mcp/tools.js +79 -6
  51. package/src/opencode-config.js +523 -0
  52. package/src/opencode2-preflight.js +724 -0
  53. package/src/owned-process-journal.js +310 -0
  54. package/src/owned-process-reconcile.js +298 -0
  55. package/src/provider-errors.js +300 -0
  56. package/src/release-a-acceptance.js +605 -0
  57. package/src/review-executor.js +218 -0
  58. package/src/review-git.js +320 -0
  59. package/src/review-input.js +142 -0
  60. package/src/review-live.js +75 -0
  61. package/src/review-payload.js +379 -0
  62. package/src/review-pr-fetch.js +158 -0
  63. package/src/review-pr-identity.js +154 -0
  64. package/src/review-pr-metadata.js +112 -0
  65. package/src/review-pr-registry.js +267 -0
  66. package/src/review-pr.js +161 -0
  67. package/src/review-scoped.js +265 -0
  68. package/src/update/install.js +1 -1
  69. package/src/usage.js +30 -5
  70. package/src/worktree-fingerprint.js +302 -0
  71. package/templates/claude-full.md +16 -4
  72. package/templates/claude.md +2 -1
  73. package/templates/codex-full.md +4 -2
  74. package/templates/codex.md +2 -1
package/CHANGELOG.md CHANGED
@@ -7,10 +7,213 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.37.0] — 2026-08-18
11
+
12
+ ### Added
13
+
14
+ - **Reliable delegation (Release C)** — sequential sharding per
15
+ `docs/reliable-delegation-contract-plan.md`:
16
+ - `--payload-mode shard` CLI + MCP shard parity: source-ordered
17
+ whole-file shards (a file is never split across shards);
18
+ - sequential executor with first-failure stop, cancellation
19
+ between/in-flight shards, fresh limit re-checks, and per-shard
20
+ attempt/usage facts;
21
+ - **no global verdict**: completed sharded execution prints
22
+ `global verdict: unavailable_for_sharded`; no aggregation call, no
23
+ cross-shard analysis, no global approval;
24
+ - structured partial errors carry completed shard verdicts only —
25
+ never completed prose or raw diff content;
26
+ - `evidence + shard` and `shard + --stream` rejected before any model
27
+ call;
28
+ - Release C synthetic acceptance (`--synthetic --release C`) and live
29
+ acceptance (`--live --release C`; PASS / SKIPPED_NO_CREDENTIALS /
30
+ BLOCKED_ENVIRONMENT recorded separately, never upgraded to success).
31
+
32
+ - **Reliable delegation (Release B)** — bounded single review, exact PR
33
+ diff acquisition, and the issue trust boundary per
34
+ `docs/reliable-delegation-contract-plan.md`:
35
+ - reloadable review limits (`TRISS_REVIEW_SINGLE_MAX_BYTES`,
36
+ `TRISS_REVIEW_SHARD_MAX_BYTES`, `TRISS_REVIEW_TOTAL_MAX_BYTES`,
37
+ `TRISS_REVIEW_MAX_SHARDS`) with atomic validation and full-default
38
+ fallback on contradictions;
39
+ - pure diff parser with exact byte accounting and coverage model
40
+ (repository vs requested-scope axes);
41
+ - exact merge-base-to-head comparison identity with sanitized Git
42
+ execution (no external diff/textconv/config, grafts and nonempty
43
+ shallow repositories rejected) and sealed empty-attribute projection;
44
+ - inventory-first rename expansion (old-only/new-only selection keeps
45
+ both sides) and selected-content acquisition bounded by pathspec;
46
+ - bounded PR acquisition: canonical input, minimum-field `gh`
47
+ metadata, disposable bare repository under a registry lock (three
48
+ concurrent runs, 120 MiB pack / 128 MiB filesystem quotas), exact
49
+ OID re-verification, source-common-dir immutability;
50
+ - streaming bounded stdin with no partial buffering;
51
+ - issue trust boundary: PR prose never triggers tracker access;
52
+ explicit `--issue` uses minimum-field bounded queries; `--skip-issue`
53
+ deprecated;
54
+ - shared single-review executor with stable error codes
55
+ (`TRISS_REVIEW_LIMIT`, `TRISS_REVIEW_INVALID_INPUT`,
56
+ `TRISS_PROVIDER_EMPTY`, `TRISS_CANCELLED`) and scoped verdict
57
+ framing; MCP single-review parity with root enforcement and
58
+ structured coverage.
59
+
60
+ ### Fixed
61
+
62
+ - `triss coder run --isolate` / `triss_coder_run` isolation downgrade
63
+ (`--allow-best-effort-caller-worktree` /
64
+ `allowBestEffortCallerWorktree`): exact opt-in semantics, stable
65
+ public codes `TRISS_CODER_ISOLATION_ENFORCEMENT_REQUIRED` and
66
+ `TRISS_CODER_ISOLATION_DOWNGRADED` in message + `err.code` +
67
+ stderr + envelope warnings, and `effective_isolation:
68
+ best_effort_caller_worktree` (advisory-only); mechanism
69
+ unavailability downgrades while slug/branch conflicts
70
+ (`already exists`) fail closed even with the opt-in via stable
71
+ `err.code` dispatch (no message parsing).
72
+
73
+ ### Artifact integrity (0.37.0)
74
+
75
+ - `triss-dsh-provider-bundle-0.37.0.tgz` — sha256
76
+ `6fd343622443b8e2b15a0d0365fa6a893054d7eb51a26fc1bca5db3c0e068b2d`,
77
+ integrity `sha512-lBTCSU8kRHm5YN9twwnw1F5wH0OXuU8iwkKyQjXSZVaVCq5ga7z20BirXCJo65rMfcMLVBrbsl1GMXYXCqzpyw==`
78
+ (computed with the pinned release npm 11.6.2 via
79
+ `scripts/publish-gate.js pack-inspect`; `npm pack` output is
80
+ byte-deterministic — tar entries carry the fixed npm epoch mtime — so a
81
+ test pins this value against every future pack of the same content).
82
+ - Root `triss-coworker-0.37.0.tgz` sha256 is reproducible via
83
+ `npm pack` at tag `v0.37.0` (the root tarball ships `CHANGELOG.md`, so its
84
+ hash cannot be recorded inside this file); registry verification compares
85
+ the packed artifact against the published tarball byte-for-byte
86
+ (`scripts/publish-gate.js pack-inspect`).
87
+
88
+ ## [0.36.0] — 2026-08-18
89
+
90
+ ### Added
91
+
92
+ - **OpenCode 2 coder engine (beta)**: `triss coder run --engine opencode2`
93
+ and `triss coder init --engine opencode2`, pinned to the exact verified
94
+ build `@opencode-ai/cli@0.0.0-next-17430` (`TRISS_CODER_OPENCODE2_VERSION`
95
+ to override). The beta runs every managed invocation `--standalone` with
96
+ auto-update disabled, isolates V2 state under `<project>/.triss/opencode2/`
97
+ (0700), folds the V2 event stream into the shared usage envelope
98
+ (`usage_source: "opencode2"`, terminal-error precedence, no fabricated
99
+ zeros), namespaces sessions per engine, and rides the shared opencode-v1
100
+ configuration backend for `coder model set` / rollback. See
101
+ docs/opencode2.md and docs/opencode2-engine-plan.md.
102
+
103
+ ### Security (opencode2 engine beta)
104
+
105
+ - Fail-closed effective-configuration preflight for `coder run --engine
106
+ opencode2`: the managed provider projection (exact package, settings keys,
107
+ credential placeholder, and the worker `baseURL` compared against the
108
+ configured Triss worker endpoint), `provider.api` and model-level
109
+ transport overrides are rejected before any credential is forwarded, and
110
+ the permission gate requires deny-everything shell policy (no live
111
+ allow/ask rule — the credential sits in the child environment), proven
112
+ with a real last-match-wins evaluator.
113
+ - `opencode2` runs resolve the engine binary to an absolute path
114
+ (`which` + `realpath`) and spawn exactly that path, re-verifying the same
115
+ path and version after the run.
116
+ - Session store reads fail closed on malformed namespaces (no silent data
117
+ loss on rewrite); the lookup happens inside the isolation cleanup guard so
118
+ a malformed store never leaks a worktree/branch.
119
+ - XDG runtime roots reject symlinked path components anywhere in the chain
120
+ below the project root.
121
+ - `opencode init --engine opencode2` owns its flow: credential prompt and
122
+ `--local`/`--global` scope are honored, an exact-pin mismatch is terminal
123
+ before any config mutation, and the post-setup audit is the full provider
124
+ + permission preflight (not just the plugin/agent scan).
125
+ - The full run preflight parses config layers through the JSONC-aware
126
+ canonical parser (comments and trailing commas no longer abort the run),
127
+ and `parseOpenCodeDocument` drops trailing commas followed by comments.
128
+
129
+ ### Fixed (review rounds 4–6)
130
+
131
+ - Dual legacy/native config forms (`provider`+`providers`,
132
+ `plugin`+`plugins`, `permission`+`permissions`) reject — the pinned build
133
+ prefers the native value while a legacy-first projection audits the other
134
+ one; the worker key/endpoint provenance gate resolves each field's
135
+ EFFECTIVE source from the pre-dotenv snapshot (a decoy key in the project
136
+ `.triss.env` cannot mask a shell key); unrelated provider definitions
137
+ (in-process npm code) reject; the audit walks the canonical
138
+ (`realpathSync.native`) runtime directory; audited sources are re-audited
139
+ immediately before the credential-bearing spawn (TOCTOU); the top-level
140
+ key table is captured from the official schema (benign keys no longer
141
+ false-reject; object `lsp`/`formatter` and `experimental` reject).
142
+ - A held session-store lock no longer discards a finished run:
143
+ `persistSessionMapping` retries with backoff and degrades to the lock-free
144
+ protocol (mapping kept, warning emitted, foreign lock untouched).
145
+ - A corrupted `sessions.json` on the V1 `--isolate` path no longer strands
146
+ the `.triss/wt/<slug>` worktree and `coder/<slug>` branch.
147
+ - `TRISS_CODER_ENGINE=opencode2` from a `.env` file now routes `coder init`
148
+ to the V2 flow (the engine is resolved after the env files load); the
149
+ pre-dotenv snapshots are threaded through so the provenance and
150
+ pin-shadow checks stay exact.
151
+ - `coder init --engine opencode2` on a tree carrying the V1 bash allowlist
152
+ rejects BEFORE any credential/config write with actionable guidance; a
153
+ fresh V2 init warns that the shared deny-everything policy removes the
154
+ allowlisted commands from plain V1 runs (documented in
155
+ docs/opencode2.md, including two new troubleshooting rows).
156
+ - Model inspection tolerates unrelated hostile config shapes (a non-string
157
+ plugin reference, unreadable directories) — the post-commit audit of a
158
+ successful `coder model set` no longer rolls back over them; `coder
159
+ models` warnings use the OpenCode branch for `opencode2` (no false
160
+ `configured-model-unavailable` for a shell-exported foreign model);
161
+ rollback of an `opencode2` record reports engine `opencode2`.
162
+ - `opencode-go/` routes price as unknown (the Go reseller's tariffs are
163
+ unmodeled); the prefixed `TRISS_PRICE_OPENCODE_GO_<MODEL>` override is
164
+ the documented way to price them.
165
+ - Unknown `engines.*` namespaces in the session store fail closed;
166
+ `ensureOpenCode2RuntimeDirs` reports the directories it created;
167
+ `.env.example` documents `TRISS_CODER_OPENCODE2_VERSION`.
168
+
169
+ ### Artifact integrity (0.36.0)
170
+
171
+ - `triss-dsh-provider-bundle-0.36.0.tgz` — sha256
172
+ `0e3100362fc02d242deac114ec6e0c4c966bbe5edde4f156a2ffdb76ef2eb329`,
173
+ integrity `sha512-P2QoA6Ahu/J9swIJhk9IkuP1OLR+bzn4zJdUKyuskutbis2GUSt5m3n+QmqP7B0rB1JAqi0WGIMkUrTBcmf9cQ==`
174
+ (computed with the pinned release npm 11.6.2 via
175
+ `scripts/publish-gate.js pack-inspect`; `npm pack` output is
176
+ byte-deterministic — tar entries carry the fixed npm epoch mtime — so a
177
+ test pins this value against every future pack of the same content).
178
+ - Root `triss-coworker-0.36.0.tgz` sha256 is reproducible via
179
+ `npm pack` at tag `v0.36.0` (the root tarball ships `CHANGELOG.md`, so its
180
+ hash cannot be recorded inside this file); registry verification compares
181
+ the packed artifact against the published tarball byte-for-byte
182
+ (`scripts/publish-gate.js pack-inspect`).
183
+
10
184
  ## [0.35.0] — 2026-08-14
11
185
 
12
186
  ### Added
13
187
 
188
+ - **Reliable delegation (Release A)** — coder envelope v2 orchestration per
189
+ `docs/reliable-delegation-contract-plan.md`:
190
+ - envelope fields `session_slug`, `result_retention`, `result_id`, and
191
+ `execution_capabilities` (eight honest `enforced|best_effort|unavailable`
192
+ values + `effective_isolation`) on every safe envelope
193
+ (`docs/reliable-delegation-release-a.md`);
194
+ - v2 session CLI: `triss coder session list`, `triss coder session clean
195
+ <slug> --engine <opencode|crush>` (per-engine store only, legacy
196
+ `.triss/sessions.json` never touched);
197
+ - retained-result registry: `triss coder result list`, `triss coder result
198
+ clean <run-id>`, and the exact result-registry codec/transitions
199
+ (1 GiB reservation / 3 GiB payload budget + 1 GiB headroom,
200
+ `TRISS_CODER_RESULT_CAP`, `TRISS_CODER_RESULT_QUOTA_REQUIRED`);
201
+ - rollback contract: `triss coder state backup|validate|adopt|reset` with
202
+ bounded no-follow backup, completion marker, and
203
+ `TRISS_CODER_ROLLBACK_RESULTS_PENDING` guard;
204
+ - quarantine transaction and quarantine clean (`quarantine-v1`,
205
+ phase-aware manifest machine, completion-marker hashing);
206
+ - credential proxy (loopback one-run token) on both engines — the real
207
+ provider key never reaches the engine environment; every run requires
208
+ the proxy (Release A rejects runs when it is unavailable);
209
+ - pure provider error classifier with stable public codes
210
+ (`TRISS_PROVIDER_AUTH/POLICY/RATE/TIMEOUT/NOT_FOUND/CONNECTION/UNKNOWN/
211
+ CONFLICT/EMPTY`);
212
+ - bounded blocker diagnostics (`environment_permission`,
213
+ `execution_policy`, `lock_or_process_state`, `unknown`; ≤16 entries,
214
+ duplicate categories collapsed);
215
+ - MCP: `allowBestEffortCallerWorktree` (default false),
216
+ `triss_coder_result_list`, `triss_coder_result_clean`.
14
217
  - `triss-dsh-provider-bundle`, a standalone npm package (workspace
15
218
  `packages/dsh-provider-bundle`) that activates the DeepSeek Harness
16
219
  `llm-pi-ai` adapter for `opencode`, `opencode-go`, and `zai` routes in a
@@ -80,6 +283,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
80
283
  the packed artifact against the published tarball byte-for-byte
81
284
  (`scripts/publish-gate.js pack-inspect`).
82
285
 
286
+ ### Changed
287
+
288
+ - Non-isolated `files_changed` is now `null` rather than `[]`;
289
+ `run_files_changed` is the only changes-expectation evidence. Consumers
290
+ must branch on `envelope_version` and `change_detection.status`.
291
+ - Bare `--continue` is rejected with migration guidance; v2 state is
292
+ selected only via `--session <slug>`.
293
+ - `ask`/`review`/MCP fail empty responses with the stable
294
+ `TRISS_PROVIDER_EMPTY` code instead of a direct `process.exit`.
295
+ - Capability-dependent Windows npm support wording: an unavailable OS
296
+ sandbox/cleanup/lock/quota never blocks a non-isolated/best-effort run but
297
+ provides none of those guarantees; unavailable credential isolation always
298
+ blocks before spawn.
299
+
300
+ ### Fixed
301
+
302
+ - `fd.readFile` position semantics in fixed-kernel-lock release (marker
303
+ clearing reads via path, keeping the fixed inode).
304
+ - Quarantine `readdir` import; result-state deletion moved to an immutable
305
+ tombstone sidecar.
306
+
83
307
  ## [0.34.0] — 2026-08-13
84
308
 
85
309
  ### Added
package/README.md CHANGED
@@ -279,7 +279,8 @@ The exposed tool set is **filtered by configured credentials**:
279
279
  `ZHIPU_API_KEY` (Z.AI GLM), `OPENCODE_API_KEY` (OpenCode Zen or Go),
280
280
  `MOONSHOT_API_KEY` (Moonshot Kimi), or `KIMI_API_KEY` (Kimi for Coding)
281
281
  (setup: `triss coder init`). `triss_coder_run` takes an optional `engine`
282
- (`opencode` default, or `crush`); its timeout defaults to
282
+ (`opencode` V1 default; `--engine opencode2` beta — see
283
+ [opencode2.md](docs/opencode2.md) — or `crush`); its timeout defaults to
283
284
  1500s (25 min) over MCP, above the CLI's 900s, since coding runs are
284
285
  expected to be long; override per call via the `timeout` arg. For
285
286
  runs that may exceed it, use `triss coder run` on the CLI instead.
@@ -311,8 +312,8 @@ and writing**.
311
312
  | `triss review` | Code review on a branch, PR, or explicitly piped diff (linked ticket only for branch/PR) | The agent reading the whole diff |
312
313
  | `triss commit-msg` | Generates a commit message from staged diff | Hand-writing or copy-pasting from web LLMs |
313
314
  | `triss usage` | Cumulative cost / token usage with per-project breakdown | Squinting at stderr after each call |
314
- | `triss coder init` | Sets up a coding agent (default `opencode` engine; `--engine crush` for crush): provider key/profile (`--provider worker` reuses `TRISS_WORKER_*`; Z.AI GLM default; Zen, Go, and Kimi are also supported), config, permission policy, and agent templates. Blocks (non-zero) on an unsafe existing `opencode.json` — missing deny-first bash policy (override with `--allow-unsafe-bash`) or a stale/cross-provider `small_model`. | Manually installing/configuring opencode |
315
- | `triss coder run` | Spawns the coding agent (the OpenAI-compatible Triss worker, GLM, Kimi, OpenCode Zen, or OpenCode Go) and prints one JSON envelope (`--engine opencode\|crush`; `--isolate` for a disposable worktree — opencode defaults to isolate-OFF, crush defaults to isolate-ON; crush adds opt-in `--restrict`/`--no-restrict` for its CLI allowlist). **POSIX only** (macOS/Linux) — refuses to run on Windows. | Manually driving `opencode run` and parsing its ndjson stream |
315
+ | `triss coder init` | Sets up a coding agent (default `opencode` V1 engine; `--engine opencode2` for the V2 beta — shares the `opencode.json` config, see [docs/opencode2.md](docs/opencode2.md); `--engine crush` for crush): provider key/profile (`--provider worker` reuses `TRISS_WORKER_*`; Z.AI GLM default; Zen, Go, and Kimi are also supported), config, permission policy, and agent templates. Blocks (non-zero) on an unsafe existing `opencode.json` — missing deny-first bash policy (override with `--allow-unsafe-bash`) or a stale/cross-provider `small_model`. | Manually installing/configuring opencode |
316
+ | `triss coder run` | Spawns the coding agent (the OpenAI-compatible Triss worker, GLM, Kimi, OpenCode Zen, or OpenCode Go) and prints one JSON envelope (`--engine opencode|opencode2|crush` — opencode V1 default, opencode2 beta per [docs/opencode2.md](docs/opencode2.md); `--isolate` for a disposable worktree — opencode defaults to isolate-OFF, crush defaults to isolate-ON; crush adds opt-in `--restrict`/`--no-restrict` for its CLI allowlist). **POSIX only** (macOS/Linux) — refuses to run on Windows. | Manually driving `opencode run` and parsing its ndjson stream |
316
317
  | `triss coder clean` | Removes finished `.triss/wt` isolation worktrees (`--all` forces all) | Manually finding and deleting stale git worktrees |
317
318
  | `triss init` | Drops a tiny (~15 line) delegation block into `CLAUDE.md` / `AGENTS.md` | Hand-writing routing rules |
318
319
  | `triss agent-help` | Prints the full delegation cookbook on demand (the nano block points here) | A 200-line CLAUDE.md that always loads |
@@ -532,8 +533,44 @@ git diff main...HEAD | triss review --stdin # review this branch's merge-base d
532
533
  triss review --skip-issue # don't try ticket lookup
533
534
  triss review --provider glm # same review flow, one-shot GLM inference
534
535
  triss review --provider kimi # …or Kimi (pro preset = kimi-k3)
536
+ triss review --files src/a.js test/b.test.js # literal file selection (after --)
537
+ triss review --issue ENG-42 # explicit tracker issue (never inferred from prose)
535
538
  ```
536
539
 
540
+ ### Reliable delegation — review safety (Release B)
541
+
542
+ Review runs are bounded and fail closed (see
543
+ [`docs/reliable-delegation-release-a.md`](docs/reliable-delegation-release-a.md)):
544
+
545
+ - **Limits**: single request (256 KiB default / 1 MiB hard max), shard
546
+ (96 KiB / 256 KiB), total corpus (4 MiB / 16 MiB), shard count (64 /
547
+ 256) — all reloadable via `TRISS_REVIEW_*_BYTES` / `TRISS_REVIEW_MAX_SHARDS`;
548
+ any contradiction falls back to the complete default set.
549
+ - **Git**: exact merge-base-to-head identity, one unique merge base, a
550
+ sanitized environment (no external diff/textconv/config injection,
551
+ grafts and nonempty shallow repositories rejected).
552
+ - **Selection**: literal `--files` selectors are expanded across renames
553
+ and only the selected content is acquired — a huge change with a small
554
+ selected file never buffers the full diff first.
555
+ - **PRs**: canonical input only, exact OID re-verification, disposable
556
+ bare repository under a registry lock (3 concurrent runs, 120 MiB pack /
557
+ 128 MiB filesystem quotas); the source directory is never mutated.
558
+ - **Issues**: PR or branch prose NEVER triggers tracker access. Only an
559
+ explicit `--issue KEY` performs a bounded minimum-field lookup;
560
+ `--skip-issue` is deprecated.
561
+ - **Empty responses** fail with `TRISS_PROVIDER_EMPTY` instead of
562
+ producing a clean verdict; usable non-empty text is never trimmed.
563
+
564
+ ### Sharding (Release C)
565
+
566
+ `triss review --payload-mode shard` runs source-ordered whole-file shards
567
+ sequentially. A file is never split across shards; the first failure or
568
+ cancellation stops the sequence. **Completed sharded execution is not a
569
+ global review**: there is no aggregation call, no cross-shard analysis, and
570
+ no global approval — the CLI prints `global verdict: unavailable_for_sharded`.
571
+ Partial errors carry completed shard verdicts only (never raw diff content).
572
+ `evidence + shard` and `shard + --stream` are rejected before any model call.
573
+
537
574
  The three diff sources are mutually exclusive: local Git (`triss review` or
538
575
  `--base`), a GitHub PR (`triss review <PR>`), or UTF-8 text piped to
539
576
  `triss review --stdin`. `--stdin` cannot be combined with a PR number or
@@ -587,8 +624,9 @@ default timeout, configurable via `--timeout <ms>`.
587
624
 
588
625
  Delegates an implementation task to a cheap coding agent — the existing
589
626
  OpenAI-compatible Triss worker, GLM, Kimi,
590
- OpenCode Zen, or OpenCode Go models (the `opencode` engine by default; `crush` is an
591
- alternative — see **Engines** below) instead of the primary model writing
627
+ OpenCode Zen, or OpenCode Go models (the `opencode` V1 engine by default;
628
+ `opencode2` beta — see [docs/opencode2.md](docs/opencode2.md) — or `crush`; see
629
+ **Engines** below) instead of the primary model writing
592
630
  the code itself.
593
631
 
594
632
  ```bash
@@ -597,8 +635,37 @@ triss coder run "add input validation to /signup" --isolate
597
635
  triss coder run "..." --engine crush # crush engine (isolates by default; restrict is opt-in)
598
636
  triss coder run "..." --engine crush --restrict # crush + CLI allowlist on top of the worktree
599
637
  triss coder clean # remove finished isolation worktrees
638
+ triss coder session list # list v2 sessions (per engine)
639
+ triss coder session clean <slug> --engine opencode # remove one inactive isolated session
640
+ triss coder result list # list retained result artifacts
641
+ triss coder state backup --project <path> # Section 15 rollback backup
642
+ triss coder state validate --project <path> --backup <dir>
600
643
  ```
601
644
 
645
+ Every `triss coder run` envelope (and the matching MCP tool) carries the
646
+ Release A contract fields: `session_slug` (explicit slug or an anonymous
647
+ generated one — never an implicit persistent conversation), `result_retention`
648
+ / `result_id` (`retained` only for isolated changed runs with enforced
649
+ result-store quota and a successful reservation), and `execution_capabilities`
650
+ (eight honest `enforced|best_effort|unavailable` values plus
651
+ `effective_isolation`). Non-isolated `files_changed` is `null`; the only
652
+ changes-expectation evidence is `run_files_changed`. Process completion is not
653
+ task satisfaction — use `--expect changes --isolate` and inspect `git status`
654
+ yourself. Unavailable OS sandbox/cleanup/lock/quota does not block a
655
+ non-isolated/best-effort run but provides none of those guarantees; explicit
656
+ or default isolation needs `--allow-best-effort-caller-worktree` (default
657
+ off) or fails before spawn with `TRISS_CODER_ISOLATION_ENFORCEMENT_REQUIRED`
658
+ (stable `err.code`; retry hint when the isolation mechanism is unavailable),
659
+ and unavailable credential isolation always blocks before spawn. With the
660
+ opt-in and only when the isolation mechanism itself is unavailable (no git
661
+ repository or worktree creation failure — slug/branch conflicts like
662
+ `already exists` still fail closed), the run downgrades to
663
+ `best_effort_caller_worktree`: stderr and envelope `warnings` carry
664
+ `TRISS_CODER_ISOLATION_DOWNGRADED` and `effective_isolation` becomes
665
+ `best_effort_caller_worktree` (advisory-only, `files_changed` is `null`,
666
+ edits may reach the caller worktree). Full details:
667
+ `docs/reliable-delegation-release-a.md`.
668
+
602
669
  `triss coder init` first asks which provider to configure — **Z.AI GLM**
603
670
  (default), the existing **Triss worker** (`--provider worker`, aliases
604
671
  `openai` / `openai-compatible`), **OpenCode Zen** (`--provider opencode-zen`, free rotating
package/bin/triss.js CHANGED
@@ -40,8 +40,19 @@ import {
40
40
  runEdit,
41
41
  runUnset,
42
42
  } from '../src/commands/config.js';
43
- import { runCoderInit, runCoderRun, runCoderClean } from '../src/commands/coder.js';
43
+ import {
44
+ runCoderInit,
45
+ runCoderRun,
46
+ runCoderClean,
47
+ runCoderSessionList,
48
+ runCoderSessionClean,
49
+ runCoderStateAdopt,
50
+ runCoderStateReset,
51
+ runCoderResultList,
52
+ runCoderResultClean,
53
+ } from '../src/commands/coder.js';
44
54
  import { runCoderModels, runCoderModelSet, runCoderModelRollback } from '../src/commands/coder-models.js';
55
+ import { runCoderStateBackup, runCoderStateValidate } from '../src/commands/coder-state-backup.js';
45
56
  import { loadIntegrations } from '../src/integrations/_registry.js';
46
57
  import { withCall } from '../src/call-context.js';
47
58
  import { positiveIntegerOption, positiveNumberOption } from '../src/option-validation.js';
@@ -191,6 +202,7 @@ program
191
202
  .option('--small-model <p/m>', 'forward coder small model')
192
203
  .option('--isolate', 'forward coder isolation')
193
204
  .option('--no-isolate', 'disable coder isolation')
205
+ .option('--allow-best-effort-caller-worktree', 'forward coder isolation downgrade')
194
206
  .option('--restrict', 'forward coder restriction')
195
207
  .option('--no-restrict', 'disable coder restriction')
196
208
  .option('--cwd <path>', 'forward coder working directory')
@@ -214,6 +226,8 @@ program
214
226
  .option('-b, --base <branch>', 'compare against this branch (default: auto-detect origin/HEAD or main/master/develop)')
215
227
  .option('--skip-issue', "don't try to look up a Jira/Linear ticket from the branch/PR title")
216
228
  .option('--stdin', 'read an explicitly piped UTF-8 diff instead of Git or PR sources')
229
+ .option('--files <paths...>', 'literal path selectors: review ONLY these files (repeatable; renames select both sides)')
230
+ .option('--issue <key>', 'explicit linked-issue key (e.g. PROJ-123); PR prose never triggers tracker access')
217
231
  .option('-q, --question <text>', 'override the default review question')
218
232
  .option('--provider <name>', 'inference provider: worker (default), deepseek (alias), glm, or kimi (alias: moonshot)')
219
233
  .option('-m, --model <name>', 'model preset (flash | pro) or full model id (default: pro)')
@@ -221,6 +235,7 @@ program
221
235
  .option('--format <format>', 'response format: text (default) or evidence')
222
236
  .option('--stream', 'force streaming even when stdout is not a TTY')
223
237
  .option('--no-stream', 'disable streaming output (default streams when stdout is a TTY)')
238
+ .option('--payload-mode <mode>', 'review payload mode: single (default) or shard (sequential whole-file shards; no global verdict)')
224
239
  .action((pr, opts) => wrap(runReview)(pr, opts));
225
240
 
226
241
  program
@@ -276,7 +291,7 @@ config
276
291
  .option('-f, --force', 're-prompt for keys that are already set')
277
292
  .option('--standard', 'API key + one model only — skip the standard/advanced prompt')
278
293
  .option('--advanced', 'full wizard with presets, base URL, integrations — skip the prompt')
279
- .option('--coder-engine <name>', 'coder target only: coding engine to configure (opencode default, or crush). `coder init` uses --engine')
294
+ .option('--coder-engine <name>', 'coder target only: coding engine to configure (opencode default, opencode2 beta, or crush). `coder init` uses --engine')
280
295
  .option('--coder-provider <name>', 'coder target only: opencode engine provider (zai, worker, opencode-zen, opencode-go, moonshot, kimi-for-coding). `coder init` uses --provider')
281
296
  .action(wrap(runWizard));
282
297
 
@@ -324,14 +339,14 @@ config
324
339
 
325
340
  const coder = program
326
341
  .command('coder')
327
- .description('Run a coding agent (OpenCode or Crush engine)');
342
+ .description('Run a coding agent (OpenCode V1, OpenCode 2 beta, or Crush engine)');
328
343
 
329
344
  coder
330
345
  .command('init')
331
- .description('Install/configure a coding engine (opencode default, or crush), provider key, permission policy, and agent templates')
346
+ .description('Install/configure a coding engine (opencode default, opencode2 beta, or crush), provider key, permission policy, and agent templates')
332
347
  .option('-g, --global', 'save to the global scope (~/.config/triss/.env, ~/.config/opencode/)')
333
348
  .option('-l, --local', 'save to the project scope (./.triss.env, ./opencode.json)')
334
- .option('--engine <name>', 'coding engine to configure: opencode (default) or crush')
349
+ .option('--engine <name>', 'coding engine to configure: opencode (default), opencode2 (beta — shares the opencode.json config; see docs/opencode2.md), or crush')
335
350
  .option('--provider <name>', 'opencode engine model provider: zai, worker (existing OpenAI-compatible TRISS_WORKER_* profile), opencode-zen, opencode-go, moonshot, or kimi-for-coding')
336
351
  .option('--allow-unverified', 'requires explicit --provider opencode-go (alias: go): allow the built-in fallback only after a temporary network or HTTP 408/429/500/502/503/504 catalogue failure (never bypasses 401/403, empty, or invalid responses)')
337
352
  .option('--allow-unsafe-bash', 'proceed even if an existing opencode.json has no deny-first bash policy (the agent runs with --auto)')
@@ -339,11 +354,11 @@ coder
339
354
 
340
355
  coder
341
356
  .command('run [prompt]')
342
- .description('Spawn a coding agent — GLM, the OpenAI-compatible Triss worker, Kimi, OpenCode Zen, or OpenCode Go (opencode default, or --engine crush) — and print a JSON envelope to stdout')
343
- .option('--engine <name>', 'coding engine: opencode (default) or crush')
357
+ .description('Spawn a coding agent — GLM, the OpenAI-compatible Triss worker, Kimi, OpenCode Zen, or OpenCode Go (opencode default; opencode2 beta or --engine crush) — and print a JSON envelope to stdout')
358
+ .option('--engine <name>', 'coding engine: opencode (default), opencode2 (beta — see docs/opencode2.md), or crush')
344
359
  .option('--session <id>', 'triss-side session slug, mapped to a real opencode session id in .triss/sessions.json')
345
360
  .option('--continue', 'continue the most recent opencode session (maps to opencode --continue)')
346
- .option('--agent <name>', 'opencode agent template to use', 'coder')
361
+ .option('--agent <name>', 'agent template to use (V1 default: coder; opencode2 beta uses its built-in primary agent unless set)')
347
362
  .option('--provider <name>', 'OpenCode only: select a provider for one run; requires --model and does not modify .env or opencode.json')
348
363
  .option('--model <p/m>', 'override the MAIN model for this one run only (does not change small_model or repair persistent config; use `triss coder model set` for that)')
349
364
  .option('--small-model <p/m>', 'with --provider, override small_model for one run (defaults to the one-shot main model)')
@@ -353,6 +368,7 @@ coder
353
368
  .option('--no-restrict', 'crush only: disable restrict (crush auto-approves every tool — the default)')
354
369
  .option('--cwd <path>', 'working directory (ignored with --isolate)')
355
370
  .option('--timeout <sec>', 'kill the engine after this many seconds', parsePositiveNumber, 900)
371
+ .option('--allow-best-effort-caller-worktree', 'allow downgrade to caller worktree when isolated isolation cannot be enforced (default off — fails before spawn without it)')
356
372
  .option('--stdin', 'read the prompt from piped stdin instead of the [prompt] argument')
357
373
  .option('--json', 'no-op — the envelope is always JSON; kept for symmetry with other commands')
358
374
  .action((prompt, opts) => wrap(runCoderRun)(prompt, opts));
@@ -366,10 +382,10 @@ coder
366
382
  coder
367
383
  .command('models')
368
384
  .description('List current + live coder models, provider compatibility, and credential readiness (read-only)\n\n' +
369
- 'Configuration sources:\n' +
370
- ' • opencode: project opencode.json (local) or ~/.config/opencode/opencode.json (global)\n' +
385
+ 'Configuration sources (layered; later sources win):\n' +
386
+ ' • opencode & opencode2: JSONC-aware layered config — global ~/.config/opencode/opencode.json, then every opencode.json/.opencode/opencode.json up from the project root to the Git boundary (project-local wins)\n' +
371
387
  ' • crush: ./.crush/crush.json (local) or ~/.local/share/crush/crush.json (global)')
372
- .option('--engine <name>', 'coding engine: opencode (default) or crush')
388
+ .option('--engine <name>', 'coding engine: opencode (default), opencode2 (beta — layered JSONC config), or crush')
373
389
  .option('--provider <name>', 'provider kind: zai, worker, opencode-zen, opencode-go, moonshot, or kimi-for-coding')
374
390
  .option('--json', 'print the stable machine-readable state object (no secrets)')
375
391
  .action(wrap(runCoderModels));
@@ -391,7 +407,7 @@ coderModel
391
407
  ' • crush: project .crush/crush.json (local) or ~/.local/share/crush/crush.json (global)'
392
408
  )
393
409
  .option('--small <model>', 'small/fast model id (omit to keep the current compatible value)')
394
- .option('--engine <name>', 'coding engine: opencode (default) or crush')
410
+ .option('--engine <name>', 'coding engine: opencode (default), opencode2 (beta — layered JSONC config), or crush')
395
411
  .option('--provider <name>', 'provider kind: zai, worker, opencode-zen, opencode-go, moonshot, or kimi-for-coding')
396
412
  .option('-g, --global', 'write to the global scope (OpenCode: ~/.config/opencode/opencode.json + global .env; Crush: ~/.local/share/crush/crush.json)')
397
413
  .option('-l, --local', 'write to the project scope (OpenCode: ./opencode.json + ./.triss.env; Crush: ./.crush/crush.json)')
@@ -408,6 +424,72 @@ coderModel
408
424
  .option('-l, --local', 'restore to the project scope (./opencode.json or ./.crush/crush.json)')
409
425
  .action((opts) => wrap(runCoderModelRollback)(opts.from, opts));
410
426
 
427
+ // `coder state` is a command GROUP whose leaves are `backup` and `validate`
428
+ // (Section 15 rollback contract).
429
+ const coderState = coder
430
+ .command('state')
431
+ .description('Back up or validate the v2 coder state of a project (rollback contract)');
432
+
433
+ coderState
434
+ .command('backup')
435
+ .description('Create a bounded rollback backup of the v2 coder state (both engine session stores + coder-state-v2)')
436
+ .requiredOption('--project <absolute-path>', 'absolute path of the validated project root')
437
+ .option('--backup <path>', 'backup destination directory (default: <project>/.triss/backups/<timestamp>)')
438
+ .option('--json', 'print the stable machine-readable result (no secrets)')
439
+ .action((opts) => wrap(runCoderStateBackup)(opts));
440
+
441
+ coderState
442
+ .command('validate')
443
+ .description('Validate a rollback backup: manifest schema, completion marker hash, per-entry verification')
444
+ .requiredOption('--project <absolute-path>', 'absolute path of the validated project root')
445
+ .requiredOption('--backup <basename>', 'backup directory to validate')
446
+ .option('--json', 'print the stable machine-readable result (no secrets)')
447
+ .action((opts) => wrap(runCoderStateValidate)(opts));
448
+
449
+ coderState
450
+ .command('adopt')
451
+ .description('Adopt owned v2 state under a NEW project id: quarantine the old id, rewrite validated owner records')
452
+ .requiredOption('--from-project-id <32hex>', 'the old project id (32 lowercase hex) to quarantine')
453
+ .action((opts) => wrap(runCoderStateAdopt)(opts));
454
+
455
+ coderState
456
+ .command('reset')
457
+ .description('Quarantine all validated local v2 state and create an empty identity (never deletes it)')
458
+ .requiredOption('--project <absolute-path>', 'absolute path of the validated project root')
459
+ .action((opts) => wrap(runCoderStateReset)(opts));
460
+
461
+ // `coder session` group (v2 session CLI contract, Atomic 23).
462
+ const coderSession = coder
463
+ .command('session')
464
+ .description('List or clean v2 isolated sessions (per-engine store; legacy .triss/sessions.json is never touched)');
465
+
466
+ coderSession
467
+ .command('list')
468
+ .description('Serialize the bounded v2 session inventory projection to stdout')
469
+ .option('--engine <name>', 'coding engine: opencode (default) or crush')
470
+ .action((opts) => wrap(runCoderSessionList)(opts));
471
+
472
+ coderSession
473
+ .command('clean <slug>')
474
+ .description('Remove a selected inactive v2 isolated session/workspace transaction')
475
+ .requiredOption('--engine <name>', 'coding engine: opencode or crush (MANDATORY)')
476
+ .action((slug, opts) => wrap(runCoderSessionClean)(slug, opts));
477
+
478
+ // `coder result` group (retained-result CLI, Atomic 23).
479
+ const coderResult = coder
480
+ .command('result')
481
+ .description('List or clean retained result artifacts (never persistent sessions)');
482
+
483
+ coderResult
484
+ .command('list')
485
+ .description('Serialize the bounded retained-result projection to stdout')
486
+ .action((opts) => wrap(runCoderResultList)(opts));
487
+
488
+ coderResult
489
+ .command('clean <run-id>')
490
+ .description('Remove only a validated retained result artifact (run-<32 lowercase hex>)')
491
+ .action((runId, opts) => wrap(runCoderResultClean)(runId, opts));
492
+
411
493
  function wrap(fn) {
412
494
  return async (...args) => {
413
495
  try {
@@ -124,3 +124,17 @@ RED tests must establish:
124
124
  Acceptance requires focused tests, the full Node test/lint suite, package
125
125
  smoke, a live synthetic worker run from a GLM-persisted temporary project, and
126
126
  sequential GLM 5.2 plus independent agent reviews.
127
+
128
+ ## Current credential-proxy limitation (2026-08)
129
+
130
+ The parent-owned credential proxy pins the upstream ORIGIN plus a path prefix
131
+ per provider. For opencode built-in providers without a documented base-URL
132
+ override (`moonshot`, `kimi-for-coding`), the engine cannot be verifiably
133
+ pointed at the proxy, so `triss coder run` fails closed BEFORE spawn instead
134
+ of handing the real upstream a one-run proxy token it would reject. `zai`
135
+ (ZAI_BASE_URL/ZHIPU_BASEURL), `opencode-zen/go` (OPENCODE_BASE_URL), the
136
+ managed `triss-worker` overlay, and every crush run keep working. Direct
137
+ non-coder calls (`triss ask/review/chat --provider kimi|moonshot`) are
138
+ unaffected — they never spawn an engine. Lifting the limitation requires an
139
+ audited provider overlay (the same mechanism the worker block uses) for these
140
+ providers.
@@ -263,16 +263,30 @@ self-hosted endpoints).
263
263
  | Variable | Required | Default | Notes |
264
264
  | -------------------------------- | -------- | ------------------ | ------------------------------------------ |
265
265
  | `ZHIPU_API_KEY` | yes¹ | — | Z.AI API key for `ask`/`review --provider glm` and GLM coder models — <https://z.ai/manage-apikey/apikey-list> |
266
- | `OPENCODE_API_KEY` | no¹ | — | Shared OpenCode credential for Zen `opencode/*` and paid Go `opencode-go/*` models (opencode engine only). A key alone does not prove Go subscription, quota, or regional readiness. See [opencode-zen.md](opencode-zen.md) and [opencode-go.md](opencode-go.md). |
266
+ | `OPENCODE_API_KEY` | no¹ | — | Shared OpenCode credential for Zen `opencode/*` and paid Go `opencode-go/*` models (opencode engines). A key alone does not prove Go subscription, quota, or regional readiness. See [opencode-zen.md](opencode-zen.md) and [opencode-go.md](opencode-go.md). |
267
267
  | `MOONSHOT_API_KEY` | no¹ | — | Moonshot AI (Kimi) key for `ask`/`review --provider kimi` and `moonshotai/*` coder models — <https://platform.kimi.ai/console/api-keys> |
268
- | `KIMI_API_KEY` | no¹ | — | Kimi for Coding subscription key (opencode engine only) — unlocks `kimi-for-coding/*` models like `kimi-for-coding/k3` — <https://www.kimi.com/code/docs/en/> |
268
+ | `KIMI_API_KEY` | no¹ | — | Kimi for Coding subscription key (opencode engines) — unlocks `kimi-for-coding/*` models like `kimi-for-coding/k3` — <https://www.kimi.com/code/docs/en/> |
269
269
  | `TRISS_KIMI_BASE_URL` | no | `https://api.moonshot.ai/v1` | Endpoint for `--provider kimi` ask/review calls — set `https://api.moonshot.cn/v1` for a China-mainland key. Trailing slashes are stripped; a blank/degenerate value falls back to the default |
270
270
  | `TRISS_CODER_MODEL` | no | `zai-coding-plan/glm-5.2` | Resolved **main** model, passed to opencode via `--model`. Worker uses `triss-worker/<id>`, Go uses `opencode-go/<id>`, and Zen uses `opencode/<id>`. Main and small must stay within one provider prefix. |
271
271
  | `TRISS_CODER_SMALL_MODEL` | no | `zai-coding-plan/glm-5-turbo` | Small/fast **management/init intent** — written to `opencode.json` `small_model` by `init`/`triss coder model set`. **Not** a runtime override of an already-pinned small role (see precedence) |
272
272
  | `TRISS_CODER_OPENCODE_VERSION` | no | `1.18.7` | Pin override for the `opencode-ai` npm install |
273
- | `TRISS_CODER_ENGINE` | no | `opencode` | Coding engine: `opencode` (default) or `crush` |
273
+ | `TRISS_CODER_ENGINE` | no | `opencode` | Coding engine: `opencode` (default), `opencode2` (beta — see [opencode2.md](./opencode2.md)), or `crush` |
274
+ | `TRISS_CODER_OPENCODE2_VERSION` | no | `0.0.0-next-17430` | Exact pin override for the `@opencode-ai/cli` npm install (opencode2 engine — exact match required) |
274
275
  | `TRISS_CODER_CRUSH_VERSION` | no | `0.1.6` | Pin override for the `@phpcraftdream/crush` npm install (crush engine) |
275
- | `TRISS_CODER_CRUSH_RESTRICT` | no | unset (`0`) | crush only — `1` opts INTO the CLI allowlist (`--restrict-run` + `--allow-bash`/`--allow-tool`, the only enforcement path that works today — crush 0.1.3 ignores the `permissions.run` config). Unset leaves crush unrestricted; crush then defaults to isolate-ON. CLI `--restrict`/`--no-restrict` overrides; crush.json `permissions.run.restrict` is the next fallback (forward-compat — currently inert) |
276
+ | `TRISS_CODER_SESSION_CAP` | no | `4` | Persistent v2 session inventory cap per engine (fail closed) |
277
+
278
+ ### Review limits (Release B)
279
+
280
+ All four limits are reloadable at runtime; values are independently clamped
281
+ to their hard maxima, and any contradiction (e.g. `shard_max > single_max`)
282
+ falls back to the complete default set — never a partial application.
283
+
284
+ | Variable | Default | Hard max | Notes |
285
+ | ------------------------------- | ------------------ | --------------- | ----------------------------------------- |
286
+ | `TRISS_REVIEW_SINGLE_MAX_BYTES` | `262144` (256 KiB) | `1048576` (1 MiB) | Single-request payload bound (metadata + question + diff) |
287
+ | `TRISS_REVIEW_SHARD_MAX_BYTES` | `98304` (96 KiB) | `262144` (256 KiB) | Per-shard payload bound (sharding not yet available) |
288
+ | `TRISS_REVIEW_TOTAL_MAX_BYTES` | `4194304` (4 MiB) | `16777216` (16 MiB) | Total corpus bound; also the stdin acquisition cap |
289
+ | `TRISS_REVIEW_MAX_SHARDS` | `64` | `256` | Shard-count bound (sharding not yet available) |
276
290
 
277
291
  `triss coder init` auto-detects which Z.AI endpoint `ZHIPU_API_KEY`
278
292
  actually authenticates against — the `zai-coding-plan` (subscription)