@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.
- package/CHANGELOG.md +175 -1
- package/MANIFEST +18 -0
- package/NOTICE +152 -0
- package/README.de.md +41 -20
- package/README.es.md +39 -19
- package/README.fr.md +41 -20
- package/README.md +15 -8
- package/README.tr.md +39 -21
- package/bin/codegen +9 -2
- package/bin/consult +6 -0
- package/bin/tezgah-adhd +3 -0
- package/bin/tezgah-agents +3 -0
- package/bin/tezgah-capture +3 -0
- package/bin/tezgah-design +329 -54
- package/bin/tezgah-docs +157 -41
- package/bin/tezgah-dsh +1 -1
- package/bin/tezgah-gate +7 -2
- package/bin/tezgah-import-ai-research +16 -4
- package/bin/tezgah-index +3 -0
- package/bin/tezgah-lessons +13 -4
- package/bin/tezgah-mcp +4 -0
- package/bin/tezgah-migrate +3 -0
- package/bin/tezgah-ocr-review +2 -1
- package/bin/tezgah-optimize +4 -1
- package/bin/tezgah-pony +3 -0
- package/bin/tezgah-research +12 -6
- package/bin/tezgah-rollback +3 -0
- package/bin/tezgah-route +1 -1
- package/bin/tezgah-setup +596 -157
- package/bin/tezgah-status +11 -1
- package/bin/tezgah-task +3 -0
- package/bin/tezgah.js +10 -3
- package/commands/adhd.md +1 -1
- package/docs/README.md +19 -8
- package/docs/architecture.md +2 -2
- package/docs/citations-baseline.json +11 -17
- package/docs/clarity-baseline.json +3 -3
- package/docs/contract.md +77 -31
- package/docs/environment.md +58 -24
- package/docs/evidence.md +49 -19
- package/docs/gate.md +71 -21
- package/docs/hosts.md +65 -52
- package/docs/layers.md +1 -1
- package/docs/operations.md +67 -24
- package/docs/research.md +1 -1
- package/docs/skills.md +31 -8
- package/docs/status-line.md +33 -23
- package/docs/testing.md +48 -17
- package/docs/vision.md +6 -2
- package/hooks/lang/en.json +5 -2
- package/hooks/lang/tr.json +3 -2
- package/hooks/projects-posttooluse.py +11 -3
- package/hooks/projects-stop.py +1 -1
- package/hooks/tezgah_context.py +40 -23
- package/hooks/tezgah_gate.py +404 -106
- package/hooks/tezgah_integrity.py +367 -97
- package/hooks/tezgah_lessons.py +5 -2
- package/hooks/tezgah_paths.py +17 -6
- package/hooks/tezgah_policy.py +13 -5
- package/hooks/tezgah_skill_pick.py +1 -1
- package/hooks/tezgah_snapshot.py +6 -3
- package/hooks/tezgah_stopspec.py +2 -1
- package/hosts/amp/hook.py +1 -1
- package/hosts/amp/tezgah-plugin.ts.in +13 -3
- package/hosts/antigravity/hook.py +1 -1
- package/hosts/codex/hook.py +1 -1
- package/hosts/commandcode/hook.py +1 -1
- package/hosts/copilot/hook.py +1 -1
- package/hosts/cursor/hook.py +2 -1
- package/hosts/droid/hook.py +1 -1
- package/hosts/gemini/hook.py +1 -1
- package/hosts/goose/hook.py +1 -1
- package/hosts/omp/hook.py +1 -1
- package/hosts/omp/tezgah-hook.ts.in +127 -35
- package/hosts/opencode/plugins/tezgah.js +209 -63
- package/hosts/qwen/hook.py +1 -1
- package/output-styles/tezgah.md +1 -1
- package/package.json +6 -3
- package/packaging/build.sh +4 -4
- package/packaging/install.sh +11 -0
- package/packaging/upgrade.sh +3 -0
- package/skills/ai-research/LICENSE +21 -0
- package/skills/design-contract/SKILL.md +27 -6
- package/skills/design-library/LICENSE +21 -0
- package/skills/plan-add/render_table.py +7 -0
- package/skills/pm-frameworks/LICENSE +21 -0
- package/skills/rl-env/LICENSE +201 -0
- package/skills/tezgah-contract/SKILL.md +2 -2
- package/third_party/open-code-review/LICENSE +201 -0
- package/third_party/open-code-review/README.md +15 -0
- package/third_party/open-code-review/prompts/main_task_system.md +26 -0
- package/third_party/open-code-review/prompts/review_filter_task_system.md +13 -0
- 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,
|
|
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
|
-
|
|
23
|
-
|
|
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 (`
|
|
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
|
|
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`).
|
|
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
|
-
|
|
62
|
-
|
|
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
|
|
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
|
|
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
|
|
77
|
-
|
|
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
|
package/docs/architecture.md
CHANGED
|
@@ -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
|
|
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":
|
|
9
|
+
"unjudged": 23
|
|
10
10
|
},
|
|
11
11
|
"docs/contract.md": {
|
|
12
|
-
"unjudged":
|
|
12
|
+
"unjudged": 3
|
|
13
13
|
},
|
|
14
14
|
"docs/environment.md": {
|
|
15
|
-
"unjudged":
|
|
15
|
+
"unjudged": 4
|
|
16
16
|
},
|
|
17
17
|
"docs/evidence.md": {
|
|
18
|
-
"unjudged":
|
|
19
|
-
},
|
|
20
|
-
"docs/feature-audit.md": {
|
|
21
|
-
"unjudged": 2
|
|
18
|
+
"unjudged": 14
|
|
22
19
|
},
|
|
23
20
|
"docs/gate.md": {
|
|
24
|
-
"unjudged":
|
|
21
|
+
"unjudged": 10
|
|
25
22
|
},
|
|
26
23
|
"docs/glossary.md": {
|
|
27
|
-
"unjudged":
|
|
24
|
+
"unjudged": 9
|
|
28
25
|
},
|
|
29
26
|
"docs/hosts.md": {
|
|
30
|
-
"unjudged":
|
|
27
|
+
"unjudged": 15
|
|
31
28
|
},
|
|
32
29
|
"docs/layers.md": {
|
|
33
|
-
"unjudged":
|
|
30
|
+
"unjudged": 7
|
|
34
31
|
},
|
|
35
32
|
"docs/operations.md": {
|
|
36
|
-
"unjudged":
|
|
33
|
+
"unjudged": 4
|
|
37
34
|
},
|
|
38
35
|
"docs/skills.md": {
|
|
39
|
-
"unjudged":
|
|
40
|
-
},
|
|
41
|
-
"docs/status-line.md": {
|
|
42
|
-
"unjudged": 9
|
|
36
|
+
"unjudged": 6
|
|
43
37
|
},
|
|
44
38
|
"docs/testing.md": {
|
|
45
|
-
"unjudged":
|
|
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":
|
|
28
|
+
"passive": 1,
|
|
29
29
|
"replace_words": 0,
|
|
30
|
-
"semicolons":
|
|
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":
|
|
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`
|
|
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,
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
`**
|
|
224
|
-
`**
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
`**
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
`**
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
|
423
|
-
legacy `~/.claude` (`hooks/tezgah_paths.py::OFF_DIRS`, `hooks/tezgah_paths.py::off`)
|
|
424
|
-
|
|
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
|
|
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`
|
|
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
|
|
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`)
|
|
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
|
|
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`
|
|
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
|
|
558
|
-
11972 B
|
|
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
|