From 0220854ddb0c6516cdf652204a98281bca846e5c Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Sun, 18 Oct 2026 18:44:39 +0700 Subject: [PATCH] t27mcp: tool_schema.t27 -- one row per tool and per field, so the MCP JSON schemas are generated, not written by hand (Closes #10444) 27 tools (26 on t27-mcp master plus t27_paper_claims), 270 field rows, inputs and outputs, with tuple, array, boolean, nested-object, enum, pattern and bounds rows; the spec filters of t27_specs and t27_grep are one group. 12 tests, 0 vacuous, negative controls for step, name, children, bounds, text and flags laws. Its seal hash is the catalog identity a client caches. Co-Authored-By: Claude Sonnet 5.5 --- .trinity/seals/t27mcp_T27mcpToolSchema.json | 28 + specs/t27mcp/tool_schema.t27 | 778 ++++++++++++++++++++ 2 files changed, 806 insertions(+) create mode 100644 .trinity/seals/t27mcp_T27mcpToolSchema.json create mode 100644 specs/t27mcp/tool_schema.t27 diff --git a/.trinity/seals/t27mcp_T27mcpToolSchema.json b/.trinity/seals/t27mcp_T27mcpToolSchema.json new file mode 100644 index 0000000000..f56f27008e --- /dev/null +++ b/.trinity/seals/t27mcp_T27mcpToolSchema.json @@ -0,0 +1,28 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+4ab200073", + "closure": [], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:95f30d70ccb105cbb7b7f84c671f5a047212a01f8036713e3ab0e0c02ab89868", + "gen_hash_rust": "sha256:a645820a69eb3ef5227d5ec8b85cdfafec99361074e1176b095fbbc544cedb10", + "gen_hash_verilog": "sha256:954c78ad915486a96422b25476548b2ac9ef3edd5566e26a42701bc92cccf06f", + "gen_hash_zig": "sha256:8cc223ea3646b426792cfe23287b5ea96920c17e553eb9acd02e32aa52985560", + "module": "T27mcpToolSchema", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-18T11:43:51Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:6997ad63a28002811cd1d1d2bd0d42c734aebe954fd428d1b580322606cb2f81", + "spec_path": "specs/t27mcp/tool_schema.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 12, + "total": 12, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/specs/t27mcp/tool_schema.t27 b/specs/t27mcp/tool_schema.t27 new file mode 100644 index 0000000000..6b1188ec34 --- /dev/null +++ b/specs/t27mcp/tool_schema.t27 @@ -0,0 +1,778 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/t27mcp/tool_schema.t27 -- the input and output schema of every tool of the t27 MCP server +; (gHashTag/t27-mcp), as one table. The JSON Schema a client reads in tools/list is generated from it. +; -- +; Why: until now each tool's inputSchema was written by hand in server.mjs, and no tool declared what it +; returns. A client could not tell an integer from a number (limit, maxSpecs, bits, minLines were all +; `number`), a closed set from free text (health, sort, state, status), or a bounded range from an open +; one (t27f_view scene 0..4, rows at most 4). MCP's schema is plumbing a client parses, so by our own rule +; it is generated from our own source, never hand-written. The idea comes from the morluto/rea 6.6.0 MCP +; contracts fix: advertise self-contained inputs, keep tuple, array, boolean and nested-object constraints +; in every schema profile, and put the output-schema profile into the catalog identity. +; -- +; The table: +; - TOOLS has one row per tool: its name, whether it takes the shared spec filters, and what it returns. +; - FIELDS has one row per schema node, in depth-first order. A row's parent is the nearest earlier row of +; the same tool and side whose depth is one less, so a row is never numbered by hand. +; - An object's children are its properties (each has a name). +; - An array has exactly one child, its item schema (no name). +; - A tuple's children are its positions in order (no name); its length is lo = hi = the child count. +; - A scalar has no children. +; - lo and hi are minLength/maxLength on a string, minimum/maximum on an integer or a number, and +; minItems/maxItems on an array or a tuple. NB means no bound. +; - choices is an enum written "a|b|c"; pattern is an ECMA-262 regular expression. Strings only. +; - Inputs are self-contained: no $ref, no definitions; a top-level input object refuses unknown keys. +; Outputs stay open (a reader must tolerate a new field), and an object with no children is any object: +; its shape belongs to another spec (spec_brief.t27 owns the brief record), not to a second copy here. +; - A tool whose result is text (OUT_TEXT) or arbitrary JSON (OUT_ANY) declares no output schema. A +; schema that claims a shape the result does not have is worse than no schema. +; -- +; Profiles. PROFILE_INPUT advertises inputs only, under protocol 2024-11-05, which has no outputSchema. +; PROFILE_OUTPUT also advertises outputSchema and returns structuredContent, under 2025-06-18. A tuple +; is prefixItems plus minItems and maxItems, so its length holds in a draft-07 reader that ignores +; prefixItems and its positions hold in a 2020-12 reader. The catalog identity a client caches is +; "/p": a change to this table or to the profile changes it. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module T27mcpToolSchema; + +/// Schema kinds, as indices into KIND_JSON. +pub const K_STRING : u32 = 1; +pub const K_INTEGER : u32 = 2; +pub const K_NUMBER : u32 = 3; +pub const K_BOOLEAN : u32 = 4; +pub const K_ARRAY : u32 = 5; +pub const K_TUPLE : u32 = 6; +pub const K_OBJECT : u32 = 7; +pub const K_ANY : u32 = 8; +pub const KIND_COUNT : u32 = 9; +/// The JSON Schema `type` of each kind; "" is no type keyword (any value). +pub const KIND_JSON : [9]str = ["", "string", "integer", "number", "boolean", "array", "array", "object", ""]; + +/// Which side of a call a row describes. +pub const IN : u32 = 0; +pub const OUT : u32 = 1; + +/// What a tool returns. +pub const OUT_OBJECT : u32 = 0; +pub const OUT_TEXT : u32 = 1; +pub const OUT_ANY : u32 = 2; + +/// No bound. +pub const NB : u32 = 4294967295; + +/// The tool column of the shared spec filters, which t27_specs and t27_grep both take. +pub const G_SPEC_FILTERS : u32 = 1000; + +/// Profiles and the protocol version each one answers. +pub const PROFILE_INPUT : u32 = 0; +pub const PROFILE_OUTPUT : u32 = 1; +pub const PROFILE_COUNT : u32 = 2; +pub const DEFAULT_PROFILE : u32 = 0; +pub const PROFILE_ENV : str = "T27_MCP_OUTPUT_PROFILE"; +pub const PROTOCOL : [2]str = ["2024-11-05", "2025-06-18"]; +/// Where tools/list carries the catalog identity, and the vendored seal it reads spec_hash from. +pub const CATALOG_META_KEY : str = "t27/catalog"; +pub const SEAL_FILE : str = "tool_schema.seal.json"; +pub const SPEC_PATH : str = "specs/t27mcp/tool_schema.t27"; + +pub struct Tool { + name: str, + filters: bool, + out: u32, +} + +pub struct Field { + tool: u32, + side: u32, + depth: u32, + name: str, + kind: u32, + req: bool, + nullable: bool, + lo: u32, + hi: u32, + choices: str, + pattern: str, + desc: str, +} + +/// Tools, as indices into TOOLS. +pub const T_STATUS : u32 = 0; +pub const T_FORMATS : u32 = 1; +pub const T_FORMAT : u32 = 2; +pub const T_SPECS : u32 = 3; +pub const T_SPEC : u32 = 4; +pub const T_GREP : u32 = 5; +pub const T_ISSUES : u32 = 6; +pub const T_EPICS : u32 = 7; +pub const T_MODULES : u32 = 8; +pub const T_WORLDS : u32 = 9; +pub const T_RAW : u32 = 10; +pub const T_GAME : u32 = 11; +pub const T_CHECK : u32 = 12; +pub const T_EXAMPLES : u32 = 13; +pub const T_FIND : u32 = 14; +pub const T_VIEW : u32 = 15; +pub const T_OUTLINE : u32 = 16; +pub const T_GRAPH : u32 = 17; +pub const T_DASHBOARD : u32 = 18; +pub const T_PAPER_CLAIMS : u32 = 19; +pub const T_TG_UNANSWERED : u32 = 20; +pub const T_TG_DIALOGS : u32 = 21; +pub const T_TG_HISTORY : u32 = 22; +pub const T_TG_SEARCH : u32 = 23; +pub const T_TG_CONTACTS : u32 = 24; +pub const T_TG_PARTICIPANTS : u32 = 25; +pub const T_TG_SCHEDULED : u32 = 26; +pub const TOOL_COUNT : u32 = 27; + +pub const TOOLS : [27]Tool = [ + Tool { .name = "t27_status", .filters = false, .out = OUT_OBJECT }, + Tool { .name = "t27_formats", .filters = false, .out = OUT_OBJECT }, + Tool { .name = "t27_format", .filters = false, .out = OUT_OBJECT }, + Tool { .name = "t27_specs", .filters = true, .out = OUT_OBJECT }, + Tool { .name = "t27_spec", .filters = false, .out = OUT_OBJECT }, + Tool { .name = "t27_grep", .filters = true, .out = OUT_OBJECT }, + Tool { .name = "t27_issues", .filters = false, .out = OUT_OBJECT }, + Tool { .name = "t27_epics", .filters = false, .out = OUT_OBJECT }, + Tool { .name = "t27_modules", .filters = false, .out = OUT_OBJECT }, + Tool { .name = "t27_worlds", .filters = false, .out = OUT_OBJECT }, + Tool { .name = "t27_raw", .filters = false, .out = OUT_ANY }, + Tool { .name = "t27_game", .filters = false, .out = OUT_OBJECT }, + Tool { .name = "t27_check", .filters = false, .out = OUT_OBJECT }, + Tool { .name = "t27_examples", .filters = false, .out = OUT_OBJECT }, + Tool { .name = "t27_find", .filters = false, .out = OUT_OBJECT }, + Tool { .name = "t27f_view", .filters = false, .out = OUT_TEXT }, + Tool { .name = "t27f_outline", .filters = false, .out = OUT_TEXT }, + Tool { .name = "t27f_graph", .filters = false, .out = OUT_TEXT }, + Tool { .name = "t27_dashboard", .filters = false, .out = OUT_TEXT }, + Tool { .name = "t27_paper_claims", .filters = false, .out = OUT_OBJECT }, + Tool { .name = "tg_unanswered", .filters = false, .out = OUT_TEXT }, + Tool { .name = "tg_dialogs", .filters = false, .out = OUT_TEXT }, + Tool { .name = "tg_history", .filters = false, .out = OUT_TEXT }, + Tool { .name = "tg_search", .filters = false, .out = OUT_TEXT }, + Tool { .name = "tg_contacts", .filters = false, .out = OUT_TEXT }, + Tool { .name = "tg_participants", .filters = false, .out = OUT_TEXT }, + Tool { .name = "tg_scheduled", .filters = false, .out = OUT_TEXT } +]; + +/// Descriptions more than one row shares. +pub const D_FRESH : str = "Bypass the 5-minute cache"; +pub const D_REF : str = "Git ref to read; default master"; +pub const D_PATH : str = "Exact corpus path, e.g. specs/tutorial/08_modules_and_arrays.t27"; +pub const D_OUT_FILE : str = "Output file (default: the temp directory)"; +pub const D_BRIEF : str = "The spec's brief record (specs/t27mcp/spec_brief.t27 owns its fields)"; +/// A corpus path to a .t27 file, as server.mjs specPath() accepts it. +pub const P_T27_PATH : str = "^[A-Za-z0-9_./-]+[.]t27$"; +/// The chat rule of telegram.t27: an optional minus and 1 to 20 digits, or an optional @ and 5 to 32 word characters. +pub const P_TG_CHAT : str = "^(-?[0-9]{1,20}|@?[A-Za-z0-9_]{5,32})$"; +pub const D_TG_CHAT : str = "A chat id (digits, maybe a leading minus) or @username, as tg_dialogs prints it"; +pub const D_TG_QUERY : str = "Words to find in message text"; +pub const D_TG_LIMIT : str = "How many; default 25, at most 100"; + +pub const FIELD_COUNT : u32 = 270; + +pub const FIELDS : [270]Field = [ + // t27_status + Field { .tool = T_STATUS, .side = IN, .depth = 0, .name = "fresh", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_FRESH }, + Field { .tool = T_STATUS, .side = OUT, .depth = 0, .name = "site", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "The site every other answer is read from" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 0, .name = "serverVersion", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "This server's version" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 0, .name = "generatedFrom", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "The manifest's provenance: source commit and build time" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 0, .name = "specCount", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 0, .name = "totalLines", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 0, .name = "wasmBytes", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 0, .name = "health", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Specs per health: ok, warn, fail" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 0, .name = "failingSpecs", .kind = K_ARRAY, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 1, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_BRIEF }, + Field { .tool = T_STATUS, .side = OUT, .depth = 0, .name = "backendFailures", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 0, .name = "categoryCount", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 0, .name = "topTags", .kind = K_OBJECT, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "The 12 biggest tags with their spec counts" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 0, .name = "totals", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 0, .name = "repos", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 0, .name = "atlas", .kind = K_OBJECT, .req = true, .nullable = true, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Atlas summary, or null when the atlas could not be read; atlasError says why" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 1, .name = "version", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 1, .name = "at", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 1, .name = "issues", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 1, .name = "specs", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 1, .name = "worlds", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 1, .name = "opportunities", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_STATUS, .side = OUT, .depth = 0, .name = "atlasError", .kind = K_STRING, .req = true, .nullable = true, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Why the atlas could not be read, or null" }, + // t27_formats + Field { .tool = T_FORMATS, .side = IN, .depth = 0, .name = "cluster", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "e.g. GoldenFloat, Ieee754Binary, Microscaling" }, + Field { .tool = T_FORMATS, .side = IN, .depth = 0, .name = "status", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "Verified|Historical|Experimental|Open_conjecture|Retracted", .pattern = "", .desc = "Catalog status" }, + Field { .tool = T_FORMATS, .side = IN, .depth = 0, .name = "bits", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 1, .hi = NB, .choices = "", .pattern = "", .desc = "Width in bits" }, + Field { .tool = T_FORMATS, .side = IN, .depth = 0, .name = "name", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Substring match on id or name" }, + Field { .tool = T_FORMATS, .side = IN, .depth = 0, .name = "fields", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Return full rows instead of a summary (default false)" }, + Field { .tool = T_FORMATS, .side = IN, .depth = 0, .name = "ref", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_REF }, + Field { .tool = T_FORMATS, .side = IN, .depth = 0, .name = "fresh", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_FRESH }, + Field { .tool = T_FORMATS, .side = OUT, .depth = 0, .name = "ref", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_FORMATS, .side = OUT, .depth = 0, .name = "source", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "URL of formats_catalog.t27 at that ref" }, + Field { .tool = T_FORMATS, .side = OUT, .depth = 0, .name = "totalInCatalog", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_FORMATS, .side = OUT, .depth = 0, .name = "matched", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_FORMATS, .side = OUT, .depth = 0, .name = "clusters", .kind = K_OBJECT, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Formats per cluster" }, + Field { .tool = T_FORMATS, .side = OUT, .depth = 0, .name = "statuses", .kind = K_OBJECT, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Formats per status" }, + Field { .tool = T_FORMATS, .side = OUT, .depth = 0, .name = "formats", .kind = K_ARRAY, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_FORMATS, .side = OUT, .depth = 1, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "A catalog row: id, name, bits, cluster, status; every field with fields:true" }, + // t27_format: the answer is the catalog row itself, any object + Field { .tool = T_FORMAT, .side = IN, .depth = 0, .name = "id", .kind = K_STRING, .req = true, .nullable = false, .lo = 1, .hi = NB, .choices = "", .pattern = "", .desc = "Catalog id, e.g. gf16, tnf8, binary16" }, + Field { .tool = T_FORMAT, .side = IN, .depth = 0, .name = "ref", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_REF }, + Field { .tool = T_FORMAT, .side = IN, .depth = 0, .name = "fresh", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_FRESH }, + // t27_specs (and the shared spec filters, G_SPEC_FILTERS) + Field { .tool = T_SPECS, .side = IN, .depth = 0, .name = "sort", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "path|lines|tokens|nodes|loss|tcErrors|depth|bytes", .pattern = "", .desc = "Sort key (default path)" }, + Field { .tool = T_SPECS, .side = IN, .depth = 0, .name = "desc", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Sort descending (default true for numeric sorts)" }, + Field { .tool = T_SPECS, .side = IN, .depth = 0, .name = "limit", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 1, .hi = NB, .choices = "", .pattern = "", .desc = "Default 50" }, + Field { .tool = T_SPECS, .side = IN, .depth = 0, .name = "fields", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Return the full manifest record per spec" }, + Field { .tool = T_SPECS, .side = IN, .depth = 0, .name = "fresh", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_FRESH }, + Field { .tool = T_SPECS, .side = OUT, .depth = 0, .name = "commit", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Short source commit of the manifest" }, + Field { .tool = T_SPECS, .side = OUT, .depth = 0, .name = "totalInCorpus", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_SPECS, .side = OUT, .depth = 0, .name = "matched", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_SPECS, .side = OUT, .depth = 0, .name = "health", .kind = K_OBJECT, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Matched specs per health" }, + Field { .tool = T_SPECS, .side = OUT, .depth = 0, .name = "repos", .kind = K_OBJECT, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Matched specs per repository" }, + Field { .tool = T_SPECS, .side = OUT, .depth = 0, .name = "categories", .kind = K_OBJECT, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "The 12 biggest categories of the matched specs" }, + Field { .tool = T_SPECS, .side = OUT, .depth = 0, .name = "specs", .kind = K_ARRAY, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_SPECS, .side = OUT, .depth = 1, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_BRIEF }, + // t27_spec + Field { .tool = T_SPEC, .side = IN, .depth = 0, .name = "path", .kind = K_STRING, .req = true, .nullable = false, .lo = 1, .hi = NB, .choices = "", .pattern = "", .desc = "Exact corpus path, e.g. specs/demos/hello_world.t27" }, + Field { .tool = T_SPEC, .side = IN, .depth = 0, .name = "source", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Include the .t27 source text (default false)" }, + Field { .tool = T_SPEC, .side = IN, .depth = 0, .name = "fields", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Return the whole manifest record (default false)" }, + Field { .tool = T_SPEC, .side = IN, .depth = 0, .name = "fresh", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_FRESH }, + Field { .tool = T_SPEC, .side = OUT, .depth = 0, .name = "path", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_SPEC, .side = OUT, .depth = 0, .name = "sourceUrl", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Where the .t27 source is published" }, + Field { .tool = T_SPEC, .side = OUT, .depth = 0, .name = "source", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "The .t27 source text, with source:true" }, + // t27_grep + Field { .tool = T_GREP, .side = IN, .depth = 0, .name = "pattern", .kind = K_STRING, .req = true, .nullable = false, .lo = 1, .hi = NB, .choices = "", .pattern = "", .desc = "JavaScript regular expression, e.g. \"fn\\s+requant\"" }, + Field { .tool = T_GREP, .side = IN, .depth = 0, .name = "ignoreCase", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Default true" }, + Field { .tool = T_GREP, .side = IN, .depth = 0, .name = "maxSpecs", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 1, .hi = 150, .choices = "", .pattern = "", .desc = "Candidate specs to open, default 40, hard cap 150" }, + Field { .tool = T_GREP, .side = IN, .depth = 0, .name = "maxHitsPerSpec", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 1, .hi = NB, .choices = "", .pattern = "", .desc = "Default 5" }, + Field { .tool = T_GREP, .side = IN, .depth = 0, .name = "fresh", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_FRESH }, + Field { .tool = T_GREP, .side = OUT, .depth = 0, .name = "pattern", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GREP, .side = OUT, .depth = 0, .name = "candidates", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "Specs the filters matched" }, + Field { .tool = T_GREP, .side = OUT, .depth = 0, .name = "opened", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = 150, .choices = "", .pattern = "", .desc = "Specs read" }, + Field { .tool = T_GREP, .side = OUT, .depth = 0, .name = "truncated", .kind = K_BOOLEAN, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "More candidates than were opened" }, + Field { .tool = T_GREP, .side = OUT, .depth = 0, .name = "specsWithHits", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GREP, .side = OUT, .depth = 0, .name = "totalHits", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GREP, .side = OUT, .depth = 0, .name = "errors", .kind = K_ARRAY, .req = false, .nullable = false, .lo = 1, .hi = NB, .choices = "", .pattern = "", .desc = "Specs that could not be read; absent when none" }, + Field { .tool = T_GREP, .side = OUT, .depth = 1, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GREP, .side = OUT, .depth = 2, .name = "path", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GREP, .side = OUT, .depth = 2, .name = "error", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GREP, .side = OUT, .depth = 0, .name = "results", .kind = K_ARRAY, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GREP, .side = OUT, .depth = 1, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GREP, .side = OUT, .depth = 2, .name = "path", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GREP, .side = OUT, .depth = 2, .name = "repo", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GREP, .side = OUT, .depth = 2, .name = "health", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GREP, .side = OUT, .depth = 2, .name = "hits", .kind = K_ARRAY, .req = true, .nullable = false, .lo = 1, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GREP, .side = OUT, .depth = 3, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GREP, .side = OUT, .depth = 4, .name = "line", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 1, .hi = NB, .choices = "", .pattern = "", .desc = "1-based line number" }, + Field { .tool = T_GREP, .side = OUT, .depth = 4, .name = "text", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = 200, .choices = "", .pattern = "", .desc = "The trimmed line, at most 200 characters" }, + // t27_issues + Field { .tool = T_ISSUES, .side = IN, .depth = 0, .name = "query", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Substring of the issue title" }, + Field { .tool = T_ISSUES, .side = IN, .depth = 0, .name = "repo", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Substring of the repository name" }, + Field { .tool = T_ISSUES, .side = IN, .depth = 0, .name = "spec", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Only issues linked to a spec whose id starts with, or path contains, this" }, + Field { .tool = T_ISSUES, .side = IN, .depth = 0, .name = "detail", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Include the spec links behind each issue" }, + Field { .tool = T_ISSUES, .side = IN, .depth = 0, .name = "limit", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 1, .hi = NB, .choices = "", .pattern = "", .desc = "Default 40" }, + Field { .tool = T_ISSUES, .side = IN, .depth = 0, .name = "fresh", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_FRESH }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 0, .name = "at", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "When the atlas was built" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 0, .name = "total", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 0, .name = "matched", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 0, .name = "issues", .kind = K_ARRAY, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 1, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 2, .name = "key", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 2, .name = "repo", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 2, .name = "number", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 1, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 2, .name = "title", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 2, .name = "specHits", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 2, .name = "links", .kind = K_ARRAY, .req = false, .nullable = false, .lo = 0, .hi = 5, .choices = "", .pattern = "", .desc = "With detail:true, the first 5 spec links" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 3, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 4, .name = "specId", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = 12, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 4, .name = "relation", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 4, .name = "coverage", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 4, .name = "score", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 4, .name = "reasons", .kind = K_ARRAY, .req = true, .nullable = false, .lo = 0, .hi = 3, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_ISSUES, .side = OUT, .depth = 5, .name = "", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + // t27_epics + Field { .tool = T_EPICS, .side = IN, .depth = 0, .name = "state", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "open|closed", .pattern = "", .desc = "" }, + Field { .tool = T_EPICS, .side = IN, .depth = 0, .name = "limit", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 1, .hi = NB, .choices = "", .pattern = "", .desc = "Default 30" }, + Field { .tool = T_EPICS, .side = IN, .depth = 0, .name = "fresh", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_FRESH }, + Field { .tool = T_EPICS, .side = OUT, .depth = 0, .name = "repo", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EPICS, .side = OUT, .depth = 0, .name = "generatedAt", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EPICS, .side = OUT, .depth = 0, .name = "rule", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EPICS, .side = OUT, .depth = 0, .name = "closedIssues", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EPICS, .side = OUT, .depth = 0, .name = "releases", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EPICS, .side = OUT, .depth = 0, .name = "matched", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EPICS, .side = OUT, .depth = 0, .name = "epics", .kind = K_ARRAY, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EPICS, .side = OUT, .depth = 1, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EPICS, .side = OUT, .depth = 2, .name = "number", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 1, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EPICS, .side = OUT, .depth = 2, .name = "title", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EPICS, .side = OUT, .depth = 2, .name = "state", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EPICS, .side = OUT, .depth = 2, .name = "ring", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EPICS, .side = OUT, .depth = 2, .name = "children", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "Closed-issue children" }, + Field { .tool = T_EPICS, .side = OUT, .depth = 2, .name = "closedAt", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + // t27_modules + Field { .tool = T_MODULES, .side = IN, .depth = 0, .name = "query", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Substring of the module path" }, + Field { .tool = T_MODULES, .side = IN, .depth = 0, .name = "limit", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 1, .hi = NB, .choices = "", .pattern = "", .desc = "Default 40" }, + Field { .tool = T_MODULES, .side = IN, .depth = 0, .name = "fresh", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_FRESH }, + Field { .tool = T_MODULES, .side = OUT, .depth = 0, .name = "repo", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_MODULES, .side = OUT, .depth = 0, .name = "commit", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_MODULES, .side = OUT, .depth = 0, .name = "generatedAt", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_MODULES, .side = OUT, .depth = 0, .name = "totalModules", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_MODULES, .side = OUT, .depth = 0, .name = "matched", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_MODULES, .side = OUT, .depth = 0, .name = "modules", .kind = K_ARRAY, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_MODULES, .side = OUT, .depth = 1, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "A row of the queen modules feed" }, + // t27_worlds + Field { .tool = T_WORLDS, .side = IN, .depth = 0, .name = "opportunities", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Include the opportunity list" }, + Field { .tool = T_WORLDS, .side = IN, .depth = 0, .name = "limit", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 1, .hi = NB, .choices = "", .pattern = "", .desc = "Opportunities to list, default 25" }, + Field { .tool = T_WORLDS, .side = IN, .depth = 0, .name = "fresh", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_FRESH }, + Field { .tool = T_WORLDS, .side = OUT, .depth = 0, .name = "at", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_WORLDS, .side = OUT, .depth = 0, .name = "owners", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_WORLDS, .side = OUT, .depth = 0, .name = "worlds", .kind = K_ARRAY, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_WORLDS, .side = OUT, .depth = 1, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_WORLDS, .side = OUT, .depth = 2, .name = "repo", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_WORLDS, .side = OUT, .depth = 2, .name = "owner", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_WORLDS, .side = OUT, .depth = 2, .name = "archived", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_WORLDS, .side = OUT, .depth = 2, .name = "description", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_WORLDS, .side = OUT, .depth = 0, .name = "opportunityCount", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_WORLDS, .side = OUT, .depth = 0, .name = "opportunities", .kind = K_ARRAY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "With opportunities:true" }, + Field { .tool = T_WORLDS, .side = OUT, .depth = 1, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "An opportunity row of the atlas" }, + // t27_raw: any JSON under the site, so no output schema + Field { .tool = T_RAW, .side = IN, .depth = 0, .name = "path", .kind = K_STRING, .req = true, .nullable = false, .lo = 2, .hi = NB, .choices = "", .pattern = "^/[^/]", .desc = "A site path with one leading slash, e.g. /queen/modules.json" }, + Field { .tool = T_RAW, .side = IN, .depth = 0, .name = "fresh", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_FRESH }, + // t27_game + Field { .tool = T_GAME, .side = IN, .depth = 0, .name = "fresh", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_FRESH }, + Field { .tool = T_GAME, .side = OUT, .depth = 0, .name = "board", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "The board's URL" }, + Field { .tool = T_GAME, .side = OUT, .depth = 0, .name = "rules", .kind = K_OBJECT, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 1, .name = "winCondition", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 1, .name = "cycle", .kind = K_TUPLE, .req = true, .nullable = false, .lo = 5, .hi = 5, .choices = "", .pattern = "", .desc = "The five steps of a cell, in order" }, + Field { .tool = T_GAME, .side = OUT, .depth = 2, .name = "", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "Issue", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 2, .name = "", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "Spec", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 2, .name = "", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "Bee", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 2, .name = "", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "Review", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 2, .name = "", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "Evidence", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 1, .name = "firstCampaign", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 1, .name = "colourLaw", .kind = K_OBJECT, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Each board colour and what it means" }, + Field { .tool = T_GAME, .side = OUT, .depth = 1, .name = "honestyRule", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 0, .name = "board_state", .kind = K_OBJECT, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 1, .name = "commit", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 1, .name = "specs", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 1, .name = "health", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 1, .name = "failing", .kind = K_ARRAY, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 2, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 3, .name = "path", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 3, .name = "why", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 1, .name = "lossAffected", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 1, .name = "tcAffected", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 1, .name = "repos", .kind = K_ARRAY, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 2, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 3, .name = "repo", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 3, .name = "specs", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 1, .name = "epics", .kind = K_INTEGER, .req = true, .nullable = true, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "null when the foundation feed could not be read" }, + Field { .tool = T_GAME, .side = OUT, .depth = 1, .name = "openEpics", .kind = K_INTEGER, .req = true, .nullable = true, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "null when the foundation feed could not be read" }, + Field { .tool = T_GAME, .side = OUT, .depth = 1, .name = "foundationError", .kind = K_STRING, .req = true, .nullable = true, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_GAME, .side = OUT, .depth = 0, .name = "howToPlay", .kind = K_OBJECT, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Numbered steps for a new player" }, + // t27_check + Field { .tool = T_CHECK, .side = IN, .depth = 0, .name = "source", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "The .t27 source text" }, + Field { .tool = T_CHECK, .side = OUT, .depth = 0, .name = "ok", .kind = K_BOOLEAN, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Parsed and typechecked; names are not resolved and no test ran" }, + Field { .tool = T_CHECK, .side = OUT, .depth = 0, .name = "errors", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_CHECK, .side = OUT, .depth = 0, .name = "warnings", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_CHECK, .side = OUT, .depth = 0, .name = "diagnostics", .kind = K_ARRAY, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_CHECK, .side = OUT, .depth = 1, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_CHECK, .side = OUT, .depth = 2, .name = "severity", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "error|warning", .pattern = "", .desc = "" }, + Field { .tool = T_CHECK, .side = OUT, .depth = 2, .name = "line", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 1, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_CHECK, .side = OUT, .depth = 2, .name = "message", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_CHECK, .side = OUT, .depth = 2, .name = "hint", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "What to write instead" }, + Field { .tool = T_CHECK, .side = OUT, .depth = 2, .name = "source", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "The source line" }, + // t27_examples: a topic's examples, or the list of topics on a miss + Field { .tool = T_EXAMPLES, .side = IN, .depth = 0, .name = "topic", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "e.g. \"clocked counter\", \"comb\", \"fn\", \"test\", \"async reset\", \"no reset\"" }, + Field { .tool = T_EXAMPLES, .side = IN, .depth = 0, .name = "fresh", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_FRESH }, + Field { .tool = T_EXAMPLES, .side = OUT, .depth = 0, .name = "topic", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EXAMPLES, .side = OUT, .depth = 0, .name = "pin", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "The commit the examples are read at" }, + Field { .tool = T_EXAMPLES, .side = OUT, .depth = 0, .name = "examples", .kind = K_ARRAY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EXAMPLES, .side = OUT, .depth = 1, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EXAMPLES, .side = OUT, .depth = 2, .name = "path", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EXAMPLES, .side = OUT, .depth = 2, .name = "lines", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "^[0-9]+-[0-9]+$", .desc = "" }, + Field { .tool = T_EXAMPLES, .side = OUT, .depth = 2, .name = "why", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EXAMPLES, .side = OUT, .depth = 2, .name = "source", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EXAMPLES, .side = OUT, .depth = 0, .name = "topics", .kind = K_ARRAY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "On a miss, the topics there are" }, + Field { .tool = T_EXAMPLES, .side = OUT, .depth = 1, .name = "", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_EXAMPLES, .side = OUT, .depth = 0, .name = "note", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + // t27_find + Field { .tool = T_FIND, .side = IN, .depth = 0, .name = "intent", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "What the module does, e.g. \"clocked counter with enable\"" }, + Field { .tool = T_FIND, .side = IN, .depth = 0, .name = "limit", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 1, .hi = 20, .choices = "", .pattern = "", .desc = "Default 5, at most 20" }, + Field { .tool = T_FIND, .side = IN, .depth = 0, .name = "fresh", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_FRESH }, + Field { .tool = T_FIND, .side = OUT, .depth = 0, .name = "intent", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_FIND, .side = OUT, .depth = 0, .name = "miss", .kind = K_BOOLEAN, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "No spec fits: write one, with tests" }, + Field { .tool = T_FIND, .side = OUT, .depth = 0, .name = "matched", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_FIND, .side = OUT, .depth = 0, .name = "skipped", .kind = K_ANY, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Matches left out as unhealthy or untested" }, + Field { .tool = T_FIND, .side = OUT, .depth = 0, .name = "note", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_FIND, .side = OUT, .depth = 0, .name = "hits", .kind = K_ARRAY, .req = true, .nullable = false, .lo = 0, .hi = 20, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_FIND, .side = OUT, .depth = 1, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_BRIEF }, + Field { .tool = T_FIND, .side = OUT, .depth = 2, .name = "next", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "The t27_spec call that reads it" }, + // t27f_view: answers with the file path and a note, as text + Field { .tool = T_VIEW, .side = IN, .depth = 0, .name = "scene", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = 4, .choices = "", .pattern = "", .desc = "Demo scene 0..4; its still frame is drawn (default 2, the focus still)" }, + Field { .tool = T_VIEW, .side = IN, .depth = 0, .name = "frame", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "Exact frame instead of a scene still" }, + Field { .tool = T_VIEW, .side = IN, .depth = 0, .name = "preset", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = 2, .choices = "", .pattern = "", .desc = "0 wide 1280x720 (default), 1 square 1080x1080, 2 tall 1080x1920" }, + Field { .tool = T_VIEW, .side = IN, .depth = 0, .name = "out", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_OUT_FILE }, + Field { .tool = T_VIEW, .side = IN, .depth = 0, .name = "status", .kind = K_ARRAY, .req = false, .nullable = false, .lo = NB, .hi = 27, .choices = "", .pattern = "", .desc = "Lesson status code per cell (27); a missing one is a gap" }, + Field { .tool = T_VIEW, .side = IN, .depth = 1, .name = "", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = 255, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_VIEW, .side = IN, .depth = 0, .name = "labels", .kind = K_ARRAY, .req = false, .nullable = false, .lo = NB, .hi = 27, .choices = "", .pattern = "", .desc = "Label per cell (default: the cell number)" }, + Field { .tool = T_VIEW, .side = IN, .depth = 1, .name = "", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_VIEW, .side = IN, .depth = 0, .name = "host", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "Host code for the channel line (default 1, Claude Code desktop)" }, + Field { .tool = T_VIEW, .side = IN, .depth = 0, .name = "look", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = 1, .choices = "", .pattern = "", .desc = "1 deck slide (default), 0 plain map" }, + Field { .tool = T_VIEW, .side = IN, .depth = 0, .name = "title", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Slide headline, at most 2 lines (default: the scene name)" }, + Field { .tool = T_VIEW, .side = IN, .depth = 0, .name = "subtitle", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Cover subtitle" }, + Field { .tool = T_VIEW, .side = IN, .depth = 0, .name = "rows", .kind = K_ARRAY, .req = false, .nullable = false, .lo = NB, .hi = 4, .choices = "", .pattern = "", .desc = "Card rows on a split slide, at most 4" }, + Field { .tool = T_VIEW, .side = IN, .depth = 1, .name = "", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + // t27f_outline, t27f_graph + Field { .tool = T_OUTLINE, .side = IN, .depth = 0, .name = "path", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = P_T27_PATH, .desc = D_PATH }, + Field { .tool = T_OUTLINE, .side = IN, .depth = 0, .name = "fresh", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_FRESH }, + Field { .tool = T_GRAPH, .side = IN, .depth = 0, .name = "path", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = P_T27_PATH, .desc = D_PATH }, + Field { .tool = T_GRAPH, .side = IN, .depth = 0, .name = "fixture", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = 1, .choices = "", .pattern = "", .desc = "0 tutorial 08 (default), 1 with a suggestion" }, + Field { .tool = T_GRAPH, .side = IN, .depth = 0, .name = "out", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_OUT_FILE }, + Field { .tool = T_GRAPH, .side = IN, .depth = 0, .name = "fresh", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = D_FRESH }, + // t27_dashboard takes nothing and answers text + // t27_paper_claims (gHashTag/t27-mcp#23) + Field { .tool = T_PAPER_CLAIMS, .side = IN, .depth = 0, .name = "paper", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "all|gf|ruler|tnf|repo", .pattern = "", .desc = "Which paper (default all)" }, + Field { .tool = T_PAPER_CLAIMS, .side = IN, .depth = 0, .name = "verdict", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "all|fits|misfit|unchecked", .pattern = "", .desc = "Which verdict (default all)" }, + Field { .tool = T_PAPER_CLAIMS, .side = OUT, .depth = 0, .name = "spec", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_PAPER_CLAIMS, .side = OUT, .depth = 0, .name = "totals", .kind = K_OBJECT, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Rows per verdict" }, + Field { .tool = T_PAPER_CLAIMS, .side = OUT, .depth = 0, .name = "rows", .kind = K_ARRAY, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_PAPER_CLAIMS, .side = OUT, .depth = 1, .name = "", .kind = K_OBJECT, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_PAPER_CLAIMS, .side = OUT, .depth = 2, .name = "row", .kind = K_INTEGER, .req = true, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_PAPER_CLAIMS, .side = OUT, .depth = 2, .name = "paper", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_PAPER_CLAIMS, .side = OUT, .depth = 2, .name = "where", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_PAPER_CLAIMS, .side = OUT, .depth = 2, .name = "claim", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_PAPER_CLAIMS, .side = OUT, .depth = 2, .name = "printed", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + Field { .tool = T_PAPER_CLAIMS, .side = OUT, .depth = 2, .name = "recomputed", .kind = K_STRING, .req = true, .nullable = true, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "null when the row is not checkable" }, + Field { .tool = T_PAPER_CLAIMS, .side = OUT, .depth = 2, .name = "verdict", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "" }, + // tg_*: the chat and limit rules are telegram.t27's; the reply is text + Field { .tool = T_TG_UNANSWERED, .side = IN, .depth = 0, .name = "limit", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = 100, .choices = "", .pattern = "", .desc = D_TG_LIMIT }, + Field { .tool = T_TG_DIALOGS, .side = IN, .depth = 0, .name = "limit", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = 100, .choices = "", .pattern = "", .desc = D_TG_LIMIT }, + Field { .tool = T_TG_HISTORY, .side = IN, .depth = 0, .name = "chat", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = P_TG_CHAT, .desc = D_TG_CHAT }, + Field { .tool = T_TG_HISTORY, .side = IN, .depth = 0, .name = "limit", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = 100, .choices = "", .pattern = "", .desc = D_TG_LIMIT }, + Field { .tool = T_TG_SEARCH, .side = IN, .depth = 0, .name = "query", .kind = K_STRING, .req = true, .nullable = false, .lo = 1, .hi = 200, .choices = "", .pattern = "", .desc = D_TG_QUERY }, + Field { .tool = T_TG_PARTICIPANTS, .side = IN, .depth = 0, .name = "chat", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = P_TG_CHAT, .desc = D_TG_CHAT }, + Field { .tool = T_TG_PARTICIPANTS, .side = IN, .depth = 0, .name = "limit", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = 100, .choices = "", .pattern = "", .desc = D_TG_LIMIT }, + Field { .tool = T_TG_SCHEDULED, .side = IN, .depth = 0, .name = "chat", .kind = K_STRING, .req = true, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = P_TG_CHAT, .desc = D_TG_CHAT }, + Field { .tool = T_TG_SCHEDULED, .side = IN, .depth = 0, .name = "limit", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = 100, .choices = "", .pattern = "", .desc = D_TG_LIMIT }, + // The shared spec filters of t27_specs and t27_grep + Field { .tool = G_SPEC_FILTERS, .side = IN, .depth = 0, .name = "query", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Substring of path, name, module, description or summary" }, + Field { .tool = G_SPEC_FILTERS, .side = IN, .depth = 0, .name = "repo", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "t27 | trinity-fpga | tri-net | trios | tt-trinity-euler | ..." }, + Field { .tool = G_SPEC_FILTERS, .side = IN, .depth = 0, .name = "category", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "e.g. specs/numeric, specs/ml, specs/fpga" }, + Field { .tool = G_SPEC_FILTERS, .side = IN, .depth = 0, .name = "health", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "ok|warn|fail", .pattern = "", .desc = "Compile health" }, + Field { .tool = G_SPEC_FILTERS, .side = IN, .depth = 0, .name = "tag", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Exact tag, e.g. has/invariants, domain/numeric, size/large" }, + Field { .tool = G_SPEC_FILTERS, .side = IN, .depth = 0, .name = "failedBackend", .kind = K_STRING, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Specs a named backend refused, e.g. verilog_hir" }, + Field { .tool = G_SPEC_FILTERS, .side = IN, .depth = 0, .name = "hasLoss", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Specs with round-trip loss > 0" }, + Field { .tool = G_SPEC_FILTERS, .side = IN, .depth = 0, .name = "hasTcErrors", .kind = K_BOOLEAN, .req = false, .nullable = false, .lo = NB, .hi = NB, .choices = "", .pattern = "", .desc = "Specs with type-check errors > 0" }, + Field { .tool = G_SPEC_FILTERS, .side = IN, .depth = 0, .name = "minLines", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "At least this many lines" }, + Field { .tool = G_SPEC_FILTERS, .side = IN, .depth = 0, .name = "maxLines", .kind = K_INTEGER, .req = false, .nullable = false, .lo = 0, .hi = NB, .choices = "", .pattern = "", .desc = "At most this many lines" } +]; + +/// The laws of one step from the previous row to this one, in the same block. A first row is depth 0; a row +/// is at most one deeper than the row before, and only under a container; a row may climb out any distance. +pub fn step_ok(first: bool, prev_depth: u32, prev_kind: u32, depth: u32) -> bool { + if (first) { return depth == 0; } + if (depth == prev_depth + 1) { return prev_kind == K_ARRAY or prev_kind == K_TUPLE or prev_kind == K_OBJECT; } + return depth <= prev_depth; +} + +/// A child's name against its parent: an object's children are named properties; array items and tuple +/// positions are unnamed. A top-level row (parent 0, no container) is a named property of the tool's object. +pub fn name_ok(parent_kind: u32, named: bool) -> bool { + if (parent_kind == K_ARRAY or parent_kind == K_TUPLE) { return named == false; } + return named; +} + +/// How many children a container may have: an array exactly one (its item schema), a tuple exactly lo = hi, +/// a scalar or an any none, an object any number (none means any object). +pub fn children_ok(kind: u32, lo: u32, hi: u32, children: u32) -> bool { + if (kind == K_ARRAY) { return children == 1; } + if (kind == K_TUPLE) { return lo == hi and children == lo and children > 0; } + if (kind == K_OBJECT) { return true; } + return children == 0; +} + +/// lo and hi apply to strings, numbers, integers, arrays and tuples; when both are set, lo <= hi. +pub fn bounds_ok(kind: u32, lo: u32, hi: u32) -> bool { + let bounded : bool = kind == K_STRING or kind == K_INTEGER or kind == K_NUMBER or kind == K_ARRAY or kind == K_TUPLE; + if (bounded == false) { return lo == NB and hi == NB; } + if (lo != NB and hi != NB) { return lo <= hi; } + return true; +} + +/// choices and pattern are string constraints only. +pub fn text_ok(kind: u32, has_choices: bool, has_pattern: bool) -> bool { + if (kind == K_STRING) { return true; } + return has_choices == false and has_pattern == false; +} + +/// required means something only for a named property; nullable only on an output. +pub fn flags_ok(side: u32, named: bool, req: bool, nullable: bool) -> bool { + if (req and named == false) { return false; } + if (nullable and side == IN) { return false; } + return true; +} + +/// Index of the parent row of row i, or NB for a top-level row: the nearest earlier row of the same block +/// one level up. This is the walk the builder in the server does with a stack. +pub fn parent_of(i: u32) -> u32 { + let f : Field = FIELDS[i]; + if (f.depth == 0) { return NB; } + var j : u32 = i; + while (j > 0) { + j = j - 1; + let p : Field = FIELDS[j]; + if (p.tool != f.tool or p.side != f.side) { return NB; } + if (p.depth + 1 == f.depth) { return j; } + } + return NB; +} + +/// Rows of row i's block one level below it. +pub fn child_count(i: u32) -> u32 { + var n : u32 = 0; + var j : u32 = i + 1; + while (j < FIELD_COUNT) { + let c : Field = FIELDS[j]; + if (c.tool != FIELDS[i].tool or c.side != FIELDS[i].side or c.depth <= FIELDS[i].depth) { return n; } + if (c.depth == FIELDS[i].depth + 1) { n = n + 1; } + j = j + 1; + } + return n; +} + +/// Rows of one tool and side. +pub fn rows_of(tool: u32, side: u32) -> u32 { + var n : u32 = 0; + var i : u32 = 0; + while (i < FIELD_COUNT) { + if (FIELDS[i].tool == tool and FIELDS[i].side == side) { n = n + 1; } + i = i + 1; + } + return n; +} + +test "the tables are the size they say, with 27 tools and their names" { + assert TOOL_COUNT == 27; + assert FIELD_COUNT == 270; + assert TOOLS[T_STATUS].name == "t27_status"; + assert TOOLS[T_CHECK].name == "t27_check"; + assert TOOLS[T_VIEW].name == "t27f_view"; + assert TOOLS[T_PAPER_CLAIMS].name == "t27_paper_claims"; + assert TOOLS[T_TG_SCHEDULED].name == "tg_scheduled"; + assert KIND_JSON[K_INTEGER] == "integer"; + assert KIND_JSON[K_TUPLE] == "array"; + assert KIND_JSON[K_ANY].len == 0; + assert PROTOCOL[PROFILE_INPUT] == "2024-11-05"; + assert PROTOCOL[PROFILE_OUTPUT] == "2025-06-18"; + assert DEFAULT_PROFILE == PROFILE_INPUT; +} + +test "every tool name is unique and has a known prefix" { + var i : u32 = 0; + while (i < TOOL_COUNT) { + let t : Tool = TOOLS[i]; + assert t.name.len > 3; + let t27 : bool = t.name[0] == 't' and t.name[1] == '2' and t.name[2] == '7'; + let tg : bool = t.name[0] == 't' and t.name[1] == 'g' and t.name[2] == '_'; + assert t27 or tg; + var j : u32 = i + 1; + while (j < TOOL_COUNT) { + assert (TOOLS[j].name == t.name) == false; + j = j + 1; + } + i = i + 1; + } +} + +test "every field row belongs to a tool or to the spec filters, and each block is contiguous" { + var i : u32 = 0; + while (i < FIELD_COUNT) { + let f : Field = FIELDS[i]; + assert f.tool < TOOL_COUNT or f.tool == G_SPEC_FILTERS; + assert f.side == IN or f.side == OUT; + assert f.kind >= K_STRING and f.kind < KIND_COUNT; + if (i > 0) { + let p : Field = FIELDS[i - 1]; + if (p.tool != f.tool or p.side != f.side) { + // A new block: it must not have appeared before. + var j : u32 = 0; + while (j < i) { + assert (FIELDS[j].tool == f.tool and FIELDS[j].side == f.side) == false; + j = j + 1; + } + } + } + i = i + 1; + } +} + +test "every row is well formed: depth steps, names, children, bounds, text constraints, flags" { + var i : u32 = 0; + while (i < FIELD_COUNT) { + let f : Field = FIELDS[i]; + var first : bool = i == 0; + var prev_depth : u32 = 0; + var prev_kind : u32 = 0; + if (i > 0) { + first = FIELDS[i - 1].tool != f.tool or FIELDS[i - 1].side != f.side; + prev_depth = FIELDS[i - 1].depth; + prev_kind = FIELDS[i - 1].kind; + } + assert step_ok(first, prev_depth, prev_kind, f.depth); + let p : u32 = parent_of(i); + var parent_kind : u32 = K_OBJECT; + if (p != NB) { parent_kind = FIELDS[p].kind; } + assert (p == NB) == (f.depth == 0); + assert name_ok(parent_kind, f.name.len > 0); + assert children_ok(f.kind, f.lo, f.hi, child_count(i)); + assert bounds_ok(f.kind, f.lo, f.hi); + assert text_ok(f.kind, f.choices.len > 0, f.pattern.len > 0); + assert flags_ok(f.side, f.name.len > 0, f.req, f.nullable); + i = i + 1; + } +} + +test "property names are unique within their object" { + var i : u32 = 0; + while (i < FIELD_COUNT) { + if (FIELDS[i].name.len > 0) { + let p : u32 = parent_of(i); + var j : u32 = i + 1; + while (j < FIELD_COUNT and FIELDS[j].tool == FIELDS[i].tool and FIELDS[j].side == FIELDS[i].side) { + if (parent_of(j) == p and FIELDS[j].depth == FIELDS[i].depth) { + assert (FIELDS[j].name == FIELDS[i].name) == false; + } + j = j + 1; + } + } + i = i + 1; + } +} + +test "a text or any result declares no output rows; an object result does" { + var t : u32 = 0; + while (t < TOOL_COUNT) { + if (TOOLS[t].out == OUT_OBJECT) { + assert rows_of(t, OUT) > 0 or t == T_FORMAT; + } else { + assert rows_of(t, OUT) == 0; + } + t = t + 1; + } + assert rows_of(T_FORMAT, OUT) == 0; + assert TOOLS[T_RAW].out == OUT_ANY; + assert TOOLS[T_DASHBOARD].out == OUT_TEXT; +} + +test "the spec filters: two tools take them, they are inputs only, and no tool repeats one" { + assert TOOLS[T_SPECS].filters; + assert TOOLS[T_GREP].filters; + assert TOOLS[T_SPEC].filters == false; + assert rows_of(G_SPEC_FILTERS, IN) == 10; + assert rows_of(G_SPEC_FILTERS, OUT) == 0; + var i : u32 = 0; + while (i < FIELD_COUNT) { + if (FIELDS[i].tool < TOOL_COUNT and TOOLS[FIELDS[i].tool].filters and FIELDS[i].side == IN) { + var j : u32 = 0; + while (j < FIELD_COUNT) { + if (FIELDS[j].tool == G_SPEC_FILTERS) { assert (FIELDS[j].name == FIELDS[i].name) == false; } + j = j + 1; + } + } + i = i + 1; + } +} + +test "inputs: the constraints a client used to miss are declared" { + var i : u32 = 0; + var ints : u32 = 0; + var enums : u32 = 0; + var patterns : u32 = 0; + while (i < FIELD_COUNT) { + let f : Field = FIELDS[i]; + if (f.side == IN) { + assert f.kind != K_NUMBER; + assert f.kind != K_ANY; + if (f.kind == K_INTEGER) { ints = ints + 1; } + if (f.choices.len > 0) { enums = enums + 1; } + if (f.pattern.len > 0) { patterns = patterns + 1; } + } + i = i + 1; + } + assert ints == 23; + assert enums == 6; + assert patterns == 6; +} + +test "the round trip fixtures: a tuple, arrays, a boolean, nested objects" { + // t27_game rules.cycle is a five-step tuple; each position is one fixed word. + var cycle : u32 = NB; + var i : u32 = 0; + while (i < FIELD_COUNT) { + if (FIELDS[i].tool == T_GAME and FIELDS[i].name == "cycle") { cycle = i; } + i = i + 1; + } + assert cycle != NB; + assert FIELDS[cycle].kind == K_TUPLE; + assert FIELDS[cycle].lo == 5 and FIELDS[cycle].hi == 5; + assert child_count(cycle) == 5; + assert FIELDS[cycle + 1].choices == "Issue"; + assert FIELDS[cycle + 5].choices == "Evidence"; + assert FIELDS[parent_of(cycle)].name == "rules"; + // t27_grep results[].hits[].line sits four levels down, under two arrays and two objects. + var line : u32 = NB; + i = 0; + while (i < FIELD_COUNT) { + if (FIELDS[i].tool == T_GREP and FIELDS[i].name == "line") { line = i; } + i = i + 1; + } + assert line != NB; + assert FIELDS[line].depth == 4; + assert FIELDS[parent_of(parent_of(line))].name == "hits"; + assert FIELDS[parent_of(parent_of(parent_of(parent_of(line))))].name == "results"; + // t27_check ok is a required boolean output. + var ok : u32 = NB; + // t27f_view status is an array of at most 27 items, each an integer 0..255. + var status : u32 = NB; + i = 0; + while (i < FIELD_COUNT) { + if (FIELDS[i].tool == T_CHECK and FIELDS[i].side == OUT and FIELDS[i].name == "ok") { ok = i; } + if (FIELDS[i].tool == T_VIEW and FIELDS[i].name == "status") { status = i; } + i = i + 1; + } + assert ok != NB; + assert FIELDS[ok].kind == K_BOOLEAN and FIELDS[ok].req; + assert status != NB; + assert FIELDS[status].kind == K_ARRAY and FIELDS[status].hi == 27 and FIELDS[status].req == false; + assert child_count(status) == 1; + assert FIELDS[status + 1].kind == K_INTEGER and FIELDS[status + 1].lo == 0 and FIELDS[status + 1].hi == 255; +} + +test "negative control: a deeper step without a container, or a first row below the top" { + assert step_ok(false, 0, K_STRING, 1) == false; + assert step_ok(false, 0, K_BOOLEAN, 1) == false; + assert step_ok(true, 0, K_OBJECT, 1) == false; + assert step_ok(false, 1, K_OBJECT, 3) == false; + assert step_ok(false, 3, K_STRING, 0); +} + +test "negative control: an unnamed property, a named array item, a two-item array, a short tuple" { + assert name_ok(K_OBJECT, false) == false; + assert name_ok(K_ARRAY, true) == false; + assert name_ok(K_TUPLE, true) == false; + assert children_ok(K_ARRAY, NB, NB, 2) == false; + assert children_ok(K_ARRAY, NB, NB, 0) == false; + assert children_ok(K_TUPLE, 5, 5, 4) == false; + assert children_ok(K_TUPLE, 0, 0, 0) == false; + assert children_ok(K_STRING, NB, NB, 1) == false; +} + +test "negative control: a bound on a boolean, an inverted range, an enum on an integer, a required item, a nullable input" { + assert bounds_ok(K_BOOLEAN, 0, NB) == false; + assert bounds_ok(K_OBJECT, NB, 3) == false; + assert bounds_ok(K_INTEGER, 5, 4) == false; + assert text_ok(K_INTEGER, true, false) == false; + assert text_ok(K_ARRAY, false, true) == false; + assert flags_ok(OUT, false, true, false) == false; + assert flags_ok(IN, true, false, true) == false; +}