@r1z4x/tezgah 1.13.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 (93) hide show
  1. package/CHANGELOG.md +175 -1
  2. package/MANIFEST +18 -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 +15 -8
  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 +157 -41
  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 +12 -6
  27. package/bin/tezgah-rollback +3 -0
  28. package/bin/tezgah-route +1 -1
  29. package/bin/tezgah-setup +596 -157
  30. package/bin/tezgah-status +11 -1
  31. package/bin/tezgah-task +3 -0
  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 +3 -3
  38. package/docs/contract.md +77 -31
  39. package/docs/environment.md +58 -24
  40. package/docs/evidence.md +49 -19
  41. package/docs/gate.md +71 -21
  42. package/docs/hosts.md +65 -52
  43. package/docs/layers.md +1 -1
  44. package/docs/operations.md +67 -24
  45. package/docs/research.md +1 -1
  46. package/docs/skills.md +31 -8
  47. package/docs/status-line.md +33 -23
  48. package/docs/testing.md +48 -17
  49. package/docs/vision.md +6 -2
  50. package/hooks/lang/en.json +5 -2
  51. package/hooks/lang/tr.json +3 -2
  52. package/hooks/projects-posttooluse.py +11 -3
  53. package/hooks/projects-stop.py +1 -1
  54. package/hooks/tezgah_context.py +40 -23
  55. package/hooks/tezgah_gate.py +404 -106
  56. package/hooks/tezgah_integrity.py +367 -97
  57. package/hooks/tezgah_lessons.py +5 -2
  58. package/hooks/tezgah_paths.py +17 -6
  59. package/hooks/tezgah_policy.py +13 -5
  60. package/hooks/tezgah_skill_pick.py +1 -1
  61. package/hooks/tezgah_snapshot.py +6 -3
  62. package/hooks/tezgah_stopspec.py +2 -1
  63. package/hosts/amp/hook.py +1 -1
  64. package/hosts/amp/tezgah-plugin.ts.in +13 -3
  65. package/hosts/antigravity/hook.py +1 -1
  66. package/hosts/codex/hook.py +1 -1
  67. package/hosts/commandcode/hook.py +1 -1
  68. package/hosts/copilot/hook.py +1 -1
  69. package/hosts/cursor/hook.py +2 -1
  70. package/hosts/droid/hook.py +1 -1
  71. package/hosts/gemini/hook.py +1 -1
  72. package/hosts/goose/hook.py +1 -1
  73. package/hosts/omp/hook.py +1 -1
  74. package/hosts/omp/tezgah-hook.ts.in +127 -35
  75. package/hosts/opencode/plugins/tezgah.js +209 -63
  76. package/hosts/qwen/hook.py +1 -1
  77. package/output-styles/tezgah.md +1 -1
  78. package/package.json +6 -3
  79. package/packaging/build.sh +4 -4
  80. package/packaging/install.sh +11 -0
  81. package/packaging/upgrade.sh +3 -0
  82. package/skills/ai-research/LICENSE +21 -0
  83. package/skills/design-contract/SKILL.md +27 -6
  84. package/skills/design-library/LICENSE +21 -0
  85. package/skills/plan-add/render_table.py +7 -0
  86. package/skills/pm-frameworks/LICENSE +21 -0
  87. package/skills/rl-env/LICENSE +201 -0
  88. package/skills/tezgah-contract/SKILL.md +2 -2
  89. package/third_party/open-code-review/LICENSE +201 -0
  90. package/third_party/open-code-review/README.md +15 -0
  91. package/third_party/open-code-review/prompts/main_task_system.md +26 -0
  92. package/third_party/open-code-review/prompts/review_filter_task_system.md +13 -0
  93. package/workflows/graph-map.js +14 -5
package/bin/tezgah-status CHANGED
@@ -117,7 +117,8 @@ import sys
117
117
  HERE = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
118
118
  sys.path.insert(0, os.path.join(HERE, "hooks"))
119
119
  from tezgah_context import ( # noqa: E402
120
- LEGEND, color_default, health_segments, render_line, skill_fitness)
120
+ LEGEND, TOOL_USE_MEASURES, color_default, health_segments, render_line,
121
+ skill_fitness)
121
122
 
