Skip to content

Latest commit

 

History

History
605 lines (384 loc) · 30.8 KB

File metadata and controls

605 lines (384 loc) · 30.8 KB

Show me

the tool that drew this, drawn by itself

Repository show-me
Revision main @ af7d5f27
Structures 19 in 6 districts
Connections 28
Measured 19 source files, 6,046 lines
Scope coverage 19/19 files claimed
Chapters 7
Traced flows 3

Open the interactive map — the same content, explorable.

Generated from system-map.json. Do not edit this file — edit the map and regenerate, or the two views will disagree. Every citation below was verified against the revision named above at generation time.

What this is

This skill turns a codebase into a picture you can trust. The design is one idea: an agent reads the code and writes down what it found, and scripts do only the things a careful reader reliably gets wrong.

Nothing here parses source. That is deliberate, and it is what makes the skill work on any language without configuration. An earlier version shipped detectors that extracted relations with regular expressions; every false relation it produced came from a pattern misreading code, it needed new rules for every stack, and the one genuinely valuable thing it did turned out to need no language knowledge at all.

What the scripts keep is the work reading cannot do. Opening ninety cited files to check every quotation is exactly the tedium a reader skims. Counting lines is arithmetic, and the moment a building size is estimated the picture becomes decoration. Placing buildings so that no connection ever crosses a footprint is a geometric invariant with a test behind it.

The sharpest of them is the coverage check, because it catches the failure a reader cannot catch in themselves: reading a directory, feeling finished, and moving on. That feels identical to having finished. Subtracting claimed files from files in scope has no such blind spot, and on this project it surfaced a whole subsystem in a directory the author had already read.

Scope. The whole skill, read from its own directory: the instructions an agent follows, the citation gate, the measuring and drawing pipeline, the published page runtime, and everything the pipeline emits.

How to read this

The map reveals the system a few structures at a time. This is that order, so the document can be read the same way.

1. What the agent reads

The skill begins as prose, not code.

Everything else here serves one document. It states the steps and the two rules that cannot be bent. It also points at three companions: the shape of the data file, how to find structure by reading code, and what each shape on the finished map means.

Nothing in this district reads code itself.

Introduces. The instructions, The contract, How to read, The visual language

2. The claim has to hold

A claim that cannot be checked is decoration.

The agent writes a map; the gate opens every file it cites and confirms the quoted line is really there. That is the whole trust model, and it is why someone who has not read the code can believe the picture.

Its own tests mutate a good map into eleven specific lies and assert each is caught.

Introduces. The citation gate, The gate self-test, The citation helper

3. Measured, never asserted

Building sizes are counted, never chosen.

Metrics resolves the globs a structure claims and counts files and lines. Geometry turns those counts into shape: height is lines of code on a square-root scale, footprint is file count.

There is no field an author could use to make a building bigger than its code.

Introduces. Measuring, Projection and mass, The scaffold

4. Where everything goes

Placement is deterministic, and edges provably never cross a footprint.

Each district gets a band of rows, ordered inside by how deep a thing sits in the dependency chain. Connections travel only in the reserved gaps between buildings.

That is the single reason the picture can draw every line before every building and never work out which is in front. A test checks that rule holds rather than trusting the argument for it.

Introduces. Placement and routing, The scene, The layout invariants

5. One file you can send someone

The output is one file with nothing to install.

The renderer runs the gate again, measures, lays out, emits the scene, and inlines the theme and the interaction layer into a single ASCII-only file. Same map in, same coordinates out, so two renders are diffable.

The page you are reading is that output.

Introduces. The assembler, The interaction layer, The theme

Flow shown. From map to page — A validated map becomes one self-contained HTML file: gated, measured, placed, drawn, then inlined.

6. The whole skill

Read the code, verify the claim, measure it, draw it.

Three things come out of one data file. The page you are looking at, the same map written as a document, and a short animation of one path for a README. All generated, so none can drift from the others.

The example maps close the loop: they are what the pipeline produces, and one of them is this map of the tool itself. The guard scripts run against them, so the artifacts are also the fixtures.

Introduces. The example maps, The text twin, The flow preview

Flow shown. Rejecting a fabricated map — A real map is corrupted eleven ways and the gate must catch every one of them.

7. The one thing that is proven

One claim is checked by a machine, not by a reader.

