diff --git a/.changeset/20281-connector-sync-moved-to-mapping.md b/.changeset/20281-connector-sync-moved-to-mapping.md index a72bca99ec0..ec19a77fc7b 100644 --- a/.changeset/20281-connector-sync-moved-to-mapping.md +++ b/.changeset/20281-connector-sync-moved-to-mapping.md @@ -25,15 +25,16 @@ declared-connector item carried neither key, and the def a provider registers is own. The `latest_wins` and `soft_delete` defaults read as configured policy and did nothing; the platform has no soft delete. -**Added (declared, not yet executed):** `mapping.connectorSource` — the pull +**Added:** `mapping.connectorSource` — the pull binding on the target side, beside the mapping's existing `targetObject`, `fieldMapping`, `mode` and `upsertKey`: `connector` (a `rest` or `openapi` connector instance), `action` (the action that reads the records), optional `input`, optional `recordsPath`, and an optional `watermark` (`field` on the record, `param` on the request) for a timestamp-incremental pull. Version 1 is a one-way pull. It carries no cadence (a `job` sets that), no credential (the -connector instance holds it) and no delete or conflict policy. Nothing executes it -in this release, and `os validate` / `os build` warn when it is authored. +connector instance holds it) and no delete or conflict policy. The connector sync +executor, `@objectstack/service-automation`'s `pullConnectorSource` (#20919), reads +it; nothing schedules a pull until the `job` stage lands. ### FROM → TO diff --git a/.changeset/21106-error-code-ledger-provenance-rows.md b/.changeset/21106-error-code-ledger-provenance-rows.md new file mode 100644 index 00000000000..255c997d320 --- /dev/null +++ b/.changeset/21106-error-code-ledger-provenance-rows.md @@ -0,0 +1,14 @@ +--- +'@objectstack/spec': minor +--- + +`ERROR_CODE_LEDGER['@objectstack/service-automation']` now lists `MAPPING_NOT_FOUND` and `UNSUPPORTED_TRANSFORM`, the two registered codes the connector sync executor (`pullConnectorSource`) stamps onto `ConnectorPullError.code` (#21106). + +Clause-②: yes + +Provenance, not identity. The per-package face of `ERROR_CODE_LEDGER` changes in this release in two steps, and neither changes the `ErrorCode` union, the wire or any HTTP answer: + +- The bulk-import runner, the mapping pipeline and the data-error classification moved out of `@objectstack/rest` (#20919), and each code's row moved to the package that now stamps it. `@objectstack/core` gains `AMBIGUOUS_MATCH`, `BLANK_MATCH_KEY`, `NO_MATCH`, `SUMMARY_RECOMPUTE_FAILED` and `UNSUPPORTED_TRANSFORM`. A new `@objectstack/types` key lists `CONCURRENT_UPDATE`, `ERR_DATASOURCE_UNAVAILABLE` and `UNIQUE_VIOLATION`. `@objectstack/rest` no longer lists those seven, because it stamps none of them now; it keeps `UNSUPPORTED_TRANSFORM`, which it still stamps. +- `@objectstack/service-automation` gains the two rows above. Both codes were already registered, under `@objectstack/rest` (and `UNSUPPORTED_TRANSFORM` under `@objectstack/core` as well). + +So a consumer reading `ERROR_CODE_LEDGER['@objectstack/rest']` sees seven fewer entries, and one reading the `@objectstack/core`, `@objectstack/types` or `@objectstack/service-automation` key sees the new ones. Nothing to migrate: every code keeps its wire value and its status. diff --git a/packages/spec/scripts/check-error-code-provenance.test.ts b/packages/spec/scripts/check-error-code-provenance.test.ts index 08b7de75990..2bba745ea65 100644 --- a/packages/spec/scripts/check-error-code-provenance.test.ts +++ b/packages/spec/scripts/check-error-code-provenance.test.ts @@ -22,7 +22,7 @@ import { deriveFindings, type StampSite, } from './check-error-code-provenance'; -import type { ProvenanceWaiver } from '../src/api/error-code-ledger.zod'; +import { ERROR_CODE_LEDGER, type ProvenanceWaiver } from '../src/api/error-code-ledger.zod'; const HERE = path.dirname(fileURLToPath(import.meta.url)); @@ -150,6 +150,43 @@ describe('deriveFindings — the reconciliation, both directions', () => { }); }); +describe('rows the scan cannot see, pinned by hand (the declared helper-call blind spot)', () => { + // The gate's header declares it BLIND to a helper indirection (a + // `makeError(code, …)` call site) and makes widening its patterns a card of + // its own. A registered code stamped ONLY through such a helper therefore + // has no machine check that its stamping package lists it, so each such row + // is pinned here, beside the site it stands for. Read off the ledger module + // inside this package, so the suite still reads nothing outside it. + // + // `@objectstack/service-automation`'s connector sync executor + // (`connector-pull.ts`, `pullConnectorSource`) stamps both codes below onto + // `ConnectorPullError.code` through its local `refuse(code, status, reason, + // message)` helper. + const HAND_PINNED_HELPER_ROWS = [ + { + owner: '@objectstack/service-automation', + code: 'MAPPING_NOT_FOUND', + site: "connector-pull.ts refuse('MAPPING_NOT_FOUND', 404, …)", + }, + { + owner: '@objectstack/service-automation', + code: 'UNSUPPORTED_TRANSFORM', + site: "connector-pull.ts refuse('UNSUPPORTED_TRANSFORM', 400, …)", + }, + ] as const; + + it('the blind spot is real: a refuse(...) call site is no stamp site to the scan', () => { + // The day this reads a site, the gate sees the form and the hand pins + // below can come out with the widening. + expect(scanSourceText("return refuse('REGISTERED_ONE', 404, 'reason', 'message');", registered)).toEqual([]); + }); + + it.each(HAND_PINNED_HELPER_ROWS)('$owner lists $code ($site)', ({ owner, code }) => { + const rows: readonly string[] = ERROR_CODE_LEDGER[owner]; + expect(rows).toContain(code); + }); +}); + describe('the shipped script', () => { it('--self-test passes (the per-pattern red legs, run exactly as CI runs them)', () => { const require = createRequire(import.meta.url); diff --git a/packages/spec/src/api/error-code-ledger.zod.ts b/packages/spec/src/api/error-code-ledger.zod.ts index eaf1bd4f5ce..229345964e1 100644 --- a/packages/spec/src/api/error-code-ledger.zod.ts +++ b/packages/spec/src/api/error-code-ledger.zod.ts @@ -1059,11 +1059,25 @@ export const ERROR_CODE_LEDGER = { 'AUTOMATION_UNSCOPED_RUN_DATA_ACCESS', 'EXECUTION_ERROR', 'INVALID_SIGNAL', // resume signal writes engine-internal variables + // [#21106] The connector sync executor (`connector-pull.ts`, + // `pullConnectorSource`) stamps this row and `UNSUPPORTED_TRANSFORM` below + // onto `ConnectorPullError.code` through its local `refuse(code, status, + // reason, message)` helper: 404 when no mapping artifact has the requested + // name, 400 for a `javascript` transform a pull does not execute. The class + // ships in this package's `dist`. No HTTP door on this tree (nothing invokes + // the pull yet; the thrown value is the boundary). Both codes are already + // registered under `@objectstack/rest` (and `UNSUPPORTED_TRANSFORM` under + // `@objectstack/core`), so these rows are provenance, not identity. + // `check:error-code-provenance` cannot see a `refuse(...)` call site (a + // declared blind spot), so `check-error-code-provenance.test.ts` pins both + // rows by hand. + 'MAPPING_NOT_FOUND', 'NODE_FAILURE', 'NO_EXECUTOR', 'RESUME_IN_PROGRESS', // duplicate resume refused while the first is running 'RUN_NOT_FOUND', // no suspension for this run id — unresumable for good 'STORE_UNAVAILABLE', // durable suspended-run store unreadable — existence unknown + 'UNSUPPORTED_TRANSFORM', // [#21106] see `MAPPING_NOT_FOUND` above ], '@objectstack/service-analytics': [ 'CUBE_NOT_FOUND',