122
123
  # How many rows of the tool histogram the plain report prints. The count is on
123
124
  # the line and `--json` carries every row, so a reader can always see what was
@@ -432,6 +433,15 @@ def main():
432
433
  if "--help" in flags:
433
434
  sys.stdout.write(__doc__.strip() + "\n")
434
435
  return 0
436
+ # the two skill-read marks are measures too; a typo here blanked every mark
437
+ measures = TOOL_USE_MEASURES | {"pony", "adhd"}
438
+ named = {n for f in flags if f.startswith("--observable=")
439
+ for n in f.split("=", 1)[1].split(",") if n}
440
+ if named - measures:
441
+ sys.stderr.write("tezgah-status: unknown measure(s) in --observable: %s "
442
+ "(known: %s)\n" % (", ".join(sorted(named - measures)),
443
+ ", ".join(sorted(measures))))
444
+ return 2
435
445
  if "--legend" in flags:
436
446
  sys.stdout.write(LEGEND)
437
447
  return 0
package/bin/tezgah-task CHANGED
@@ -2048,6 +2048,9 @@ def main(argv):
2048
2048
  print(__doc__, file=sys.stderr)
2049
2049
  return 2
2050
2050
  cmd, rest = argv[1], argv[2:]
2051
+ if cmd in ("-h", "--help"):
2052
+ print(__doc__.strip())
2053
+ return 0
2051
2054
  if writes_a_record(cmd, rest):
2052
2055
  mark = owner_mark(repo_root(os.getcwd()))
2053
2056
  if mark:
package/bin/tezgah.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // bin/tezgah.js - npm's bin entry: finds a Python interpreter and execs
3
3
  // bin/tezgah-setup with every argument passed through. npm resolves this
4
4
  // symlink to the global prefix, so process.argv[1] is the shim, not the target.
5
- const { spawn } = require("child_process");
5
+ const { spawn, spawnSync } = require("child_process");
6
6
  const { existsSync } = require("fs");
7
7
  const os = require("os");
8
8
  const path = require("path");
@@ -18,9 +18,12 @@ const candidates = process.env.TEZGAH_PYTHON
18
18
  ? [process.env.TEZGAH_PYTHON]
19
19
  : ["python3", "python3.12", "python3.11", "python3.10", "python"];
20
20
 
21
+ // Probed exactly as it is spawned below - no shell - so a value the probe
22
+ // accepts is one spawn can start (`TEZGAH_PYTHON="python3 -S"` passed a shell
23
+ // probe, then died in spawn with a stack trace).
21
24
  const py = candidates.find(c => {
22
- try { require("child_process").execSync(c + " -c ''", { stdio: "ignore" }); return true; }
23
- catch (_) { return false; }
25
+ const probe = spawnSync(c, ["-c", ""], { stdio: "ignore" });
26
+ return !probe.error && probe.status === 0;
24
27
  });
25
28
 
