boxbox-plugin 0.0.0-stage → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +179 -2
- package/bin/boxbox.mjs +121 -0
- package/package.json +17 -5
- package/payload/bundle.json +8 -0
- package/payload/claude/.claude-plugin/marketplace.json +15 -0
- package/payload/claude/bundle.json +9 -0
- package/payload/claude/claude-registration.mjs +80 -0
- package/payload/claude/codex-registration.mjs +62 -0
- package/payload/claude/install-state.mjs +46 -0
- package/payload/claude/install.mjs +150 -0
- package/payload/claude/packages/boxbox-0.2.8.tgz +0 -0
- package/payload/claude/platform.mjs +107 -0
- package/payload/claude/plugin/.claude-plugin/plugin.json +18 -0
- package/payload/claude/plugin/agents/readability-inspector.md +23 -0
- package/payload/claude/plugin/hooks/hooks.json +54 -0
- package/payload/claude/plugin/scripts/boxbox.mjs +42 -0
- package/payload/claude/plugin/skills/boxbox/SKILL.md +188 -0
- package/payload/claude/plugin/skills/boxbox/references/charter.md +72 -0
- package/payload/claude/plugin/skills/boxbox/references/grammar.md +147 -0
- package/payload/claude/plugin/skills/boxbox/references/readability-contract.md +311 -0
- package/payload/claude/plugin/skills/boxbox/references/typescript-grid-template.md +334 -0
- package/payload/claude-registration.mjs +80 -0
- package/payload/codex/.agents/plugins/marketplace.json +12 -0
- package/payload/codex/bundle.json +9 -0
- package/payload/codex/claude-registration.mjs +80 -0
- package/payload/codex/codex-registration.mjs +62 -0
- package/payload/codex/install-state.mjs +46 -0
- package/payload/codex/install.mjs +150 -0
- package/payload/codex/packages/boxbox-0.2.8.tgz +0 -0
- package/payload/codex/platform.mjs +107 -0
- package/payload/codex/plugin/.codex-plugin/plugin.json +29 -0
- package/payload/codex/plugin/hooks/hooks.json +42 -0
- package/payload/codex/plugin/scripts/boxbox.mjs +42 -0
- package/payload/codex/plugin/skills/boxbox/SKILL.md +188 -0
- package/payload/codex/plugin/skills/boxbox/references/charter.md +72 -0
- package/payload/codex/plugin/skills/boxbox/references/grammar.md +147 -0
- package/payload/codex/plugin/skills/boxbox/references/readability-contract.md +311 -0
- package/payload/codex/plugin/skills/boxbox/references/typescript-grid-template.md +334 -0
- package/payload/codex-registration.mjs +62 -0
- package/payload/install-state.mjs +46 -0
- package/payload/platform.mjs +107 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# BoxBox Charter
|
|
2
|
+
|
|
3
|
+
Status: confirmed (M1.8 S1) · Date: 2026-09-30
|
|
4
|
+
|
|
5
|
+
This document is **layer 0** of the five normative layers. Grammar (layer 1), surface forms (layer 2), conventions (layer 3) and the example book (layer 4) all sit below it, and when they conflict with this document, this document wins. Only maintainers may change it.
|
|
6
|
+
|
|
7
|
+
## Core (generating source)
|
|
8
|
+
|
|
9
|
+
The three lines below are copied verbatim to the top of `SKILL.md` and into the marked section of a user repository's `AGENTS.md`. When the wording changes, the copies must change with it (pinned by a test in M1.8 S7).
|
|
10
|
+
|
|
11
|
+
<!-- boxbox:core:begin -->
|
|
12
|
+
1. Write same-role code in the same pattern. Code is language.
|
|
13
|
+
2. Simplify first, align second. Never cover a reducible difference with whitespace.
|
|
14
|
+
3. Fix automatically only whitespace, and only when certain. Suggest everything else.
|
|
15
|
+
<!-- boxbox:core:end -->
|
|
16
|
+
|
|
17
|
+
## 1. Principle — readability
|
|
18
|
+
|
|
19
|
+
Let the reader follow the flow from top to bottom and compare same-role code vertically. Every rule in this product exists for this one sentence.
|
|
20
|
+
|
|
21
|
+
## 2. Values — code is language
|
|
22
|
+
|
|
23
|
+
- Code is made of **words** (names), **sentences** (one line's declaration, call or condition), **paragraphs** (groups of sentences with one purpose) and **texts** (files).
|
|
24
|
+
- Write same-role sentences in **the same pattern**. Uniformity does not mean fitting every line to the longest one; it means giving the same role **the same minimal composition**.
|
|
25
|
+
- Symbols such as `=`, `:`, `from` and `(` are **particles** that mark roles inside a sentence. Alignment lines up the particles of the same pattern vertically so the reader can scan roles column by column.
|
|
26
|
+
- The same grammar serves both **guiding what gets written** (GUIDE) and **checking it** (REVIEW). The two must never look at different standards.
|
|
27
|
+
- We also know the limits of this metaphor. Understanding code relies mainly on reasoning, not on language ability. So patterns are defined by **role**, not by English word order, and the goal is to reduce the reader's reasoning load.
|
|
28
|
+
|
|
29
|
+
## 3. Identity — a deterministic program
|
|
30
|
+
|
|
31
|
+
- boxbox is a **deterministic program**, and the agent plugin is a channel that calls it. Rules are set by the program and by human approval, not by an agent's judgment.
|
|
32
|
+
- So it has what a program needs: a version, a config schema, exit codes, and a CLI that runs without an agent (CI, hooks, editors).
|
|
33
|
+
- Users can change **conventions** (layer 3) and **examples** (layer 4) to their taste. **Invariants** (section 5) can be changed by no one. Users write only one file themselves, the config; everything else is written by tools and approved by people.
|
|
34
|
+
|
|
35
|
+
## 4. Order — simplify first, align second, verify always
|
|
36
|
+
|
|
37
|
+
*Simplify first. Align second. Verify always. — Follow the structure. Don't force it.*
|
|
38
|
+
|
|
39
|
+
- Alignment is not the goal. A structure lengthened by an exceptional element is first removed, wrapped inward or normalized.
|
|
40
|
+
- Only real, irreducible information is accepted as cell data and aligned at minimum width.
|
|
41
|
+
- Covering a reducible difference with whitespace alignment, or letting whitespace pull semantic units apart, is a failure.
|
|
42
|
+
|
|
43
|
+
## 5. Invariants — unchangeable and not configurable
|
|
44
|
+
|
|
45
|
+
An attempt to turn off any item in this list through configuration is not silently ignored; it is a schema error. An item with an empty "enforced at" exists only as a documented promise, and moving it into code is roadmap work.
|
|
46
|
+
|
|
47
|
+
| # | Invariant | Enforced at |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| 1 | **Only whitespace changes.** Automatic fixes touch only spaces, tabs and line breaks. Non-whitespace tokens, strings and comments must be byte-identical | `common/09_preservation.ts`, `typescript/16_token-preservation.ts`, `test/mutation.test.ts` |
|
|
50
|
+
| 2 | **Output is re-parsed.** A fix is written only when the language's official parser rereads it with the same diagnostics | `common/08_parse-check.ts`, `typescript/07_align-tables.ts` |
|
|
51
|
+
| 3 | **When uncertain, do not touch.** `REFUSE` when it cannot be split losslessly, `UNCERTAIN` when classification is ambiguous. A missed fix is better than a wrong fix | `REFUSE`: across the engine · `UNCERTAIN`: not implemented |
|
|
52
|
+
| 4 | **Automatic fixes only for 3+ rows.** Two-row pairs are only suggested. The only exceptions are user-approved `=` declaration tables and imports | `common/06_table-engine.ts`, `typescript/13_grid-kinds.ts` `minRows` |
|
|
53
|
+
| 5 | **Idempotent.** Running twice gives the same result | per-language alignment tests (`test/lang-harness.ts`) |
|
|
54
|
+
| 6 | **Only relative placement is stored.** Width is always the minimum that table needs, and tables never share widths | `common/06_table-engine.ts` |
|
|
55
|
+
| 7 | **Deterministic.** The same input and config give the same result. There is no LLM and no similarity search on the check/fix path. An LLM is used only to read guidance or to pre-sort the review queue | design rule (no reproduction test) |
|
|
56
|
+
| 8 | **Only humans approve.** Evidence never becomes a rule by itself. Agents only investigate and propose | read-only agents: `test/plugin-contract.test.ts` · approval flow: not implemented |
|
|
57
|
+
| 9 | **Layer priority:** safety > project > user > preset > base. Nothing overrides safety | not implemented (M1.8 S3–S5) |
|
|
58
|
+
| 10 | **Single source, thin adapters.** The engine, grammar and conventions live in one place; hosts differ only in install location and manifest. The result JSON and pattern keys are versioned public contracts | `common/24_result-json.ts`, `common/02_signature.ts` |
|
|
59
|
+
| 11 | **Runs last, after the formatter.** The canonical form of a file is `boxbox(formatter(file))`, and rerunning must not change it | not implemented (M2) |
|
|
60
|
+
|
|
61
|
+
## 6. What this document does not do
|
|
62
|
+
|
|
63
|
+
- It does not define concrete patterns, roles or rules → grammar (`grammar.md`), conventions (`presets/`, `.boxbox/rules/`).
|
|
64
|
+
- It does not list the order in which tools are called → `SKILL.md`.
|
|
65
|
+
- It does not describe the product structure or roadmap → `docs/development/design.md`.
|
|
66
|
+
|
|
67
|
+
## 7. How to change it
|
|
68
|
+
|
|
69
|
+
1. First write the reason and evidence for the change in `docs/development/plan.md`.
|
|
70
|
+
2. A maintainer approves it.
|
|
71
|
+
3. When an invariant changes, change the code and tests that enforce it in the same change.
|
|
72
|
+
4. When the three core lines change, change the copies (`SKILL.md`, the marked section of the user's `AGENTS.md`) in the same change.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# BoxBox Grammar
|
|
2
|
+
|
|
3
|
+
Status: confirmed (M1.8 S2) · Date: 2026-09-30 · First scope: TypeScript, Python (Java is on hold; the Java column in the tables is for reference)
|
|
4
|
+
Review: Codex cross-review applied (`docs/development/reviews/2026-09-30-codex-grammar-review.md`)
|
|
5
|
+
|
|
6
|
+
This document is **layer 1** of the five normative layers. It sits below the charter (`charter.md`) and **defines every ID** used by surface forms (layer 2), conventions (layer 3) and the example book (layer 4). Lower layers cannot create IDs that are not in this document. This document defines **roles**, not the grammar of a particular language. Layer 2 decides which symbols and word order express each role in each language.
|
|
7
|
+
|
|
8
|
+
IDs are written in English capitals and are never translated.
|
|
9
|
+
|
|
10
|
+
## 0. Scope and premises
|
|
11
|
+
|
|
12
|
+
- This grammar is for **sentences that become alignment targets** and for **the sentence shapes GUIDE recommends**. It does not claim to classify every sentence in code. Sentences that fit no pattern are left unclassified and untouched.
|
|
13
|
+
- **The parser classifies first.** A pattern is a name attached to a structure the parser (AST, masked tokens) has confirmed. The "code is language" metaphor must never decide classification ahead of the parser and semantic analysis. Even with the same symbol shape, scope, control flow and side effects can differ.
|
|
14
|
+
- If it cannot be confirmed, it is `UNCERTAIN` (charter invariant 3).
|
|
15
|
+
|
|
16
|
+
## 1. Four units
|
|
17
|
+
|
|
18
|
+
| ID | Unit | In code | What is checked | Automation |
|
|
19
|
+
|---|---|---|---|---|
|
|
20
|
+
| `WORD` | word | a name (identifier) | whether the role shows, whether the name matches the behavior | GUIDE |
|
|
21
|
+
| `SENTENCE` | sentence | one line's declaration, call, condition or exit | whether it fits a pattern (section 3) | SUGGEST |
|
|
22
|
+
| `PARAGRAPH` | paragraph | a group of sentences separated by blank lines | whether it has one purpose (section 4) | SUGGEST |
|
|
23
|
+
| `TEXT` | text | a file, a function body | whether the paragraph order fits the genre (section 5) | GUIDE |
|
|
24
|
+
|
|
25
|
+
**When the property differs, split the paragraph.** A sentence's property is its pattern (section 3) and, for a definition, its **declaration kind**. Put a blank line where the property changes. Examples: a group of value definitions (`const`) → a check (`WHEN`+`EXIT`) → a result (`EXIT`); in tests, a group of setup (`const`) → a group of assertions (`expect`).
|
|
26
|
+
|
|
27
|
+
- **Declaration kind** uses only what can be determined reliably per language. The first criterion separates **declarations erased after compilation** (TypeScript `type`, `interface`, `import type`, `declare`) from **declarations that remain at run time** (`const`/`let`/`var`, `function`, `class`, `enum`). `class` and `enum` are also types but remain at run time, so they belong to the latter. Erased declarations are grouped separately instead of being slotted one by one between executed declarations.
|
|
28
|
+
- **Observational information:** visibility (`export`) and reassignability (`const`/`let`) are not mandatory criteria for splitting paragraphs. Making them mandatory over-fragments paragraphs; `const` only forbids reassignment rather than making a value immutable; and Python has no matching marker. They are shown as information in reports, and conventions (layer 3) decide whether to split.
|
|
29
|
+
- **Keep the original order.** The engine never reorders sentences. The basic principle is **preserving the original execution and evaluation order**, and dependencies (no use before declaration, TDZ) are one reason for it. Whether moving is safe differs by language: JavaScript function declarations are hoisted and erased types have no execution order, but Python's `def` evaluates defaults and decorators at definition time and module-level `import` can have side effects. So grouping by property goes only as far as inserting blank lines **within sentences that are already consecutive**, and proposals to move sentences are left to humans.
|
|
30
|
+
- **Inserting a blank line is also a suggestion.** It changes only whitespace, but changing a paragraph boundary changes what the next alignment targets. Inserting automatically first requires verifying syntactic boundaries, behavior preservation, a fixed split criterion, invariance on rerun, and that splitting and aligning do not keep flipping each other. Line breaks inside strings or continued expressions are not targets.
|
|
31
|
+
|
|
32
|
+
**Exception — a check right after a definition:** a guard that checks a name defined just above (`if (!found) throw …;` after `const found = …;`) stays in the same paragraph as the definition, because "find it, and refuse if missing" is one purpose (the extract → refuse ladder of the `ALGORITHM` genre has this shape too). If the guard checks a different name, it is not an exception.
|
|
33
|
+
|
|
34
|
+
**Paragraphs and tables differ.** A paragraph is a group separated by blank lines, and one paragraph may hold sentences with several roles. Normally a **table** is **three or more consecutive sentences of the same pattern** within one paragraph, with documented declaration/import pair exceptions. Tables have independent widths and normally stop at blank lines or comments.
|
|
35
|
+
|
|
36
|
+
**Scoped return-guard exception (2026-10-21 user clarification):** supported `WHEN` sentences of the form
|
|
37
|
+
`if (condition) return value;` can share a return-start column across ordinary statements and blank lines in one
|
|
38
|
+
immediate block. All statements retain their original order. Inner blocks calculate their own widths; their guards
|
|
39
|
+
do not enter the outer table. Comments between statements, unsupported guards, unconditional exits and function/class
|
|
40
|
+
declarations separate regions. See [column-sharing regions](readability-contract.md#column-sharing-regions).
|
|
41
|
+
|
|
42
|
+
## 2. Roles and markers
|
|
43
|
+
|
|
44
|
+
Sentences are made of **roles**, and roles show through **markers** (symbols). Markers work like grammatical particles: even when word order differs, the marker tells the role. Alignment lines up the markers of the same pattern vertically.
|
|
45
|
+
|
|
46
|
+
**Roles are defined only within a pattern.** The same `:` is the marker after `KEY` in `MAP` and the marker after `NAME` in `DESCRIBE`. Pairs whose role boundaries look blurry (`NAME`/`TARGET`/`KEY`, `VALUE`/`RESULT`, `ITEM`/`ARGS`) are told apart only by the role lists in section 3, after the pattern is decided.
|
|
47
|
+
|
|
48
|
+
| ID | Role | Used in patterns |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| `NAME` | a newly created name | `DEFINE`, `DESCRIBE` |
|
|
51
|
+
| `TYPE` | a type | `DEFINE`, `DESCRIBE` |
|
|
52
|
+
| `VALUE` | the value given to a name, target or key | `DEFINE`, `DESCRIBE`, `MAP`, `CHANGE` |
|
|
53
|
+
| `KEY` | the left side of a mapping | `MAP` |
|
|
54
|
+
| `ITEM` | one cell of a list | `LIST`, `IMPORT` |
|
|
55
|
+
| `SOURCE` | where something is imported from | `IMPORT` |
|
|
56
|
+
| `TARGET` | something that already exists (the receiver of a call, the thing that changes) | `DO`, `CHANGE` |
|
|
57
|
+
| `ACTION` | the function or method called | `DO` |
|
|
58
|
+
| `ARGS` | the values passed to a call | `DO` |
|
|
59
|
+
| `CONDITION` | a condition, the match pattern of a `case` | `WHEN` |
|
|
60
|
+
| `RESULT` | what is handed over when ending the flow (return value, thrown exception) | `EXIT` |
|
|
61
|
+
|
|
62
|
+
The match pattern of a `case` differs from a general condition (value comparison, destructuring), but this grammar groups both under `CONDITION`. If they ever need to be told apart, this document changes to create a new role.
|
|
63
|
+
|
|
64
|
+
## 3. Patterns (closed set, 9)
|
|
65
|
+
|
|
66
|
+
| ID | Meaning | Roles (in logical order) | TypeScript | Python | Java |
|
|
67
|
+
|---|---|---|---|---|---|
|
|
68
|
+
| `IMPORT` | imports | ITEM · SOURCE | `import { a } from "m";` | `from m import a` | `import m.a;` |
|
|
69
|
+
| `DEFINE` | defines | NAME · [TYPE] · VALUE | `const a = 1;` | (ambiguous, rule 2) | `int a = 1;` |
|
|
70
|
+
| `DESCRIBE` | describes | NAME · TYPE · [VALUE] | `a: string;` | `a: str` | `private int a;` |
|
|
71
|
+
| `MAP` | maps | KEY · VALUE | `a: handler,` | `"a": 1,` | — |
|
|
72
|
+
| `LIST` | lists | ITEM… | `[1, 2],` | `(1, 2),` | `{1, 2},` · `RED,` |
|
|
73
|
+
| `DO` | acts | TARGET · ACTION · ARGS | `log.info(a);` | — | — |
|
|
74
|
+
| `CHANGE` | changes | TARGET · VALUE | `a = 1;` | `self.a = 1` | — |
|
|
75
|
+
| `WHEN` | if the condition holds | CONDITION · (inner sentence) | `case A: …` | — | — |
|
|
76
|
+
| `EXIT` | ends | [RESULT] | `return a;` · `throw e;` | `return a` | `return a;` |
|
|
77
|
+
|
|
78
|
+
- `—` means that language's engine does not build tables of this pattern yet (layer 2's job). It does not mean the pattern is absent from the language.
|
|
79
|
+
- `EXIT` covers `return`, `throw`, `break` and `continue`. An unconditional `return value` is `EXIT`, not `WHEN`. A guard (`if (x) return;`) is **a sentence with `EXIT` inside `WHEN`**.
|
|
80
|
+
- The inner sentence of `WHEN` is another pattern (`EXIT`, `DO`, `CHANGE` …). `WHEN` only names the pairing of a condition and its inner sentence.
|
|
81
|
+
|
|
82
|
+
### Decision rules (ambiguous cases)
|
|
83
|
+
|
|
84
|
+
1. **An enumeration with values is `MAP`; names only is `LIST`.** `enum { A = 1 }` and `RED(1, 2)` are MAP, and `RED,` is LIST.
|
|
85
|
+
2. **With a declaration marker it is `DEFINE`; if the target certainly exists already it is `CHANGE`; otherwise it is ambiguous.**
|
|
86
|
+
- Declaration markers: TypeScript `const`/`let`/`var`, Java type declarations.
|
|
87
|
+
- Certain `CHANGE`: assignment to a property or element (`a.b =`, `a[i] =`), assignment without declaration in TypeScript and Java.
|
|
88
|
+
- Python's `a = v` shares syntax between declaration and assignment. Until the parser confirms whether it is module level (indentation cannot tell: the inside of a module-level `if` is indented too), whether it is `global`/`nonlocal`, and whether the name is bound for the first time, it is **ambiguous**.
|
|
89
|
+
3. **Ambiguous rows are never grouped into one table with certain rows.** When a certain `DEFINE` row and an ambiguous row are adjacent, the table is split. Whether a table of ambiguous rows may be fixed is decided by verdict (layer 4). Until a verdict exists, the current behavior is kept and the result is marked ambiguous.
|
|
90
|
+
4. **The container decides the pattern.** `a: T = v` in a class body is `DESCRIBE` (a field with a default), and `a: T = v` inside a function is `DEFINE`. In an engine that does not know the container (the current Python line engine), it is ambiguous.
|
|
91
|
+
5. **A new pattern exists only by changing this document.** It needs evidence (corpus table counts) and human approval. Conventions and examples cannot create patterns.
|
|
92
|
+
|
|
93
|
+
## 4. Paragraph purposes
|
|
94
|
+
|
|
95
|
+
States what a `PARAGRAPH` groups sentences for. Used when checking the order of a function body (text). **The levels are only SUGGEST and GUIDE; there is no automatic fix.**
|
|
96
|
+
|
|
97
|
+
| ID | What it does | Patterns mainly used |
|
|
98
|
+
|---|---|---|
|
|
99
|
+
| `CHECK` | sends back the failing cases first | `WHEN` + `EXIT` |
|
|
100
|
+
| `PREPARE` | gathers what is needed | `DEFINE` |
|
|
101
|
+
| `DECIDE` | decides without side effects | `DEFINE`, `WHEN` |
|
|
102
|
+
| `ACT` | changes the outside world | `DO`, `CHANGE` |
|
|
103
|
+
| `RESULT` | returns | `EXIT` |
|
|
104
|
+
|
|
105
|
+
**Verbs are only hints.** Lookup verbs (`find`, `get`) likely mean preparation and action verbs (`save`, `send`) likely mean acting, but often, as with `load` or `create`, the name alone cannot reveal side effects. If control flow and outputs cannot confirm it, the purpose is `UNCERTAIN`. Verb dictionaries differ by project, so they live in conventions (layer 3) and the example book (layer 4).
|
|
106
|
+
|
|
107
|
+
Loop shapes (filter, find, collect, emit) are convention (layer 3) vocabulary, not grammar IDs, because their purpose spans several sentences rather than one line's pattern.
|
|
108
|
+
|
|
109
|
+
## 5. Genres
|
|
110
|
+
|
|
111
|
+
Order rules for a text (`TEXT`) differ by genre. Genres are set by config scopes (`scopes`).
|
|
112
|
+
|
|
113
|
+
| ID | Text order | Examples |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| `BUSINESS` | CHECK → PREPARE → DECIDE → ACT → RESULT | services, handlers |
|
|
116
|
+
| `ALGORITHM` | no order rule; an "extract → refuse" ladder is considered normal | parsers, alignment engines |
|
|
117
|
+
| `TEST` | Arrange → Act → Assert | tests |
|
|
118
|
+
| `CONFIG` | mostly `MAP` and `LIST`, no order rule | config, registration tables |
|
|
119
|
+
|
|
120
|
+
Scopes without a genre get no order rule.
|
|
121
|
+
|
|
122
|
+
## 6. Evidence (measured 2026-09-30)
|
|
123
|
+
|
|
124
|
+
Every **table of three or more rows** in the pinned corpus and the stress corpus was counted and mapped to a pattern. These numbers concern sentences that became tables; they are not evidence that every sentence is explained by the nine patterns (section 0).
|
|
125
|
+
|
|
126
|
+
| Pattern | TypeScript (7,214) | Python (879) | Java (162) |
|
|
127
|
+
|---|---|---|---|
|
|
128
|
+
| `DEFINE` | 3,367 | — | 154 |
|
|
129
|
+
| `DEFINE` or `CHANGE` (ambiguous) | — | 301 | — |
|
|
130
|
+
| `DESCRIBE` | 871 | 128 (including 26 container-ambiguous) | 2 |
|
|
131
|
+
| `MAP` | 2,259 | 339 | — |
|
|
132
|
+
| `LIST` | — | 111 | 6 |
|
|
133
|
+
| `DO` | 602 | — | — |
|
|
134
|
+
| `CHANGE` | 83 | — | — |
|
|
135
|
+
| `WHEN` | 32 | — | — |
|
|
136
|
+
|
|
137
|
+
- No table was left unmapped.
|
|
138
|
+
- TypeScript and Python: the pinned corpus in `development/corpus/manifest.json` (vscode, prisma, trpc, zod, requests, fastapi, pydantic, django), at most 400 files per source.
|
|
139
|
+
- Java: dropped from the first scope (2026-09-30). The table's numbers come only from five OpenJDK files in the stress corpus, chosen as the longest files and therefore biased, and are GPL so used for checking only. **Java distributions are not used as evidence until a permissively licensed Java corpus is pinned (M1.8 S2.5).**
|
|
140
|
+
- `IMPORT` is handled by a separate step (`00_normalize-imports.ts`), so it is not counted. `EXIT` rarely forms tables of three or more rows, so it is not counted; it is used in GUIDE and paragraph checks.
|
|
141
|
+
- 51.5% of TypeScript tables are inside function bodies. Patterns are used not only in declarations but also inside business logic.
|
|
142
|
+
|
|
143
|
+
## 7. Open questions
|
|
144
|
+
|
|
145
|
+
- Whether to introduce scope analysis (the official `ast` module) to separate Python `DEFINE`/`CHANGE`.
|
|
146
|
+
- Classification priority of value shapes (value/computation, call/construction, `await`, object/array, `as`) when they overlap, as in `await f() as T`.
|
|
147
|
+
- Whether to separate the match pattern of a `case` from `CONDITION`.
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# Readability Contract
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Compare code as rows, columns and cells, where uniformity means **giving the same role the same minimal composition**. First remove, bind or normalize structures that became long because of exceptions. Apply tabular alignment when three or more comparable same-role rows are contiguous. Make a table from two rows only when the user confirmed the comparison is useful. Column 120 is a signal to consider splitting a new structure, not a ceiling for dismantling an already accepted canonical grid. Alignment is not the goal in itself: covering a reducible structure with whitespace, or letting whitespace push semantic units apart, is a failure.
|
|
6
|
+
|
|
7
|
+
Uniformity priorities:
|
|
8
|
+
|
|
9
|
+
1. Give the same role the same structure and reading order.
|
|
10
|
+
2. Check whether a foreign element mixed into the base form is a required contract or a structural leak.
|
|
11
|
+
3. Simplify the structure by removal, internal binding, normalization or table splitting while preserving meaning and type safety.
|
|
12
|
+
4. Accept only irreducible, real information as cell data and align it at minimum width.
|
|
13
|
+
|
|
14
|
+
`The same minimal composition` does not mean abbreviating until meaning is lost. Remove only foreign elements that can go while preserving behavior, type safety and domain responsibility; keep necessary differences as explicit contracts.
|
|
15
|
+
|
|
16
|
+
Therefore the longest code is not always the alignment basis. First decide whether that length is domain information, boundary information, an implementation detail or a duplicate expression, then use only the preserved longest data as the basis.
|
|
17
|
+
|
|
18
|
+
## Top-to-bottom role structure
|
|
19
|
+
|
|
20
|
+
- A feature implementation file puts the public features consumers look for first, then individual processing, then response conversion and validation. The reference order for a lookup adapter is `public feature → individual lookup → response conversion`. Do not add functions or sections just to fill a missing role.
|
|
21
|
+
- Functions with the same role use the same reading order and naming scheme. Before reordering declarations, check initialization and registration order dependencies, and preserve execution order.
|
|
22
|
+
- Rename abbreviations whose role must be decoded — `opts`, `res`, `d` — to role-revealing names such as `options`, `response`, `document`. Collections reveal their content and lookup key, as in `placesById`; do not mechanically lengthen idiomatic indices.
|
|
23
|
+
- Apply reuse only to implementations with confirmed identical responsibility, input/output and failure contract. Do not merge lookups or conversions that merely look alike and thereby hide the call flow, and do not add wrappers that only forward names.
|
|
24
|
+
- When applying these structural principles to other languages such as Kotlin, first check that project's declaration and lifecycle rules. Do not port the TypeScript column alignment and check commands verbatim.
|
|
25
|
+
|
|
26
|
+
## Grid model
|
|
27
|
+
|
|
28
|
+
- **Table**: a contiguous code block with the same syntax structure and comparison purpose.
|
|
29
|
+
- **Row**: one declaration, property, import or execution flow.
|
|
30
|
+
- **Column**: a repeated syntax boundary such as `:`, `( )`, `{ }`, `< >`, `=>`, `from`, `= await`, `//`.
|
|
31
|
+
- **Cell**: the actual identifier, type, value, statement or note between columns.
|
|
32
|
+
- **Sub-table**: an inner grid re-decomposed into name, separator and type when a repeated structure such as `:` appears again inside a cell.
|
|
33
|
+
- **Alignment policy**: align contiguous repeated blocks of three or more comparable rows at minimum width. User-confirmed exceptions: `=` declaration tables and imports align from two rows. Other two-row groups are reported as pairs and never fixed automatically. Methods of a public Port split by JSDoc or different lifecycle responsibilities are not merged into one table. Per-field JSDoc does not by itself split same-role data fields: a required data record keeps its fields in one table. This authoring principle also applies beyond the implemented detector; record unsupported regions separately. Preserve the `import`, `:`, `=>` and `case` axes of a reference file accepted by the user. The inner width of `( )` in a repeated function table is set by the actual longest argument, not by existing spaces; only short values are centered within that minimum width.
|
|
34
|
+
|
|
35
|
+
The public B-layout Align path changes horizontal layout only. It preserves non-whitespace tokens, comments, identifiers, their
|
|
36
|
+
order, leading indentation, physical line count and the structural TypeScript AST. It may align an existing function
|
|
37
|
+
call, but it never introduces a function or performs a C-level structural refactor. Authoring may choose line breaks or
|
|
38
|
+
move a leading logical operator before this path; those changes require their own token/comment and structural-AST checks.
|
|
39
|
+
|
|
40
|
+
## Column-sharing regions
|
|
41
|
+
|
|
42
|
+
The matrix is defined by comparable syntax boundaries, not by whole-line length. A condition and a callee can have
|
|
43
|
+
different widths while their corresponding `return` or call parentheses share a column. Scope the comparison first,
|
|
44
|
+
then compute minimum widths from the cells in that region. A different expression is not automatically a different role.
|
|
45
|
+
|
|
46
|
+
- **Early-return guards:** treat the start of `return` in `if (condition) return value;` as the `guard-return` gap axis.
|
|
47
|
+
In one immediate block, three or more supported guards can share that column across ordinary statements, including
|
|
48
|
+
declarations and loops, and blank lines. Preserve every statement in place. Only immediate sibling guards contribute
|
|
49
|
+
to the column width; a nested block owns its own widths. Comments between statements, unsupported guards (including
|
|
50
|
+
`else`), unconditional return/throw/break/continue and function/class declarations separate regions. A trailing
|
|
51
|
+
comment also separates that statement from the next guard region. The final
|
|
52
|
+
unconditional return is a different role.
|
|
53
|
+
- **Different callees:** consecutive supported call-valued declarations can share their outer `(` and `)` columns even
|
|
54
|
+
when callee names or arities differ. The argument list is one cell in this outer table; compatible same-call rows can
|
|
55
|
+
additionally share inner argument columns. A trailing non-null assertion stays attached after `)`. Empty `()` stays
|
|
56
|
+
attached and unsupported wrappers keep their own shape. `declaration-call-envelope` has the declaration two-row floor.
|
|
57
|
+
- **Member-call boundary:** split a supported property callee into `receiver | . | method`, then its call parentheses.
|
|
58
|
+
`call-member-dot` aligns the final receiver/method separator when at least two rows have that role. For example,
|
|
59
|
+
`Array.from` and `rows.filter` share the dot column as well as the parentheses. In `registry.rows.filter`, the receiver
|
|
60
|
+
is `registry.rows`; its inner dot remains inside that cell. A bare function has no member-dot cell. Decimal points,
|
|
61
|
+
string contents, optional chains and dots inside arguments are different syntax and are not matched to this axis.
|
|
62
|
+
- **Property rows:** a property header keeps its `name | :` comparison when its value continues on later lines.
|
|
63
|
+
Shorthand properties participate in the same property run without inventing a `:` boundary.
|
|
64
|
+
A multiline value's terminal punctuation belongs to that value; it does not widen a comma column on unrelated
|
|
65
|
+
one-line properties. Within the retained region, each consecutive run of at least three ordinary one-line explicit
|
|
66
|
+
properties computes its own minimum-width colon, value-start and comma columns. Remaining explicit and multiline
|
|
67
|
+
headers share a fallback family; shorthand contributes only its existing comma gap and never gains a colon or a
|
|
68
|
+
missing comma. Families are ordered by source: the first uses unsuffixed axes and later families use suffixes `2`,
|
|
69
|
+
`3`, and so on. Only a three-or-more ordinary one-line explicit family exposes `property-value`. Blank lines and
|
|
70
|
+
interstitial comments split the comparison region.
|
|
71
|
+
- **Function parameters:** an already vertical `FunctionDeclaration` list with at least three contiguous one-line
|
|
72
|
+
identifier parameters is a `vertical-function-parameters` table. Typed rows share `parameter-colon`, defaulted rows
|
|
73
|
+
share `parameter-equals`, and existing separators share `parameter-comma`. Each axis needs two occurrences within
|
|
74
|
+
the three-or-more-row table; two defaulted rows do not create a two-row table exception. The last parameter keeps
|
|
75
|
+
its original comma presence. Optional names and generic declarations keep their syntax. Destructuring, rest,
|
|
76
|
+
parameter modifiers, comments, multiline types/defaults, blank lines and shared opening/closing lines exclude the
|
|
77
|
+
whole list rather than exposing a supported subset. A declaration spanning at least three distinct parameter rows
|
|
78
|
+
reports that exclusion as `REFUSE` with its original parameter range; shorter and single-line lists are not candidates.
|
|
79
|
+
Arrow functions and methods are outside this kind. Parameter
|
|
80
|
+
descriptions map to `DESCRIBE`; detection and guidance do not grant public write approval.
|
|
81
|
+
- **Logical chains:** an existing multiline homogeneous `||` or `&&` chain uses `operator | one space | optional ! |
|
|
82
|
+
operand`. The operator cell is two characters and the negation cell is either one `!` or empty. `!=` and `!==` are
|
|
83
|
+
indivisible comparison operators, and `!!value` stays opaque. A virtual first-row operator/negation prefix is an
|
|
84
|
+
authoring aid only; public Align never inserts it or moves an operator across lines.
|
|
85
|
+
- **Conditional call arguments:** declarations with the same callee and arity may align a compatible comparison or
|
|
86
|
+
conditional expression at the same argument position. This nested layer has the declaration two-row floor; ordinary
|
|
87
|
+
kinds still need three rows. Callee, arity, argument position and expression shape must match. An incompatible nested
|
|
88
|
+
expression drops the layer rather than broadening the parent table. Inside a larger heterogeneous declaration group,
|
|
89
|
+
recursively detected disjoint runs of two or more adjacent compatible declarations form local
|
|
90
|
+
`declaration-call-expressions` subgroups. The outer `declaration-equals` group keeps its `equals`/`semicolon` axes and
|
|
91
|
+
legacy approval identity; each local subgroup owns only its call and comparison/conditional axes. A wholly compatible
|
|
92
|
+
declaration group keeps the existing whole-group path. Local subgroup detection is not approval, so an unknown subgroup
|
|
93
|
+
remains blocked from public writes.
|
|
94
|
+
- **Adjacent `if` headers:** three or more adjacent same-family headers may align even when a one-line condition has a
|
|
95
|
+
multiline body. Blank lines and interstitial comments split the run. Braced rows align `)` and `{`; unbraced-return
|
|
96
|
+
rows align `)` and `return`. The two forms are separate families, and alignment never inserts braces.
|
|
97
|
+
- **Exceptional type shapes:** in an authorized Refactor, keep ordinary interface properties in a compact table and
|
|
98
|
+
put nested object type members in their own paragraphs, below the ordinary fields when the type contract permits it.
|
|
99
|
+
Expand their nested properties into independent tables. Preserve names, readonly/optional modifiers and member types.
|
|
100
|
+
Runtime declarations and object initializers retain their evaluation order; separate a dependent exceptional expression
|
|
101
|
+
in place instead of moving it below its consumers. A new type alias needs a domain reason beyond shortening a column.
|
|
102
|
+
|
|
103
|
+
The return-guard region is an explicit exception to contiguous-row grouping. Other kinds keep their existing grouping
|
|
104
|
+
rules. These authoring choices and engine detection do not create public write approval: new group/axis lookup keys
|
|
105
|
+
still require their own proof. Report checked columns and unsupported regions separately.
|
|
106
|
+
|
|
107
|
+
## Confirmed rules
|
|
108
|
+
|
|
109
|
+
1. Modify only one product file at a time.
|
|
110
|
+
Files the user designated as close to canonical are excluded from modification and read only as a comparison basis.
|
|
111
|
+
2. Imports separate runtime and type roles, and paths inside `src` use the `@/` alias. Splitting a named import that mixes values and `type` specifiers into `import` and `import type` declarations changes tokens and belongs to a separate Write/Refactor normalization path; the layout-only import authoring path refuses and reports it. A single named specifier always stays on one line; two or more fold onto one line when the completed line's display width is 120 or less. Only above 120 do they break into one item per line, with the closing `}` and `from` on the same line. All brace imports of an import section (groups separated only by blank lines) form one table: the head cell always reserves the `import type` width, so `{` sits in one column; the fixed-width `{ }` cell (names left-aligned one space after `{`), `from`, the module specifier and the terminating `;` share columns across every group. An import goes vertical — one name per line on the names column, aligned commas, and the closing `}` on the table's `}` column — when (A) its one-line form exceeds 120 columns, (B) its names are 20+ columns wider than the second-widest names, or (C) the aligned table still exceeds 120 columns, taking the widest names first. Preserve whether the original final named specifier has a trailing comma in both compact and vertical form. Imports without braces (default only, namespace, `defer`, side-effect, `import x = require`) and imports with attributes stay as written without breaking the table; a lone brace import stays compact. Files without semicolons get no `;` column. When applying the alias to a new boundary, also verify the architecture import graph.
|
|
112
|
+
3. Split a table block into cells by syntax units: identifier, `:`, `( )`, `=>`, `{ }`, `< >`, `= await`, `from`, trailing `//`.
|
|
113
|
+
4. Unify each cell at the minimum width required by the longest actual content in the same block. Left-align feature/function identifiers and execution cells. Never create an arbitrary column farther than the actual longest row.
|
|
114
|
+
5. Parentheses, braces and angle brackets are each independent outer boundaries. In the same comparison block, align both opening and closing symbols. The inner width is set by the longest actual single-line content; short and empty values sit inside that fixed width.
|
|
115
|
+
6. When type parameters push a line past column 120, break one parameter per line.
|
|
116
|
+
7. Repeated syntax inside an outer cell is also aligned as a sub-table when there are two or more comparable rows. Recompute the width of a function-argument cell from the actual longest value. Center short values within that width; write empty arguments attached as `()` and put the leftover width outside the parentheses. Do not reuse existing unnecessary inner spaces as a width basis.
|
|
117
|
+
8. Keep the `{ }` of production destructuring as a fixed-width cell. The imported callable identifiers are function names, so left-align them one space after `{` with leftover width on the right. Align the closing `}` and the following `= await` column on every row.
|
|
118
|
+
9. The body `{ }` of a one-line arrow function is an execution block, not a value cell. Align the opening `{` and the start column of the last statement, but end the last call at its actual length. Put exactly one space after the terminating `;` and close with `}`; add no trailing spaces to align the closing brace column.
|
|
119
|
+
10. Group production dependencies by role — run, list, select, write. Do not arbitrarily break the accepted `const { } = await import(...)` and execution-cell axes of the reference file into multiple lines.
|
|
120
|
+
11. A trailing note is the last open cell. Align the `//` start column across role sections in the same declaration table. Keep the minimum width of preceding cells such as `Promise< >` and move `//` only with the outer space after the statement terminator. Left-align the note one space after `//`; add no right padding to equalize note lengths.
|
|
121
|
+
12. Repeated implementations are one line by default. Break into two lines at a semantic boundary only when a line becomes excessively long; do not expand short rows wholesale.
|
|
122
|
+
13. Keep a dependency interface as one interface and split roles with `// Run`, `// List`, `// Select`, `// Write` section comments. Do not split the type into per-role interfaces.
|
|
123
|
+
Each role section is an independent table. The widths of `( )`, `Promise< >`, `;` and `//` are computed only from the actual longest value inside the section; do not borrow a long type from another section as a width basis.
|
|
124
|
+
14. Do not extract function signatures into named local types just to reduce table width. In the same comparison block, use the longest actual signature as the width basis as is. Split out a type only when it has an independent domain meaning or reuse contract.
|
|
125
|
+
15. Before repeating an inline object type, find and reuse an existing Domain or Port type that owns the same meaning. The purpose of naming a type is to reveal an existing contract's meaning, not to shorten rows.
|
|
126
|
+
16. Column header comments above a table use only ASCII labels such as `NAME`, `PARAMETERS`, `RETURN TYPE`. CJK display width varies across terminals and editors, so it is not used as reference data for column positions. The header's syntax boundaries and sub-cell start columns must match the actual code rows.
|
|
127
|
+
17. When a long exceptional element is found, check its definition, callers and tests before aligning. Information consumers do not choose — such as a parameter passed as the same constant at every call — should be considered for removal from the interface and binding inside the implementation adapter.
|
|
128
|
+
18. Structural simplification must preserve meaning and type safety. Removing required inputs, merging different contracts, or adding a type alias just to look shorter is not simplification.
|
|
129
|
+
19. Uniformity does not force the longest exception's width onto every row. When an element that differs from the base rows widens the whole table, first consider moving it to a separate row, a separate table or the internal implementation; include it in the common width only when it cannot be separated semantically.
|
|
130
|
+
20. `?` does not mark something for removal; it means omission of the value is part of the contract. Keep it only where omission is really possible at the input boundary, and narrow to a concrete type as early as possible inside, via condition checks, defaults and normalization. On a public or boundary interface where the meaning of omission is not obvious from the name, a short `/** JSDoc */` right before the property owns that meaning, and its exposure is verified with an actual property hover.
|
|
131
|
+
21. Do not mix `null` and `undefined` for the same absent state. Express explicit results such as a user cancel with `null`, and a missing object property with `?`. When code appears to convert between the two across layers, revisit the boundary contract.
|
|
132
|
+
22. For `!` on array indices and `as` on wide values, first check whether a local variable and a conditional branch can remove them. An assertion that silences the checker without runtime evidence is not simplification.
|
|
133
|
+
23. Do not alias primitives that are already short and clear, such as `void`, `string`, `boolean`. Give a short, specific name to a long, repeated composite type only when it has an independent domain meaning.
|
|
134
|
+
24. Supplementary documentation of named types and public optional properties is owned by a `/** JSDoc */` right before the declaration. String literals and trailing `//` are not hover documentation; find the actual declaration with the TypeScript LSP `textDocument/documentSymbol`, then confirm the documentation appears in the `textDocument/hover` response. Specify same-name declarations as `name#occurrence`, and fail ambiguous requests or requests without JSDoc.
|
|
135
|
+
However, consecutive local type aliases that are not exported are grouped into a `type / name / = / actual type / ; / // note` table. That note is for in-file navigation and does not replace the public hover contract.
|
|
136
|
+
25. Record the minimal common form of the repeated rows first. When a type modifier, extra parameter, wrapper, absence expression or conversion code absent from the base form appears, do not adopt it as the width basis immediately; classify it as a foreign element.
|
|
137
|
+
26. Judge each foreign element as one of `required domain information`, `boundary-only information`, `implementation detail`, `duplicate expression`. Keep boundary information near the boundary, implementation details inside, and remove duplicate expressions.
|
|
138
|
+
27. Do not accept a structural change just because it looks shorter. Accept it as simplification only when the base form actually became more uniform and behavior, type safety and layer responsibility were preserved.
|
|
139
|
+
28. With `exactOptionalPropertyTypes` off, `property?: T` also accepts an explicit `property: undefined`. Inspect property omission and explicit `undefined` at call sites separately, and unify on omission when they mean the same absence. Keep `| undefined` as an explicit contract only when a different meaning is confirmed.
|
|
140
|
+
29. For optional tightening, first run `04_audit-optional-types.ts` as a read-only migration gate. The default run requires zero errors, and `--max-errors <current baseline>` fails on any increase. `--file <target file>` keeps the global verdict while separately reporting line, column, error code and diagnostic message for that file. Enable it in `tsconfig.json` only after confirming zero total errors and passing related behavior tests; never pass the gate by appending `| undefined` wholesale.
|
|
141
|
+
30. The population for the uncertainty audit is extracted from the TypeScript AST by `03_audit-type-uncertainty.ts --file <target file>`. It reports optional properties, parameters, methods, tuples and types, `null`, `undefined`, non-null assertions, definite assignments, `as` and angle-bracket assertions as distinct kinds. Logical negation and the conditional operator are not listed; the output is not an automatic removal list, and each item is judged from definitions, callers and tests.
|
|
142
|
+
31. A feature implementation file may carry a function map at the top (`GROUP | FUNCTION | INPUT | RETURN | CALLS | ROLE`). FUNCTION maps 1:1 to declarations in the file, the INPUT count must match the parameters, and names listed in CALLS must exist in the file. Drift between map and code is locked by the `09_function-map.ts` check. GROUP and ROLE follow the sections and roles of the reading flow. The repository-wide flow summary is still owned by audit documents under `docs/`.
|
|
143
|
+
32. Public Port and Domain contracts default to compact TypeScript notation. Do not add role comments to a method list that mixes JSDoc, optional capabilities and lifecycle responsibilities to create a fake table, and do not pad type internals to align the closing `>` of `Promise< >`. This exception does not apply merely because an ordinary data field has JSDoc; same-role data fields retain their common name, colon and type columns.
|
|
144
|
+
33. In a same-role object array, each object is one row. When omitting an optional property is merely a presentational default, normalize every row to the same property cells and align `{`, each `:`, each `,` and `}` to the actual longest value. When the omission itself carries domain meaning, do not overwrite it with an explicit value; split it into a separate table.
|
|
145
|
+
34. An object-based column specification and the generation of data rows for those columns are independent tables. Do not nest the two tables inside complex call arguments; split each into a named local value and keep the call site minimal, as in `{ columns, rows }`.
|
|
146
|
+
35. The terminating `;` is no longer attached as an exception. Within a table, align its column to the actual longest value like any other column. One-line execution blocks where `}` immediately follows `;` (rule 9) remain a separate case and are not overridden by this rule.
|
|
147
|
+
36. A standalone statement with no rows to compare against (fewer than three same-role repetitions, so no table forms) is not split vertically for alignment. Short flows originally read as one line, such as `if (condition) run() return`, prefer the compressed one-line form.
|
|
148
|
+
37. In interface and class member declarations, one member owns one physical row. Do not chain several property or method declarations on the same row with `;`, hiding the table's row boundaries. Short public contracts are no exception.
|
|
149
|
+
38. In authoring, a homogeneous guard or validation chain with at least four operands may be split into a multiline condition with `||` or `&&` at line start. Column 120 is an authoring consideration, not a generic wrapping trigger, and an accepted canonical grid stays intact. Mixed operators, `!!`, comments and other unsupported shapes are reported for manual judgment. This layout rule does not create predicates, functions or wrappers; any such refactor is a separately authorized structural change.
|
|
150
|
+
39. In a separately authorized Refactor, do not inline feature slots (approvalPreview, reconciliation, execute, ...) inside anonymous ternaries or conditional spreads. Extract each slot into one named local function so it reads top to bottom, and leave the assembly point in the minimal form `...(write ? { approvalPreview, reconciliation } : {})`. B-layout only reports this structural opportunity.
|
|
151
|
+
|
|
152
|
+
## Column anchors
|
|
153
|
+
|
|
154
|
+
Before writing a repeated block, decide the cells and syntax axes to compare. Besides object arrays, registration objects and local computation declarations are in scope.
|
|
155
|
+
|
|
156
|
+
- **Registration objects**: one entry is one row. Split into `property name | : | expression` and align `:` and the expression start column. Inside repeated `new` or `scroll(new …)`, compare constructor names and argument parentheses as sub-cells. Do not force a lone exceptional wrapper onto other rows as a width basis.
|
|
157
|
+
- **Consecutive declarations**: in `const | name | = | expression` of the same computation step, align `=` and the expression start column. A multi-line array initialization is a separate table and does not share axes with local declarations inside callbacks or with other computation steps.
|
|
158
|
+
- **Check scope**: identify every such block in the change diff and pass each block's line range, tokens and expected columns to the measurer. Passing some blocks is not passing the file. Leave unchecked blocks marked as unchecked.
|
|
159
|
+
|
|
160
|
+
## Column alignment check steps
|
|
161
|
+
|
|
162
|
+
Proceed to the next step only after the current one passes. Do not adjust a later step's whitespace first and blur an earlier step's boundaries.
|
|
163
|
+
|
|
164
|
+
1. **Confirm comparison blocks**: group only contiguous rows with the same role and syntax structure into one table.
|
|
165
|
+
2. **Record the base form**: record the minimal structure and reading order shared by the most common normal rows as the table's base form.
|
|
166
|
+
3. **Judge exceptional elements**: check definitions and callers to decide whether the longest row's type, parameters and return value are a required contract or an implementation detail reducible by binding into the adapter or removing duplication.
|
|
167
|
+
4. **Simplify structure**: classify foreign elements as required domain information, boundary information, implementation detail or duplicate expression, and consider removal, internal binding, default normalization and table splitting in that order.
|
|
168
|
+
5. **Judge type uncertainty**: find `?`, `null`, `undefined`, `!` and `as`, and classify them as real absence contracts, duplicate expressions or removable assertions. Give names and JSDoc only to meaningful composite types.
|
|
169
|
+
With `exactOptionalPropertyTypes` off, count optional-property omission and explicit `undefined` arguments separately, and normalize same-meaning arguments in the current file to omission.
|
|
170
|
+
6. **Align outermost boundaries**: fix the columns of `:` after identifiers, outer `( )`, `=>`, the outer `{ }` of function bodies, statement-ending `;` and trailing `//`, left to right.
|
|
171
|
+
7. **Compute widths**: compute each cell's width only from the minimum required by the actual content preserved after simplification. Split multiple parameters per line first, and use the longest line as the basis. Close an empty parameter list as `()` and align the column up to the next syntax boundary with outer padding.
|
|
172
|
+
8. **Align inner constructs**: once outer boundaries are fixed, align the start columns of repeated inner constructs such as `import`, `from`, `const`, `= await`, and the final `await`/`return`.
|
|
173
|
+
9. **Place content**: left-align feature/function names and execution cells. Write empty `()` and array markers `[]` without inner spaces. Measure an object array as one outer row from the opening `{` to the closing `}`, and confirm that every preserved property's `:` and separating `,` share a column.
|
|
174
|
+
10. **Handle overlong rows**: consider splitting a new row over column 120 at a semantic boundary. Do not change an accepted grid in a reference file without user confirmation.
|
|
175
|
+
11. **Verify by measurement**: confirm the selected short repeated blocks and JSDoc exposure with the display-width and hover measurers. Confirm with execution, type checking and target tests that behavior did not change.
|
|
176
|
+
|
|
177
|
+
## Authoring axis contract — the single basis for prompts and checks
|
|
178
|
+
|
|
179
|
+
Code is written **from the moment of authoring** with the axis columns below already aligned. Before completion, the checker applies a second lock. This table maps 1:1 to the `KIND_AXES` registry in `13_grid-kinds.ts`. The registry owns implemented axis identifiers; the accepted readability contract and the user's confirmed reference own the intended layout. When they differ, investigate detector coverage and rule scope. Zero registered groups and reviewer agreement do not override an accepted reference.
|
|
180
|
+
|
|
181
|
+
Each newly supported shape or axis set has a new kind and suppresses any overlapping legacy region. Detection never
|
|
182
|
+
widens an older approval key: approval remains exact to kind and ordered axes. Public whole-file writes are denied when
|
|
183
|
+
an applicable changed group lacks approval, even when a legacy kind covering nearby syntax was approved.
|
|
184
|
+
|
|
185
|
+
- **Gap axis** — leave at least one space before the token. Within a table, pad with spaces up to the actual longest value to align the column.
|
|
186
|
+
- **Insert axis** — preserve existing TypeScript conventions. Do not pry apart attached tokens (`)`, `):`, `":`); padding goes outside.
|
|
187
|
+
- A table normally needs three or more contiguous same-role rows, with the documented declaration/import pair exceptions.
|
|
188
|
+
The scoped return-guard exception is defined in [column-sharing regions](#column-sharing-regions). The bounded
|
|
189
|
+
`documented-data-members` kind also permits attached per-field JSDoc in an interface made entirely of required,
|
|
190
|
+
identifier-named data properties on their own physical lines, with JSDoc on preceding comment lines. Blank paragraphs, ordinary comments and different readonly modifiers split its runs.
|
|
191
|
+
Methods, optional capabilities, explicitly callable types, nested object/mapped types, classes and type literals retain their existing
|
|
192
|
+
boundaries. Other kinds stop at blank lines, comments and JSDoc. Conditional-type `:`, `for` header `;`, empty statements and unsupported one-line
|
|
193
|
+
execution blocks are not alignment targets.
|
|
194
|
+
- Axis names use the checker registry identifiers verbatim. Core names include `colon` (`:` after a name), `equals` (`=`), `semicolon` (`;`), `comma` (`,`), `comment` (trailing `//`), `open-paren` (`(`), `close-paren` (`)`), `case-colon` (case label `:`), `call-paren` (first call `(` in a case row), `subject-close-paren` (call-statement subject `)`), `matcher-dot` (right-hand matcher `.`), `matcher-open-paren` / `matcher-close-paren` (matcher argument cell), `return-tail` (start of a bare `return;`), `value` (expression start), and `open-brace` / `close-brace` (object row `{ }`). New-kind identifiers appear verbatim in the table below.
|
|
195
|
+
|
|
196
|
+
| Kind | Gap axes | Insert axes |
|
|
197
|
+
|---|---|---|
|
|
198
|
+
| declaration | `:` , `;` | `//` |
|
|
199
|
+
| declaration-equals (declaration table with `=`) | `:` , `=` , `;` | `//` |
|
|
200
|
+
| declaration-values (every initializer a string, number, boolean or null) | `:` , `=` , `;` | `//` |
|
|
201
|
+
| declaration-call-envelope | `colon`, `equals`, `semicolon` | `call-member-dot`, `call-envelope-open`, `call-envelope-close`, `comment` |
|
|
202
|
+
| declaration-call-expressions | `colon`, `equals`, `call-arg{i}`, `list-value-comma{i}`, `list-value-el{i}`, `list-arg{i}-comma{i}`, `list-arg{i}-el{i}`, `comparison-operator{i}`, `comparison-right{i}`, `conditional-question{i}`, `conditional-true{i}`, `conditional-colon{i}`, `conditional-false{i}`, `semicolon` | `call-open-paren`, `call-comma{i}`, `call-close-paren`, `list-value-open`, `list-value-close`, `list-arg{i}-open`, `list-arg{i}-close`, `subscript-close`, `comment` |
|
|
203
|
+
| return-guards | `guard-return` | — |
|
|
204
|
+
| if-headers-braced | `if-open-brace` | `if-close-paren` |
|
|
205
|
+
| if-headers-return | `if-return` | `if-close-paren` |
|
|
206
|
+
| logical-chains | `comparison-operator{i}`, `comparison-right{i}` | `logical-operator`, `logical-not`, `logical-operand` |
|
|
207
|
+
| vertical-call-arguments | — | `vertical-call-comma` |
|
|
208
|
+
| type-alias | `=` , `;` | `//` |
|
|
209
|
+
| method-signature · class-method | — | `(` , `)` , return `:` , `//` |
|
|
210
|
+
| interface-members · class-members · type-members (`type X = { ... }`) | `:` , `=` , `;` | `//` |
|
|
211
|
+
| documented-data-members (bounded required interface data fields with attached JSDoc) | `colon`, `member-type` | `semicolon`, `comment` |
|
|
212
|
+
| enum-member | `=` , `,` | `//` |
|
|
213
|
+
| case-clause | — | case `:` |
|
|
214
|
+
| object-rows (`{i}` per property position) | property start `{i}` , `:{i}` , `value{i}` start , `,{i}` , `}` | `{` , tail `tail` (`,` `;` `)` `as const`) |
|
|
215
|
+
| object-properties | `property-colon`, `property-value`, `property-comma`, `property-colon{i}`, `property-value{i}`, `property-comma{i}` | — |
|
|
216
|
+
| registration | `:` , expression start , trailing `,` | — |
|
|
217
|
+
| assignments (`this.x = y;` assignment table) | `=` , `;` | `//` |
|
|
218
|
+
| call-statements (`expect(…).toBe(…);` call-statement table) | `;` | subject `)` , matcher `.` , matcher `(` , matcher `)` , `//` |
|
|
219
|
+
| literal-array-rows (one scalar literal per line in a `const` array asserted `as const`) | existing `,` | — |
|
|
220
|
+
|
|
221
|
+
Authoring examples — all identical to the checker's `--write` output:
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
const alpha : string = "a" ;
|
|
225
|
+
const beta : number = 22 ;
|
|
226
|
+
const gamma : boolean = true ;
|
|
227
|
+
|
|
228
|
+
type AppOptions = RunOptions ;
|
|
229
|
+
type SessionList = RecentSessionSummary[] ;
|
|
230
|
+
type ThreadSelection = string | null ;
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
runFirst (first: string ): Promise<void>;
|
|
235
|
+
runSecond(second: number, extra: string): void;
|
|
236
|
+
listAll () : Promise<string[]>;
|
|
237
|
+
|
|
238
|
+
dashboard : new DashboardRail(get, synthetic),
|
|
239
|
+
workflow : new WorkflowRail(get, synthetic),
|
|
240
|
+
context : new ContextRail(get, synthetic),
|
|
241
|
+
|
|
242
|
+
Low = 1 ,
|
|
243
|
+
Middle = 22 ,
|
|
244
|
+
High = 333 ,
|
|
245
|
+
|
|
246
|
+
case "a" : return "first";
|
|
247
|
+
case "bb" : return "second";
|
|
248
|
+
case "ccc": return "third";
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Insert-axis conventions — things the checker never reverts, however the code is written:
|
|
252
|
+
|
|
253
|
+
- Empty parameter lists stay attached as `()`, are excluded from `)` alignment, and keep their padding outside the parentheses.
|
|
254
|
+
- Return types attach to `)` as in `): Promise<void>`. The `:` of `case "a":` also attaches to the label.
|
|
255
|
+
- Between rows with long and short parameter lists, the `)` column is aligned only by widening the inner space right after `(` and right before `)`.
|
|
256
|
+
|
|
257
|
+
Two-point lock:
|
|
258
|
+
|
|
259
|
+
1. **Authoring time (prompt)** — when an LLM or a person writes code, they write it in the form of the table and examples above. Agent definitions use SKILL.md as their manual, so this section is the prompt.
|
|
260
|
+
2. **Completion time (check)** — every changed TypeScript file passes the selected import check
|
|
261
|
+
(`00_normalize-imports.ts --layout-only` for B-layout; the default full normalizer for an authorized
|
|
262
|
+
Write/Refactor). Every changed TypeScript or Python file passes its language aligner
|
|
263
|
+
(`typescript/07_align-tables.ts --file` or `python/02_align-tables.ts --file`, `misaligned=0`). If you want a form that cannot pass both checks, change this contract first.
|
|
264
|
+
|
|
265
|
+
## Grid-first authoring
|
|
266
|
+
|
|
267
|
+
Alignment normalizes rows that already share a structure. When you write new code, design the structure first: before
|
|
268
|
+
writing a group of three or more same-role values, name the column set they share, then write each value as one row of
|
|
269
|
+
that set. Do not write them in whatever shape is convenient and hope the aligner can pair them afterwards. This is
|
|
270
|
+
advisory structural guidance, not approval data: it never authorizes an automatic write, and Align stays whitespace-only.
|
|
271
|
+
|
|
272
|
+
| Situation | Row form that keeps the columns shared |
|
|
273
|
+
|---|---|
|
|
274
|
+
| Several values read from one source (options, config, request fields) | One reader family with one argument shape, defaults passed explicitly: `name = readX(source, "key", default)`. A value with a different role (`write = flag && !other`) is derived on its own line after a blank line. |
|
|
275
|
+
| An object literal with several fields | One field per row: `key : value ,`. Do not leave the whole object on one long line. |
|
|
276
|
+
| The same operation over several lists | One row per list: `name = list .map(item => transform(item))`. Move the part that differs into a named transform so the rows keep one shape. |
|
|
277
|
+
| A branch that picks between two implementations | Pick the function in one row (`const run = flag ? a : b;`) and let both return the same record, so the code after it is the same rows for either path. |
|
|
278
|
+
| A report assembled from parts | `[name, value]` pairs or one row per section, joined once. |
|
|
279
|
+
| Per-item results folded into totals | One row per fold over the same list: `runs.map(…)`, `runs.flatMap(…)`, `runs.some(…)`. |
|
|
280
|
+
|
|
281
|
+
Limits — these keep grid-first from becoming padding by other means:
|
|
282
|
+
|
|
283
|
+
1. A row must carry data the code needs. Do not add wrappers, parameters or helpers whose only job is to fill a column, and
|
|
284
|
+
do not merge roles that merely look alike (see the reuse rule above).
|
|
285
|
+
2. A different role is a different region: derived values, guards, early returns and control flow stay outside the table.
|
|
286
|
+
Keep a side-effecting callback outside a value-row table unless an independently justified function already exposes
|
|
287
|
+
the needed result; grid shape alone never justifies a wrapper.
|
|
288
|
+
3. Preserve behavior: execution order, when errors surface, and output bytes. Verify a rewrite of existing code by comparing
|
|
289
|
+
the old and new versions on the same inputs, not only by running the existing tests.
|
|
290
|
+
4. Hot paths: `.map` chains and tuple arrays allocate. Use them freely for configuration, reporting, CLI glue, test data and
|
|
291
|
+
pipelines that run once per item of work; measure before using them in code that runs millions of times.
|
|
292
|
+
5. Report what the grid cannot hold. A row or a region that cannot share the columns is a visible exception, not a failure.
|
|
293
|
+
|
|
294
|
+
## Discarded rules
|
|
295
|
+
|
|
296
|
+
- Do not place `=>` or comments in an arbitrary column farther than the longest row.
|
|
297
|
+
- Do not force statements with different meanings into one column across the file just because they share a token.
|
|
298
|
+
- Unrelated blocks do not share one maximum width. Cell widths are computed only within the current block being compared.
|
|
299
|
+
- Do not change the whole repository at once like an automatic formatter.
|
|
300
|
+
- Do not create center alignment with no comparison value, or arbitrary widths wider than the actual longest data.
|
|
301
|
+
|
|
302
|
+
## Interface split criteria
|
|
303
|
+
|
|
304
|
+
Dependencies that one consumer receives all at once, such as `CliDependencies`, stay as a single interface.
|
|
305
|
+
|
|
306
|
+
- `Run`: starts execution or delegates a command.
|
|
307
|
+
- `List`: looks up multiple items.
|
|
308
|
+
- `Select`: receives a user selection.
|
|
309
|
+
- `Write`: outputs to the user.
|
|
310
|
+
|
|
311
|
+
Expose each role with a section comment such as `// Run`. Inside a section use execution-flow order; do not reorder alphabetically. Do not create empty sections such as `Read` to fill a role that does not exist.
|