@r1z4x/tezgah 1.4.0 → 1.5.1

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 (43) hide show
  1. package/CHANGELOG.md +120 -1
  2. package/MANIFEST +0 -1
  3. package/README.md +6 -4
  4. package/bin/tezgah-docs +3 -3
  5. package/bin/tezgah-route +12 -6
  6. package/bin/tezgah-setup +67 -24
  7. package/bin/tezgah-status +3 -1
  8. package/bin/tezgah-task +132 -39
  9. package/bin/tezgah-taste +49 -31
  10. package/bin/tezgah-triage +12 -7
  11. package/docs/architecture.md +1 -1
  12. package/docs/clarity-baseline.json +7 -7
  13. package/docs/contract.md +21 -11
  14. package/docs/environment.md +3 -1
  15. package/docs/evidence.md +4 -3
  16. package/docs/glossary.md +1 -1
  17. package/docs/hosts.md +9 -0
  18. package/docs/judge.md +95 -27
  19. package/docs/layers.md +1 -1
  20. package/docs/models.md +17 -7
  21. package/docs/operations.md +23 -13
  22. package/docs/skills.md +1 -1
  23. package/docs/testing.md +1 -1
  24. package/docs/vision.md +10 -5
  25. package/hooks/tezgah_agents.py +149 -105
  26. package/hooks/tezgah_context.py +42 -7
  27. package/hooks/tezgah_judge.py +252 -79
  28. package/hooks/tezgah_models.py +50 -3
  29. package/hooks/tezgah_paths.py +180 -27
  30. package/hooks/tezgah_policy.py +41 -10
  31. package/hooks/tezgah_replay.py +26 -15
  32. package/hooks/tezgah_task.py +160 -46
  33. package/hooks/tezgah_taste.py +2 -2
  34. package/hooks/tezgah_taste_ledger.py +13 -4
  35. package/hosts/codex/hook.py +5 -0
  36. package/package.json +1 -2
  37. package/skills/harness/SKILL.md +4 -0
  38. package/skills/plan-add/SKILL.md +23 -7
  39. package/skills/plan-add/render_table.py +3 -2
  40. package/skills/plan-status/SKILL.md +12 -4
  41. package/skills/plan-sync/SKILL.md +19 -9
  42. package/skills/tezgah-contract/SKILL.md +29 -14
  43. package/agents/tezgah-reviewer.md +0 -70
