@r1z4x/tezgah 1.4.0 → 1.5.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.
@@ -435,18 +435,21 @@ builds it (`capture_signals`, `bin/tezgah-taste::capture_signals`). A signal
435
435
  holds the prompt, the files written, the agent's edits, and the user's own edit
436
436
  between two agent writes. That last one compares the after blob with the next
437
437
  write's pre-state, or with the file on disk (`user_diff`,
438
- `bin/tezgah-taste::user_diff`). `learn` asks TypeSafe for one typed decision per
439
- signal (`decide`, `bin/tezgah-taste::decide`, [judge](judge.md)). The decision
438
+ `bin/tezgah-taste::user_diff`). `learn` asks the Jev model for one typed decision
439
+ per signal (`decide`, `bin/tezgah-taste::decide`, [judge](judge.md)). Any Jev
440
+ carrier will do: TypeSafe, OpenRouter System One, Cloudflare Workers AI or a
441
+ compatible endpoint. The decision
440
442
  names preference, defect or none. It picks one of eleven fixed categories and a
441
443
  scope. It also says how the signal relates to each learning held: supports,
442
- contradicts, narrows or unrelated. When another provider answers, `learn` records
443
- the decision in the `decisions` table as `unverified` and never applies it. A
444
+ contradicts, narrows or unrelated. When a provider outside the Jev family answers,
445
+ `learn` records the decision in the `decisions` table as `unverified`. It never
446
+ applies it. The report counts the typed decisions under `jev`. A
444
447
  defect goes to the `defects` table and never into the ledger.
445
448
 
446
449
  Armed taste also learns by itself. At session start, `learn` starts in the
447
450
  background when new signals arrived since the last start. It starts at most
448
451
  once an hour per repository (`learn_later`, `hooks/tezgah_taste.py::learn_later`). It runs
449
- with `--no-fallback` and only when a TypeSafe key resolves, so it never pays a
452
+ with `--no-fallback` and only when a Jev carrier resolves, so it never pays a
450
453
  generative provider for a decision it could not apply. It does not run on
451
454
  Windows, which has no ledger lock yet. The stamp is a `taste_learn` row of the
452
455
  cache's `tezgah.db`. The log lives under the cache dir, in `taste-learn/`.
@@ -661,7 +664,7 @@ or its optional tools and config, on its own initiative
661
664
 
662
665
  ## Triage: `tezgah-triage` and the `judge-off` switch
663
666
 
664
- One judgement seam serves five callers, each of them asking TypeSafe a batched
667
+ One judgement seam serves five callers, each of them asking the judge a batched
665
668
  question instead of paying an agent to read a page: the analyze-app loop's snapshot
666
669
  triage (`bin/tezgah-triage`), `bin/tezgah-docs` for a query its keyword index
667
670
  cannot place, and the prompt-path skill hint (`hooks/tezgah_skill_pick.py`), the
@@ -700,7 +703,10 @@ made - no credential, the switch below, or a failed call - and the loop reads th
700
703
  tree directly instead.
701
704
 
702
705
  The state leaves the machine. A judgement sends the state and the questions to
703
- `api.typesafe.ai` (`ask()`, `hooks/tezgah_judge.py::ask`) - for the triage that is
706
+ the host of whichever provider answers - the session's own CLI (omp, claude,
707
+ opencode, cursor, codex), or the chosen Jev
708
+ carrier's (`api.typesafe.ai`, `openrouter.ai`, `api.cloudflare.com` or a
709
+ `JEV_API_BASE_URL`, see [judge](judge.md)) (`ask()`, `hooks/tezgah_judge.py::ask`) - for the triage that is
704
710
  the snapshot's own text, so a screen carrying personal data is read by a third
705
711
  party, and for the docs fallback it is the reader's query. Nothing else goes: no
706
712
  session id, no workspace path, no credential beyond the bearer header, and the
@@ -710,11 +716,16 @@ request cannot drift. That is why the two shell callers are on-demand - an agent
710
716
  asks for a judgement, no gate does - while the skill hint asks only with its own
711
717
  marker armed, and why the switch below is the off button for the whole path.
