Skip to content

Commit df1feae

Browse files
fix(spec): the pending connector-sync note matches the executor, and the error-code ledger lists its two refuse() codes (#21106) (#21150)
Fixes #21106 Clause-②: yes Three release-text follow-ups from the at-tier contract review of PR #21084 (comment `5926057594`), all due before Version Packages #20639 next picks up PR #21084. Each sentence was checked against the code at PR #21084's merge commit `8368f1c0`. The target files are byte-identical between `8368f1c0` and this branch's base (`git diff --stat` printed nothing). No package source changes. 1. **A deliberate correction of the stage-① pending note.** `.changeset/20281-connector-sync-moved-to-mapping.md` said `connectorSource` was "declared, not yet executed" and that "Nothing executes it in this release". The stage-② note `.changeset/20919-spec-connector-source-live.md` says the executor reads it. Both compile into the same `@objectstack/spec` CHANGELOG version. 2. **The "provenance, not identity" ledger sentence** for this release's `ERROR_CODE_LEDGER` face changes, in a new spec note, `.changeset/21106-error-code-ledger-provenance-rows.md`. 3. **The two `@objectstack/service-automation` provenance rows**, `MAPPING_NOT_FOUND` and `UNSUPPORTED_TRANSFORM`, in `packages/spec/src/api/error-code-ledger.zod.ts`. A hand pin in the provenance gate's own test, `packages/spec/scripts/check-error-code-provenance.test.ts`, guards them. ## 1 · The stage-① note correction | | Before | After | |:---|:---|:---| | Lead label | "**Added (declared, not yet executed):**" | "**Added:**" | | Last sentence of that paragraph | "Nothing executes it in this release, and `os validate` / `os build` warn when it is authored." | "The connector sync executor, `@objectstack/service-automation`'s `pullConnectorSource` (#20919), reads it; nothing schedules a pull until the `job` stage lands." | Everything else in the file is byte-identical: the front matter, the summary line, the BREAKING banner, the FROM → TO table and the retirement kit. The diff is +4 / −3 lines in one paragraph. **Readings at `8368f1c0`:** - **"reads it".** `packages/services/service-automation/src/connector-pull.ts` `pullConnectorSource` (:209) reads the mapping through `getMetaItem` and its `connectorSource`. It makes one action call and writes through `runImport`. The liveness rows say the same: `packages/spec/liveness/mapping.json`, `connectorSource` `live`, evidence `connector-pull.ts#pullConnectorSource`. - **"nothing schedules a pull until the `job` stage lands".** Outside tests, `pullConnectorSource` has three places in its own package: the definition, the index re-export and the plugin method (`plugin.ts:621`). No package source calls the plugin method. - **Why the review's suggested clause "`os validate` / `os build` warn when it is authored" is NOT kept.** It is false at `8368f1c0`, measured: - The ledger row is `live` with `authorWarn: true`. `lintLivenessProperties`'s `describe()` (`packages/lint/src/lint-liveness-properties.ts`) has no `live` branch. It throws its sentinel for that pair, by design: its own header calls the pair "a ledger authoring mistake". - Through lint's source, against the built spec: `runAuthoringRules('validate', …)`, the call `os validate` makes (`packages/cli/src/commands/validate.ts:522`), THREW `lintLivenessProperties: ledger entry has unrecognised status "live"` for a stack whose `mappings[]` carries `connectorSource`. The control without `connectorSource` gave 0 findings. - The runtime metadata-write door (`runRuntimeAuthoringRules`) answered one `authoring-rule-threw` advisory in place of the liveness warning. - Read at `99398542b` and again at this head. `lint-liveness-properties.ts`, `authoring-rules.ts`, `runtime-gate.ts` and `liveness/mapping.json` have no diff between `8368f1c0` and `99398542b`. The CLI process itself was not run: its closure was not built here. - So the corrected note claims no warning. The defect is reported to the seat as a separate finding below and not changed here. ## 2 · The ledger sentence, and the counts behind it Measured on `ERROR_CODE_LEDGER` at `8368f1c0` against its parent `2742e537`, comments stripped: | Key | Parent | `8368f1c0` | Change | |:---|---:|---:|:---| | `@objectstack/rest` | 83 | 76 | −7: `AMBIGUOUS_MATCH`, `BLANK_MATCH_KEY`, `CONCURRENT_UPDATE`, `ERR_DATASOURCE_UNAVAILABLE`, `NO_MATCH`, `SUMMARY_RECOMPUTE_FAILED`, `UNIQUE_VIOLATION` | | `@objectstack/core` | 12 | 17 | +5: `AMBIGUOUS_MATCH`, `BLANK_MATCH_KEY`, `NO_MATCH`, `SUMMARY_RECOMPUTE_FAILED`, `UNSUPPORTED_TRANSFORM` | | `@objectstack/types` | absent | 3 | new key: `CONCURRENT_UPDATE`, `ERR_DATASOURCE_UNAVAILABLE`, `UNIQUE_VIOLATION` | | owner keys | 30 | 31 | | | union (`ErrorCode`) | 280 | 280 | none added, none removed | **The card and the review say rest lost −8. The measurement is −7.** Eight codes moved (5 to core and 3 to types), but rest kept `UNSUPPORTED_TRANSFORM`, which `resolveNamedMapping` still stamps. Literal counts in `packages/rest/src` non-test source at `8368f1c0`: 0 for each of the seven, and 1 for `UNSUPPORTED_TRANSFORM`. The note says seven. The sentence follows the #20206 (`5f9d7d78`) and #19441 (`3f9e2eaa`) paragraphs. It names both steps of this release's face change, the #20919 move and this PR's two rows, and says the union, the wire and the HTTP answers are unchanged. ## 3 · The two rows, and why a pin rather than a wider gate - **Stamp sites at `8368f1c0`** (unchanged since). `connector-pull.ts:234` `refuse('MAPPING_NOT_FOUND', 404, 'mapping_not_found', …)` and `:303-304` `refuse('UNSUPPORTED_TRANSFORM', 400, 'unsupported_transform', …)`. Both go onto `ConnectorPullError.code`, and the class is exported from the package index. - **Both codes are registered extension codes.** They are listed under `@objectstack/rest`, and `UNSUPPORTED_TRANSFORM` under `@objectstack/core` too. Neither is in `errors.zod.ts`. The executor's other five codes are standard-catalog members and owe no row: `VALIDATION_ERROR`, `EXTERNAL_SERVICE_ERROR`, `INTEGRATION_ERROR`, `SERVICE_UNAVAILABLE`, `INVALID_FIELD`. - **Placement.** The rows go in the key's existing ASCII order: `MAPPING_NOT_FOUND` after `INVALID_SIGNAL`, and `UNSUPPORTED_TRANSFORM` last. One comment records the stamp sites, the statuses and the reachability reading: no HTTP door on this tree, so the thrown value is the boundary. No other package's rows were touched. - **Not widened.** `check:error-code-provenance`'s header declares it blind to a helper indirection (a `makeError(code, …)` call site). It also says "Widening is a gate-population change with an unmeasured blast radius — its own card, never a rider". So this PR keeps the gate's patterns and pins the two rows by hand instead. - **Measured for the seat, read-only.** The `refuse('CODE', …)` call-site form stamps a registered code at 7 sites in 3 packages: core `artifact-packages.ts` ×4, runtime `artifact-collections.ts` ×1, and these two. Before this PR, these two were the only unlisted ones; after it, none is unlisted. - **The pin.** A new block in the gate's test file has two halves. One pins the blind spot itself: `scanSourceText` finds no site in a `refuse('X', …)` call. The other asserts that `ERROR_CODE_LEDGER['@objectstack/service-automation']` lists each code. It reads the ledger module inside `packages/spec`, so the suite still reads nothing outside its package. - **Ablation, two legs** through `scripts/ablation-replace.mjs` in WRAP mode, run from the committed state at `c227eb366`: - Removing the `MAPPING_NOT_FOUND` row gave `1 failed | 16 passed (17)`: "expected [ …(9) ] to include 'MAPPING_NOT_FOUND'". - Removing the `UNSUPPORTED_TRANSFORM` row gave `1 failed | 16 passed (17)`, on that code's case. - Each leg restored the file (blob `229345964e13` = HEAD, `git diff HEAD` empty). - A first attempt at leg one was refused by the tool before any test ran: its replacement text already existed in the file. The anchor was changed and the leg re-run. That first attempt is not counted as a run. ## Changeset gate: no `skip-changeset`, and `Check Changeset` stays red by design This PR edits a pending changeset that it did not add. `node scripts/check-empty-changeset.mjs --base origin/main` exits 1 and refuses `.changeset/20281-connector-sync-moved-to-mapping.md` as the DELIBERATE CORRECTION class. The precedents are PR #20991 and PR #21012. Ruling D on #18375 says `skip-changeset` is never applied to a PR that edits an existing changeset, so no label is applied. `Check Changeset` is not a required context. **Confirmation requested in writing on this PR:** the stage-① note's `connectorSource` paragraph now says the executor reads the binding and nothing schedules a pull yet. It no longer says nothing executes it or that `os validate` / `os build` warn. ## `Clause-②: yes`, not the claim's `no` The claim (`5926939917`) and the dispatch say `patch` with `Clause-②: no`. The dispatch also says not to keep `no` silently if the diff widens a public surface, and it does: - Two literal members are added to `ERROR_CODE_LEDGER['@objectstack/service-automation']`, a published `as const` face. - The ledger header (the #16404 ruling) names this ledger, together with `StandardErrorCode`, as the published contract face for error codes. - The two precedents this card names, #20206 and #19441, each declared a provenance-row addition as `@objectstack/spec` `minor` with `Clause-②: yes`. Both say "What widens is the per-package face". So the new note is `minor` with `Clause-②: yes`, and this body's second line matches it. No accept set changes: the `ErrorCode` union is unchanged. Every package is in the one `fixed` group, and `@objectstack/spec` already has a pending `minor`, so the released version is the same either way. The seat can flip it back if it reads the precedent differently. ## Verification at `5748c8ddd` (base `99398542b`, merged with `origin/main` `39ab2940e`) - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (exit 0) derived 87 commands from the four changed paths. Each ran on this head, with its exit code captured before any pipe: - **84 exit 0.** Among them: `check:error-code-provenance` ("scanned 2636 files; 335 registered-code stamp site(s): 316 listed, 19 waived" / "OK"), `check-adr-0087-registration.mjs --base origin/main` ("this PR adds no declared-breaking changeset (2 non-breaking changeset(s) seen)"), `check-changeset-no-major.mjs --base origin/main` ("This diff introduces no `major` bump"), `check:api-surface`, `check:authorable-surface`, `check:docs`, `check:liveness`, `check:error-code-casing`, `check:dispatcher-error-vocabulary`, `check:cross-package-test-inputs`, `check:nul-bytes`, `check:query-options-erasure` and `check:type-check-debt`. - **1 exit 1, by design:** `check-empty-changeset.mjs --base origin/main`, the DELIBERATE CORRECTION class above. - **2 NOT MEASURED** (exit 3, `PREREQUISITE NOT MET`): `check:dual-build-cjs-loads` (needs a full `pnpm build`) and `check:lean-entry-closure` (needs `@objectstack/objectql` built). CI builds the tree. Reason: a built tree was not produced here. - Reconciled: `dispatch-gates.mjs --ran` exit 0, "87 derived famil(ies) accounted for — 85 run, 2 NOT-MEASURED". - `pnpm --filter @objectstack/spec build`: `VERDICT command-exit 0`. Then `pnpm --filter @objectstack/spec check:generated`: "All 15 generated artifacts are up to date". - `vitest run --project local` on `src/api` plus `scripts/check-error-code-provenance.test.ts`: 47 files / 1546 tests passed. The 12 other spec test files that read the ledger: 182 tests passed. - `pnpm --filter @objectstack/spec typecheck`: exit 0. That covers `tsc`, `check:scripts-typecheck`, and `check:test-typecheck` ("52 file(s) / 246 error(s) / 135 pinned signature(s) held", unchanged). - Main moved during the run: `origin/main` gained 13 commits after the branch point. None touches the four files. The delta against `39ab2940e` is exactly them (+70 / −4). ## Acceptance notes - **Read and kept in the stage-① note:** - "Runtime behaviour is deliberately **unchanged**: no connector sync ever ran." This is about the retired connector-attached keys (`syncConfig` / `fieldMappings`), and it stays true of them. - "It carries no cadence (a `job` sets that)." This describes the design; the corrected clause says nothing schedules a pull until the `job` stage lands. - **Reported to the seat, not changed here:** - **The liveness lint throws on `connectorSource`.** The `live` + `authorWarn` row reaches `describe()`'s sentinel throw, so `os validate` / `os build` stop with an internal lint error, and the runtime door answers `authoring-rule-threw`, instead of warning on an authored `connectorSource`. The comments in `authoring-rules.ts` and `runtime-gate.inert-type-writes.test.ts` still say it warns. The fix is a choice for its own card: teach `describe()` a caveat branch for `live`, or change the row. - **`turbo` 2.11.5 edits `AGENTS.md`.** It arrived with the development-dependencies bump (`840ec9dab`, now on `main`). On every turbo invocation it sees as an AI agent's, it appends a managed `turborepo-agent-rules` block to `AGENTS.md`, a Tier H governed surface, and `turbo.json` declares no `agentGuidance: false`. Measured here: `pnpm exec turbo run build --filter='@objectstack/lint...'` and `pnpm check:type-check-debt` each left `M AGENTS.md`, +11 lines. Each time it was restored with `git checkout HEAD -- AGENTS.md` (blob `e9e211fc` = HEAD) and never committed. An agent that commits with `-a` would carry it into its PR. --- _Generated by [Claude Code](https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 2488b98 commit df1feae

4 files changed

Lines changed: 70 additions & 4 deletions

File tree

‎.changeset/20281-connector-sync-moved-to-mapping.md‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,16 @@ declared-connector item carried neither key, and the def a provider registers is
2525
own. The `latest_wins` and `soft_delete` defaults read as configured policy and did
2626
nothing; the platform has no soft delete.
2727

28-
**Added (declared, not yet executed):** `mapping.connectorSource` — the pull
28+
**Added:** `mapping.connectorSource` — the pull
2929
binding on the target side, beside the mapping's existing `targetObject`,
3030
`fieldMapping`, `mode` and `upsertKey`: `connector` (a `rest` or `openapi`
3131
connector instance), `action` (the action that reads the records), optional
3232
`input`, optional `recordsPath`, and an optional `watermark` (`field` on the
3333
record, `param` on the request) for a timestamp-incremental pull. Version 1 is a
3434
one-way pull. It carries no cadence (a `job` sets that), no credential (the
35-
connector instance holds it) and no delete or conflict policy. Nothing executes it
36-
in this release, and `os validate` / `os build` warn when it is authored.
35+
connector instance holds it) and no delete or conflict policy. The connector sync
36+
executor, `@objectstack/service-automation`'s `pullConnectorSource` (#20919), reads
37+
it; nothing schedules a pull until the `job` stage lands.
3738

