Commit ab41a8f
fix(scripts): apply the tenant-audit census's engine-door rule to inline type literals (225 -> 227) (#19073)
Fixes #18944
Clause-②: no
The census applied its engine-door rule to **named declarations only**.
A write
call site whose receiver type is an inline type literal had no name for
the
engine type index to be keyed on, so it was classified `kind: 'other'`
and
subtracted from the certified population — however plainly its own text
stated
an ObjectQL write door. The census already **printed** both such sites
on every
run and called the subtraction "probably WRONG"; it then took the
subtraction
anyway. A diagnostic that names a subtraction as wrong and still takes
it is a
deferral, not a report.
## JOB ONE — the crux, answered before the classifier was touched
**Both sites PASS. This is a classification repair, not a live
violation.**
Measured with a probe that calls the census's own exported functions
(`resolveReceiver`, `resolveObjectNameArg`, `declaredObjects`,
`tenantContextOf`) on the **unmodified** module, i.e. before any edit to
the
classifier. Sites selected by symbol — file + verb + receiver text —
never by
the card's line numbers.
| site (re-derived by symbol) | object | tenancy | context | elevated |
violation? |
|---|---|---|---|---|---|
| `plugin-auth/src/audience-gate-test-support.ts` · `engine.insert` ·
re-derived at **:72** | `sys_invitation` | **enabled** |
`options.context` | yes | **no** |
| `plugin-auth/src/sso-client-secret.ts` · `e.update` · re-derived at
**:303** | `sys_sso_provider` | **disabled** (declared opt-out) |
`options.context` | yes | **no** |
Controls, because a "no" needs one:
- **corpus control (HITS)** — both files are in the census corpus on
this tree
(`collectSources().includes(...)` → `true` for each). A "no violation"
from a
file the instrument never reads would be void.
- **predicate control (HITS)** — the same violation predicate
(`tenancy === 'enabled' && carriesTenantContext === false`) lights on
**9**
other sites of 227, three named in the report
(`auth-plugin.ts`, `scim-connection-service.ts`,
`datasource-admin-plugin.ts`). The instrument can say "yes"; it said
"no"
here.
- what would have changed the reading: either site dropping its
`{ context: { isSystem: true } }` argument (→ `carries: false`), or
`sys_invitation` declaring `tenancy.enabled: false`.
⇒ **The figure downstream cards cite does not move.** `9 / 225` becomes
`9 / 227`: the numerator is unchanged and the denominator becomes
truthful. The
repair is to what this page can be read as having examined — ⛔ not to
any count
that anything cites as a violation.
On triage's still-open escalation branch
(「被减掉的两个写点里任一个在生产路径上执行过 ⇒ 升 p1」): still **not run**, and
it still needs a runtime reading. What this PR adds is that the branch's
safety
premise is spent for these two sites either way — once placed, neither
breaks
the rule the population exists to enforce. One of them is nonetheless
production-shaped (`migrateLegacySsoClientSecrets`, a boot-path
migration), so
the branch is reported as open rather than closed.
## The fix
`inlineEngineDoorOrOther()` judges a declared type text one more time
when no
indexed **name** is found in it, by reading the same door rule off the
text
itself. It calls `typeTextDeclaresEngineDoor` → `memberIsEngineDoor` —
the one
function that already answers that question for a named declaration — so
there
is no second spelling of the rule to drift from. Applied at all four
`resolveReceiver` returns that carry a declared type text (`fromEntry`,
an
`as` cast, a member access, a call's return type), not only at the two
that
happen to fire today. A named engine type still wins and still reports
its own
name; an inline literal with no **write** door is still a subtraction
that says
why.
⛔ Not a widening of the definition: it IS the definition, applied where
it had
only been reported.
## Before / after, with the ablation proof
Same tree, same command (`node scripts/tenant-audit-census.mjs`):
| | before | after |
|---|---:|---:|
| write call sites | 225 | **227** |
| object name decidable | 149 | 151 |
| tenancy enabled | 149 | 150 |
| declares tenancy off | 0 | **1** |
| threading a tenant context | 141 | 143 |
| **provably none AND tenancy-enabled** | **9** | **9** |
| options unreadable AND tenancy-enabled | 32 | 32 |
| decidably elevated | 106 | 108 |
| non-engine calls subtracted | 146 | 144 |
| subtractions the census could NOT defend | 3 | **1** |
| …of those, type text states an engine door | 2 | **0** |
Ablation, through `scripts/ablation-replace.mjs` so the write is
verified
against the disk rather than against an exit code (anchor: the helper's
own
guard, replaced with a dead one):
```text
anchor hits 1 time(s), as declared anchor 1 -> 0, replacement 0 -> 1
blob 40921ef -> f76872fe8b03 (git hash-object, before -> under mutation)
under mutation: census reads 225; anonymous-type 2; undefended 3; doorShaped 2
under mutation: check-tenant-audit-census.mjs EXIT=1
committed : | write call sites on the application surface | **227** |
census : | write call sites on the application surface | **225** |
restore: git checkout HEAD -- PATH (the explicit-HEAD form, absolute path)
blob after restore 40921ef
blob at HEAD 40921ef
git diff HEAD empty
```
Predicted direction before running it: reverting the classifier turns
the gate
red. Observed: exactly that — and the census reverts to the pre-fix
reading in
every one of the eleven rows above. So the artefacts in this PR are held
to the
fixed classifier, and the file that was edited is the file that ran.
## Both artefacts AND the prose
Both generated artefacts were rewritten by the census's own tooling
(`node scripts/tenant-audit-census.mjs --write`), ⛔ never by hand.
The page's **hand-written** prose was re-read against the new numbers.
One claim
was **falsified** by the placement and is corrected:
> exactly two opt out (`sys_api_key`, `sys_sso_provider`), and no write
call site
> on this surface targets either.
One now does — the legacy client-secret migration writes
`sys_sso_provider`
under an elevated context, which is why the generated `declares tenancy
off` row
reads **1** rather than 0. `sys_api_key` still has no write site on this
surface
(measured: no site in `--json` names it). Nine further hand-written
figures
moved and are updated (the gate holds every one of them: 23 prose
figures).
Two things stated rather than rewritten on a guess:
- **"18 classifier cases, 6 of which red against the old reading"** —
could not
be verified. The generator's self-test reports 44 cases before this
change
and 49 after, and the gate's own reports 24; none is 18, so the claim
counts
some subset this PR cannot identify. It is left as written and its
sentence
is narrowed to the two over-claims it was written for, rather than being
silently extended over the third instance this PR adds.
- **the `(47%)` → `(48%)` parenthetical** is hand-written and unenforced
(the
gate requires digits-then-`%`, not a value). 108/227 = 47.58%, so floor
gives
47 and nearest gives 48. Both surviving data points on the page (77% and
47%)
round the same way under either rule, so the convention is undetermined
from
the page itself; nearest-integer was used and is flagged here for the
seat.
## Verification
Gate families derived from the **real** change set after the final
commit:
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`
derived at `6c23858e5`, the final commit (3 paths vs merge base
`0ec81857a`;
the tool's own repo+commit declaration is in its stderr, and the list is
byte-identical to the derivation taken before the last commit).
**All 62 commands it printed were run on that HEAD, each exit code
captured
before any pipe: 62 run, 62 exit 0.** Five of them first refused as
`PREREQUISITE NOT MET` in a fresh worktree — `@objectstack/spec`,
`@objectstack/lint`, `@objectstack/formula`, then
`@objectstack/client-react`
were not built — so those packages' closures were built and the families
were
re-run rather than reported unmeasured. The per-command table is in the
report
comment on #18944.
NOT MEASURED, and named rather than counted as passes: the 14 families
that
apply only once a changeset exists (this PR declares none, see below),
the 11
that declare a whole-root population no path narrows, the 2 that take a
value
from the workflow, the 2 path-scheduled CI jobs (`Test Core`, `Build
Docs`) and
the 52 artifact-roster families whose `silent` verdict is a fact about a
list
rather than about these paths.
`scripts/check-tenant-audit-census.mjs` is this census's gate and it
judges:
```text
✓ check-tenant-audit-census: OK -- 227 write call sites certified (151 decidable;
9 tenancy-enabled sites PROVABLY carry no tenant context, 32 more unreadable),
23 prose figures held to the census, every write call site placed and every
`UNTYPED_RECEIVERS` row matched.
```
⛔ No gate was weakened: no ratchet raised, no floor lowered, no ledger
row
deleted, no test skipped. The `doorShaped` diagnostic is **kept** — now
0 by
construction — as the alarm for this hole reopening, and five new
self-test
cases pin the rule in both directions.
## Changeset
No changeset, and the `skip-changeset` label is **owed but not applied
by this
PR** — label management was withheld by this dispatch. Measured, not
assumed:
no package directory contains any of the three files (30 manifests
inspected;
the repo-root manifest is `private: true`), so nothing any package's
`files[]`
ships can move. Positive control: `packages/spec` is published and its
`files[]`
is non-empty, so the instrument can say "shipped" when it is. An
empty-frontmatter changeset is explicitly not an option here (the gate
rejects
newly added ones).
## Acceptance notes
- **to file (class (a), pre-existing, ⛔ not fixed here)** — the census
aborts
with a `ts-parse` refusal (exit 3) on a receiver whose inline type
literal
separates its members by a **newline only**: `declaredTypesIn` collapses
whitespace, so the separator is lost and the synthetic
`type CensusReceiver = ...` re-parse fails with "';' expected".
Reproduced
directly; control lit (the semicolon-separated form of the same literal
returns `true`). Live on `main` through the `anonymous-type` arm, which
has
called `typeTextDeclaresEngineDoor` on the same collapsed text since PR
#18943; this PR widens the same call to brace-free type texts that
mention a
write verb. No such receiver exists in the corpus today (573 sources,
exit 0).
- noted, not filed: `NON_ENGINE_REASONS` is exported and read by nothing
— its
keys are produced by `nonEngineReason` and its descriptions are
documentation
only, so its docblock's "closed set of seven" is closed by review, not
by a
gate. True today. Successor: the next author of this classifier.
- noted, not filed: the module docblock's "There are 45 of them" for
erased
(`any`) receivers reads 44 on this tree (18 + 15 + 11), a pre-existing
one-off drift in a comment, untouched here.
---
_Generated by [Claude
Code](https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 6f8d751 commit ab41a8f
3 files changed
Lines changed: 212 additions & 49 deletions
File tree
- content/docs/permissions
- docs/audits
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
74 | 84 | | |
75 | 85 | | |
76 | 86 | | |
| |||
86 | 96 | | |
87 | 97 | | |
88 | 98 | | |
89 | | - | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
90 | 105 | | |
91 | 106 | | |
92 | 107 | | |
| |||
106 | 121 | | |
107 | 122 | | |
108 | 123 | | |
109 | | - | |
| 124 | + | |
110 | 125 | | |
111 | 126 | | |
112 | 127 | | |
| |||
140 | 155 | | |
141 | 156 | | |
142 | 157 | | |
143 | | - | |
144 | | - | |
145 | | - | |
146 | | - | |
147 | | - | |
148 | | - | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
149 | 180 | | |
150 | 181 | | |
151 | 182 | | |
| |||
155 | 186 | | |
156 | 187 | | |
157 | 188 | | |
158 | | - | |
| 189 | + | |
159 | 190 | | |
160 | | - | |
161 | | - | |
| 191 | + | |
| 192 | + | |
162 | 193 | | |
163 | 194 | | |
164 | 195 | | |
| |||
169 | 200 | | |
170 | 201 | | |
171 | 202 | | |
172 | | - | |
| 203 | + | |
173 | 204 | | |
174 | 205 | | |
175 | 206 | | |
176 | 207 | | |
177 | 208 | | |
178 | | - | |
| 209 | + | |
179 | 210 | | |
180 | 211 | | |
181 | 212 | | |
182 | | - | |
| 213 | + | |
183 | 214 | | |
184 | 215 | | |
185 | 216 | | |
| |||
191 | 222 | | |
192 | 223 | | |
193 | 224 | | |
194 | | - | |
195 | | - | |
| 225 | + | |
| 226 | + | |
196 | 227 | | |
197 | | - | |
198 | | - | |
199 | | - | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
200 | 231 | | |
201 | 232 | | |
202 | 233 | | |
203 | 234 | | |
204 | | - | |
| 235 | + | |
205 | 236 | | |
206 | 237 | | |
207 | 238 | | |
208 | 239 | | |
209 | 240 | | |
210 | | - | |
| 241 | + | |
211 | 242 | | |
212 | 243 | | |
213 | 244 | | |
214 | 245 | | |
215 | | - | |
216 | | - | |
| 246 | + | |
| 247 | + | |
217 | 248 | | |
218 | 249 | | |
219 | 250 | | |
| |||
232 | 263 | | |
233 | 264 | | |
234 | 265 | | |
235 | | - | |
236 | | - | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
237 | 274 | | |
238 | 275 | | |
239 | 276 | | |
| |||
253 | 290 | | |
254 | 291 | | |
255 | 292 | | |
256 | | - | |
| 293 | + | |
257 | 294 | | |
258 | 295 | | |
259 | 296 | | |
260 | 297 | | |
261 | 298 | | |
262 | 299 | | |
263 | | - | |
| 300 | + | |
264 | 301 | | |
265 | 302 | | |
Lines changed: 18 additions & 12 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
36 | | - | |
37 | | - | |
| 36 | + | |
| 37 | + | |
38 | 38 | | |
39 | | - | |
40 | | - | |
41 | | - | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | | - | |
| 46 | + | |
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
| |||
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
65 | | - | |
66 | | - | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
67 | 73 | | |
68 | 74 | | |
69 | 75 | | |
70 | | - | |
71 | | - | |
72 | 76 | | |
73 | 77 | | |
74 | 78 | | |
| |||
80 | 84 | | |
81 | 85 | | |
82 | 86 | | |
83 | | - | |
| 87 | + | |
84 | 88 | | |
85 | 89 | | |
86 | 90 | | |
87 | 91 | | |
88 | 92 | | |
89 | 93 | | |
90 | | - | |
| 94 | + | |
91 | 95 | | |
92 | 96 | | |
93 | 97 | | |
| |||
112 | 116 | | |
113 | 117 | | |
114 | 118 | | |
| 119 | + | |
115 | 120 | | |
116 | 121 | | |
117 | 122 | | |
| |||
133 | 138 | | |
134 | 139 | | |
135 | 140 | | |
| 141 | + | |
136 | 142 | | |
137 | 143 | | |
138 | 144 | | |
| |||
0 commit comments