Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .changeset/20281-connector-sync-moved-to-mapping.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
---
'@objectstack/spec': minor
---
Expand Down Expand Up @@ -25,15 +25,16 @@
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

Expand Down
14 changes: 14 additions & 0 deletions .changeset/21106-error-code-ledger-provenance-rows.md
Original file line number Diff line number Diff line change
@@ -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.
39 changes: 38 additions & 1 deletion packages/spec/scripts/check-error-code-provenance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand Down Expand Up @@ -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);
Expand Down
14 changes: 14 additions & 0 deletions packages/spec/src/api/error-code-ledger.zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Loading