26
29
  if (!py) {
@@ -29,6 +32,10 @@ if (!py) {
29
32
  }
30
33
 
31
34
  const child = spawn(py, [setup, ...process.argv.slice(2)], { stdio: "inherit" });
35
+ child.on("error", err => {
36
+ process.stderr.write("tezgah: cannot start " + py + ": " + err.message + "\n");
37
+ process.exit(1);
38
+ });
32
39
  // A child killed by a signal reports code null, and process.exit(null) is exit
33
40
  // 0: an installer killed mid-run read as a success (audit L-10, ENV-06). The
34
41
  // shell convention 128 + signal number carries the failure out instead.
package/commands/adhd.md CHANGED
@@ -12,7 +12,7 @@ adhd="$HOME/.config/tezgah/bin/tezgah-adhd"
12
12
 
13
13
  - No argument: run it bare, which prints `on` or `off`.
14
14
  - `off`: disarms the rule everywhere on this machine.
15
- - `on`: arms it again (`off` is the default).
15
+ - `on`: arms it again (`on` is the default).
16
16
  - Anything else: the CLI exits 2 with the usage on stderr - show that to the
17
17
  user and stop.
18
18
 
package/docs/README.md CHANGED
@@ -7,7 +7,8 @@ tool to someone deciding whether to install it; `AGENTS.md` says how to check a
7
7
  change; these pages explain how the thing actually works, so a session does not
8
8
  have to re-derive it by reading 55 Python files. Every page is written for one
9
9
  reader with one question in mind, and every non-obvious claim in it carries a
10
- citation you can open: `path::symbol` into Python code, `path:line` elsewhere.
10
+ citation you can open: `path::symbol` into Python, JavaScript and TypeScript
11
+ code, `path:line` elsewhere.
11
12
 
12
13
  ## Start here
13
14
 
@@ -55,26 +56,36 @@ citation you can open: `path::symbol` into Python code, `path:line` elsewhere.
55
56
  belong on a page: delete it rather than soften it.
56
57
  - Code in a Python file is cited by symbol: `path::name` for a top-level def,
57
58
  class or assignment, `path::Class.method` or `path::outer.inner` for a nested
58
- one (`hooks/tezgah_gate.py::decision`). It carries no line number, so an edit
59
+ one (`hooks/tezgah_gate.py::decision`). A page cites a JavaScript or
60
+ TypeScript file's top-level `const`, `let`, `function` or `class` the same way
61
+ (`hosts/opencode/plugins/tezgah.js::noteBegan`). It carries no line number, so an edit
59
62
  that moves code above or inside the symbol moves no citation. The sentence says
60
63
  which part of the symbol it means. Everything else stays `path:line`: a
61
- JavaScript, TypeScript, JSON, YAML or Markdown target, module-level code outside
62
- every def and class, and a range across several symbols. Never name a symbol
64
+ JSON, YAML, shell or Markdown target, code outside every top-level symbol, and a
65
+ range across several symbols. Never name a symbol
63
66
  that does not contain what the sentence cites.
64
67
  - `bin/tezgah-docs --citations` checks both forms. It reads every page under `docs/`
65
68
  and the comments and docstrings of the Python code. A string literal, such as a
66
69
  fixture or a captured output, is data and is not read. A symbol citation is
67
- resolved against the AST of the file it names. It fails when that file defines
70
+ resolved against the AST of the Python file it names, or the top-level
71
+ declarations of a JavaScript or TypeScript one. It fails when that file defines
68
72
  no such name. A name without a dot may also name the one method of that name
69
73
  in a class. That is how a test pin names its test. A def local to a function
70
74
  never stands in for it. A `path:line` with a symbol named
71
75
  before it, or right after it in parentheses, must point inside that symbol's
72
- body. A bare file name means the one code file of that name. The suite also
76
+ body. A bare file name means the one code file of that name, else the file of
77
+ that name at the repository root. A path under the private `.tezgah/` is never
78
+ judged, because a CI checkout has no copy of it. A comma list reads every range in it. A bare
79
+ range, `(8-9)`, `(14,34-38)` or an unquoted :8-9, is read against the path cited
80
+ before it in the same sentence or table row. One that starts at 0 or runs
81
+ backwards is a value, not lines. The audit flags a cited range past the end of
82
+ its file, and one over blank lines only, with or without a symbol beside it. In a shell script, a name beside the range must be on its
83
+ lines. The suite also
73
84
  checks that a cited file exists and that a cited line is inside it
74
85
  (`tests/test_docs.py`). Whether a line still *shows the thing the sentence names*
75
86
  is a judgement, not a regex. So a `path:line` with no symbol beside it is left
76
- unjudged rather than guessed at. So is a `path:N` the pattern cannot read
77
- (unquoted, or a comma list), but it is counted.
87
+ unjudged rather than guessed at. So is an unquoted `path:N` the pattern cannot
88
+ read, but it is counted.
78
89
  On this tree, 2026-10-06: 996 judged (995 of them symbol citations), 577 not
79
90
  judgeable, 85 of those unreadable.
80
91
  The unjudged count is a ratchet. `docs/citations-baseline.json` holds each file's
@@ -222,8 +222,8 @@ So tezgah never creates a `.tezgah`, `.cursor`, `.opencode` or graph index insid
222
222
  line armed (`hooks/tezgah_guard.py::safe`, `hooks/projects-pretooluse.py::main`).
223
223
  A host that cannot call the core in process gets the same rule in its own
224
224
  language: opencode's plugin runs every awaited core CLI through one `collect`
225
- helper with a 10 s deadline (`SPAWN_DEADLINE_MS`,
226
- `hosts/opencode/plugins/tezgah.js:108-178`). Both fail open, because a core
225
+ helper with a 10 s deadline (`hosts/opencode/plugins/tezgah.js::SPAWN_DEADLINE_MS`,
226
+ `hosts/opencode/plugins/tezgah.js::collect`). Both fail open, because a core
227
227
  that has crashed has refused nothing.
228
228
  - **The root boundary.** Every hook is inert outside a configured root:
229
229
  a linked `git worktree` counts as inside when its main checkout is, read from
@@ -6,43 +6,37 @@
6
6
  "unjudged": 1
7
7
  },