package/CHANGELOG.md CHANGED
@@ -6,6 +6,123 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.5.1] - 2026-10-09
10
+
11
+ ### Fixed
12
+
13
+ - **Subagents load in the first session.** Claude and Codex missed every role a
14
+ session wrote into a new repo until the next session. Codex also ignored
15
+ `.codex/agents` in an untrusted repo. The install now writes the roles to
16
+ `~/.claude/agents` and `$CODEX_HOME/agents`, as it already did for omp. A
17
+ session removes the copies an older release left in a repo's `.claude/agents`
18
+ and `.codex/agents`. Cursor read 9 of 10 role descriptions as ">" and got a
19
+ model id it does not list. It now gets its own `.cursor/agents` with a
20
+ one-line description and `model: inherit`.
21
+
22
+ - **`tezgah-route` names the specialists.** It could only name a tier. The
23
+ five specialists ran 0 times in 121 delegations. The generic task agent ran 86.
24
+ The judge now also answers whether a brief is a specialist's whole
25
+ job, and the route names that agent. `--phase review` and `--phase research`
26
+ name the reviewer and the researcher. A security brief that only reviews
27
+ goes to `tezgah-security`. The routing and fan-out text say to spawn the
28
+ named agent, never the generic one.
29
+ - **Jev through OpenRouter asks a model OpenRouter has.** The carrier sent
30
+ `typesafe/jev-latest`, which OpenRouter answers with 400 (no such model). It
31
+ now sends the bare `jev-latest`, which OpenRouter maps itself.
32
+ - **The Workers AI model id picks Clef.** `tezgah-setup` refused
33
+ `--jev @cf/cloudflare/clef-flash`, and the same value in
34
+ `TEZGAH_JEV_PROVIDER` chose no carrier. Both now name `clef-flash`, and
35
+ `@cf/cloudflare/clef` names `clef`.
36
+ - **opencode's judge run denies tools at agent level too.** A user's
37
+ `agent.build.permission` beat the inline top-level deny.
38
+ - **The refusal note needs an asked session CLI.** The seam writes it only
39
+ when it asked the session CLI first. A caller that named the CLI gets it.
40
+ - **A chosen carrier reads only its own key.** A `clef` choice no longer opens
41
+ omp's login database on every status-line redraw.
42
+ - **`pnpm test` names a command.** An Acceptance item counted as a command
43
+ only when a word after the first was an option or a path. So `pnpm test`,
44
+ `cargo test` and `make test` read as missing, and the phase move refused the
45
+ plan. A known runner followed by `test` or `run` now counts. Prose such as
46
+ `run the tests` still does not.
47
+ - **Each carrier's price is now pinned by a test.** The one cost check priced a
48
+ TypeSafe answer at Jev's rate. It passed with or without per-carrier pricing.
49
+ New tests in `tezgah-taste`, `tezgah-triage` and the replay labeller price Clef
50
+ and Clef-flash answers at their own rates. A run with an unpriced answer prints
51
+ `cost unknown`.
52
+ - **Claude lists the reviewer once.** The plugin shipped its own
53
+ `agents/tezgah-reviewer.md`, which Claude shows as `tezgah:tezgah-reviewer`
54
+ beside the generated reviewer and never shadows. The plugin now ships no
55
+ agent, and its release-time renderer is gone with it.
56
+
57
+ ## [1.5.0] - 2026-10-09
58
+
59
+ ### Changed
60
+
61
+ - **A plan gets one review, at most two rounds.** The review reads the plan's
62
+ whole diff once, in its verification phase. Round one spawns its reviewers in
63
+ one message, one per dimension, or one reviewer for a diff under 300 lines.
64
+ Only a confirmed critical or major finding opens round two. Round two reads
65
+ the fix's delta. A minor finding goes on the fix-later list. `tezgah-task review`
66
+ refuses a third round. What is left after round two goes to the user. The
67
+ reviewer agent, the contract and plan-sync now state this one rule. Before,
68
+ the reviewer looped until a round confirmed nothing.
69
+ - **One Acceptance rule.** A plan enters a writing phase only when each
70
+ Acceptance item names its command or says `unverifiable` and why. Before, one
71
+ command-bearing item let the move through. `render_table --acceptance
72
+ --strict` then refused the same plan. Both now read one predicate,
73
+ `tezgah_task.unproven`, and the refusal names each item's line.
74
+ - **Typed decisions take any Jev carrier.** Taste's `learn`, `label`,
75
+ `calibrate`, `gate` and `measure --typed` require the Jev model, not
76
+ TypeSafe's address. So do taste's background start and replay's
77
+ `--label-model`. OpenRouter System One, Cloudflare Workers AI or a
78
+ compatible endpoint counts the same. Old `typesafe` rows keep counting.
79
+ `learn`'s report key `typesafe` is now `jev`. Replay's `--provider typesafe`
80
+ is now `--provider jev`, and the old name still works. The triage's no-judge
81
+ message names every carrier, the selection knob and the session CLIs.
82
+ - **The fan-out rule arms on the lists the owner writes.** Items written
83
+ `1 - ...`, `1- `, `1:`, `a)` or `+` now arm it. An inline `1. ... 2. ...`
84
+ arms it, and so does "subagentlerle". Three files named as one list arm it
85
+ too, and so do "First, ... Second, ..." and three imperative clauses. A later
86
+ list in the same session gets a line that names the action: count the items,
87
+ one subagent per item in one message. The rule covers every phase: a plan's
88
+ tasks, its acceptance commands and a survey's sources fan out too. The clause
89
+ that let a list of small items run one by one is gone.
90
+
91
+ ### Added
92
+
93
+ - **Plan order and progress in `tezgah-task status`.** A plan can name the
94
+ plans it waits on with `after:`. `status` prints each open plan's ticked
95
+ Acceptance boxes and what it still waits on. A `parallel:` line names the
96
+ ready plans whose `allowed_paths:` share no prefix. Those can run at once.
97
+ An open plan whose `status:` is not open or blocked is a `FAIL` line, and so
98
+ is an `after:` id no plan carries. `--json` adds `open` and `parallel`.
99
+ - **The judge asks opencode, Cursor and Codex sessions too.** The judgement
100
+ seam's session CLI was omp or claude only. It now also runs `opencode run`,
101
+ `cursor-agent -p` and `codex exec` headless when their host's marker is set
102
+ (`OPENCODE`, `CURSOR_AGENT`/`CURSOR_VERSION`, `CODEX_THREAD_ID`).
103
+ `TEZGAH_JUDGE_CLI`, else `judge_cli` in `config.json`, picks one of the five
104
+ by name. A picked CLI that is not installed means no session CLI.
105
+ - **Jev through any carrier.** The judge reaches Jev through TypeSafe,
106
+ OpenRouter, Cloudflare Workers AI or a System One-compatible URL
107
+ (`JEV_API_BASE_URL`, `JEV_API_KEY`).
108
+ `TEZGAH_JEV_PROVIDER`, `JEV_PROVIDER` or config.json `jev` picks the carrier,
109
+ and `tezgah-setup --jev <value>` writes the config key. `auto` asks every
110
+ carrier that resolves. A named carrier without its credential gets no
111
+ request, and no other carrier stands in. `tezgah-status --judge` prints the
112
+ choice and its carriers.
113
+ - **The omp login is a TypeSafe key.** With no `TYPESAFE_API_KEY` and no key
114
+ file, the seam reads the active omp login, read-only.
115
+ - **Clef and Clef-flash as typed judges.** Cloudflare's own decision models
116
+ answer the System One API on Workers AI with the Cloudflare token and account
117
+ id. `--jev clef` or `--jev clef-flash` picks one, and `auto` asks them after
118
+ the Jev carriers. A typed decision counts them like Jev. Cost rows now price
119
+ the carrier that answered: $0.24 per 1M input for Clef, $0.09 for Clef-flash.
120
+
121
+ ### Fixed
122
+
123
+ - **A spent key stops costing a request.** OpenRouter answers 403 to a key
124
+ past its limit. A 403 now marks a provider down for five minutes, like a 401.
125
+
9
126
  ## [1.4.0] - 2026-10-08