712
718
 
713
- The credential resolves from `TYPESAFE_API_KEY`, else from
714
- `~/.config/typesafe/key` (`credential()`, `hooks/tezgah_judge.py::credential`), and when
715
- neither resolves the same questions go to the OpenRouter fallback, whose key rides
716
- `OPENROUTER_API_KEY` and then `~/.config/openrouter/key`
717
- (`openrouter_key()`, `hooks/tezgah_judge.py::openrouter_key`). The file is the channel that
719
+ With no session CLI, the third party is the Jev model through one of its carriers.
720
+ TypeSafe's credential resolves from `TYPESAFE_API_KEY`, else from
721
+ `~/.config/typesafe/key`, else from the active omp login (`typesafe_key()`,
722
+ `hooks/tezgah_paths.py::typesafe_key`). `jev` chooses which of the other Jev
723
+ carriers the seam asks - OpenRouter's System One endpoint, Cloudflare Workers AI
724
+ and any System One-compatible URL (`jev_carriers()`,
725
+ `hooks/tezgah_paths.py::jev_carriers`). The OpenRouter chat fallback's key rides
726
+ `OPENROUTER_API_KEY` and then `~/.config/openrouter/key` (`openrouter_key()`,
727
+ `hooks/tezgah_judge.py::openrouter_key`). When no carrier answers, the same
728
+ questions go to that chat fallback. The file is the channel that
718
729
  matters on a machine exporting the variable from `~/.zshenv`: a hook or a bin tool
719
730
  runs in a non-interactive shell, where that export never ran, so the file is what a
720
731
  judgement actually resolves. Cost is input tokens alone - $0.042 per
package/docs/skills.md CHANGED
@@ -176,7 +176,7 @@ it (`tests/test_skills.py::SkillStandards`).
176
176
  | `no-ai-slop` | Edits a draft sharper and more human without losing the voice, or flags slop; fires on a draft review, on a draft in a language other than English, on text that must keep terms, identifiers or unit symbols verbatim, and before publishing English prose tezgah writes itself. |
177
177
  | `analyze-app` | Inspects and drives a running web or mobile app from its accessibility / DOM / view tree, its rendered screen (screenshots, blur and grayscale tests, breakpoints) and measurements taken in the page; fires when a task must verify behaviour or read the UI, not just read source. |
