clearotron 0.3.1-beta.4 → 0.3.2-beta.0
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/CONTRIBUTING.md +30 -1
- package/README.md +1 -0
- package/build-info.json +2 -2
- package/docs/writing-rules.md +208 -0
- package/docs/writing-standard.md +85 -0
- package/driver/CHANGELOG.md +81 -0
- package/driver/contract-e3-backlog.mjs +3 -3
- package/driver/contract-vocabulary.mjs +15 -14
- package/driver/gateway.mjs +33 -17
- package/driver/package.json +1 -1
- package/driver/publish/render-knockout.mjs +6 -26
- package/driver/publish/render.mjs +25 -5
- package/driver/suite-census.json +30 -0
- package/driver/verify.mjs +22 -3
- package/mcp-server/CHANGELOG.md +19 -0
- package/mcp-server/package.json +1 -1
- package/package.json +1 -1
- package/portal-ui/package.json +1 -1
- package/providers/oauth-mcp-bridge/CHANGELOG.md +8 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/scripts/mint-writing-standard-backlog.mjs +82 -0
- package/scripts/release-approve-parked.mjs +151 -0
- package/scripts/writing-standard-check.mjs +122 -0
- package/shared/says-something-new.mjs +62 -0
- package/shared/writing-standard-caveats.json +14 -0
- package/shared/writing-standard-classes.mjs +526 -0
package/CONTRIBUTING.md
CHANGED
|
@@ -93,7 +93,7 @@ served to the model at run time — `synthesis-rules.md` is a 16,000-word progra
|
|
|
93
93
|
brevity, tone or tidiness changes what a clearance concludes. Nothing in this section, and nothing in
|
|
94
94
|
any writing pass over the documentation, applies to them.
|
|
95
95
|
|
|
96
|
-
## The
|
|
96
|
+
## The four rules that fail CI
|
|
97
97
|
|
|
98
98
|
**1. Build `portal-ui/dist` before you push.** The bundle is not committed; it is gitignored, and
|
|
99
99
|
CI runs `npm run build:ui` from source. So there is nothing to add — run it locally when you touch
|
|
@@ -130,6 +130,35 @@ The reviewer is the check. Say what you checked in the PR rather than citing a p
|
|
|
130
130
|
**3. Types are enforced, and `vite build` does not typecheck.** `npm run typecheck -w portal-ui`
|
|
131
131
|
runs as its own CI step. Run it before you push.
|
|
132
132
|
|
|
133
|
+
**4. Every sentence a customer reads is written against the standard, and five classes of it are
|
|
134
|
+
checked.** The standard is [`docs/writing-standard.md`](docs/writing-standard.md); the prose rules
|
|
135
|
+
under it are [`docs/writing-rules.md`](docs/writing-rules.md), and they apply first. Together they
|
|
136
|
+
bind report HTML, portal screens, the README and the docs.
|
|
137
|
+
|
|
138
|
+
`node scripts/writing-standard-check.mjs` refuses what your change ADDS to one of those surfaces:
|
|
139
|
+
an engineering identifier in text a client reads, a reviewer-only marker in rendered output, a known
|
|
140
|
+
caveat sentence, a screen that writes its own page heading instead of using `PageHeader`, and a lede
|
|
141
|
+
whose words are all already in its title. It names the class and prints the line. It never rewrites —
|
|
142
|
+
a rewritten sentence is a sentence nobody reviewed.
|
|
143
|
+
|
|
144
|
+
It refuses what you add, not what is already here. The standing population is counted per file and
|
|
145
|
+
per class in `driver/test/fixtures/writing-standard-backlog.json`, and the floor beside it refuses
|
|
146
|
+
any file that grows, so the number can only fall. If you repair one, re-mint the floor with
|
|
147
|
+
`node scripts/mint-writing-standard-backlog.mjs --apply` so it drops with the tree.
|
|
148
|
+
|
|
149
|
+
### The review step, for what no check can judge
|
|
150
|
+
|
|
151
|
+
Tone is not one of the five classes and no word list will ever catch it. A sentence can carry no
|
|
152
|
+
identifier, no caveat and no banned word and still be the thing the standard exists to stop: a
|
|
153
|
+
heading that restates its section, a paragraph explaining what the reader can already see, a sentence
|
|
154
|
+
that only works if you know how the engine is built.
|
|
155
|
+
|
|
156
|
+
So: **a pull request that changes text a customer reads is reviewed against the rendered page or
|
|
157
|
+
report, not against the diff.** One reviewer reads it as someone who has never seen this product and
|
|
158
|
+
asks one question of every new sentence — *what would a reader with zero context think this means?*
|
|
159
|
+
If the answer needs the codebase, the sentence is cut or rewritten. One reviewer, one question,
|
|
160
|
+
recorded in the pull request.
|
|
161
|
+
|
|
133
162
|
### If your change touches a Markdown file, two more will catch you
|
|
134
163
|
|
|
135
164
|
Both are in `npm run test:full` rather than the fast tier, and neither is something a reviewer spots.
|
package/README.md
CHANGED
|
@@ -139,6 +139,7 @@ credential.
|
|
|
139
139
|
| Check it works before spending anything | [docs/E2E.md](docs/E2E.md) |
|
|
140
140
|
| Understand the architecture | [docs/architecture/](docs/architecture/) · [decisions](docs/decisions/) |
|
|
141
141
|
| Run it under your own name, or fork it | [docs/branding.md](docs/branding.md) · [TRADEMARKS.md](TRADEMARKS.md) |
|
|
142
|
+
| Write a sentence a customer will read | [docs/writing-standard.md](docs/writing-standard.md) · [docs/writing-rules.md](docs/writing-rules.md) |
|
|
142
143
|
|
|
143
144
|
## Development
|
|
144
145
|
|
package/build-info.json
CHANGED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Writing Rules
|
|
2
|
+
|
|
3
|
+
From the On Style writing rules, https://github.com/gundersen/on-style, MIT licence; reproduced here so the standard is complete on its own. The licence text is at the end of this file.
|
|
4
|
+
|
|
5
|
+
*Apply these to all prose you produce.*
|
|
6
|
+
|
|
7
|
+
*These rules fight trained instincts. Pay special attention to: no hedging, no over-explaining, keep it short, no metadiscourse, finish strong, and don't fake contradiction. These are the rules most likely to be violated — enforce them first.*
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Words — what to use and what to cut
|
|
12
|
+
|
|
13
|
+
## Cut dead weight
|
|
14
|
+
|
|
15
|
+
Every word must earn its place. Kill:
|
|
16
|
+
- **Zombie nouns**: "make an appearance" → "appear"; "provide an explanation" → "explain"
|
|
17
|
+
- **Light verbs** propping up nominalizations: make, do, have, bring, put, take
|
|
18
|
+
- **Metaconcept filler**: matter, view, subject, process, basis, factor, level, model
|
|
19
|
+
- **Throat-clearing openers**: "It is worth noting that...", "It should be pointed out that...", "There is a tendency for..."
|
|
20
|
+
|
|
21
|
+
If a 40-word sentence says what 15 words can say, use 15.
|
|
22
|
+
|
|
23
|
+
## Be concrete
|
|
24
|
+
|
|
25
|
+
Use nouns and verbs, not adjectives and adverbs. Prefer plain words over fancy ones. Replace abstractions with the tangible things they refer to — people, actions, objects. If the reader can't picture it, rewrite it. Be concrete about the argument; be vague about the scenery.
|
|
26
|
+
|
|
27
|
+
"The company experienced a significant deterioration in operational performance across multiple facilities during the quarter" — how many facilities? What happened? "Three factories missed production targets and the Chicago plant shut down for two weeks" — now the reader sees it.
|
|
28
|
+
|
|
29
|
+
## No jargon
|
|
30
|
+
|
|
31
|
+
If a phrase requires a second phrase to explain it, use the explanation. "How the AI in our weapons makes decisions" — not "autonomy governance." "We raised $50M to build bigger rockets" — not "we secured a strategic capital infusion to scale our launch vehicle program." The test: could a smart outsider follow it without a glossary?
|
|
32
|
+
|
|
33
|
+
## No hedging — make definite assertions
|
|
34
|
+
|
|
35
|
+
Don't reflexively pad with: almost, apparently, fairly, somewhat, sort of, to a certain degree, relatively, presumably. Cut throat-clearing qualifiers: actually, of course, surely, in fact, just, so, pretty, that said. Hedge only when genuine uncertainty exists and the reader needs to know about it. When you hedge, do it once and with precision — not as a verbal tic.
|
|
36
|
+
|
|
37
|
+
Beyond avoiding hedges, write with force. A sentence can be hedge-free and still limp. "The project went okay" has no qualifiers but says nothing. "The project shipped three weeks late and lost the client" says something.
|
|
38
|
+
|
|
39
|
+
## No intensifiers
|
|
40
|
+
|
|
41
|
+
Drop: very, highly, extremely, incredibly, truly, really, quite. These words weaken rather than strengthen. An unmodified claim reads as categorical and confident. Adding "very" turns it into a score on a scale.
|
|
42
|
+
|
|
43
|
+
## No GPT-isms
|
|
44
|
+
|
|
45
|
+
If it sounds like a language model wrote it, rewrite it. Ban list:
|
|
46
|
+
- **Compound modifiers that mean nothing**: "operationally relevant," "institutionally protected," "non-fragmentable," "identity-defining institutional constraint," "mission-critical," "best-in-class"
|
|
47
|
+
- **Faux-precision hedges**: "it's worth noting that," "it's important to understand," "this is particularly significant"
|
|
48
|
+
- **Breathless connectors**: "moreover," "furthermore," "indeed," "notably," "crucially," "importantly"
|
|
49
|
+
- **Vague authority words**: "robust," "comprehensive," "strategic," "leveraging," "ecosystem," "synergy," "paradigm," "holistic"
|
|
50
|
+
- **Performative structure**: "Let's break this down," "Here's the thing," "There are several key factors to consider"
|
|
51
|
+
|
|
52
|
+
The test: if the phrase exists only because an LLM stitched together impressive-sounding words, cut it.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
# Sentences — how they move
|
|
57
|
+
|
|
58
|
+
## Active voice by default
|
|
59
|
+
|
|
60
|
+
Put the agent before the action. "The board approved the deal" — not "The deal was approved by the board." If you can't name who's doing it, the sentence is hiding. Use passive only when the doer is unknown, unimportant, or when you need to shift emphasis deliberately.
|
|
61
|
+
|
|
62
|
+
## Light before heavy
|
|
63
|
+
|
|
64
|
+
Put short, simple phrases before long, complex ones. Topic before comment. Given information before new information. End sentences with the material that deserves the most emphasis.
|
|
65
|
+
|
|
66
|
+
"The executive team, after weeks of deliberation and consultation with outside counsel regarding the potential regulatory implications, approved the merger." The verb arrives too late. Flip it: "The executive team approved the merger after weeks of deliberation over regulatory risk."
|
|
67
|
+
|
|
68
|
+
## One idea per sentence
|
|
69
|
+
|
|
70
|
+
If a sentence tries to do two things, split it. If the reader has to reread it, it's too long or too tangled. The difficulty of a sentence depends on its geometry, not just its word count — avoid deeply nested clauses.
|
|
71
|
+
|
|
72
|
+
## Punctuation is breath
|
|
73
|
+
|
|
74
|
+
Punctuation controls how the reader hears you — the pacing, the pauses, the asides. A comma sounds different than a semicolon; parentheses make a different noise than dashes. Read sentences aloud. Where you pause naturally, punctuate. Where the punctuation forces an unnatural pause, remove it.
|
|
75
|
+
|
|
76
|
+
Two bright-line rules:
|
|
77
|
+
- **Always use the serial comma.** "Guards, dogs, and sensors" — keep the comma before "and." It eliminates ambiguity and costs nothing.
|
|
78
|
+
- **Never use exclamation points.** Let the words carry the emphasis. If the sentence needs a ! to land, the sentence isn't strong enough.
|
|
79
|
+
|
|
80
|
+
## Don't fake contradiction
|
|
81
|
+
|
|
82
|
+
Don't toss in a "But" or "However" to create the illusion that a second thought contradicts a first when it doesn't. "However" is not a synonym for "and" or "also" — it signals a turn. If there's no turn, drop it. Use contrastive connectors only when there is an actual contrast.
|
|
83
|
+
|
|
84
|
+
## Watch for dangling modifiers
|
|
85
|
+
|
|
86
|
+
"Walking through the data, the trend becomes clear" — the trend isn't walking. Make sure the modifier attaches to the subject of the sentence. "Walking through the data, we noticed a clear trend." If the sentence reads fine but the grammar is wrong, the reader will stumble even if they can't say why.
|
|
87
|
+
|
|
88
|
+
## Name the thing
|
|
89
|
+
|
|
90
|
+
Don't open a sentence with "this," "that," "these," or "it" unless the referent is unmistakable from the previous sentence. Vague pronouns break coherence — the reader has to look back to figure out what you mean. When in doubt, name the thing. "This approach" instead of "This." "The deal" instead of "It."
|
|
91
|
+
|
|
92
|
+
## Consistent terms, consistent vantage
|
|
93
|
+
|
|
94
|
+
If you use two different words, the reader assumes you mean two different things. Don't vary terminology for variety's sake. Stay in one tense, one voice, one point of view — don't flip without reason.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
# Paragraphs & Structure — shape and sequence
|
|
99
|
+
|
|
100
|
+
## Show, don't announce
|
|
101
|
+
|
|
102
|
+
Lead with the thing itself. Never open with "In this section I will discuss..." or "It's important to note that..." — just say it. No metadiscourse. No narrating your own writing process.
|
|
103
|
+
|
|
104
|
+
## Connect every sentence
|
|
105
|
+
|
|
106
|
+
Each sentence must follow from the one before it. Don't dump facts without showing how they relate. Use a logical sequence: general to specific, cause to effect, chronological, or big to small. If adding "therefore," "however," or "for example" between two sentences doesn't make sense, the transition is broken.
|
|
107
|
+
|
|
108
|
+
## Every paragraph earns its place
|
|
109
|
+
|
|
110
|
+
Each paragraph should have a single job. Begin with a sentence that states or implies the point. Let the following sentences develop it. End with emphasis, not a digression. A paragraph is a complete thought — not a visual break you insert when the block of text looks too long. If you can't say what a paragraph is about in one sentence, it's doing too many things.
|
|
111
|
+
|
|
112
|
+
## Finish strong
|
|
113
|
+
|
|
114
|
+
End paragraphs and sections with the sentence that carries the most weight. Don't trail off with qualifications or filler. The last thing the reader sees is what sticks.
|
|
115
|
+
|
|
116
|
+
"The deal, which was the largest acquisition in the company's history, closed Tuesday at $4.2 billion after six months of negotiation." The best fact is buried in the middle. Rearrange: "After six months of negotiation, the deal closed at $4.2 billion — the largest acquisition in the company's history."
|
|
117
|
+
|
|
118
|
+
## Say it once
|
|
119
|
+
|
|
120
|
+
State a principle in one place. Reference it after that. Never re-derive the same idea from scratch in every section.
|
|
121
|
+
|
|
122
|
+
## The rule of threes
|
|
123
|
+
|
|
124
|
+
People remember things better when grouped in threes. Three main points, three examples, three items in a list. It keeps structure clear and memorable. When you have five points, see if two of them are really the same point. When you have seven, you've lost the reader.
|
|
125
|
+
|
|
126
|
+
## Keep it short
|
|
127
|
+
|
|
128
|
+
If an email could be 3 sentences, don't write 3 paragraphs. Say it in as few paragraphs as the argument requires. Cut entire paragraphs the same way you cut words — if removing it doesn't hurt the argument, it was never helping.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
# Calibration — proportions and matching claims
|
|
133
|
+
|
|
134
|
+
## Respect the reader
|
|
135
|
+
|
|
136
|
+
Assume the reader is intelligent but doesn't share your specialized knowledge. Explain terms without being condescending. Never write in a way that makes the reader feel stupid — classic style makes the reader feel like a genius.
|
|
137
|
+
|
|
138
|
+
## Sometimes less detail is more accurate
|
|
139
|
+
|
|
140
|
+
Don't fabricate specifics to sound authoritative. "The company was founded in the early 2000s" is more accurate than "the company was founded in 2003" if you don't actually know the year. Precision with nonessential details invites errors and adds noise. Be specific when the detail matters and you're confident in it.
|
|
141
|
+
|
|
142
|
+
## Do not overstate
|
|
143
|
+
|
|
144
|
+
If you say "revolutionary" and the reader thinks "incremental," you've lost them — not just on that sentence but on everything that follows. Make claims proportional to the evidence. Overstatement erodes trust faster than understatement. Let the facts do the persuading.
|
|
145
|
+
|
|
146
|
+
## Do not explain too much
|
|
147
|
+
|
|
148
|
+
State it, show it, move on. If you state a principle and then explain it and then explain the explanation, you're writing a textbook, not a memo. Let examples reveal the point. Trust the reader to follow.
|
|
149
|
+
|
|
150
|
+
## Numbers over superlatives
|
|
151
|
+
|
|
152
|
+
If a number could replace a word or phrase, use the number. "Revenue growth significantly outpaced expectations in Q2" — by how much? "Revenue grew 34% in Q2, beating the Street estimate by 11 points" — now the reader can think with it. If a sentence still needs a qualitative descriptor after the number is included, use a calibrated and neutral term ("modest," "material" with a defined threshold) rather than an evocative one.
|
|
153
|
+
|
|
154
|
+
## No emotionally charged framing
|
|
155
|
+
|
|
156
|
+
Phrases like "not your salvation," "you're in trouble," "this is the one to stare at" are persuasion techniques, not analysis. Present the data and let the conclusion follow naturally. Analysis should read like a term sheet, not an op-ed.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
# Voice & Framing — identity and stance
|
|
161
|
+
|
|
162
|
+
## Say what you are, never what you're not
|
|
163
|
+
|
|
164
|
+
Define by positive assertion. Don't build identity through negation. "We build vertical rockets" — not "we are not a traditional aerospace company, not a components supplier, not a consultancy."
|
|
165
|
+
|
|
166
|
+
## No hidden negatives
|
|
167
|
+
|
|
168
|
+
Some sentences look positive but structurally say "we don't do X." Flip them. "We design what we can build in volume" — not "We subordinate performance ambition to scalable manufacturability." "We ship weekly" — not "We don't let perfect block progress." If the sentence only makes sense by contrast with something you're rejecting, rewrite it as a direct claim.
|
|
169
|
+
|
|
170
|
+
## Pass the "so what?" test
|
|
171
|
+
|
|
172
|
+
Every sentence must earn its place. If it sounds authoritative but wouldn't help settle an argument in a room, cut it. If you could swap in any name — any company, any person, any product — and the sentence still works, it's empty.
|
|
173
|
+
|
|
174
|
+
## Guide decisions, don't prescribe process
|
|
175
|
+
|
|
176
|
+
A framework tells you what matters when you're making a choice. It doesn't create governance structures, escalation pathways, or approval chains. Those belong in an ops manual, not a strategy document or memo.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Self-check
|
|
181
|
+
|
|
182
|
+
Rules decay over long outputs — the longer you write, the more likely you revert to trained habits. After writing any prose longer than two paragraphs, re-read it against the preamble list before presenting it. Check for: hedging, over-explaining, verbosity, metadiscourse, trailing caveats, fake contradiction, and vague pronoun references.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Licence of these rules
|
|
187
|
+
|
|
188
|
+
MIT License
|
|
189
|
+
|
|
190
|
+
Copyright (c) 2026 gundy
|
|
191
|
+
|
|
192
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
193
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
194
|
+
in the Software without restriction, including without limitation the rights
|
|
195
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
196
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
197
|
+
furnished to do so, subject to the following conditions:
|
|
198
|
+
|
|
199
|
+
The above copyright notice and this permission notice shall be included in all
|
|
200
|
+
copies or substantial portions of the Software.
|
|
201
|
+
|
|
202
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
203
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
204
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
205
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
206
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
207
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
208
|
+
SOFTWARE.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Writing standard
|
|
2
|
+
|
|
3
|
+
Every surface a customer reads: report HTML, portal screens, README, docs. Two parts.
|
|
4
|
+
|
|
5
|
+
## Part one: how to write
|
|
6
|
+
|
|
7
|
+
`writing-rules.md` beside this file is the prose standard, and it applies to every sentence in those
|
|
8
|
+
surfaces before anything below does. Read it in full once. The six rules it says to enforce first, because
|
|
9
|
+
they are the ones a trained writer and a language model both break by instinct: no hedging, no
|
|
10
|
+
over-explaining, keep it short, no metadiscourse, finish strong, and do not fake contradiction. If a
|
|
11
|
+
sentence sounds like a language model wrote it, rewrite it.
|
|
12
|
+
|
|
13
|
+
## Part two: the product's own rules
|
|
14
|
+
|
|
15
|
+
Eight rules, each with the sentence that caused it.
|
|
16
|
+
|
|
17
|
+
## One header per page, and nothing restating it
|
|
18
|
+
|
|
19
|
+
A page names itself once. A line under the title earns its place only by saying something the title
|
|
20
|
+
does not.
|
|
21
|
+
|
|
22
|
+
- Before: `Home` over `Now`.
|
|
23
|
+
- After: `Home`.
|
|
24
|
+
|
|
25
|
+
## Example text goes inside the field
|
|
26
|
+
|
|
27
|
+
A field's example belongs in its placeholder, where the reader is typing. A sentence above the field is
|
|
28
|
+
read before the reader knows what the field is for.
|
|
29
|
+
|
|
30
|
+
- Before, above the field: "A sentence is enough, or paste the whole thread."
|
|
31
|
+
- After, in the field: "Need a quick check on AQUAPLUS for energy drinks in the US before Friday."
|
|
32
|
+
|
|
33
|
+
## No caveat, no disclaimer, no "what this is not"
|
|
34
|
+
|
|
35
|
+
Say what the search did and what happens next. Never define the product by negation.
|
|
36
|
+
|
|
37
|
+
- Before: "What it is not. A clearance search. We drew no register conclusions and give no filing advice."
|
|
38
|
+
- After: "A name that passes here goes on to clearance."
|
|
39
|
+
|
|
40
|
+
## No engineering word in anything a client reads
|
|
41
|
+
|
|
42
|
+
Error codes, connector names, routing tables and internal identifiers are not the reader's vocabulary.
|
|
43
|
+
State the limit and its consequence.
|
|
44
|
+
|
|
45
|
+
- Before: "the Japan adapter was unavailable this session (CONNECTION_CLOSED)".
|
|
46
|
+
- After: "Case-law research could not be completed for Japan."
|
|
47
|
+
|
|
48
|
+
## A count only when it changes what the reader does
|
|
49
|
+
|
|
50
|
+
Register hit counts tell a lawyer how crowded a field is. Keep those. Activity counts measure the
|
|
51
|
+
engine, not the answer.
|
|
52
|
+
|
|
53
|
+
- Before: "Fifty-two meaning and connotation queries were run across the Latin, katakana and hiragana forms."
|
|
54
|
+
- After: "Meaning was checked in three scripts; nothing loaded attaches to the name."
|
|
55
|
+
|
|
56
|
+
## The product's own words for its own states
|
|
57
|
+
|
|
58
|
+
A state has one name, used on the screen, in the report, in help and in the export.
|
|
59
|
+
|
|
60
|
+
- Before: "No permissions".
|
|
61
|
+
- After: "View reports".
|
|
62
|
+
|
|
63
|
+
## The audit lives in the workbook and the MCP server, never on the page
|
|
64
|
+
|
|
65
|
+
Every search run, every empty result and the working notes go to the audit workbook, and the MCP server
|
|
66
|
+
answers questions about any of them. The page carries the finding.
|
|
67
|
+
|
|
68
|
+
- Before: "Source routing attempted: Per the source-selection table, Japan is a non-US, non-EU
|
|
69
|
+
jurisdiction, so the applicable adapter is…"
|
|
70
|
+
- After: nothing on the page. One row in the workbook.
|
|
71
|
+
|
|
72
|
+
## A fault is fixed, or shown to the person who can act on it
|
|
73
|
+
|
|
74
|
+
A customer cannot repair a missing coverage record. Narrating the failure to them turns our defect into
|
|
75
|
+
their problem.
|
|
76
|
+
|
|
77
|
+
- Before: "No coverage record was produced for this run. This section normally lists what each search
|
|
78
|
+
covered and what is still open… Ask us before relying on it."
|
|
79
|
+
- After: the operator is told; the report carries the coverage that exists.
|
|
80
|
+
|
|
81
|
+
## What holds these
|
|
82
|
+
|
|
83
|
+
`enforcement.md` lists the classes a CI check refuses in a diff. Tone is not one of them, and no word
|
|
84
|
+
list will ever catch it: a reviewer reads the rendered page as someone who has never seen this product,
|
|
85
|
+
against `writing-rules.md`, and asks what they would think each sentence means.
|
package/driver/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,86 @@
|
|
|
1
1
|
# clearotron-driver
|
|
2
2
|
|
|
3
|
+
## 0.3.2-beta.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7cc0d29: Fixed: A report now lists every part of the search that was left open. One with a short name could be hidden by another line that happened to mention the same word. The overall result was never affected, only the list of what remained open.
|
|
8
|
+
|
|
9
|
+
## 0.3.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 240673c: Fixed: A chat notice now carries the channel to send it on, so an assistant with several chat channels no longer drops it silently.
|
|
14
|
+
- a2c4b94: Fixed: A correction to a coverage note or an action is now applied, or the run records why it was not.
|
|
15
|
+
- 240673c: Fixed: Naming a country in words rather than by code now works for every country, including Belgium and Luxembourg. Before, some were carried as unrecognised.
|
|
16
|
+
- b320c65: Fixed: A demo sample that cannot be read is named in the demo's output, and the other demos still publish.
|
|
17
|
+
- 240673c: Fixed: A family search on a name whose first or last word is a single letter or digit now runs. Before, one register refused it and the search was reported as an outage.
|
|
18
|
+
|
|
19
|
+
Fixed: A conflict whose owner could not be identified is no longer given a risk rating. It is carried as an open item naming who must be identified.
|
|
20
|
+
- d0d42f7: Fixed: A name in a knockout batch is no longer rated above every conflict found against it. Its rating now follows from the conflicts on its own page.
|
|
21
|
+
|
|
22
|
+
Fixed: Before, a rule forced any name made of everyday words off the lowest band, whatever the search found. That rule is gone for every client.
|
|
23
|
+
- f96c089: Fixed: Running the test suite from inside another test run no longer lets the outer run delete the inner run's temporary files.
|
|
24
|
+
- f96c089: Fixed: A clearance for a new client can be ordered through an assistant connector without setting up a company first.
|
|
25
|
+
- 240673c: Fixed: In the staff editor, a refused territory in a project now highlights the field it is about, as it already did when editing a customer. Before, the message appeared but no field was marked.
|
|
26
|
+
- 240673c: Fixed: A sign-in refusal now always says which instance answered — by organisation, by sign-in service, or by the address it runs on.
|
|
27
|
+
- 240673c: Fixed: The published list of settings this build reads no longer keeps a name after the code stops reading it. The list was derived from a scan that included the list itself, so a retired name kept itself alive.
|
|
28
|
+
- c3228f3: Fixed: A clearance that stops is recorded as owing a notice even when the folder its notice is queued in cannot be written to.
|
|
29
|
+
- d9798db: Fixed: A clearance that stops now always records that its notice is still owed, so a failure cannot be passed over as already handled.
|
|
30
|
+
- 240673c: Fixed: A search step that streams at a crawl is now stopped early and retried, instead of running to its time limit and losing the work.
|
|
31
|
+
- 56a760f: Fixed: Giving somebody access to everything on the installation now works from the access form. It used to refuse. The message it refused with said you can only give access to what you hold yourself, which was not true of the person seeing it.
|
|
32
|
+
- ae2cc82: For operators: An assistant asking which searches still owe someone a notice now gets all of them, not just the fifty most recent. Asking for recent searches is unchanged.
|
|
33
|
+
- f96c089: Fixed: A mark whose main element contains no vowel — a consonant-only initialism, for example — can now be cleared. Before, the search plan refused to compile and the whole clearance ended without delivering anything.
|
|
34
|
+
- 8c3bc3b: New: Ask AI on a report now opens Claude or ChatGPT with a question about that report already typed in. One press, in a new tab, and nothing is sent until you send it.
|
|
35
|
+
|
|
36
|
+
The button used to hand over a connector address and a question carrying the run's internal code, with no indication of which one you needed. The address belongs on the Use your own AI page, where you set the connector up once. It is no longer shown on reports at all.
|
|
37
|
+
|
|
38
|
+
If you have not connected an assistant yet, the button explains that in a line and offers to take you there.
|
|
39
|
+
|
|
40
|
+
For operators: the report's own "Ask your AI" band is gone, so there is one Ask AI control rather than two. Reports rendered before this upgrade keep the band in their own file, and it is hidden when the portal serves them.
|
|
41
|
+
- c3228f3: Fixed: Check now reports the same problems Save would refuse, so a company setting can no longer pass the check and then fail to save.
|
|
42
|
+
- 240673c: Fixed: Use your AI now gives the steps your connector actually takes — sign-in where it signs you in, a key only where a key works.
|
|
43
|
+
- f96c089: Fixed: `doctor` now names the deployment it is checking, and refuses a name that is missing or not recognised. Before, a deployment that was misnamed — or not named at all — passed the check in silence.
|
|
44
|
+
- 240673c: Fixed: Setup and the framed first-run box now say what to do when the page that opens belongs to another program.
|
|
45
|
+
|
|
46
|
+
Fixed: The port that advice suggests is never the port already in use.
|
|
47
|
+
- 6d0c8f5: Fixed: The portal now tells your browser not to store the data its screens read. Those responses carry people's names, company access and run lists, and nothing previously said how long a browser could keep them.
|
|
48
|
+
|
|
49
|
+
For operators: every JSON response from the portal now sends `Cache-Control: no-store` and `Vary: Accept`. A route that sets a stricter policy of its own keeps it.
|
|
50
|
+
- f96c089: For operators: The repository's own comment-to-code references are now checked for having moved, not only for existing.
|
|
51
|
+
- c3228f3: Fixed: On older Windows-Subsystem installations the engine now identifies the platform by its interop registration rather than by the kernel version string.
|
|
52
|
+
- 240673c: Fixed: On WSL, the "on this computer" rows now start the server inside WSL for you, so an assistant running on Windows can use them.
|
|
53
|
+
- 56a760f: Fixed: `clearotron grant remove --tenant` now refuses when the person has access to everything on the installation. It used to remove the organisation and then warn that nothing they could see had changed.
|
|
54
|
+
|
|
55
|
+
Fixed: Removing somebody whose address is spelled with different capitalisation in different parts of the access file now removes all of them. Half of the entry used to survive, and the command reported success.
|
|
56
|
+
- 56a760f: New: Access can now be narrowed and taken away, not only added to. Somebody who manages one organisation removes that organisation alone. Somebody who can see all of a person removes their access to the installation, and withdraws the keys their AI assistant was using.
|
|
57
|
+
|
|
58
|
+
Fixed: A removal now says plainly when the connector cannot be told about it yet, instead of implying the assistant lost access too.
|
|
59
|
+
- 240673c: Fixed: Somebody you add on the People page can sign in straight away. Before, they were refused until the service restarted with a changed setting.
|
|
60
|
+
- e390417: For operators: The portal's start-up check now says when its engine address is behind a sign-in it cannot pass, instead of reporting that address as reachable.
|
|
61
|
+
- 240673c: Fixed: The demo now removes everything it created when its window closes, and says so. Pass `--keep` to leave the folder and its reports.
|
|
62
|
+
|
|
63
|
+
Fixed: Trying the demo a second time on a machine that has run it before now works. Before, it refused its own folder and suggested dropping a flag that had not been given.
|
|
64
|
+
- 240673c: Fixed: The demo opens ports of its own rather than the ones an installation uses, so the page it points you at is the demo's.
|
|
65
|
+
- 240673c: Fixed: A search now covers every spelling and sound-alike of the name in each category of goods or services the engine judges relevant.
|
|
66
|
+
|
|
67
|
+
Fixed: Before, the added categories were searched for the name exactly and nothing else. The matter frame records each one with the reason it was added.
|
|
68
|
+
- 240673c: Fixed: An off-register search now also covers the channels the matter itself names, not only the account's usual marketplaces.
|
|
69
|
+
|
|
70
|
+
Fixed: A channel no pass ran is now recorded as open rather than described in a note.
|
|
71
|
+
|
|
72
|
+
Fixed: A finding reads what the platform's own record says before calling an owner unidentified.
|
|
73
|
+
- 240673c: For operators: The portal can now call the engine over a local socket instead of a network port, by naming it as its engine address. The deployment check reports that address as wired and says which socket it is.
|
|
74
|
+
- f96c089: Fixed: The run purge no longer deletes a clearance whose report or failure notice has not been sent yet.
|
|
75
|
+
|
|
76
|
+
Fixed: Those runs are marked in the table the purge prints, and removing one now takes a flag that says so.
|
|
77
|
+
|
|
78
|
+
For operators: Every applied purge leaves a record of what it removed, when, and whether any of it was still owed.
|
|
79
|
+
- 1905ea4: Fixed: Saving a territory the engine cannot search now says so, instead of suggesting the kind of entry that was just refused.
|
|
80
|
+
- f1c5925: Fixed: Where a search covers two ratified forms of a name, the report now reasons each form and says which conflicts differ between them.
|
|
81
|
+
|
|
82
|
+
Fixed: Before, both forms were searched but one combined read came back. When the forms read alike the report now says so, rather than leaving it unsaid.
|
|
83
|
+
|
|
3
84
|
## 0.3.1-beta.4
|
|
4
85
|
|
|
5
86
|
### Patch Changes
|
|
@@ -488,7 +488,7 @@ export const E3_BACKLOG = [
|
|
|
488
488
|
where: "driver/stages.mjs:3231",
|
|
489
489
|
surface: "stage-message",
|
|
490
490
|
evidence: "- meters: {\"mark_similarity\":{...},\"goods_proximity\":{...},\"use\":{...},\"enforcer\":{...}} — all four present, each {\"token\",\"basis\",\"source\"}. … mark_similarity = high | medium | low. goods_proximity = high | medium | low. enforcer = high | medium | low | unknown. use = confirmed | not-confirmed | un",
|
|
491
|
-
reparsedBy: "driver/findings-model.mjs:813 parseFindingsJson; driver/verify.mjs:
|
|
491
|
+
reparsedBy: "driver/findings-model.mjs:813 parseFindingsJson; driver/verify.mjs:1127 checkFindingsSibling gates meters.*.source; finding_basis_source_missing",
|
|
492
492
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
493
493
|
},
|
|
494
494
|
{
|
|
@@ -684,7 +684,7 @@ export const E3_BACKLOG = [
|
|
|
684
684
|
// because an un-anchorable row lands in the NOT-CHECKED slice, and that is coverage lost rather than
|
|
685
685
|
// a pass.
|
|
686
686
|
evidence: "is ONE of coverage-disposition | fact | rating | narrative — pick the one your own legal read says the correction IS: coverage-disposition (a coverage row / disposition placement is wrong or dishonest)",
|
|
687
|
-
reparsedBy: "driver/verify.mjs:
|
|
687
|
+
reparsedBy: "driver/verify.mjs:803 CORRECTION_KIND_RE = /\\[kind:\\s*([a-z][a-z-]*)\\s*\\]/i → parseCorrectionKinds (verify.mjs:1026), consumed in pipelineInner() in pipeline.mjs for the run.jsonl `correction-kinds` histogram, which since #1558 also carries `kindChannelOk` — the counts are DERIVED from the parsed rows, and that key states whether the reviewer's kind channel produced anything at all rather than leaving a reader to infer it by comparing untyped against total. Telemetry only today",
|
|
688
688
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
689
689
|
},
|
|
690
690
|
{
|
|
@@ -697,7 +697,7 @@ export const E3_BACKLOG = [
|
|
|
697
697
|
// `[on: -]` case went from a value to an ABSENCE — you omit the field — which is the one part a
|
|
698
698
|
// reader could get wrong from the old wording, since there is no value meaning "no finding".
|
|
699
699
|
evidence: "**AND EVERY FLAG CARRIES WHICH FINDING IT IS ABOUT** — the `on` field, an array of ordinals. Same rule as `kind`: you send the values, the driver renders the token.",
|
|
700
|
-
reparsedBy: "driver/verify.mjs:
|
|
700
|
+
reparsedBy: "driver/verify.mjs:820 CORRECTION_ON_RE = /\\[on:\\s*([0-9,\\s-]*?)\\s*\\]/i. SKILL-FILE ONLY — the stage message at stages.mjs:1842-1877 never mentions `[on:]`. This is #850's \"the element shape is in the skill file, not the stage message\" in its purest form: an E3 lint reading stages.mjs alone sees the [kind:] token and misses its twin",
|
|
701
701
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
702
702
|
},
|
|
703
703
|
{
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
// extractor never sees:
|
|
17
17
|
//
|
|
18
18
|
// D1 verify.mjs:940 fail(`connotation_${reason}:…`) — reason iterates the table at line 894
|
|
19
|
-
// D2 verify.mjs
|
|
20
|
-
// D3 verify.mjs:
|
|
21
|
-
// D4 verify.mjs
|
|
19
|
+
// D2 verify.mjs fail(String(e.message)) — parseFindingsJson throws token-first
|
|
20
|
+
// D3 verify.mjs:1184 checkJson: fail(String(e.message)) — FIVE parsers reach this one site
|
|
21
|
+
// D4 verify.mjs parseCoverageLedgerJson, same shape
|
|
22
22
|
// D5 verify.mjs:1504 fail(`${unaccounted[0].token}:…`) — token minted in a DATA ROW
|
|
23
23
|
// D6 verify.mjs:1567 fail(`${violations[0].token}…`) — validatePlanFeasibility in register-plan.mjs
|
|
24
24
|
// D7 verify.mjs:1558 fail(`${v2[0].token}${detail}…`) — register-plan.mjs:2018 disclosureTextByAxis
|
|
@@ -99,7 +99,7 @@ export const VOCABULARY = [
|
|
|
99
99
|
{ token: "ratified_form_unread", stages: ["synthesis"], site: "driver/verify.mjs" },
|
|
100
100
|
{ token: "coverage_recommendation", stages: ["synthesis"], site: "driver/verify.mjs" },
|
|
101
101
|
{ token: "coverage_gap_unexplained", stages: ["synthesis"], site: "driver/verify.mjs" },
|
|
102
|
-
{ token: "finding", stages: ["synthesis"], site: "driver/verify.mjs
|
|
102
|
+
{ token: "finding", stages: ["synthesis"], site: "driver/verify.mjs", family: "driver/findings-model.mjs (token-first throws; `finding_*` and `findings_*`)", dynamic: "D2" },
|
|
103
103
|
|
|
104
104
|
// ── register-digest ────────────────────────────────────────────────────────────────────────────────
|
|
105
105
|
{ token: "coverage_form_damaged", stages: ["register-digest"], site: "driver/verify.mjs" },
|
|
@@ -109,16 +109,16 @@ export const VOCABULARY = [
|
|
|
109
109
|
{ token: "coverage_form_missing", stages: ["register-digest"], site: "driver/verify.mjs" },
|
|
110
110
|
{ token: "coverage_form_empty", stages: ["register-digest"], site: "driver/verify.mjs" },
|
|
111
111
|
{ token: "coverage_status_offenum", stages: ["register-digest"], site: "driver/verify.mjs:2050" },
|
|
112
|
-
{ token: "coverage_deferred_unaccounted", stages: ["register-digest"], site: "driver/verify.mjs:
|
|
112
|
+
{ token: "coverage_deferred_unaccounted", stages: ["register-digest"], site: "driver/verify.mjs:1510 coverageFormFail", family: "driver/register-plan.mjs:1642 PROVIDER_HARD_ERROR_PREFIX — token on a data row", dynamic: "D5" },
|
|
113
113
|
{ token: "coverage_clean_unexecuted", stages: ["register-digest"], site: "driver/verify.mjs, the matterContext validator", family: "driver/register-plan.mjs:1417 validatePlanFeasibility", dynamic: "D6" },
|
|
114
114
|
{ token: "coverage_clean_skipped", stages: ["register-digest"], site: "driver/verify.mjs, the matterContext validator", family: "driver/register-plan.mjs:1766 searchedJurisdictionsFromPlan", dynamic: "D6" },
|
|
115
115
|
{ token: "coverage_clean_unverified_incomplete", stages: ["register-digest"], site: "driver/verify.mjs, the matterContext validator", family: "driver/register-plan.mjs:2018 disclosureTextByAxis", dynamic: "D7" },
|
|
116
116
|
{ token: "coverage_clean_tainted", stages: ["register-digest"], site: "driver/verify.mjs" },
|
|
117
|
-
{ token: "coverage_ledger_", stages: ["register-digest"], site: "driver/verify.mjs
|
|
118
|
-
{ token: "coverage_key_unknown", stages: ["register-digest"], site: "driver/verify.mjs
|
|
119
|
-
{ token: "coverage_axis_", stages: ["register-digest"], site: "driver/verify.mjs
|
|
120
|
-
{ token: "coverage_status_invalid", stages: ["register-digest"], site: "driver/verify.mjs
|
|
121
|
-
{ token: "coverage_classes_invalid", stages: ["register-digest"], site: "driver/verify.mjs
|
|
117
|
+
{ token: "coverage_ledger_", stages: ["register-digest"], site: "driver/verify.mjs", family: "driver/coverage-ledger.mjs (parseCoverageLedgerJson token-first throws)", dynamic: "D4" },
|
|
118
|
+
{ token: "coverage_key_unknown", stages: ["register-digest"], site: "driver/verify.mjs", family: "driver/coverage-ledger.mjs", dynamic: "D4" },
|
|
119
|
+
{ token: "coverage_axis_", stages: ["register-digest"], site: "driver/verify.mjs", family: "driver/coverage-ledger.mjs", dynamic: "D4" },
|
|
120
|
+
{ token: "coverage_status_invalid", stages: ["register-digest"], site: "driver/verify.mjs", family: "driver/coverage-ledger.mjs", dynamic: "D4" },
|
|
121
|
+
{ token: "coverage_classes_invalid", stages: ["register-digest"], site: "driver/verify.mjs", family: "driver/coverage-ledger.mjs", dynamic: "D4" },
|
|
122
122
|
{ token: "plan_execution_unreadable", stages: ["register-digest", "narrative-refutation"], site: "driver/verify.mjs:1496, 1712" },
|
|
123
123
|
|
|
124
124
|
// ── matter-frame / prelim-variants / blind-frame / frame-diff ──────────────────────────────────────
|
|
@@ -130,7 +130,7 @@ export const VOCABULARY = [
|
|
|
130
130
|
{ token: "variantmodel_term_markup", stages: ["prelim-variants"], site: "driver/verify.mjs" },
|
|
131
131
|
{ token: "variantmodel_missing", stages: ["prelim-variants"], site: "driver/verify.mjs:1153, 1212" },
|
|
132
132
|
// Recovered during E2 authoring, absent from the draft census: variant-manifest.json is strict-parsed
|
|
133
|
-
// through checkSiblingJson (verify.mjs:
|
|
133
|
+
// through checkSiblingJson (verify.mjs:1197) → checkJson (:742), so the WHOLE variantmodel_* family
|
|
134
134
|
// reaches prelim-variants, not just the four literal tokens above.
|
|
135
135
|
// CONVERSION 3 widened this family's SOURCE without widening its prefix. `acceptPrelimVariants` raises
|
|
136
136
|
// `variantmodel_scope_layer_invalid`, `_scope_status_invalid`, `_scope_item_missing` and `_scope_pipe`
|
|
@@ -319,7 +319,8 @@ export const STAGE_UNREACHABLE_VALIDATORS = [
|
|
|
319
319
|
*
|
|
320
320
|
* The drift is CLUSTERED, not random: 34, 35, 36, 52 recur. That is one insertion above a block moving
|
|
321
321
|
* every citation below it, all at once, silently, in a PR that was about something else entirely. A
|
|
322
|
-
* reader following `coverage_ledger_ → verify.mjs
|
|
322
|
+
* reader following `coverage_ledger_ → verify.mjs` to line 1603 landed on a comment 219 lines from the
|
|
323
|
+
* dispatcher. (That number is the 2026 measurement's own datum, not a pointer into today's file.)
|
|
323
324
|
*
|
|
324
325
|
* `symbol:` names a thing instead — a function, a constant, an exported name. It survives every move.
|
|
325
326
|
* `site:` stays as the hint it always was, and `contract-audit.test.mjs` asserts that any `symbol:` a row
|
|
@@ -419,9 +420,9 @@ export const INNER_CODES = Object.freeze([
|
|
|
419
420
|
// CONNOTATION_FORM_REASONS at connotation-search.mjs:1731 is `CONNOTATION_REASONS` minus
|
|
420
421
|
// `no_recorded_queries`, and the `connotation_` family row (dynamic D1) is declared against exactly
|
|
421
422
|
// that list. The four call codes are folded by a template — `connotation_${callFail.reason}` at
|
|
422
|
-
// verify.mjs
|
|
423
|
+
// verify.mjs — so they are namespaced by construction rather than one branch at a time.
|
|
423
424
|
{ code: "call_never_made", mints: ["driver/connotation-search.mjs:2007"], rollsUpTo: ["connotation_call_never_made"],
|
|
424
|
-
why: "CALL_AUDIT_ROWS. The typed transport's four call states, handed in by disposition-call-audit.mjs and namespaced at verify.mjs
|
|
425
|
+
why: "CALL_AUDIT_ROWS. The typed transport's four call states, handed in by disposition-call-audit.mjs and namespaced at verify.mjs." },
|
|
425
426
|
{ code: "call_truncated", mints: ["driver/connotation-search.mjs:2008"], rollsUpTo: ["connotation_call_truncated"],
|
|
426
427
|
why: "As call_never_made — same table, same projection." },
|
|
427
428
|
{ code: "call_schema_violation", mints: ["driver/connotation-search.mjs:2009"], rollsUpTo: ["connotation_call_schema_violation"],
|