Most of what a map claims is checked by a person reading it. One claim is checked by a machine, every time: no connection ever crosses a building. Watch it being proven over the finished field.

Introduces. Nothing new — this is the whole system at once.

Flow shown. Proving edges never cross — Every shipped map is re-laid-out and checked: no connection may enter a building footprint.

Structures

WHAT THE AGENT READS

Structure Kind Files Lines Depended on by
INS The instructions types and constants 1 284 0
SCH The contract types and constants 1 250 1
EXT How to read types and constants 1 137 1
VIS The visual language types and constants 1 93 1

THE CITATION GATE

Structure Kind Files Lines Depended on by
VAL The citation gate entry point 1 637 6
GT The gate self-test entry point 1 193 0
CIT The citation helper entry point 1 102 0

MEASURING

Structure Kind Files Lines Depended on by
MET Measuring service 1 213 3
GEO Projection and mass service 1 159 6
SCA The scaffold entry point 1 162 0

PLACING AND DRAWING

Structure Kind Files Lines Depended on by
LAY Placement and routing service 1 375 3
SVG The scene service 1 210 2
LT The layout invariants entry point 1 143 0

THE PAGE

Structure Kind Files Lines Depended on by
RND The assembler entry point 1 315 1
APP The interaction layer client 1 792 1
CSS The theme types and constants 1 523 1

WHAT COMES OUT

Structure Kind Files Lines Depended on by
EXM The example maps store 1 1,054 2
TWN The text twin service 1 233 0
PRV The flow preview service 1 171 0

Structures in detail

The instructions INS

types and constants · 1 files · 284 lines

The whole method, written for an agent about to map something. It sets out the seven steps, the two rules that cannot be bent, and the division of labour that everything else follows.

How it's built. A single Markdown file with YAML frontmatter naming the skill and describing when to reach for it. It states the pipeline as shell commands and delegates the detail to three reference documents rather than restating it.

Connections.

  • imports types from The contract (the contract)
    SKILL.md:62 — Read 'references/schema.md' before writing any JSON
  • imports types from How to read (how to read)
    SKILL.md:63 — Read 'references/extraction.md' for how to find structure by reading
  • imports types from The visual language
    SKILL.md:65 — Read 'references/visual-language.md' for what each shape and channel means
  • calls The citation gate (step 5)
    SKILL.md:276 — node scripts/validate.mjs <map>.json --repo <worktree>
  • calls The assembler (step 6)
    SKILL.md:279 — node scripts/render.mjs <map>.json --repo <worktree> --out <out>.html

Evidence.

  • SKILL.md:276 — node scripts/validate.mjs <map>.json --repo <worktree>

Files. SKILL.md

The contract SCH

types and constants · 1 files · 250 lines

The exact shape of the file an agent writes. Everything the picture shows must be derivable from that document, so this is the one place where the format is defined.

How it's built. Annotated JSON with the required fields, the kinds a structure may take, the line styles each connection kind renders as, and the rules that keep a traced flow honest.

Connections.

  • ← The instructions imports types from this

Evidence.

  • references/schema.md:1 — # 'system-map.json' contract (v1)

Files. references/schema.md

How to read EXT

types and constants · 1 files · 137 lines

The reading recipes. How to find what depends on what without guessing, how to cross an async boundary, and how to get a citation without ever typing a line number.

How it's built. Ranks evidence from strongest to weakest, gives the ripgrep invocations that produce file, line and literal text in one step, and explains why queue names in particular must be read rather than matched.

Connections.

  • imports types from Measuring
    references/extraction.md:96 — REPO=/path/to/worktree node -e "import('./scripts/lib/metrics.mjs')
  • ← The instructions imports types from this

Evidence.

  • references/extraction.md:1 — # Finding structure by reading

Files. references/extraction.md

The visual language VIS

types and constants · 1 files · 93 lines

What the picture means. Which shape says a thing runs versus stores versus buffers, what makes one building taller than another, and why a hollow outline means the code is not ours.

How it's built. Binds every visual property to a measured number and records the formulas alongside a note to keep them in step with the geometry module, because a document that misstates the measurement undoes the promise it describes.

Connections.

  • imports types from Projection and mass
    references/visual-language.md:37 — These formulas live in 'scripts/lib/geometry.mjs'
  • ← The instructions imports types from this

