@r1z4x/tezgah 1.6.0 → 1.7.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 +80 -1
- package/MANIFEST +5 -0
- package/README.md +6 -3
- package/bin/tezgah-docs +18 -16
- package/bin/tezgah-doctor +152 -0
- package/bin/tezgah-research +234 -10
- package/bin/tezgah-route +48 -1
- package/bin/tezgah-setup +26 -13
- package/bin/tezgah-task +589 -17
- package/bin/tezgah-taste +48 -10
- package/docs/architecture.md +7 -4
- package/docs/clarity-baseline.json +2 -2
- package/docs/contract.md +108 -24
- package/docs/evidence.md +201 -28
- package/docs/feature-audit.md +1 -1
- package/docs/gate.md +60 -22
- package/docs/glossary.md +4 -4
- package/docs/hosts.md +8 -8
- package/docs/index.json +11 -5
- package/docs/judge.md +9 -3
- package/docs/models.md +35 -2
- package/docs/operations.md +103 -3
- package/docs/research.md +346 -13
- package/docs/skills.md +1 -1
- package/docs/status-line.md +5 -5
- package/docs/testing.md +2 -2
- package/docs/vision.md +12 -4
- package/hooks/hooks.json +2 -2
- package/hooks/lang/en.json +323 -0
- package/hooks/lang/tr.json +344 -0
- package/hooks/projects-posttooluse.py +24 -5
- package/hooks/projects-pretooluse.py +9 -4
- package/hooks/projects-stop.py +28 -16
- package/hooks/tezgah_agents.py +29 -10
- package/hooks/tezgah_clarity.py +7 -3
- package/hooks/tezgah_context.py +425 -406
- package/hooks/tezgah_gate.py +583 -33
- package/hooks/tezgah_integrity.py +1480 -197
- package/hooks/tezgah_models.py +337 -15
- package/hooks/tezgah_paths.py +103 -10
- package/hooks/tezgah_policy.py +258 -126
- package/hooks/tezgah_rank.py +29 -27
- package/hooks/tezgah_replay.py +200 -30
- package/hooks/tezgah_research.py +1294 -9
- package/hooks/tezgah_stopspec.py +7 -0
- package/hooks/tezgah_store.py +74 -27
- package/hooks/tezgah_task.py +915 -6
- package/hosts/codex/hook.py +9 -10
- package/hosts/cursor/hook.py +21 -14
- package/hosts/dsh/hooks.json +2 -2
- package/hosts/omp/hook.py +44 -23
- package/hosts/omp/tezgah-hook.ts.in +93 -7
- package/hosts/opencode/plugins/tezgah.js +102 -2
- package/output-styles/tezgah.md +76 -42
- package/package.json +1 -1
- package/skills/i-have-adhd/EVALS.md +22 -2
- package/skills/i-have-adhd/SKILL.md +12 -5
- package/skills/plan-add/SKILL.md +5 -3
- package/skills/plan-sync/SKILL.md +5 -3
- package/skills/ponytail/EVALS.md +21 -2
- package/skills/ponytail/SKILL.md +4 -4
- package/skills/research/SKILL.md +89 -1
- package/skills/tezgah-contract/EVALS.md +52 -7
- package/skills/tezgah-contract/SKILL.md +151 -56
package/hooks/tezgah_stopspec.py
CHANGED
|
@@ -102,6 +102,13 @@ NOT_COUNTED = (
|
|
|
102
102
|
"constants over one trace, read off the reply, not a row"),
|
|
103
103
|
("evidence tampered", "a trace-validity guard in the selector, before the fold"),
|
|
104
104
|
)
|
|
105
|
+
# Plan 080 (2026-10-11), after the pre-registration and not a change to it: the
|
|
106
|
+
# admission ("doğrulanmadı") is applied by the selector to whichever fold it is
|
|
107
|
+
# given (`tezgah_integrity._admitted`: a check-repair class cleared after a check
|
|
108
|
+
# that failed since the newest change), and `residual work` is a selector verdict
|
|
109
|
+
# asked at every exit that would end the turn (`_residual_block`). Both reach the
|
|
110
|
+
# spec's verdict through the shared selector, so neither is a formula here.
|
|
111
|
+
|
|
105
112
|
# GO 2's live floor: shadow rows needed before the 99% bar is read. At 400 a
|
|
106
113
|
# 99% rate is 4 disagreements, so one more or less moves it by 0.25 pp.
|
|
107
114
|
MIN_LIVE_N = 400
|
package/hooks/tezgah_store.py
CHANGED
|
@@ -269,17 +269,58 @@ def _taste_put(table):
|
|
|
269
269
|
return put
|
|
270
270
|
|
|
271
271
|
|
|
272
|
+
def _recorded(conn, name, st):
|
|
273
|
+
"""(the bytes `imported` counts for the file `name` with stat `st`, the
|
|
274
|
+
device id of the row they were found under when it is not the current one)
|
|
275
|
+
or None. A volume remount can renumber the device of a file whose name and
|
|
276
|
+
inode are unchanged, and a store from before the fix imported such a file
|
|
277
|
+
again from offset 0 under the new id, so every row for (name, inode) is one
|
|
278
|
+
import stream. The longest is the file's offset under any id - this id's
|
|
279
|
+
own row included, when the id comes back to a shorter stream - so no byte
|
|
280
|
+
goes in twice."""
|
|
281
|
+
found = conn.execute("SELECT bytes, dev FROM imported WHERE name = ? AND ino = ? "
|
|
282
|
+
"ORDER BY bytes DESC, dev = ? DESC LIMIT 1",
|
|
283
|
+
(name, st.st_ino, st.st_dev)).fetchone()
|
|
284
|
+
if found is None:
|
|
285
|
+
return None
|
|
286
|
+
return found[0], None if found[1] == st.st_dev else found[1]
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def _record(conn, key, start, end, stale):
|
|
290
|
+
"""The file `key` in up to byte `end`, its import having resumed at `start`.
|
|
291
|
+
An import resumed from a row under another device id (`stale`) moves that
|
|
292
|
+
row to the current key, and a shorter stream's row already under the
|
|
293
|
+
current key takes the stale id: the two swap, so one row per import stream
|
|
294
|
+
stays, which is what the doctor's repair counts a double import by
|
|
295
|
+
(`tezgah-doctor --repair-evidence`). A file cut below what went in (a row
|
|
296
|
+
past `start`) no longer holds what its imports read: it goes on as one
|
|
297
|
+
stream, which resumes at its end and which the repair leaves alone."""
|
|
298
|
+
name, dev, ino = key
|
|
299
|
+
if conn.execute("SELECT 1 FROM imported WHERE name = ? AND ino = ? AND bytes > ?",
|
|
300
|
+
(name, ino, start)).fetchone():
|
|
301
|
+
conn.execute("DELETE FROM imported WHERE name = ? AND ino = ?", (name, ino))
|
|
302
|
+
elif stale is not None:
|
|
303
|
+
conn.execute("DELETE FROM imported WHERE name = ? AND dev = ? AND ino = ?",
|
|
304
|
+
(name, stale, ino))
|
|
305
|
+
conn.execute("UPDATE imported SET dev = ? WHERE name = ? AND dev = ? AND ino = ?",
|
|
306
|
+
(stale, name, dev, ino))
|
|
307
|
+
conn.execute("INSERT OR REPLACE INTO imported (name, dev, ino, bytes) "
|
|
308
|
+
"VALUES (?, ?, ?, ?)", key + (end,))
|
|
309
|
+
|
|
310
|
+
|
|
272
311
|
def _offset(conn, name, fh):
|
|
273
|
-
"""(the file's key, the byte offset its import resumes at, whether
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
312
|
+
"""(the file's key, the byte offset its import resumes at, whether the
|
|
313
|
+
recorded row must be rewritten, the device id of the row it resumes from
|
|
314
|
+
when that is not the current one (`_recorded`)). The row is rewritten when
|
|
315
|
+
its offset differs or it is under another id: a file cut below what went
|
|
316
|
+
in (rewritten in place) resumes at its end, so the rows appended after the
|
|
317
|
+
cut still import."""
|
|
277
318
|
st = os.fstat(fh.fileno())
|
|
278
319
|
key = (name, st.st_dev, st.st_ino)
|
|
279
|
-
found = conn
|
|
280
|
-
"AND ino = ?", key).fetchone()
|
|
320
|
+
found = _recorded(conn, name, st)
|
|
281
321
|
start = min(found[0], st.st_size) if found else 0
|
|
282
|
-
|
|
322
|
+
stale = found[1] if found else None
|
|
323
|
+
return key, start, bool(found and found[0] != start) or stale is not None, stale
|
|
283
324
|
|
|
284
325
|
|
|
285
326
|
def _import_rows(conn, name, src, st, put=None, keep=False):
|
|
@@ -303,14 +344,14 @@ def _import_rows(conn, name, src, st, put=None, keep=False):
|
|
|
303
344
|
_failed(conn, name, st)
|
|
304
345
|
return
|
|
305
346
|
with fh: # closing it releases the flock
|
|
306
|
-
key, start, cut = _offset(conn, name, fh)
|
|
347
|
+
key, start, cut, _stale = _offset(conn, name, fh)
|
|
307
348
|
fh.seek(start)
|
|
308
349
|
raw = fh.read()
|
|
309
350
|
if b"\n" in raw or cut:
|
|
310
351
|
with transaction(conn):
|
|
311
352
|
if not _locked(fh):
|
|
312
353
|
return # its writer holds it: the next open takes it
|
|
313
|
-
key, start, cut = _offset(conn, name, fh)
|
|
354
|
+
key, start, cut, stale = _offset(conn, name, fh)
|
|
314
355
|
fh.seek(start)
|
|
315
356
|
raw = fh.read()
|
|
316
357
|
whole = raw[:raw.rfind(b"\n") + 1]
|
|
@@ -318,8 +359,7 @@ def _import_rows(conn, name, src, st, put=None, keep=False):
|
|
|
318
359
|
put = put or _taste_put(name[:-len(".jsonl")])
|
|
319
360
|
for line in whole.split(b"\n"):
|
|
320
361
|
put(conn, line)
|
|
321
|
-
conn
|
|
322
|
-
"VALUES (?, ?, ?, ?)", key + (start + len(whole),))
|
|
362
|
+
_record(conn, key, start, start + len(whole), stale)
|
|
323
363
|
# aside only when every byte went in (no fragment past the last newline)
|
|
324
364
|
# and no writer appended past the read (the old writer appends unlocked
|
|
325
365
|
# once its own wait runs out), under the flock so no writer is inside
|
|
@@ -523,27 +563,36 @@ def _insert(conn, session, text):
|
|
|
523
563
|
ts if ts is not None and -2 ** 63 <= ts < 2 ** 63 else None, text))
|
|
524
564
|
|
|
525
565
|
|
|
566
|
+
def _line_text(line):
|
|
567
|
+
"""The row text one legacy ledger line (no newline) imports as, None for a
|
|
568
|
+
blank one: kept as it was written, so a line that does not parse stays a
|
|
569
|
+
line the reader names as damage."""
|
|
570
|
+
if not line.strip():
|
|
571
|
+
return None
|
|
572
|
+
try:
|
|
573
|
+
return line.decode("utf-8")
|
|
574
|
+
except UnicodeDecodeError:
|
|
575
|
+
# the marker `tezgah_integrity._parse` names as damage, hashed over the
|
|
576
|
+
# line with its newline as the file reader hashed it, so a damage row
|
|
577
|
+
# it wrote still names the same line
|
|
578
|
+
return "\x00not utf-8 %s" % hashlib.sha1(line + b"\n").hexdigest()[:12]
|
|
579
|
+
|
|
580
|
+
|
|
526
581
|
def _evidence_put(session):
|
|
527
|
-
"""The import of one legacy ledger line
|
|
528
|
-
that does not parse stays a line the reader names as damage."""
|
|
582
|
+
"""The import of one legacy ledger line (`_line_text`)."""
|
|
529
583
|
def put(conn, line):
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
text = line.decode("utf-8")
|
|
534
|
-
except UnicodeDecodeError:
|
|
535
|
-
# the marker `tezgah_integrity._parse` names as damage, hashed
|
|
536
|
-
# over the line with its newline as the file reader hashed it, so
|
|
537
|
-
# a damage row it wrote still names the same line
|
|
538
|
-
text = "\x00not utf-8 %s" % hashlib.sha1(line + b"\n").hexdigest()[:12]
|
|
539
|
-
_insert(conn, session, text)
|
|
584
|
+
text = _line_text(line)
|
|
585
|
+
if text is not None:
|
|
586
|
+
_insert(conn, session, text)
|
|
540
587
|
return put
|
|
541
588
|
|
|
542
589
|
|
|
543
590
|
def _sync(held, path):
|
|
544
591
|
"""Import the legacy JSONL at `path` past the bytes already in. Without a
|
|
545
592
|
file, or with nothing new in it, this is one stat (and one lookup the first
|
|
546
|
-
time a process sees it).
|
|
593
|
+
time a process sees it). A file whose offset sits under another device id
|
|
594
|
+
(`_recorded`) goes through the import even with nothing new, whose write
|
|
595
|
+
re-keys it."""
|
|
547
596
|
try:
|
|
548
597
|
st = os.stat(path)
|
|
549
598
|
except OSError:
|
|
@@ -553,9 +602,7 @@ def _sync(held, path):
|
|
|
553
602
|
if seen.get(path) == mark:
|
|
554
603
|
return
|
|
555
604
|
name = os.path.basename(path)
|
|
556
|
-
|
|
557
|
-
(name, st.st_dev, st.st_ino)).fetchone()
|
|
558
|
-
if found and found[0] == st.st_size:
|
|
605
|
+
if _recorded(conn, name, st) == (st.st_size, None):
|
|
559
606
|
seen[path] = mark
|
|
560
607
|
return
|
|
561
608
|
if conn.execute("SELECT 1 FROM import_failed WHERE name = ? AND mtime_ns = ? "
|