Commit f5a4ef0
* refactor(spec)!: connector-side error enums get Connector* names (ADR-0112 D9a)
packages/spec exported two mutually incompatible ErrorCategory types and two
RetryStrategy types — api/errors.zod.ts (HTTP error responses) and
integration/connector.zod.ts (connector normalisation). Separate subpath
exports kept TypeScript quiet, but the doc/schema generators key on the
stripped name, so a cross-reference to 'ErrorCategory' resolved to whichever
was scanned last.
Rename the connector-side pair to ConnectorErrorCategory / ConnectorRetryStrategy
and say in each docblock why the name is prefixed. Both are spec-internal (only
connector.zod.ts and its test referenced them), so no downstream break.
The json-schema ratchet caught the rename as an unpublish; the two manifest keys
are renamed alongside, per its instructions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaSQn77TT5fUgHK9CesaDK
* refactor(cloud-connection,plugin-auth,hono)!: sweep the two largest lowercase code clusters (ADR-0112 batch 2)
Converts 105 lowercase snake_case error codes to the declared vocabulary and
registers the service-specific ones in ERROR_CODE_LEDGER.
Generic conditions collapse onto the standard catalog rather than keeping a
synonym: 'unauthorized'/'unauthenticated' -> UNAUTHENTICATED, 'forbidden' ->
PERMISSION_DENIED, 'not_found' -> RESOURCE_NOT_FOUND, 'internal' ->
INTERNAL_ERROR, 'unavailable' -> SERVICE_UNAVAILABLE, 'not_supported' ->
NOT_IMPLEMENTED, and 'bad_request' -> INVALID_REQUEST (a status name is not a
semantic code). Domain conditions get registered codes, prefixed where the bare
name wouldn't carry meaning (MARKETPLACE_STORAGE_FAILED, PLUGIN_MANIFEST_INVALID,
ENVIRONMENT_BIND_FAILED, RESEED_SKIPPED, ...). New ledger entries:
@objectstack/cloud-connection and @objectstack/hono.
Also closes an unbounded code source found while sweeping: the OAuth client
registration route put better-auth's arbitrary `body.error` string straight into
`error.code`, so any upstream string became a code and the closed set could not
hold. The code is now ours (OAUTH_REGISTER_FAILED) and the upstream
discriminator moves to `details.upstreamError`.
Per-package consumer check (ADR-0112 batch-2 precondition): no reader outside
either package branches on the swept spellings; the in-package test assertions
move with the emitters.
Refs #4003, #3841.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaSQn77TT5fUgHK9CesaDK
* refactor(metadata-protocol,service-automation,spec)!: sweep the thrown-error codes; keep the audit column out (ADR-0112 batch 2)
Three clusters, all of which reach `error.code` on the wire — a thrown error's
`.code` is copied straight into the response body (rest-server.ts:268), so these
were never merely internal.
- metadata-protocol: 24 thrown codes (ITEM_LOCKED, NOT_OVERRIDABLE,
METADATA_CONFLICT, DESTRUCTIVE_CHANGE, ...) registered under the package, plus
the two `evaluateLockFor*` helpers in spec that feed them.
- service-automation resume refusals: 'forbidden' -> PERMISSION_DENIED,
'invalid_signal' -> INVALID_SIGNAL, together with the runtime branch that
reads them and the contract docstring that documents them.
Two decisions worth keeping:
`sys_metadata_audit.code` is deliberately NOT swept. It spells its denial
reasons identically to the codes the protocol throws, so a naive sweep renames
it too — but the column is persisted audit history (rows predating ADR-0112 keep
their spelling forever) and it also holds non-error outcomes ('ok',
'lock_override'). Following the catalog would buy a data migration or one column
with two vocabularies mixed by write date. It stays lowercase and self-contained;
the divergence is recorded at both sites and as ADR-0112 D6b, so it reads as a
decision rather than a missed rename.
The ADR's "no consumer branches on the lowercase dialect" claim was wrong: the
per-service re-verification it mandated found three, each renamed atomically
here. The ADR now records the correction plus a sweep checklist of the six
literal forms a `code: '...'` grep misses (comparisons, property assignment,
computed ternaries, regex assertions, toBe assertions, docstrings) — every one
of them cost a red suite in this batch.
Refs #4003, #3841.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaSQn77TT5fUgHK9CesaDK
* refactor(rest,service-messaging,plugin-webhooks)!: sweep the last top-level codes and un-nest three flat envelopes (ADR-0112 batch 2)
- rest: OBJECT_NOT_FOUND, UNAUTHENTICATED (it sat in the same if/else as an
already-SCREAMING PERMISSION_DENIED), INVALID_REQUEST.
- service-messaging redeliver: RESOURCE_NOT_FOUND / DELIVERY_NOT_ELIGIBLE, with
the plugin-webhooks route that branches on them.
Three routes also had a bare code string in the `error:` slot with a real
`message:` beside it, so `error` was a code and the declared object envelope was
nowhere. They now emit `error: { code, message }` — a rename alone would have
left them unvalidated, which is how the lowercase dialect survived the first
place.
`mapDataError`'s downcasing is inverted, not just renamed. It deliberately
translated the internal SCREAMING OBJECT_NOT_FOUND down to `object_not_found` on
the way out, and a test pinned exactly that ("does not let the generic 4xx
passthrough ship the internal SCREAMING_CASE code") — correct when the data
routes were believed to speak their own dialect, backwards once there is one
vocabulary. The translation is gone and the test is rewritten to pin what still
matters: no per-route dialect translation, what the gate threw is what ships.
Two comments that still taught the lowercase wire code are updated with it.
Field-level codes in `import-coerce.ts` are deliberately untouched (ADR-0112 D6,
#3977).
Refs #4003, #3841.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaSQn77TT5fUgHK9CesaDK
* docs: add batch-2 changeset; webhook redeliver doc follows the new envelope
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaSQn77TT5fUgHK9CesaDK
* refactor(trigger-api,spec)!: trigger-api answers in the declared envelope; draw the catalog's boundary (ADR-0112 batch 2)
The API-trigger webhook endpoint returned `{ error: '<code>' }` — a bare code in
the message slot, six times, three of them with no message at all. It now answers
`{ success: false, error: { code, message } }` with catalog codes
(RESOURCE_NOT_FOUND, INVALID_REQUEST, SERVICE_UNAVAILABLE) plus two registered
ones (INVALID_SIGNATURE, ENQUEUE_FAILED). Every branch gains the message it was
missing.
Also records what batch 2 decided NOT to sweep, as ADR-0112 D6c, because three
lowercase clusters look like stragglers and are not:
- Diagnostics records (`build-probes.ts` RuntimeBuildIssue,
`metadata-diagnostics.ts`) ship as the payload of a 200, describe an artifact
rather than the request, and carry a severity that can be 'warning'.
- Param/field-addressed issues (`ActionParamIssue`, plugin-sharing's `fields[]`)
are D6 territory; plugin-sharing's own top-level code is already
VALIDATION_FAILED.
- CLI `emitJson({ error: '...' })` output is a terminal contract read by shell
scripts, not a request response — renaming it would break users' pipelines
with neither a schema nor this ADR asking for it.
The line, now written down once: the catalog governs the code a failing REQUEST
answers with. Field-addressed (D6), persisted (D6b), or shipped inside a
successful response (D6c) means a different vocabulary with different consumers.
Refs #4003, #3841.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaSQn77TT5fUgHK9CesaDK
* fix(spec)!: the automation resume contract's code type follows the rename
The resume result's `code` is a literal-union TYPE in the service contract, not
just a value, and batch 2 renamed only the values plus the docstring beside it.
Nothing in spec failed: `tsc --noEmit` on the declaring package is happy with a
narrower union than its callers emit. The break surfaced two packages away — the
dts build of every consumer of @objectstack/spec/integration, which is what took
Build Core, Test Core and the dogfood shards red at once.
Added to the ADR's sweep checklist as its own row, since it is the one form whose
error appears in a package the sweep never touched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaSQn77TT5fUgHK9CesaDK
* fix(runtime,plugin-approvals,metadata-protocol): the last four readers of the automation and publish codes
Four more consumers of codes batch 2 renamed, found by running the full suite
rather than grepping harder:
- plugin-approvals asserts the resume refusal code (its approval node IS the
service-gated case), a package the sweep never touched.
- http-dispatcher.test mocks the automation service's result, so its two
literals are the emitter's contract restated in a mock.
- batch_aborted -> BATCH_ABORTED: it rides in the same failed[] array as the
causal item's catalog code, so leaving it lowercase made one array carry two
vocabularies.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaSQn77TT5fUgHK9CesaDK
* fix(qa): the dogfood package-first authoring assertion follows the rename
Sixth consumer found, and the one my local verification could not have caught:
the full-suite run I used as the safety net excludes @objectstack/dogfood by
design, so the dogfood shards were the one gate I had not actually run.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaSQn77TT5fUgHK9CesaDK
* feat(ci): guard against new lowercase error codes (ADR-0112, #4003)
The ledger's admission test enforces casing on every code someone REGISTERS.
Nothing enforced the ones nobody registers — and an unregistered lowercase
literal in a code position is invisible twice: to the ledger, which has nothing
to check, and to the schema, on any route that never parses its own response.
That is how 208 lowercase literals accumulated across 10 packages. Without a
guard, number 209 lands the same way.
`scripts/check-error-code-casing.mjs` scans packages/ for four code positions —
emission, property assignment, comparison, and literal-union type — which are
the forms batch 2 actually got bitten by, comparison and union-type being the two
that fail silently or two packages away.
The design problem is that `code` is also an ordinary domain field name: a
license plan code, a seed industry code, a locale. A guard that flags those gets
switched off within a week, so a hit only counts when an error-shaped neighbour
(error/message/throw/status/issues/...) appears within a few lines, and the
D6 families are recognised structurally — a `field`/`param`/`path`/`target` key
in the same record, checked over a window because `{ code, field, message }` is
routinely spread across three lines.
Three escape hatches, in descending preference:
- structural: the D6 shapes above, no annotation needed;
- `// adr0112-ok: <reason>` at the site, for a file that legitimately holds
both vocabularies (protocol.ts throws a catalog code and writes an audit row
beside it) — the reason is required, a bare marker does not count;
- EXEMPT_FILES, for a file that OWNS a non-catalog vocabulary (a diagnostics
producer, OData's foreign error vocabulary), each line carrying its reason.
`--self-test` runs 17 cases (each violating form, each exemption route, each
known false-positive shape) before the scan, so a green run means the matcher
was checked, not just quiet. End-to-end verified by reintroducing
`code: 'object_not_found'` in rest-server.ts: the guard fails, and passes again
when reverted.
Two real fixes fell out of turning it on: the `'locked'` fixtures in two
publish tests now spell what the protocol actually throws (ITEM_LOCKED), and the
audit-row sites carry their D6b marker so the divergence reads as a decision.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaSQn77TT5fUgHK9CesaDK
* docs: the batch-2 changeset covers trigger-api, plugin-approvals and the new guard
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaSQn77TT5fUgHK9CesaDK
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent fb7de07 commit f5a4ef0
79 files changed
Lines changed: 923 additions & 445 deletions
File tree
- .changeset
- .github/workflows
- content/docs
- automation
- references
- api
- integration
- kernel
- docs/adr
- packages
- adapters/hono/src
- cloud-connection/src
- metadata-core/src/objects
- metadata-protocol/src
- objectql/src
- plugins
- plugin-approvals/src
- plugin-auth/src
- plugin-webhooks/src
- qa/dogfood/test
- rest/src
- runtime/src
- domains
- services
- service-automation/src
- service-messaging/src
- spec
- src
- api
- contracts
- integration
- kernel
- triggers/trigger-api/src
- scripts
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
114 | 114 | | |
115 | 115 | | |
116 | 116 | | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
117 | 126 | | |
118 | 127 | | |
119 | 128 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
399 | 399 | | |
400 | 400 | | |
401 | 401 | | |
402 | | - | |
403 | | - | |
404 | | - | |
405 | | - | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
406 | 407 | | |
407 | 408 | | |
408 | 409 | | |
| |||
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | | - | |
16 | | - | |
| 15 | + | |
| 16 | + | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
20 | 20 | | |
21 | 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 | 22 | | |
52 | 23 | | |
53 | 24 | | |
| |||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
137 | 137 | | |
138 | 138 | | |
139 | 139 | | |
| 140 | + | |
140 | 141 | | |
141 | 142 | | |
| 143 | + | |
142 | 144 | | |
143 | 145 | | |
144 | 146 | | |
145 | 147 | | |
146 | 148 | | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
147 | 152 | | |
148 | 153 | | |
149 | 154 | | |
| |||
152 | 157 | | |
153 | 158 | | |
154 | 159 | | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
155 | 164 | | |
| 165 | + | |
156 | 166 | | |
157 | 167 | | |
158 | 168 | | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
159 | 172 | | |
160 | 173 | | |
161 | 174 | | |
| |||
187 | 200 | | |
188 | 201 | | |
189 | 202 | | |
| 203 | + | |
190 | 204 | | |
191 | 205 | | |
192 | 206 | | |
193 | 207 | | |
| 208 | + | |
| 209 | + | |
194 | 210 | | |
195 | 211 | | |
196 | 212 | | |
197 | 213 | | |
198 | 214 | | |
| 215 | + | |
199 | 216 | | |
200 | 217 | | |
201 | 218 | | |
| 219 | + | |
202 | 220 | | |
203 | 221 | | |
204 | 222 | | |
205 | 223 | | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
206 | 227 | | |
207 | 228 | | |
208 | 229 | | |
| |||
211 | 232 | | |
212 | 233 | | |
213 | 234 | | |
| 235 | + | |
| 236 | + | |
214 | 237 | | |
| 238 | + | |
215 | 239 | | |
| 240 | + | |
216 | 241 | | |
217 | 242 | | |
218 | 243 | | |
219 | 244 | | |
| 245 | + | |
220 | 246 | | |
221 | 247 | | |
| 248 | + | |
222 | 249 | | |
| 250 | + | |
223 | 251 | | |
224 | 252 | | |
225 | 253 | | |
| |||
231 | 259 | | |
232 | 260 | | |
233 | 261 | | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
234 | 265 | | |
235 | 266 | | |
236 | 267 | | |
| |||
246 | 277 | | |
247 | 278 | | |
248 | 279 | | |
| 280 | + | |
| 281 | + | |
249 | 282 | | |
250 | 283 | | |
251 | 284 | | |
252 | 285 | | |
253 | 286 | | |
254 | 287 | | |
255 | 288 | | |
| 289 | + | |
256 | 290 | | |
257 | 291 | | |
258 | 292 | | |
| |||
266 | 300 | | |
267 | 301 | | |
268 | 302 | | |
| 303 | + | |
269 | 304 | | |
270 | 305 | | |
271 | 306 | | |
| |||
283 | 318 | | |
284 | 319 | | |
285 | 320 | | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
286 | 324 | | |
287 | 325 | | |
288 | 326 | | |
| |||
0 commit comments