3839
### FROM → TO
3940

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
`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).
6+
7+
Clause-②: yes
8+
9+
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:
10+
11+
- 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.
12+
- `@objectstack/service-automation` gains the two rows above. Both codes were already registered, under `@objectstack/rest` (and `UNSUPPORTED_TRANSFORM` under `@objectstack/core` as well).
13+
14+
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.

‎packages/spec/scripts/check-error-code-provenance.test.ts‎

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
deriveFindings,
2323
type StampSite,
2424
} from './check-error-code-provenance';
25-
import type { ProvenanceWaiver } from '../src/api/error-code-ledger.zod';
25+
import { ERROR_CODE_LEDGER, type ProvenanceWaiver } from '../src/api/error-code-ledger.zod';
2626

2727
const HERE = path.dirname(fileURLToPath(import.meta.url));
2828

@@ -150,6 +150,43 @@ describe('deriveFindings — the reconciliation, both directions', () => {
150150
});
151151
});
152152

153+
describe('rows the scan cannot see, pinned by hand (the declared helper-call blind spot)', () => {
154+
// The gate's header declares it BLIND to a helper indirection (a
155+
// `makeError(code, …)` call site) and makes widening its patterns a card of
156+
// its own. A registered code stamped ONLY through such a helper therefore
157+
// has no machine check that its stamping package lists it, so each such row
158+
// is pinned here, beside the site it stands for. Read off the ledger module
159+
// inside this package, so the suite still reads nothing outside it.
160+
//
161+
// `@objectstack/service-automation`'s connector sync executor
162+
// (`connector-pull.ts`, `pullConnectorSource`) stamps both codes below onto
163+
// `ConnectorPullError.code` through its local `refuse(code, status, reason,
164+
// message)` helper.
165+
const HAND_PINNED_HELPER_ROWS = [
166+
{
167+
owner: '@objectstack/service-automation',
168+
code: 'MAPPING_NOT_FOUND',
169+
site: "connector-pull.ts refuse('MAPPING_NOT_FOUND', 404, …)",
170+
},
171+
{
172+
owner: '@objectstack/service-automation',
173+
code: 'UNSUPPORTED_TRANSFORM',
174+
site: "connector-pull.ts refuse('UNSUPPORTED_TRANSFORM', 400, …)",
175+
},
176+
] as const;
177+
178+
it('the blind spot is real: a refuse(...) call site is no stamp site to the scan', () => {
179+
// The day this reads a site, the gate sees the form and the hand pins
180+
// below can come out with the widening.
181+
expect(scanSourceText("return refuse('REGISTERED_ONE', 404, 'reason', 'message');", registered)).toEqual([]);
182+
});
183+
184+
it.each(HAND_PINNED_HELPER_ROWS)('$owner lists $code ($site)', ({ owner, code }) => {
185+
const rows: readonly string[] = ERROR_CODE_LEDGER[owner];
186+
expect(rows).toContain(code);
187+
});
188+
});
189+
153190
describe('the shipped script', () => {
154191
it('--self-test passes (the per-pattern red legs, run exactly as CI runs them)', () => {
155192
const require = createRequire(import.meta.url);

‎packages/spec/src/api/error-code-ledger.zod.ts‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,11 +1059,25 @@ export const ERROR_CODE_LEDGER = {
10591059
'AUTOMATION_UNSCOPED_RUN_DATA_ACCESS',
10601060
'EXECUTION_ERROR',
10611061
'INVALID_SIGNAL', // resume signal writes engine-internal variables
1062+
// [#21106] The connector sync executor (`connector-pull.ts`,
1063+
// `pullConnectorSource`) stamps this row and `UNSUPPORTED_TRANSFORM` below
1064+
// onto `ConnectorPullError.code` through its local `refuse(code, status,
1065+
// reason, message)` helper: 404 when no mapping artifact has the requested
1066+
// name, 400 for a `javascript` transform a pull does not execute. The class
1067+
// ships in this package's `dist`. No HTTP door on this tree (nothing invokes
1068+
// the pull yet; the thrown value is the boundary). Both codes are already
1069+
// registered under `@objectstack/rest` (and `UNSUPPORTED_TRANSFORM` under
1070+
// `@objectstack/core`), so these rows are provenance, not identity.
1071+
// `check:error-code-provenance` cannot see a `refuse(...)` call site (a
1072+
// declared blind spot), so `check-error-code-provenance.test.ts` pins both
1073+
// rows by hand.
1074+
'MAPPING_NOT_FOUND',
10621075
'NODE_FAILURE',
10631076
'NO_EXECUTOR',
10641077
'RESUME_IN_PROGRESS', // duplicate resume refused while the first is running
10651078
'RUN_NOT_FOUND', // no suspension for this run id — unresumable for good
10661079
'STORE_UNAVAILABLE', // durable suspended-run store unreadable — existence unknown
1080+
'UNSUPPORTED_TRANSFORM', // [#21106] see `MAPPING_NOT_FOUND` above
10671081
],
10681082
'@objectstack/service-analytics': [
10691083
'CUBE_NOT_FOUND',

0 commit comments

Comments
 (0)