Repository navigation
Commit 9dac9eb
<!-- claude-projects-attribution: {"github_login":"os-sales"} -->
_Requested by **Sales** · [project
thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuCEzLkAB12FfRHL9ipwfKPd)_
Part of #25222
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6384677413
Sales's ruling B (comment 6384677413) puts this census-reader PR before
slice 8 of the `engine.ts` split. Its claim is comment 6384913796. It
changes one file, `scripts/tenant-audit-census.mjs`. The census now
reads a `this.X` receiver through the host type its function declares
for `this`, so a method body moved out of its class keeps the verdict it
had inside the class. On `main`, no census reading changes.
## Why
- Slice 8 moves 21 `AutomationEngine` bodies. They hold 33
`this.FIELD.delete(` sites, all on the class's own `Map` and `Set`
fields.
- A moved body's function declares `this` as a `Pick` of a host
interface, with each member typed `AutomationEngine['FIELD']`. That is
the shape slices 1 to 7 use.
- Before this PR, the census read `this.X` only through a class property
declared in the same file. Inside a `function`, `this` names no
container, so the lookup fell to the file-wide floor, and a host module
declares no class. So every moved site would have been unreadable. Each
one was either refused as `[untyped-receiver]` or placed by its `name:
string` parameter as an engine write, which turns 238 sites into 239.
## What changed
- **`resolveReceiver`, the `this.X` branch.** Some functions declare
`this` as their first parameter (`this: …`). When that is the function
that binds the site's `this`, the census answers through the declaration
(`hostMemberEntry`). It takes three steps:
1. the type, with `Pick` and the other narrowing wrappers peeled off,
names an interface or a type-literal alias at the top level of the file;
2. that shape declares the member as a property with a type;
3. the member type is read as written. If it is `C['x']`, an indexed
access on a class, the census reads the class's own declaration of `x`
instead, in the file that declares the class (`classDeclarationIn`,
`scopedNames.own`).
When the host reading resolves, it wins, because it is what the compiler
checks the body against. When it cannot be read, the site stays on the
floor it had, so no receiver that resolved before stops resolving. That
covers these cases:
- no declared `this`;
- a host that is not a top-level shape;
- a class the census cannot load;
- an indexed access on an interface.
- **Across files.** The census reaches a class in another file through
the site file's named import of it.
- `declaredTypesIn` records named imports and takes an optional
`loadModule`.
- `runCensus` passes a loader. The loader resolves a relative specifier
(`moduleFileFor`: `./engine.js` to `engine.ts`).
- It reads only files in the census's own corpus, the same tracked,
non-test list it scans. It never reads `node_modules` or an untracked
file.
- **`fromEntry`**, the rule that turns one declaration into a verdict,
is lifted out of `resolveReceiver` unchanged as `receiverFromEntry`, and
`nameOf` becomes `indexedNameIn`. This lets the census read a class's
declaration against that class's own file. The two closures in
`resolveReceiver` now call them.
- **Not changed:** `UNTYPED_RECEIVERS`, the two rescue rules,
`check-tenant-audit-census.mjs`, the census page and the counts file. ⛔
No new gate, ledger row, CI step or `check:` script. `skip-changeset`,
because the diff publishes nothing.
## Self-test
Eight new cases (93 to 101), in a new battery whose sources import each
other:
| Case | Verdict | With the host reading ablated |
| --- | --- | --- |
| ⭐⭐ a host member typed off a class's `Map` in another file |
`other/constructed-locally` | fails, `unresolved/` |
| ⭐⭐ positive control: a host member typed off the same class's engine |
`engine/IProbeEngine` | fails, `unresolved/` |
| the class in the same file, after a decoy class whose same-named
properties are typed the other way round | `other/constructed-locally \|
engine/IProbeEngine` | fails, the decoy answers: `engine/IProbeEngine \|
other/platform-type` |
| a host member typed directly (`readonly engine: IProbeEngine`) |
`engine/IProbeEngine` | fails, `unresolved/` |
| an arrow inside the host function | `other/constructed-locally` |
fails, `unresolved/` |
| ⛔ a `function` expression inside the host function | `unresolved/` |
passes |
| ⛔ a host member typed off a class the census cannot load, with a
same-named typed property on the floor | `engine/IProbeEngine`, from the
floor | passes |
| ⛔ an indexed access on an interface | `unresolved/` | passes |
The ablation is one line, `const host = null;` in the `this.X` branch,
made in a scratch checkout. 5 of the 8 new cases fail, including both
fixtures the ruling names. The 3 that pass pin the fallback to the
floor, which is the same with or without the host reading.
## Readings
The base is `49653f91`, this branch's merge base with `main`, in a
worktree of that commit. `main` has since moved to `baae6ed1`, and
nothing that landed in between touches the census files. The head is
this PR's commit, `9a4c20ea`. Both were read in this container.
- **The 33 sites.** I built one probe module per `this.FIELD.delete(`
site in `engine.ts`, next to it. Each probe imports `AutomationEngine`
with `import type` from `./engine.js`, declares a host member typed
`AutomationEngine['FIELD']`, and calls `FIELD.delete` from a function
whose `this` is a `Pick` of that host. All 33 probes get the same
verdict as their in-class site: 32 `constructed-locally` and 1
`corpus-type` (`store?: SuspendedRunStore`). The 21 members that hold
the sites hold no other `insert`, `update` or `delete` call.
- **On `main`, the new branch answers no site.** I instrumented a
scratch copy to print every write-call receiver that reaches a declared
`this`, and over the whole corpus it printed none. 45 non-test files in
the corpus declare such functions. Their only `this.X.delete(` lines
(`this.readAt.delete` in `auth-manager-password-session-policy.ts`) sit
inside a class.
- **Outputs**, at both ends:
| Output | Base sha256 | Head sha256 |
| --- | --- | --- |
| `node scripts/tenant-audit-census.mjs`, stdout and stderr |
`5a3e8991ecd717edd4e123e9b9439d09d4d7d3b0b3c14125650b6696c08bda7e` |
`5a3e8991ecd717edd4e123e9b9439d09d4d7d3b0b3c14125650b6696c08bda7e` |
| `node scripts/tenant-audit-census.mjs --json`, with `measuredAt`
removed |
`d189877c2ae1afb94324f88fc9bef24925feda79b1897d5b246b509b6fa7055a` |
`d189877c2ae1afb94324f88fc9bef24925feda79b1897d5b246b509b6fa7055a` |
| `node scripts/check-tenant-audit-census.mjs` |
`7f0cc90798085180772c7255aa4f21d35b3b6a6bc6f041f4181234fd10cd1172` |
`7f0cc90798085180772c7255aa4f21d35b3b6a6bc6f041f4181234fd10cd1172` |
- The raw `--json` differs on one line: `measuredAt.ref`, which is the
HEAD sha (`49653f918` and `9a4c20ea7`).
- The gate's verdict at both ends: 238 write call sites certified (162
decidable; 2 provably without tenant context, 29 more unreadable), 23
prose figures held, every site placed and every `UNTYPED_RECEIVERS` row
matched.
- I also ran `--write` at each end and then reverted it. At both ends it
rewrites the same three lines in each artefact, against the committed
page and counts file. The base and head results differ only in the sha
on the unenforced `Measured on … at` line.
- **`node scripts/check-tenant-audit-census.mjs --self-test`** exits 0
at both ends. Its output differs on one line: the census self-test's
summary, whose count goes from 93 to 101 and whose sentence gains a
clause for the new cases. The gate's own line (27 cases) is identical.
Every earlier case still passes.
- This corrects item 2 of the ruling record: a self-test that counts its
cases cannot stay byte-identical once it gains cases. The default
output, the `--json` output and the gate output can, and do.
## Gates
- **Derived.** `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` on this commit derives 32 families from the
one changed path. All 32 ran in this container on this commit, with each
exit code captured before any pipe, and all 32 exit 0.
`dispatch-gates.mjs --ran` reconciles: 32 derived, 32 run, 0
NOT-MEASURED, 0 UNRUN.
- **`check-governed-merges --branch`** on this commit: 0 of 1 path on
the register, NOT governed, 356 changed lines (+317 / -39).
- **ESLint** on the file passes.
- **Open PRs.** I re-listed them before opening this one: 19. I diffed
each one's head against its merge base with `origin/main`. None touches
`scripts/tenant-audit-census.mjs`,
`scripts/check-tenant-audit-census.mjs`, `scripts/ts-parse.mjs`, the
census page or the counts file.
```text
node scripts/check-ci-filter-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-declaration-mirrors.mjs :: exit 0
node scripts/check-declaration-mirrors.mjs --self-test :: exit 0
node scripts/check-position-name-fold-loaders.mjs :: exit 0
node scripts/check-position-name-fold-loaders.mjs --self-test :: exit 0
node scripts/check-scripts-symbol-anchors.mjs :: exit 0
node scripts/check-scripts-symbol-anchors.mjs --self-test :: exit 0
node scripts/check-self-test-wired.mjs :: exit 0
node scripts/check-self-test-wired.mjs --self-test :: exit 0
node scripts/check-self-test-workflow-commands.mjs :: exit 0
node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0
node scripts/check-tenant-audit-census.mjs :: exit 0
node scripts/check-tenant-audit-census.mjs --self-test :: exit 0
node scripts/check-whole-set-label-write.mjs :: exit 0
node scripts/check-whole-set-label-write.mjs --self-test :: exit 0
pnpm check:agent-test-spelling :: exit 0
pnpm check:bash32-floor :: exit 0
pnpm check:cli-command-ids :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:entry-guard :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:parse-guard :: exit 0
pnpm check:pnpm-filter-targets :: exit 0
pnpm check:pnpm-flag-spellings :: exit 0
pnpm check:ratchet-remedy-authority :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:watch-hint-literal :: exit 0
```
## Next
When this merges, slice 8 moves the 21 site-bearing bodies. Its census
reading is the one the 33-site probe above already takes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01Mnj4nkWuhJ18xEdZvb2cjE
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1286eea commit 9dac9eb
1 file changed
Lines changed: 317 additions & 39 deletions
0 commit comments