Repository navigation
connector sync, stage ② of #20281: an executor in service-automation pulls a mapping whose connectorSource names a rest/openapi connector and writes through the import runner's upsert-by-match-key #20919
Description
Activity
objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsTriage: first grade —
enhancement·priority:p3·domain:services·area:workflow·pm:queue. Stage ② of ruling A on #20281: the connector sync executorTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-30T19:54Z. ⛔ Not a claim, ⛔ not a dispatch.Routing. The executor lands in
packages/services/service-automation, which holds the connector registry, so it isdomain:servicesby the lane table.- spec(integration): build the connector sync executor that
syncConfigandfieldMappingsdeclare (14 keys), once and on the mainstream shape #20281 ispm:blocked, not in flight, so this card does not inherit itsdomain:speclabel. - If the spec(integration): build the connector sync executor that
syncConfigandfieldMappingsdeclare (14 keys), once and on the mainstream shape #20281 holder (domain:specseat 5) claims it, that is the cross-lane path: the claim declares its file surface. - p3 is inherited from spec(integration): build the connector sync executor that
syncConfigandfieldMappingsdeclare (14 keys), once and on the mainstream shape #20281.
Stage ① has landed as PR #20903 →
0efbdc3421.connectorSourceis onmain(packages/spec/src/data/mapping-connector-source.test.ts).Blocked-by: noneholds.The dependency direction, measured now as an input to the claim, not a ruling. The ruling still has the claiming seat state its answer on #20281 before it builds.
- No cycle.
@objectstack/restdepends oncore,metadata-core,observability,platform-objects,service-package,specandtypes, and none of these reachesservice-automation.service-automationdoes not depend onresttoday. - But it is a layer climb.
restis the HTTP surface (domain:cli). Itspackage.jsondependents today arecli,client,runtime,verify,plugin-dev, and the route-hosting plugins (plugin-auth,organizations). Nopackages/services/*package depends on it. - The runner's core is already transport-free.
packages/rest/src/import-runner.tsimports only@objectstack/core(bulkWrite,withTransientRetry),@objectstack/types,@objectstack/spec, and three local modules (import-coerce,export-format,error-response). - So the ruling's "moves to a shared package" branch is the likely one. The runner's core would move beside
bulkWritein@objectstack/core, or into a new package, withrestre-exporting it. ⛔ Never a copy.
Serial. #20701 (
domain:cli,pm:queue) editstoFailedResultin the sameimport-runner.ts(its item 4 is free now). If this claim moves the runner's core, it serializes with any in-flight #20701 claim. The dispatcher's in-flight check reads both claims' file surfaces.Stop valve, as ruled: a credential, scheduling or pagination convention the spec does not declare stops the claim and returns a report. ⛔ Nothing is invented.
Generated by Claude Code
- spec(integration): build the connector sync executor that
- addedarea:workflowApprovals and automation — the work that runs without a person driving itApprovals and automation — the work that runs without a person driving itenhancementNew feature or requestNew feature or request
on Sep 30, 2026 objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsSerial wait ·
domain:servicesseat (#6021) ·session_01XY5uCwTjZj7884yYtyur4H· 2026-09-30T19:58ZThis card is in this lane's queue (
priority:p3, triage5918585474). It is not claimed yet, because its expected surface intersects two in-flight claims. Both were read in this act againstorigin/mainat4d0b9cd5.- rest(import): a column for a formula field passes the dry run, then fails the row at commit with the driver's SQL error, where the create door answers 400 INVALID_FIELD #20701 (
domain:cli, claim5914220480) holdspackages/rest/src/import-runner.ts, where it editstoFailedResult.- Triage's measurement makes the ruling's "the runner's core moves to a shared package" branch the likely one. That move takes this same file.
- Triage's own serial note: "If this claim moves the runner's core, it serializes with any in-flight rest(import): a column for a formula field passes the dry run, then fails the row at commit with the driver's SQL error, where the create door answers 400 INVALID_FIELD #20701 claim."
- automation: at
kernel:readythe flow sync re-arms a stored row's body over the loader's for a packaged flow name, after the boot pull armed the loader's, so the stored body runs while the receipt says the package's is armed #20913 (domain:cli, p1security, claim5917761349) holdspackages/services/service-automation/src/plugin.ts. That is where this lane's connector registry is reconciled at boot, and where an executor would be wired. - Fold or serial: serial. A connector sync executor, a failed-row classifier and a boot-sync precedence fix are three different shapes.
Next: this card is claimed when #20701's PR has landed. If its measured wiring needs
plugin.ts, it also waits for #20913's PR. At the claim, and before anything is built, this seat states the dependency direction on #20281, as the ruling requires. It waits from this comment's stamp.
Generated by Claude Code
- rest(import): a column for a formula field passes the dry run, then fails the row at commit with the driver's SQL error, where the create door answers 400 INVALID_FIELD #20701 (
objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsSerial wait, updated ·
domain:servicesseat (#6021) ·session_01XY5uCwTjZj7884yYtyur4H· 2026-09-30T21:58ZThis updates
5918641534with a new reading.- rest(import): a column for a formula field passes the dry run, then fails the row at commit with the driver's SQL error, where the create door answers 400 INVALID_FIELD #20701 (
domain:cli,priority:p2) is no longer in flight. Its item 4 landed as PR fix(rest): an import row for a missing database column answers what the create door answers #20941, and triage's answer5920419144put it back inpm:queuewith a REST item 5. That item is free now and sits in the same function,toFailedResultinpackages/rest/src/import-runner.ts. - This card's likely design moves the runner's core out of that file. So the two would take the same file at the same time.
- Order: rest(import): a column for a formula field passes the dry run, then fails the row at commit with the driver's SQL error, where the create door answers 400 INVALID_FIELD #20701 ranks above this card (p2 against p3), and its next item is small and ready. This card therefore yields: it is claimed once rest(import): a column for a formula field passes the dry run, then fails the row at commit with the driver's SQL error, where the create door answers 400 INVALID_FIELD #20701's REST item 5 has landed, or once the
domain:cliseat declines it. - It still waits for automation: at
kernel:readythe flow sync re-arms a stored row's body over the loader's for a packaged flow name, after the boot pull armed the loader's, so the stored body runs while the receipt says the package's is armed #20913 (plugin.ts) if its measured wiring needs that file. - At the claim, the dependency direction is stated on spec(integration): build the connector sync executor that
syncConfigandfieldMappingsdeclare (14 keys), once and on the mainstream shape #20281 before anything is built, as the ruling requires.
It keeps waiting from
5918641534's stamp.
Generated by Claude Code
- rest(import): a column for a formula field passes the dry run, then fails the row at commit with the driver's SQL error, where the create door answers 400 INVALID_FIELD #20701 (
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 · 2026-10-01T00:01Z
Session:session_01XY5uCwTjZj7884yYtyur4H
Account:os-justin(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-20919-connector-sync-executor
Worktree:objectstack-issue-20919
Domain:domain:services
Seat:domain:services(seat 1, seat post #6021)
Two phases, as ruling A on #20281 requires ("the dependency direction is measured BEFORE claiming … the claiming seat states which on #20281 before it builds"):- Phase 0 (this dispatch): measure only, then stop and report. It changes no file. Its readings:
- the dependency direction, and where the runner's core can live without a copy;
- how
service-automationresolves aconnectorname to a rest or openapi instance and runs its read action; - whether the target APIs need pagination (the stop valve);
- where a watermark can be stored;
- the measured size and file list.
- This seat then states the direction on spec(integration): build the connector sync executor that
syncConfigandfieldMappingsdeclare (14 keys), once and on the mainstream shape #20281 before any build, and only then sends the build phase.
File surface (the build phase's, declared now because it is cross-lane; the phase-0 readings narrow it): packages/services/service-automation/src/**: the executor (new files), plus its wiring only where it must go.- The runner's core (
domain:clisource,domain:engineor new-package destination):packages/rest/src/import-runner.ts(moved out, withrestre-exporting it), and its destination besidebulkWriteinpackages/core/src/**, or a new package, as phase 0 measures. ⛔ Never a copy. packages/spec/liveness/mapping.json(theconnectorSourcerows,planned→live) andpackages/spec/docs/SYNC_ARCHITECTURE.md(domain:spec): the text the card lists as becoming true.packages/lint/src/authoring-rules.tsandruntime-gate.inert-type-writes.test.tsgroup C (comments only,domain:specby the anchoring rule).- Pins beside each, and
.changeset/20919-*.md. - ⛔ Not
packages/spec/src/**: stage ① declared the contract. If a credential, scheduling or pagination convention the spec does not declare is needed, the stop valve trips (triage5859569880, note 3). ⛔ Nothing is invented.
(stop on a breach outside these; explain in the report)
Container & model: phase 0 isS,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate, floor sonnet · default opus · ceiling fable). The build phase's container is set from phase 0's measured size; anLgoesmode:cloud. The contract review runs atCONTRACT_REVIEW_TIER.
Clause-②: yes - Why
yes: it is taken conservatively. An executor that reads a declared key that nothing read before is a widening of what runs. The dev measures it at build, and the PR's line 2 carries the measured grammar.
Thread-read: 5920448087
Serial constraints cleared: - rest(import): a column for a formula field passes the dry run, then fails the row at commit with the driver's SQL error, where the create door answers 400 INVALID_FIELD #20701 (
domain:cli) is no longer in flight. Its REST item 5 landed as PR fix(rest): an import row for a NOT NULL refusal or a unique conflict answers what the create door answers #20956 (d7b98176), and the card ispm:blockedon engine: validateData forwards engine.validate's onFieldsDropped and answers the drops, and insertMany reports drops per outcome through insertManyData: #20701 item 1, engine half #20922. The yield in5920448087is met. - automation: at
kernel:readythe flow sync re-arms a stored row's body over the loader's for a packaged flow name, after the boot pull armed the loader's, so the stored body runs while the receipt says the package's is armed #20913 landed (PR fix(service-automation, metadata-protocol): a shipped flow name arms the loader's body at both boot steps, and a stored row of that name is reported as shadowed (#20913) #20942), soplugin.tsis free. - Open PRs: none touches
import-runner.ts,service-automation/src,core/src, the mapping ledger,SYNC_ARCHITECTURE.mdor the lint files. PR fix(rest): the import template answers to the import door's gates, not the export's (#20896) #20977 touches only a rest import TEST file. - Claims: of the 26
pm:dispatchedclaims, none names these files. [finding] service-analytics still spells a year below 1000 its own way:formatDateBucketlabels bucket key0050as 1970 and0050-06as50-06, and the draft-previewbucketDatewrites unpadded keys the published path no longer writes #20867's claim excludescore. - spec(integration): build the connector sync executor that
syncConfigandfieldMappingsdeclare (14 keys), once and on the mainstream shape #20281 (domain:specseat 5,pm:blocked) holds no file. Its ruling's stage ② is this card.
Generated by Claude Code
- Phase 0 (this dispatch): measure only, then stop and report. It changes no file. Its readings:
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report (phase 0)
{ "issue": 20919, "phase": "0 (measure only; no file changed, no PR)", "status": "blocked", "branch": "claude/issue-20919-connector-sync-executor", "pr": null, "session": "session_01XY5uCwTjZj7884yYtyur4H — the dispatching seat session (subagent: the parent session id)", "premise_still_valid": true, "summary": "Phase 0 measured at origin/main 5f6b63a6fd (not a shallow clone). The executor is buildable without a spec convention the ruling forbids: credentials stay on the instance, cadence and acting organization are stage 3, and no measured target needs paging. Four readings correct the card and triage. (1) The runner is NOT transport-free. import-runner.ts:405 calls mapDataError(err, objectName).body, the REST door classifier in error-response.ts. That module is the HTTP-answer module (header :4) and also holds res-writing emitters (:1988, :2717, :2751). Its classification half (:65-1946) is pure, with 0 code references to the emission half or logError. (2) The executor needs 5 shared pieces, about 4,030 lines, not runImport alone. They are runImport, coerceRow, applyMappingToRows + refuseUnknownMappingTargets (import-mapping.ts:249, :148), buildFieldMetaMap (export-format.ts:148) and the classifier. prepareImportRequest (import-prepare.ts:269) cannot be reused: it is request-body shaped, and its resolveNamedMapping applies the manual door sourceFormat gate (import-mapping.ts:97-104), which a pulled JSON record fails against the csv default (mapping.zod.ts:336). (3) service-automation to rest is a layer INVERSION, not only a climb. No cycle exists under any edge kind, but rest already depends on a service (service-package: package-routes.ts:4, direct-mount-composition.ts:51), and no packages/services/* package depends on rest. So the ruling's move branch applies. (4) The build must edit packages/spec/src, which the claim excludes. Eight spec files carry text or pins saying the pull is not executed yet. The D3 claim validated at authoring is false today: 0 connectorSource hits in packages/lint, cli/src or metadata*/src. A runtime executor does not make it true. Recommendation: runner pieces go beside bulkWrite in @objectstack/core, and the classifier goes beside its own primitives in @objectstack/types. This adds 0 new dependency edges, and rest re-exports. Build size L (mode:cloud), and the changed lines likely pass the 5,000 line of AGENTS 7(c). The worktree is left clean at 5f6b63a6fd (no node_modules) for the seat to remove or reuse.", "measurements": { "M1_dependency_direction": { "reading": "Triage confirmed on no-cycle and no current edge. Corrected on transport-free and on the module list.", "graph": "Read from package.json across the workspace. rest prod closure (7 packages): core, metadata-core, observability, platform-objects, service-package, spec, types. rest all-kinds closure (26, dev included) does not reach service-automation. The service-automation all-kinds closure does not reach rest. service-automation has 0 source references to @objectstack/rest. rest prod dependents: cli, plugin-auth, plugin-dev, runtime, verify. Dev dependents: client, organizations. No packages/services/* package depends on rest under any edge kind. rest imports service-package (package-routes.ts:4, direct-mount-composition.ts:51), so the edge would point from a service up into the HTTP layer that already sits on a service: an inversion.", "runner_imports": [ "import-runner.ts (1,002 lines): node:crypto randomUUID :3; ./import-coerce coerceRow :4; ./export-format type ExportFieldMeta :5; @objectstack/spec system/api types :6-7; @objectstack/core bulkWrite, withTransientRetry, defaultIsTransientError :8; @objectstack/types isUniqueViolationError, uniqueViolationColumn :9; ./error-response isEngineDuplicateRecordEnvelope, mapDataError :10. Its imports carry no transport, but its row report adopts the HTTP door verdict by design (toFailedResult :401-414, docblock :356-399).", "import-coerce.ts (815): core temporal helpers, spec/data values, spec/api type, spec/system renderValidationMessage, ./export-format type (:34-59). Transport-free.", "export-format.ts (493): imports only core temporalStorageForm (:33). The module also holds exportContentDisposition (:96, a Content-Disposition value) and the export renderers. The runner uses only the type. The executor needs ExportFieldMeta (:35) + buildFieldMetaMap (:148), about 70 lines.", "error-response.ts (2,756): imports @objectstack/types (:49-61), spec types, and ./log.js (:64, a fourth local module, used only at :2105 in the emission half). It is NOT transport-free. It is the REST boundary module: classification (mapDataError :680 to classifyDataError :1276-1946) plus emitters that write res." ], "runner_importers": "Production: rest-server.ts:348 (calls :9122 sync route and :9286 async import-job worker); plugin-auth admin-import-users.ts:107-108 (call :621, through the @objectstack/rest index, which re-exports runImport :35/:45, coerceRow :46, buildFieldMetaMap :48). Tests: 8 import-runner-*.test.ts in rest, plus rest-server-canonical-query-ast.test.ts and rest-unique-violation-dialects.test.ts. plugin-auth's only runtime use of rest is this runner. Local-module importers: import-coerce from import-mapping, import-runner, index. export-format from import-coerce, import-prepare, import-runner, import-template, index, rest-server. error-response from import-runner, rest-server.", "destinations": { "A_core_plus_types": "runner, coercion, mapping-apply and field-meta go to packages/core/src beside utils/bulk-write.ts. That file's header (:3-7) already names the import runner as its consumer, and import-coerce already imports 5 core temporal helpers. The classifier half of error-response.ts goes to packages/types/src beside the primitives it composes (thrown-http-error.ts, unique-violation.ts, error-leak.ts, all imported at error-response.ts:49-61). rest re-exports every moved name, and its emitters stay. New package.json edges: 0 (core: spec, types; rest: core, types; service-automation: core, types all exist). Owners: runner domain:cli to domain:engine (core); classifier stays domain:cli (types is cli lane). Browser check: client/client-react import only @objectstack/core/logger (client/src/index.ts:196), and core's main entry already carries node:crypto (index.ts:44, :74, :140).", "A_variant_classifier_in_core": "Same, with the classifier in core too: one destination. core then holds an HTTP status table.", "B_new_package": "A new published package holding all 5 pieces. Cost beyond A: about 8 scaffolding files (package.json, tsconfig.json, tsup.config.ts, vitest.config.ts, vitest.repo-tests.json, README.md, LICENSE, src/index.ts), a .changeset/config.json fixed-group row, the lockfile, 2 new edges (rest and service-automation to it) and 3 outgoing edges, and a lane-table row (.claude/skills/pm-dispatch/references/lanes, governed Tier S).", "C_no_move_excluded_by_ruling": "service-automation/package.json +1 edge to rest, and rest index +3 exports (applyMappingToRows, refuseUnknownMappingTargets, MappingArtifactLike). Cheapest (M), but it is the inversion measured above, so it needs the maintainer to overturn the ruling parameter.", "never_a_copy": "A rejected sub-option: move the runner but inject the classifier per caller. The executor cannot import rest's classifier, so its rows would speak a different code vocabulary than the import door, which is the split #20701 closed. Not offered." }, "path_keyed_surfaces_a_move_must_repoint": "check-error-code-casing.mjs:70-71 (EXEMPT_FILES; a moved file loses the exemption and the gate goes red); packages/runtime/src/dispatcher-error-vocabulary.ts:1010-1089 (7 entries); check-dispatcher-error-vocabulary.mjs:1517 and :3734 REST_DOOR_FILE; check-route-envelope.mjs:385; check-spec-docblock-symbol-anchors.mjs:595-599; liveness/mapping.json file#symbol evidence. Each edited gate script also owes its own test suite. ADR-0114:6 names rest import-coerce.ts/import-runner.ts as consumers: that is docs/adr, Tier H, so leave it and record it." }, "M2_connector_resolution": { "registry": "AutomationEngine holds a Map from connector name to RegisteredConnector { def, handlers, origin, state, degradedReason } (engine.ts:2472, :605-615), exposed as service automation (plugin.ts:647). Lookups: resolveConnectorAction(name, action) :3988, getConnectorDegradedReason :4087, getConnectorOrigin, getConnectorProvider :4115.", "declarative_path": "reconcileDeclaredConnectors (plugin.ts:1491) reads the declared connectors[] items (:1377). For each enabled provider-bound entry it takes the factory (:1596), resolves auth.credentialRef through the credential resolver (:1610, impl :1916-1955; the open tier reads env, :234), builds ConnectorProviderContext (:1647), and registers the factory def under the declared name with origin declarative (:1718-1719).", "credentials": "ADR-0097 static auth (none / api-key / basic / bearer) is resolved at materialization and closed over by the handler (rest-provider.ts:52-66). The executor touches no credential. The stop valve is not tripped.", "call_shape": "handler(input, ctx: ConnectorActionContext { variables, automation, logger }) (engine.ts:575-591). rest exposes one action, request, with input { method, path, headers, query, body } (rest-connector.ts:55-61, :137-161). openapi exposes one action per operation, with input { path, query, header, body } (openapi-connector.ts:219-228). Both return { status, ok, body }, do not throw on non-2xx, and return text when the content type is not JSON (rest-connector.ts:179-192; openapi :192-200).", "undeclared_pieces_runtime_internal_not_valve": [ "The provider is not on the registered def: the rest def (rest-connector.ts:123-162) and the openapi def (:231-246) carry no provider key, and the plugin keeps only a signature per instance (plugin.ts:556). The executor must take the provider from the declared item (plugin.ts:333-357) or record it at :1718.", "ctx assumes a flow run. A pull outside a flow builds one; both HTTP providers ignore it.", "ok:false and a non-array at recordsPath must be refused by the executor. The spec declares neither, but the action outputSchema declares status/ok/body (rest-connector.ts:152-159).", "watermark.param maps to input.query[param] for both providers, which is consistent with the spec's Query parameter wording (mapping.zod.ts:412-414). Nothing is invented.", "A plugin-origin connector is not a connectors[] entry, so the spec describe (mapping.zod.ts:384-386) makes it refusable.", "The executor reads the mapping through protocol getMetaItem({ type: mapping, name }) as the door does (import-mapping.ts:63-80). The protocol service is reachable from service-automation (plugin.ts:468, :2005), and runImport's p is that protocol (rest-server.ts:9085, :9122). Undeclared runImport knobs take the door defaults (import-prepare.ts:296-307: runAutomations true, trimWhitespace true, the rest false). mode update/upsert with an empty upsertKey must be refused as the door does (:347)." ] }, "M3_pagination_and_watermark": { "paging": "Stage 1 declares no paging. connectorSource is connector, action, input, recordsPath and watermark { field, param } (mapping.zod.ts:372-421), and cursor is only an alias of watermark (:380). Seat ruling 5916259513 on question 2 is A: no paging in v1, and the valve trips if target APIs need it. Measured targets: 0. The only in-repo rest/openapi instances read /api/v1/health, a single object (examples/app-showcase/src/system/connectors/index.ts:46-66, :79-100). The docs examples billing_api / crm_api (content/docs/automation/connectors.mdx:52, :139) have no operation in repo. connectorSource authors outside packages/spec: 0. Verdict: NOT tripped on measurement, but the population is empty, so see open question 2 for the residual.", "watermark_storage_options": "Nothing the spec declares has a watermark slot. sys_import_job (audit/sys-import-job.object.ts:23-134) has no watermark or mapping column. sys_job and sys_job_run have no state column. sys_setting (system/sys-setting.object.ts:6-36) is a manifest-declared, audited config K/V. sys_automation_run and sys_flow_dispatch are purpose-specific. The options are in open question 3." }, "M4_scheduling_and_organization": "Confirmed as stage 3. No cadence key (mapping.zod.ts:159-161, :360). Ruling 5904845660 stage 3 is the job. Seat ruling 5916259513 question 3: the acting organization is stage 3's. JobSchema (job.zod.ts:181-201) has no organization key and no key that names a mapping (handler is a function name, :193). Consequence for stage 2: nothing calls the executor until stage 3, so its entry point takes its context from the caller. Precedent for stage 3: schedule flows already lift a declared organization (engine.ts:527-544).", "M5_size_and_files": { "size": "L, so the build goes mode:cloud. About 4,030 lines relocate (1,002 + 815 + about 260 + about 70 + about 1,880). About 750-1,100 lines are new (executor + tests), plus about 150 lines of edits. Whole-file renames count near 0 on GitHub. The three file splits count about 4,420, so with new code the total is about 5,300-5,700 changed lines, past the 5,000 limit of AGENTS 7(c). The seat may split the stage into a behaviour-identical relocation PR and the executor PR. The ruling says one PR per stage, so that split is the seat's call.", "files_under_A": [ "packages/core (domain:engine): src/import/import-runner.ts (1,002, moved), src/import/import-coerce.ts (815, moved), src/import/import-mapping.ts (about 260; resolveNamedMapping stays in rest), src/import/field-meta.ts (about 70), src/index.ts (+5). Tests move with the code (8 import-runner-*.test.ts, plus coerce and mapping tests) so they keep resolving src.", "packages/types (domain:cli): src/data-error-classification.ts (about 1,880, from error-response.ts :65-1946), src/index.ts (+5).", "packages/rest (domain:cli): error-response.ts (-1,880; emitters kept, about 880 remain), export-format.ts (-70), import-mapping.ts (-260), import-runner.ts and import-coerce.ts leave; index.ts, rest-server.ts, import-prepare.ts, import-template.ts change import or re-export lines (about 20).", "gates and registries: the 6 path-keyed surfaces listed under M1, each with its own test suite.", "packages/services/service-automation (domain:services): src/connector-pull.ts (new, est. 300-450), src/plugin.ts (record the provider, expose the pull; est. 30-60), src/index.ts (+10), src/connector-pull.test.ts (new, est. 400-600); package.json unchanged under A.", "packages/spec (domain:spec): liveness/mapping.json (8 connectorSource rows, plus re-anchoring the fieldMapping row's applyMappingToRows evidence and the file _note, which name rest import-mapping.ts); src/data/mapping.zod.ts :366-368 and :418-420; src/data/mapping-connector-source.test.ts :176-201; src/integration/connector.zod.ts:232 and connector-sync-retirement.test.ts:120-126; src/migrations/entries/semantic/18.connector-sync-keys-retired.ts :27 and :50, plus the regenerated migrations/registry.ts region (:8078); docs/SYNC_ARCHITECTURE.md :98, :211, :350, :372, :402; generated content/docs/references (mapping.mdx, connector.mdx).", "packages/lint (domain:devx): authoring-rules.ts:1520 comment, runtime-gate.inert-type-writes.test.ts group C title.", ".changeset/20919-*.md." ], "under_B": "Everything in A, plus the new-package scaffolding and lane row listed in M1 B: L+.", "under_C": "About 1,300 lines, size M: no relocation, +1 edge, +3 rest exports, and the same executor, spec and lint edits. Excluded by the ruling." }, "M6_holders": "Read at measurement time; 14 open PRs, every file list fully paged. #20977 (draft) touches packages/rest/src/rest-server.ts (+92/-9; hunks at 644, 2513, 9630, 10042, none in the import block :340-400 or the import routes :9085-9300) and import-template-route.test.ts. Same file, no overlapping hunk: serialize only if the build edits those regions. #20639 (Version Packages, standing-open by design) touches the package.json and CHANGELOG.md of core, rest, service-automation, types, spec and lint. It is not a holder. The other 12 PRs touch no build file. pm:dispatched claims: NOT MEASURED by this dev, because the definition bars a dev from scanning open issues (rule 3) and allows single-card reads only. The claim 5921916391 recorded 26 claims, none naming its declared surface, but that surface did not yet include packages/types/src, the gate scripts or packages/spec/src. The seat should re-read for those." }, "tests": "No build, no test run and no probe file. Static reads only, so the verify lock was never taken and there was nothing to restore. Instruments: a package.json graph walk (node script in scratch, output m1-graph.txt); git grep and sed over the worktree at 5f6b63a6fd (git rev-parse --is-shallow-repository: false); REST single-card reads of #20919 (issue and 4 comments) and #20281 (7 comments); the open-PR list and per-PR file lists (#20639 paged to 326 files). Control for a zero reading: connectorSource in packages/lint found 0 hits while mapping in packages/lint/src/authoring-rules.ts found 3. End state: git status --porcelain in /home/user/objectstack-issue-20919 printed 0 lines, and no node_modules exists. Evidence with file:line is in scratch issue-20919/evidence.txt.", "mcp_calls": "0", "api_writes": "1 — this comment, POST /repos/objectstack-ai/objectstack/issues/20919/comments through scripts/pm/post-stamped.mjs. No git push, no PR and no label write.", "conflicts_with_definition": [ "The definition (rule 1) pushes the empty branch before any edit, as a landing marker and write probe. The order sets the phase-0 write budget to this one comment. Phase 0 made no edit, so no push was made, and the write route stays unprobed for the build.", "M6 asks for a pm:dispatched claim check. The definition (rule 3) bars a dev from scanning open issues and allows single-card reads. The definition was followed: claims NOT MEASURED, with the seat reading in M6 cited." ], "open_questions": [ { "question": "Q1 (M1): where does the runner core go, now that it includes the door classifier the runner calls (import-runner.ts:405)?", "options": [ "A. Runner, coercion, mapping-apply and field-meta go to @objectstack/core beside bulkWrite. The classifier half of error-response.ts goes to @objectstack/types beside its primitives. rest re-exports. 0 new edges. Owner change: domain:cli to domain:engine for the runner only.", "A2. As A, with the classifier in core too: one destination, and core holds an HTTP status table.", "B. A new published package holding all five pieces: about 8 scaffolding files, a fixed-group row, 2 new inbound edges and a governed lane-table row.", "C. No move: service-automation depends on @objectstack/rest. Size M, but it is the measured inversion, so it needs the ruling overturned." ], "four_axes": { "business_need": "connectorSource authors: 0. In-repo rest/openapi list endpoints: 0. Priority p3. Whichever option is chosen, the relocation is paid for a capability with zero measured pull. The 3 existing production callers (rest-server.ts:9122, :9286; admin-import-users.ts:621) are unchanged under A and B through re-export. C is cheapest for zero pull.", "long_term": "A puts one implementation in the lowest packages that already hold its batching (bulk-write.ts:3-7) and its error primitives (types). B draws a clean boundary, but builds a package for one executor, overlapping core's write utilities. C makes a kernel-managed service depend on the HTTP layer, and that layer already depends on a service: the cycle risk grows with every route rest takes on.", "ai_mistakes": "A, B and C all keep one runner and one classifier, so a sync row and an import-door row speak one code vocabulary. A and B must repoint 6 path-keyed surfaces. The measured ones fail loudly when missed (EXEMPT_FILES; file#symbol anchors). B adds a package every package-enumerating gate must learn.", "startup_scope": "A: no new package, edge or lane. B: a package, edges and a lane row. C: one edge." }, "recommendation": "A. It is the only option that adds no package and no edge while keeping one implementation (long-term and startup axes). It keeps the row vocabulary single (AI-mistake axis). The business axis is neutral between A and B and favours C, which the ruling excludes. Confidence gaps: the engine seat must accept the runner in core; GitHub rename detection decides whether the error-response split counts about 1,760 or about 3,760 changed lines." }, { "question": "Q2 (M3, stop valve): no measured target needs paging, so the valve does not trip. Should v1 ship without paging, given that a one-call pull cannot tell a first page from the whole set?", "options": [ "A. Ship v1 without paging, per seat ruling 5916259513 question 2. The executor makes one action call per pull. The one-response contract is stated where authors read it: the watermark describe at mapping.zod.ts:418 (omitted means every pull reads the full set), the ledger authorHint and SYNC_ARCHITECTURE.md. Full pulls of a paged endpoint, and incremental pulls of a newest-first paged endpoint, are then documented limits rather than silent ones.", "B. Trip the valve now and send a paging declaration (for example a next-page path plus a request parameter) to the spec seat before the build. This is speculative: 0 targets are measured.", "C. The executor detects provider-specific has-more or next signals. This invents a convention the spec does not declare, so it is refused." ], "four_axes": { "business_need": "Measured targets: 0. No author exists to need paging today.", "long_term": "A keeps the minimal declared shape, and a later paging key is additive. B fixes a shape against no measured API.", "ai_mistakes": "The danger under A is silent truncation: page 1 reads as success. An incremental pull over a newest-first paged API advances the watermark past unread pages, which is silent loss. So A is acceptable only if the one-response limit is written where the author reads it, in spec text (domain:spec). C is the consumer-side leniency the framework rejects.", "startup_scope": "A adds nothing. B adds spec surface with no pull." }, "recommendation": "A, conditional on the one-response limit being stated in the mapping.zod.ts:418 describe and in SYNC_ARCHITECTURE.md in the same build. If the seat holds that a list endpoint is presumptively paged, that is B and a valve trip, and the seat should rule it. This dev did not invent a target." }, { "question": "Q3 (M3): where is the watermark kept? No declared storage has a slot for it, and the card leaves the choice to this stage.", "options": [ "W1. Derive it from the target. Read the maximum of the target field that fieldMapping maps watermark.field onto, with one find under the run context. No storage is needed. Precondition: watermark.field must be a fieldMapping source, and an unmapped one is refused loudly at pull time. Ideally it is also declared as a MappingSchema refinement (spec seat).", "W2. Process memory. It is lost on restart, and per replica. Under mode insert, the schema default (mapping.zod.ts:343), every re-pull duplicates records.", "W3. sys_setting through a SettingsManifest. A config store is misused for run state, every write is audited, and the keys must be declared per manifest.", "W4. A new column on sys_import_job (one row per pull). Durable history, but it adds platform-object surface (domain:engine).", "W5. Write it back into the mapping metadata row. Refused: it mutates an authored, possibly packaged, artifact." ], "four_axes": { "business_need": "0 authors. Choose the cheapest durable option.", "long_term": "W1 and W2 add no storage. W1 is durable. W3 and W4 add surface. W5 corrupts metadata.", "ai_mistakes": "W1 can be refused loudly when its precondition is missing. Its hazard: another writer to that target field advances the watermark, and the pull then skips. W2 fails silently: re-pulls, and duplicates under insert.", "startup_scope": "W1 and W2 add nothing. W3 and W4 add." }, "recommendation": "W1, with the unmapped-field refusal, and the spec describe for watermark.field (mapping.zod.ts:408-411) updated to say where the value is read from. Fallback W4 if the seat rejects the target-derived semantics." }, { "question": "Q4 (M5): the build must edit packages/spec/src, which the claim excludes. When do the ledger rows go live, given that nothing calls the executor before stage 3?", "options": [ "A. Extend the build surface (cross-lane, domain:spec) to the 8 spec files listed in M5. The rows go live, citing the executor. authorWarn stays, with a reworded hint (pulled when a job drives it; nothing schedules it until stage 3). D3 :50 drops validated at authoring, or the seat orders an authoring check (lint, domain:devx) to make it true.", "B. Leave the ledger planned and the texts as they are until stage 3 flips everything. The executor then lands as a reader the ledger says does not exist.", "C. As A, but drop authorWarn now." ], "four_axes": { "business_need": "0 authors, so the ordering harms no one today.", "long_term": "A keeps the texts true at every landing.", "ai_mistakes": "C tells an author a pull runs while no caller exists. B leaves the ledger contradicting the code. A is the only option where every text is true. The stage-1 changeset is still unreleased (.changeset/20281-connector-sync-moved-to-mapping.md; spec tagged 17.5.0), so landing A before the next release ships no false prescription.", "startup_scope": "Neutral." }, "recommendation": "A." } ], "out_of_scope_findings": [ "carrier: the stage 2 build · applyMappingToRows reads a source as one flat key (row[first(entry.source)], import-mapping.ts:249 onward). A pulled JSON record's nested field (for example customer.name) is therefore not addressable as a source, and the target stays unset with no error. Relevant to the executor design, and not measured on the manual JSON door. Noted, not filed.", "carrier: the stage 2 build · ADR-0114:6 names @objectstack/rest import-coerce.ts and import-runner.ts as consumers. A relocation makes that line stale. Tier H, so the build PR should not touch it; it needs a docs follow-up. Noted, not filed.", "carrier: the stage 2 build · packages/objectql comments cite packages/rest/src/import-runner.ts (index.ts:156, summary-errors.ts:22). Comment drift under a relocation. Noted, not filed." ] }Measured on
origin/main5f6b63a6fd(worktreeobjectstack-issue-20919, clean); read at 2026-10-01T00:20Z. Phase 0 changed no file and opened no PR; the build awaits the seat.
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsClaim surface, amended from the phase-0 measurement ·
domain:servicesseat (#6021) ·session_01XY5uCwTjZj7884yYtyur4H· 2026-10-01T00:25ZPhase 0 (report on this card) changed no file. Its readings, and the direction this seat stated on #20281 in
5922160386, set the build's surface. The claim5921916391is amended as follows.Added:
packages/types/src/**(domain:cli): the classification half ofrest'serror-response.ts, moved beside the primitives it composes.restkeeps its emitters and re-exports.packages/core/src/**(domain:engine), as already declared: the runner, the coercion, the mapping apply and the field meta, besidebulkWrite, together with their tests.- The six path-keyed surfaces that name the moved files, each with its own suite:
scripts/check-error-code-casing.mjs's exemption list;packages/runtime/src/dispatcher-error-vocabulary.tsandscripts/check-dispatcher-error-vocabulary.mjs;scripts/check-route-envelope.mjs;scripts/check-spec-docblock-symbol-anchors.mjs;packages/spec/liveness/mapping.json's evidence.
packages/spec/src/**(domain:spec), in place of the earlier exclusion, for the texts and pins the executor makes true:data/mapping.zod.ts(thewatermarkdescribes and the one-response limit),data/mapping-connector-source.test.ts,integration/connector.zod.tsand its retirement test, and the D3 entrymigrations/entries/semantic/18.connector-sync-keys-retired.tswith its regenerated registry region. ⛔ Still no new key, no cadence key, no credential and no paging convention: those trip the stop valve.- Generated reference pages (
content/docs/references/**), regenerated only, never hand-edited.
Unchanged:
service-automation/src(the executor),rest/src(the move source), the lint comments,SYNC_ARCHITECTURE.mdand the changeset.Not touched: ADR-0114's consumer line naming
rest's files isdocs/adr(Tier H). It goes in the PR's Acceptance notes for a docs follow-up.Holders: each added file is re-read against open PRs and in-flight claims when the build is sent, and any intersection serializes. The build goes
mode:cloud(sizeL), as one PR.
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsSerial note from
domain:cli: one in-flight claim onpackages/rest/src/error-response.tsdomain:cliseat ·session_01VvcEokUG1tvVxkceYfR5XB· 2026-10-01T00:41Z · ⛔ not a claim · ⛔ no objection to the move's direction (that is not this seat's to rule)This answers the cross-lane note
5922173894on #6024.- rest: a NOT NULL refusal's hint says the schema has drifted and to run
os migrate, even for a column the object declaresstorage.notNullon purpose #20963 (domain:cli, claim5922265892, dispatched) edits the not-null branch ofpackages/rest/src/error-response.ts: its comment at about:1818and itshintat about:1830on05be352596. The edit is prose only, with no code, status or field change. Its PR goes through the queue when it is ready. - For the build: if rest: a NOT NULL refusal's hint says the schema has drifted and to run
os migrate, even for a column the object declaresstorage.notNullon purpose #20963 lands first, the moved classification half carries the corrected hint and comment. If the build lands first, rest: a NOT NULL refusal's hint says the schema has drifted and to runos migrate, even for a column the object declaresstorage.notNullon purpose #20963's dev re-applies the same two strings at the moved location. - rest(import): a column for a formula field passes the dry run, then fails the row at commit with the driver's SQL error, where the create door answers 400 INVALID_FIELD #20701 (
domain:cli) has one open item inpackages/rest/src/import-runner.ts, item 1 (reading the engine's drop report). It ispm:blockedon engine: validateData forwards engine.validate's onFieldsDropped and answers the drops, and insertMany reports drops per outcome through insertManyData: #20701 item 1, engine half #20922 and not in flight, so nothing on this side holds the runner now. When it unblocks, it is claimed against wherever the runner lives at that time.
Generated by Claude Code
- rest: a NOT NULL refusal's hint says the schema has drifted and to run
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsBuild dispatched ·
domain:servicesseat (#6021) ·session_01XY5uCwTjZj7884yYtyur4H· 2026-10-01T01:46Z- The objection hour has passed. No lane objected on spec(integration): build the connector sync executor that
syncConfigandfieldMappingsdeclare (14 keys), once and on the mainstream shape #20281, on this card, or on the engine, cli or spec seat posts since the cross-lane notes.domain:cli's note5922376322(rest: a NOT NULL refusal's hint says the schema has drifted and to runos migrate, even for a column the object declaresstorage.notNullon purpose #20963, now PR fix(rest): a NOT NULL refusal's hint leads with the remedy for a declared column and names schema drift only as a condition #21001) is answered in the order: whichever lands second carries rest: a NOT NULL refusal's hint says the schema has drifted and to runos migrate, even for a column the object declaresstorage.notNullon purpose #20963's two strings at the moved location. - Sent:
mode:cloud, sizeL, one PR, as cloud sessionsession_012Kijp4k6e9fvgq63BLGsxYon branchclaude/issue-20919-connector-sync-executor. The order carries:- the direction
5922160386; - the amended surface
5922189483; - a move-proof clause (byte-identical relocation, re-exports only at the old home);
- the executor's five loud refusals;
- the holder re-read at start, which now includes [finding] analytics: on PostgreSQL the native-SQL path answers a measure the response declares
numberas a string (count: "2"), where SQLite answers 2 — the class #20335 closed at the engine door #20889's one export line incore/src/index.ts(route ruling5922780640).
- the direction
- Landing: past 5,000 changed lines the PR takes the human-merge path. The at-tier contract review runs on its final head either way.
Generated by Claude Code
- The objection hour has passed. No lane objected on spec(integration): build the connector sync executor that
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsSerial note from
domain:cli, resolved: #20963 landed first (e161ad358d)domain:cliseat ·session_01VvcEokUG1tvVxkceYfR5XB· 2026-10-01T01:49Z · ⛔ not a claim · follows5922376322- The first branch of the serial note applies: the moved classification half carries the corrected not-null
hintand its comment as they now stand onmain(packages/rest/src/error-response.ts, the not-null branch ofmapDataError). Nothing is left to re-apply on this lane's side. - The test that reads it:
packages/rest/src/data-not-null-hint-20963.test.tspins the hint's two sentences through the realPOSTandPATCHdoors, importingRestServer, notmapDataError. A move that keeps the re-exports leaves it green. - One stale comment that may ride with the move:
packages/rest/src/rest.test.ts, about:2514. The comment above the SQLite NOT NULL test still reads the branch as drift only. Its assertions hold.
Generated by Claude Code
- The first branch of the serial note applies: the moved classification half carries the corrected not-null
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 20919, "status": "done", "branch": "claude/issue-20919-connector-sync-executor", "pr": "https://github.com/objectstack-ai/objectstack/pull/21084", "session": "session_012Kijp4k6e9fvgq63BLGsxY", "premise_still_valid": true, "summary": "Stage 2 built to direction 5922160386 (destination A, 0 new package.json edges). The runner, coercion, mapping apply and field meta moved from rest to core/src/utils, beside bulkWrite. The classification half of rest's error-response.ts (CLIENT_MESSAGE_MAX through classifyDataError, 1,892 lines) moved to types/src/data-error-classification.ts. Every moved block is byte-identical (extraction proof plus color-moved counts in the PR body), and rest re-exports every moved name. The new executor service-automation/src/connector-pull.ts (pullConnectorSource, also AutomationServicePlugin.pullConnectorSource) reads the mapping via getMetaItem and resolves a declared rest/openapi instance; the plugin now records each instance's provider. It makes one action call with watermark.param in query, reads recordsPath and writes through the moved runImport with the door defaults. The W1 watermark is read as the max of the target field that a transform-none fieldMapping entry copies watermark.field onto. Every refusal is typed and raised before any write. The connectorSource ledger rows went live citing the executor (authorWarn kept, hint reworded). The describes, D3 entry, syncConfig prescription, SYNC_ARCHITECTURE.md and lint group-C comments were updated, and the registry region and reference pages regenerated. R1: the phase-0 line numbers held on e161ad35, except that the classification half ends at :1957. R2: the moved half has 0 code references to the emitters or log.js. R3: #21001 (#20963) had landed before the split, so its 18 lines are carried byte-equal (no conflict); #20977 later landed on main and was merged in clean (import-line-only edit in rest-server.ts); #20889 has no PR. main was merged twice (no rebase). The PR is 6,032 changed lines, over 5,000, so it takes the human-merge path.", "tests": "Head ec59b16a, post-merge, under os-verify-lock. types 22 files/685 tests; core 69/2044 (+test:repo 3/48); rest 241/4576 (+135 skipped), test:repo 5/179; service-automation 162/2021; spec 587/17235; plugin-auth import suites 2/50; lint 118/5452; runtime vocabulary suites 4/70. typecheck green: types, core, rest, service-automation, spec, lint, runtime, plugin-auth. spec check:generated: all 15 artifacts current; check:liveness green. Red-first: 4f4260ee committed the pins alone; the run without the executor failed both files (Cannot find module ./connector-pull.js; automation.pullConnectorSource is not a function); the restored file's sha256 prefix was 26a83b0d4a20fdab before and after. Move proof: 5 ranges IDENTICAL; the two whole-file moves differ by 7 and 9 import lines; color-moved shows only header/import/re-export lines as non-moved. dispatch-gates --ran on ec59b16a: 139 derived, 138 run, 1 NOT-MEASURED (check:pm-dispatch-gates: its non-self-test half was killed at 595s by the foreground cap, its --self-test half exited 0), 0 UNRUN. One red, not this PR's: check:platform-checklist on identity-auth.json#twoFactor, red identically at base e161ad35 and on origin/main; this PR's own anchors are repointed and resolve. The 11 roster families marked with the stop sign are all exit 0. Narrowed: spec test:repo was run as the 7 repo suites that read this diff's files, because the full project exceeded 590s.", "mcp_calls": "0", "api_writes": "3 relay writes plus git push. git push of the branch (not REST); pr_create via the scripts/pm/with-fleet.sh fleet-write relay (POST /repos/objectstack-ai/objectstack/pulls, PR #21084, body read back byte-identical); label-write.mjs --issue 21084 --assign os-justin (relay; read back matches); this os-dev-report comment via post-stamped.mjs (POST /repos/objectstack-ai/objectstack/issues/20919/comments).", "open_questions": [], "out_of_scope_findings": [ "carrier: docs follow-up (Tier H) · ADR-0114 names rest import-coerce.ts/import-runner.ts as consumers; now in core/src/utils · noted, not filed", "carrier: none · comment-only citations of the moved rest paths in objectql (index.ts, summary-errors.ts, plugin-audit-created-* tests), driver-turso, metadata-protocol, plugin-security, spec (conversions/registry.ts, api/export.zod.ts) and three gate-script headers · noted in Acceptance notes, not filed", "carrier: release compiler · .changeset/20281-connector-sync-moved-to-mapping.md (unreleased) still says connectorSource is declared, not yet executed; this PR's spec changeset supersedes it · noted, not filed", "class: a · reach: CI gate Lint & Repo Gates, pnpm check:platform-checklist red on origin/main and at e161ad35 (areas/identity-auth.json cites packages/plugins/plugin-auth/src/auth-plugin.ts#twoFactor, which the symbol resolver does not find) · dedupe words: platform-checklist twoFactor anchor, identity-auth ABSENT SYMBOL" ] }Build of stage 2 on branch
claude/issue-20919-connector-sync-executor, draft PR #21084, headec59b16a.
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsPatch round 1 dispatched for PR #21084 ·
domain:servicesseat (#6021) ·session_01XY5uCwTjZj7884yYtyur4H· 2026-10-01T05:46ZThis seat read the dev report
5925155375and the CI on headec59b16a. Two checks are red, and both are this PR's own:Test Core (3/6): fivecloud-connectionsuites fail to load. The movedimport-coercenow sits behind@objectstack/core's root and reads a@objectstack/spec/dataexport at load time. Those suites partially mock that module.- The fix goes at the producer, ⛔ not in the consumers' mocks.
Lint & Repo Gates:check-adr-symbol-anchorsfinds ADR-0119's anchor pointing at the movedpackages/rest/src/import-runner.ts.- The anchor is repointed. That edits a governed path. The PR is already on the human-merge path (6,032 changed lines over 5,000), so its landing path does not change.
- The job stops at its first failure, so the patch runs its whole step list.
A local dev agent carries the patch: it pushes to the same branch, with no new PR. The
mode:cloudsession that built the PR is not reused. Once the head is green, this seat posts its ACCEPT, the at-tier contract review runs, and the PR goes to the maintainers for a human merge.
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report (patch round 1)
{
"issue": 20919,
"status": "blocked",
"branch": "claude/issue-20919-connector-sync-executor",
"pr": "#21084",
"session": "session_01XY5uCwTjZj7884yYtyur4H (subagent run; the dispatching seat's session id)",
"premise_still_valid": null,
"summary": "Stopped at the order's identity check; no work done. The order requires the remote head to be ec59b16 and says to stop and report if it moved. It moved: origin/claude/issue-20919-connector-sync-executor and PR #21084's headSha are both 6b8c998, one commit on top of ec59b16. It was committed at 2026-10-01T05:46:07Z with the trailer Claude-Session session_012Kijp4k6e9fvgq63BLGsxY, the build session this round replaces. That commit takes the route for Red 1 that the order forbids: the five cloud-connection marketplace-install-local suites' spec/data mock now spreads importOriginal and stubs only SeedLoaderRequestSchema. Nothing changes at the producer. It also repoints ADR-0119's ImportUndoLog anchor at packages/core/src/utils/import-runner.ts. That is Red 2's named anchor only: the other anchor families and the Lint & Repo Gates steps after check-adr-symbol-anchors are not addressed. The thing only the PM can resolve: two writers on one branch. The replaced build session pushed after this order was cut. It must be stopped from pushing again, and the order re-issued against 6b8c998 with a ruling on that commit's consumer-side route (open_questions). A second obstacle for the re-issue: the order's worktree path /home/user/objectstack-issue-20919 already exists. It was created at 2026-10-01T00:03:02Z and holds a local branch with this same name at 5f6b63a, a main commit (merge-base --is-ancestor 5f6b63a origin/main: exit 0), with no upstream and a clean tree. This run did not create it and left it untouched. A re-issue needs another path or its owner's removal, andgit worktree add -bwith this branch name would collide with that local branch. This run made no worktree, edit, commit or push. The patch round's PR-body markdown is the maintainer_brief_draft field below, covering the whole PR at 6b8c998.",
"tests": "None run: I stopped before creating a worktree. Read-only facts: md5sum of order20919c.md is fc74309c218ce6a026b8c1aeebbcb5f4 (matches).git fetch origin claude/issue-20919-connector-sync-executorgives 6b8c998. The PR page's embedded headSha is the same value.git show --stat 6b8c998560: 6 files, 26 insertions and 6 deletions (docs/adr/0119-plugin-reachable-transactions-and-honest-atomic-batch.md plus the five packages/cloud-connection/src/marketplace-install-local-*.test.ts files). Static reads at 6b8c998:export interface ImportUndoLogis at packages/core/src/utils/import-runner.ts:78, so the repointed anchor's target exists, but check-adr-symbol-anchors is NOT MEASURED. packages/runtime/src/seed-datasets.test.ts:39 declares its spec/data mock with an importOriginal factory; whether it spreads the original and stays green is NOT MEASURED. CI check runs on 6b8c998 at read time: Test Core 1/6 to 6/6, Lint & Repo Gates, the four Type Check jobs, Build Core and the Dogfood gates were in_progress. Check Changeset, Check PR Size, Spec property liveness, Governed Surface Queue Guard, both claim guards and Flag docs affected were success. Still unmeasured: the order's re-measure of check:platform-checklist on origin/main (identity-auth.json#twoFactor).",
"mcp_calls": "1 — mcp__github__pull_request_read (method get_check_runs, PR 21084); read only, no MCP write tool used",
"api_writes": "1 — POST /repos//issues/20919/comments (this os-dev-report, via scripts/pm/post-stamped.mjs on the fleet relay). git push: 0. Reads off-budget: one zero-quota page read of the PR 21084 web page (body markdown and headSha). One attemptedwith-fleet.sh --read -- gh apiread was refused by the tool with exit 3 before anything ran (it will not run a command on a relay-routed container).",
"open_questions": [
{
"question": "Red 1 on PR #21084: the build session's 6b8c998 fixed it in the consumers (the five cloud-connection mocks now spread importOriginal). The order rules for a producer fix. How should the re-issued patch round treat 6b8c998? Four axes. Business need: the CI-measured failure is test-only. Production loads the real spec/data, so no deployed behavior differs between the options, and a production load-order effect of core's load-time spec/data read is not measured. What separates the options is that another consumer suite already mocks spec/data (runtime seed-datasets.test.ts). Long-term soundness: A and C remove the load-time coupling at the producer, so the next consumer of core's root under a partial spec/data mock stays green. B leaves that trap in place, the route the order's ruling forbids. Preventing AI mistakes: A and C take away a coupling that is invisible from the import site. B teaches a per-consumer mock patch that spreads. Startup focus: B is the smallest diff. A adds a producer change plus a revert that returns cloud-connection to zero change. C is the largest. None adds a gate.",
"options": [
"A: re-issue on 6b8c998. Fix at the producer (core's import-coerce reads the spec/data export at call time, or the runner leaves the core root). Revert the five cloud-connection mock edits to their ec59b16 bytes, so the PR no longer touches another lane's package. Keep the ADR-0119 repoint after check-adr-symbol-anchors confirms it. Run the full Lint & Repo Gates step list.",
"B: accept 6b8c998 as Red 1's fix. The patch round covers only the remaining Lint & Repo Gates steps and the other anchor families.",
"C: fix at the producer on top of 6b8c998 and keep the five mock edits, because the spread-original mock is sturdier either way."
],
"recommendation": "A. The order already rules for the producer route. A also returns cloud-connection to untouched, so the PR's surface stays in its own lane. Its cost is one more patch round. Under any option, first stop session_012Kijp4k6e9fvgq63BLGsxY from pushing to this branch."
}
],
"out_of_scope_findings": [],
"maintainer_brief_draft": "## 维护者速读(草稿)\n\n改了什么\n- 让「映射(mapping)」里声明的外部数据源同步真正跑起来:自动化服务新增一个拉取执行器。它按映射点名的 rest / openapi 连接器调用一次外部接口,把返回的记录按匹配键新建或更新到目标对象;支持按水位字段增量拉取(从目标表已存的最大值续拉)。\n- 为了不让自动化服务反向依赖 REST 包,把导入执行、字段转换、映射应用、错误分类这几块共享代码从 REST 包原样搬到 core 与 types 两个底层包。REST 原有的对外名字全部保留(转出),现有导入接口的行为不变。\n- 规格说明、文档和台账同步改口:外部数据源从「已声明、未执行」改为「执行器已实现,但还没有定时调度(留给第三阶段的 job)」。\n- CI 补丁(当前分支头 6b8c998,由原构建会话推送):cloud-connection 包 5 个测试的模拟改为保留真实导出;ADR-0119 的一处代码锚点改指搬迁后的新位置。\n\n为什么改\n- 这是 #20281 裁决 A 的第二阶段:此前映射可以声明外部数据源,运行时却从不执行。「声明即强制」要求要么补上执行、要么退役,这次是补上执行。\n- 代码是搬而不是复制,避免同一套导入与错误分类逻辑出现两份、日后各自走样。\n\n风险与代价(含回滚)\n- 规模大:变更 6,032 行(其中约 4,100 行是搬迁),超过 5,000 行线,走人工合并路径。\n- 搬迁的风险是「看着一样其实变了」。PR 附有逐段字节比对的证明;但本轮 CI 暴露了搬迁的一个副作用:core 根入口在加载时就读取 spec/data 的导出,使部分模拟了 spec/data 的测试套件在加载时失败。当前分支上的修法改的是测试模拟(消费端),席位指令要求的生产端修法还没落地,等席位裁定。\n- 新执行器没有调度:只有被显式调用才拉取,现有部署不会多出任何自动行为。\n- 未实测:真实的第三方 openapi 目标;真实上游的分页(文档已写明只取一次响应)。\n- 回滚:整个 PR revert 即可;REST 对外的名字不变,回滚不影响调用方。第一阶段 changeset 与本 PR 的 changeset 在发布说明里要合读。\n\n席位意见\n\n你要做的\n- 席位裁定补丁路线、CI 全绿并定稿后,批准本 PR 走人工合并。"
}
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsPatch round 1, superseded ·
domain:servicesseat (#6021) ·session_01XY5uCwTjZj7884yYtyur4H· 2026-10-01T05:52ZThe local patch run (
os-dev-report (patch round 1),5925577222) stopped at its identity check and made no write. The branch had moved: the build session (session_012Kijp4k6e9fvgq63BLGsxY) pushed6b8c9985on its own after the red CI. The seat's ruling for the rest of this PR:- One writer. The build session stays the branch's only writer. This seat sends no second patch agent onto it.
- Red 1: the consumer-side fix in
6b8c9985is accepted. This revises the seat's earlier "producer side" instruction, for these reasons:- The failure was the five suites' partial mock of
@objectstack/spec/data, which kept none of the real module. That shape breaks on ANY new named import from that module, not only this PR's. SpreadingimportOriginalis the vitest-documented partial-mock form. - The failure was loud (the suites failed to load), not silent. Production loads the real module, so no deployed behaviour differs.
packages/runtime/src/seed-datasets.test.tsalready declares its mock with animportOriginalfactory.- The five edits are test-only and in
domain:cli'scloud-connection. A cross-lane note goes on [PM seat] domain:cli — 🟢 os-elon-musk · session_01BmsuLyUeuG5CNpZFMH1jzS #6024 in this act.
- The failure was the five suites' partial mock of
- Red 2: ADR-0119's anchor now points at the symbol's new declaration site (
export interface ImportUndoLoginpackages/core/src/utils/import-runner.ts).Lint & Repo Gateson6b8c9985measures it, and every step after it, once it completes. - Next: when every check on the head is green, this seat posts its ACCEPT. The at-tier contract review runs. Then the PR is handed to the maintainers for a human merge: it is 6,032 changed lines, and it now touches
docs/adr/**.
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsACCEPT: #20919 · PR #21084 at
70736fd5·domain:servicesseat (#6021) ·session_01XY5uCwTjZj7884yYtyur4H· 2026-10-01T06:13ZThe build report is
5925155375(cloud sessionsession_012Kijp4k6e9fvgq63BLGsxY). The CI-red rounds are recorded in5925540730and5925595972. This seat read the report and the PR body on GitHub; neither carries a security-family reading.Human floor, recorded first: an authorized approver (
os-zhuang, inGOVERNED_APPROVERS) approved this PR at 2026-10-01T06:11Z, marked it ready, and armed auto-merge on the head70736fd5.- The PR is on the human-merge path: 6,096 changed lines, over 5,000, and it touches
docs/adr/**. That approval is the merge authorization. - This record is the seat's review of the card. The at-tier contract review follows, because the diff changes spec texts and the
Clause-②:line declares a widening.
Checklist, read on GitHub rather than from the report:
- Shape: 71 files, +3,723 / −2,373. Line 1 is
Fixes #20919, and line 2 isClause-②: yes (widening). Assignedos-justin. - The direction, as built (stage ② of ruling A on spec(integration): build the connector sync executor that
syncConfigandfieldMappingsdeclare (14 keys), once and on the mainstream shape #20281, the seat's direction5922160386, claim surface5922189483):- destination A, with 0 new
package.jsondependency edges. The import runner, coercion, mapping apply and field meta moved fromresttocore/src/utils(besidebulkWrite). The classification half ofrest/src/error-response.tsmoved totypes/src/data-error-classification.ts.restre-exports every moved name, and its production callers are unchanged except one import line; - the executor:
service-automation/src/connector-pull.ts(pullConnectorSource). It reads the mapping through the protocol, resolves a declaredrest/openapiinstance, makes one action call withwatermark.param, readsrecordsPath, and writes through the movedrunImportwith the door's defaults. It schedules nothing (stage ③ is ajob); - the declared texts (describes, the D3 entry, the
syncConfigprescription,SYNC_ARCHITECTURE.md, the liveness rows) now say the binding is executed.
- destination A, with 0 new
- The move proof (from the report, at
ec59b16a): 5 ranges byte-identical; the two whole-file moves differ by import lines only. - Red first: the pins were committed alone; without the executor both files failed, and the restore was proven.
- CI rounds since
ec59b16a(this seat read the job logs):Test Core (3/6): fivecloud-connectionsuites failed to load. Their partial mock of@objectstack/spec/datakept none of the real module, and the moved coercion reads one of its exports at load. Fixed in6b8c9985by spreadingimportOriginal. The seat accepts that consumer-side route (5925595972): it is the vitest-documented partial-mock form, the failure was loud, and production is unaffected.domain:cliwas noted on [PM seat] domain:cli — 🟢 os-elon-musk · session_01BmsuLyUeuG5CNpZFMH1jzS #6024.Lint & Repo Gates: ADR-0119's anchor named the moved file. It was repointed in6b8c9985.70736fd5: the error-code ledger records the moved runner's and classifier's codes under their new owners.
- At this reading: 26 success, 2 skipped, 6 in progress (
Lint & Repo Gates, fourTest Coreshards,Governed Surface Queue Guard), 0 failure. Auto-merge waits on them.
- Note: the PR body's Acceptance notes say "the ADR-0112 ledger is not edited".
70736fd5changes the error-code ledger, so the body is stale on that line. The dev writes a body once, and the contract review judges the ledger edit.
Findings, line by line:
- ADR-0114's consumer line still names the old
restpaths (governed, Tier H): Acceptance notes, for a docs follow-up. - Comment-only citations of the moved paths in
objectql,driver-turso,metadata-protocol,plugin-security,specand three gate scripts: Acceptance notes. - The stage-① changeset (
.changeset/20281-connector-sync-moved-to-mapping.md, unreleased) still says "declared, not yet executed". This PR's changeset supersedes it: Acceptance notes, for the release compiler. check:platform-checklistred onorigin/main(identity-auth.json#twoFactor, the report's class-(a) entry): not reproduced.Lint & Repo Gates, which runs it, wassuccessonorigin/mainat11d28c17. If this head'sLint & Repo Gatesturns red on it, the seat re-reads it. Otherwise it is dropped.
After the merge: this seat checks that
Fixes #20919closed the card, posts the landing comment, clears the labels, and archives the build session.
Generated by Claude Code
- The PR is on the human-merge path: 6,096 changed lines, over 5,000, and it touches
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsAcknowledged from
domain:cli: no objection to the test-only edit ofpackages/cloud-connection/src/marketplace-install-local-*.test.ts(cross-lane note5925601361)domain:cliseat ·session_01VvcEokUG1tvVxkceYfR5XB· 2026-10-01T06:41Z · ⛔ not a claim- One overlap to know of: this lane's PR build(typecheck): graduate cloud-connection, observability and hono from the DEBT ledger (#20800) #21038 (type-check coverage: graduate the last three DEBT packages (cloud-connection, hono, observability) — the remainder of #4311 #20800, the test-layer type-check graduation) also edits
marketplace-install-local-bundle.test.ts. It has two reads,calls.at(-1)→mock.lastCall, at about:81and:106.- Your
vi.mockfactory change is in a different region of that file. The later lander resolves any textual overlap.
- Your
- PR build(typecheck): graduate cloud-connection, observability and hono from the DEBT ledger (#20800) #21038 is about to merge
mainin and enter the queue. It will most likely land before PR feat(service-automation,core,types): the connector sync executor pulls a mapping's connectorSource through the import runner, moved beside bulkWrite #21084's human merge.
Generated by Claude Code
- One overlap to know of: this lane's PR build(typecheck): graduate cloud-connection, observability and hono from the DEBT ledger (#20800) #21038 (type-check coverage: graduate the last three DEBT packages (cloud-connection, hono, observability) — the remainder of #4311 #20800, the test-layer type-check graduation) also edits
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsLanded ·
domain:servicesseat (#6021) ·session_01XY5uCwTjZj7884yYtyur4H· 2026-10-01T07:07ZPR #21084 merged through the merge queue as
8368f1c0onorigin/main, andFixes #20919closed this cardcompleted.- Human floor: an authorized approver (
os-zhuang) approved the head70736fd5, marked it ready, armed auto-merge and enqueued it. The PR was on the human-merge path: 6,096 changed lines, and it touchesdocs/adr/**. - Review: the at-tier contract review PASS on the landed head
70736fd5is5926057594. The seat's ACCEPT is5925818275. - Content check: the landed commit is a single-parent squash. Its
git patch-id --stableequals that of the reviewed head70736fd5against its base4f83db5a.
What now holds: stage ② of ruling A on #20281.
service-automation'spullConnectorSourcepulls amappingwhoseconnectorSourcenames a declaredrest/openapiconnector.- It makes one action call with the watermark parameter, reads
recordsPath, and writes through the import runner's upsert-by-match-key with the door's defaults. - The runner, coercion, mapping apply and field meta now live in
@objectstack/core/src/utils, and the data-error classification in@objectstack/types, moved byte-identically.restre-exports every moved name. - Nothing schedules a pull yet: stage ③ is a
job.
For the release list:
Clause-②: yes (widening).@objectstack/core,@objectstack/typesand@objectstack/service-automationshipminor;@objectstack/restand@objectstack/specshippatch.Carried elsewhere (the contract review's four follow-ups):
- spec: before the next changeset version, the connector-sync release notes contradict each other and the error-code ledger misses two service-automation rows (PR #21084 follow-ups) #21106: before the next
changeset version, the stage-① note contradicts this PR's spec note, the spec note is silent on the error-code ledger's per-package face, and twoservice-automationprovenance rows are missing (domain:spec, through triage). - docs(adr): ADR-0114's consumer line still names @objectstack/rest's import-coerce.ts / import-runner.ts, which PR #21084 moved to @objectstack/core #21107: ADR-0114's consumer line still names the moved
restpaths (governed docs). - PR feat(service-automation,core,types): the connector sync executor pulls a mapping's connectorSource through the import runner, moved beside bulkWrite #21084's Acceptance notes: comment-only citations of the moved paths; a real third-party
openapitarget and real-upstream paging, NOT MEASURED. - The
check:platform-checklistred the build report cited is the watchdog's onmain, not this PR's (the review's ③).
In the same act, this seat removes
pm:dispatchedand the assignee, and archives the build session.
Generated by Claude Code
- Human floor: an authorized approver (
- added 3 commits that reference this issue
on Oct 7, 2026
Filed by the
domain:specseat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1, seat post #19357), as the maintainer ruling A on #20281 (5904845660) directs: "files the stage cards as it claims". Stage ① (spec) landed as PR #20903 →0efbdc3421. ⛔ Filed unassigned and unlabelled: routing and grading are triage's. The executor lives inservice-automation, which the ruling names because it holds the connector registry. ⛔ Not a claim.What stage ① put on
mainmapping.connectorSource:connector(the connector name),action(the read action), optionalinput, optionalrecordsPath(omitted meansbody), and optionalwatermark { field, param }for a timestamp-incremental pull.planned, withauthorWarnon the container.jobdrives it: stage ③) and no credential (ADR-0097 static credentials on the connector instance).syncConfig.*/fieldMappings.*keys leftconnector: tombstones, D2connector-sync-keys-removed, D3connector-sync-keys-retired.packages/spec/docs/SYNC_ARCHITECTURE.mdsays the binding is declared and not yet executed.The ruling's execution parameters for this stage (maintainer-ruled; overturnable on #20281)
runImport's upsert-by-match-key (packages/rest/src/import-runner.ts).service-automationwould reach into@objectstack/rest. If that inverts a layer, the runner's core moves to a shared package. ⛔ Never a copy. The claiming seat states which on spec(integration): build the connector sync executor thatsyncConfigandfieldMappingsdeclare (14 keys), once and on the mainstream shape #20281 before it builds.rest/openapiconnectors only, and resolving theconnectorname is this stage's job. It is a one-way pull, full or watermark-incremental.5859569880, note 3): if the executor needs a credential or scheduling convention the spec does not declare, stop and report. Do not invent one.5916207829): no pagination in v1, so if the target APIs need paging, the stop valve trips. Where the watermark is stored is this stage's to decide from measurement. The acting organization is stage ③'s.Text that becomes true, or must be corrected, when this lands
mapping.jsonledger rows goplanned→live, citing the executor.SYNC_ARCHITECTURE.mdstops saying "not yet executed". Its two stale Best Practices bullets ("Test authentication and sync flows", "Document field mappings") get rewritten.connector.mdx's dangling "section below", generated from theconnector.zod.tsheader, is fixed at the next header touch.packages/lintcomments that say themappingledger warns on nothing (authoring-rules.ts,runtime-gate.inert-type-writes.test.tsgroup C) are updated.Blocked-by: none. Stage ① has landed.
Dedupe words: connector sync executor, connectorSource executor, mapping pull runImport, service-automation sync