Skip to content

spec(integration): build the connector sync executor that syncConfig and fieldMappings declare (14 keys), once and on the mainstream shape #20281

Description

@objectstack-fleet

Ruled: 5904845660 · letter A — the sync definition lives on the target-side mapping type (source binding + runImport + job cadence); the 14 connector keys retire under ADR-0087; v1 one-way pull; three staged PRs (spec → service → job) · 2026-09-30T05:40Z

Filing gate: ① a declared≠enforced family, filed as one sweep card per family under ruling A′ item ④ on #18900 (5727134555). This is triage's standing request 5857165909 on the seat post. Family connector-sync, seat verdict ENFORCE.

Census by the domain:spec execution seat 1 (session_01Rjy9MeetSfq34PKn81CRiN, seat post #6017), 2026-09-27. Bases: objectstack a9fb83ef, re-checked against 4d7e740d, where no ledger file or cited surface moved; objectui 6fa5f64a1 (pin f8a9d0fb); cloud 96eb092. Ledger instrument: check-liveness.mts --json, whose byStatus equals the committed state-counts.md row for row. ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim. The ranking is by value, user-visible risk × keys. This family's rank is 2 of 16. The sibling family cards filed so far are #20273 and #20274.

Capability: Declarative data sync (replication) from an external system into a local table: strategy (full/incremental/upsert/append), direction, watermark field, conflict policy, batch size, delete mode and a source→target field map

key ledger status ledger row what the ledger cites
connector.syncConfig.strategy dead (verified 2026-09-17) packages/spec/liveness/connector.json:150 note: syncConfig has NO reader outside packages/spec, and this ledger re-measured it rather than inheriting the claim: the only non-spec occurrence of the word in packages/ or examples/ is a prose comment in packages/qa/dogfood/test/expression-conformance.ledge…
connector.syncConfig.direction dead (verified 2026-09-17) packages/spec/liveness/connector.json:155 note: Dead for the one reason recorded on syncConfig.strategy — no connector-attached sync engine exists.
connector.syncConfig.realtimeSync dead (verified 2026-09-17) packages/spec/liveness/connector.json:160 note: Dead for the one reason recorded on syncConfig.strategy. The webhooks subtree below is the mechanism this flag claims to enable, and it is dead on this type too.
connector.syncConfig.timestampField dead (verified 2026-09-17) packages/spec/liveness/connector.json:165 note: Dead for the one reason recorded on syncConfig.strategy — nothing tracks a last-sync watermark for a connector.
connector.syncConfig.conflictResolution dead (verified 2026-09-17) packages/spec/liveness/connector.json:170 note: Dead for the one reason recorded on syncConfig.strategy. Its default (latest_wins) is the kind of value that reads as a configured policy and resolves nothing.
connector.syncConfig.batchSize dead (verified 2026-09-17) packages/spec/liveness/connector.json:175 note: Dead for the one reason recorded on syncConfig.strategy — no bulk path reads it.
connector.syncConfig.deleteMode dead (verified 2026-09-17) packages/spec/liveness/connector.json:180 note: Dead for the one reason recorded on syncConfig.strategy. soft_delete by default, applied by nothing.
connector.syncConfig.filters dead (verified 2026-09-17) packages/spec/liveness/connector.json:185 note: Dead for the one reason recorded on syncConfig.strategy. Declared z.record(z.string(), z.unknown()), so it is not a container for this walk and no subtree rides on this verdict.
connector.fieldMappings.source dead (verified 2026-09-17) packages/spec/liveness/connector.json:194 note: Re-verified, not inherited: the ADR-0087 conversion registry's field-mapping-transform-removed entry (that is the id in full — no connector- prefix; the connector--prefixed neighbour is connector-rate-limit-config-removed, a different retirement) me…
connector.fieldMappings.target dead (verified 2026-09-17) packages/spec/liveness/connector.json:199 note: Dead for the one reason recorded on fieldMappings.source.
connector.fieldMappings.defaultValue dead (verified 2026-09-17) packages/spec/liveness/connector.json:209 note: Dead for the one reason recorded on fieldMappings.source.
connector.fieldMappings.dataType dead (verified 2026-09-17) packages/spec/liveness/connector.json:214 note: Dead for the one reason recorded on fieldMappings.source. SYNC_ARCHITECTURE.md's corrected note says an L3 mapping 'declares dataType and syncMode and performs no value transformation' — true about the transform half, and this row records that the dec…
connector.fieldMappings.required dead (verified 2026-09-17) packages/spec/liveness/connector.json:219 note: Dead for the one reason recorded on fieldMappings.source. A .default(false) that no validation consults.
connector.fieldMappings.syncMode dead (verified 2026-09-17) packages/spec/liveness/connector.json:224 note: Dead for the one reason recorded on fieldMappings.source. read_only / write_only / bidirectional gate nothing — there is no sync to gate.

Mainstream evidence:

  • ServiceNow Import Sets + Transform Maps: field maps from source to target, coalesce as the upsert key, and scheduled data imports.
  • Microsoft Power Platform dataflows (Power Query) loading Dataverse tables, with scheduled refresh and incremental refresh on a date column (the watermark).
  • Airtable Sync (synced tables from another base or from external sources). Two-way sync for some sources is UNVERIFIED.
  • Salesforce Data Cloud data streams, with refresh mode full refresh or upsert. Salesforce Connect is the virtualization alternative (no copy).
  • Odoo: no declarative external sync in core. The OCA connector framework is a community add-on, UNVERIFIED as mainstream. Retool / Appsmith: none (they query live).
  • Shape note: every one of these binds the sync definition to the TARGET table or dataflow, never to the connection. "Build it once, correctly" (一次做对) therefore includes deciding whether the shape stays on the connector.

Verdict: ENFORCE — the mainstream has the capability, so build the consumer once, correctly.

Reader that must exist / disposition: NEW in objectstack: a sync executor, in packages/services/service-automation or a new service, that reads syncConfig + fieldMappings from a materialized declarative connector, schedules it and writes through ObjectQL. Today nothing outside packages/spec reads either key (0 code hits; see cross-check C1).

User-visible risk (2): The docs advertise the capability (SYNC_ARCHITECTURE.md L3, content/docs/references/integration/connector.mdx), and the defaults (conflictResolution: latest_wins, deleteMode: soft_delete) read like configured policy while nothing applies them.

Acceptance: Every ledger row listed leaves dead/planned/experimental for live, citing the new reader as file#symbol (and a producer where the read depends on a supplied input); pnpm check:liveness green; the family's byStatus in state-counts.md regenerated.

Lane: domain:spec parent (contract) + domain:services sub-issue (executor), both in objectstack

File surface: packages/spec/src/integration/connector.zod.ts:946,952 (DataSyncConfigSchema, ConnectorFieldMappingSchema) · packages/services/service-automation/src/engine.ts#registerConnector · packages/spec/docs/SYNC_ARCHITECTURE.md · packages/spec/liveness/connector.json

Dedupe: syncConfig \| fieldMappings \| ConnectorFieldMapping \| DataSyncConfig \| conflictResolution \| timestampField → 0 open hits.

四轴:

Blocked-by: #20919

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions