Repository navigation
lab L1: the t27c lab's decisions as .t27 specs, lab.py as the oracle - #11066
Merged
Merged
Conversation
…loses #11041) Five specs under specs/lab/ port every decision of infra/t27c-lab/lab.py as pure functions of text and numbers, host-agnostic, with the Python as the oracle until the cutover (epic #11040): - text.t27 the byte-text rules Python decides lines, spaces and characters by - paths.t27 SHA_RE, BRANCH_RE, the checkout and enqueue guards, safe_path, do_GET routing - plan.t27 fnmatch.fnmatchcase, watched, parse_heads, plan, stale_dropped - verdict.t27 the closed gate table, summary, misread_counts, misread_line, verdict, red_gates - prune.t27 the keep-N slice, the run ranking, the scratch-target reaping decision Every assert of lab.py self_check() is a test with the same inputs and outputs, and tables of inputs run through the Python functions are pinned (prune() and reap_scratch_targets() were run for real on temporary directories). Not ported, and refused rather than answered differently: a backwards range in a glob, a count of more than 18 digits. Seals under .trinity/seals/lab_Lab*.json. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…loses #11041) Merge of master plus sealing, gates run on the merged tree with t27c from master: tests clean with vacuous passes 0, seal --verify MATCH, tri types ratchet CLEAN, dupe_scan failure set a subset of master's. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Contributor
This was referenced Oct 19, 2026
This was referenced Oct 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #11041. Part of the lab epic #11040.
Five specs under
specs/lab/port every decision of the t27c lab (infra/t27c-lab/lab.py, unchanged) as pure functions of text and numbers, with the Python as the oracle until the cutover. They name no host and no cloud: whoever runs a gate (one lab, or any node that takes a lab job, #11043) hands them the log, the exit code and the facts. The eight gates are a closed table (lab_gate_*,lab_gate_known), so a gate id outside it is refused and the command comes from the table, never from text.Functions ported
watched,fnmatch.fnmatchcaselab_watched,lab_fnmatch(*?[seq][!seq], ranges, code points)parse_headslab_parse_heads(dict order and duplicates)planlab_planstale_droppedlab_stale_dropped"master" in item["branches"](run_item)lab_item_has_branchSHA_RE,BRANCH_RElab_is_sha,lab_is_branch(with Python's$newline)checkout(),enqueue(),take_requests()guardslab_branch_fetchable,lab_ref_enqueueable,lab_request_kindsafe_pathlab_path_parts,lab_path_kind,lab_path_writeHandler.do_GETrouting and content typelab_route,lab_path_is_jsonGATES,NEEDS_BUILD,REPORT_ONLYlab_gate_name/cmd/timeout/pattern/index/known,lab_name_needs_build,lab_name_report_onlysummarylab_summary,lab_summary_write,lab_pattern_hitsmisread_countslab_misread_counts,lab_misread_rowmisread_linelab_misread_lineverdictlab_verdict,lab_verdict_nameredin run_item, skipped gate,built, seal refresh duelab_red_gates,lab_gate_skipped,lab_built_after,lab_seals_duesh()limit (exit 124)lab_exit_afterpublish_latest()head statelab_head_state,lab_head_state_nameprune()keep-N,runs[KEEP_RUNS:]lab_prune_start,lab_run_rank,lab_prune_runsreap_scratch_targets()lab_reap_pressure,lab_scratch_name,lab_reap_decisionsplitlines,strip,\s,[:300]lab_break_len,lab_space_len,lab_strip_*,lab_cut_chars, writersReused by
use, nothing copied:specs/claw/lab_disk.t27(disk_reason,R_FULL,R_LOW,R_OK: prune.t27 ties the reaper's 15% line to the 10% and 1 GiB lines of the hold gate, on the tick-40 volume and a grid of five volumes at 0 to 100% free) andspecs/tri/lab/receipt.t27(span_is).specs/claw/lab_receipt.t27decides about a hand-run patch check, not about the lab's own gates, so it has no overlap to reuse.lab.py
self_check()to tests, one to oneAll 32
assertstatements; each test holds the same inputs and outputs, taken by running the Python (the 5-path loop is 5 inputs).watchedx4lab_py_self_check_watched_is_the_index_of_the_first_globparse_headslab_py_self_check_parse_heads_keeps_heads_with_a_sha_onlyplanorder, branches, andplan(...) == []lab_py_self_check_plan_groups_by_sha_in_glob_orderstale_droppedlab_py_self_check_stale_dropped_keeps_live_and_asked_entriessummaryx4lab_py_self_check_summary_is_the_last_matching_line_else_the_last_non_empty_lineverdictx5lab_py_self_check_verdict_is_green_only_when_every_counting_gate_exited_zeromisread_countsx4lab_py_self_check_misread_counts_reads_the_table_and_nothing_elsemisread_linelab_py_self_check_misread_line_says_the_counts_in_wordssafe_pathroutes,.log/.patchrefusalslab_py_self_check_safe_path_reads_the_four_routes,..._refuses_the_wrong_extension_and_every_traversal..._refuses_the_wrong_extension_and_every_traversalBRANCH_RElab_py_self_check_branch_rereap_scratch_targetson old-target, new-target, targetlab_py_self_check_the_reaper_drops_the_old_scratch_target_and_nothing_elsePinned cases (1474 table asserts, produced by running the Python)
paths 457 (SHA_RE 35, BRANCH_RE 65, checkout guard 65, enqueue rule 50, request kind 66, safe_path 134, do_GET routing 28, self_check 14), plan 271 (80 glob rows x 39 names = 3120 pairs, 28 watch rows x 39 names = 1092 pairs, parse_heads 38, plan 45, stale_dropped 35, item_has 37, self_check 8), verdict 411 (summary 51 texts x 5 patterns, misread_counts 37, misread_line 26, verdict 64, red_gates 63, gate table 24, run_item decisions 100, sh limit 13, head state 19, self_check 14), prune 335 (reaper 129 entries run through the real
reap_scratch_targetson temporary directories: a directory, a link, a file, old and new, 20 names, min_free 2.0 and 0.0; pressure and idle 53;prune()run for real on run files with chosen mtimes 57; slice start 92; self_check 4). The loops in text.t27 check every code point below U+3100 against Python's 29 spaces and 10 line breaks.Every function has a negative control (a name outside the gate table, a backwards range, a count of 19 digits, a second newline, a symlink or a file named
x-target, a volume with room, a line that is not a run, atargetthat is not a scratch name).Mutants
59 single-literal or comparison mutants, each run against its own spec's tests, all killed: text 11, paths 12, plan 12, verdict 13, prune 11 (for example
c <= 31toc <= 30,<to<=in the reaper's pressure and idle tests,has_master and builttoor,k < stok <= sin the ls-remote dedupe,star != LAB_NOto==in the glob matcher).Where the port differs from Python, on purpose and pinned (tests say NOT Python)
[z-a]) is REFUSED (LAB_FN_REFUSED,LAB_WATCH_BAD, plan returnsLAB_NO). Python gives such a class no members and a second range after it can move which ends are dropped; the lab's globs (master,claude/t27c-*) have none.summary()takes a pattern id, not a regex: the five patterns of the lab (none and the four ofGATES) are table rows. A sixth is a new row.T27_WATCHitself, one sha per line,branch TAB shaper line); the glob list is theT27_WATCHtext, so a glob has no comma.Things the oracle does that are worth knowing (reproduced, not fixed;
lab.pyis not touched)SHA_REandBRANCH_REend in$, which also accepts one trailing newline, soenqueue("<sha>\n")andenqueue("main\n")are accepted.\s+\Scross lines: a row of three numbers with no description takes the first character of the next line, and that next row is then not counted (" 1 2 3\n 4 5 6 x\n"counts only 1, 2, 3). The real table has a description on every row, so today this is harmless.LAB_KEEP_RUNS=-5is a slice from the end: only the five oldest runs would ever be deleted.Not ported (runtime glue or host probes, left for L3) and not expressible
take_requestsandpoll_once(file reads, the orderasked + queue + planis a composition of functions here),newest_mtime(a file-system walk; the fold ismax),cpu_quotaandmem_gb(cgroup probes;round(x, 1)),refresh_seals(ctypes into the generatedseal_refresh),run_item's JSON records, the HTTP banner and headers, the sha256 file name of an enqueue request..t27. One compiler trap met on the way, filed on its own: an unknown string escape (\r,\0,\v,\f) is kept as a backslash and a letter without a word, so tests build those bytes from[N]u8buffers (the binaries I ran decode\n \t \\ \"and\xNN): t27c lexer: an unknown string escape (\r, \0, \v, \f) is kept as backslash plus letter, silently #11065.Gates (the shared t27c, built 2026-10-19 20:34, and tri, built 00:40)
Boundary kept:
specs/lab/*.t27and.trinity/seals/lab_Lab*.json. No change toinfra/,contrib/,cli/,scripts/,specs/analog/,.claude/ordocs/reports/t27b_expectations.json; no hand-written code in another language.Not part of this PR (the lane's throwaway generators of the tables are not committed).
🤖 Generated with Claude Code