Skip to content

sync-in P2 engine (epic #31763, ADR-0153): windowed and paged reads, committed position, delete marking, provenance, drift, sys_sync_run, organization scope #32062

Open

Description

@objectstack-fleet

triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

Filing gate: ④ coordination node. This is a child of the epic #31763, filed from ADR-0153's 〈Children the epic files on acceptance〉 table (docs/adr/0153-one-way-sync-in-beside-federation.md, merged in #32014). domain:spec#2 asked triage to file it (mailbox comment 6760065220). ⛔ Not a claim.
Business reason: inherited from #31763 (p1). Sync-in turns external data, including a warehouse table, into ordinary objects that reports, joins and AI question answering can use. Every gap it closes (G1–G9) was measured in #31778.
Dedupe (open and closed): datasourceSource, sys_sync_run, managedBy synced. Only #31763, #31778 and #31780 match, and none of them is this child. New.
Part of #31763.
Blocked-by: #32061
Reader: the domain:server seat.
class: c · type: Feature

维护者速读

同步导入的主体:分页与窗口读取、只在提交后推进的读取位置、删除标记、来源字段、漂移拒绝、sys_sync_run 运行记录,以及组织范围。这是最大的一张卡(约 6–9 个 agent 日),排在 spec 卡之后。

Scope (ADR-0153's P2 engine row; the ADR is the spec)

  • D2 invariants. On a built-in driver, aligned windows are read whole with no cap and paged by the key. Bounded windows sit only below the run-start max(field) (invariant 12), with one more high-water read before the open-above window. Window widths and other tuning are this child's call.
  • Floored drivers. The windowed read lo <= field < hi runs through the floor, with the halving.
    • ExternalRowCapExceededError carries the row count and the watermark values, on the error object only.
    • The refusals are sync_window_unsplittable and sync_bounds_not_pushed.
  • Keys and the commit point.
    • The BIGINT key and integer-watermark client default is verified per dialect, and an out-of-range value is refused at run time.
    • A window commits only at its end. The connector paging loop commits once, after its last page.
  • D3, deletes: marking with the completeness rule and its range limit, the NULL-watermark count, and the incremental-mark refusal.
  • D4, provenance: _sync_ stamping, the injected-column plan rows (resolveInjectedSystemColumns), the _sync_ write refusal and the purge.
  • D6: the three drift arms and the record-level watermark refusal.
  • D7: sys_sync_run with its writer, retention, the end-of-pull log line and the interrupted-run close.
  • D8: the organization predicate and source_organization_scope_unstated, at boot and at the install route, with JOB_ORGANIZATION_DESCRIPTION rewritten.
  • ExternalRowCapExceededError's remedy is re-pointed from DataSync to sync-in.
  • Conformance cases for both read paths, exactly as the ADR row lists them.
  • Each planned row flips to live with its reader, and its authorWarn is dropped.

Declaration

Clause-②: no (narrowing). D8 makes job.organization required for a pull job under group, and D3's refusal narrows. Metadata-protocol: narrowed.

Size L (≈ 6–9 agent-days). Order: after P2 spec.
Grade: p1, inherited from #31763.


Generated by Claude Code

Activity

  1. added
    enhancementNew feature or request
    priority:p1High: required for production / M2
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    pm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-set
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 10, 2026
  2. objectstack-fleet commented on Nov 10, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#3 · os-support-ai · session_01WGufzh7cM8EmUX4T2CBNHX

    Carrier note from the contract review of PR #32118 (card #32061, sync-in P2 spec; record 6762969887). ⛔ Not a claim. The spec child leaves these run-time edges to this card's readers. Each is within ADR-0153's letter, and each was graded a nit on the spec side.

    1. source_key_not_exact when the source declares no id field. The build judges the key's exactness only when the source object declares id (packages/spec/src/stack.zod.ts ~L4826, ~L4902). A remote BIGINT key mapped onto an undeclared id passes the build, so the engine's run-time refusal has to cover it.
    2. A sourced mapping in a stack with no objects. collectSourcedMappingErrors runs after the zero-objects early return (~L5604 vs ~L5700), so such a stack gets no synced-target or source-object judgment at build. This is inherited from the existing targetObject rule. The engine refuses at run time.
    3. Finding names as tokens. No ADR-0112 codes were registered for source_key_missing, source_key_not_exact, watermark_not_exact or watermark_field_unmapped; the envelope code carries them. The run-time refusals reuse the same tokens verbatim.
    4. watermark_field_unmapped for connectorSource. It is judged at build for datasourceSource only, as D3 has it. The connector keeps the executor's run-time check.
    5. Re-parsing injected columns. The _sync_ reservation sits in the ObjectSchema.fields key grammar, so resolveInjectedSystemColumns output must never be parsed through ObjectSchema again after injection.
    6. Gaps the ADR does not name (close them here or in a dated ADR-0153 note):
      • a synced target that itself carries an external binding;
      • a source object equal to the target;
      • an upsertKey that no fieldMapping entry writes, which matches nothing and duplicates every run (packages/spec/src/data/mapping.zod.ts ~L669).
    7. Remedy sentences that turn false. packages/cloud-connection/src/marketplace-install-local-plugin.ts ~L1111 tells the author to add a connectorSource and says os validate refuses the same pull. Both are false for a datasourceSource pull until this card lands.
  3. objectstack-fleet commented on Nov 10, 2026

    @objectstack-fleet
    ContributorAuthor
  4. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    on Nov 10, 2026
  5. objectstack-fleet commented on Nov 10, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#7 · os-justin · session_01QSeKYm8sWjp2Pk76warN7w

    Claim: PM loop round 1
    Session: session_01QSeKYm8sWjp2Pk76warN7w
    Account: os-justin
    Branch: claude/issue-32062-sync-in-p2-engine
    Worktree: objectstack-issue-32062 (cloud container)
    Domain: domain:server
    Seat: domain:server#7
    File surface: ADR-0153's P2 engine row is the scope. Expected landing: packages/services/service-automation (job.pull, connector-pull.ts, a sys_sync_run object and writer beside sys-automation-run.object.ts), packages/objectql/src/external-driver-floor.ts and external-driver-errors.ts (windowed read, halving, the ExternalRowCapExceededError remedy), packages/metadata-core/src/injected-system-columns.ts (_sync_ plan rows), the packages/metadata-protocol _sync_ write refusal, driver conformance cases, packages/spec/liveness/*.json rows this engine reads (planned → live, authorWarn dropped) and only those spec lines; packages/services/service-job only for the D8 organization predicate; their tests; root .changeset/. ⛔ Not docs/adr/**, no other packages/spec/src/** change (a needed one is reported, not made). Delivered in slices: each PR Part of #32062, the last one closes the card. Stop on breach; explain in the report.
    Container & model: L, mode:cloud, model: opus (default judgment tier; no path-derived mandate; build-heavy across packages/spec and other packages, so a cloud container), effort: xhigh (p1, L)
    Clause-②: no (narrowing)
    Metadata-protocol: narrowed
    Responsibility: n/a — not a defect card
    Thread-read: 6766620696
    Premise: holds (read-only premise reading at 2026-11-10T13:43Z, origin/main e1146e80: #32061 closed via #32118; sys_sync_run, sync_window_unsplittable and source_organization_scope_unstated have zero hits in code, positive control 10 hits in the ADR; mapping.json sync-in rows are planned)
    Serial constraints cleared: none of the live server claims names connector-pull.ts, external-driver-*, injected-system-columns.ts or liveness/mapping.json; #32073 (seat 3) holds service-automation plugin.ts (a slice that must touch it waits for that card to land and says so)

    Dispatched 2026-11-10T13:48Z.

  6. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 10, 2026
  7. objectstack-fleet commented on Nov 10, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#7 · os-justin · session_01QSeKYmycB2eTbWXTULu4zX
    os-dev-report

    {
      "issue": 32062,
      "status": "done",
      "slice": "1 of the proposed 6 (PR body: Proposed slice order); Part of #32062, the card stays open",
      "branch": "claude/issue-32062-sync-in-p2-engine",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/32275",
      "session": "session_01QSeKYmycB2eTbWXTULu4zX",
      "premise_still_valid": true,
      "summary": "Slice 1 of the sync-in P2 engine: the windowed read of a datasource source on both ADR-0153 D1 paths (built-in: canon-aligned windows paged by key, bounded only below the run-start high-water value, a second high-water read before the open-above window; floored: open-above first, bounded windows below the largest value an over-cap answer showed, halving, sync_window_unsplittable and sync_bounds_not_pushed), the invariant-9 commit at a window's end, the organization conjunct on every read, and the run-time exactness refusals (source_key_not_exact, watermark_not_exact, plus watermark_start_invalid). ExternalRowCapExceededError now carries the returned rows in a private field (returnedRowCount, returnedValuesOf) and its remedy points at sync-in instead of DataSync. The reader has no caller yet; wiring it to job.pull is slice 2, which is blocked on the contract question below. Premise readings held on b6fc8908: #32061 closed via #32118, zero code hits for sync_window_unsplittable before this PR, ExternalRowCapExceededError remedy named DataSync, resolveInjectedSystemColumns carries no _sync_ rows, mapping.json sync-in rows planned. Assignee untouched (claim's Account os-justin carried by pr_create).",
      "tests": "head 647ec91d. pnpm --filter @objectstack/objectql test: Test Files 584 passed (584), Tests 12047 passed (12047), exit 0. pnpm --filter @objectstack/service-automation test: Test Files 271 passed (271), Tests 3585 passed (3585), exit 0; the changed test file re-run after the last commits: 25 passed (25), VERDICT command-exit 0. Ablations, each via scripts/ablation-replace.mjs (anchor 1 -> 0, blob changed, restored blob == HEAD, git diff HEAD empty; subjects resolve from source in their own test files, so no dist leg): (1) sync_window_unsplittable reason swapped: 2 failed / 23 passed; (2) sync_bounds_not_pushed guard deleted: 3 failed / 22 passed; (3) source_key_not_exact guard deleted: 2 failed / 23 passed; (4) watermark_not_exact answered NaN: 2 failed / 23 passed; (5) watermark_start_invalid answered 0: 1 failed / 24 passed; (6) floor passes no rows to the refusal: external-driver-floor.test.ts 1 failed / 65 passed.",
      "gates": "local, head 647ec91d: dispatch-gates --commands derived 69 families; all 69 run, every one exit 0; --ran with recorded exit codes: '69 derived famil(ies) accounted for — 69 run, 0 NOT-MEASURED (a DERIVED zero — all 69 recorded an exit code and none of them is 3)'. Prerequisites met first, not skipped: check-plugin-teardown-shape --self-test after a depth-1 fetch of its pinned control commit 621a4876 (shallow clone), check:dual-build-cjs-loads after the dogfood build closure (122 entry points / 72 packages load). pnpm lint exit 0. Outside the derivation: check:error-code-casing 0, check:filter-alias-parity 0, check-platform-checklist --changed-from origin/main 0. Typecheck: objectql and service-automation typecheck exit 0, check:test-typecheck OK for both. Build closure '@objectstack/service-automation...' VERDICT 0. Driver conformance ledger before and after: 66 covered, 6 DEBT, 0 exempt (unchanged). CI: PR check-runs at 647ec91d answered total_count 0 when this report was written — in_progress, not yet reported; the PM reads CI convergence.",
      "dogfood_battery": "647ec91d · os-verify-lock: VERDICT command-exit 0 (held 33m13s) · Test Files 446 passed, 3 skipped (449) · Tests 3712 passed, 42 skipped (3754) · no failures",
      "line_budget": "8 files, +1322 / -51 (1373 changed lines, under the 3,000 line class (c) threshold)",
      "files_changed": [
        ".changeset/32062-sync-in-windowed-read.md",
        "packages/objectql/src/external-driver-errors.ts",
        "packages/objectql/src/external-driver-floor.ts",
        "packages/objectql/src/external-driver-floor.test.ts",
        "packages/services/service-automation/src/connector-pull.ts",
        "packages/services/service-automation/src/datasource-window-read.ts",
        "packages/services/service-automation/src/datasource-window-read.test.ts",
        "packages/services/service-automation/src/pull-refusal.ts"
      ],
      "deviations": [
        "Worktree: the card branch was checked out on the primary checkout by the harness; it was detached there and the branch checked out in ../objectstack-issue-32062 (worktree-first), no history change.",
        "The reader ships with no caller in this slice (dead until slice 2), so no liveness row flips here; declared in the PR's Acceptance notes.",
        "A stray 6-byte file /lint.pid (outside the repo) was written by a mis-scoped shell variable; the safety check refused its removal, so it is left for the operator.",
        "Commit trailers are the AGENTS.md model-free pair; the harness's model-named trailer was not used (AGENTS.md takes precedence)."
      ],
      "mcp_calls": "0 — no MCP GitHub tool called",
      "api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (pr_create through the fleet-write relay, run 42850705881, opened #32275 as draft with assignee os-justin); this report comment through scripts/pm/post-stamped.mjs. git push is not a REST write.",
      "open_questions": [
        {
          "question": "FORK, blocks slice 2: IAutomationService.pullConnectorSource returns ConnectorSourcePullResult, whose connector and action are required strings (packages/spec/src/contracts/automation-service.ts). A datasource pull through the same contract method has neither. The runtime binder (packages/runtime/src/app-artifact-handlers.ts, judgeJobPull + pullRunOutcomeOf) calls only this method. This lane makes no packages/spec/src change.",
          "options": [
            "A — the spec lane widens the contract: a source discriminator on the result (e.g. a datasourceSource arm naming the object and datasource, connector/action kept for the connector arm); cost: a published TS interface change and its consumers, about half a day on the spec lane",
            "B — fill connector with the datasource name and action with a fixed word for a datasource pull; cost: zero, but every reader of result.connector is told something false",
            "C — a second contract method for a datasource pull; cost: a new optional method, a second binder branch, more surface than the ADR names"
          ],
          "recommendation": "A, because the result is read by the binder and by any operator surface as 'where the rows came from'; B is a lie on a machine-readable surface and C doubles the door for one shape difference"
        },
        {
          "question": "Lane conflict, blocks slices 3 and 6: ADR-0153's P2 engine row puts the _sync_ plan rows in packages/spec/src/data/injected-system-columns.ts (resolveInjectedSystemColumns) and rewrites JOB_ORGANIZATION_DESCRIPTION in packages/spec/src/system/job.zod.ts; the dispatch forbids packages/spec/src changes in this lane beyond the liveness rows. Not chosen silently.",
          "options": [
            "A — the seat authorizes those two spec lines for this card (both are named by the accepted ADR row; the describe rewrite only restates D8's ruling)",
            "B — the spec lane carries them in a sibling PR that this card's slices then read"
          ],
          "recommendation": "A, because the ADR row assigns both to the engine child and a sibling PR adds a cross-lane ordering dependency for two small edits"
        },
        {
          "question": "Where sys_sync_run is registered (D7, slice 2): beside SysAutomationRun in service-automation's plugin.ts registerRunObject (held by #32073), beside SysJob / SysJobRun in service-job's job-service-plugin.ts (the claim allows service-job only for D8), or as a platform-objects object registered by the runtime.",
          "options": [
            "A — service-automation plugin.ts, after #32073 lands (the writer's own package; waits)",
            "B — service-job's plugin beside sys_job_run, the record it links (no wait; widens the claim's service-job use)"
          ],
          "recommendation": "A, because the pull executor is the one writer and the manual trigger needs no job service; the wait is one card"
        },
        {
          "question": "Every over-cap read during halving is logged by the engine's find middleware as one WARN 'Find operation failed' with the refusal's message and stack (measured in this suite's output). A large source halved many times writes many such lines per run.",
          "options": [
            "A — leave it; one summary info line per pull lands with D7",
            "B — the windowed read passes a read option that the engine's find honours by not warning on EXTERNAL_ROW_CAP_EXCEEDED (an engine option, objectql)"
          ],
          "recommendation": "A for now, because the lines carry no row data and an engine option is a wider change than the pull needs; revisit if an operator reports the noise"
        }
      ],
      "security_findings": "none",
      "out_of_scope_findings": [
        "class: none (stale comment) · packages/spec/src/api/error-code-ledger.zod.ts still describes the EXTERNAL_ROW_CAP_EXCEEDED remedy as 'mirror the data with DataSync' in a code comment; not edited (no spec src change in this lane) · carrier: the slice or lane that next touches that file · noted, not filed"
      ]
    }
  8. 11 remaining items

  9. objectstack-fleet commented on Nov 11, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#7 · os-justin · session_01QSeKYmycB2eTbWXTULu4zX
    os-dev-report addendum

    PR #32385, patch round on the contract review 6786503520: S1, N2 and N3 are fixed, and N1, N4, N5 and N6 are left as directed. The head moved from a87604ac (the seat's merge of main) to de2dd9b0. No merge of main was made in this round. The round is 10 files, +311 / −56.

    Changes

    • S1, the key. sys_sync_run gains mapping_package and target_object. The package is the resolved holder, never the caller:
      • on a package read, the holder the resolution names;
      • on a no-package read, the answer's packageId, unless it is the sys_metadata stored-row marker or __runtime__. Core's resolution reads neither as a package.
    • S1, the scope. lastCommitted and the holder hand-over are scoped by holder package, mapping name and organization. The resume guard now also requires the same target object. The run is identified once pullOnce resolves the mapping (SyncRunHandle.identify), and describe stores the identity.
    • S1, the sentences. These now say what a run resumes from: both changesets (the slice-2 note again, see below), jobs.mdx, the liveness container and watermark.start notes, and the object's doc and field descriptions.
    • N2. run_record_unavailable names its four places in pull-refusal.ts and in the changeset: open, describe, the last-position read, and the commit. Each refusal sentence now says what was read. The describe and last-position cases say "Nothing was read."; only the commit case says that the windows finished before stay written.
    • N3. The organization_id comment now says that under single the engine stamps the Default Organization, which is why the ledger reads it back.
    • Artefacts. None moved. The tenancy census, the tenant-audit census, the engine-double ledger and check:liveness all pass unchanged.

    Pins. Five new real-engine cases in datasource-pull.test.ts. The package cases resolve through the real mappingNameResolver over the engine's own registry.

    1. Two packages' same-name mappings in one organization keep two positions, each held by its own run.
    2. A caller reading another package's mapping records the holder and shares the holder's position.
    3. Same name, same source, two targets: the second target receives the whole history.
    4. A mapping whose targetObject was edited starts its new target from watermark.start.
    5. A no-package read records the served package, never the two markers.

    Ablations on de2dd9b0, each through scripts/ablation-replace.mjs (anchor 1 → 0, restored blob == HEAD, git diff HEAD empty), against the 34 tests of datasource-pull.test.ts:

    • package scope dropped: 1 failed (case 1);
    • target guard dropped: 1 failed (case 4);
    • caller recorded instead of the holder: 1 failed (case 2);
    • served markers kept as packages: 1 failed (case 5);
    • package scope and target guard both dropped (--hold and then the second mutation): 3 failed (cases 1, 3 and 4). Case 3 is held by either key part alone, so it reddens only with both off.

    Readings on de2dd9b0

    • service-automation typecheck exit 0 and check:test-typecheck OK. pnpm test: Test Files 275 passed (275), Tests 3733 passed (3733).
    • datasource-pull, plugin-suspended-run-wiring and connector-pull: 92 / 92.
    • dispatch-gates.mjs --commands on this round's 10 paths derived 101 families, and all 101 ran: 100 exit 0, plus check-empty-changeset exit 1 (the declared DELIBERATE CORRECTION).
      • check:dual-build-cjs-loads first exited 3, with PREREQUISITE NOT MET, and exited 0 after the builds it reads.
      • --ran: "101 derived famil(ies) accounted for — 101 run, 0 NOT-MEASURED (a DERIVED zero — all 101 recorded an exit code and none of them is 3)".
    • The boot-close pin's hand-built run now names its target, as every pull's run does.

    The PR body is now wrong in these places (the dev does not edit it):

    • the run-record bullet lacks the two new columns;
    • the committed-position bullet says a run resumes from "the latest committed run of the mapping in its organization … same source object on the same watermark.field": it is now per holder package and name, and the target must match too;
    • the retention bullet says "the run of a mapping and organization committed last": it is per holder package, name and organization;
    • the run_record_unavailable bullet lists three places, not four;
    • Tests and Verification predate this round.
    • The DELIBERATE CORRECTION quote of .changeset/32062-sync-in-datasource-pull.md changed again. The note now reads: "Where a run starts. Each run reads on from the read position the last run of the same mapping (its package and name) committed in the same organization, stored on sys_sync_run (ADR-0153 D7, in the same release: see its own entry), so it reads only the rows written since, plus the rows sharing that position, which the keyed upsert writes again harmlessly. The first run, every run of a mapping that declares full: true, and a run whose mapping now reads another source object or field or writes another target, read from watermark.start. deletePolicy and deletedField still warn their author that they change nothing yet."

    Counts: this comment is the round's one REST write, through post-stamped.mjs. git push is not a REST write, and the head-sha reply goes by session message. MCP calls: 0.

  10. objectstack-fleet commented on Nov 11, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#7 · os-justin · session_01QSeKYm8sWjp2Pk76warN7w

    Slice 3 ACCEPT → PR #32385 comment 6787577456 · 2026-11-11T05:06Z.

  11. objectstack-fleet commented on Nov 11, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#7 · os-justin · session_01QSeKYm8sWjp2Pk76warN7w

    Slice 3 landed · next: slice 4 = the run's job id and trigger · 2026-11-11T05:34Z

    PR #32385 merged as ed95466e83 (Part of; this card stays open and claimed). Every pull leaves a sys_sync_run row. A datasource pull reads on from the position the last run of the same mapping committed, keyed by holder package, mapping name and organization, and guarded by source, field and target. Contract review: PASS 6786503520, then delta PASS 6787442175. The DELIBERATE CORRECTION of the slice-2 changeset was confirmed in ACCEPT 6787577456.

    Claim widening for slice 4 (option A of the slice-3 open question). D7 lists the sys_job_run id that drove a pull and its trigger. Scope:

    Still after it, in the dev's order: D3 deletes, D4 provenance, D6 drift, the D8 refusals (job_organization_missing), slice-1 N2, carrier note 6762989212 item 6, N7, and the per-dialect cases.

  12. objectstack-fleet commented on Nov 11, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#7 · os-justin · session_01QSeKYmycB2eTbWXTULu4zX
    os-dev-report

    {
      "issue": 32062,
      "status": "done",
      "slice": "4 = ADR-0153 D7's job run id and trigger, scoped and claim-widened by the seat in 6788004425 (option A of the slice-3 open question); Part of #32062, the card stays open",
      "branch": "claude/issue-32062-sync-in-p2-engine",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/32405",
      "session": "session_01QSeKYmycB2eTbWXTULu4zX",
      "premise_still_valid": true,
      "summary": "Contract: JobHandler's context gains optional runId and trigger (schedule / manual), with a new JobRunTrigger type; ConnectorSourcePullRequest gains optional jobRunId and trigger. Adapters: CronJobAdapter and IntervalJobAdapter tell the handler the trigger on each path (scheduled fire, with and without the cluster lock, and trigger()); DbJobAdapter adds the sys_job_run id of the attempt it is recording, one per attempt, and none under recordRuns: false. The runtime binder's pull form forwards both, each only as the contract spells it (two lines plus the run signature). sys_sync_run gains job_run_id (text, not a lookup) and trigger, written at open; a direct pull leaves both NULL. Deviations from the order: (1) the branch is this session's platform-assigned claude/issue-32062-sync-in-p2-engine, recreated from origin/main ed95466e (#32385 merged, its branch deleted on the remote), not claude/issue-32062-sync-in-p2-slice4, which this session may not push to; (2) the changeset level is patch, not minor: the level gate refuses minor beside Metadata-protocol: none, and no authorable key moves. packages/spec/src hunks beyond job-service.ts: contracts/automation-service.ts (ConnectorSourcePullRequest, the symbol the order names), and the two contract test files whose pins the widening moves (the four-keys pin becomes six; a new type-level block for the context); api-surface/ and export-origins/ record JobRunTrigger. One runtime test pin moved: app-plugin.job-data-reach.test.ts names the contract's trigger in the handler form's exact key set. DELIBERATE CORRECTION of the unreleased .changeset/32062-sync-in-run-record.md bullet that listed the job run and trigger as not recorded (quoted in the PR body). #32355 has not landed; the binder hunk sits apart from its hunks and a trial merge with its head ae41bb74 is clean, as is one with origin/main 767f1106.",
      "tests": "service-job: pnpm test, 12/12 files, 145/145 tests (94ed0284). New job-run-context.test.ts 9/9. runtime: the nine job and binder files, 127/127 (94ed0284). New app-artifact-handlers.job-pull-run.test.ts 6/6, including the real DbJobAdapter driving the real binder: a manual run and a scheduled fire each hand the pull the sys_job_run row the adapter wrote, with their trigger. service-automation: pnpm test, 275/275 files, 3737/3737 tests (da8c3e05; the package is unchanged after it); 4 new real-engine cases in datasource-pull.test.ts (job-driven, refused-at-open, trigger alone, direct pull NULL). spec: contract tests 37/37; check:generated, all 14 current with check:test-typecheck (e7e9bf94, 8192 heap). Typecheck exit 0 for service-job, service-automation and runtime, with check:test-typecheck OK. Runtime's first run refused one file for an unbuilt @objectstack/driver-turso (TS2307) and passed after it was built. Ablations: 17 on 0ba54a17, one per new forwarding line plus the two contract-spelling guards, each red, each restored byte-exact (blob == HEAD, git diff HEAD empty): cron 3/1/1/1, interval 5/3/2, DbJobAdapter 3, binder 3/4/1/1, ledger 2/3, pull 3/2/3 failed. The first run on da8c3e05 gave the same counts.",
      "gates": "dispatch-gates --commands derived 124 families from 22 paths at 0ba54a17; all 124 ran there: 123 exit 0, check-empty-changeset exit 1 (the declared DELIBERATE CORRECTION). --ran: '124 derived famil(ies) accounted for — 124 run, 0 NOT-MEASURED (a DERIVED zero — all 124 recorded an exit code and none of them is 3)'. Found on earlier heads and fixed: api-surface and export-origins (JobRunTrigger); objectql-double-limit and where-matcher (the new job-store doubles); engine-double-contract (update via assertEngineUpdateDispatch, then --write); test-source-alias (collection-time import in the edited suite); check:generated's test-typecheck leg (the four-keys pin). Prerequisite exits (3) for check:skill-examples and check:dual-build-cjs-loads cleared after building the fresh worktree. Level axis offline with the PR body as the event: exit 0, patch. A container restart stopped one run part-way; the final head's run is complete. CI check-runs: not yet read; the seat moves the head for CI if the push started none. Not run locally: pnpm lint and the CI farm.",
      "dogfood_battery": "n/a — no `!` in the PR title and the Clause-② line carries the widening arm",
      "mcp_calls": "3 — mcp__github__issue_read get #32062, mcp__github__pull_request_read get #32385, mcp__github__issue_read get_comments #32062 (the seat's 6788004425); no write tool",
      "api_writes": "2 — the relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches, run 42986524201, read back identical) and this comment through post-stamped.mjs; git push is not a REST write",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "class: b · reach: named producer DbJobAdapter (the default durable adapter): schedule() wraps the handler with a fixed defaultTrigger 'schedule' (wrap(name, handler, 'schedule', options)), and trigger() runs that same wrapper through the inner adapter, so a manual run's sys_job_run row reads trigger: 'schedule'; replay() adds its synthetic 'replay' row beside the wrapper's own 'schedule' row · evidence: db-job-adapter.test.ts:302 expects trigger: 'schedule' after adapter.trigger('on'); contradicts content/docs/automation/jobs.mdx ('trigger (schedule | manual | replay)') and the SysJobRun docblock ('whether the run was a manual trigger or a scheduled tick'); since this slice, a manual pull's sys_sync_run.trigger reads manual while the sys_job_run row it names reads schedule · dedupe words: sys_job_run trigger manual, DbJobAdapter defaultTrigger, manual trigger recorded schedule, replay row trigger · carrier: none — for the seat to file",
        "class: none (observation) · @objectstack/runtime's JobHandlerContext type does not declare runId or trigger, though the handler form passes the job service's context through at run time (pinned in app-plugin.job-data-reach.test.ts) · carrier: none · noted, not filed"
      ]
    }
  13. objectstack-fleet commented on Nov 11, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#7 · os-justin · session_01QSeKYm8sWjp2Pk76warN7w

    Slice 4 ACCEPT → PR #32405 comment 6791958834 · 2026-11-11T09:17Z. Nits N1–N5 of record 6791935060 ride slice 5 (D3).

  14. objectstack-fleet commented on Nov 11, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#7 · os-justin · session_01QSeKYm8sWjp2Pk76warN7w

    Slice 4 landed · 2026-11-11T09:51Z

    PR #32405 merged as b96d492db8 (Part of). This card stays open and claimed.

    Verified on origin/main:

    • JobHandler's context carries optional runId and trigger (JobRunTrigger).
    • The job adapters pass both.
    • A pull records them on sys_sync_run (job_run_id, trigger).

    Next: slice 5 (D3, deletes). It carries nits N1–N5 of record 6791935060. It is not dispatched yet: it waits on the maintainer's decision about this card's seat, now that this seat is moving to the ADR-0150 epic seat.

    Still after it, in the dev's order: D4 provenance, D6 drift, the D8 refusals, slice-1 N2, carrier note 6762989212 item 6, N7, and the per-dialect cases.

    Related card: #32406 (sys_job_run.trigger for manual and replay runs).

  15. objectstack-fleet commented on Nov 11, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#7 · os-justin · session_01QSeKYm8sWjp2Pk76warN7w

    Release: session_01QSeKYm8sWjp2Pk76warN7w (domain:server#7) · reason: the seat moves to the ADR-0150 epic seat on the maintainer's word, and he chose to send this card back to the server lane (「A」) rather than keep it as an exception · next: pm:queue for the next domain:server seat
    Resume-from: none

    Written 2026-11-11T12:18Z. The slice branch claude/issue-32062-sync-in-p2-engine was deleted when its last PR merged. The next claimant starts a fresh branch from origin/main.

    State at release (origin/main 7d82b2dab8):

    Slice PR What landed
    1 #32275 windowed datasource read
    2 #32339 a job pulls a mapping's datasourceSource
    3 #32385 sys_sync_run, the committed read position (keyed by holder package, mapping name and organization; guarded by source, field and target), retention, boot close
    4 #32405 the job's runId / trigger reach the pull and sys_sync_run (job_run_id, trigger)

    Next: slice 5 = D3 (deletes). It carries nits N1–N5 of contract record 6791935060:

    • N1. JobHandlerContext, the changeset and the jobs.mdx member table should name the trigger / runId the handler form now receives.
    • N2. originOf should bound its inputs.
    • N3. The self-contradicting jobs.mdx sentence about the two columns needs rewording. The replacement wording is in the record.
    • N4. The cli CHANGELOG's dangling "see its own entry".
    • N5. The sys_sync_run field descriptions should say when job_run_id is empty.

    After it, in the dev's order:

    • D4 provenance, with the injected-system-columns.ts plan rows;
    • D6 drift;
    • the D8 boot and install refusals, spelled job_organization_missing;
    • slice-1 N2 (the watermark-only error payload);
    • carrier note 6762989212 item 6;
    • N7;
    • the per-dialect cases.

    Open cards around it:

    Release order: slices 1–4 are on main and unreleased. No release constraint is open between them; slice 3's S1 was fixed before landing.

    Claim surface for the next claimant: read the original claim 6769041777 and the slice-4 widening 6788004425. The contracts/ piggyback and service-job handler lines were slice 4's and are done. D8 reopens service-job for the organization predicate.

    Dev context: the cloud dev session that built slices 1–4 is told to stand down. Its knowledge is in the four PR bodies and the reports on this card.

  16. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Nov 11, 2026
  17. added
    pm:on-holdDecision made, deliberately deferred — no dispatch, no nag; restart condition in the hold comment
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 11, 2026
  18. objectstack-fleet commented on Nov 13, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH

    Path: none

    Triage: regrade p1 → p2 under the adopted grading rules (#32997) · 2026-11-13T03:10Z

    ⛔ Not a claim; the card's claim and state are unchanged.


    Generated by Claude Code

  19. added and removed
    priority:p1High: required for production / M2
    on Nov 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setpm:on-holdDecision made, deliberately deferred — no dispatch, no nag; restart condition in the hold commentpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions