diff --git a/.agents/skills/README.md b/.agents/skills/README.md index 73fd15e..ab2f2e1 100644 --- a/.agents/skills/README.md +++ b/.agents/skills/README.md @@ -3,7 +3,7 @@ This directory contains repo-local copies of canonical skills from `The-Interdependency/skill-lib`. -Source commit: `a0cb6285e37734609b4b487ae4a2e44c6108d2b8` +Source commit: `c14ee9d500579a4b5d6821f62c9d82ca96e73608` Repo-local copies are not the source of truth. Edit `skill-lib` first, then propagate from the canonical source. diff --git a/.agents/skills/msdmd/SKILL.md b/.agents/skills/msdmd/SKILL.md index 15f49d6..ffa10c4 100644 --- a/.agents/skills/msdmd/SKILL.md +++ b/.agents/skills/msdmd/SKILL.md @@ -48,10 +48,16 @@ claims to prove those obligations. See - **Fence**: `=== ===` opens, `=== END ===` closes. Block name is uppercase snake_case (e.g. `CONTRACTS`, `CHECKS`, `DOCS`, `CAPABILITIES`, `OWNERS`). -- **Comment marker**: whatever is idiomatic for the file's language. - `#` for Python / Ruby / Elixir / shell. `//` for TS / JS / Rust / Go / - Java / C / C++ / Swift. `--` for SQL / Lua / Haskell. The marker - appears at the start of every line inside the block. +- **Comment marker**: whatever is idiomatic for the file's language. The + reference parsers auto-detect these line-comment families by extension: + `#` for Python, Ruby, Elixir, shell, Perl, R, Julia, PowerShell, Tcl, and + Raku; `//` for TypeScript/JavaScript, Rust, Go, Java, C, C++ (including + `.c+`, `.c++`, `.cxx`, and header variants), Swift, Kotlin, C#, + Objective-C++, Scala, Dart, Zig, Groovy, and PHP; `--` for SQL, Lua, + Haskell, Ada, VHDL, and Lean; `%` for Erlang and Prolog; `;` for + Clojure/Lisp/Scheme/Racket; `!` for Fortran; `'` for Visual Basic; and + `*>` for COBOL. The marker appears at the start of every line in the block. + `COMMENT_MARKERS` in both universal parsers is the exact extension registry. - **Entry boundary**: every entry begins with `id:`. The id must be unique within its block and stable across refactors (so it can be referenced from external tooling). @@ -138,6 +144,17 @@ A reference implementation in pure stdlib Python lives at Both commit to zero non-stdlib dependencies so you can copy them into any project. +Extension detection refuses ambiguous suffixes rather than sniffing content. +For example, `.m` can mean Objective-C or MATLAB/Octave and therefore has no +automatic marker. A caller that already knows the language may still call +`parse_text` / `parseText` with an explicit marker. Languages that cannot carry +the msdmd shape as repeated line comments need a future versioned syntax +extension; they are not approximated with an invalid fence. + +The paired RATIOS helper preserves the interpreter boundary: a non-empty +line-1 shebang may precede opening RATIOS, which must then occupy line 2 with no +gap. See [`ratios/SKILL.md`](../ratios/SKILL.md) for the complete seal contract. + ## Repo collection point and visualizer Every consuming repo SHOULD maintain one repo-level collection point named @@ -219,9 +236,9 @@ Implementation rules every runner MUST follow: 1. **Walk the source tree** under a configurable root, skipping conventional non-source paths (`__pycache__`, `node_modules`, `.git`, build outputs, the runner's own test directory). -2. **Detect comment marker by extension**, not by content sniffing. - `.py / .rb / .ex / .sh → #`. `.ts / .js / .tsx / .jsx / .rs / .go / - .java / .c / .cpp / .swift → //`. `.sql / .lua / .hs → --`. +2. **Detect comment marker by extension**, not by content sniffing. Consume the + parser's `COMMENT_MARKERS` registry rather than maintaining a runner-local + language list. Python and TypeScript registries must remain identical. 3. **Parse all matching blocks** in each file. Multiple blocks of the same type concatenate; entries from different blocks are distinguishable only by id, not by source block. diff --git a/.agents/skills/msdmd/collection.ts b/.agents/skills/msdmd/collection.ts index 697dca2..00195df 100644 --- a/.agents/skills/msdmd/collection.ts +++ b/.agents/skills/msdmd/collection.ts @@ -1,4 +1,4 @@ -// ratios: loc_comments=67:0 imports_exports=0:0 calls_definitions=1:0 +// ratios: loc_comments=hmmm imports_exports=hmmm calls_definitions=hmmm /** * Shared TypeScript shapes for repo-level msdmd collection points. * @@ -72,4 +72,4 @@ export interface MsdmdCollection { export function defineMsdmdCollection(collection: MsdmdCollection): MsdmdCollection { return collection; } -// ratios: loc_comments=67:0 imports_exports=0:0 calls_definitions=1:0 +// ratios: loc_comments=hmmm imports_exports=hmmm calls_definitions=hmmm diff --git a/.agents/skills/msdmd/parsers/universal.py b/.agents/skills/msdmd/parsers/universal.py index 7bc86bf..204f533 100644 --- a/.agents/skills/msdmd/parsers/universal.py +++ b/.agents/skills/msdmd/parsers/universal.py @@ -1,4 +1,4 @@ -# ratios: loc_comments=128:49 imports_exports=4:7 calls_definitions=51:10 +# ratios: loc_comments=161:57 imports_exports=4:7 calls_definitions=55:10 """Universal msdmd parser — pure stdlib. Implements the parser contract from ``msdmd/SKILL.md``: extracts every @@ -7,6 +7,8 @@ Comment marker is auto-detected by file extension. The block syntax itself is identical across languages; only the per-line marker changes. +``COMMENT_MARKERS`` is public so runners can distinguish parser support +from their narrower language-specific execution or metric coverage. Public API: @@ -15,12 +17,13 @@ walk_tree(root, block_name, *, skip=None, extensions=None) -> tuple[annotated, untested] RATIOS is the one msdmd declaration that is *not* a fenced block — it is a -single comment line carried on a file's first and last non-blank lines. The -reader for it lives here too, as a sanctioned extension rather than a fork: +single comment line carried on a file's opening and closing source boundaries. +A valid interpreter shebang owns literal line 1, so the opening RATIOS line is +literal line 2 in that case. The reader lives here as a sanctioned extension: parse_ratios(text, marker="#") -> list[dict] parse_ratios_file(path) -> list[dict] - ratios_placement(text, marker="#") -> tuple[first_ok, last_ok] + ratios_placement(text, marker="#") -> tuple[opening_ok, closing_ok] This module has zero non-stdlib dependencies and is safe to copy verbatim into any project that wants msdmd support. @@ -30,13 +33,39 @@ from pathlib import Path from typing import Iterable -# extension → comment marker -_MARKERS: dict[str, str] = { - ".py": "#", ".rb": "#", ".ex": "#", ".exs": "#", ".sh": "#", - ".ts": "//", ".tsx": "//", ".js": "//", ".jsx": "//", ".mjs": "//", - ".rs": "//", ".go": "//", ".java": "//", ".c": "//", ".cpp": "//", - ".cc": "//", ".h": "//", ".hpp": "//", ".swift": "//", ".kt": "//", +# extension → line-comment marker. Keep this registry entry-for-entry equivalent +# to universal.ts; tests fail if either parser gains or loses an extension alone. +COMMENT_MARKERS: dict[str, str] = { + ".py": "#", ".pyw": "#", ".pyi": "#", + ".rb": "#", ".rake": "#", ".gemspec": "#", + ".ex": "#", ".exs": "#", + ".sh": "#", ".bash": "#", ".zsh": "#", ".fish": "#", + ".pl": "#", ".pm": "#", ".t": "#", + ".r": "#", ".jl": "#", + ".ps1": "#", ".psm1": "#", ".tcl": "#", + ".raku": "#", ".rakumod": "#", + ".ts": "//", ".tsx": "//", ".mts": "//", ".cts": "//", + ".js": "//", ".jsx": "//", ".mjs": "//", ".cjs": "//", + ".rs": "//", ".go": "//", ".java": "//", + ".c": "//", ".cc": "//", ".cp": "//", ".cpp": "//", + ".cxx": "//", ".c+": "//", ".c++": "//", + ".h": "//", ".hh": "//", ".hp": "//", ".hpp": "//", + ".hxx": "//", ".h+": "//", ".h++": "//", + ".tcc": "//", ".ipp": "//", ".inl": "//", + ".swift": "//", ".kt": "//", ".kts": "//", ".cs": "//", + ".mm": "//", ".scala": "//", ".dart": "//", ".zig": "//", + ".groovy": "//", ".gradle": "//", ".php": "//", ".sql": "--", ".lua": "--", ".hs": "--", + ".adb": "--", ".ads": "--", ".vhd": "--", ".vhdl": "--", + ".lean": "--", + ".erl": "%", ".hrl": "%", ".prolog": "%", + ".clj": ";", ".cljs": ";", ".cljc": ";", ".bb": ";", + ".lisp": ";", ".lsp": ";", ".cl": ";", + ".scm": ";", ".ss": ";", ".rkt": ";", + ".f": "!", ".for": "!", ".f90": "!", ".f95": "!", + ".f03": "!", ".f08": "!", + ".vb": "'", ".vbs": "'", + ".cob": "*>", ".cbl": "*>", } _DEFAULT_SKIP = ( @@ -48,7 +77,7 @@ def marker_for(path: Path) -> str | None: """Return the comment marker for a file path, or None if unsupported.""" - return _MARKERS.get(path.suffix.lower()) + return COMMENT_MARKERS.get(path.suffix.lower()) def _block_regex(block_name: str, marker: str) -> re.Pattern[str]: @@ -124,7 +153,7 @@ def walk_tree( ext_set = ( set(e.lower() if e.startswith(".") else "." + e.lower() for e in extensions) if extensions is not None - else set(_MARKERS.keys()) + else set(COMMENT_MARKERS.keys()) ) def iter_source_files(path: Path) -> Iterable[Path]: @@ -155,8 +184,9 @@ def iter_source_files(path: Path) -> Iterable[Path]: # --- RATIOS single-line declaration (msdmd extension) -------------------- # Unlike every other declaration, RATIOS is not fenced. It is a single -# comment line carrying the three canonical ratios, placed on the file's -# first and last non-blank lines: +# comment line carrying the three canonical ratios at the opening source +# boundary and last non-blank line. A valid line-1 shebang moves the opening +# boundary to literal line 2: # ratios: loc_comments=N:M imports_exports=N:M calls_definitions=N:M RATIO_IDS = ("loc_comments", "imports_exports", "calls_definitions") _RATIOS_TOKEN_RE = re.compile(r"(?P[a-z_]+)=(?P\S+)") @@ -171,9 +201,9 @@ def parse_ratios(text: str, marker: str = "#") -> list[dict]: RATIOS is not a fenced block: it is one comment line of the form `` ratios: loc_comments=N:M imports_exports=N:M calls_definitions=N:M`` - placed on the file's first and last non-blank lines. Returns one flat - ``{"id", "value"}`` dict per (declaration line x ratio token) so a drift - gate can verify every occurrence. + placed at the file's opening and closing source boundaries. Returns one + flat ``{"id", "value"}`` dict per (declaration line x ratio token) so a + drift gate can verify every occurrence. """ line_re = _ratios_line_re(marker) out: list[dict] = [] @@ -198,17 +228,29 @@ def parse_ratios_file(path: Path) -> list[dict]: def ratios_placement(text: str, marker: str = "#") -> tuple[bool, bool]: - """Return ``(first_line_has_ratios, last_non_blank_line_has_ratios)``.""" + """Return ``(opening_ratios_ok, closing_ratios_ok)``. + + A non-empty ``#!`` interpreter directive may occupy literal line 1. It is + the only accepted preamble and RATIOS must immediately follow it. + """ line_re = _ratios_line_re(marker) lines = text.splitlines() if not lines: return (False, False) - first_ok = bool(line_re.match(lines[0].rstrip())) + has_shebang = lines[0].startswith("#!") and bool(lines[0][2:].strip()) + opening_index = 1 if has_shebang else 0 + opening_ok = ( + len(lines) > opening_index + and bool(line_re.match(lines[opening_index].rstrip())) + ) + if opening_index == 0 and len(lines) > 1: + displaced = lines[1].startswith("#!") and bool(lines[1][2:].strip()) + opening_ok = opening_ok and not displaced last_ok = False for raw in reversed(lines): if raw.strip() == "": continue last_ok = bool(line_re.match(raw.rstrip())) break - return (first_ok, last_ok) -# ratios: loc_comments=128:49 imports_exports=4:7 calls_definitions=51:10 + return (opening_ok, last_ok) +# ratios: loc_comments=161:57 imports_exports=4:7 calls_definitions=55:10 diff --git a/.agents/skills/msdmd/parsers/universal.ts b/.agents/skills/msdmd/parsers/universal.ts index 2db076f..141f46c 100644 --- a/.agents/skills/msdmd/parsers/universal.ts +++ b/.agents/skills/msdmd/parsers/universal.ts @@ -1,4 +1,4 @@ -// ratios: loc_comments=176:0 imports_exports=2:0 calls_definitions=53:0 +// ratios: loc_comments=hmmm imports_exports=hmmm calls_definitions=hmmm /** * Universal msdmd parser — pure Node stdlib (fs, path). * @@ -9,12 +9,13 @@ * * Comment marker auto-detected by file extension. The block syntax * itself is identical across languages; only the per-line marker - * changes. + * changes. COMMENT_MARKERS is public so runners can keep their narrower + * language-specific execution or metric coverage separate. * * RATIOS is the one msdmd declaration that is not a fenced block — it is a - * single comment line on a file's first and last non-blank lines. Its reader - * (parseRatios / parseRatiosFile / ratiosPlacement) lives here too, as a - * sanctioned extension rather than a fork. + * single comment line on a file's opening and closing source boundaries. A + * valid interpreter shebang owns literal line 1, moving opening RATIOS to + * literal line 2. Its reader lives here as a sanctioned extension. * * Zero non-stdlib dependencies. Safe to copy verbatim into any * Node/Deno/Bun project that wants msdmd support. @@ -24,12 +25,39 @@ import { join, extname } from "node:path"; export type Entry = Record; -const MARKERS: Record = { - ".py": "#", ".rb": "#", ".ex": "#", ".exs": "#", ".sh": "#", - ".ts": "//", ".tsx": "//", ".js": "//", ".jsx": "//", ".mjs": "//", - ".rs": "//", ".go": "//", ".java": "//", ".c": "//", ".cpp": "//", - ".cc": "//", ".h": "//", ".hpp": "//", ".swift": "//", ".kt": "//", +// Keep this registry entry-for-entry equivalent to universal.py; tests fail if +// either parser gains or loses an extension alone. +export const COMMENT_MARKERS: Record = { + ".py": "#", ".pyw": "#", ".pyi": "#", + ".rb": "#", ".rake": "#", ".gemspec": "#", + ".ex": "#", ".exs": "#", + ".sh": "#", ".bash": "#", ".zsh": "#", ".fish": "#", + ".pl": "#", ".pm": "#", ".t": "#", + ".r": "#", ".jl": "#", + ".ps1": "#", ".psm1": "#", ".tcl": "#", + ".raku": "#", ".rakumod": "#", + ".ts": "//", ".tsx": "//", ".mts": "//", ".cts": "//", + ".js": "//", ".jsx": "//", ".mjs": "//", ".cjs": "//", + ".rs": "//", ".go": "//", ".java": "//", + ".c": "//", ".cc": "//", ".cp": "//", ".cpp": "//", + ".cxx": "//", ".c+": "//", ".c++": "//", + ".h": "//", ".hh": "//", ".hp": "//", ".hpp": "//", + ".hxx": "//", ".h+": "//", ".h++": "//", + ".tcc": "//", ".ipp": "//", ".inl": "//", + ".swift": "//", ".kt": "//", ".kts": "//", ".cs": "//", + ".mm": "//", ".scala": "//", ".dart": "//", ".zig": "//", + ".groovy": "//", ".gradle": "//", ".php": "//", ".sql": "--", ".lua": "--", ".hs": "--", + ".adb": "--", ".ads": "--", ".vhd": "--", ".vhdl": "--", + ".lean": "--", + ".erl": "%", ".hrl": "%", ".prolog": "%", + ".clj": ";", ".cljs": ";", ".cljc": ";", ".bb": ";", + ".lisp": ";", ".lsp": ";", ".cl": ";", + ".scm": ";", ".ss": ";", ".rkt": ";", + ".f": "!", ".for": "!", ".f90": "!", ".f95": "!", + ".f03": "!", ".f08": "!", + ".vb": "'", ".vbs": "'", + ".cob": "*>", ".cbl": "*>", }; const DEFAULT_SKIP = new Set([ @@ -43,7 +71,7 @@ function escapeRegex(s: string): string { } export function markerFor(path: string): string | null { - return MARKERS[extname(path).toLowerCase()] ?? null; + return COMMENT_MARKERS[extname(path).toLowerCase()] ?? null; } export function parseText( @@ -104,7 +132,7 @@ export function walkTree( ): { annotated: Array<[string, Entry[]]>; untested: string[] } { const skip = opts.skip ?? DEFAULT_SKIP; const extensions = - opts.extensions ?? new Set(Object.keys(MARKERS)); + opts.extensions ?? new Set(Object.keys(COMMENT_MARKERS)); const annotated: Array<[string, Entry[]]> = []; const untested: string[] = []; @@ -145,8 +173,9 @@ export function walkTree( // --- RATIOS single-line declaration (msdmd extension) -------------------- // Unlike every other declaration, RATIOS is not fenced. It is a single -// comment line carrying the three canonical ratios, placed on the file's -// first and last non-blank lines: +// comment line carrying the three canonical ratios at the opening source +// boundary and last non-blank line. A valid line-1 shebang moves the opening +// boundary to literal line 2: // ratios: loc_comments=N:M imports_exports=N:M calls_definitions=N:M export const RATIO_IDS = ["loc_comments", "imports_exports", "calls_definitions"] as const; @@ -184,13 +213,22 @@ export function ratiosPlacement(text: string, marker: string = "#"): [boolean, b const lineRe = ratiosLineRe(marker); const lines = text.split("\n"); if (lines.length === 0) return [false, false]; - const firstOk = lineRe.test(lines[0].replace(/\s+$/, "")); + const hasShebang = lines[0].startsWith("#!") && lines[0].slice(2).trim().length > 0; + const openingIndex = hasShebang ? 1 : 0; + const openingOk = + lines.length > openingIndex && + lineRe.test(lines[openingIndex].replace(/\s+$/, "")); + const displacedShebang = + openingIndex === 0 && + lines.length > 1 && + lines[1].startsWith("#!") && + lines[1].slice(2).trim().length > 0; let lastOk = false; for (let i = lines.length - 1; i >= 0; i--) { if (lines[i].trim() === "") continue; lastOk = lineRe.test(lines[i].replace(/\s+$/, "")); break; } - return [firstOk, lastOk]; + return [openingOk && !displacedShebang, lastOk]; } -// ratios: loc_comments=176:0 imports_exports=2:0 calls_definitions=53:0 +// ratios: loc_comments=hmmm imports_exports=hmmm calls_definitions=hmmm diff --git a/.agents/skills/ratios/SKILL.md b/.agents/skills/ratios/SKILL.md index cf2b7cd..076181c 100644 --- a/.agents/skills/ratios/SKILL.md +++ b/.agents/skills/ratios/SKILL.md @@ -1,6 +1,6 @@ --- name: ratios -description: Self-declaring module composition ratios on msdmd — a single comment line on a file's first and last line (never a fenced block). The canonical seal is `The-Interdependency/a0`'s compact positional `N:M C:D I:O` annotation (code:comment · consumed:declared · fan-in:fan-out), computed by a0's `scripts/annotate.py`; the named `loc_comments=… imports_exports=… calls_definitions=…` line is a portable, per-file adaptation for standalone libraries, verified by the stdlib `ratios_check.py` (drift/misplacement failures, visible gaps). JSON/Markdown are out of scope. Load this when recording a module's composition ratios, when authoring or extending the ratio registry, or when wiring ratio verification into CI. +description: Self-declaring module composition ratios on msdmd — one comment line at a file's opening and closing source boundaries (never a fenced block), with a valid interpreter shebang allowed before the opening seal. The canonical seal is `The-Interdependency/a0`'s compact positional `N:M C:D I:O` annotation (code:comment · consumed:declared · fan-in:fan-out), computed by a0's `scripts/annotate.py`; the named `loc_comments=… imports_exports=… calls_definitions=…` line is a portable, per-file adaptation for standalone libraries, verified by the stdlib `ratios_check.py` (drift/misplacement failures, visible gaps). JSON/Markdown are out of scope. Load this when recording a module's composition ratios, when authoring or extending the ratio registry, or when wiring ratio verification into CI. --- # ratios — Module composition ratios on msdmd @@ -23,14 +23,19 @@ the file is a build failure, not a stale comment nobody noticed. `The-Interdependency/a0` is the **canonical origin** of the ratios seal — the convention every other form adapts, not the other way round. a0 stamps three -composition metrics on the first and last line of every Python / TypeScript -file, written and verified by its own `scripts/annotate.py`: +composition metrics at the opening and closing source boundaries of every +Python / TypeScript file, written and verified by its own `scripts/annotate.py`: ```text # N:M C:D I:O (Python) // N:M C:D I:O (TypeScript / TSX) ``` +Ordinary modules put the opening seal on literal line 1. Directly executable +scripts reserve literal line 1 for a non-empty `#!` interpreter directive and +put the opening seal immediately on literal line 2. The matching closing seal +is the last non-blank line in both cases. + | pair | meaning | how computed | |---|---|---| | `N:M` | code lines : comment+docstring lines (internal density; `N` budget ≤ 400) | per-file | @@ -73,16 +78,17 @@ are per-file stand-ins for the surface/graph intent of `C:D` and `I:O`. Repos already stamped in this form (skill-lib, aimmh, edcmbone, pcna, pcta, ptca, pcea) remain valid and are **not** required to reconvert. -Both forms keep one seal discipline: a single line on the file's first and last -non-blank line, with nothing above it. +Both forms keep one seal discipline: a single line at the opening and closing +source boundaries. Nothing may precede the opening seal except one valid +interpreter shebang on literal line 1. -### The single line, and the first/last rule +### The single line and shebang-safe boundary rule RATIOS is the one msdmd declaration that is **not a fenced block**. It applies to executable/source files with a language comment marker (`#`, `//`, or `--`), not to JSON, Markdown, or other data/documentation files. In covered source -files it is a single comment line carrying all three ratios, placed on the -file's **literal first line and its last non-blank line**: +files it is a single comment line carrying all three ratios, placed at the +opening boundary and repeated on the file's last non-blank line: ```python # ratios: loc_comments=128:49 imports_exports=4:7 calls_definitions=51:10 @@ -91,6 +97,20 @@ file's **literal first line and its last non-blank line**: # ratios: loc_comments=128:49 imports_exports=4:7 calls_definitions=51:10 ``` +For a directly executable script: + +```bash +#!/usr/bin/env bash +# ratios: loc_comments=40:12 imports_exports=0:0 calls_definitions=0:0 +... +# ratios: loc_comments=40:12 imports_exports=0:0 calls_definitions=0:0 +``` + +The shebang exception is structural, not a general license for preambles: it +must be non-empty, occupy literal line 1, and be followed immediately by the +opening seal. Blank lines, encoding headers, copyright comments, or other text +before the seal fail placement. + The form is: ```text @@ -101,10 +121,10 @@ The form is: - The three ids are fixed: `loc_comments`, `imports_exports`, `calls_definitions`. Each carries an `A:B` value, or `hmmm` if the ratio is declared-but-not-yet-resolved. -- The same line opens and closes the file. The file is a self-measuring - object; its boundary lines carry the measurement, opening and closing. +- The same line opens and closes the file. Without a shebang the opening seal + is literal line 1; with a valid shebang it is literal line 2. - Scope: executable source files only. `json` and `.md` files are out of - scope for first/last-line RATIOS bookends. + scope for RATIOS boundary seals. There is no fenced `# === RATIOS === … # === END RATIOS ===` block. An earlier draft of this skill described one; that was wrong. Tooling reads the single @@ -126,7 +146,7 @@ a fork (`msdmd/parsers/universal.py`): from msdmd.parsers.universal import parse_ratios, ratios_placement, RATIO_IDS parse_ratios(text, marker) # -> [{"id": "loc_comments", "value": "128:49"}, ...] -ratios_placement(text, marker) # -> (first_line_ok, last_non_blank_line_ok) +ratios_placement(text, marker) # -> (opening_ok, closing_ok) ``` `parse_ratios` returns one flat `{"id", "value"}` dict per (declaration line × @@ -144,7 +164,7 @@ registry. A computer is a pure function `file_text -> "A:B"`. The runner: measurement; - compares the recomputed value to the recorded value; - on mismatch, emits a **drift** error and exits non-zero; -- on a declaration that is not on both the first and last line, emits a +- on a declaration that is not on both source boundaries, emits a **misplaced** error and exits non-zero; - on `value: hmmm`, reports a living continuation (pending), never a failure — the transition out of `hmmm` is the owner's decision; @@ -152,10 +172,13 @@ registry. A computer is a pure function `file_text -> "A:B"`. The runner: unverifiable (informational), so unknown ratios stay visible rather than silently trusted. -Covered source files with no `ratios:` line surface as coverage gaps, exactly -as in the build checker. JSON, Markdown, and other files with no supported -source comment marker are skipped rather than reported as gaps. The gap list is -informational unless `--strict`. +The bundled named-form computers currently implement Python syntax. Python +files without `ratios:` surface as coverage gaps; other parser-supported +languages do not become false gaps merely because msdmd learned their comment +marker. If a non-Python file already carries a named seal, the runner checks +its placement and reports its values as visible-but-unverifiable until a +language-aware computer is registered. JSON, Markdown, and unsupported files +remain out of scope. The Python gap list is informational unless `--strict`. ## The three ratios @@ -252,9 +275,9 @@ as gaps is expected, not a bug. It is a single `ratios:` line; the block form was a mistake. - Recording a ratio by hand instead of recomputing it. The point is that the file measures itself; a hand-typed value is a contract that drifts. -- Placing the line anywhere but the file's first and last line. The first/last - placement is the convention; a mid-file `ratios:` line defeats the - at-a-glance reading and fails the placement gate. +- Placing anything before opening RATIOS except a valid line-1 shebang, or + leaving a gap between that shebang and the opening seal. Either breaks the + source-boundary invariant and fails placement. - Counting a `ratios:` line in its own ratio. Always self-exclude. - Inventing ratio ids whose computer does not exist and recording a number for them. If there's no computer, the value cannot be verified — record @@ -265,16 +288,17 @@ as gaps is expected, not a bug. ## Completion criteria -A run is complete when every covered executable/source file carries a correctly placed `ratios:` -line on its first and last line, the registry's three computers -(`loc_comments`, `imports_exports`, `calls_definitions`) recompute each -recorded value with no drift, and any unresolved ratio is recorded as `hmmm` -rather than guessed. +A run is complete when every computer-covered source file carries a correctly +placed opening RATIOS line (literal line 1, or line 2 immediately after a valid +line-1 shebang), an identical closing line on the last non-blank line, the +registry's three computers (`loc_comments`, `imports_exports`, +`calls_definitions`) recompute each recorded value with no drift, and any +unresolved ratio is recorded as `hmmm` rather than guessed. hmmm -- the reference computers implement the Python counting rules; language-aware - computers for TypeScript/other markers are a documented extension point, not - yet implemented in `ratios_check.py` +- language-aware named-form computers for TypeScript and other parser-supported + languages remain an extension point; the runner now exposes them as outside + computer scope instead of applying Python semantics - whether ratios verification joins CI beside the other msdmd checks - calls_definitions: whether lambda assignments count as definitions - imports_exports: whether re-exported names from `__init__.py` aggregate diff --git a/.agents/skills/ratios/annotate_index.py b/.agents/skills/ratios/annotate_index.py index e4d7e59..8a88945 100644 --- a/.agents/skills/ratios/annotate_index.py +++ b/.agents/skills/ratios/annotate_index.py @@ -1,4 +1,4 @@ -# ratios: loc_comments=232:33 imports_exports=7:4 calls_definitions=83:14 +# ratios: loc_comments=264:34 imports_exports=7:4 calls_definitions=99:17 """Portable computer for the canonical ratios seal — a0's `N:M C:D I:O`. This is the shared, stdlib port of `The-Interdependency/a0`'s @@ -59,12 +59,30 @@ def _is_seal(line: str, ext: str) -> bool: return bool((_ANN_PY if ext == ".py" else _ANN_TS).match(s)) +def _has_valid_shebang(lines: list[str]) -> bool: + """Return whether literal line 1 is a non-empty interpreter directive.""" + return bool(lines and lines[0].startswith("#!") and lines[0][2:].strip()) + + +def _opening_index(lines: list[str]) -> int: + return 1 if _has_valid_shebang(lines) else 0 + + +def _last_nonblank_index(lines: list[str]) -> int | None: + for index in range(len(lines) - 1, -1, -1): + if lines[index].strip(): + return index + return None + + def _strip_seal(lines: list[str], ext: str) -> list[str]: w = lines[:] - if w and _is_seal(w[0], ext): - w = w[1:] - if w and _is_seal(w[-1], ext): - w = w[:-1] + opening = _opening_index(w) + if len(w) > opening and _is_seal(w[opening], ext): + del w[opening] + closing = _last_nonblank_index(w) + if closing is not None and _is_seal(w[closing], ext): + del w[closing] return w @@ -289,18 +307,39 @@ def main(argv: list[str] | None = None) -> int: lines = path.read_text(encoding="utf-8").splitlines() except OSError: continue - have = lines[0].strip() if lines else "" - placed_ok = bool(lines) and _is_seal(lines[0], ext) and _is_seal(lines[-1], ext) + opening = _opening_index(lines) + closing = _last_nonblank_index(lines) + have = lines[opening].strip() if len(lines) > opening else "" + close = lines[closing].strip() if closing is not None else "" + placed_ok = ( + len(lines) > opening + and _is_seal(lines[opening], ext) + and closing is not None + and _is_seal(lines[closing], ext) + and not ( + opening == 0 + and len(lines) > 1 + and lines[1].startswith("#!") + and bool(lines[1][2:].strip()) + ) + ) if write: working = _strip_seal(lines, ext) - new = "\n".join([want] + working + [want]) + "\n" + if _has_valid_shebang(working): + new_lines = [working[0], want, *working[1:], want] + else: + new_lines = [want, *working, want] + new = "\n".join(new_lines) + "\n" if new != path.read_text(encoding="utf-8"): path.write_text(new, encoding="utf-8") print(f" stamped {path.relative_to(root)} [{want}]") else: - if not placed_ok or have != want: + if not placed_ok or have != want or close != want: drift += 1 - print(f" DRIFT {path.relative_to(root)}: have '{have}' want '{want}'") + print( + f" DRIFT {path.relative_to(root)}: " + f"opening '{have}' closing '{close}' want '{want}'" + ) if check: print(f"annotate_index: {len(files)} files, {drift} drift/misplaced") return 1 if drift else 0 @@ -309,4 +348,4 @@ def main(argv: list[str] | None = None) -> int: if __name__ == "__main__": raise SystemExit(main()) -# ratios: loc_comments=232:33 imports_exports=7:4 calls_definitions=83:14 +# ratios: loc_comments=264:34 imports_exports=7:4 calls_definitions=99:17 diff --git a/.agents/skills/ratios/ratios_check.py b/.agents/skills/ratios/ratios_check.py index 12f470e..59eafe0 100644 --- a/.agents/skills/ratios/ratios_check.py +++ b/.agents/skills/ratios/ratios_check.py @@ -1,18 +1,21 @@ -# ratios: loc_comments=190:27 imports_exports=6:7 calls_definitions=78:10 +# ratios: loc_comments=218:33 imports_exports=6:7 calls_definitions=86:10 """ratios skill executor — recompute the canonical ratios and gate on drift. Reference runner for the ``ratios`` skill. It reads the single-line RATIOS declaration (`` ratios: loc_comments=N:M imports_exports=N:M -calls_definitions=N:M``) from a file's first and last non-blank lines, +calls_definitions=N:M``) from a file's opening and closing source boundaries, recomputes each ratio from the source, and fails on: * drift — a recorded ratio no longer matches what the source computes; - * misplaced — a RATIOS declaration not on both the first and last line; - * gaps — (only under ``--strict``) source files with no RATIOS at all. + * misplaced — a RATIOS declaration not on both source boundaries; + * gaps — (only under ``--strict``) computer-supported files with no + RATIOS at all. ``value: hmmm`` is reported as pending, never a failure. A recorded id with no registered computer is reported as unverifiable (informational). Pure stdlib; the single-line reader is reused from the msdmd universal parser, not forked. +The bundled computers are Python-specific. Parser support for another language +does not silently opt that language into Python metric semantics. Usage: python ratios_check.py path/to/module.py # verify one file @@ -148,6 +151,11 @@ def compute_calls_definitions(text: str) -> str: "calls_definitions": compute_calls_definitions, } +# The named-form computers above use Python imports, definitions, docstrings, +# and call syntax. Other parser-supported languages remain discoverable without +# becoming false strict-mode gaps or receiving Python-shaped verification. +COMPUTER_EXTENSIONS = {".py"} + def _iter_source(root: Path): """Yield every source file under ``root`` with a known comment marker.""" @@ -167,13 +175,36 @@ def _verify_file(path: Path, base: Path, rep: dict) -> None: text = path.read_text(encoding="utf-8", errors="ignore") entries = parse_ratios(text, marker) if not entries: - rep["gaps"].append(rel) + if path.suffix.lower() in COMPUTER_EXTENSIONS: + rep["gaps"].append(rel) + else: + rep["outside_computer_scope"].append(rel) return rep["covered"] += 1 - first_ok, last_ok = ratios_placement(text, marker) - if not (first_ok and last_ok): - rep["misplaced"].append({"file": rel, "first_line": first_ok, "last_line": last_ok}) + opening_ok, closing_ok = ratios_placement(text, marker) + if not (opening_ok and closing_ok): + rep["misplaced"].append( + {"file": rel, "opening": opening_ok, "closing": closing_ok} + ) + + if path.suffix.lower() not in COMPUTER_EXTENSIONS: + for entry in entries: + value = (entry.get("value") or "").strip() + if value == "hmmm": + rep["pending"].append( + {"file": rel, "id": entry.get("id", ""), "reason": "language computer unavailable"} + ) + continue + rep["unverifiable"].append( + { + "file": rel, + "id": entry.get("id", ""), + "value": value, + "reason": f"no named-form computer for {path.suffix.lower()}", + } + ) + return for entry in entries: cid = entry.get("id", "") @@ -202,7 +233,7 @@ def run(target: Path) -> dict: rep: dict = { "skill": "ratios", "root": str(target), "scanned": 0, "covered": 0, "gaps": [], "drift": [], "misplaced": [], "pending": [], - "verified": [], "unverifiable": [], + "verified": [], "unverifiable": [], "outside_computer_scope": [], } if target.is_file(): rep["scanned"] = 1 @@ -215,6 +246,7 @@ def run(target: Path) -> dict: rep["drift_count"] = len(rep["drift"]) rep["misplaced_count"] = len(rep["misplaced"]) rep["pending_count"] = len(rep["pending"]) + rep["outside_computer_scope_count"] = len(rep["outside_computer_scope"]) rep["verified_count"] = len(rep["verified"]) return rep @@ -226,6 +258,7 @@ def summary(rep: dict) -> str: f"{rep['verified_count']} verified . {rep['drift_count']} drift . " f"{rep['misplaced_count']} misplaced . " f"{rep['pending_count']} hmmm . {len(rep['unverifiable'])} unverifiable" + f" . {rep['outside_computer_scope_count']} outside-computer-scope" ) @@ -242,7 +275,10 @@ def main(argv: list[str] | None = None) -> int: for d in rep["drift"][:30]: print(f" drift: {d['file']} :: {d['id']}: recorded {d['recorded']} != computed {d['computed']}") for m in rep["misplaced"][:30]: - print(f" misplaced: {m['file']}: first_line={m['first_line']} last_line={m['last_line']}") + print( + f" misplaced: {m['file']}: " + f"opening={m['opening']} closing={m['closing']}" + ) if strict: for g in rep["gaps"][:30]: print(f" gap: {g}") @@ -253,4 +289,4 @@ def main(argv: list[str] | None = None) -> int: if __name__ == "__main__": raise SystemExit(main()) -# ratios: loc_comments=190:27 imports_exports=6:7 calls_definitions=78:10 +# ratios: loc_comments=218:33 imports_exports=6:7 calls_definitions=86:10 diff --git a/.agents/skills/the-interdependency/SKILL.md b/.agents/skills/the-interdependency/SKILL.md index 74f4d53..9f7ba4c 100644 --- a/.agents/skills/the-interdependency/SKILL.md +++ b/.agents/skills/the-interdependency/SKILL.md @@ -17,6 +17,7 @@ description: Protocol and workflow for all tasks involving The Interdependency o ## Core Doctrine +- **Agent/work context gate**: `skill-lib` is standing context for org agents. At every agent instantiation, resolve the available skill-lib entrypoint/index plus the governing repository instructions before that agent may reason about or execute org work. At the start of every unit of work, reevaluate the request against skill descriptions and read every applicable `SKILL.md` before acting. Child/sub-agents inherit already-resolved repository identities, governing contracts, and applicable skill context from the parent, then reevaluate triggers for their own assignment. Previously resolved authoritative instructions stay resolved until their source changes, conflicts, becomes unavailable, or is explicitly superseded. Do not ask the user to restate repository knowledge that authoritative sources already resolve. If required authority cannot be resolved, stop that boundary as `hmmm`; do not guess or reconstruct stable project semantics from conversational repetition. - **Structure preservation first**: Before any summarization, compression, decision, or output, preserve the complete relational structure, variables, topology, epistemic status (declared / implemented / inferred / hmmm), distinct layers (lived experience vs formal claims vs emotional), and explicitly mark all unresolveds. This follows the org's neurodivergence-preserving interaction principles. - **Resource-run preflight and completion**: Resource scarcity requires contemplation **before** a compute run begins. Before launch, inspect or estimate whether available time under real external constraints, CPU, memory, disk, battery/power, network, quotas, API/tool usage limits, and session/process durability are sufficient for the run to reach its natural terminal condition. If there is material doubt that it can finish, do not start it: reduce, stage/checkpoint, relocate, acquire resources, or leave it `hmmm`. Once a healthy run begins, let it finish to completion or deterministic computational failure unless the user explicitly cancels it or an unforeseen real resource/safety emergency requires interruption. Do **not** invent or enforce a wall-clock cutoff merely to make work bounded, falsifiable, or convenient. Runtime/resource ceilings are stopping criteria only when the quantity is itself load-bearing to the hypothesis or acceptance criterion, an authorized safety boundary, or a real externally imposed hard limit, and they must be justified before launch. - **METAPAT consultation gate**: Consult current `The-Interdependency/metapat` before committing a conceptual choice when the task must decide which distinctions, relations, boundaries, transformations, scales, or cross-domain correspondences should organize downstream work. METAPAT consultation is also required when an unresolved conceptual choice would constrain architecture, semantics, measurement, ontology, or later falsifiable claims. Do not consult METAPAT merely to execute an already-fixed implementation, run tests, repair syntax, move data, or apply a relation whose meaning and boundary are already established. METAPAT is the source of truth for its own doctrine; skill-lib routes to it and must not duplicate a frozen theory snapshot. @@ -34,6 +35,44 @@ description: Protocol and workflow for all tasks involving The Interdependency o - **Usage guidance requirement**: Every code file, SKILL.md update, README change, research summary, or artifact produced under this skill **must contain clear, actionable usage guidance**. This is non-negotiable for accessibility, onboarding, and reducing signal loss. - **Research & canon alignment**: Ground all claims in source-backed canon (cross-load `canon` skill). Use `char-compress` for context handoff. Leave genuine uncertainty as `hmmm`. +## Operator workflow contract + +These constraints govern how work is selected and executed; they do not override repository-local authority about what a project means. + +- **Audit before assent**: Test a proposal against current code, canon, evidence, constraints, and failure modes before agreeing with it. Agreement is a conclusion, not a conversational default. +- **Preserve concepts; reject bad placement**: When a proposal is useful but architecturally misplaced, preserve the concept and move or re-scope it to the owning layer rather than either accepting the wrong placement or discarding the idea. +- **Useful, good, true**: Do not generate work merely to create activity. Prefer artifacts and actions that are useful to the stated goal, operationally sound, and truthfully supported by evidence or explicit status. +- **KISS under reality contact**: Prefer the smallest skilled design that survives actual execution. A clever mechanism that is fragile, opaque, untestable, or needlessly expensive is not simpler than a slightly longer mechanism that works. +- **Prior planning before execution**: Resolve authority, placement, dependency order, resource needs, validation, rollback, and terminal condition before expensive or destructive work begins. Planning exists to prevent avoidable failure, not to create an approval ceremony. +- **Complete within granted scope**: When the request, authority, and safety boundary already permit the next action, continue through the coherent workflow instead of repeatedly asking the operator to approve each obvious intermediate step. Ask only when a real unresolved decision cannot be recovered from authoritative sources or safely isolated as `hmmm`. +- **Usage-limit aware orchestration**: Treat model-plan limits, API quotas, tool-call limits, rate limits, context budgets, and session durability as real resources during preflight. Stage or redistribute work before launch so a workflow does not predictably die midway from exhaustion. Do not silently downgrade evidence quality merely to fit a limit. +- **Purposeful functions**: Every function, script, workflow step, and abstraction must have a defensible purpose, coherent inputs/outputs, failure behavior, and a reason to exist at that layer. Remove dead indirection and mechanisms whose only justification is that they already exist. +- **Deprecation is removal plus replacement when capability remains required**: Once a mechanism is declared deprecated, stop routing new work through it and provide or identify its supported replacement when the retired capability remains required. If the capability is intentionally retired as unnecessary, complete removal is the replacement outcome. Do not preserve deprecated behavior by default out of inertia. +- **`hmmm` is mandatory honest incompletion**: `hmmm` is the boundary object for unresolved constraints, missing authority, incomplete evidence, or a living continuation. Never erase an unresolved merely to make an artifact look finished. Where the boundary would otherwise be empty, leave a brief apropos, cogent, or humorous nonsequitur rather than silently dropping it. + +## Operational authority topology + +This section records durable ownership boundaries, not a frozen inventory of the operator's current machines, clients, logins, quotas, or provider sessions. + +- **GitHub repository boundary**: GitHub is the canonical remote source, review, and merge surface for repositories under `The-Interdependency`. GitHub Actions is validation evidence only where a repository's current workflows actually execute the claimed gates; inspect those jobs rather than inferring health from a green badge. +- **VM control-plane authority**: `skill-lib/vm-mcp` owns reusable VM MCP implementation and doctrine. Its authority profiles are deployment choices: bounded defaults remain appropriate for shared or first-contact environments, while the explicit `personal-console` profile is available for a deliberately configured single-owner private VM. The personal-console profile entered canonical skill-lib in merged #81 at `222ba4d4348022d81950c3fad054bae7e528b6a0`. Repository tests do not prove that any particular VM currently satisfies that profile. +- **Stack deployment authority**: `The-Interdependency/stack` owns stack-specific deployment and operational-use guidance. Stack's consumption of the canonical `vm-mcp` personal console entered stack in merged #12 at `22b74340d0c603883193a4ecf53e2ef3f9c3e780`. When stack deployment consumes `vm-mcp`, resolve that exact pinned skill-lib identity and the current `stack/backend/deploy` instructions before acting. No implementation or doctrine authority transfers from skill-lib into stack merely because stack consumes it. +- **Concrete host/client facts are runtime evidence**: A hostname or alias such as `a0`, a client such as Termux, Git transport/authentication method, tunnel state, installed CLI, provider login, exact version, quota, and API availability must be discovered from the current deployment/operator environment before use. This skill must not elevate those transient facts into unconditional organization-wide routing doctrine. +- **Provider execution capacity is not source authority**: OpenAI/Codex, xAI/Grok, DeepSeek/DeepCode, or another provider may be usable execution capacity when currently authenticated and within quota. Their availability must be checked at runtime, and choosing an executor does not transfer repository, semantic, mathematical, measurement, or publication authority. +- **Deprecated/stale routes do not revive themselves**: Historical services, hosts, clients, authentication paths, or provider assumptions are not automatic fallbacks. If a route is deprecated, migrate to its supported replacement and remove obsolete routing when compatibility permits; otherwise preserve the unresolved deployment boundary as `hmmm`. + +### Operational usage guidance + +Before routing work to a machine or provider: + +1. resolve the repository and exact commit that owns the work; +2. read the current deployment instructions owned by the consuming repository; +3. verify the actual host/client/authentication/tunnel/provider state; +4. choose only the authority profile and executor justified by that evidence; and +5. keep human recovery access independent where the deployment contract requires it. + +A statement like "use `a0`" is therefore a runtime/operator decision backed by current deployment evidence, not standing organization canon in this skill. + ## METAPAT consultation test Ask one question before conceptual or architectural commitment: @@ -66,13 +105,14 @@ When consultation triggers, inspect the current METAPAT repository state before ## Workflow -1. **Trigger detection**: Activate on any The-Interdependency context or the example trigger phrases listed in the description. -2. **Resource preflight**: Before starting any compute run, decide whether the available resources can sustain it to its natural terminal condition. If not, do not launch it. Do not substitute an arbitrary timeout for preflight judgment. -3. **METAPAT gate**: Before conceptual or architectural commitment, run the consultation test above. If triggered, inspect current METAPAT before selecting the relation, boundary, transformation, or cross-domain mapping. -4. **Context assembly**: For transcript work, explicitly structure output using EDCMBONE energy-dissonance mapping, F-metrics, failure-mode tags, and accessibility annotations. Preserve full original relations. -5. **Artifact production**: Write code/docs with msdmd blocks (if applicable) + dedicated "Usage Guidance" section or equivalent. Include examples that can be copy-pasted. -6. **GitHub hygiene**: Check drift, update indexes, propagate only after validation. Reference this skill in commit messages where relevant. -7. **Output packaging**: Structure responses with: +1. **Agent/work context gate**: On agent birth, resolve skill-lib plus governing repository instructions before org work begins. On every work start, reevaluate skill triggers and load applicable contracts before reasoning or acting. Inherit resolved authority into child/sub-agents; do not make the user restate stable repository knowledge. Missing required authority is `hmmm` and blocks that boundary. +2. **Trigger detection**: Activate on any The-Interdependency context or the example trigger phrases listed in the description. +3. **Resource preflight**: Before starting any compute run, decide whether the available resources can sustain it to its natural terminal condition. If not, do not launch it. Do not substitute an arbitrary timeout for preflight judgment. +4. **METAPAT gate**: Before conceptual or architectural commitment, run the consultation test above. If triggered, inspect current METAPAT before selecting the relation, boundary, transformation, or cross-domain mapping. +5. **Context assembly**: For transcript work, explicitly structure output using EDCMBONE energy-dissonance mapping, F-metrics, failure-mode tags, and accessibility annotations. Preserve full original relations. +6. **Artifact production**: Write code/docs with msdmd blocks (if applicable) + dedicated "Usage Guidance" section or equivalent. Include examples that can be copy-pasted. +7. **GitHub hygiene**: Check drift, update indexes, propagate only after validation. Reference this skill in commit messages where relevant. +8. **Output packaging**: Structure responses with: - Preserved structure / epistemic layers first. - EDCMBONE-mapped analysis where transcripts are involved. - Usage guidance and examples. @@ -81,6 +121,8 @@ When consultation triggers, inspect the current METAPAT repository state before ## Anti-patterns +- Beginning org work or instantiating an org agent without resolving skill-lib, governing repo instructions, and applicable contracts first. +- Asking the user to restate stable repository knowledge instead of resolving it from its authoritative source. - Flattening, dropping variables, or losing topology/relations before acting or summarizing (directly conflicts with neurodivergence preservation). - Starting a compute run when available resources have not been considered sufficiently to expect completion. - Terminating a healthy compute run because of an arbitrary wall-clock limit that was not actually load-bearing to the claim, safety boundary, or external resource limit. @@ -93,6 +135,7 @@ When consultation triggers, inspect the current METAPAT repository state before - Using METAPAT to decorate a routine implementation decision. - Making a conceptual architecture choice that crosses the METAPAT gate without consulting current METAPAT. - Copying METAPAT doctrine into skill-lib and allowing the copy to become a competing authority. +- Treating a concrete host, client, provider login, or quota as standing organization authority without current deployment evidence. ## Output Rubric (active whenever this skill is loaded) @@ -107,5 +150,5 @@ hmmm - Precise harness integration for automatically fetching current METAPAT after this gate triggers; the skill currently defines the decision rule and source-of-truth boundary, while the consuming agent uses its available GitHub/local-repo access. - Whether the historical `meta` skill should remain as a compatibility router or be removed after all consumers propagate this gate. - Whether a companion metadata-block skill (e.g. `# === TIW_WORKFLOW ===` or `# === INTERDEPENDENCY ===`) should be added for self-declaring modules inside The-Interdependency repos. -- Deeper integration with a0p-instancing / agent-instantiation so that TIW-context automatically loads this skill for sub-agents. - Exact canonical reference for the full EDCMBONE transcript assembly protocol — should the detailed steps live in this skill or be expanded inside the edcmbone repo's own skill definitions? +- Actual VM state, current client/private-tunnel state, provider sessions, and quotas remain runtime evidence outside this skill.