@r1z4x/tezgah 1.0.0 → 1.2.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 +92 -1
- package/MANIFEST +10 -0
- package/bin/tezgah-agents +10 -1
- package/bin/tezgah-docs +1 -1
- package/bin/tezgah-doctor +11 -0
- package/bin/tezgah-research +31 -14
- package/bin/tezgah-setup +154 -29
- package/bin/tezgah-skill +59 -0
- package/bin/tezgah-skill.py +59 -0
- package/bin/tezgah-status +62 -1
- package/bin/tezgah-taste +504 -5
- package/bin/tezgah-triage +5 -3
- package/docs/README.md +1 -0
- package/docs/architecture.md +5 -2
- package/docs/citations-baseline.json +1 -1
- package/docs/clarity-baseline.json +9 -9
- package/docs/contract.md +68 -26
- package/docs/environment.md +6 -3
- package/docs/evidence.md +7 -0
- package/docs/gate.md +13 -7
- package/docs/glossary.md +1 -1
- package/docs/hosts.md +48 -1
- package/docs/index.json +34 -3
- package/docs/judge.md +75 -60
- package/docs/models.md +14 -8
- package/docs/operations.md +80 -25
- package/docs/orca.md +126 -0
- package/docs/research.md +38 -3
- package/docs/skills.md +74 -0
- package/docs/status-line.md +3 -1
- package/docs/testing.md +33 -21
- package/docs/vision.md +12 -8
- package/hooks/projects-posttooluse.py +7 -0
- package/hooks/projects-pretooluse.py +17 -5
- package/hooks/tezgah_agents.py +135 -13
- package/hooks/tezgah_components.py +3 -0
- package/hooks/tezgah_context.py +84 -22
- package/hooks/tezgah_gate.py +133 -17
- package/hooks/tezgah_integrity.py +60 -13
- package/hooks/tezgah_judge.py +288 -66
- package/hooks/tezgah_models.py +41 -5
- package/hooks/tezgah_orca.py +144 -0
- package/hooks/tezgah_paths.py +40 -6
- package/hooks/tezgah_policy.py +109 -35
- package/hooks/tezgah_research.py +104 -8
- package/hooks/tezgah_skill_pick.py +459 -8
- package/hooks/tezgah_stack.py +382 -0
- package/hooks/tezgah_stopspec.py +2 -2
- package/hooks/tezgah_taste.py +35 -9
- package/hooks/tezgah_taste_ledger.py +628 -0
- package/hosts/codex/hook.py +12 -1
- package/hosts/cursor/hook.py +12 -4
- package/hosts/omp/hook.py +18 -6
- package/hosts/omp/tezgah-hook.ts.in +20 -7
- package/hosts/opencode/plugins/tezgah.js +28 -15
- package/output-styles/tezgah.md +34 -19
- package/package.json +1 -1
- package/skills/i-have-adhd/SKILL.md +2 -2
- package/skills/research/SKILL.md +60 -17
- package/skills/tezgah-contract/SKILL.md +55 -13
package/hosts/cursor/hook.py
CHANGED
|
@@ -48,11 +48,12 @@ from tezgah_guard import attest_session, import_failed, safe # noqa: E402
|
|
|
48
48
|
try:
|
|
49
49
|
from tezgah_context import ( # noqa: E402
|
|
50
50
|
command_text, context_for, record, shell_kind, slug, under)
|
|
51
|
-
from tezgah_gate import decision, explored # noqa: E402
|
|
51
|
+
from tezgah_gate import decision, explored, graph_advice # noqa: E402
|
|
52
52
|
from tezgah_integrity import ( # noqa: E402
|
|
53
53
|
SUBAGENT_CHANNEL, bind_session, note, note_tool, ran_nothing, report_bytes,
|
|
54
54
|
stop_reason)
|
|
55
55
|
from tezgah_paths import cache_dir, off # noqa: E402
|
|
56
|
+
from tezgah_taste import write_note # noqa: E402
|
|
56
57
|
from tezgah_untrusted import marks # noqa: E402
|
|
57
58
|
except Exception as exc:
|
|
58
59
|
import_failed(exc)
|
|
@@ -318,9 +319,16 @@ def dispatch(payload):
|
|
|
318
319
|
if (kind in ("graph", "consult") and under(cwd) and not quiet
|
|
319
320
|
and first_time(session_id, "graph")):
|
|
320
321
|
reinforce = REINFORCE
|
|
321
|
-
#
|
|
322
|
-
#
|
|
323
|
-
|
|
322
|
+
# the gate's graph line for a code-symbol search (graph_advice) rides
|
|
323
|
+
# the same channel; all three are one line each for the model, on the
|
|
324
|
+
# result they belong to, read together rather than one replacing another
|
|
325
|
+
advice = (graph_advice(gate_name(payload.get("tool_name", "")), inp, cwd,
|
|
326
|
+
"cursor") if under(cwd) and not quiet else None)
|
|
327
|
+
# the opt-in taste note for the first write to this file type
|
|
328
|
+
# (hooks/tezgah_taste.write_note)
|
|
329
|
+
taste = (write_note(session_id, inp, cwd, gate_name(payload.get("tool_name", "")))
|
|
330
|
+
if under(cwd) and not call_failed(payload) else None)
|
|
331
|
+
text = "\n".join(t for t in (notice, reinforce, advice, taste) if t)
|
|
324
332
|
out = {"additional_context": text} if text else {}
|
|
325
333
|
elif event == "postToolUseFailure":
|
|
326
334
|
if kind:
|
package/hosts/omp/hook.py
CHANGED
|
@@ -19,7 +19,8 @@ the same code every other host runs.
|
|
|
19
19
|
{"event": "user_prompt", "cwd": ..., "prompt": ...}
|
|
20
20
|
-> {"context": <reminder + the rules this prompt arms>}
|
|
21
21
|
{"event": "pre_tool_use", "cwd": ..., "tool": ..., "input": {...}}
|
|
22
|
-
-> {"deny": reason}
|
|
22
|
+
-> {"deny": reason} | {"advice": line} (the graph line the bridge hands
|
|
23
|
+
the model with this call's result; never a block)
|
|
23
24
|
{"event": "post_tool_use", "cwd": ..., "tool": ..., "input": {...},
|
|
24
25
|
"failed": bool, "idx": glyph}
|
|
25
26
|
-> {"status": "pony✓ ...", "idx": glyph, "label": one line}
|
|
@@ -57,11 +58,12 @@ try:
|
|
|
57
58
|
from tezgah_context import ( # noqa: E402
|
|
58
59
|
color_default, command_text, context_for, health_segments, record,
|
|
59
60
|
render_tiers, shell_kind, skill_read_kind)
|
|
60
|
-
from tezgah_gate import decision # noqa: E402
|
|
61
|
+
from tezgah_gate import decision, graph_advice # noqa: E402
|
|
61
62
|
from tezgah_integrity import ( # noqa: E402
|
|
62
63
|
SUBAGENT_CHANNEL, bind_session, note, note_tool, stop_reason, untrusted_label,
|
|
63
64
|
untrusted_source)
|
|
64
65
|
from tezgah_paths import off, root_for # noqa: E402
|
|
66
|
+
from tezgah_taste import write_note # noqa: E402
|
|
65
67
|
from tezgah_untrusted import marks # noqa: E402
|
|
66
68
|
except Exception as exc:
|
|
67
69
|
import_failed(exc)
|
|
@@ -142,9 +144,12 @@ def handle(payload):
|
|
|
142
144
|
# answered at every turn boundary
|
|
143
145
|
return answered(*status_line(cwd, session_id))
|
|
144
146
|
if event == "pre_tool_use":
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
147
|
+
tool, inp = payload.get("tool", ""), payload.get("input") or {}
|
|
148
|
+
reason = decision(tool, inp, cwd, session_id)
|
|
149
|
+
if reason:
|
|
150
|
+
return {"deny": reason}
|
|
151
|
+
advice = graph_advice(tool, inp, cwd, "omp")
|
|
152
|
+
return {"advice": advice} if advice else {}
|
|
148
153
|
if not root_for(cwd):
|
|
149
154
|
return {}
|
|
150
155
|
if event == "session_start":
|
|
@@ -179,7 +184,9 @@ def handle(payload):
|
|
|
179
184
|
with_core=False)
|
|
180
185
|
return {"context": context} if context else {}
|
|
181
186
|
if event == "user_prompt":
|
|
182
|
-
|
|
187
|
+
# host="omp": the skill hint drops the absolute path, since omp's
|
|
188
|
+
# read tool resolves the skill:// address on its own
|
|
189
|
+
context = context_for("user_prompt", cwd, dict(payload, host="omp"))
|
|
183
190
|
return {"context": context} if context else {}
|
|
184
191
|
if event == "post_tool_use":
|
|
185
192
|
tool = payload.get("tool", "")
|
|
@@ -240,6 +247,11 @@ def handle(payload):
|
|
|
240
247
|
# deny: the bridge puts it in front of the content itself, so the model
|
|
241
248
|
# reads where the text came from - or, on an effect after such a read,
|
|
242
249
|
# the taint notice - while it reads the result.
|
|
250
|
+
# the opt-in taste note for the first write to this file type
|
|
251
|
+
# (hooks/tezgah_taste.write_note) rides the same label
|
|
252
|
+
taste = (safe(session_id, write_note, session_id, inp, cwd, tool)
|
|
253
|
+
if failed is not True else None)
|
|
254
|
+
label = "\n".join(t for t in (label, taste) if t)
|
|
243
255
|
if label:
|
|
244
256
|
out["label"] = label
|
|
245
257
|
return out
|
|
@@ -263,11 +263,17 @@ function skillRead(tool: string, args: any): boolean {
|
|
|
263
263
|
|
|
264
264
|
// The gate is only consulted for the tools the shared rule knows: the refusal
|
|
265
265
|
// of the grep-only explorer (task/agent), the anti-shortcut and attribution
|
|
266
|
-
// checks (bash),
|
|
266
|
+
// checks (bash), the test-skip check (the edit/write names), and the code-graph
|
|
267
|
+
// nudge on omp's search tools (grep, find, search, ast_grep). An MCP tool is
|
|
267
268
|
// consulted when its name carries an effect verb (MCP_EFFECT, see `tool_call`).
|
|
268
|
-
const GATED = ["grep", "
|
|
269
|
-
"
|
|
270
|
-
"search_replace", "powershell", "pwsh"];
|
|
269
|
+
const GATED = ["grep", "find", "search", "ast_grep", "bash", "shell", "command",
|
|
270
|
+
"task", "agent", "edit", "write", "multiedit", "notebookedit", "apply_patch",
|
|
271
|
+
"edit_file", "write_file", "search_replace", "powershell", "pwsh"];
|
|
272
|
+
|
|
273
|
+
// The gate's non-blocking graph line (tezgah_gate.graph_advice), held from the
|
|
274
|
+
// call to its result: `tool_call` can only block, `tool_result` can hand the
|
|
275
|
+
// model `additionalContext`, and carrying it across costs no second spawn.
|
|
276
|
+
const ADVICE = new Map<string, string>();
|
|
271
277
|
|
|
272
278
|
// The effect verbs of tezgah_integrity.MCP_VERBS, as one pattern: a read-only
|
|
273
279
|
// MCP call (a code-graph query) reaches no content rule, so it pays no spawn.
|
|
@@ -647,15 +653,22 @@ export default function tezgahOmp(pi: any): void {
|
|
|
647
653
|
return undefined; // a hook that cannot answer cannot refuse either
|
|
648
654
|
}
|
|
649
655
|
if (res.deny) return { block: true, reason: res.deny };
|
|
656
|
+
if (res.advice && event?.toolCallId != null) {
|
|
657
|
+
ADVICE.set(String(event.toolCallId), String(res.advice));
|
|
658
|
+
}
|
|
650
659
|
return undefined;
|
|
651
660
|
});
|
|
652
661
|
|
|
653
662
|
pi.on("tool_result", async (event: any, ctx: any) => {
|
|
654
663
|
const tool = String(event?.toolName ?? "");
|
|
655
664
|
const args = event?.input ?? {};
|
|
665
|
+
const id = event?.toolCallId != null ? String(event.toolCallId) : "";
|
|
666
|
+
const advice = ADVICE.get(id);
|
|
667
|
+
ADVICE.delete(id);
|
|
668
|
+
const graph = advice ? { additionalContext: advice } : undefined;
|
|
656
669
|
if (!WATCHED.includes(tool.toLowerCase()) && !tool.startsWith("mcp__")
|
|
657
670
|
&& !skillRead(tool, args)) {
|
|
658
|
-
return;
|
|
671
|
+
return graph;
|
|
659
672
|
}
|
|
660
673
|
// Tri-state: an unread outcome must never be written as a passed check,
|
|
661
674
|
// because the Stop rule below reads this ledger as evidence.
|
|
@@ -680,9 +693,9 @@ export default function tezgahOmp(pi: any): void {
|
|
|
680
693
|
// place; every other result is left exactly as the host produced it.
|
|
681
694
|
if (res.label) {
|
|
682
695
|
const content = labelled(event?.content, res.label);
|
|
683
|
-
if (content) return { content };
|
|
696
|
+
if (content) return { content, ...graph };
|
|
684
697
|
}
|
|
685
|
-
return
|
|
698
|
+
return graph;
|
|
686
699
|
});
|
|
687
700
|
|
|
688
701
|
pi.on("session_stop", async (event: any, ctx: any) => {
|
|
@@ -105,7 +105,8 @@ const SETUP_BIN = join(CONFIG, "bin", "tezgah-setup")
|
|
|
105
105
|
const CONTEXT_BIN = join(CONFIG, "bin", "tezgah-context")
|
|
106
106
|
const CAPTURE_BIN = join(CONFIG, "bin", "tezgah-capture")
|
|
107
107
|
const GATE_BIN = join(CONFIG, "bin", "tezgah-gate")
|
|
108
|
-
const
|
|
108
|
+
const SEARCH_SYMBOL =
|
|
109
|
+
/^(?:\\b)?(?:(def|class|function|func|fn|interface|struct|type)\s+)?([A-Za-z_][A-Za-z0-9_]{2,})(\\?\()?(?:\\b)?$/
|
|
109
110
|
// A hung core must not hold a tool call open. Every awaited core CLI runs
|
|
110
111
|
// through `collect`, which gives it a deadline and answers a timeout with the
|
|
111
112
|
// same fail-open value a missing binary or a non-zero exit gets: a core that
|
|
@@ -1265,9 +1266,10 @@ async function recordEvidence(sessionID, tool, args, result, workspace, cwd,
|
|
|
1265
1266
|
// hooks/tezgah_taste.note_write in process, and this host writes its edit row
|
|
1266
1267
|
// here, so the same call goes to that module through a process. The marker
|
|
1267
1268
|
// lives where the kill switches do (tezgah_paths.armed), so off it costs two
|
|
1268
|
-
// stats and spawns nothing.
|
|
1269
|
+
// stats and spawns nothing. The process prints the taste note for the first
|
|
1270
|
+
// write to this file type (tezgah_taste.write_note), returned to the caller.
|
|
1269
1271
|
if (kind === "edit" && row.exit !== 1 && off("taste-on")) {
|
|
1270
|
-
await noteTaste(sessionID, row.id, args, cwd)
|
|
1272
|
+
return await noteTaste(sessionID, row.id, args, cwd)
|
|
1271
1273
|
}
|
|
1272
1274
|
}
|
|
1273
1275
|
|
|
@@ -2111,10 +2113,12 @@ function ledgerStem(sessionID) {
|
|
|
2111
2113
|
}
|
|
2112
2114
|
|
|
2113
2115
|
// The nearest enclosing project that holds a codegraph index. The answer is the
|
|
2114
|
-
// indexed directory itself, which is what the nudge names
|
|
2116
|
+
// indexed directory itself, which is what the nudge names; a `.no-graph` met
|
|
2117
|
+
// first is the repo's own switch (hooks/tezgah_gate.index_slug), so no index.
|
|
2115
2118
|
function indexSlug(dir) {
|
|
2116
2119
|
let d = dir
|
|
2117
2120
|
for (;;) {
|
|
2121
|
+
if (existsSync(join(d, ".no-graph"))) return null
|
|
2118
2122
|
if (existsSync(join(d, GRAPH_DB))) return d
|
|
2119
2123
|
const parent = d.replace(/\/[^/]+\/?$/, "")
|
|
2120
2124
|
if (!parent || parent === d) return null
|
|
@@ -2122,17 +2126,23 @@ function indexSlug(dir) {
|
|
|
2122
2126
|
}
|
|
2123
2127
|
}
|
|
2124
2128
|
|
|
2129
|
+
// hooks/tezgah_gate.symbol_in: a code symbol (inner underscore, a lower-to-upper
|
|
2130
|
+
// step, or any identifier in a `def x` / `x(` pattern); a plain word such as
|
|
2131
|
+
// `TODO` is literal text and is never nudged.
|
|
2132
|
+
function symbolIn(text) {
|
|
2133
|
+
const m = String(text || "").trim().replace(/^['"]+|['"]+$/g, "").match(SEARCH_SYMBOL)
|
|
2134
|
+
if (!m) return null
|
|
2135
|
+
const tok = m[2]
|
|
2136
|
+
const shaped = tok.replace(/^_+|_+$/g, "").includes("_") || /[a-z][A-Z]/.test(tok)
|
|
2137
|
+
return m[1] || m[3] || shaped ? tok : null
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2125
2140
|
function identifierFrom(tool, args) {
|
|
2126
|
-
if (tool === "grep")
|
|
2127
|
-
const p = String(args.pattern || "")
|
|
2128
|
-
return IDENT.test(p) ? p : null
|
|
2129
|
-
}
|
|
2141
|
+
if (tool === "grep") return symbolIn(args.pattern)
|
|
2130
2142
|
if (tool === "bash" || tool === "shell") {
|
|
2131
2143
|
const cmd = String(args.command || "")
|
|
2132
2144
|
const m = cmd.match(/(?:^|[|;&(]\s*|\s)(?:grep|rg)\s+((?:-\S+\s+)*)(\S+)/)
|
|
2133
|
-
|
|
2134
|
-
const tok = m[2].replace(/^['"]|['"]$/g, "")
|
|
2135
|
-
return IDENT.test(tok) ? tok : null
|
|
2145
|
+
return m ? symbolIn(m[2]) : null
|
|
2136
2146
|
}
|
|
2137
2147
|
return null
|
|
2138
2148
|
}
|
|
@@ -2365,7 +2375,8 @@ function captureSnapshot(tool, args, dir, sessionID) {
|
|
|
2365
2375
|
|
|
2366
2376
|
// hooks/tezgah_taste.py's own entry point, found beside the bin the snapshot
|
|
2367
2377
|
// capture runs (the installed bins are links into the checkout, which holds no
|
|
2368
|
-
// copy of hooks/ under CONFIG). Best effort
|
|
2378
|
+
// copy of hooks/ under CONFIG). Best effort, like captureSnapshot: a failure is
|
|
2379
|
+
// ignored. It resolves to the taste note the process printed, else undefined.
|
|
2369
2380
|
function noteTaste(sessionID, id, args, cwd) {
|
|
2370
2381
|
let script
|
|
2371
2382
|
try {
|
|
@@ -2374,7 +2385,8 @@ function noteTaste(sessionID, id, args, cwd) {
|
|
|
2374
2385
|
} catch { return undefined }
|
|
2375
2386
|
return collect([script, JSON.stringify({
|
|
2376
2387
|
session_id: sessionID, id, input: args, cwd, host: "opencode" })],
|
|
2377
|
-
{ stdio: "ignore" }, null,
|
|
2388
|
+
{ stdio: ["ignore", "pipe", "ignore"] }, null,
|
|
2389
|
+
(code, out) => (code === 0 && out.trim() ? out.trim() : undefined))
|
|
2378
2390
|
}
|
|
2379
2391
|
|
|
2380
2392
|
export const Tezgah = async ({ directory }) => {
|
|
@@ -2727,8 +2739,9 @@ export const Tezgah = async ({ directory }) => {
|
|
|
2727
2739
|
if (notice) labelResult(output, notice)
|
|
2728
2740
|
const kind = await classify(tool, args)
|
|
2729
2741
|
if (kind) await record(sessionID, kind)
|
|
2730
|
-
await recordEvidence(sessionID, tool, args, output, workspace,
|
|
2731
|
-
|
|
2742
|
+
const taste = await recordEvidence(sessionID, tool, args, output, workspace,
|
|
2743
|
+
dir, own || inherited)
|
|
2744
|
+
if (taste) labelResult(output, taste)
|
|
2732
2745
|
} catch {}
|
|
2733
2746
|
},
|
|
2734
2747
|
|
package/output-styles/tezgah.md
CHANGED
|
@@ -25,27 +25,40 @@ it - no apology theater, no self-justifying phrasing.
|
|
|
25
25
|
**Ponytail (minimal code).** Laziest solution that works: YAGNI -> reuse an
|
|
26
26
|
existing helper -> stdlib -> native platform feature -> installed dependency ->
|
|
27
27
|
one line -> minimum code. No unrequested abstractions, no scaffolding "for
|
|
28
|
-
later", shortest working diff. Trace the
|
|
29
|
-
simplify away validation, error handling,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
later", deletion over addition, fewest files, shortest working diff. Trace the
|
|
29
|
+
problem fully before climbing; never simplify away validation, error handling,
|
|
30
|
+
security, accessibility or anything requested, and build the full version
|
|
31
|
+
without re-arguing when the user insists. Bug fix = root cause where all
|
|
32
|
+
callers route through. A deliberate corner cut gets a `ponytail:` comment
|
|
33
|
+
naming the ceiling. Non-trivial logic leaves ONE runnable check (an assert
|
|
34
|
+
self-check or one small test); a one-liner needs none. A complex ask ships the
|
|
35
|
+
lazy version and names the fuller one in the same reply instead of stalling.
|
|
36
|
+
Reply: code first, then at most three lines - what was skipped, when to add it.
|
|
37
|
+
In force from the first turn; levels lite|full|ultra are set with `tezgah-pony`
|
|
38
|
+
and ride the per-turn reminder when not `full`; read the full `ponytail` skill
|
|
39
|
+
from the router for the level table and examples. Off: "stop ponytail".
|
|
35
40
|
|
|
36
41
|
**Output shape: ADHD-friendly.** The answer or the next action is on the first
|
|
37
|
-
line
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
line - a command, path, snippet or the decision itself, never context or the
|
|
43
|
+
question restated - prose after it. Multi-step work is a numbered list, one
|
|
44
|
+
bounded action per step, and while it is in flight its position is restated in
|
|
45
|
+
one line - the todo list is that source, never re-narrate the plan. End with one
|
|
46
|
+
concrete next step. Finish the issue in hand before raising a second one; an
|
|
47
|
+
error states location, cause and fix with no drama, naming a cause only when the
|
|
48
|
+
evidence identifies it (else what is known and what would confirm it); after a
|
|
49
|
+
change say what now works. A list shows at most five items, ranked, the rest
|
|
50
|
+
kept in reserve rather than dropped - presentation only, never the analysis. An
|
|
43
51
|
estimate is in concrete units and marked as an estimate, never presented as a
|
|
44
52
|
measurement. No preamble, no recap, no closer, and a question the reader raises
|
|
45
|
-
mid-work is answered rather than deferred as the second issue.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
the
|
|
53
|
+
mid-work is answered rather than deferred as the second issue. Break the shape
|
|
54
|
+
only where it would delete the answer: an explanation runs as long as it needs,
|
|
55
|
+
with headers; an options question gets two to four ranked options,
|
|
56
|
+
recommendation first; after three "still broken" turns, name the suspect
|
|
57
|
+
assumption and ask one diagnostic question. Before sending, delete an opening
|
|
58
|
+
sentence that announces, a closing one that recaps or asks "anything else?", any
|
|
59
|
+
by-the-way sidebar and any hedge that carries no real uncertainty. In force from
|
|
60
|
+
the first answer; read the full `i-have-adhd` skill from the router for the
|
|
61
|
+
examples. Off: `tezgah-adhd off`, or the repo's `.no-adhd`.
|
|
49
62
|
|
|
50
63
|
**Deliver the whole ask; never the shortcut.** The request defines the
|
|
51
64
|
deliverable: every named item is in scope until the user says otherwise, and the
|
|
@@ -118,7 +131,9 @@ Never install, upgrade, restart or kill anything for tezgah, and never open an
|
|
|
118
131
|
issue for one of its tools mid-session. Name a missing capability in one line,
|
|
119
132
|
use the documented fallback (grep/find, or the second opinion skipped), and carry
|
|
120
133
|
on with the task in hand. Tezgah maintenance is in scope when the user asks for
|
|
121
|
-
it, or when the repo IS the tezgah checkout.
|
|
134
|
+
it, or when the repo IS the tezgah checkout. Before working a topic an
|
|
135
|
+
installed skill covers, run `tezgah-skill <words>` and read only the range it
|
|
136
|
+
returns.
|
|
122
137
|
|
|
123
138
|
**Workspace: `.tezgah/` only.** Every per-project tezgah artifact - plans,
|
|
124
139
|
research lines, analysis, lessons - lives under `<repo>/.tezgah/`, which the
|
|
@@ -139,4 +154,4 @@ rule), `workspace-off` (the root plans/research/analysis refusal),
|
|
|
139
154
|
per-repo `.no-ponytail`, `.no-adhd`, `.no-graph`, `.no-lessons`.
|
|
140
155
|
The ponytail intensity level is not a switch: `tezgah-pony lite|full|ultra`.
|
|
141
156
|
|
|
142
|
-
**On-demand rules (armed when the task class matches; full text in the `tezgah-contract` skill).** Spec-first for an underspecified or quality-only ask. A second opinion before a call that is hard to reverse or that one model would answer with unearned confidence. OpenResearch routing for research. Product analysis is a five-axis evidence task - value, usability (the running app, not the source), feasibility (a cited `path:line`), competition, and keep/fix/cut/bet triage - with one named evidence class per finding. The code graph for "who calls X" and "what breaks if Z changes". The design floor a UI turn is judged against is `.tezgah/design-contract.md`: `tezgah-design derive` writes it from the repository's own tokens, `tezgah-design check` measures a change against it, and the `design-contract` skill owns its shape.
|
|
157
|
+
**On-demand rules (armed when the task class matches; full text in the `tezgah-contract` skill).** Spec-first for an underspecified or quality-only ask. A second opinion before a call that is hard to reverse or that one model would answer with unearned confidence. OpenResearch routing for research. Product analysis is a five-axis evidence task - value, usability (the running app, not the source), feasibility (a cited `path:line`), competition, and keep/fix/cut/bet triage - with one named evidence class per finding. The code graph for "who calls X" and "what breaks if Z changes". Two or more independent items fan out to parallel subagents in one message, one git worktree per writing slice. The design floor a UI turn is judged against is `.tezgah/design-contract.md`: `tezgah-design derive` writes it from the repository's own tokens, `tezgah-design check` measures a change against it, and the `design-contract` skill owns its shape.
|
package/package.json
CHANGED
|
@@ -34,8 +34,8 @@ whether they still apply, they do.
|
|
|
34
34
|
|
|
35
35
|
Off: the `adhd-off` kill switch (`~/.config/tezgah/adhd-off`), a repo's
|
|
36
36
|
`.no-adhd` mark, or the user saying "stop adhd mode" / "normal mode". The
|
|
37
|
-
always-on text carries
|
|
38
|
-
|
|
37
|
+
always-on text carries every rule below in compressed form from the first turn
|
|
38
|
+
of a session; this file adds the reasoning and the worked examples.
|
|
39
39
|
|
|
40
40
|
## What changes about reading
|
|
41
41
|
|
package/skills/research/SKILL.md
CHANGED
|
@@ -22,11 +22,44 @@ evidence survives the session that produced it: the state on disk, the protocol
|
|
|
22
22
|
committed before the run, the claims with their falsification criteria, and the
|
|
23
23
|
review that decided what the evidence actually supports.
|
|
24
24
|
|
|
25
|
+
## Scope and method: answer the question asked
|
|
26
|
+
|
|
27
|
+
Three ways a line goes wrong before any rule below can catch it: it turns a
|
|
28
|
+
question into metrics, it grows past what was asked, and it wanders into a topic
|
|
29
|
+
nobody asked about. So before anything else:
|
|
30
|
+
|
|
31
|
+
1. **Restate the question verbatim** (`--ask "<the user's words>"`) and write
|
|
32
|
+
what is in scope and what is out (`--in "<covered>"` per item, `--out "<left
|
|
33
|
+
alone>"` per item; state.json `scope`). The in-scope items are `S1`, `S2`, ...
|
|
34
|
+
in order. Nothing is added to the list the user did not ask for.
|
|
35
|
+
2. **Pick the method from the question.** A qualitative, conceptual or reference
|
|
36
|
+
question - "what does X say", "how does Y work", "which sources support Z" - is
|
|
37
|
+
`--method qualitative`: a source review, no metric, no experiment, no orx
|
|
38
|
+
experiment tree. A question whose answer is a number or a comparison of
|
|
39
|
+
numbers is `--method measured`, and only then does the evaluation lock, the
|
|
40
|
+
protocol and the experiment tree below apply. `check` refuses a qualitative
|
|
41
|
+
line that records results, and a rules-5 line past `bootstrap` with no scope or
|
|
42
|
+
method.
|
|
43
|
+
3. **A plain factual or reference question gets no line** (`--tier quick` is
|
|
44
|
+
refused): answer it in the reply from the primary source, cited, with its
|
|
45
|
+
confidence.
|
|
46
|
+
4. **Every finding traces to an in-scope item.** A claim's `trace` names its
|
|
47
|
+
`S<n>` (or the item's text); `claim` and `check` refuse one that names none. A
|
|
48
|
+
finding that answers nothing asked is dropped; a tangent worth having is one
|
|
49
|
+
line under `## Open questions` for the user to pick, never a section of the
|
|
50
|
+
report.
|
|
51
|
+
5. **Depth is the primary source, not breadth.** Read the spec, the paper, the
|
|
52
|
+
code or the vendor's own documentation itself, cite it in the finding, and
|
|
53
|
+
state a confidence (high, medium or low, and the reason). A summary of other
|
|
54
|
+
summaries, or a finding with no source, is not an answer.
|
|
55
|
+
|
|
25
56
|
## The engine: OpenResearch
|
|
26
57
|
|
|
27
58
|
Execution goes through the `orx` CLI when it is installed, never ad-hoc
|
|
28
|
-
scripting
|
|
29
|
-
|
|
59
|
+
scripting. A qualitative line uses its literature commands (`orx discover`,
|
|
60
|
+
`orx paper`, the `orx skill lit-review` module). A measured line also uses its
|
|
61
|
+
experiment tree, and there its cardinal rules are not style preferences -
|
|
62
|
+
breaking one silently invalidates the run:
|
|
30
63
|
|
|
31
64
|
- never edit a node once a run has answered it; branch a child instead,
|
|
32
65
|
- the run command and environment are a fixed contract identical on every node,
|
|
@@ -34,7 +67,8 @@ silently invalidates the run:
|
|
|
34
67
|
- grow the experiment tree downward, not sideways.
|
|
35
68
|
|
|
36
69
|
Load its manual before driving it: `orx skill`, then the module for the step
|
|
37
|
-
(`orx skill
|
|
70
|
+
(`orx skill lit-review` for sources; `orx skill experiment-tree` and
|
|
71
|
+
`orx skill evidence` for a measured line).
|
|
38
72
|
Local runs need no login; managed compute does - ask the user to run `orx login`.
|
|
39
73
|
If `orx` is absent, say the research tooling is unavailable and do not improvise
|
|
40
74
|
its protocol; the workspace below still applies to whatever you run instead.
|
|
@@ -48,10 +82,10 @@ One directory per research line, `<repo>/.tezgah/research/<slug>/`:
|
|
|
48
82
|
|
|
49
83
|
| Path | What it holds |
|
|
50
84
|
|---|---|
|
|
51
|
-
| `state.json` | the question, phase, direction, the locked evaluation (`metric`, `baseline`, `locked_at`, optional `environment`, and the optional two-gate pair `capability_tolerance` + `counter_metric`), the hypothesis list, `rules` (the rule set the line was opened under), `deliverable` (`kind`: design, plan, analysis, code or finding; `path`; `ask`, the ask's items one per entry; `min_variants`), and `supersedes` when it is a new version of an older line |
|
|
85
|
+
| `state.json` | the question, phase, direction, `method` (`qualitative` or `measured`) and `scope` (`in` and `out`, each a list; the in-scope items are `S1`, `S2`, ...), the locked evaluation of a measured line (`metric`, `baseline`, `locked_at`, optional `environment`, and the optional two-gate pair `capability_tolerance` + `counter_metric`), the hypothesis list, `rules` (the rule set the line was opened under), `deliverable` (`kind`: design, plan, analysis, code or finding; `path`; `ask`, the ask's items one per entry; `min_variants`), and `supersedes` when it is a new version of an older line |
|
|
52
86
|
| `log.md` | the decision timeline: one line per decision, experiment, dead end or pivot, with the evidence that drove it |
|
|
53
87
|
| `findings.md` | `## What we know`, `## Patterns`, `## Lessons`, `## Open questions` |
|
|
54
|
-
| `claims.jsonl` | one claim per line: `statement`, `status`, `provenance`, `kind`, `falsification`, `proof`, `dependencies`, `scope` (what the claim's numbers were measured on, and it may not be wider than the rows it rests on), and `supersedes` when the claim replaces an earlier one - recorded with `tezgah-research claim <slug>`, never by editing the file |
|
|
88
|
+
| `claims.jsonl` | one claim per line: `statement`, `status`, `provenance`, `kind`, `falsification`, `proof`, `dependencies`, `trace` (the in-scope item it answers, `S<n>`, required from rules 5), `scope` (what the claim's numbers were measured on, and it may not be wider than the rows it rests on), and `supersedes` when the claim replaces an earlier one - recorded with `tezgah-research claim <slug>`, never by editing the file |
|
|
55
89
|
| `experiments/<hypothesis>/` | `protocol.md`, `results.jsonl` (one JSON object per row, each carrying its `source`, its `scope` - what the numbers were measured on - and, when that scope is `fixture`, the `fixture` description of the input that was generated), `analysis.md`, and `raw/<runId>.log` for a run read back from the engine |
|
|
56
90
|
| `literature/` | one file per source, saved when you read it, not later, and each named by its row in `literature/INDEX.jsonl` - every file under it, at any depth and of any extension, is a source the index names |
|
|
57
91
|
| `decisions/<id>/` | one comparison of the deliverable's variants: `criteria.json`, `variants.jsonl`, `comparison.jsonl` and `decision.md` - see "Variants" below |
|
|
@@ -79,7 +113,8 @@ Scaffold and check it with the CLI (`~/.config/tezgah/bin/tezgah-research`, or
|
|
|
79
113
|
`bin/tezgah-research` in the checkout):
|
|
80
114
|
|
|
81
115
|
```sh
|
|
82
|
-
~/.config/tezgah/bin/tezgah-research init my-line --ask "<the user's words>" --question "does X hold under Y?"
|
|
116
|
+
~/.config/tezgah/bin/tezgah-research init my-line --ask "<the user's words>" --question "does X hold under Y?" \
|
|
117
|
+
--method measured --in "X under Y" --out "X under Z"
|
|
83
118
|
~/.config/tezgah/bin/tezgah-research commit my-line "<message>" # commit in .tezgah's repo
|
|
84
119
|
~/.config/tezgah/bin/tezgah-research check # open lines + done/ seals; 0 clean, 1 broken rule, 2 misuse
|
|
85
120
|
~/.config/tezgah/bin/tezgah-research check --all-lines # every line in full, done/ included
|
|
@@ -220,9 +255,11 @@ rules the domain library enforces on a compiled artifact apply here:
|
|
|
220
255
|
|
|
221
256
|
**Bootstrap.** Search the literature with more than one source - `orx discover
|
|
222
257
|
keyword|embedding|openalex "<query>"` first, `orx paper <id>` to read one (the
|
|
223
|
-
`orx skill lit-review` module is the procedure) -
|
|
224
|
-
to `literature/` as you go
|
|
225
|
-
|
|
258
|
+
`orx skill lit-review` module is the procedure) - for the in-scope items only,
|
|
259
|
+
and save every source to `literature/` as you go. A qualitative line stops here
|
|
260
|
+
and answers from those sources. A measured line identifies the gap, forms
|
|
261
|
+
testable hypotheses about the in-scope items (see Ideation below), and locks the
|
|
262
|
+
evaluation before running anything: the metric, the
|
|
226
263
|
baseline, the threshold. Write it into `state.json` and commit it before the first
|
|
227
264
|
results row - a criterion chosen after seeing results is not a criterion, and
|
|
228
265
|
`check` refuses a line whose first commit holding a metric and a baseline does not
|
|
@@ -377,7 +414,9 @@ Pick the move that matches the state you are in:
|
|
|
377
414
|
| "the run is stuck" | name the hidden constraint and drop it (see below) |
|
|
378
415
|
| "the results are surprising" | re-derive the assumption they break, then go back to the literature |
|
|
379
416
|
|
|
380
|
-
- **Diverge first**: list candidate mechanisms and framings without judging them
|
|
417
|
+
- **Diverge first**: list candidate mechanisms and framings without judging them,
|
|
418
|
+
every one of them about an in-scope item - a framing of a different question is
|
|
419
|
+
an Open question for the user, not a candidate.
|
|
381
420
|
- **Expose a hidden constraint**: list the constraints of the current approach and
|
|
382
421
|
classify them hard (physically necessary), soft (convention) or hidden (never
|
|
383
422
|
stated). The most productive move in the library's whole ideation set is
|
|
@@ -474,12 +513,15 @@ and `--strict` refuses it; two raters at least, and state their agreement.
|
|
|
474
513
|
|
|
475
514
|
## Variants
|
|
476
515
|
|
|
477
|
-
A deliverable a reader acts on - a design, a plan,
|
|
478
|
-
version redone until it looks right. Serial versions fixate on the first
|
|
479
|
-
and a design shown alone gets inflated ratings (Dow et al. 2010, Tohidi et
|
|
480
|
-
2006). Produce at least three variants (two for `code`; `min_variants` in
|
|
516
|
+
A deliverable a reader acts on and chooses between - a design, a plan, code - is
|
|
517
|
+
not one version redone until it looks right. Serial versions fixate on the first
|
|
518
|
+
option and a design shown alone gets inflated ratings (Dow et al. 2010, Tohidi et
|
|
519
|
+
al. 2006). Produce at least three variants (two for `code`; `min_variants` in
|
|
481
520
|
`deliverable`, and below the default only with a `single_variant_reason`) and
|
|
482
|
-
compare them under criteria fixed first, in `decisions/<id
|
|
521
|
+
compare them under criteria fixed first, in `decisions/<id>/`. An `analysis`
|
|
522
|
+
answers its question once on a rules-5 line: variants are a choice the user
|
|
523
|
+
makes, not a way to triple the answer (an older line keeps the three it was
|
|
524
|
+
opened under):
|
|
483
525
|
|
|
484
526
|
| File | What it holds |
|
|
485
527
|
|---|---|
|
|
@@ -495,8 +537,9 @@ after it was committed; a missing cell once `decision.md` exists or the line is
|
|
|
495
537
|
delivering; a `chosen` that is not a produced variant, a rejected variant with no
|
|
496
538
|
criterion, no `flip:`; and a chosen variant another one beats or ties on every
|
|
497
539
|
numeric criterion. A judged cell with fewer than two raters warns (`--strict`
|
|
498
|
-
refuses). A design
|
|
499
|
-
runs, is refused once it delivers, and keeps
|
|
540
|
+
refuses). A design or plan (and an analysis on a line before rules 5) with no
|
|
541
|
+
`decisions/` warns while the line runs, is refused once it delivers, and keeps
|
|
542
|
+
the line open.
|
|
500
543
|
|
|
501
544
|
When the variants are code or config, they are sibling orx nodes under one head
|
|
502
545
|
node (orx cardinal rule 4: fan within a round, then descend onto the winner), and
|