Evidence.

  • references/visual-language.md:37 — These formulas live in 'scripts/lib/geometry.mjs'. If you change them there,

Files. references/visual-language.md

The citation gate VAL

entry point · 1 files · 637 lines

The reason a map can be trusted. It opens every cited file and proves the quoted text is really there, and it reports which files in the declared region no structure claims. Nothing renders until it passes.

How it's built. Structural checks, then for each citation it reads the file and searches a few lines either side for the literal evidence string. Also holds the coverage report and a relocate mode that repairs citations whose lines drifted while the code moved.

Concerns.

  • Proves a cited line exists and says what was quoted, never that it was read correctly, so a misreading passes the gate.

Connections.

  • calls Measuring (measure globs)
    scripts/validate.mjs:429 — const { expandGlobs, fileMetrics, discoverTestSiblings } = await import('./lib/metrics.mjs')
  • ← The instructions calls this
  • ← The assembler calls this
  • ← The gate self-test calls this
  • ← The text twin calls this
  • ← The flow preview calls this
  • ← The citation helper imports types from this

Evidence.

  • scripts/validate.mjs:62 — if (!window.includes(evidence)) {
  • scripts/validate.mjs:402 — export async function scopeCoverage(map, repoRoot) {

Files. scripts/validate.mjs

The gate self-test GT

entry point · 1 files · 193 lines

Proof that the gate actually rejects lies. It takes a real map, corrupts it eleven different ways, and checks that each corruption is caught.

How it's built. Picks whichever shipped example resolves against the repo, clones it per case, and mutates one thing: a fabricated quotation, a real string cited at the wrong line, a flow that teleports, a branch step misdeclared as a hop.

Connections.

  • calls The citation gate (eleven lies)
    scripts/self-test.mjs:14 — import { validateMap, resolveNodeFiles } from './validate.mjs'
  • reads from The example maps (fixture)
    scripts/self-test.mjs:26 — for (const name of readdirSync(examples).filter((f) => f.endsWith('.system-map.json')).sort())

Evidence.

  • scripts/self-test.mjs:14 — import { validateMap, resolveNodeFiles } from './validate.mjs'

Files. scripts/self-test.mjs

Measuring MET

service · 1 files · 213 lines

Counts. How many files a structure holds and how many lines they run to, plus which of them are tests. Building size comes from here and nowhere else.

How it's built. A dependency-free glob matcher over a directory walk, line counting that ignores blanks, and sibling discovery that finds a test file next to its subject whatever the language calls it.

Connections.

  • ← The citation gate calls this
  • ← How to read imports types from this
  • ← The scaffold calls this

Evidence.

  • scripts/lib/metrics.mjs:96 — export function expandGlobs(repoRoot, patterns) {
  • scripts/lib/metrics.mjs:28 — const TEST_PATTERN = new RegExp([

Files. scripts/lib/metrics.mjs

Projection and mass GEO

service · 1 files · 159 lines

Turns measurements into shapes. It decides how wide and how tall a structure is from its file count and line count, and produces the three visible faces of every building.

How it's built. A two-to-one isometric projection, a square-root height scale so a range spanning three orders of magnitude stays legible, and one branch per shape returning its own mesh.

Connections.

  • ← The assembler calls this
  • ← Placement and routing calls this
  • ← The scene calls this
  • ← The visual language imports types from this
  • ← The text twin imports types from this
  • ← The flow preview calls this

Evidence.

  • scripts/lib/geometry.mjs:32 — export function massOf({ fileCount, loc }) {
  • scripts/lib/geometry.mjs:12 — export function project(gx, gy, z = 0) {

Files. scripts/lib/geometry.mjs

Placement and routing LAY

service · 1 files · 375 lines

Decides where everything sits. Each group becomes a raised band of its own. Every connection travels only through the gaps between buildings, which is why no line is ever ambiguously in front of or behind one.

How it's built. Groups become contiguous row bands, columns and rows size to their own contents, and connections route through reserved corridors with lanes assigned by interval colouring so two routes contend only where they actually overlap.

Connections.

  • calls Projection and mass
    scripts/lib/layout.mjs:16 — import { massOf, project } from './geometry.mjs'
  • ← The assembler calls this
  • ← The layout invariants calls this
  • ← The flow preview calls this

Evidence.

  • scripts/lib/layout.mjs:16 — import { massOf, project } from './geometry.mjs'

Files. scripts/lib/layout.mjs

The scene SVG

service · 1 files · 210 lines

Draws the field: the ground, the raised district platforms, the connections, the buildings painted far to near, and the labels that appear only when a connection is highlighted.

How it's built. Emits inline SVG from coordinates already computed, so the browser does no layout work and the markup is diffable between runs. Hatch density on a roof encodes how many other structures depend on it.

Connections.

  • calls Projection and mass
    scripts/lib/svg.mjs:4 — import { buildMesh, groundGrid, project } from './geometry.mjs'
  • ← The assembler calls this
  • ← The flow preview imports types from this

Evidence.

  • scripts/lib/svg.mjs:4 — import { buildMesh, groundGrid, project } from './geometry.mjs'

Files. scripts/lib/svg.mjs

The assembler RND

entry point · 1 files · 315 lines

Puts the page together. It refuses to run on a map that has not passed the gate. Then it measures, places, draws, and folds everything into one file that needs no server and no build step.

How it's built. Runs the gate first, resolves globs to measurements, calls layout and the scene emitter, then inlines the stylesheet, the interaction layer and the map itself as pure ASCII so no host charset can corrupt it.

Connections.

  • calls The citation gate (gate first)
    scripts/render.mjs:16 — import { validateMap, resolveNodeFiles } from './validate.mjs'
  • calls Placement and routing
    scripts/render.mjs:17 — import { layout } from './lib/layout.mjs'
  • calls The scene
    scripts/render.mjs:18 — import { renderScene, escapeHtml as esc, DEFAULT_SHAPE }
  • calls Projection and mass
    scripts/render.mjs:19 — import { massOf } from './lib/geometry.mjs'
  • reads from The theme (inlined)
    scripts/render.mjs:218 — const css = readFileSync(join(assets, 'app.css'), 'utf8')
  • reads from The interaction layer (inlined)
    scripts/render.mjs:219 — const js = readFileSync(join(assets, 'app.js'), 'utf8')
  • ← The instructions calls this

Evidence.

  • scripts/render.mjs:16 — import { validateMap, resolveNodeFiles } from './validate.mjs'
  • scripts/render.mjs:218 — const css = readFileSync(join(assets, 'app.css'), 'utf8')

Files. scripts/render.mjs

The layout invariants LT

entry point · 1 files · 143 lines

Proof that a connection never enters a building. That single property is the only reason the scene can draw every line before every building without sorting them by depth.

How it's built. Recomputes the layout for every shipped map and asserts no route enters a footprint, no footprints overlap, everything falls inside the frame, districts are contiguous, and two runs agree exactly.

Connections.

  • calls Placement and routing
    scripts/layout-test.mjs:16 — import { layout } from './lib/layout.mjs'
  • reads from The example maps
    scripts/layout-test.mjs:19 — const examples = join(here, '..', 'examples')

Evidence.

  • scripts/layout-test.mjs:16 — import { layout } from './lib/layout.mjs'

Files. scripts/layout-test.mjs

The interaction layer APP

client · 1 files · 792 lines

Everything the page does once it is open. Panning and zooming, clicking a structure to read about it, and playing a traced path one step at a time. The pace is set by how long each step takes to read.

How it's built. Vanilla JavaScript over the pre-computed scene. Flow playback gives each step a travel time from its path length and a dwell from its own note, and scrolls the panel so the sentence explaining a hop is on screen while that hop is lit.

Connections.

  • ← The assembler reads from this

Evidence.

  • assets/app.js:192 — const READING_WORDS_PER_SECOND = 3.2; // unhurried prose, not skimming
  • assets/app.js:292 — function scrollStepIntoView(el) {

Files. assets/app.js

The theme CSS

types and constants · 1 files · 523 lines

How the page looks in either colour scheme, and the panel layout around the map. Colour never encodes a category, so the picture survives being printed or read by someone colour-blind.

How it's built. One complete palette as custom properties, redefined for dark under both a media query and an explicit attribute so every combination of viewer preference resolves. Components only ever read tokens.

Concerns.

  • Pulls a webfont from Google Fonts, the one external host an artifact may reach; every face declares a fallback stack behind it.

Connections.

  • ← The assembler reads from this

Evidence.

  • assets/app.css:1 — @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono

Files. assets/app.css

The example maps EXM

store · 1 files · 1,054 lines

Three finished maps, which double as the fixtures both guard scripts run against. One of them is this map: the skill described by itself.

How it's built. Plain JSON conforming to the contract. Each cites paths in the repository it was built from, so a guard pointed at a different checkout skips it with a note rather than failing.

Concerns.

  • Citations are repo-relative, so extracting the skill on its own leaves the example maps unresolvable.

Connections.

  • ← The gate self-test reads from this
  • ← The layout invariants reads from this

Evidence.

  • examples/show-me.system-map.json:2 — "schema": "system-map/v1"

Files. examples/*.system-map.json

The text twin TWN

service · 1 files · 233 lines

Writes the same map out as a document instead of a picture. The picture is better for grasping shape; the words are better for everything afterwards. They can be searched, compared between two versions, reviewed alongside a code change, and read where there is no browser.

How it's built. Takes the same data file the page is drawn from, re-runs the gate, resolves the file globs for their counts, and emits Markdown: a header table, the reading order, one section per structure with its connections and the citation behind each, then the traced flows and a provenance note.

Connections.

  • calls The citation gate (gate first)
    scripts/twin.mjs:18 — import { validateMap, resolveNodeFiles, scopeCoverage } from './validate.mjs'
  • imports types from Projection and mass
    scripts/twin.mjs:19 — import { massOf } from './lib/geometry.mjs'

Evidence.

  • scripts/twin.mjs:33 — export async function twin(map, repoRoot) {
  • scripts/twin.mjs:243 — writeFileSync(resolve(flags.get('out')), text, 'utf8')

Files. scripts/twin.mjs

The flow preview PRV

service · 1 files · 171 lines

Makes a small moving picture of one traced path, for a README or a message. A still image cannot show what this tool is for. The interesting part is a payload walking a real route, so the preview moves.

How it's built. Runs the same placement the page uses, frames only the structures the chosen flow touches, and emits an animated SVG. Each hop gets a lit copy of its route and a token timed to arrive in turn. Vector rather than a recording, so it needs no browser to capture and cannot drift from the map.

Connections.

  • calls The citation gate (gate first)
    scripts/preview.mjs:18 — import { validateMap, resolveNodeFiles } from './validate.mjs'
  • calls Placement and routing (same placement)
    scripts/preview.mjs:19 — import { layout } from './lib/layout.mjs'
  • calls Projection and mass
    scripts/preview.mjs:20 — import { buildMesh, project } from './lib/geometry.mjs'
  • imports types from The scene
    scripts/preview.mjs:21 — import { tagText, DEFAULT_SHAPE, escapeHtml as esc } from './lib/svg.mjs'

Evidence.

  • scripts/preview.mjs:34 — export async function preview(map, repoRoot, flowId, { step = 1200 } = {}) {
  • scripts/preview.mjs:53 — const travel = round((step * 0.62) / cycle)

Files. scripts/preview.mjs

The scaffold SCA

entry point · 1 files · 162 lines

Does the clerical half of starting a map. It reads which revision you are on, counts what is there, finds the manifests, and writes one empty shell per directory. It decides nothing about what anything is.

How it's built. Rolls the file list up by directory with line counts, reports likely doorways by filename, and emits a skeleton whose every prose field is a placeholder the gate rejects. That refusal is the point: a scaffold cannot be mistaken for a finished map.

Connections.

  • calls Measuring (count the files)
    scripts/scaffold.mjs:23 — import { expandGlobs, fileMetrics, TEST_PATTERN } from './lib/metrics.mjs'

Evidence.

  • scripts/scaffold.mjs:25 — const BUDGET = {
  • scripts/scaffold.mjs:54 — export function scaffold(repoRoot, { branch = null, scope = [], mode = 'system' } = {}) {

Files. scripts/scaffold.mjs

The citation helper CIT

entry point · 1 files · 102 lines

Turns a search result into a citation. Copying a path, a line number and a quote by hand dozens of times is the grindiest part of writing a map, and it is the part most likely to go wrong.

How it's built. Reads ripgrep output from a pipe or an argument, handles both of the shapes ripgrep emits, and checks each quote really sits at the line named before printing it. A bad paste is caught here rather than at the gate.

Concerns.

  • It re-implements the gate's window check instead of importing it, so the two could drift apart.

Connections.

  • imports types from The citation gate (same check, copied) — inferred
    scripts/cite.mjs:46 — export function verify(citation, repoRoot, window = 4) {

Evidence.

  • scripts/cite.mjs:24 — export function parseHit(line, fallbackFile = null) {
  • scripts/cite.mjs:46 — export function verify(citation, repoRoot, window = 4) {

Files. scripts/cite.mjs

Traced flows

From map to page

A validated map becomes one self-contained HTML file: gated, measured, placed, drawn, then inlined.

Trigger. The author runs render.mjs on a map that has passed the gate

Payload. system-map.json

  1. The instructions → The assembler The pipeline hands the map to the assembler, which is the only step that produces a file anyone looks at. SKILL.md:279 — node scripts/render.mjs <map>.json --repo <worktree> --out <out>.html
  2. The assembler → The citation gate (side effect; the path does not advance) The gate runs again first, so a map that fails verification cannot be rendered even by accident. scripts/render.mjs:16 — import { validateMap, resolveNodeFiles } from './validate.mjs'
  3. The assembler → Projection and mass (side effect; the path does not advance) Measured file counts and line counts become a footprint and a height, which is why no field can inflate a building. scripts/render.mjs:19 — import { massOf } from './lib/geometry.mjs'
  4. The assembler → Placement and routing (side effect; the path does not advance) Groups become raised bands and every connection is routed through the corridors between buildings. scripts/render.mjs:17 — import { layout } from './lib/layout.mjs'
  5. The assembler → The scene (side effect; the path does not advance) Final coordinates are emitted as inline SVG, so the browser does no layout work and two runs are diffable. scripts/render.mjs:18 — import { renderScene, escapeHtml as esc, DEFAULT_SHAPE }
  6. The assembler → The interaction layer (side effect; the path does not advance) The stylesheet, the interaction layer and the map itself are inlined as pure ASCII, leaving one portable file. scripts/render.mjs:219 — const js = readFileSync(join(assets, 'app.js'), 'utf8')

Rejecting a fabricated map

A real map is corrupted eleven ways and the gate must catch every one of them.

Trigger. Anyone changes the validator or the schema

Payload. a mutated clone of a real map

  1. The gate self-test → The example maps (side effect; the path does not advance) It picks whichever shipped example resolves here, so the gate can still be tested from a checkout missing the others. scripts/self-test.mjs:26 — for (const name of readdirSync(examples).filter((f) => f.endsWith('.system-map.json')).sort())
  2. The gate self-test → The citation gate Each case clones that map, breaks exactly one thing, and asserts the gate rejects it for the expected reason. scripts/self-test.mjs:14 — import { validateMap, resolveNodeFiles } from './validate.mjs'
  3. The citation gate → Measuring (side effect; the path does not advance) Glob resolution is checked too, because a structure pointing at no files cannot be drawn or measured at all. scripts/validate.mjs:429 — const { expandGlobs, fileMetrics, discoverTestSiblings } = await import('./lib/metrics.mjs')

Proving edges never cross

Every shipped map is re-laid-out and checked: no connection may enter a building footprint.

Trigger. Anyone changes placement, routing or projection

Payload. routed grid points

  1. The layout invariants → The example maps (side effect; the path does not advance) It runs over every shipped map, skipping any whose citations do not resolve against the repository given. scripts/layout-test.mjs:19 — const examples = join(here, '..', 'examples')
  2. The layout invariants → Placement and routing Placement and routing are recomputed, then every segment is tested against every footprint for intersection. scripts/layout-test.mjs:16 — import { layout } from './lib/layout.mjs'
  3. Placement and routing → Projection and mass (side effect; the path does not advance) Projection and mass come from geometry, so a change to the height scale is caught by this test as well. scripts/lib/layout.mjs:16 — import { massOf, project } from './geometry.mjs'

Provenance

Every structure, connection and flow step above cites a file, a line, and a literal string found at that line. The map is rejected at build time until all of them resolve, so this document cannot drift from the code silently — it can only fail to build.

What the check does not cover: it proves a cited line exists and says what is quoted, not that it was read correctly. Interpretation is human.

Marked inferred (real, but not pinnable to one line):

  • CIT → VAL