10
127
 
11
128
  ### Changed
@@ -864,7 +981,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
864
981
  the copy could not find `hooks/` and died on import. It now links into the
865
982
  installed tree, and also installs the command as `tezgah`.
866
983
 
867
- [Unreleased]: https://github.com/r1z4x/tezgah/compare/v1.4.0...HEAD
984
+ [Unreleased]: https://github.com/r1z4x/tezgah/compare/v1.5.1...HEAD
985
+ [1.5.1]: https://github.com/r1z4x/tezgah/releases/tag/v1.5.1
986
+ [1.5.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.5.0
868
987
  [1.4.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.4.0
869
988
  [1.3.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.3.0
870
989
  [1.2.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.2.0
package/MANIFEST CHANGED
@@ -19,7 +19,6 @@ README.md
19
19
  README.tr.md
20
20
  RELEASING.md
21
21
  SECURITY.md
22
- agents/tezgah-reviewer.md
23
22
  assets/logo/README.md
24
23
  assets/logo/tezgah-logo-16.png
25
24
  assets/logo/tezgah-logo-256.png
package/README.md CHANGED
@@ -109,10 +109,12 @@ it set out to arm is not armed.
109
109
  `~/.codex/AGENTS.md`, `~/.omp/agent/RULES.md`). Claude Code's hooks ship in a
110
110
  plugin registered through `claude plugin`; with no `claude` CLI, Claude stays
111
111
  unarmed and the report says so.
112
- - **Agent files in your repositories.** Inside the configured roots, a session
113
- writes subagent files to `.claude/agents/`, `.codex/agents/` and
114
- `.opencode/agents/`, and lists those directories in the clone's own
115
- `.git/info/exclude` (`TEZGAH_NO_EXCLUDE=1` stops that).
112
+ - **Agent files.** The install writes tezgah's subagent roles to
113
+ `~/.claude/agents/`, `$CODEX_HOME/agents/` (default `~/.codex/agents/`) and
114
+ `~/.omp/agent/agents/`. Inside the configured roots, a session writes them to
115
+ the repository's `.cursor/agents/` and `.opencode/agents/`, and lists those
116
+ directories in the clone's own `.git/info/exclude` (`TEZGAH_NO_EXCLUDE=1`
117
+ stops that).
116
118
  - **A daily update check.** The status line asks for a newer release at most
117
119
  once a day; `~/.config/tezgah/update-check-off` or `TEZGAH_UPDATE_CHECK=0`
118
120
  turns it off.
package/bin/tezgah-docs CHANGED
@@ -24,7 +24,7 @@ every word is part of the query.
24
24
 
25
25
  Matching is word-based and case-insensitive against an entry's title, its
26
26
  questions, its sources and its path; every word must match, so a second word
27
- narrows. A query that matches nothing is the last resort of one TypeSafe Choice
27
+ narrows. A query that matches nothing is the last resort of one judge-seam Choice
28
28
  question over the pages themselves; it answers `none` for a query no page
29
29
  covers. When that question gives no judgement - unavailable (`docs-judge-off`,
30
30
  the seam's `judge-off`, no credential) or a call that failed or came back
@@ -106,7 +106,7 @@ RUN = re.compile(r"((?:`[A-Za-z_][A-Za-z0-9_]*(?:\(\))?`\s*[/,]?\s*){1,5})$")
106
106
  CITE = re.compile(r"`([\w.-]+(?:/[\w.-]+)+|[\w-]+\.\w+):(\d+)(?:-(\d+))?`"
107
107
  r"|`(:(\d+)(?:-(\d+))?)`")
108
108
  # The symbol written after the citation instead of before it, the way a table
109
- # cell reads: `hooks/tezgah_policy.py:667-928` (`CORE`).
109
+ # cell reads: `hooks/tezgah_policy.py:680-948` (`CORE`).
110
110
  AFTER = re.compile(r"\s*\(`([A-Za-z_][A-Za-z0-9_]*)(?:\(\))?`\)")
111
111
  # Any `path:line`, backticked or not: what CITE does not match of these (a
112
112
  # bare token, a comma list `x.py:12,40`) is counted as unjudged, so a citation
@@ -1036,7 +1036,7 @@ def record(result):
1036
1036
 
1037
1037
 
1038
1038
  def judge_pick(query):
1039
- """(judged, page): whether a TypeSafe Choice question came back with one of
1039
+ """(judged, page): whether a judge-seam Choice question came back with one of
1040
1040
  its options, and the page it named (None for `none`).
1041
1041
 
1042
1042
  The last resort for a query the index could not place: a word the index does
package/bin/tezgah-route CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """tezgah-route - which tier worker a delegated task goes to, and the model table.
2
+ """tezgah-route - which worker a delegated task goes to, and the model table.
3
3
 
4
4
  tezgah-route "<brief>" [--phase P] [--json] the agent to spawn, and why
5
5
  tezgah-route --check the table's age and refresh flags
@@ -9,11 +9,17 @@
9
9
  tezgah-route --report each route's first check, by tier
10
10
 
11
11
  A brief naming stored data, a migration, credentials, the gate or security goes to
12
- tezgah-frontier by rule; otherwise Jev picks the tier (one Choice, ~650 input
13
- tokens, ~0.4 s); with no key or `judge-off`, `--phase`
14
- (explore|code|mechanical|plan|review|research) picks it from the static table, and
15
- with neither the middle tier is used. The brief is sent to api.typesafe.ai (or the
16
- OpenRouter fallback) - write it in English, the language the judge reads best.
12
+ tezgah-frontier by rule - or to tezgah-security when it only reviews; otherwise
13
+ Jev answers two Choices in one call (~700 input tokens, ~0.4 s): the tier, and
14
+ whether the brief is the whole job of a specialist (tezgah-tester, -docs,
15
+ -security, -ui, -researcher, -reviewer), which is then the agent named. With no
16
+ key or `judge-off`, `--phase review|research` names the reviewer or the
17
+ researcher, another `--phase` (explore|code|mechanical|plan) picks the tier from
18
+ the static table, and with neither the middle tier is used. The brief is sent to the judge seam's
19
+ provider - the session's own CLI (omp, claude, opencode, cursor, codex) or the Jev
20
+ model through TypeSafe, OpenRouter System One, Cloudflare Workers AI or a
21
+ compatible endpoint (TEZGAH_JEV_PROVIDER picks one), else the OpenRouter chat
22
+ fallback - so write it in English, the language the judge reads best.
17
23
 
18
24
  Every routed call writes one `route` row to the session's ledger (TEZGAH_SESSION):
19
25
  tier, agent, what set the tier (`via`: rule|jev|phase|default), the static
package/bin/tezgah-setup CHANGED
@@ -13,7 +13,6 @@
13
13
  tezgah-setup --adopt move the predecessor wiring aside
14
14
  tezgah-setup --sync dev: copy this checkout over the Claude plugin copy
15
15
  tezgah-setup --write-manifest release: regenerate the tracked MANIFEST from git
16
- tezgah-setup --write-plugin-agents release: re-render the plugin's agents/ from the role source
17
16
  tezgah-setup --status [PATH] print the armed/used checklist
18
17
  tezgah-setup --agents [PATH] regenerate this repo's subagent set
19
18
  tezgah-setup --deps [--dry-run] install missing optional tools only
@@ -884,6 +883,22 @@ def save_features(ids):
884
883
  return True
885
884
 
886
885
 
886
+ def set_jev(value):
887
+ """Persist `value` as config.json["jev"] - this key's only writer - and print
888
+ the carriers it resolves to now. An unchanged value writes nothing, like
889
+ `save_features`; a config that does not parse is refused, not replaced."""
890
+ import tezgah_judge
891
+ cfg = read_config(tp.CONFIG, {})
892
+ if cfg is None:
893
+ sys.stderr.write("tezgah-setup: %s is not valid JSON; jev not set\n" % tp.CONFIG)
894
+ return 1
895
+ if cfg.get("jev") != value:
896
+ cfg["jev"] = value
897
+ write_json(tp.CONFIG, cfg)
898
+ print("%s %s %s" % (OK, tp.CONFIG, tezgah_judge.jev_summary()))
899
+ return 0
900
+
901
+
887
902
  def app_servers(selected=None):
888
903
  """The app-analysis MCP servers to wire this run (shared spec)."""
889
904
  return tezgah_apps.servers(selection() if selected is None else selected)
@@ -1147,9 +1162,33 @@ def install_claude():
1147
1162
  # (`TEZGAH_CORE_IN_FILE`, hooks/projects-auto-init.py), so a session pays for
1148
1163
  # the contract once.
1149
1164
  print("%s %s (always-on contract)" % (OK, write_rules("claude")))
1165
+ write_user_agents("claude")
1150
1166
  register_claude_plugin()
1151
1167
 
1152
1168
 
1169
+ def write_user_agents(host):
1170
+ """The host's tezgah roles in its user agent dir, where it loads them from
1171
+ the first session (a repo dir written at session start is missed there),
1172
+ then tezgah's MARKER files no longer wanted - `agents-off`, a lost
1173
+ capability, a retired role - are removed. The user's own files stay."""
1174
+ agents_dir = tezgah_agents.user_agent_dir(host)
1175
+ wanted = tezgah_agents.user_agents(host)
1176
+ for name, text in sorted(wanted.items()):
1177
+ path = os.path.join(agents_dir, name)
1178
+ if write_if_changed(path, text):
1179
+ print("%s %s" % (OK, path))
1180
+ for path in tezgah_agents.remove_stale(agents_dir, wanted):
1181
+ print("%s removed %s" % (OK, path))
1182
+
1183
+
1184
+ def user_agent_files(host):
1185
+ """{filename: text} of tezgah's MARKER files in the host's user agent dir."""
1186
+ d = tezgah_agents.user_agent_dir(host)
1187
+ return {n: t for n in (os.listdir(d) if os.path.isdir(d) else [])
1188
+ if n.startswith("tezgah-")
1189
+ and tezgah_agents.MARKER in (t := tezgah_agents._read(os.path.join(d, n)) or "")}
1190
+
1191
+
1153
1192
  # The marketplace a manifest rendered by this installer carries, so Claude
1154
1193
  # installs the plugin as `tezgah@tezgah-local`. A manifest the tree already has
1155
1194
  # under another name (the maintainer's) is kept, name included.
@@ -1342,6 +1381,7 @@ def install_codex():
1342
1381
  # instructions Codex keeps for the whole session. Global, like omp's
1343
1382
  # RULES.md, because a per-project file would be a tracked project file.
1344
1383
  print("%s %s (always-on contract)" % (OK, write_rules("codex")))
1384
+ write_user_agents("codex")
1345
1385
 
1346
1386
 
1347
1387
  def _toml_str(value):
@@ -3277,6 +3317,9 @@ def verify_uninstall(hosts, full):
3277
3317
  # the user's own CLAUDE.md may name tezgah; only the block is ours
3278
3318
  clean("claude global CLAUDE.md block gone", [CLAUDE_RULES],
3279
3319
  needle="<!-- tezgah:start")
3320
+ claim("claude subagents gone",
3321
+ [os.path.join(tezgah_agents.user_agent_dir("claude"), n)
3322
+ for n in user_agent_files("claude")])
3280
3323
  if "codex" in hosts:
3281
3324
  clean("codex hooks.json unwired", [os.path.join(CODEX, "hooks.json")])
3282
3325
  claim("codex skills unlinked",
@@ -3288,6 +3331,9 @@ def verify_uninstall(hosts, full):
3288
3331
  clean("codex global AGENTS.md block gone",
3289
3332
  [os.path.join(CODEX, n) for n in CODEX_RULES],
3290
3333
  needle="<!-- tezgah:start")
3334
+ claim("codex subagents gone",
3335
+ [os.path.join(tezgah_agents.user_agent_dir("codex"), n)
3336
+ for n in user_agent_files("codex")])
3291
3337
  if "opencode" in hosts:
3292
3338
  claim("opencode plugin unlinked",
3293
3339
  [os.path.join(OPENCODE, d, "tezgah.js") for d in ("plugins", "plugin")]
@@ -3438,6 +3484,13 @@ def uninstall_claude():
3438
3484
  unwire_claude_settings()
3439
3485
  drop_claude_mcp_row()
3440
3486
  strip_managed_md(CLAUDE_RULES)
3487
+ remove_user_agents("claude")
3488
+
3489
+
3490
+ def remove_user_agents(host):
3491
+ """Every tezgah MARKER file in the host's user agent dir; the user's stay."""
3492
+ for path in tezgah_agents.remove_stale(tezgah_agents.user_agent_dir(host), ()):
3493
+ print("%s removed %s" % (OK, path))
3441
3494
 
3442
3495
 
3443
3496
  def drop_claude_mcp_row():
@@ -3472,6 +3525,7 @@ def uninstall_codex():
3472
3525
  # both names: the override may have appeared, or gone, since the install
3473
3526
  for name in CODEX_RULES:
3474
3527
  strip_managed_md(os.path.join(CODEX, name))
3528
+ remove_user_agents("codex")
3475
3529
 
3476
3530
 
3477
3531
  def uninstall_opencode():
@@ -4413,6 +4467,8 @@ def host_checks_claude():
4413
4467
  bool(copies) and all(plugin_copy_current(t) for t in copies)),
4414
4468
  ("CLAUDE.md carries the contract (matches a fresh render)",
4415
4469
  rules_current("claude")),
4470
+ ("subagents in %s (match a fresh render)" % tezgah_agents.user_agent_dir("claude"),
4471
+ user_agent_files("claude") == tezgah_agents.user_agents("claude")),
4416
4472
  ] + claude_mcp_rows(copies)
4417
4473
 
4418
4474
 
@@ -4601,6 +4657,8 @@ def host_checks_codex():
4601
4657
  ("MCP in config.toml", "mcp_servers.codegraph" in toml),
4602
4658
  ("%s carries the contract (matches a fresh render)"
4603
4659
  % os.path.basename(codex_rules()), rules_current("codex")),
4660
+ ("subagents in %s (match a fresh render)" % tezgah_agents.user_agent_dir("codex"),
4661
+ user_agent_files("codex") == tezgah_agents.user_agents("codex")),
4604
4662
  ] + mcp_rows(toml_servers(toml_path), "config.toml") + [
4605
4663
  mcp_row(TEZGAH_MCP_NAME, toml_mcp_entry(toml_path, TEZGAH_MCP_NAME),
4606
4664
  "config.toml", tezgah_mcp_command()),
@@ -4767,7 +4825,7 @@ def host_checks_omp():
4767
4825
  tp.have_typesafe_key()),
4768
4826
  # One row per channel: the two disagree in both directions - a key file
4769
4827
  # alone serves the seam, an omp login-store record alone serves omp.
4770
- ("judgement seam key resolvable (env or key file)",
4828
+ ("judgement seam key resolvable (session CLI, Jev carrier or OpenRouter)",
4771
4829
  tp.have_judge_key()),
4772
4830
  ("status line answers", omp_hook_answers()), ("tier models in task.agentModelOverrides and modelRoles.plan/slow", tier_models_current()),
4773
4831
  ]
@@ -4913,24 +4971,6 @@ def write_manifest():
4913
4971
  return 0
4914
4972
 
4915
4973
 
4916
- def write_plugin_agents():
4917
- """Re-render the plugin's tracked `agents/*.md` from tezgah_agents' role
4918
- bodies, the source the per-repo agents are generated from. A plain write,
4919
- not write_if_changed: a tracked file needs no `.tezgah-bak` beside it."""
4920
- for name, text in tezgah_agents.plugin_agents().items():
4921
- path = os.path.join(HERE, "agents", name)
4922
- try:
4923
- with open(path, encoding="utf-8") as fh:
4924
- if fh.read() == text:
4925
- continue
4926
- except OSError:
4927
- pass
4928
- with open(path, "w", encoding="utf-8") as fh:
4929
- fh.write(text)
4930
- print("%s %s" % (OK, path))
4931
- return 0
4932
-
4933
-
4934
4974
  def tree_sha(root, files):
4935
4975
  """sha256 over each relpath and its bytes, or None when one is unreadable."""
4936
4976
  h = hashlib.sha256()
@@ -5631,8 +5671,6 @@ def main(argv):
5631
5671
  help="regenerate this repo's subagent set (default: cwd)")
5632
5672
  ap.add_argument("--write-manifest", action="store_true",
5633
5673
  help="release: regenerate the tracked MANIFEST from git")
5634
- ap.add_argument("--write-plugin-agents", action="store_true",
5635
- help="release: re-render the plugin's agents/ from the role source")
5636
5674
  ap.add_argument("--deps", action="store_true")
5637
5675
  ap.add_argument("--mcp-schemas", action="store_true",
5638
5676
  help="measure the MCP tool-schema band by asking each server")
@@ -5679,6 +5717,11 @@ def main(argv):
5679
5717
  help="with --install: the language replies are asked for and "
5680
5718
  "the Stop rule checks (config.json `reply_lang`; `tr` "
5681
5719
  "when unset, `any` checks none)")