178
178
  | `plan-add` | Creates a plan under `.tezgah/plans/open/` with its own `allowed_paths:` scope, bootstraps the `.tezgah/plans/` layer if missing, commits it in the private `.tezgah` repository (`git -C .tezgah`, never the project's git); fires on `/tezgah:plan-add`, "track this as a plan". |
179
- | `plan-status` | Reads every open plan, enriches rows with live PR state from `gh`, rewrites the README table (committed in the private `.tezgah` repository) and recommends the next plan; fires on `/tezgah:plan-status` or "what plans are open". |
179
+ | `plan-status` | Reads every open plan and `tezgah-task status` (each plan's Acceptance boxes ticked, the plans its `after:` waits on, the ready plans that can run at once), enriches rows with live PR state from `gh`, rewrites the README table (committed in the private `.tezgah` repository) and recommends the next plan; fires on `/tezgah:plan-status` or "what plans are open". |
180
180
  | `plan-sync` | Closes finished plans whose PR merged or closed through `tezgah-task close`, which moves a plan to `.tezgah/plans/done/` as done only when it records an approving `review:`; commits one message per plan in the private `.tezgah` repository; fires on `/tezgah:plan-sync`, "sync plans". |
181
181
  | `research` | Runs an auditable research line - two loops, protocol before run, six-dimension review, provenance; fires when the deliverable is evidence: a lit review, a hypothesis, a benchmark or ablation. |
182
182
  | `product-analysis` | Runs a product analysis across five axes - value (HEART), usability (heuristics, the cognitive walkthrough, WCAG 2.2, axe-core, the running app read through `analyze-app` down to component x state, tree and image both), feasibility (a cited `path:line`), competition (a dated teardown) and triage (keep/fix/cut/bet) - with one named evidence class per finding, executed as a research line; fires on a product, feature, retention or roadmap question rather than a code change. |
package/docs/vision.md CHANGED
@@ -22,11 +22,16 @@ the workspace's own records; every number below names where it came from.
22
22
  runs, 106 minutes, 10 of them green confirmations, in one session - the rule
23
23
  "run the full suite once, on the final tree" was already written down and
24
24
  `AGENTS.md` said "before every commit".)
25
- 3. **A review loop reads every fix.** A round that confirms a defect gets a
26
- round over the fix. The loop ends when a round confirms none
27
- (`hooks/tezgah_agents.py::_reviewer_body`). The same defect family three times means the
28
- design changes. (Measured: two of four severe defects in one internal plan came from
29
- fixes, after round two.)
25
+ 3. **One review per plan, two rounds, fanned out.** Reviewers read the plan's
26
+ whole diff once, in its verification phase. Round one spawns them together,
27
+ one per dimension. Only a confirmed critical or major finding opens round two,
28
+ and round two reads the fix's delta. After round two the user gets what is
29
+ left, or the plan lists it as fix-later. `tezgah-task review` refuses a third
30
+ round (`hooks/tezgah_agents.py::_reviewer_body`, `bin/tezgah-task::cmd_review`).
31
+ (Measured: the old loop opened a round on any finding and ended only on a
32
+ clean round. It reviewed one finished plan round after round. In another plan,
33
+ fixes brought two of four severe defects. So round two reads the fix, and a
34
+ severe finding left after it blocks.)
30
35
  4. **Doubt lowers nothing and hides nothing.** An unverifiable check never
31
36
  downgrades a finding, an unknown criterion never relaxes a cap, and a line
32
37
  whose own criteria are unmet is reported as unanswered rather than concluded.
@@ -206,15 +206,22 @@ def _reviewer_body(host):
206
206
  "is lowered only by evidence: a check you could not complete never\n"
207
207
  "downgrades a confirmed finding - keep its severity and mark it\n"
208
208
  "provisional. Disclose the order you read the files in.\n\n"
209
- "Scope and budget. Every round that confirms a defect is followed by a\n"
210
- "round over that fix's delta; the review ends when a round confirms none.\n"
211
- "A later round reads only the delta since the sha the previous round\n"
212
- "recorded (`tezgah-task review` prints it): re-reading the whole diff\n"
213
- "re-reports what was already triaged. Test results are input, not a task\n"
214
- "- read the author's output file; do not run the suite, and run at most one\n"
215
- "`-k` test when a specific accusation needs it. The same defect family\n"
216
- "confirmed three times is a sign the design needs a pivot, not another\n"
217
- "patch.\n\n"
209
+ "Scope and budget. One review per plan, over its whole diff in the\n"
210
+ "verification phase; at most two rounds - only a confirmed critical or\n"
211
+ "major finding opens round two, a minor or suggestion one is recorded\n"
212
+ "fix-later, and after round two what is left goes to the user or is\n"
213
+ "recorded fix-later, never a third round. A round may be a fan-out of\n"
214
+ "reviewers spawned at once, one per dimension (correctness and contract,\n"
215
+ "security, tests and performance) or per file group: when your brief\n"
216
+ "names a dimension or files, review only those - a sibling owns the rest.\n"
217
+ "Round two reads only the delta since the sha round one recorded\n"
218
+ "(`tezgah-task review` prints it), plus the round-one hunks and direct\n"
219
+ "callers of the symbols that delta touches, and marks every round-one\n"
220
+ "finding closed or open: re-reading the whole diff re-reports what was\n"
221
+ "already triaged. Test results are input, not a task - read the author's\n"
222
+ "output file; do not run the suite, and run at most one `-k` test when a\n"
223
+ "specific accusation needs it. The same defect family confirmed in both\n"
224
+ "rounds is a sign the design needs a pivot, not another patch.\n\n"
218
225
  "%s" % (_graph_howto(host), _blast_radius(host), EMPTY_IS_NOT_PROOF,
219
226
  _may_use(host)))
220
227
 