8
8
  "docs/architecture.md": {
9
- "unjudged": 25
9
+ "unjudged": 23
10
10
  },
11
11
  "docs/contract.md": {
12
- "unjudged": 5
12
+ "unjudged": 3
13
13
  },
14
14
  "docs/environment.md": {
15
- "unjudged": 8
15
+ "unjudged": 4
16
16
  },
17
17
  "docs/evidence.md": {
18
- "unjudged": 18
19
- },
20
- "docs/feature-audit.md": {
21
- "unjudged": 2
18
+ "unjudged": 14
22
19
  },
23
20
  "docs/gate.md": {
24
- "unjudged": 29
21
+ "unjudged": 10
25
22
  },
26
23
  "docs/glossary.md": {
27
- "unjudged": 12
24
+ "unjudged": 9
28
25
  },
29
26
  "docs/hosts.md": {
30
- "unjudged": 61
27
+ "unjudged": 15
31
28
  },
32
29
  "docs/layers.md": {
33
- "unjudged": 14
30
+ "unjudged": 7
34
31
  },
35
32
  "docs/operations.md": {
36
- "unjudged": 21
33
+ "unjudged": 4
37
34
  },
38
35
  "docs/skills.md": {
39
- "unjudged": 9
40
- },
41
- "docs/status-line.md": {
42
- "unjudged": 9
36
+ "unjudged": 6
43
37
  },
44
38
  "docs/testing.md": {
45
- "unjudged": 41
39
+ "unjudged": 21
46
40
  },
47
41
  "hooks/tezgah_gate.py": {
48
42
  "unjudged": 1
@@ -25,9 +25,9 @@
25
25
  },
26
26
  "docs/environment.md": {
27
27
  "long_sentences": 3,
28
- "passive": 2,
28
+ "passive": 1,
29
29
  "replace_words": 0,
30
- "semicolons": 3
30
+ "semicolons": 2
31
31
  },
32
32
  "docs/evidence.md": {
33
33
  "long_sentences": 112,
@@ -97,7 +97,7 @@
97
97
  },
98
98
  "docs/skills.md": {
99
99
  "long_sentences": 35,
100
- "passive": 18,
100
+ "passive": 17,
101
101
  "replace_words": 0,
102
102
  "semicolons": 16
103
103
  },
package/docs/contract.md CHANGED
@@ -88,7 +88,7 @@ to an adhd ask still opened on a diagnosis. Measured on this
88
88
  machine: 6-15 ms warm, and 1.36 s for the one cold rebuild over 7 502 sections.
89
89
  The line costs 187-201 bytes on omp, 264-287 on other hosts and 450-483 on
90
90
  Claude, plus 231 for the `i-have-adhd` clause. It is not a judgement, so
91
- `judge-off` leaves it on, and `reminder-off` drops it with the per-turn text.
91
+ `judge-off` leaves it on, and `reminder-off` leaves it on too: that switch drops only the reminder line.
92
92
  The always-on core also
93
93
  sends every session to the same search by hand. Run `tezgah-skill <words>`
94
94
  before working a topic a skill covers, then read only the returned range.
@@ -217,21 +217,22 @@ never lowers a severity (`hooks/tezgah_policy.py::EXEC`).
217
217
 
218
218
  Paragraphs are concatenated in the order they appear in `CORE` and identified by
219
219
  the bold label each starts with (`CORE_RULES`, `hooks/tezgah_context.py::CORE_RULES`).
