Repository navigation
Commit 48297ad
Fixes #21822
Clause-②: no
## What changed
After a restart whose `kernel:ready` rehydrate refused a
protocol-incompatible package (ADR-0087 D1), `GET
/api/v1/marketplace/install-local` listed it like any loaded package,
and each GET logged a `warn` that it could not read the package's seed
rows. Now the rehydrate records each entry it refuses, and the listing
serves that entry with a `notLoaded` marker carrying the refusal's code
and the declared range. The listing reads no seed rows for it, so the
per-request warn is gone. This is the direction triage ruled
(`5988934231`): **a marker, not omission**.
`packages/cloud-connection/src/marketplace-install-local-plugin.ts`
only. ⛔ No change to the refusal itself (its line, level and wording),
to DELETE, to the install route or to `handleReseed`. No `packages/spec`
path.
## The wire shape (for objectstack-ai/objectui#11645 to render)
A refused entry, as an operator (`manage_metadata`) reads it:
```json
{
"packageId": "com.example.crm",
"versionId": "…",
"manifestId": "com.example.crm",
"version": "…",
"installedAt": "…",
"notLoaded": { "code": "OS_PROTOCOL_INCOMPATIBLE", "requiredRange": "^16" },
"installedBy": "…"
}
```
- `notLoaded` is CLOSED: exactly `code` and `requiredRange`, the
handshake diagnostic's own member names (the same `requiredRange` the
install door's 422 carries in `error.details`). It is not a spread of
the diagnostic.
- `notLoaded` stands **in place of** `withSampleData`. The listing does
not read the package's rows, so it makes no claim about them: the key is
omitted, not `false`. This follows the same rule as `installedBy` for a
narrowed caller.
- A narrowed caller (authenticated, no `manage_metadata`) gets the same
item without `installedBy`. The marker is served to every authenticated
caller.
- A loaded entry is byte-identical to before: `withSampleData`, no
`notLoaded` key. `total` counts marked entries.
- After `DELETE` the entry is gone from the ledger and the listing. Once
a compatible version is installed over it, the entry is listed as
loaded, with no marker.
## Where "not loaded" comes from (H3, measured on the code)
I compared two sources:
- **A**, the rehydrate's own record of what it refused.
- **B**, re-running `checkProtocolCompat` on each ledger entry per
request.
I chose **A**. It records what the rehydrate did, so it agrees with it
by construction. B is a second judgement, and it disagrees with what
happened in reachable states:
- `rehydrate` returns before the handshake loop when there is no
`manifest` service ("no `manifest` service — rehydrate skipped"). B
would mark entries whose refusal never happened.
- The listing re-reads the ledger directory on every request. The
rehydrate's own comment names a ledger shared with "a runtime of another
protocol", so an entry written there after this boot was never judged
here, and B would report a refusal this runtime never made.
The record is keyed by manifest id and holds the refused entry's
`installedAt`, which says which entry was refused. The install door is
the only writer of a new entry, it always stamps a fresh `installedAt`,
and it runs the same handshake first, so it can never write an
incompatible entry. So a compatible re-install stops the marker without
any write to the record from the install door or from DELETE.
`rehydrate` clears the record when it starts.
## Pins (the ruling's, verbatim, plus the order's addition)
- "after a restart whose rehydrate refused an entry, `GET
/install-local` lists it with the marker and the code."
- "A loadable entry is unchanged, and DELETE on the marked entry still
works."
- No seed-row read and no `withSampleData` warn for the marked entry.
Unit (`src/marketplace-install-local-listing-not-loaded.test.ts`, 7
cases): a fresh plugin over a ledger with one refused and one loadable
entry. The engine double answers only for registered objects, the way
the real one answers `Object '…' not found`. The cases cover:
- the marker, on the operator's item and the narrowed caller's;
- the closed member set;
- zero reads of the marked package's object and zero seed-row warnings,
with a lit control that the loadable entry's object was read;
- the loadable item, byte-equal to the one a ledger without the refused
entry serves;
- DELETE;
- a compatible re-install clears the marker.
Door
(`packages/qa/dogfood/test/install-local-listing-not-loaded.dogfood.test.ts`,
7 cases): real showcase boots over one `databaseFile` and one ledger.
- Boot 1 installs the CRM package (28 seed rows) and a small loadable
package.
- Between the boots, the CRM ledger entry is rewritten to declare
`^(major-1)`, an install made for an older runtime.
- Boot 2's rehydrate refuses it; the capture holds the `error` line.
- The GET serves the marker. The loadable item is byte-identical to its
boot-1 item. DELETE answers 200, and the listing then holds the loadable
package alone.
- The no-warning assertion has a lit control: an unreadable ledger file
planted before boot 2 makes the same GET log its own `warn` through the
same logger, in the same capture window.
## Reverse verification (one-off, not kept)
The plugin file was restored to the merge base (`c4d57131`, blob
`50a71c2f`) with a trap that restores `HEAD`. Each leg was proven on
disk: the `refusedAtRehydrate` count went 6 → 0 → 6, the hash equalled
the BASE blob, then the `HEAD` blob `411ad7a9`, and `git diff HEAD` came
back empty. Both suites import the plugin from `src`: the unit test by a
relative path, and the dogfood `isolated` project aliases
`@objectstack/cloud-connection` to
`../../cloud-connection/src/index.ts`. So no `dist` leg applies.
- Unit, pre-fix: **3 failed | 4 passed (7)**. The marker cases fail, and
the read/warn case shows both facts: `readsOfMarkedEntry:
["qa_old_account"]` and the warning `com.example.qaold21822: the
installed-apps listing could not read this package's seed rows
(qa_old_account: Object 'qa_old_account' not found), so it answers
withSampleData: false for it`. With the fix: 7 passed.
- Door, pre-fix: **2 failed | 5 passed (7)**. `notLoaded` is
`undefined`, and the GET logs `WARN [MarketplaceInstallLocal]
com.example.crm: the installed-apps listing could not read this
package's seed rows (crm_account: Object 'crm_account' not found;
crm_contact: …; crm_opportunity: …; crm_lead: …; crm_activity: …)`. This
is the card's own reading, reproduced. With the fix: 7 passed.
The preservation pins (the loadable entry, DELETE, the compatible
re-install, the preconditions and the capture control) pass on both
sides, as preservation pins should.
## Tests and gates
All readings are from `claude/issue-21822-listing-not-loaded-marker`
after one merge of `origin/main` (`07bf21ff`). Final head: `36817931`.
- **Build:** the `@objectstack/cloud-connection` and
`@objectstack/dogfood` dependency closures, rebuilt after the merge:
`turbo run build`, 63/63 tasks.
- **`pnpm --filter @objectstack/cloud-connection test`:** 40 files / 492
tests passed (at `6bb9f960`). The one later commit, `36817931`, changes
only the new test's engine double, and that file was re-run at
`36817931`: 7/7.
- **`pnpm --filter @objectstack/cloud-connection typecheck`:** exit 0.
`tsc --listFiles` includes the new test file.
- **`pnpm --filter @objectstack/dogfood typecheck`:** exit 0. `tsc
--listFiles` includes the new dogfood file.
- **Dogfood, narrowed:** `vitest run --project isolated` over
`install-local-listing-not-loaded`, `install-local-listing-sample-data`,
`install-local-purge-sample-data` and
`install-local-no-active-organization`: 4 files / 27 tests passed. This
narrowing is proven, not assumed: `git grep -l -E
'MarketplaceInstallLocalPlugin|marketplace/install-local' --
packages/qa/dogfood/test` at HEAD names exactly these four files. The
rest of the suite belongs to CI's Dogfood Regression Gate.
- **Gate battery:** the 67 commands `dispatch-gates --commands` derives
for this change set (the dispatch's 49 plus 18 more), all exit 0 at
`36817931`. `dispatch-gates --ran`: "67 derived, 67 run, 0 NOT-MEASURED,
0 UNRUN".
- `check:dual-build-cjs-loads` first answered `PREREQUISITE NOT MET`:
eight packages outside this closure had no `dist`. They were built and
the gate re-ran green: "106 published require entry point(s) across 66
package(s) load".
- `check:where-matcher` caught the new engine double reading a
combinator as a field name. The double now refuses what it does not
implement: 461/461 conforming.
- **`pnpm lint`** (`eslint . --no-inline-config`, the whole repository):
exit 0 at `36817931`.
- **Stale-tree note:** after the one merge, `origin/main` gained
`088428fb` (#21823: the runtime dispatcher and
`scripts/check-route-envelope.mjs`). So `check:route-envelope` ran on
its pre-#21823 copy. CI runs on the merge ref.
## Acceptance notes
- **Other not-loaded states stay unmarked; they are outside this ruling,
which names the protocol refusal.** None was measured through a public
door, and none is filed.
- A rehydrate whose `register` throws ("rehydrate failed for …", at
`error`) is still listed with `withSampleData`, and gets the per-request
seed-row warning.
- A cloud-snapshot install whose hot-register failed ("will load on next
restart") answers 200 and is listed as installed until the restart.
- No `manifest` service at `kernel:ready` ("rehydrate skipped") lists
every entry as installed.
- If a second not-loaded cause is ever marked, `notLoaded.code` is its
slot. Today the shape is closed to the one code.
- **Not measured here:** reseed and purge on a marked entry.
objectstack-ai/objectui#11645 already says Details offers no action that
needs a loaded package.
- **Release text:** the pending #21775 changeset's "(for example, a
package the runtime did not load)" stays true, for the first case above.
- **The refusal line is unchanged:** same level (`error`), same wording.
Only its docblock now says the listing marks the entry.
- **Concurrency:** #21776's dev edits `handleReseed` in the same file.
This PR touches only the rehydrate's refusal branch, the record field,
`handleList` and a helper after it, so there is no overlapping region.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01RWZbGvPFcRKvUqASZtunCU)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 2799155 commit 48297ad
4 files changed
Lines changed: 608 additions & 17 deletions
File tree
- .changeset
- packages
- cloud-connection/src
- qa/dogfood/test
Lines changed: 12 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
Lines changed: 298 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 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 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
0 commit comments