@r1z4x/tezgah 1.11.0 → 1.14.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 (109) hide show
  1. package/CHANGELOG.md +507 -1
  2. package/MANIFEST +40 -0
  3. package/NOTICE +152 -0
  4. package/README.de.md +41 -20
  5. package/README.es.md +39 -19
  6. package/README.fr.md +41 -20
  7. package/README.md +18 -10
  8. package/README.tr.md +39 -21
  9. package/bin/codegen +9 -2
  10. package/bin/consult +6 -0
  11. package/bin/tezgah-adhd +3 -0
  12. package/bin/tezgah-agents +3 -0
  13. package/bin/tezgah-capture +3 -0
  14. package/bin/tezgah-design +329 -54
  15. package/bin/tezgah-docs +158 -42
  16. package/bin/tezgah-dsh +1 -1
  17. package/bin/tezgah-gate +7 -2
  18. package/bin/tezgah-import-ai-research +16 -4
  19. package/bin/tezgah-index +3 -0
  20. package/bin/tezgah-lessons +13 -4
  21. package/bin/tezgah-mcp +4 -0
  22. package/bin/tezgah-migrate +3 -0
  23. package/bin/tezgah-ocr-review +2 -1
  24. package/bin/tezgah-optimize +4 -1
  25. package/bin/tezgah-pony +3 -0
  26. package/bin/tezgah-research +51 -10
  27. package/bin/tezgah-rollback +3 -0
  28. package/bin/tezgah-route +1 -1
  29. package/bin/tezgah-setup +1741 -95
  30. package/bin/tezgah-status +11 -1
  31. package/bin/tezgah-task +58 -17
  32. package/bin/tezgah.js +10 -3
  33. package/commands/adhd.md +1 -1
  34. package/docs/README.md +19 -8
  35. package/docs/architecture.md +2 -2
  36. package/docs/citations-baseline.json +11 -17
  37. package/docs/clarity-baseline.json +12 -6
  38. package/docs/contract.md +83 -34
  39. package/docs/environment.md +59 -24
  40. package/docs/evidence.md +128 -25
  41. package/docs/gate.md +77 -23
  42. package/docs/glossary.md +1 -1
  43. package/docs/hosts.md +343 -48
  44. package/docs/layers.md +2 -2
  45. package/docs/models.md +6 -0
  46. package/docs/operations.md +83 -29
  47. package/docs/research.md +37 -11
  48. package/docs/skills.md +31 -8
  49. package/docs/status-line.md +39 -23
  50. package/docs/testing.md +50 -17
  51. package/docs/vision.md +6 -2
  52. package/hooks/hooks.json +1 -1
  53. package/hooks/lang/en.json +16 -2
  54. package/hooks/lang/tr.json +14 -2
  55. package/hooks/projects-posttooluse.py +11 -3
  56. package/hooks/projects-stop.py +3 -2
  57. package/hooks/tezgah_agents.py +27 -3
  58. package/hooks/tezgah_attest.py +79 -10
  59. package/hooks/tezgah_context.py +61 -31
  60. package/hooks/tezgah_gate.py +480 -126
  61. package/hooks/tezgah_integrity.py +577 -133
  62. package/hooks/tezgah_lessons.py +5 -2
  63. package/hooks/tezgah_paths.py +60 -8
  64. package/hooks/tezgah_policy.py +48 -18
  65. package/hooks/tezgah_replay.py +8 -2
  66. package/hooks/tezgah_research.py +167 -24
  67. package/hooks/tezgah_skill_pick.py +1 -1
  68. package/hooks/tezgah_snapshot.py +6 -3
  69. package/hooks/tezgah_stack.py +236 -1
  70. package/hooks/tezgah_stopspec.py +2 -1
  71. package/hooks/tezgah_task.py +2 -1
  72. package/hosts/amp/hook.py +217 -0
  73. package/hosts/amp/tezgah-plugin.ts.in +396 -0
  74. package/hosts/antigravity/hook.py +255 -0
  75. package/hosts/codex/hook.py +3 -2
  76. package/hosts/commandcode/hook.py +239 -0
  77. package/hosts/copilot/hook.py +284 -0
  78. package/hosts/cursor/hook.py +4 -2
  79. package/hosts/droid/hook.py +259 -0
  80. package/hosts/dsh/hooks.json +1 -1
  81. package/hosts/gemini/hook.py +213 -0
  82. package/hosts/goose/hook.py +145 -0
  83. package/hosts/omp/hook.py +5 -2
  84. package/hosts/omp/tezgah-hook.ts.in +157 -42
  85. package/hosts/opencode/plugins/tezgah.js +209 -63
  86. package/hosts/qwen/hook.py +206 -0
  87. package/output-styles/tezgah.md +11 -6
  88. package/package.json +6 -3
  89. package/packaging/build.sh +4 -4
  90. package/packaging/install.sh +11 -0
  91. package/packaging/upgrade.sh +3 -0
  92. package/skills/ai-research/LICENSE +21 -0
  93. package/skills/design-contract/SKILL.md +27 -6
  94. package/skills/design-library/LICENSE +21 -0
  95. package/skills/harness/SKILL.md +2 -0
  96. package/skills/i-have-adhd/SKILL.md +10 -1
  97. package/skills/plan-add/SKILL.md +5 -1
  98. package/skills/plan-add/render_table.py +7 -0
  99. package/skills/pm-frameworks/LICENSE +21 -0
  100. package/skills/research/SKILL.md +22 -9
  101. package/skills/rl-env/LICENSE +201 -0
  102. package/skills/tezgah-contract/SKILL.md +21 -8
  103. package/statusline.py +20 -9
  104. package/third_party/open-code-review/LICENSE +201 -0
  105. package/third_party/open-code-review/README.md +15 -0
  106. package/third_party/open-code-review/prompts/main_task_system.md +26 -0
  107. package/third_party/open-code-review/prompts/review_filter_task_system.md +13 -0
  108. package/workflows/graph-map.js +14 -5
  109. package/workflows/graph-review.js +14 -2