220
- That order, with the line each label sits on in `hooks/tezgah_policy.py`:
221
- `**{REPLY_LANG}, BLUF.**` :802, `**Ponytail (minimal code).**` :811, `**Output shape:
222
- ADHD-friendly.**` :828, `**Deliver the whole ask; never the shortcut.**` :852,
223
- `**Integrity: evidence, or a named blocker.**` :869, `**Loop discipline.**` :915,
224
- `**Spec before building.**` :925 *(conditional)*, `**Lessons ledger: stop
225
- repeating mistakes.**` :939, `**Code discovery: graph first.**` :947
226
- *(conditional)*, `**Consult before irreversible.**` :956 *(conditional)*,
227
- `**Research: route it to OpenResearch.**` :974 *(conditional)*, `**Product
228
- analysis: five axes, one evidence class per finding.**` :1003 *(conditional)*,
229
- `**Design: UI work reads the design skills.**` :1039 *(conditional)*,
230
- `**No AI attribution, ever, on any host.**` :1048, `**Identifiers and messages
231
- stay English.**` :1058, `**Session scope: the user's repo, not tezgah.**` :1064,
232
- ``**Workspace: `.tezgah/` only.**`` :1075, `**Parallel by default: fan out
233
- independent items.**` :1082 *(conditional)*,
234
- `**Kill switches:**` :1112. The first label renders as `**Turkish, BLUF.**` under
220
+ That order, read from `hooks/tezgah_policy.py::CORE` (the labels are the
221
+ anchors, so an edit above one moves nothing here):
222
+ `**{REPLY_LANG}, BLUF.**`, `**Ponytail (minimal code).**`, `**Output shape:
223
+ ADHD-friendly.**`, `**Deliver the whole ask; never the shortcut.**`,
224
+ `**Integrity: evidence, or a named blocker.**`, `**Loop discipline.**`,
225
+ `**Spec before building.**` *(conditional)*, `**Lessons ledger: stop
226
+ repeating mistakes.**`, `**Code discovery: graph first.**`
227
+ *(conditional)*, `**Consult before irreversible.**` *(conditional)*,
228
+ `**Research: route it to OpenResearch.**` *(conditional)*, `**Product
229
+ analysis: five axes, one evidence class per finding.**` *(conditional)*,
230
+ `**Design: UI work reads the design skills.**` *(conditional)*,
231
+ `**No AI attribution, ever, on any host.**`, `**Identifiers and messages
232
+ stay English.**`, `**Session scope: the user's repo, not tezgah.**`,
233
+ ``**Workspace: `.tezgah/` only.**``, `**Parallel by default: fan out
234
+ independent items.**` *(conditional)*,
235
+ `**Kill switches:**`. The first label renders as `**Turkish, BLUF.**` under
235
236
  `language` `tr`.
236
237
 
237
238
  `always_on_core()` (`hooks/tezgah_context.py::always_on_core`) drops the seven
@@ -419,9 +420,12 @@ with no transcript pays nothing.
419
420
  ## How a rule is disarmed
420
421
 
421
422
  A kill switch removes the rule's text, not just a status mark
422
- (`hooks/tezgah_context.py::core_split`). `off()` checks `~/.config/tezgah` and the
423
- legacy `~/.claude` (`hooks/tezgah_paths.py::OFF_DIRS`, `hooks/tezgah_paths.py::off`); the drop itself
424
- happens in `core_split()`.
423
+ (`hooks/tezgah_context.py::core_split`). `off()` checks tezgah's config dir and
424
+ the legacy `~/.claude` (`hooks/tezgah_paths.py::OFF_DIRS`, `hooks/tezgah_paths.py::off`).
425
+ The config dir is `~/.config/tezgah`, or `$XDG_CONFIG_HOME/tezgah` when
426
+ `XDG_CONFIG_HOME` is set (`hooks/tezgah_paths.py::CONFIG_DIR`). The core and the
427
+ per-turn reminder name the same dir (`hooks/tezgah_policy.py::SWITCH_DIR`). The
428
+ drop itself happens in `core_split()`.
425
429
 
426
430
  The session latches a switch (`hooks/tezgah_paths.py::latch`). A hook that knows
427
431
  its session binds the session's ledger first
