@r1z4x/tezgah 1.11.0 → 1.13.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.
- package/CHANGELOG.md +333 -1
- package/MANIFEST +22 -0
- package/README.md +4 -3
- package/bin/tezgah-docs +1 -1
- package/bin/tezgah-research +39 -4
- package/bin/tezgah-setup +1229 -22
- package/bin/tezgah-task +55 -17
- package/docs/citations-baseline.json +1 -1
- package/docs/clarity-baseline.json +9 -3
- package/docs/contract.md +6 -3
- package/docs/environment.md +3 -2
- package/docs/evidence.md +81 -8
- package/docs/gate.md +6 -2
- package/docs/glossary.md +1 -1
- package/docs/hosts.md +297 -15
- package/docs/layers.md +1 -1
- package/docs/models.md +6 -0
- package/docs/operations.md +16 -5
- package/docs/research.md +36 -10
- package/docs/status-line.md +6 -0
- package/docs/testing.md +3 -1
- package/hooks/hooks.json +1 -1
- package/hooks/lang/en.json +11 -0
- package/hooks/lang/tr.json +11 -0
- package/hooks/projects-stop.py +2 -1
- package/hooks/tezgah_agents.py +27 -3
- package/hooks/tezgah_attest.py +79 -10
- package/hooks/tezgah_context.py +21 -8
- package/hooks/tezgah_gate.py +79 -23
- package/hooks/tezgah_integrity.py +212 -38
- package/hooks/tezgah_paths.py +43 -2
- package/hooks/tezgah_policy.py +35 -13
- package/hooks/tezgah_replay.py +8 -2
- package/hooks/tezgah_research.py +167 -24
- package/hooks/tezgah_stack.py +236 -1
- package/hooks/tezgah_task.py +2 -1
- package/hosts/amp/hook.py +217 -0
- package/hosts/amp/tezgah-plugin.ts.in +386 -0
- package/hosts/antigravity/hook.py +255 -0
- package/hosts/codex/hook.py +2 -1
- package/hosts/commandcode/hook.py +239 -0
- package/hosts/copilot/hook.py +284 -0
- package/hosts/cursor/hook.py +2 -1
- package/hosts/droid/hook.py +259 -0
- package/hosts/dsh/hooks.json +1 -1
- package/hosts/gemini/hook.py +213 -0
- package/hosts/goose/hook.py +145 -0
- package/hosts/omp/hook.py +4 -1
- package/hosts/omp/tezgah-hook.ts.in +30 -7
- package/hosts/opencode/plugins/tezgah.js +2 -2
- package/hosts/qwen/hook.py +206 -0
- package/output-styles/tezgah.md +10 -5
- package/package.json +1 -1
- package/skills/harness/SKILL.md +2 -0
- package/skills/i-have-adhd/SKILL.md +10 -1
- package/skills/plan-add/SKILL.md +5 -1
- package/skills/research/SKILL.md +22 -9
- package/skills/tezgah-contract/SKILL.md +19 -6
- package/statusline.py +20 -9
- package/workflows/graph-review.js +14 -2
package/docs/research.md
CHANGED
|
@@ -69,13 +69,15 @@ sections below. Rules 5 add the scope lock. Rules 6 add the selection warnings
|
|
|
69
69
|
("Held-out set, repeats and noise band" below). Rules 7 add the `exploratory`
|
|
70
70
|
method ("Rules 7: exploratory lines" below). A bump never weakens an older set.
|
|
71
71
|
Rules 8 (`EVIDENCE_RULES`) add captured evidence and a computed certainty per
|
|
72
|
-
claim ("Rules 8: captured evidence and computed certainty" below).
|
|
73
|
-
|
|
72
|
+
claim ("Rules 8: captured evidence and computed certainty" below). Rules 9
|
|
73
|
+
(`OWNER_CHOICE_RULES`) split a decision's pick: the line writes `recommended:`,
|
|
74
|
+
and `chosen:` is the owner's ("Variants of the deliverable" below). `init` writes 9
|
|
75
|
+
now: `RULES` is `OWNER_CHOICE_RULES` (`hooks/tezgah_research.py::OWNER_CHOICE_RULES`).
|
|
74
76
|
|
|
75
|
-
A `rules` above `
|
|
77
|
+
A `rules` above `RULES` means a later tezgah wrote the line. `check` then reports
|
|
76
78
|
one error that names both numbers and reads nothing else. The writers refuse the
|
|
77
79
|
line too: `claim`, `predict`, `compare`, `source --run`, `verdict`, `conclude`,
|
|
78
|
-
`close` and `migrate` (`newer_rules`). Upgrade tezgah to work on such a line.
|
|
80
|
+
`close`, `choose` and `migrate` (`newer_rules`). Upgrade tezgah to work on such a line.
|
|
79
81
|
|
|
80
82
|
## Protocol sections (rules 4)
|
|
81
83
|
|
|
@@ -170,7 +172,7 @@ One comparison lives in `decisions/<id>/`, and one loader reads its files
|
|
|
170
172
|
| `criteria.json` | `criteria`: a list of `{id, kind, direction}`, with `kind` `measured` or `judged` and `direction` `max`, `min` or `pass`. `baseline_variant`: the id of the status-quo variant in `variants.jsonl` |
|
|
171
173
|
| `variants.jsonl` | one `{id, status}` row per variant. A `produced` variant names its `artifact`: a file in the line or the repository, `git:<rev>` or `orx:<id>`. A `dropped` one gives `drop_reason` |
|
|
172
174
|
| `comparison.jsonl` | one cell per variant and criterion, written by `compare` |
|
|
173
|
-
| `decision.md` | the
|
|
175
|
+
| `decision.md` | the line's pick (`recommended:`), the owner's (`chosen:` with `owner:`) and why the others lost |
|
|
174
176
|
|
|
175
177
|
Write and commit `criteria.json` and `variants.jsonl` before the first cell. Then
|
|
176
178
|
record each cell with `tezgah-research compare <slug> <decision>`, one JSON object
|
|
@@ -190,12 +192,27 @@ this same rule.
|
|
|
190
192
|
- A judged cell with fewer than two distinct raters warns.
|
|
191
193
|
- The criteria have to enter the history before the first cell, and
|
|
192
194
|
`comparison.jsonl` only grows.
|
|
193
|
-
- `decision.md` names a produced variant on a `chosen: <id>` line
|
|
195
|
+
- `decision.md` names a produced variant on a `chosen: <id>` line, else on a
|
|
196
|
+
`recommended: <id>` line (`hooks/tezgah_research.py::decision_pick`).
|
|
197
|
+
- On a rules-9 line the line writes its own pick as `recommended:`. A `chosen:`
|
|
198
|
+
line with no `owner:` line fails with `names no owner choice`. Choosing a
|
|
199
|
+
variant or option a research, analysis or review recommends is the owner's
|
|
200
|
+
call. `tezgah-research choose <slug> <decision> <variant> --ack
|
|
201
|
+
"<the owner's words>"` writes `chosen:` and `owner: <date> "<words>"` and one
|
|
202
|
+
`log.md` line (`hooks/tezgah_research.py::choose_line`). It works on a done
|
|
203
|
+
line too: the order seal hashes the experiments alone. It keeps the line's
|
|
204
|
+
own pick. A pick a rules-8 line wrote as `chosen:` becomes `recommended:`. A
|
|
205
|
+
rules-8 line's `chosen:` is read as before.
|
|
206
|
+
- `conclude` prints `report to the owner: <id> is recommended, not chosen` for
|
|
207
|
+
each decision still waiting (`hooks/tezgah_research.py::owner_waits`).
|
|
208
|
+
- The three rules below judge the line's own pick, never the owner's
|
|
209
|
+
(`hooks/tezgah_research.py::line_pick`). An owner may take a dominated
|
|
210
|
+
variant, and that is no error of the line.
|
|
194
211
|
- Each other produced variant shares a line of `decision.md` with a criterion id
|
|
195
212
|
that ruled it out. Else: `rejects <id> on no criterion`.
|
|
196
213
|
- `decision.md` has a `flip:` line, the result that would reverse the choice.
|
|
197
214
|
- When every criterion is `max` or `min` and scored, no produced variant may
|
|
198
|
-
dominate the
|
|
215
|
+
dominate the picked one. A dominating variant is at least as good on every
|
|
199
216
|
criterion and better on one (`hooks/tezgah_research.py::dominated_by`).
|
|
200
217
|
|
|
201
218
|
## One line, one directory
|
|
@@ -263,7 +280,15 @@ rule names the open lines with their reason counts on every research turn
|
|
|
263
280
|
"<reason>"`, which writes the reason into the new line's `log.md` - an explicit
|
|
264
281
|
decision on the record instead of a silent second line - and which is itself
|
|
265
282
|
refused while any open line has `check` errors (`broken_open_lines`): a hatch past
|
|
266
|
-
broken lines is how they stayed broken.
|
|
283
|
+
broken lines is how they stayed broken. A success criterion whose verdict is
|
|
284
|
+
still `pending` is a line in flight, not a broken one. That error alone does not
|
|
285
|
+
block the hatch, and any other error does. A line another session is running is
|
|
286
|
+
not this session's to fix or close. Both refusals say so
|
|
287
|
+
(`hooks/tezgah_research.py::OWNER_ASK`). The session stops and asks the owner,
|
|
288
|
+
never a way around a tezgah refusal (another folder, a hidden dir). A hidden dir
|
|
289
|
+
under `research/`, `research/open/` or `research/done/` holds no line `check` can
|
|
290
|
+
see. The no-slug `check` and `status` fail it by its path
|
|
291
|
+
(`hooks/tezgah_research.py::stray_dirs`). What is left of a line that will not be
|
|
267
292
|
finished is recorded with `tezgah-research close <slug> --limit "<reason>"`
|
|
268
293
|
(`close_line`), which concludes it and writes the reasons it was still open into
|
|
269
294
|
`state.json` `closed` and `log.md` and seals it ("the order seal", below). Such
|
|
@@ -292,7 +317,7 @@ through its seal.
|
|
|
292
317
|
| `tezgah-research init <slug> [--question "..."] [--class R1..R12] [--allow-open "<reason>"] [--supersedes <slug>]` | scaffolds the line and makes sure `.tezgah/` is ignored by the project and has its own git repository (`tezgah_paths.ensure_workspace`). It refuses while another line is still open (see below), names each open line and its reasons one per line, and `--allow-open "<reason>"` is the way past unless an open line has `check` errors: the reason lands in the new line's `log.md` as its first entry, and an empty reason is misuse. A `--question` another line already asks is refused unless `--supersedes <that line>` says the new line is its next version (`serial_twins`). `--class` writes `state.question_class` and prints `obligations: <step>, ...` (`obligations`), the tier's `budget <slug>: sources a/b searches c/d runs e/f` (`budget`) and `overlap: <line>/<claim> <status> <similarity>` for each done claim close to the question (`overlaps`); `--class R0` exits 1 (`R0 gets no line`) before any other argument is read. Its next-step message names the commit loop: `tezgah-research commit` for the protocol, then again for the results in a later commit (`cmd_init`, `bin/tezgah-research`) | 0, 1 refused, 2 misuse |
|
|
293
318
|
| `tezgah-research commit <slug> "<message>"` | stages and commits only that line's path in `.tezgah`'s private repository - the commit the order rule reads (`cmd_commit`, `bin/tezgah-research`). With nothing left to commit it prints `nothing to commit: <paths> is already committed in <workspace>` and exits 0 | 0 (also with nothing to commit), 1 not a work tree or git failed, 2 misuse |
|
|
294
319
|
| `tezgah-research check [<slug>] [--json] [--strict] [--orx] [--online] [--all-lines] [-- <slug>]` | the discipline checks below. With no slug it checks the open lines in full and a line under `done/` by its order seal alone (`done_seal`, `hooks/tezgah_research.py::done_seal`), naming it only when the seal fails or its `state.json` or seal cannot be read, then prints `research: <n> done line(s): <s> checked by their seal, <u> unsealed (not checked); ...`; `--all-lines` checks every line in full, and `check <slug>` always checks that line in full (ADR 015). `--json` prints the report (no-slug default: the open lines and any done line whose seal read fails), `--strict` turns the unverifiable class into a refusal, `--orx` adds the registry check that asks `orx project view` for this repository (`check_orx`, `hooks/tezgah_research.py::check_orx`) and, on a rules-8 line, the run census; `--online` re-fetches a rules-8 line's citation receipts; a slug after `--` is never read as a flag, which is how the MCP tool `tezgah_research_check` passes its validated `slug`. A rules-8 line (`_check_rules8`) also prints report lines that are never counted - `certainty`, `stats`, `effect`, `instrument`, `ppi`, `stopping`, `online`, `budget` - and `--json` adds its per-claim `certainty` | 0 clean, 1 a line failed a rule or names no line |
|
|
295
|
-
| `tezgah-research status` | one line per line: an open line `ok` or a problem count, a line under `done/` read by its seal like the no-slug `check` - `done, seal ok`, `done, <n> seal problem(s)` or `done, unsealed (not checked)` (`summary`, `hooks/tezgah_research.py::summary`, plan 058 part 4) | 0 |
|
|
320
|
+
| `tezgah-research status` | one line per line: an open line `ok` or a problem count, a line under `done/` read by its seal like the no-slug `check` - `done, seal ok`, `done, <n> seal problem(s)` or `done, unsealed (not checked)` (`summary`, `hooks/tezgah_research.py::summary`, plan 058 part 4); then one `FAIL` line per hidden dir under `research/` (`stray_dirs`) | 0 |
|
|
296
321
|
| `tezgah-research --all` | every checkout of this repository (the main checkout and each linked `git worktree`, `tezgah_paths.worktrees`), one header per checkout, then ` <slug>: <phase>` per line with the reasons it is still open; a checkout with none says `no research line`. Read-only: each checkout keeps its own `.tezgah`, locks and private repository, and nothing here writes to any of them (`across`, `hooks/tezgah_research.py::across`) | 0, 2 misuse |
|
|
297
322
|
| `tezgah-research claim <slug>` | reads one claim from stdin and either appends it under an exclusive lock or refuses it, printing one reason per problem | 0, 1 refused, 2 misuse |
|
|
298
323
|
| `tezgah-research predict <slug>` | reads one prediction row from stdin and either appends it under the same lock or refuses it with one reason per problem; a row whose `commit` git cannot place is appended with the warning printed, which is the fail-open `check` uses, and a row written now has to name at least one component the manifest defines (`append_prediction`, `hooks/tezgah_research.py::append_prediction`) | 0, 1 refused, 2 misuse |
|
|
@@ -302,7 +327,8 @@ through its seal.
|
|
|
302
327
|
| `tezgah-research compare <slug> <decision>` | reads one variants x criteria cell from stdin and appends it to `decisions/<decision>/comparison.jsonl` under the lock, or refuses it by the rule `check` applies (`append_comparison` and `comparison_problems`, `hooks/tezgah_research.py`); it notes when `criteria.json` is not committed yet | 0, 1 refused, 2 misuse |
|
|
303
328
|
| `tezgah-research close <slug> --limit "<reason>"` | concludes the line as a deliberate limit, writing the reasons it was still open into `state.json` `closed` and `log.md`, and seals it (`close_line`) | 0, 1 unreadable state, 2 misuse |
|
|
304
329
|
| `tezgah-research verdict <slug> <id> met\|not-met\|unanswerable --evidence "<ref>"` | records one success criterion's verdict and the evidence it rests on in the line's `state.json`, or refuses it with one `FAIL verdict:` line per problem (`verdict_problems`, `hooks/tezgah_research.py::verdict_problems`) | 0, 1 refused or unreadable state, 2 misuse |
|
|
305
|
-
| `tezgah-research conclude <slug>` | concludes a line once every success criterion carries a verdict, seals it and moves it to `done/` (`conclude_line`, `hooks/tezgah_research.py::conclude_line`) | 0, 1 refused, 2 misuse |
|
|
330
|
+
| `tezgah-research conclude <slug>` | concludes a line once every success criterion carries a verdict, seals it and moves it to `done/` (`conclude_line`, `hooks/tezgah_research.py::conclude_line`); then prints `report to the owner: <id> is recommended, not chosen; ...` for each decision with no owner choice (`owner_waits`) | 0, 1 refused, 2 misuse |
|
|
331
|
+
| `tezgah-research choose <slug> <decision> <variant> --ack "<the owner's words>"` | records the owner's pick: `chosen:` and `owner: <date> "<words>"` in the decision's `decision.md`, one `log.md` line; refuses empty `--ack`, a missing decision and a variant the decision did not produce, on an open or a done line (`choose_line`, `hooks/tezgah_research.py::choose_line`) | 0, 1 refused, 2 misuse |
|
|
306
332
|
| `tezgah-research migrate-layout` | moves flat `research/<slug>` directories into `open/` or `done/` by each line's state, and is idempotent (`migrate_layout`, `hooks/tezgah_research.py::migrate_layout`) | 0, 2 misuse |
|
|
307
333
|
| `tezgah-research seal <slug> [--history-lost] --ack "<owner decision>"` | seals a line under `done/` that was concluded before seals existed: plainly, as `conclude` would, when every experiment's order still checks; with `--history-lost`, with that verdict for every experiment whose order the lost history left undecidable. Refuses a line with a real order violation, a plain seal on any order finding, and a `history-lost` seal with nothing lost (`retro_seal`, `hooks/tezgah_research.py::retro_seal`) | 0, 1 refused, 2 misuse |
|
|
308
334
|
| `tezgah-research import <checkout> [<slug>] [--allow-open "<reason>"]` | brings a line - or every line this checkout lacks - from another checkout's `.tezgah` repository with its history; an open line arrives beside open ones only with `--allow-open` (`import_line`, `hooks/tezgah_research.py::import_line`) | 0, 1 refused, 2 misuse |
|
package/docs/status-line.md
CHANGED
|
@@ -311,6 +311,12 @@ Every other surface reads the store.
|
|
|
311
311
|
legend on hover, pins it on click (16-19,35,47-52,63-64).
|
|
312
312
|
- **Codex** — hosts/codex/hook.py puts the plain line in `systemMessage`
|
|
313
313
|
(182-184,195-198).
|
|
314
|
+
- **Amp** — the plugin draws the widest plain tier
|
|
315
|
+
(`hosts/amp/hook.py::status_line`) on Amp's experimental status item
|
|
316
|
+
(`createStatusItem`, TUI only, plain text). It draws only for the thread the
|
|
317
|
+
user is looking at. It redraws on `session.start`, `agent.end` and each
|
|
318
|
+
watched tool result, and shows `hook✗` while the hook fails. A build without
|
|
319
|
+
the API has no status surface (`hosts/amp/tezgah-plugin.ts.in`).
|
|
314
320
|
|
|
315
321
|
## Seeing and debugging the line
|
|
316
322
|
|
package/docs/testing.md
CHANGED
|
@@ -66,7 +66,9 @@ conclude from either.
|
|
|
66
66
|
`.github/workflows/ci.yml` runs on a push to `main` and on a pull request, so
|
|
67
67
|
a branch commit runs once, through its pull request, not twice. A tag push does
|
|
68
68
|
not start it. `release.yml` calls it (`ci.yml:13`) as its `ci` job, so npm and
|
|
69
|
-
brew publish only after CI passed on the release commit.
|
|
69
|
+
brew publish only after CI passed on the release commit. Its first job,
|
|
70
|
+
`on-main`, fails the release when the tag's commit is not on `origin/main`.
|
|
71
|
+
Then no job builds or uploads anything. A newer push to the
|
|
70
72
|
same ref cancels the run it replaces, except on a release (`concurrency`). Every
|
|
71
73
|
job has a `timeout-minutes`, so a hung test cannot hold a runner for six hours.
|
|
72
74
|
No matrix cancels its other legs when one fails (`fail-fast: false`).
|
package/hooks/hooks.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
{ "hooks": [ { "type": "command", "command": "TEZGAH_HOST=claude \"${TEZGAH_PYTHON:-python3}\" \"${CLAUDE_PLUGIN_ROOT}/hooks/projects-auto-init.py\"", "timeout": 10 } ] }
|
|
14
14
|
],
|
|
15
15
|
"PreToolUse": [
|
|
16
|
-
{ "matcher": "Agent|agent|Task|task|Grep|grep|Bash|bash|PowerShell|pwsh|Shell|shell|subagent|Edit|Write|MultiEdit|NotebookEdit|AskUserQuestion", "hooks": [ { "type": "command", "command": "\"${TEZGAH_PYTHON:-python3}\" \"${CLAUDE_PLUGIN_ROOT}/hooks/projects-pretooluse.py\"", "timeout": 5 } ] },
|
|
16
|
+
{ "matcher": "Agent|agent|Task|task|Grep|grep|Bash|bash|PowerShell|pwsh|Shell|shell|Monitor|monitor|subagent|Edit|Write|MultiEdit|NotebookEdit|AskUserQuestion", "hooks": [ { "type": "command", "command": "\"${TEZGAH_PYTHON:-python3}\" \"${CLAUDE_PLUGIN_ROOT}/hooks/projects-pretooluse.py\"", "timeout": 5 } ] },
|
|
17
17
|
{ "matcher": "^mcp__(?:.*[^A-Za-z0-9])?(?:append|cancel|clear|click|close|comment|commit|copy|create|delete|deploy|drag|edit|email|evaluate|exec|execute|fill|insert|install|kill|launch|merge|message|mkdir|move|navigate|notify|patch|pause|post|press|print|publish|push|put|release|remove|rename|replace|reply|resume|review|run|save|send|set|share|skip|start|stop|submit|swipe|tag|tap|terminate|type|uninstall|update|upload|write)(?:[^A-Za-z0-9].*)?$", "hooks": [ { "type": "command", "command": "\"${TEZGAH_PYTHON:-python3}\" \"${CLAUDE_PLUGIN_ROOT}/hooks/projects-pretooluse.py\"", "timeout": 5 } ] }
|
|
18
18
|
],
|
|
19
19
|
"PostToolUse": [
|
package/hooks/lang/en.json
CHANGED
|
@@ -224,6 +224,9 @@
|
|
|
224
224
|
"plan_build": [
|
|
225
225
|
"\\bplans?\\b[^.?!\\n]{0,40}\\b(?:is ready|ready|written|drafted)\\b|\\b(?:wrote|opened|drafted|added|created) (?:the |a |this )?plans?\\b|\\b(?:build|implement|execute|start on|start) (?:the |this |that )?plans?\\b"
|
|
226
226
|
],
|
|
227
|
+
"owner_choice": [
|
|
228
|
+
"\\bwhich (?:variant|option|approach|design)s?\\b|\\b(?:v\\d+|option [a-h1-9]|variant [a-z0-9]+),? or (?:v\\d+|option [a-h1-9]|variant [a-z0-9]+)\\b|\\b(?:another|other|the other) session\\w*|\\bsession'?s (?:line|plan|branch)\\b|\\bclos\\w*\\b[^?\\n]{0,40}\\bline\\b|--allow-open|--limit\\b|\\.staging\\b|\\bhidden (?:dir|directory|folder)\\b|\\b(?:another|a different) (?:folder|dir|directory)\\b|\\b(?:work ?)?around\\b[^?\\n]{0,40}\\brefusal\\b"
|
|
229
|
+
],
|
|
227
230
|
"spend": [
|
|
228
231
|
"spend\\w*|\\bpaid\\b|billing|\\bcost\\w*|\\bbudget\\w*|\\busd\\b|\\$\\s?\\d"
|
|
229
232
|
],
|
|
@@ -236,6 +239,12 @@
|
|
|
236
239
|
"cheap_answer": [
|
|
237
240
|
"yes|yep|yeah|ok(?:ay)?|sure|go(?: on| ahead)?|continue|proceed|do it|carry on|keep going|approved"
|
|
238
241
|
],
|
|
242
|
+
"hand_over": [
|
|
243
|
+
"\\bplease\\s+(?:run|execute)\\b|\\byour\\s+(?:step|turn|move)\\b(?=\\s*[:.;,!]|\\s*$)|\\bover to you\\b"
|
|
244
|
+
],
|
|
245
|
+
"ran_it": [
|
|
246
|
+
"\\bi (?:ran|tested|verified|checked|executed)\\b|\\bran the (?:tests?|suite|build|lint|checks?)\\b|\\bwe ran\\b"
|
|
247
|
+
],
|
|
239
248
|
"outward": {
|
|
240
249
|
"push": "\\bpush\\w*",
|
|
241
250
|
"release": "\\brelease\\w*|\\btag(?:s|ged|ging)?\\b|version bump|bump the version",
|
|
@@ -329,6 +338,8 @@
|
|
|
329
338
|
"slice_lead": "",
|
|
330
339
|
"ready_lead": "",
|
|
331
340
|
"ask_lead": "",
|
|
341
|
+
"handed_lead": "",
|
|
342
|
+
"plan_lead": "",
|
|
332
343
|
"approval_heading": "Approval questions",
|
|
333
344
|
"recommended": "Recommended",
|
|
334
345
|
"grant_examples": "\"push without asking\", \"I approve\""
|
package/hooks/lang/tr.json
CHANGED
|
@@ -268,6 +268,9 @@
|
|
|
268
268
|
"plan_build": [
|
|
269
269
|
"\\bplan\\w*[^.?!\\n]{0,40}(?:hazır|yazıldı|yazdım|oluşturdum|oluşturuldu|açtım|açıldı|eklendi|uygula\\w*|başla\\w*|kur\\w*)|(?:yazdığım|açtığım|hazırladığım|oluşturduğum) plan\\w*"
|
|
270
270
|
],
|
|
271
|
+
"owner_choice": [
|
|
272
|
+
"\\b(?:v\\d+|seçenek \\w+|varyant \\w+)(?:'\\w+)? m[ıiuü]\\b,? (?:v\\d+|(?:seçenek |varyant )?\\w+)(?:'\\w+)? m[ıiuü]\\b|\\bhangi (?:varyant|seçenek|opsiyon|yaklaşım|tasarım)\\w*|\\bhangisini seç\\w*|\\b(?:başka|diğer) (?:bir )?oturum\\w*|\\bhatt[ıi]\\w* kapat\\w*|--allow-open|--limit\\b|\\.staging\\b|\\bgizli (?:dizin|klasör)\\w*|\\b(?:başka|farklı) (?:bir )?(?:klasör|dizin)\\w*|\\bred\\w* (?:aş|etrafından)\\w*|\\bengel\\w* (?:aş|etrafından)\\w*"
|
|
273
|
+
],
|
|
271
274
|
"spend": [
|
|
272
275
|
"ücret\\w*|ödeme\\w*|harca\\w*|bütçe\\w*|maliyet\\w*|\\bpara\\b|dolar\\w*|\\$\\s?\\d"
|
|
273
276
|
],
|
|
@@ -280,6 +283,12 @@
|
|
|
280
283
|
"cheap_answer": [
|
|
281
284
|
"evet|tamam|olur|devam(?: et)?|yap|başla|ilerle|okey|onay(?:lıyorum)?"
|
|
282
285
|
],
|
|
286
|
+
"hand_over": [
|
|
287
|
+
"\\b(?:sen|siz)\\b[^.!?\\n]{0,32}?\\bçalıştır(?:ın|ınız)?\\b|\\bsenin adımın\\b|\\badım(?:ı)? sende\\b|\\bsıradaki adım(?:ı)? sen\\b"
|
|
288
|
+
],
|
|
289
|
+
"ran_it": [
|
|
290
|
+
"\\b(?:test(?:ler)?i|suite'?i|komutu|kontrolü|denetimi)?\\s*(?:çalıştırdım|koştum|çalıştırdık)\\b|\\btest ettim\\b|\\bdoğruladım\\b|\\bkontrol ettim\\b|\\bdenetledim\\b"
|
|
291
|
+
],
|
|
283
292
|
"outward": {
|
|
284
293
|
"push": "\\bpush\\w*",
|
|
285
294
|
"release": "versiyon\\w*|sürüm\\w*|\\brelease\\w*|etiket\\w*",
|
|
@@ -350,6 +359,8 @@
|
|
|
350
359
|
"slice_lead": "Durmuş bir dilim var. ",
|
|
351
360
|
"ready_lead": "Hazır adımlar bekliyor. ",
|
|
352
361
|
"ask_lead": "Bu soruyu sorma: kararı ver ya da önerili onay sorusuna çevir. ",
|
|
362
|
+
"handed_lead": "Bu adımı sen çalıştır, kullanıcıya bırakma. ",
|
|
363
|
+
"plan_lead": "Plan modunda hiçbir şey koşmadı. ",
|
|
353
364
|
"approval_heading": "Onay için sorular",
|
|
354
365
|
"recommended": "Öneri",
|
|
355
366
|
"grant_examples": "\"bana sormadan pushla\", \"onay veriyorum\""
|
package/hooks/projects-stop.py
CHANGED
|
@@ -64,7 +64,8 @@ def main():
|
|
|
64
64
|
# a core that cannot answer has refused nothing, so the turn ends: the rule
|
|
65
65
|
# never traps a session, and that holds for its own failure too
|
|
66
66
|
reason = safe(session_id, stop_reason, p.get("last_assistant_message"),
|
|
67
|
-
session_id, cwd=cwd, after_block=bool(p.get("stop_hook_active"))
|
|
67
|
+
session_id, cwd=cwd, after_block=bool(p.get("stop_hook_active")),
|
|
68
|
+
mode=p.get("permission_mode"))
|
|
68
69
|
if reason:
|
|
69
70
|
json.dump({"decision": "block", "reason": reason}, sys.stdout)
|
|
70
71
|
|
package/hooks/tezgah_agents.py
CHANGED
|
@@ -245,6 +245,15 @@ def _reviewer_body(host):
|
|
|
245
245
|
"for those files: account for every listed file as reviewed or skipped\n"
|
|
246
246
|
"with a reason, and still apply everything above - OCR output is input,\n"
|
|
247
247
|
"never a verdict.\n\n"
|
|
248
|
+
"Plan readiness. When the target is a plan file\n"
|
|
249
|
+
"(`.tezgah/plans/open/NNN-*.md`) rather than a diff, review whether it\n"
|
|
250
|
+
"can be built without a question. First restate each section's intent\n"
|
|
251
|
+
"in one sentence of your own; a section you cannot restate lacks\n"
|
|
252
|
+
"context, which is a major finding. Then check that every choice the\n"
|
|
253
|
+
"build needs is made in the plan - schema, API path, dependency,\n"
|
|
254
|
+
"edge-case handling, the files to change; a choice left to the\n"
|
|
255
|
+
"implementer is a major finding. A file the plan will create is not a\n"
|
|
256
|
+
"missing reference. Classify and report exactly as for a diff.\n\n"
|
|
248
257
|
"%s" % (_graph_howto(host), _blast_radius(host), EMPTY_IS_NOT_PROOF,
|
|
249
258
|
decision, decision, _may_use(host)))
|
|
250
259
|
|
|
@@ -265,14 +274,26 @@ def _worker_body(tier):
|
|
|
265
274
|
"report what changed and what each check printed, verbatim. Report\n"
|
|
266
275
|
"progress, but never redefine the work. Never widen or narrow the brief's\n"
|
|
267
276
|
"scope, budget or question. If the brief cannot be met as written, say\n"
|
|
268
|
-
"so and stop rather than answering a different question.\n%s\n"
|
|
277
|
+
"so and stop rather than answering a different question.\n%s\n%s"
|
|
269
278
|
"A brief that builds or changes UI: read the `design-contract` skill\n"
|
|
270
279
|
"(and `design-library` for critique) and derive/check with\n"
|
|
271
280
|
"`~/.config/tezgah/bin/tezgah-design` before editing.\n"
|
|
272
|
-
"Never spawn subagents.\n%s" % (tier, tier, scope,
|
|
281
|
+
"Never spawn subagents.\n%s" % (tier, tier, scope, OWNER_ESCALATE,
|
|
282
|
+
WORKER_OUTPUT))
|
|
273
283
|
return body
|
|
274
284
|
|
|
275
285
|
|
|
286
|
+
# A question only the owner can answer climbs past every tier to the user: a
|
|
287
|
+
# worker that settles it itself, frontier included, decided for the owner
|
|
288
|
+
# (plan 136: a line ran under a hidden dir and wrote `chosen:` with no owner
|
|
289
|
+
# choice). One text on every tier.
|
|
290
|
+
OWNER_ESCALATE = ("A question only the owner can answer - choosing a variant or option a\n"
|
|
291
|
+
"research, analysis or review recommends; a way around a tezgah\n"
|
|
292
|
+
"refusal; deciding about another session's work (its line, plan or\n"
|
|
293
|
+
"branch) - is never yours to decide, on any tier: stop and answer\n"
|
|
294
|
+
"`ESCALATE: owner` with the options and a recommended default.\n")
|
|
295
|
+
|
|
296
|
+
|
|
276
297
|
# The report a consumer reads: the orchestrator re-briefs a report missing one
|
|
277
298
|
# of these fields (_orch_body), so only a role with a reader gets them. One
|
|
278
299
|
# text on every host, since a worker body ignores its host.
|
|
@@ -282,7 +303,8 @@ WORKER_OUTPUT = ("End the report, after any other shape asked for, with `Output
|
|
|
282
303
|
"- commit: the sha\n"
|
|
283
304
|
"- checks: each command, its exit code and the last lines of its\n"
|
|
284
305
|
" output file, verbatim\n"
|
|
285
|
-
"- escalate: the `ESCALATE:` reason (
|
|
306
|
+
"- escalate: the `ESCALATE:` reason (on tezgah-frontier only\n"
|
|
307
|
+
" `ESCALATE: owner`, else `none`)")
|
|
286
308
|
|
|
287
309
|
|
|
288
310
|
# The specialists beside the tier workers: name -> (what it does, the skills it
|
|
@@ -483,6 +505,8 @@ def _orch_body(names):
|
|
|
483
505
|
"or an ESCALATE, gets one narrower re-brief of the same role naming the\n"
|
|
484
506
|
"missing field or the escalated judgement; only a second miss is\n"
|
|
485
507
|
"restarted on tezgah-frontier with the original brief, never continued.\n"
|
|
508
|
+
"An `ESCALATE: owner` is neither: its options and default go to the user\n"
|
|
509
|
+
"in the approval batch, and the orchestrator never answers it itself.\n"
|
|
486
510
|
"Never delegate a task a specialist is not\n"
|
|
487
511
|
"listed for, and never let a subagent spawn its own subagents. Verify each\n"
|
|
488
512
|
"returned claim against the code before acting." % (listed, say()["blocker_tokens"]))
|
package/hooks/tezgah_attest.py
CHANGED
|
@@ -11,8 +11,9 @@ One store, not two: the install record is the per-artifact sha file the contract
|
|
|
11
11
|
artifacts already use (`contract.sha256`, `bin/tezgah-setup::record_contract`),
|
|
12
12
|
one line per entry under a `hook:<host>:<entry>` key. An entry is one hook event's
|
|
13
13
|
tezgah-owned groups (Claude, dsh, Codex, Cursor - so a removed PreToolUse is named
|
|
14
|
-
while PostToolUse stays), opencode's plugin links, and nothing for omp
|
|
15
|
-
is compared to a fresh render, the way `rules_current`
|
|
14
|
+
while PostToolUse stays), opencode's plugin links, and nothing for omp and Amp:
|
|
15
|
+
their bridge or plugin is compared to a fresh render, the way `rules_current`
|
|
16
|
+
compares a rules file. The
|
|
16
17
|
code an entry runs is hashed only on a packaged install (a tree with no `.git`):
|
|
17
18
|
a checkout install points into a tree its owner edits on purpose. A hook file
|
|
18
19
|
any user can write (0666) is drift too; group write is left alone, because a
|
|
@@ -43,10 +44,22 @@ CODE_TARGETS = {
|
|
|
43
44
|
"cursor": (os.path.join("hosts", "cursor", "hook.py"),),
|
|
44
45
|
"opencode": (os.path.join("hosts", "opencode", "plugins", "tezgah.js"),),
|
|
45
46
|
"omp": (os.path.join("hosts", "omp", "hook.py"),),
|
|
47
|
+
"qwen": (os.path.join("hosts", "qwen", "hook.py"),),
|
|
48
|
+
"droid": (os.path.join("hosts", "droid", "hook.py"),),
|
|
49
|
+
"commandcode": (os.path.join("hosts", "commandcode", "hook.py"),),
|
|
50
|
+
"copilot": (os.path.join("hosts", "copilot", "hook.py"),),
|
|
51
|
+
"gemini": (os.path.join("hosts", "gemini", "hook.py"),),
|
|
52
|
+
"antigravity": (os.path.join("hosts", "antigravity", "hook.py"),),
|
|
53
|
+
"goose": (os.path.join("hosts", "goose", "hook.py"),),
|
|
54
|
+
"amp": (os.path.join("hosts", "amp", "hook.py"),
|
|
55
|
+
os.path.join("hosts", "amp", "tezgah-plugin.ts.in")),
|
|
46
56
|
}
|
|
47
57
|
OPENCODE_DIRS = ("plugins", "plugin")
|
|
48
58
|
OMP_BRIDGE = os.path.join(tp.HOST_DIRS["omp"], "agent", "hooks", "pre",
|
|
49
59
|
"tezgah-hook.ts")
|
|
60
|
+
# The first line of every Amp plugin tezgah renders: an uninstall removes a
|
|
61
|
+
# file at AMP_PLUGIN only when it equals a fresh render or starts with this.
|
|
62
|
+
AMP_HEADER = "// tezgah: Amp plugin rendered by tezgah-setup"
|
|
50
63
|
|
|
51
64
|
|
|
52
65
|
def sha(text):
|
|
@@ -82,10 +95,15 @@ def own_groups(data, own=False):
|
|
|
82
95
|
return out
|
|
83
96
|
|
|
84
97
|
|
|
85
|
-
def _manifest(path, own):
|
|
98
|
+
def _manifest(path, own, bare=False, key=None):
|
|
99
|
+
"""`bare`: the file is keyed by event at the top, as Droid's hooks.json is;
|
|
100
|
+
`key`: the events sit under that top-level name, as Antigravity's do."""
|
|
86
101
|
try:
|
|
87
102
|
with open(path, encoding="utf-8") as fh:
|
|
88
|
-
|
|
103
|
+
data = json.load(fh)
|
|
104
|
+
if key:
|
|
105
|
+
data, bare = (data.get(key) if isinstance(data, dict) else None) or {}, True
|
|
106
|
+
return own_groups({"hooks": data} if bare else data, own)
|
|
89
107
|
except (OSError, ValueError):
|
|
90
108
|
return None
|
|
91
109
|
|
|
@@ -97,11 +115,31 @@ def omp_bridge(root=ROOT):
|
|
|
97
115
|
with open(os.path.join(root, "hosts", "omp", "tezgah-hook.ts.in"),
|
|
98
116
|
encoding="utf-8") as fh:
|
|
99
117
|
return fh.read().replace(
|
|
100
|
-
"@HOOK@", os.path.join(root, "hosts", "omp", "hook.py"))
|
|
118
|
+
"@HOOK@", js_string(os.path.join(root, "hosts", "omp", "hook.py")))
|
|
101
119
|
except OSError:
|
|
102
120
|
return ""
|
|
103
121
|
|
|
104
122
|
|
|
123
|
+
def amp_plugin(root=ROOT):
|
|
124
|
+
"""The Amp plugin as an install writes it: the template with this tree's
|
|
125
|
+
hook path, or "" when the template is unreadable. The install, the report
|
|
126
|
+
row, the live probe and the attestation all compare against this."""
|
|
127
|
+
try:
|
|
128
|
+
with open(os.path.join(root, "hosts", "amp", "tezgah-plugin.ts.in"),
|
|
129
|
+
encoding="utf-8") as fh:
|
|
130
|
+
return fh.read().replace(
|
|
131
|
+
"@HOOK@", js_string(os.path.join(root, "hosts", "amp", "hook.py")))
|
|
132
|
+
except OSError:
|
|
133
|
+
return ""
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def js_string(path):
|
|
137
|
+
"""`path` as the inside of a JS double-quoted string: a Windows checkout's
|
|
138
|
+
`\\t`, `\\u` or `\\x` would otherwise become a control character or a
|
|
139
|
+
SyntaxError, and the bridge would not load (review of plan 140)."""
|
|
140
|
+
return json.dumps(path)[1:-1]
|
|
141
|
+
|
|
142
|
+
|
|
105
143
|
def registration(host, root=ROOT):
|
|
106
144
|
"""({entry: text}, [files]) for the tezgah-owned hook entries this host has
|
|
107
145
|
on disk; the dict is None when the host's file cannot be read."""
|
|
@@ -109,9 +147,30 @@ def registration(host, root=ROOT):
|
|
|
109
147
|
path = os.path.join(root, *(("hooks",) if host == "claude"
|
|
110
148
|
else ("hosts", "dsh")), "hooks.json")
|
|
111
149
|
return _manifest(path, own=True), [path]
|
|
112
|
-
if host in ("codex", "cursor"):
|
|
113
|
-
|
|
114
|
-
|
|
150
|
+
if host in ("codex", "cursor", "qwen", "droid", "commandcode"):
|
|
151
|
+
# Qwen's and Command Code's hooks live in a shared settings.json
|
|
152
|
+
path = os.path.join(tp.HOST_DIRS[host], "settings.json"
|
|
153
|
+
if host in ("qwen", "commandcode") else "hooks.json")
|
|
154
|
+
return _manifest(path, own=False, bare=host == "droid"), [path]
|
|
155
|
+
if host == "copilot":
|
|
156
|
+
# tezgah's own file in Copilot's user hooks dir: every entry is ours
|
|
157
|
+
path = os.path.join(tp.HOST_DIRS["copilot"], "hooks", "tezgah.json")
|
|
158
|
+
return _manifest(path, own=True), [path]
|
|
159
|
+
if host == "gemini":
|
|
160
|
+
# tezgah's own extension: every entry in its hooks.json is ours
|
|
161
|
+
path = os.path.join(tp.HOST_DIRS["gemini"], "extensions", "tezgah", "hooks",
|
|
162
|
+
"hooks.json")
|
|
163
|
+
return _manifest(path, own=True), [path]
|
|
164
|
+
if host == "antigravity":
|
|
165
|
+
# the shared ~/.gemini/config/hooks.json: every entry under its
|
|
166
|
+
# top-level `tezgah` name is ours
|
|
167
|
+
path = os.path.join(os.path.dirname(tp.HOST_DIRS["antigravity"]), "config",
|
|
168
|
+
"hooks.json")
|
|
169
|
+
return _manifest(path, own=True, key="tezgah"), [path]
|
|
170
|
+
if host == "goose":
|
|
171
|
+
# tezgah's own plugin: every entry in its hooks.json is ours
|
|
172
|
+
path = os.path.join(tp.GOOSE_PLUGIN, "hooks", "hooks.json")
|
|
173
|
+
return _manifest(path, own=True), [path]
|
|
115
174
|
if host == "opencode":
|
|
116
175
|
links = (os.path.join(tp.HOST_DIRS["opencode"], d, "tezgah.js")
|
|
117
176
|
for d in OPENCODE_DIRS)
|
|
@@ -124,6 +183,12 @@ def registration(host, root=ROOT):
|
|
|
124
183
|
return {"bridge": fh.read()}, [OMP_BRIDGE]
|
|
125
184
|
except OSError:
|
|
126
185
|
return None, [OMP_BRIDGE]
|
|
186
|
+
if host == "amp":
|
|
187
|
+
try:
|
|
188
|
+
with open(tp.AMP_PLUGIN, encoding="utf-8") as fh:
|
|
189
|
+
return {"plugin": fh.read()}, [tp.AMP_PLUGIN]
|
|
190
|
+
except OSError:
|
|
191
|
+
return None, [tp.AMP_PLUGIN]
|
|
127
192
|
return None, []
|
|
128
193
|
|
|
129
194
|
|
|
@@ -148,7 +213,7 @@ def record_texts(host, root=ROOT):
|
|
|
148
213
|
"""{key: text} an install records for one host (`bin/tezgah-setup::record_hooks`)."""
|
|
149
214
|
entries, _files = registration(host, root)
|
|
150
215
|
out = {}
|
|
151
|
-
if host
|
|
216
|
+
if host not in ("omp", "amp"):
|
|
152
217
|
out.update((KEY % (host, name), text)
|
|
153
218
|
for name, text in (entries or {}).items())
|
|
154
219
|
code = code_text(host, root) if packaged(root) else None
|
|
@@ -173,10 +238,14 @@ def drift(host, root=ROOT, shas=None):
|
|
|
173
238
|
if mode & 0o002:
|
|
174
239
|
found.append("%s mode %04o" % (path, mode))
|
|
175
240
|
if entries is None:
|
|
176
|
-
|
|
241
|
+
# a host `registration` does not know names no file: say so, never IndexError
|
|
242
|
+
found.append("%s unreadable or missing" % (files[0] if files else host))
|
|
177
243
|
elif host == "omp":
|
|
178
244
|
if entries["bridge"] != omp_bridge(root):
|
|
179
245
|
found.append("omp bridge %s differs from a fresh render" % OMP_BRIDGE)
|
|
246
|
+
elif host == "amp":
|
|
247
|
+
if entries["plugin"] != amp_plugin(root):
|
|
248
|
+
found.append("amp plugin %s differs from a fresh render" % tp.AMP_PLUGIN)
|
|
180
249
|
else:
|
|
181
250
|
names = set(recorded) - {"code"}
|
|
182
251
|
if not names:
|
package/hooks/tezgah_context.py
CHANGED
|
@@ -1354,11 +1354,17 @@ def session_of(payload):
|
|
|
1354
1354
|
# again on 2026-10-22 (plan 126, second pass): a research ask arms the Research
|
|
1355
1355
|
# and Product paragraphs (6239 B on Claude, S4Remeasure p08) and the skill hint
|
|
1356
1356
|
# that names the research skill (about 330 B with its path) was the block given
|
|
1357
|
-
# up, so the prompt budget moves to 7000 B.
|
|
1357
|
+
# up, so the prompt budget moves to 7000 B. Moved again on 2026-10-28 (plan
|
|
1358
|
+
# 136): the subagent pointer says an owner-only question goes back as
|
|
1359
|
+
# `ESCALATE: owner` (+330 B), the fixture's subagent_start reached 5958 B and
|
|
1360
|
+
# gave up `consult` (205 B), so subagent_start moves to 6100 B. Moved again on
|
|
1361
|
+
# 2026-10-29 (plan 142): the output-shape paragraph says what the host's plan
|
|
1362
|
+
# mode changes (+168 B), and the worktree fixture's session_start gave up its
|
|
1363
|
+
# worktrees line again, so the session budgets move to 16200 B. A
|
|
1358
1364
|
# budget in bytes, not tokens: this file has no tokenizer and a wrong estimate
|
|
1359
1365
|
# would be worse than a bound.
|
|
1360
|
-
CONTEXT_BUDGET = {"session_start":
|
|
1361
|
-
"subagent_start":
|
|
1366
|
+
CONTEXT_BUDGET = {"session_start": 16200, "post_compact": 16200,
|
|
1367
|
+
"subagent_start": 6100, "user_prompt": 7000}
|
|
1362
1368
|
DEFAULT_BUDGET = 13000
|
|
1363
1369
|
# The blocks in the order they are given up when the budget is exceeded, lowest
|
|
1364
1370
|
# value first: text another surface already carries (the plan table lives in the
|
|
@@ -1520,12 +1526,13 @@ GATE_MIN_CALLS = 3
|
|
|
1520
1526
|
# The tool names whose every call leaves a PreToolUse row (`began`,
|
|
1521
1527
|
# tezgah_gate.decision's write and shell branch) and every refused one a `deny`:
|
|
1522
1528
|
# Claude's write and shell tools, omp's `bash`/`edit`/`write` and Codex's
|
|
1523
|
-
# `exec_command`/`apply_patch`. A
|
|
1529
|
+
# `exec_command`/`apply_patch`, Droid's `Execute`/`Create`/`ApplyPatch`. A
|
|
1524
1530
|
# tool the gate may pass without a row (Read, Grep, Task, WebFetch, an MCP
|
|
1525
1531
|
# call) is not counted, so its absence from the ledger is never read as a
|
|
1526
1532
|
# disarmed gate.
|
|
1527
1533
|
GATED_TOOLS = re.compile(r"(?i)^(?:bash|powershell|pwsh|edit|write|multiedit|"
|
|
1528
|
-
r"notebookedit|exec_command|shell|apply_patch
|
|
1534
|
+
r"notebookedit|exec_command|shell|apply_patch|"
|
|
1535
|
+
r"execute|create|applypatch)$")
|
|
1529
1536
|
GATE_INACTIVE = (
|
|
1530
1537
|
"tezgah gate inactive on this host: %d gated tool call(s) since this "
|
|
1531
1538
|
"session's first turn and not one row from the gate, so the shortcut "
|
|
@@ -1651,7 +1658,8 @@ def context_nudge(payload, session_id):
|
|
|
1651
1658
|
# The ledger kinds a hook other than the tool hooks writes: the prompt hook's
|
|
1652
1659
|
# turn marker, judge row, lesson rows and disarm row, the Stop hook's claim,
|
|
1653
1660
|
# refusal, after_block and shape rows and the stop_spec rows of plan 063's shadow
|
|
1654
|
-
# (old ledgers only), the subagent-end hook's subagent_end row,
|
|
1661
|
+
# (old ledgers only), the subagent-end hook's subagent_end row, Droid's Stop
|
|
1662
|
+
# `reply_unread` row (a turn end whose reply the transcript did not yield),
|
|
1655
1663
|
# compaction, the subagent mark and the `subagent_start` lineage row (both from
|
|
1656
1664
|
# SubagentStart), the omp fork's `forked` row, the guard's crash
|
|
1657
1665
|
# row (any hook), the damage row any reader writes and the session start's
|
|
@@ -1664,7 +1672,7 @@ NOT_TOOL_HOOK = frozenset((b"turn", b"judge", b"claim", b"refusal",
|
|
|
1664
1672
|
b"after_block", b"subagent_end", b"stop_spec",
|
|
1665
1673
|
b"shape", b"compact", b"orch", b"crash", b"route",
|
|
1666
1674
|
b"spawned", b"forked", b"lesson", b"disarm", b"ledger_damage",
|
|
1667
|
-
b"attest", b"subagent_start"))
|
|
1675
|
+
b"attest", b"subagent_start", b"reply_unread"))
|
|
1668
1676
|
# The rows PostToolUse writes (`note_tool`): they prove the host ran the call,
|
|
1669
1677
|
# not that the gate saw it - a PostToolUse hook keeps writing them while a
|
|
1670
1678
|
# broken PreToolUse hook lets every call through - so they are no proof either.
|
|
@@ -2450,7 +2458,12 @@ def context_for(event, cwd, payload=None, with_core=True):
|
|
|
2450
2458
|
"(each tagged DERIVED, you concluded it, or RETRIEVED, "
|
|
2451
2459
|
"you read it), Evidence (the path:line or command behind "
|
|
2452
2460
|
"each), Confidence, Unresolved, Disconfirming, Conflicts. "
|
|
2453
|
-
"
|
|
2461
|
+
"A question only the owner can answer - choosing a variant "
|
|
2462
|
+
"or option a research, analysis or review recommends; a way "
|
|
2463
|
+
"around a tezgah refusal; deciding about another session's "
|
|
2464
|
+
"work (its line, plan or branch) - is never yours: return "
|
|
2465
|
+
"`ESCALATE: owner` with the options and a recommended "
|
|
2466
|
+
"default. Full rules: the `tezgah-contract` skill."))
|
|
2454
2467
|
else:
|
|
2455
2468
|
parts.append(("pointer", POINTER_LINE))
|
|
2456
2469
|
text = budgeted(event, [(key, render(text.strip())) for key, text in parts])
|