package/CHANGELOG.md CHANGED
@@ -6,6 +6,504 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.14.0] - 2026-11-01
10
+
11
+ ### Fixed
12
+
13
+ - **The gate reads hook skips behind wrappers (plan 147).** It refuses a hook
14
+ skip behind a wrapper with arguments (`nice -n 5`, `timeout 60`). That covers
15
+ long value options, option clusters and `env -S`. It also refuses one inside
16
+ `eval`, `eval --` or `builtin eval`. One wrapper reader serves three rules:
17
+ shortcut, control and `VERIFY`. So a wrapped check meets the neuter and piped
18
+ refusals. A check piped into `rg`, `awk`, `sort`, `cat`, `less`, `sed` or
19
+ `tee` meets a refusal too. A trap other than plain cleanup on exit records
20
+ the check as ran, never as passed.
21
+
22
+ - **The gate closes control-plane, credit, language and credential bypasses
23
+ (plan 148).** Git config hooks settings meet a refusal: `core.hooksPath`,
24
+ `include.path`, `includeIf` and worktree config. The gate reads them in a
25
+ file, in `git config`, in `--file` and in `-c`. It refuses credits through `--trailer`, a git
26
+ path or `bash -c`. It refuses non-English names from `git worktree add -b`,
27
+ `git branch -m`, `gh -t`, `git tag` and push refspecs. It refuses credential
28
+ files staged by `git stage`, `git commit <path>` or `update-index --add`. It
29
+ refuses `ditto`, `mkfifo` and `${VAR:-…}` into the control plane. It refuses
30
+ MCP writes, moves and deletes into protected paths. It refuses a write racing
31
+ another session's in-flight write.
32
+
33
+ - **opencode sends shell writes to the core gate first (plan 149).** Every shell
34
+ write, swallowed check and hook-skipping line goes to the core gate. It gets
35
+ the core control, credential, workspace and shortcut refusals verbatim. A
36
+ refusal the plugin decides itself writes the `began` row the core writes.
37
+ `TEZGAH_HOME` is no longer exported.
38
+
39
+ - **The Stop rule reads more claim shapes (plan 150).** An empty Claude Bash
40
+ result is no pass. English past-tense and bare test claims and "not
41
+ tested/checked" admissions read like their Turkish twins. Prose-only and
42
+ benchmark claim reads honour the question and negation window. A plan claim
43
+ records as a claim. A subagent brief authorizes no switch.
44
+
45
+ - **Uninstall restores what the install found (plans 151, 152, 154).** Every
46
+ key and file goes back to its pre-install state. That covers your statusLine,
47
+ opencode compaction, omp codegraph row and `{}` settings files. It covers the
48
+ no-attribution values of Copilot, Command Code, Amp and Droid, and JSON
49
+ indent. It removes the directories tezgah created. It counts only tezgah's
50
+ own markers as leftovers. A partial uninstall keeps the farm the still-armed
51
+ hosts run from. A HOME behind a symlink no longer leaves
52
+ `~/.codex/bin/consult`.
53
+
54
+ - **Install names a host file it cannot read (plan 152).** Such a host rules or
55
+ settings file makes install exit 1. It no longer overwrites that file or
56
+ crashes. Install skips a commented host settings file with exit 0 and names it.
57
+
58
+ - **omp gates MCP device writes and `ast_edit` (plan 153).** An MCP device write
59
+ (`xd://mcp__…`) and `ast_edit` reach the gate as the calls they carry. The
60
+ device write itself meets the gate too. A glob or directory edit target takes the
61
+ control state it reaches. An MCP query records as external, not an edit.
62
+
63
+ - **`--uninstall` fails while anything tezgah wrote survives (plan 154).** It
64
+ exits 1 in that case. The farm carries `tezgah-optimize` and `tezgah-taste`.
65
+ Install refuses abbreviated flags. Piped `--upgrade` output reads in order.
66
+ Install names an unwritable HOME instead of printing a traceback. It refuses
67
+ Python below 3.10 early and names the version found.
68
+
69
+ - **The npm package and the READMEs match what ships (plan 156).** The package
70
+ ships NOTICE and the open-code-review prompts and no `__pycache__`. Every
71
+ vendored skill tree carries its upstream LICENSE. package.json and all five
72
+ READMEs name the fourteen hosts and the 17 refusal rules plus the nudge. They
73
+ also name the 17 rule switches and the current state of the Turkish claim
74
+ reader.
75
+
76
+ - **Farm CLIs answer help (plan 158).** They answer `-h/--help` with exit 0 and
77
+ refuse unknown flags. The `consult` CLI no longer sends a mistyped flag to the
78
+ panel. `tezgah-research check --json` prints pure JSON. `tezgah-status
79
+ --observable=` refuses an unknown measure.
80
+
81
+ - **Build and upgrade scripts fail clearly (plan 159).** `build.sh` without
82
+ `--version` names the artifact after the newest release heading instead of
83
+ `Unreleased`. `upgrade.sh` stops early with a clear message when `curl` or
84
+ `tar` is missing. The Windows CI leg checks that `install.ps1` refuses a
85
+ tampered artifact.
86
+
87
+ - **The graph-map workflow names a failed agent (plan 160).** A failed survey,
88
+ module reader, synthesize or gap-check agent lands in an `unknown` list. It is
89
+ no longer reported as an empty, clean result.
90
+
91
+ - **Subagents read the parent's switches (plan 165).** A subagent latches kill
92
+ switches on its parent's first ledger row when that came first. Every adapter
93
+ takes the parent from the host, the omp and Amp bridges and the opencode
94
+ plugin included. A switch the parent made mid-session no longer disarms rules
95
+ in subagents it spawns later.
96
+
97
+ - **The design chain reaches real UI work (plan 144).** `uygulamayı redesign et`
98
+ arms the design rule, and the frozen arming corpus stays the same. A
99
+ route-file write (`src/app/dashboard/page.tsx`, `src/routes/+page.svelte`)
100
+ owes the design check. `tezgah-design derive` reads a nested Tailwind palette
101
+ (`brand: {50..900}`) and every config in a monorepo, not only the first.
102
+
103
+ - **The citation ratchet holds in every checkout (plan 168).** `bin/tezgah-docs
104
+ --citations` never judges a path under the private `.tezgah/`. The unjudged
105
+ ratchet now holds the same in CI, worktrees and the main checkout.
106
+
107
+ ### Added
108
+
109
+ - **`derive` reads JS/TS theme modules (plan 145).** A repository whose only
110
+ token source is a module such as `src/config/theme.ts` now derives a contract.
111
+ It carries `source: theme-module` with its colours, spacing unit and type
112
+ scale. `read_text` opens each path component with `O_NOFOLLOW` under the
113
+ repository root. It refuses a parent swapped for a symlink.
114
+
115
+ - **`derive --suggest` prints a candidate component inventory (plan 146).** It
116
+ lists `{name, kind}` candidates read off component files and writes nothing.
117
+ It skips tests, stories and names already in the contract. It guesses `kind`
118
+ only from the name and exits 2 when there is no candidate. A person adds the
119
+ ones the repository owns.
120
+
121
+ - **`tezgah-docs --citations` judges more citation shapes (plan 166).** It
122
+ judges bare `(N-M)` and `:N-M` ranges after a cited path, and JS/TS top-level
123
+ symbols. It flags ranges past a file end. Stale opencode plugin citations are
124
+ re-anchored by symbol.
125
+
126
+ ### Changed
127
+
128
+ - **Two sessions meet on their first write of one file (plan 171).** The race
129
+ rule counts another session's unanswered write for 60 seconds. After that it
130
+ counts only if that host answers its writes. A refused prompt holds the file
131
+ about a minute.
132
+
133
+ - **The gate parses a command once per call (plan 169).** Its readers share
134
+ one parse of a command's heredocs and shell commands. Median gate latency is
135
+ back at the 1.13.0 figure.
136
+
137
+ - **Docs match the code again (plan 161).** The hosts, operations, skills,
138
+ research and testing pages document the tezgah-mcp tools and tezgah-decisions.
139
+ They list the omp events and the opencode `event` hook. They state that
140
+ `--install --dry-run` writes. Live probes no longer read as host obedience.
141
+ Drifted citations now name their symbol. tests/test_docs_reach.py keeps them
142
+ in step.
143
+
144
+ - **Ledger rows carry their version (plan 162).** Snapshot and rollback rows now
145
+ carry the row version `v`. The evidence page (`docs/evidence.md`) lists every
146
+ ledger kind, all optional fields and every `subagent_end` writer. It also
147
+ lists the exact prose-exemption names, and a new test checks it.
148
+
149
+ - **docs/contract.md and docs/environment.md match the code (plan 157).** They
150
+ cover the switch table, every environment variable read, budgets, DROP_ORDER
151
+ and the label-anchored composition order. The core and the reminder name the
152
+ kill-switch directory from XDG_CONFIG_HOME. The Ponytail Off line names its
153
+ switches. The suite never runs a real gh on upgrade.
154
+
155
+ - **Status-line marks and docs are complete (plan 163).** `python3
156
+ bin/tezgah-docs` and `python3 bin/tezgah-research` earn the judge and research
157
+ marks. The legend and docs name every host that cannot observe skill reads.
158
+ docs/status-line.md documents the extension `{"segments": [...]}` output and
159
+ an unknown `--observable` measure exiting 2. `/tezgah:adhd` states `on` as the
160
+ default.
161
+
162
+ - **`reminder-off` drops only the per-turn reminder line (plan 164).** Armed
163
+ rules, the gate-inactive notice and the off-note stay. Kill-switch off-notes
164
+ also name the matching section of the `tezgah` output style.
165
+
166
+ - **The impacted test map covers docs pages (plan 167).** tests/impacted.py runs
167
+ tests/test_docs_reach.py for docs/ page changes. It maps the omp status-line
168
+ e2e by the generic helper rule. The research seal test parses `check --json`
169
+ stdout as pure JSON.
170
+
171
+ - **Neuter rows for the audit guards (plan 155).** The neuter matrix gains 59
172
+ rows. Each guard the audit fixes added has one, with a behavioural killer.
173
+ It writes its killer cache only with `--update-killers`.
174
+ It answers `--help`. It runs on a push touching any mutated file. Gate vectors
175
+ cover every refusal rule: stateless ones by vector, stateful ones by named
176
+ tests.
177
+
178
+ ## [1.13.0] - 2026-10-29
179
+
180
+ ### Fixed
181
+
182
+ - **omp handed the Stop rule a message object, not the reply (plan 143).**
183
+ omp's `session_stop` carries `last_assistant_message` as the object its
184
+ `getLastAssistantMessage()` returns. The bridge passed it through, so the
185
+ rule judged `str(dict)`: one line, no list items, diluted prose.
186
+ - Every line-shaped class read a text with no lines: the list cap, the
187
+ residual sections, the question the reply ends on, the handed step. The
188
+ language check read a share that was not the reply's.
189
+ - Measured on the owner's evidence database: 1643 of 1656 omp `shape` rows
190
+ of 45 days carried `lines: 1`. Every other host wrote 209 multi-line of
191
+ 231. Of three days' refusals, 24 were `reply language` over such a row.
192
+ - `stop_reason` reads every reply through `reply_prose` now, so no host can
193
+ repeat it. The omp bridge sends the text blocks.
194
+
195
+ ### Added
196
+
197
+ - **The Stop rule knows the host's plan mode (plan 142).** Every Stop adapter
198
+ passes the host's `permission_mode` to the core now. omp's extension sends
199
+ `plan` from `ctx.sessionModes.getState()`. `plan` and Droid's `spec` are the
200
+ read-only ones.
201
+ - In such a turn the rule lets the approval question through. It used to
202
+ refuse it as a `needless ask` - the one question that mode allows.
203
+ - A first-person claim that a command or check ran, made in such a turn, is
204
+ the new `plan claim` class. Nothing could run, so the claim says what
205
+ cannot have happened. A check that passed anyway clears it, and a plan's
206
+ own acceptance line ("Done when all tests pass") is no claim.
207
+ - The new `handed step` class refuses a reply that tells the reader to run a
208
+ command. It asks only of a turn that ran commands of its own, outside plan
209
+ mode. An outward step, one no default may take, the owner's consent,
210
+ tezgah's own install and a `brew` step stay the user's. So does a line that
211
+ types its blocker, and an answer about how a command is run.
212
+ - Measured on the owner's transcripts: of 3307 user turns, 27 replies handed
213
+ back a command and no class refused any of them. The shipped shapes refuse
214
+ 9 turns, 8 of them a step the session could have taken.
215
+
216
+ ## [1.12.0] - 2026-10-29
217
+
218
+ ### Added
219
+
220
+ - **Amp is a host (`amp`, plan 140).** `tezgah-setup --install --hosts amp`
221
+ writes one TypeScript plugin, `tezgah.ts`, into Amp's system plugin dir.
222
+ That dir is `$XDG_CONFIG_HOME/amp/plugins/`, else `~/.config/amp/plugins/`.
223
+ The install renders it from `hosts/amp/tezgah-plugin.ts.in`. Amp loads every
224
+ `.ts` there, and a running Amp needs `plugins: reload`. The install links the
225
+ skills into `~/.config/amp/skills` and writes the always-on block into
226
+ `~/.config/amp/AGENTS.md`. It sets Amp's two commit trailers
227
+ (`amp.git.commit.coauthor.enabled`, `amp.git.commit.ampThread.enabled`) to
228
+ false in `settings.json` unless the user set them. It writes that file only
229
+ while it is plain JSON with no `settings.jsonc` beside it. The uninstall
230
+ removes the plugin only when it is tezgah's render. It drops a trailer key
231
+ only while that key is still false.
232
+ - The plugin spawns `hosts/amp/hook.py` without blocking. One plugin process
233
+ serves many threads, so it keys its state by thread id. A dead, slow or
234
+ crashing hook allows every call, and the plugin tells each thread once.
235
+ - `tool.call` refuses with `{action: "reject-and-continue"}`. It asks the gate
236
+ for `Bash`, `shell_command`, the edit tools, `Task` and MCP effect names. A
237
+ shell call's text reaches the gate as `command`, also when Amp sends `cmd`.
238
+ - A tool result counts as passed only with an integer `exitCode` on its
239
+ output object. `error` and `cancelled` are failures. A bare `done` shell
240
+ result is a run, never a pass.
241
+ - `agent.end` judges a `done` turn and continues it with `[tezgah:stop]
242
+ <reason>`. The plugin never sends that prompt as the user's. So it writes no
243
+ turn row, grants nothing and cannot reset the re-ask budget. A thread with
244
+ a parent thread is a subagent. It gets the subagent brief, and its end is a
245
+ record-only row.
246
+ - The session brief rides the thread's first `agent.start`. The status line
247
+ goes on Amp's experimental status item for the active thread.
248
+ - `--report` has Amp rows. `--report --live` loads the plugin with node and
249
+ drives its `tool.call` and `tool.result`. The row stays UNVERIFIED, since
250
+ Amp's own loader did not run it. `tezgah-doctor --stack` reads Amp's
251
+ guidance files, skills, plugins and `amp.mcpServers`. `read_web_page` is a
252
+ web channel for the untrusted label. README and docs name all fourteen
253
+ hosts.
254
+ - **Goose is a host (`goose`, plan 139 slice 5).** `tezgah-setup --install
255
+ --hosts goose` writes tezgah's own plugin dir, `~/.agents/plugins/tezgah/`.
256
+ It holds `plugin.json`, `hooks/hooks.json` and `skills/` links, and it is
257
+ wholly tezgah's. Goose loads the skills as `tezgah:<skill>`. The timeouts are in seconds: Stop 30, the
258
+ rest 5. `on_failure` stays at Goose's default `allow`, so the gate fails open
259
+ on a hook fault. The always-on block goes in `~/.config/goose/.goosehints`.
260
+ Goose adds no co-author trailer and names no setting for one, so there is
261
+ nothing to turn off. The uninstall deletes the plugin dir and the block and
262
+ leaves the rest of `~/.agents` alone.
263
+ - `hosts/goose/hook.py` handles SessionStart (attestation only),
264
+ UserPromptSubmit (the turn row only), PreToolUse, PostToolUse,
265
+ PostToolUseFailure and Stop. The event is the payload's `event`, the cwd its
266
+ `working_dir`, else the hook's own. `shell`, `write` and `edit` map to Bash,
267
+ Write and Edit, with `path` as `file_path` and `before`/`after` as
268
+ `old_string`/`new_string`. `<extension>__<tool>` becomes
269
+ `mcp__<extension>__<tool>`.
270
+ - PreToolUse and Stop block with `{"decision": "block", "reason": ...}` on
271
+ stdout. The outcome is the event: PostToolUse is a pass, PostToolUseFailure
272
+ a failure. Stop judges `last_assistant_message`, or writes a
273
+ `reply_unread` row. With no host block count, the turn rows bound the
274
+ re-asks.
275
+ - `--report` has Goose rows, including one for `disabledPlugins`. `--report
276
+ --live` probes the hook command with Goose's payload and block shape.
277
+ `tezgah-doctor --stack` reads Goose's hints, skills, plugins and the
278
+ `extensions:` map of `config.yaml`. No run has shown the binary obeying the
279
+ block. README and docs/layers.md now name all thirteen hosts.
280
+ - **Antigravity CLI is a host (`antigravity`, plan 139 slice 4).**
281
+ `tezgah-setup --install --hosts antigravity` writes tezgah's hook set under
282
+ the `tezgah` name of `~/.gemini/config/hooks.json`. The user's other names
283
+ stay. The Antigravity App and IDE load that file too. The timeouts are in
284
+ seconds: Stop 30, the rest 5. The status line goes in
285
+ `~/.gemini/antigravity-cli/settings.json` while it is plain JSON, and the skill
286
+ links go in `antigravity-cli/skills`. Antigravity adds no co-author
287
+ trailer of its own, so there is nothing to turn off. The always-on block is
288
+ the `~/.gemini/GEMINI.md` one Gemini CLI shares. An uninstall now keeps it only
289
+ while another armed host reads the same file, so with `GEMINI_CLI_HOME` set
290
+ the two blocks come and go apart.
291
+ - `hosts/antigravity/hook.py` handles SessionStart, PreInvocation,
292
+ PreToolUse, PostToolUse and Stop. The payload names no event, so each
293
+ command passes it as its first argument. `conversationId` is the session
294
+ and `workspacePaths[0]` the cwd. The adapter renames the PascalCase tool
295
+ arguments into the gate's fields: `CommandLine`, `TargetFile`, `CodeContent`,
296
+ `TargetContent`/`ReplacementContent` and `ReplacementChunks`.
297
+ - PreToolUse denies with a top-level `decision: deny`. An allowed call
298
+ answers `decision: allow`, since Antigravity requires the field. PostToolUse reads
299
+ the outcome off `error`: empty is a pass, anything else a failure. The first
300
+ invocation of a turn writes the turn row and carries the reminder as an
301
+ `ephemeralMessage`. A loop that a refusal re-entered writes no turn row, so
302
+ the refusal budget holds.
303
+ - Stop reads the reply from `transcriptPath`, or writes a `reply_unread` row.
304
+ It judges only an idle `model_stop`, and `decision: continue` sends the
305
+ model back into the loop with the reason.
306
+ - `--report` has Antigravity rows, including one for `enabled: false` on the
307
+ `tezgah` name. `--report --live` probes the hook command with Antigravity's
308
+ camelCase payload and deny shape. No run has shown the binary obeying the
309
+ deny.
310
+ - **Gemini CLI is a host (`gemini`, plan 139 slice 3).** `tezgah-setup
311
+ --install --hosts gemini` writes tezgah's own extension dir,
312
+ `~/.gemini/extensions/tezgah/`. It holds `gemini-extension.json`,
313
+ `hooks/hooks.json` and links to the skills.
314
+ `GEMINI_CLI_HOME` moves `~/.gemini` under it. The timeouts are in
315
+ milliseconds: AfterAgent 30000, the rest 5000. settings.json is never
316
+ written, since it is JSON with comments and holds the user's keys. Gemini's
317
+ default prompt adds no co-author trailer and no setting names one. The
318
+ always-on block goes in `~/.gemini/GEMINI.md`, which Antigravity reads too.
319
+ `--uninstall` deletes the extension dir and strips the block only while no
320
+ other host that reads it stays armed.
321
+ - `hosts/gemini/hook.py` handles SessionStart, BeforeAgent, BeforeTool,
322
+ AfterTool and AfterAgent. BeforeAgent writes the turn row and carries the
323
+ per-turn reminder. BeforeTool maps `run_shell_command`, `write_file`,
324
+ `replace`, the grep tools and the subagent tools into the gate's names, and
325
+ `mcp_<server>_<tool>` into `mcp__<server>_<tool>`. It denies with a
326
+ top-level `decision: deny` on exit 0.
327
+ - AfterTool reads the outcome off `tool_response`: an `error` is a failure,
328
+ and a shell result's `Exit Code: N` names a non-zero code. A shell run that
329
+ printed its `Output:` with no exit-code line is a pass, since Gemini writes
330
+ that line only for a non-zero code. AfterAgent judges `prompt_response` and
331
+ denies with `decision: deny`. Gemini sends the reason back through
332
+ BeforeAgent as the next prompt, and that turn row does not reset the
333
+ refusal budget. The status segment rides `systemMessage`.
334
+ - `--report` has Gemini rows, including one for `hooksConfig.enabled: false`
335
+ in settings.json, which runs no hook at all. `--report --live` probes the
336
+ hook command with Gemini's own payload and deny shape. No run has shown the
337
+ binary loading the extension or obeying the deny.
338
+ - **GitHub Copilot CLI is a host (`copilot`, plan 139 slice 2).** `tezgah-setup
339
+ --install --hosts copilot` writes `$COPILOT_HOME/hooks/tezgah.json`, a hooks
340
+ file that is wholly tezgah's. `COPILOT_HOME` defaults to `~/.copilot`. The
341
+ timeouts are in seconds: PreToolUse 10, Stop and SubagentStop 30, the rest 5.
342
+ The inline `hooks` key of settings.json is never used, since one bad item
343
+ there rejects every hook in it. The install sets two keys in settings.json
344
+ while it is plain JSON. The status line runs `tezgah-statusline --copilot`.
345
+ `includeCoAuthoredBy` is `false`, so the agent's commits carry no co-author
346
+ trailer. A value the user set stays. The install links the skills into
347
+ `$COPILOT_HOME/skills` and writes the always-on block in
348
+ `copilot-instructions.md`. `--uninstall` deletes tezgah.json and takes back
349
+ only tezgah's settings keys, links and block.
350
+ - `hosts/copilot/hook.py` uses the PascalCase events, so payloads carry
351
+ snake_case fields and Claude tool names. It maps the runtime spellings too.
352
+ An Edit that names no file but carries a patch reaches the gate as
353
+ `apply_patch`. An MCP tool's `<server>-<tool>` becomes
354
+ `mcp__<server>__<tool>` for a server `mcp-config.json` names. It denies
355
+ with a top-level `permissionDecision: deny` on exit 0.
356
+ - A PreToolUse hook that times out lets the call through, by Copilot's own
357
+ rule. One that crashes denies the call. A repo's `.claude/settings.json`
358
+ hooks run there too.
359
+ - PostToolUse records a pass and PostToolUseFailure a failure. A shell
360
+ result's `exit code N` overrides either. Stop carries no reply, so the hook
361
+ reads the last `assistant.message` from `transcript_path`. A turn end with
362
+ none there becomes a `reply_unread` row. Copilot drops a command hook's
363
+ UserPromptSubmit output, so there is no per-turn reminder. The event still
364
+ writes the turn row. subagentStart gives a delegate the brief. A
365
+ SubagentStop only records its verdict.
366
+ - `--report` has Copilot rows, and `--report --live` probes the hook command
367
+ with Copilot's own payload and deny shape. No run has shown the binary
368
+ obeying the deny.
369
+ - **Command Code is a host (`commandcode`, plan 139 slice 1).** `tezgah-setup
370
+ --install --hosts commandcode` merges tezgah's hook groups into the `hooks`
371
+ key of `~/.commandcode/settings.json` beside the user's. Their timeouts are in
372
+ seconds. The same write sets `attribution.commit` to `""`, so commits carry no
373
+ `CommandCodeBot` co-author trailer. A value the user set stays. The install
374
+ links the skills into `~/.commandcode/skills`. It writes the always-on block
375
+ in `~/.commandcode/AGENTS.md`. `--uninstall` takes back only tezgah's groups,
376
+ links and block. It drops the attribution key only while it holds what the
377
+ install wrote.
378
+ - Command Code's settings hooks see only shell, read, write and edit. So the
379
+ PreToolUse matcher is `shell|write|edit`. The gate there never sees read,
380
+ MCP, subagent or web calls, and plan mode skips every hook.
381
+ - `hosts/commandcode/hook.py` maps `shell_command`, `write_file` and
382
+ `edit_file` onto the gate. It folds a shell call's `args` into `command`.
383
+ It hands `old_value`/`new_value` over as `old_string`/`new_string`. It
384
+ denies with `permissionDecision: deny` on exit 0. It reads a shell call's
385
+ outcome off an `exit code N` in its result. A result with no code counts
386
+ as a run, never a pass.
387
+ - Stop carries no reply, so the hook reads it from `transcript_path`. A turn
388
+ end with none there becomes a `reply_unread` row. The status segment rides
389
+ `systemMessage` at SessionStart and Stop. There is no prompt event, so no
390
+ per-turn reminder.
391
+ - `--report` has Command Code rows, and `--report --live` probes the hook
392
+ command with Command Code's own payload. No run has shown the binary
393
+ obeying the deny.
394
+ - **Factory Droid is a host (`droid`, plan 138 wave 1).** `tezgah-setup --install
395
+ --hosts droid` merges tezgah's hook groups into `~/.factory/hooks.json` beside
396
+ the user's. Their timeouts are in seconds, as Droid reads them. The install
397
+ also links the skills into `~/.factory/skills` beside the user's own links.
398
+ It writes the always-on block in `~/.factory/AGENTS.md`. It sets the
399
+ `statusLine` command and `includeCoAuthoredByDroid: false` in `settings.json`.
400
+ The install edits a settings.json with comments in place and keeps its
401
+ comments (plan 141). `--uninstall` takes back only tezgah's groups, links and keys.
402
+ - Droid reads the `hooks` key of settings.json only while hooks.json is
403
+ absent. So the install creates no hooks.json beside such a key, and the
404
+ report row says why. Then settings.json stays untouched.
405
+ - `hosts/droid/hook.py` maps Droid's tool ids (`Execute`, `Create`,
406
+ `ApplyPatch`, `FetchUrl`) onto the gate and hands ApplyPatch's `input` over
407
+ as `patch`. It denies with `permissionDecision: deny` on exit 0 and reads a
408
+ shell call's outcome off its `[Process exited with code N]` line.
409
+ - Droid's Stop carries no reply, so the hook reads the reply from
410
+ `transcript_path`. A turn end whose reply is not there yet becomes a
411
+ `reply_unread` row and is not judged. SubagentStop records `task_result`
412
+ as a `subagent_end` row.
413
+ - `--report` has Droid rows, and `--report --live` probes the hook command
414
+ with Droid's own payload. `tests/e2e_droid.py` runs the real binary only
415
+ under `TEZGAH_E2E_SPEND=1`, because it spends a model call.
416
+
417
+ - **Qwen Code is a host (`qwen`, plan 138 wave 1).** `tezgah-setup --install
418
+ --hosts qwen` merges tezgah's hook groups into `~/.qwen/settings.json` beside
419
+ Orca's. Their timeouts are in milliseconds, as Qwen reads them. The install
420
+ also adds the `ui.statusLine` command, the skill links and the always-on
421
+ block in `~/.qwen/QWEN.md`. `--uninstall` takes back only tezgah's groups and keys.
422
+ - `hosts/qwen/hook.py` maps Qwen's tool ids (`run_shell_command`,
423
+ `write_file`, `edit`, `agent`, `grep_search`) onto the gate. It denies with
424
+ `permissionDecision: deny` on exit 0, blocks Stop on
425
+ `last_assistant_message`, briefs a subagent at SubagentStart and records
426
+ its end at SubagentStop.
427
+ - `--report` has Qwen rows, and `--report --live` probes the hook command
428
+ with Qwen's own payload. `tests/e2e_qwen.py` runs the real binary only
429
+ under `TEZGAH_E2E_SPEND=1`, because it spends a model call.
430
+ - `tezgah-doctor --stack` and the session attestation know the host. An
431
+ unknown host no longer crashes the attestation.
432
+ - A host-file backup is now owner-only, and a rewritten host config keeps
433
+ its file mode, because Qwen's settings.json holds provider keys.
434
+
435
+ - **Review rules taken from google/elephant-goldfish-model (plan 137).** The
436
+ owner chose them from research line `egm-lessons`. No EGM file ships:
437
+ - `tezgah-task review NNN <r> approve` refuses a plan with no review slice
438
+ record. A host with no slice journal passes `--no-slice "<why>"`, and the
439
+ reason lands on the review line (`bin/tezgah-task`).
440
+ - The contract says that no fresh subagent means no review verdict. It also
441
+ says the author contests a finding only with a file:line and never lowers
442
+ a severity (`hooks/tezgah_policy.py`).
443
+ - tezgah-reviewer has a plan-readiness mode for a plan file. A design choice
444
+ left to the implementer is a major finding, and so is a section it cannot
445
+ paraphrase. plan-add runs it before `phase implementation`
446
+ (`hooks/tezgah_agents.py`).
447
+ - graph-review merges findings with the same file, line and title before its
448
+ verification cap, and reports `caps.deduplicated`.
449
+ - `tests/test_links.py` resolves every relative markdown link the repository
450
+ owns.
451
+
452
+ ### Changed
453
+
454
+ - **The gate refuses a credit verb with "with" or "using" too.** The attribution
455
+ rule read `written by` and `built by`, so `Drafted with Claude` passed. The
456
+ credit verbs now include `drafted`, `produced` and `composed`, and each takes
457
+ `by`, `with` or `using` before a model or vendor name. Prose such as
458
+ `drafted with the owner` still passes (`hooks/tezgah_gate.py::_CREDIT`, plan
459
+ 137).
460
+
461
+ - **A release tag must sit on main.** A new `on-main` job in `release.yml`
462
+ fails the run on a tag off `origin/main`. Then no job builds or uploads
463
+ anything (plan 137).
464
+
465
+ - **An owner-only choice waits for the owner.** Class F now names three more
466
+ steps. One is picking a variant a research, analysis or review recommends.
467
+ The others are a way around a tezgah refusal and deciding about another
468
+ session's work. The core
469
+ Ask policy, the per-turn reminder and the ask warning carry them too. The Stop
470
+ rule's `needless ask` lets a question that asks the owner to pick between
471
+ options through (`OWNER_CHOICE`, `hooks/tezgah_integrity.py`). A worker returns
472
+ such a question as `ESCALATE: owner`, and the orchestrator relays it to the
473
+ user instead of answering it (plan 136).
474
+
475
+ - **A research line records the owner's pick apart from its own.** A rules-9
476
+ line writes its pick as `recommended:` in `decision.md`. A `chosen:` line
477
+ needs the owner's words, which `tezgah-research choose <slug> <decision>
478
+ <variant> --ack "<words>"` writes. The line's rules judge its own pick, never
479
+ the owner's, so an owner may take a dominated variant. `conclude` says when a
480
+ recommendation still waits for the owner (`hooks/tezgah_research.py`).
481
+
482
+ - **A line in flight no longer blocks `init --allow-open`.** A line whose only
483
+ `check` errors are pending verdicts is in flight, not broken. Both `init`
484
+ refusals now say that another session's line goes to the owner.
485
+
486
+ ### Fixed
487
+
488
+ - **Three new hosts no longer record a check that did not pass as a pass.**
489
+ Antigravity's PostToolUse has no output. A command that went to the
490
+ background also ends with an empty `error`. So the hook records each
491
+ Antigravity shell call as ran, never a pass, and Antigravity can never show
492
+ a passing check. A Copilot shell success with no `exit code N` text (a
493
+ background launch) is ran, never a pass. A Gemini result with a `Signal:`
494
+ line (a runner killed by a signal) is a failure.
495
+ - **The gate guards Gemini's and Goose's hook-off keys.** These are Gemini's
496
+ `hooksConfig` and Goose's `disabledPlugins`. Claude's `disableAllHooks` had
497
+ the same guard.
498
+ - **The Command Code uninstall removes tezgah's `attribution.commit: ""`
499
+ beside the user's other attribution keys**, not only when it stood alone.
500
+
501
+ - **A hidden research dir is no longer invisible.** A dot dir under
502
+ `.tezgah/research/` is an error in `status` and in `check` with no slug. The
503
+ gate refuses a write there (`research_hidden_reason`, `hooks/tezgah_gate.py`).
504
+ A session had run a whole line under `.tezgah/research/.staging/` after an
505
+ `init` refusal.
506
+
9
507
  ## [1.11.0] - 2026-10-23
10
508
 
11
509
  ### Added
@@ -36,6 +534,10 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
36
534
 
37
535
  - **A contract reads Hallmark's locked `design.md`, and one precedence table decides the floor.** A `design.md` or `DESIGN.md` at the repository root now feeds `derive`. It gives the palette and font roles, with `source: design-md`. The palette converts from OKLCH to the hex `check` compares. A token file beside it no longer competes. The contract gains a `precedence` block. It names `ours` (states, components, evidence, facts) and `theirs` (material, motion character). It names `shared` (contrast, focus-visible, reduced-motion, tap-target) and `verbatim: style`. `check` refuses with exit 2 a key named by two classes. It also refuses `facts` handed to a theme or to the user's own words.
38
536
 
537
+ - **`tezgah-ocr-review` runs open-code-review's loop on omp's model.** It reaches the model through a loopback shim.
538
+
539
+ - **`tezgah-review-quote` places review findings on the diff.** It finds each finding's location by the code it quotes.
540
+
39
541
 
40
542
  ### Changed
41
543
 
@@ -1299,7 +1801,11 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1299
1801
  the copy could not find `hooks/` and died on import. It now links into the
1300
1802
  installed tree, and also installs the command as `tezgah`.
1301
1803
 
1302
- [Unreleased]: https://github.com/r1z4x/tezgah/compare/v1.10.1...HEAD
1804
+ [Unreleased]: https://github.com/r1z4x/tezgah/compare/v1.14.0...HEAD
1805
+ [1.14.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.14.0
1806
+ [1.13.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.13.0
1807
+ [1.12.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.12.0
1808
+ [1.11.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.11.0
1303
1809
  [1.10.1]: https://github.com/r1z4x/tezgah/releases/tag/v1.10.1
1304
1810
  [1.10.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.10.0
1305
1811
  [1.9.2]: https://github.com/r1z4x/tezgah/releases/tag/v1.9.2