@@ -438,8 +442,8 @@ three cases:
438
442
  (`hooks/tezgah_integrity.py::_in_flight`). A host hands its prompt hook a
439
443
  prompt between calls. An unanswered call older than ten minutes no longer
440
444
  counts, since a host writes no answer for a call the user interrupted.
441
- - it is the stand-down `tezgah-setup --uninstall --full` writes into
442
- `pretooluse-off` (`hooks/tezgah_paths.py::STAND_DOWN`).
445
+ - it is the stand-down a full `tezgah-setup --uninstall` (one covering every
446
+ armed host) writes into `pretooluse-off` (`hooks/tezgah_paths.py::STAND_DOWN`).
443
447
  - it appeared within the first row's second, because the row's `ts` holds whole
444
448
  seconds.
445
449
 
@@ -448,6 +452,18 @@ a hook with no session id bind nothing and read the file alone, as before. The
448
452
  opencode plugin answers the same way (`off(name, sessionID)`). A prompt match is
449
453
  a keyword match, not intent: "don't touch `verify-off`" names the switch too.
450
454
 
455
+ A subagent session is no next session. When the payload names the session that
456
+ spawned it (`parent`), the hook binds the parent's ledger too
457
+ (`hooks/tezgah_integrity.py::bind_session`). The subagent then latches on the
458
+ earlier of its own and its parent's first row. A switch the parent made
459
+ mid-session does not count in a subagent it spawns afterwards. A switch the
460
+ user's prompt to the parent named counts there too. The subagent's own prompt
461
+ is its parent's brief and authorizes nothing. The omp and Amp bridges send
462
+ `parent` on a subagent's events. The opencode plugin keeps the parent opencode
463
+ names on the child's `session.created` (`hosts/opencode/plugins/tezgah.js::PARENTS`)
464
+ and hands it to the core it asks (`bin/tezgah-gate`). The parent always comes
465
+ from the host, never from a ledger row an agent could write.
466
+
451
467
  | Switch file | Rule it removes | Where the drop is implemented |
452
468
  |---|---|---|
453
469
  | `exec-mode.off` | `**{REPLY_LANG}, BLUF.**` | `hooks/tezgah_context.py::switches` |
@@ -457,27 +473,43 @@ a keyword match, not intent: "don't touch `verify-off`" names the switch too.
457
473
  | `design-off` | `**Design: UI work reads the design skills.**` | `hooks/tezgah_context.py::switches` |
458
474
  | `verify-off` | `**Integrity: evidence…**`, and the per-turn evidence-scope line with it | `hooks/tezgah_context.py::switches`, `hooks/tezgah_context.py::context_for` |
459
475
  | `consult-off` | `**Consult before irreversible.**` | `hooks/tezgah_context.py::switches` |
460
- | `research-off` | `**Research: route it to OpenResearch.**` | `hooks/tezgah_context.py::switches` |
476
+ | `research-off` | `**Research: route it to OpenResearch.**` and `**Product analysis: five axes, one evidence class per finding.**` | `hooks/tezgah_context.py::switches` |
461
477
  | `orchestrate-off` | `**Parallel by default: fan out independent items.**`, and the orchestration section of the on-demand skill | `hooks/tezgah_context.py::switches` drops the paragraph, `hooks/tezgah_context.py::context_for` injects "Orchestration is off" and drops the specialists line, and the skill-ignore note is `hooks/tezgah_context.py::context_for` |
462
- | `reminder-off` | the per-turn reminder | `hooks/tezgah_context.py::context_for` returns `None` |
478
+ | `reminder-off` | the per-turn reminder line alone; the armed paragraphs, the gate notice, the off-note and the other per-turn parts stay | `hooks/tezgah_context.py::context_for` leaves the `reminder` part out |
463
479
  | `judge-off` | the judgement seam: the snapshot triage, the docs page fallback and the skill hint | `hooks/tezgah_judge.py::available` — `available()` is asked before any call, so an armed switch makes no request at all |
464
480
  | `triage-off` | the snapshot triage alone (`bin/tezgah-triage`), leaving the docs fallback and the skill hint armed | `bin/tezgah-triage::off_reason` — `off_reason()` answers this switch before the seam's, so the analyze-app loop reads the tree instead of paying for a judgement |