@@ -343,7 +350,8 @@ ROLES = (
343
350
  ("tezgah-reviewer",
344
351
  "Adversarial, read-only review of a diff, branch or PR: derives the blast "
345
352
  "radius, checks correctness, contract, security, tests and performance, and "
346
- "classifies every finding confirmed/refuted/unverified.",
353
+ "classifies every finding confirmed/refuted/unverified. One review per plan, "
354
+ "at most two rounds; spawn one per dimension in one message for a large diff.",
347
355
  lambda infra: infra["caps"]["graph"], _reviewer_body, True),
348
356
  ("tezgah-cheap",
349
357
  "Mechanical, fully specified edits on the cheapest model tier: renames, "
@@ -389,7 +397,10 @@ def _orch_body(names):
389
397
  "verify; you delegate execution and read the evidence back. Available\n"
390
398
  "specialists: %s.\n\n"
391
399
  "Fan out by default: when the request or your todo list holds two or more\n"
392
- "independent items, spawn one subagent per item in ONE message. Give\n"
400
+ "independent items, count them and spawn one subagent per item in ONE\n"
401
+ "message - at every phase: a plan's tasks, its acceptance commands and the\n"
402
+ "sources of a survey are items too, and checks that share no state run as\n"
403
+ "tool calls in one message, never a queue. Give\n"
393
404
  "every writing slice its own branch and checkout - `orca worktree create\n"
394
405
  "--name <slug> --parent-worktree active --json` when the session runs\n"
395
406
  "inside Orca, else `git worktree add -b <branch> <absolute path>` - and\n"
@@ -26,8 +26,9 @@ from tezgah_integrity import (STEP_KINDS, _heredocs, _path as _ledger_path,
26
26
  UNTRUSTED_CHANNEL)
27
27
  from tezgah_lessons import (lesson_key, lines as lesson_lines,
28
28
  tainted as tainted_lessons)
29
- from tezgah_policy import (CONDITIONAL_KEYS, CORE, POINTERS, PROMPT_REMINDER,
30
- REPLY_LANG_TEXT, open_lines_note, pony_level_line)
29
+ from tezgah_policy import (CONDITIONAL_KEYS, CORE, FANOUT_ACTION, POINTERS,
30
+ PROMPT_REMINDER, REPLY_LANG_TEXT, open_lines_note,
31
+ pony_level_line)
31
32
  from tezgah_paths import (CACHE, REPO_MARKS, SWITCHES, ai_research_dir, armed,
32
33
  cache_dir, codegraph_bin, consult_options,
33
34
  ensure_workspace, fallback_cache, have_judge_key, off,
@@ -106,6 +107,21 @@ def _tr_ui_ask(word):
106
107
  return (r"(?:\b(?:%s)\w*[^.!?\n]{0,40}\b(?:%s))" % (TR_UI_OBJECTS, word))
107
108
 
108
109
 
110
+ # The fan-out hint's parts (PROMPT_HINTS["fanout"]). A list item's marker:
111
+ # `1.`, `1)`, `1 -`, `1:`, `a)`, or a `-`/`*`/`+`/`•` bullet, then its text.
112
+ _FAN_ITEM = r"(?:\d+[ \t]*[.):\u2013-]|[a-z]\)|[-*+\u2022])[ \t]+\S"
113
+ # A file a prompt names: a path with a directory, or a name with an extension.
114
+ # Anchored at a token start: unanchored, a long run of word characters made
115
+ # every position a start and the search quadratic (100 KB took minutes).
116
+ _FAN_FILE = (r"(?<![\w./-])[`'\"]?(?:(?:[\w.-]+/)+[\w.-]*\w|"
117
+ r"\w[\w-]*\.[a-z]{1,4}\b)[`'\"]?")
118
+ # The imperative that opens an English clause and closes a Turkish one.
119
+ _FAN_VERB_EN = (r"(?:fix|add|update|write|remove|delete|rename|move|refactor|"
120
+ r"document|bump|create|implement|migrate|replace)")
121
+ _FAN_VERB_TR = (r"(?:düzelt|güncelle|ekle|yaz|sil|kaldır|taşı|bitir|tamamla|"
122
+ r"incele|oluştur|değiştir)")
123
+
124
+
109
125
  PROMPT_HINTS = (
110
126
  ("spec", r"\b(normal (user )?behaviou?r|clean ui|nicer|more intuitive|"
111
127
  r"un?professional|polish(ed)?|improve the (ui|ux)|make it (better|"
@@ -340,9 +356,22 @@ PROMPT_HINTS = (
340
356
  # had to say "do everything with subagents and in parallel" on every task
341
357
  # because nothing armed the fan-out rule from the shape of the request.
342
358
  # `parallelize` alone is code work, so only the delegation phrasings count.
343
- ("fanout", r"(?ms:^[ \t]*\d+[.)][ \t]+\S.*?^[ \t]*\d+[.)][ \t]+\S)|"
344
- r"(?ms:^[ \t]*[-*•][ \t]+\S.*?^[ \t]*[-*•][ \t]+\S)|"
345
- r"\b(?:in parallel|paralel\w*|sub-?agents?|alt ?ajan\w*|"
359
+ # The owner's own shapes armed nothing until 2026-10-09: `1 - `, `1- `,
360
+ # `1:`, `a)`, `+` items, an inline `1. ... 2. ...`, "subagentlerle", three
361
+ # files named as one list, "First, ... Second, ...", and three imperative
362
+ # clauses in one sentence (clause-initial in English, clause-final in
363
+ # Turkish). A timestamp, a range and a traceback stay out (the corpus).
364
+ ("fanout", r"(?ms:^[ \t]*" + _FAN_ITEM + r".*?^[ \t]*" + _FAN_ITEM + r")|"
365
+ r"(?<![\w.])1[ \t]*[.)-][ \t]+\S[^\n]*?(?<![\w.])2[ \t]*[.)-][ \t]+\S|"
366
+ + _FAN_FILE + r"(?:[ \t]*(?:,[ \t]*(?:(?:and|ve)[ \t]+)?|"
367
+ r"(?:and|ve|&)[ \t]+)" + _FAN_FILE + r"){2,}|"
368
+ r"(?s:\bfirst(?:ly)?[,:].*?\bsecond(?:ly)?[,:])|"
369
+ r"(?s:\bbirinci(?:si)?[,:].*?\bikinci(?:si)?[,:])|"
370
+ r"(?m:(?:(?:^|[,;]|\band\b)[ \t]*(?:and[ \t]+)?" + _FAN_VERB_EN
371
+ + r"\b[^\n]*?){3})|"
372
+ r"(?m:(?:\b" + _FAN_VERB_TR + r"\w{0,4}[ \t]*(?:[,;.]|\bve\b|$)"
373
+ r"[^\n]*?){3})|"
374
+ r"\b(?:in parallel|paralel\w*|sub-?agent\w*|alt ?ajan\w*|"
346
375
  r"worktrees?|fan[- ]?out|orchestrat\w*|orkestra\w*)\b"),
347
376
  )
348
377
 
@@ -1094,8 +1123,13 @@ ARMED_RESURFACE = 20
1094
1123
  def armed_again(key):
1095
1124
  """The one line a matching prompt pays for a paragraph this session was
1096
1125
  already shown. The research rule keeps its `{OPEN_LINES}` slot: the open
1097
- lines are a fact about the repo now, not rule text the session holds."""
1126
+ lines are a fact about the repo now, not rule text the session holds. The
1127
+ fan-out rule keeps its action (`FANOUT_ACTION`): the rule is a thing to do
1128
+ on this turn's list, and a bare reminder that it exists was not done."""
1098
1129
  label = dict(CORE_RULES)[key]
1130
+ if key == "fanout":
1131
+ return ("%s Armed again: %s The full rule is in the `tezgah-contract` "
1132
+ "skill." % (label, FANOUT_ACTION))
1099
1133
  return ("%s Armed again: the full rule was given earlier this session and "
1100
1134
  "is in the `tezgah-contract` skill.%s"
1101
1135
  % (label, "{OPEN_LINES}" if key == "research" else ""))