5720
+ ap.add_argument("--jev", type=tp.jev_name, choices=tp.JEV_CHOICES,
5721
+ help="the carrier the judgement seam reaches the Jev model "
5722
+ "through (config.json `jev`; `auto` tries every one "
5723
+ "that resolves); TEZGAH_JEV_PROVIDER and JEV_PROVIDER "
5724
+ "still win over it")
5682
5725
  # `--version` is argparse's print-and-exit action, so `--upgrade --version V`
5683
5726
  # would print the current version, exit 0 and move nothing at all - the
5684
5727
  # "reads as done, did nothing" shape this repo hunts. Refused by name here,
@@ -5731,6 +5774,8 @@ def main(argv):
5731
5774
  if args.features:
5732
5775
  features_report()
5733
5776
  return 0
5777
+ if args.jev:
5778
+ return set_jev(args.jev)
5734
5779
  if args.refresh:
5735
5780
  paths = refresh_contract()
5736
5781
  for path in paths:
@@ -5740,8 +5785,6 @@ def main(argv):
5740
5785
  return 0
5741
5786
  if args.write_manifest:
5742
5787
  return write_manifest()
5743
- if args.write_plugin_agents:
5744
- return write_plugin_agents()
5745
5788
  if args.status:
5746
5789
  print(tezgah_context.health_lines(os.path.realpath(expand(args.status))))
5747
5790
  # The install-health half of this report, from the same function the
package/bin/tezgah-status CHANGED
@@ -298,7 +298,9 @@ def main():
298
298
  last = tj.last_use()
299
299
  if "--json" in flags:
300
300
  print(json.dumps(last, sort_keys=True))
301
- elif not last:
301
+ return 0
302
+ print(tj.jev_summary())
303
+ if not last:
302
304
  print("judge: no judgement recorded")
303
305
  elif last.get("provider"):
304
306
  print("judge: %s answered with %s/%s (fallback=%s)%s" % (