465
481
  | `docs-judge-off` | the docs page fallback alone (`bin/tezgah-docs`), leaving the triage and the skill hint armed | `bin/tezgah-docs::off` — `off()` answers this switch first, so a query the index cannot place is ranked by shared words instead (`ranked`), and exits 1 only when no page shares one |
466
482
  | `lang-off` | `**Identifiers and messages stay English.**` | `hooks/tezgah_context.py::switches` drops the paragraph; the gate's own check reads the same switch (`hooks/tezgah_gate.py::decision`) |
483
+ | `workspace-off` | ``**Workspace: `.tezgah/` only.**``, and the gate's refusal of a root `plans/`, `research/` or `analysis/` write | `hooks/tezgah_context.py::switches` drops the paragraph, and the gate reads the same switch (`hooks/tezgah_gate.py::DENY_RULES`, [gate.md](gate.md#workspace-tezgah-state-outside-tezgah)) |
484
+ | `task-off` | the gate's task rule: the active plan's phase, its path allowlist, the plan record and a shell write | `hooks/tezgah_gate.py::decision` ([gate.md](gate.md#task-the-active-plans-phase-and-path-allowlist)) |
467
485
  | `pretooluse-off` | the gate's denials, not a rule | [gate.md](gate.md) |
468
486
  | `.no-ponytail` | `**Ponytail (minimal code).**` | `hooks/tezgah_context.py::switches` |
469
487
  | `.no-adhd` | `**Output shape: ADHD-friendly.**` | `hooks/tezgah_context.py::switches` |
470
488
  | `.no-graph` | `**Code discovery: graph first.**` | `hooks/tezgah_context.py::switches` |
471
489
  | `.no-lessons` | `**Lessons ledger: stop repeating mistakes.**` | `hooks/tezgah_context.py::switches`, and neither the session's lessons block (`hooks/tezgah_context.py::lessons`) nor the per-turn one (`relevant_lessons`) is injected |
490
+ | `.no-plan-gate` | the gate's plan rule in that repository: a turn's third product file on a branch no plan binds | `hooks/tezgah_gate.py::plan_reason` ([gate.md](gate.md#plan-a-turns-third-product-file-on-a-branch-no-plan-binds)) |
491
+ | `.no-taste` | the opt-in taste capture in that repository | `hooks/tezgah_taste.py::MARK` ([operations](operations.md#opt-in-taste-capture-learn-apply)) |
472
492
 
473
- The last four are per-repo [marks](glossary.md#per-repo-mark), read by
493
+ The last six are per-repo [marks](glossary.md#per-repo-mark), read by
474
494
  `repo_marks()`, walking up to the enclosing [root](glossary.md#root)
475
495
  (`hooks/tezgah_context.py::repo_marks`). Every switch that fired is named back to the
476
- session at start (`hooks/tezgah_context.py::context_for`) and per turn (`hooks/tezgah_context.py::context_for`), with the instruction to
496
+ session at start (`hooks/tezgah_context.py::context_for`), with the instruction to
477
497
  ignore the matching section in `tezgah-contract` — that skill is loaded
478
- separately and would otherwise re-arm the rule. `tezgah-adhd off` writes the same
498
+ separately and would otherwise re-arm the rule — and in Claude's static `tezgah`
499
+ output style, which no switch re-renders. The per-turn note names the switches
500
+ again and the output style alone. Both notes are the budget's `offnote` block.
501
+ A turn over its byte budget drops it like the other droppable blocks
502
+ ([the budget](#when-the-injected-text-grows-too-large)), so "per turn" holds
503
+ while the turn fits. `tezgah-adhd off` writes the same
479
504
  channel as the file (`bin/tezgah-adhd::SWITCH`); the ponytail *level* is not a switch.
480
505
 
506
+ `off()` and `armed()` also read five feature switches, not rule switches.
507
+ ADR 007 leaves them unclassified (`hooks/tezgah_paths.py::SWITCHES`): `agents-off`, `update-check-off`, `slices-off`, `taste-on`, `skill-suggest-on`.
508
+
509
+ One environment variable disarms a refusal too: `TEZGAH_REPO_RULE=0` skips a
510
+ repository's own `.tezgah/gate.py` (`hooks/tezgah_gate.py::REPO_RULE_VALVE`;
511
+ [gate.md](gate.md#a-repositorys-own-rule), [environment](environment.md)).
512
+
481
513
  One more file in a repository's own tree is not a rule switch either. A
482
514
  `tezgah.owner` at the repository's top says who owns the workspace, and its two
483
515
  settings turn seams off rather than dropping a paragraph. `tasks: off` stops the
@@ -545,7 +577,7 @@ brief carries the same caller-list floor (`hooks/tezgah_agents.py`), and
545
577
 
546
578
  ## When the injected text grows too large
547
579
 
548
- Each event has a byte budget: `session_start` and `post_compact` 16000,
580
+ Each event has a byte budget: `session_start` and `post_compact` 16200,
549
581
  `user_prompt` 7000, `subagent_start` 6100, anything else 13000
550
582
  (`hooks/tezgah_context.py::CONTEXT_BUDGET`). The rationale and the measured
551
583
  sizes sit beside it. In an empty fixture repo, `session_start` with the core
@@ -554,8 +586,9 @@ builds 9842 B before the ponytail and ADHD rules went inline and 10884 B after
554
586
  113 B. The healthy fixture with a lesson and a plan then built 12059 B. At the
555
587
  old 12000 B budget it lost its lessons and sibling checkouts to pay for core
556
588
  rules, so the budget moved to 13000 B. The residual rule (plan 080) added
557
- 1473 B to the core and 151 B to the reminder, so the always-on band is
558
- 11972 B (`tests/test_setup.py`). At 13000 B the healthy fixture gave up its
589
+ 1473 B to the core and 151 B to the reminder, so the always-on band reached
590
+ 11972 B. `tests/test_setup.py::VersionPrefixIsNotContract` pins today's band.
591
+ At 13000 B the healthy fixture gave up its
559
592
  lessons, plans and steer blocks. At 6000 B the busiest prompt turn gave up its
560
593
  active-task line. The session budgets moved to 14500 B, the prompt budget to 6500 B.
561
594
  The Ask policy (plan 086, S3) added 913 B to the core. At 14500 B the healthy
@@ -563,7 +596,10 @@ fixture then gave up a lessons, plans or knowledge block. The session budgets mo
563
596
  The subagent brief stays the same size.
564
597
  A research ask arms the Research and Product paragraphs: 6239 B on Claude
565
598
  (S4Remeasure p08). At 6500 B the skill hint naming the research skill was the
566
- block given up. The prompt budget moved to 7000 B (2026-10-22).
599
+ block given up. The prompt budget moved to 7000 B (2026-10-22). A longer
600
+ subagent pointer (plan 136) moved `subagent_start` to 6100 B. The output-shape
601
+ paragraph's plan-mode clause (plan 142, +168 B) put the worktree fixture's
602
+ `session_start` over 16000 B again. The session budgets moved to 16200 B.
567
603
  `SkillRulesInForceAtSessionStart` (`tests/test_context.py`) pins the rules
568
604
  themselves, not the size. A
569
605
  `user_prompt` that arms all seven conditional rules
@@ -578,7 +614,17 @@ table first, then the Orca line, the tooling-availability lines and the live gra
578
614
  glance, then the index line and the resume state (it outlives those because it
579
615
  is the only one that says what this session was doing, and it still yields to a
580
616
  rule), then the evidence-scope warning, the skill hint, the task phase, the
581
- delta, and the skill pointer last (`hooks/tezgah_context.py::DROP_ORDER`).
617
+ delta, and the skill pointer last (`hooks/tezgah_context.py::DROP_ORDER`). The
618
+ whole tuple, in this order:
619
+
620
+ ```
621
+ knowledge worktrees lessons lessons_turn taste plans subagents steer orca
622
+ consult research research_broken graph offnote orchestrate index resume
623
+ slices context scratch skill task delta pointer
624
+ ```
625
+
626
+ `offnote` is the note naming the kill switches that fired, so a turn over budget
627
+ can lose it before the index line.
582
628
  The skill hint moved from near the front to after `lessons_turn` and `index`.
583
629
  It names the skill this ask needs, and no other block can work that out.
584
630
  It was the first block given up in 32 logged live drops (plan 126). Any key absent from that tuple is