Repository navigation
Commit eadea57
fix(console): the injection assertion reads the plain runs inside long or escaped describe literals, so a .objectstack-sha pin gets a real witness (#24672)
Fixes #24663
Clause-②: no
The in-build console spec-injection assertion was vacuous at
`.objectui-sha` `12dc7b47` and passed the control bundle (exit 0). The
cause is in `describeCandidates` (`scripts/console-spec-probes.mjs`): it
took only `.describe()` literals of 32 to 160 characters with no escape.
Between objectui's `.objectstack-sha` `f4755a68a797` and this tree, the
one describe that changed (the flow `runAs` text) is over 160
characters, and its em dash is a backslash-u escape (a backslash, then
`u2014`) in the built dist. So both candidate pools came out empty and
the stamp recorded `freshWitness: null`.
## The change
- **A second candidate source.** A describe literal the old rule skips
is cut at every escape sequence and every quote character. Each run of
32 characters or more is now a candidate.
- **Why cut at escapes.** An escape can be spelled one way in a spec's
dist and another way in a bundle, but the text between escapes is plain
characters in both.
- **Why cut at quotes.** A bundler may re-quote a string and add a
backslash before a quote.
- **Why runs are not cut to 160.** That bound was about a whole literal
surviving the bundler. Windows of a changed run would also still be
contained in the other spec, and `pickProbe`'s substring rule would drop
them.
- **Old candidates are unchanged.** A literal the old rule accepted is
still taken whole, so every candidate it produced is still produced.
`chooseProbes`, the stamp shape and the cache-hit replay
(`check-console-injection.mjs`) are unchanged.
- **The fixture.** Self-test battery 16 in
`scripts/check-console-injection.mjs` (16 cases) is shaped like this
pin:
- a `.objectstack-sha` pin, with the reference as objectui's own build
of it;
- each distinguishing literal over 160 characters and carrying a
backslash-u escape (a backslash, then `u2014`);
- a shared literal over 160 characters, and a shared run before the
escape;
- an injected bundle where the dash is emitted raw, and a control bundle
where the escape is kept.
It asserts that the stamp carries the changed run as `freshWitness`,
that the assertion exits 1 on the control bundle, and that the good
stamp replayed over the control exits 1. The roster floor is 13 → 14.
- **No release workflow and nothing under `.github/workflows/**`
changed.** The diff is 2 files under `scripts/`.
## Reverse verification
The fix was committed first. Then the `literalSegments` call was
replaced by a comment, and `node scripts/check-console-injection.mjs
--self-test` exited 1 with 8 failures. Among them: the control bundle
"expected 1, got 0", the witness "expected … got null", and the replay
"expected 1, got 0". Restored with `git restore --source=HEAD`, the
porcelain was clean and the self-test exited 0 with 100 assertions.
## Proof on a real build (pin `12dc7b47c340`)
PR #24660 has not landed, so `.objectui-sha` on `main` is still
`4016370af3c9`. The build ran in a throwaway worktree: this branch at
`50f12801` merged locally with #24660's head `0b45222fef`. Nothing from
that merge is pushed. I will merge `main` into this branch once #24660
lands.
The build ran `turbo run build --filter=@objectstack/client...
--concurrency=2 && bash scripts/build-console.sh`, which is what `pnpm
objectui:build` runs, and it exited 0. It took the `.objectstack-sha`
branch, built objectui's own `@objectstack/*` at `f4755a68a797` as the
reference (`.stamp.json`: `source: pin`, `sha` `f4755a68a797…`), and
packed this tree in through `OBJECTSTACK_ROOT`. Its log:
```
✓ Console bundle carries THIS tree's @objectstack/spec, and only it (vendored: @objectstack/spec of objectui's own build of objectstack f4755a6).
present (injected only): " system flows keep system privileges; after an approval decision their attribution user is the decider; user = the human whose act is running this segment: the trigger user until a human decision, the decider after it (RLS-respecting; the nodes after an approval decision run with the decider"
— 3 of 3 injected-only descriptions are in the bundle
absent (vendored only): " not yet rendered] The PARENT record this related list hangs off, bound by a query of its own: an object binding "
— and all 2 pin-only descriptions are absent
```
The stamp records `skew: true`, `vendoredPin` `f4755a68a797…`,
`freshWitness` the run quoted above (was `null`), and `staleDetector`
the pin-only run.
| reading | before (`main`'s probes) | this branch |
|:--|:--|:--|
| candidate pools at this pin (injected-only / pin-only) | 0 / 0 | 3 / 2
|
| `pnpm check:console-injection --require-stamp` on the injected dist |
0 ("nothing assertable") | 0, the witness present and the detector
absent |
| `assert-console-spec-injection.mjs` on the **control** bundle | **0**,
"no observable skew" | **1**, "still carries the @objectstack/spec of
objectui's own build of objectstack f4755a6" |
| the injected stamp replayed over the control dist
(`check-console-injection.mjs --dist … --require-stamp`) | n/a (null
witness) | 1 |
- **The control bundle** is objectui's own build of the pin, with
nothing from this tree, built in the same build tree with
`OBJECTSTACK_ROOT` unset (`objectstack-pin.mjs status`: "built at the
pin f4755a6"). The bundle has no stamp.
- **Grep counts over each dist's `assets/*.js`** (`grep -lF`): the full
witness is in 1 injected asset and 0 control assets; the full detector
is in 0 injected assets and 1 control asset.
## Gates
- `node scripts/pm/check-governed-merges.mjs --branch
claude/issue-24663-injection-witness`: NOT governed; 134 changed lines.
- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands` derived **35** commands, and **all 35 exited 0**. The
`--ran` reconciliation reports "35 run, 0 NOT-MEASURED".
- `pnpm exec eslint` on both files exited 0.
- **No changeset:** the diff publishes nothing from any released package
(repo tooling under `scripts/`), as with PR #24532. Label
`skip-changeset`.
- **Not measured:**
- The Console Pin Gate does not run on a PR or in the merge queue. Its
first CI run on this change is `main`'s battery after the merge.
- The 15 pending-changeset families do not apply without a changeset.
## Acceptance notes
- The cache-hit replay labels the stale line "absent (published only)"
even when the stamp's reference is a pin build. That label is unchanged
here.
- The issue's other suggested source, the metadata-type registry
`description:` text (for example the `sharing_rule` entry), is not
added. The describe runs alone give a non-null witness at this pin.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---
_Generated by [Claude
Code](https://claude.ai/code/session_019oG4xEYrGxr5n2cdSNbWMK)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 8fd3944 commit eadea57
2 files changed
Lines changed: 130 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
182 | 182 | | |
183 | 183 | | |
184 | 184 | | |
| 185 | + | |
185 | 186 | | |
186 | 187 | | |
187 | 188 | | |
| |||
216 | 217 | | |
217 | 218 | | |
218 | 219 | | |
| 220 | + | |
219 | 221 | | |
220 | 222 | | |
221 | 223 | | |
222 | 224 | | |
223 | | - | |
| 225 | + | |
224 | 226 | | |
225 | 227 | | |
226 | 228 | | |
| |||
1176 | 1178 | | |
1177 | 1179 | | |
1178 | 1180 | | |
| 1181 | + | |
| 1182 | + | |
| 1183 | + | |
| 1184 | + | |
| 1185 | + | |
| 1186 | + | |
| 1187 | + | |
| 1188 | + | |
| 1189 | + | |
| 1190 | + | |
| 1191 | + | |
| 1192 | + | |
| 1193 | + | |
| 1194 | + | |
| 1195 | + | |
| 1196 | + | |
| 1197 | + | |
| 1198 | + | |
| 1199 | + | |
| 1200 | + | |
| 1201 | + | |
| 1202 | + | |
| 1203 | + | |
| 1204 | + | |
| 1205 | + | |
| 1206 | + | |
| 1207 | + | |
| 1208 | + | |
| 1209 | + | |
| 1210 | + | |
| 1211 | + | |
| 1212 | + | |
| 1213 | + | |
| 1214 | + | |
| 1215 | + | |
| 1216 | + | |
| 1217 | + | |
| 1218 | + | |
| 1219 | + | |
| 1220 | + | |
| 1221 | + | |
| 1222 | + | |
| 1223 | + | |
| 1224 | + | |
| 1225 | + | |
| 1226 | + | |
| 1227 | + | |
| 1228 | + | |
| 1229 | + | |
| 1230 | + | |
| 1231 | + | |
| 1232 | + | |
| 1233 | + | |
| 1234 | + | |
| 1235 | + | |
| 1236 | + | |
| 1237 | + | |
| 1238 | + | |
| 1239 | + | |
| 1240 | + | |
| 1241 | + | |
| 1242 | + | |
| 1243 | + | |
| 1244 | + | |
| 1245 | + | |
| 1246 | + | |
| 1247 | + | |
| 1248 | + | |
| 1249 | + | |
| 1250 | + | |
| 1251 | + | |
| 1252 | + | |
| 1253 | + | |
| 1254 | + | |
| 1255 | + | |
| 1256 | + | |
| 1257 | + | |
| 1258 | + | |
| 1259 | + | |
| 1260 | + | |
| 1261 | + | |
| 1262 | + | |
| 1263 | + | |
| 1264 | + | |
| 1265 | + | |
| 1266 | + | |
| 1267 | + | |
| 1268 | + | |
| 1269 | + | |
1179 | 1270 | | |
1180 | 1271 | | |
1181 | 1272 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
107 | | - | |
108 | | - | |
109 | | - | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
110 | 112 | | |
111 | 113 | | |
112 | 114 | | |
113 | 115 | | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
114 | 149 | | |
115 | 150 | | |
116 | 151 | | |
| |||
0 commit comments