Skip to content

Commit 413869d

Browse files
committed
docs,changeset: install-local refuses a job pull that does not bind
The jobs page states the refusal and the rehydrate behaviour. The unreleased stage-3 changeset said install-local never refuses a pull job, which this change makes false; it now says a pull job that binds installs. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016GiHYRmLSNWTfbX9gVQkpz
1 parent 4320abb commit 413869d

4 files changed

Lines changed: 31 additions & 6 deletions

File tree

‎.changeset/20281-job-pull-organization.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Clause-②: yes (widening)
1212
- **`JobSchema.organization`**. The organization a job runs as. It applies to the body's `ctx.api`, to the handler's new `executionContext`, and to the pull's reads and writes. The value shape is the scheduled flow's: a non-empty `sys_organization.id`. A near-miss spelling (`organizationId`, `orgId`, `tenantId`, …) is refused at parse and pointed at the key.
1313
- **`defineStack`, and so `os validate`**, refuses a job whose `pull` names a mapping the stack does not declare, or a mapping with no `connectorSource`. The refusal is the existing `STACK_CROSS_REFERENCE_INVALID` envelope.
1414
- **`IAutomationService.pullConnectorSource`** (`@objectstack/spec/contracts`, with `ConnectorSourcePullRequest`, `ConnectorSourcePullResult` and `ConnectorSourcePullSummary`). The connector sync executor is now on the `automation` service. `@objectstack/service-automation`'s engine serves it from the executor `AutomationServicePlugin` attaches at init (`AutomationEngine.setConnectorPullSource`). A bare engine refuses with `SERVICE_UNAVAILABLE` (503).
15-
- **The job binder** (`@objectstack/runtime`, `scheduleAppArtifactJobs`) schedules a `pull` job on every door: the boot, and `os package install` on install and rehydrate. Each run calls `pullConnectorSource` through the service registry. A refused pull fails the run, and `retryPolicy` applies. A pull whose rows the import runner refused records the run `degraded`, with the counts. A pull naming a mapping the artifact does not carry is not scheduled, and neither is one whose mapping has no `connectorSource`, nor one on a kernel whose `automation` service cannot pull. Each case is logged at `warn` with the reason. `collectJobsWithoutBody` no longer names a `pull` job, so `os package install` does not refuse one. The result gains `pulls` and `missingOrganization`.
15+
- **The job binder** (`@objectstack/runtime`, `scheduleAppArtifactJobs`) schedules a `pull` job on every door: the boot, and `os package install` on install and rehydrate. Each run calls `pullConnectorSource` through the service registry. A refused pull fails the run, and `retryPolicy` applies. A pull whose rows the import runner refused records the run `degraded`, with the counts. A pull naming a mapping the artifact does not carry is not scheduled, and neither is one whose mapping has no `connectorSource`, nor one on a kernel whose `automation` service cannot pull. Each case is logged at `warn` with the reason. `collectJobsWithoutBody` does not name a `pull` job that binds, so `os package install` installs one. The result gains `pulls` and `missingOrganization`.
1616
- **The organization, judged at bind** by the posture rule scheduled flows use (`resolveScheduledWorkPolicy`). Every run carries `{ isSystem: true, tenantId: <organization> }`, or `{ isSystem: true }` for a job that declares none. Under `single` the key is not required. Under `group` it is optional; an undeclared job is scheduled and named once at `warn`, because a tenant-scoped row it writes is refused. Under `isolated`, with package-authored scheduled work switched on, it is **required**. **Action on such a deployment:** declare `organization` on each packaged job, or the job is not scheduled; the error log names the job. Until now such a job was scheduled, and every tenant-scoped write it made was refused at the write. An unrecognized `OS_TENANCY_POSTURE` withholds every job (`scheduled-work-policy-unreadable`) instead of guessing whether a declaration is required.
1717
- **Texts this makes true.** The `mapping.connectorSource` description, the `connector.syncConfig` tombstone prescription and the `connector-sync-keys-retired` upgrade entry said "nothing schedules a pull yet". They now name the `job` `pull` that drives it.
1818

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@objectstack/runtime': minor
3+
'@objectstack/cloud-connection': minor
4+
---
5+
6+
fix(runtime,cloud-connection)!: install-local refuses an enabled job whose `pull` does not bind, as it refuses a job `body` that does not bind (#21672)
7+
8+
Clause-②: yes (narrowing)
9+
10+
<!-- adr-0087: not-required (no-migration-prescription) no authorable key, spelling, export of a published release or stored shape moves: `JobSchema` and `MappingSchema` are unchanged, so `objectstack migrate meta` has nothing to rewrite. What changes is which packages one install door accepts. The other categories are closed on facts: the packages publish (not `unpublished`); no ADR-0087 id covers a refused install (not `registered` / `already-registered`); and the change is runtime behaviour, not a declaration (not `runtime-interface-only` / `type-surface-only`). -->
11+
12+
**BREAKING**: `os package install` (the install-local door, `POST /api/v1/marketplace/install-local`) now refuses a package whose enabled job declares a `pull` that does not bind. It used to install such a package with a 200, and the job was never scheduled; only a server warn said so.
13+
14+
- **What does not bind.** The `pull` names a mapping the package does not declare, or a mapping with no `connectorSource`, or the job declares `body` or `handler` beside its `pull`. The door judges this with the scheduler's own judgement, so the door and the scheduler cannot disagree. `defineStack` and `os validate` already refuse the same `pull`, so only a hand-edited package reaches the door with one.
15+
- **The refusal.** The install answers `422` with `VALIDATION_ERROR`, the answer the door already gives an enabled job whose `body` does not bind. One answer names everything the door cannot run, and gives each such job the reason its `pull` does not bind, prefixed with the key it names (`pull.mapping: …`). Nothing is installed: nothing is registered, persisted or scheduled. `os package install` exits non-zero and prints the code beside the status.
16+
- **Unchanged.** A pull job naming a declared mapping with a `connectorSource` installs and is scheduled as before. A disabled pull job does not block its install. A package installed by an earlier version still rehydrates after a restart, and its pull job that does not bind is not scheduled, with a warn naming the job and the reason, as before.
17+
- **Runtime.** `collectJobsWithoutBody` now names an enabled job whose `pull` does not bind, and `JobWithoutBody` gains an optional `pullRefusal`: the reason the scheduler gives when it does not schedule the job. Such a job carries no `bodyRefusal`.
18+
19+
The route for a refused package: declare the mapping the job's `pull` names in the package, with a `connectorSource` naming the `rest` or `openapi` connector it reads from, or correct the `pull` as the refusal says. `os validate` refuses the same `pull`. This ships as `minor`, under the launch-window convention for narrowings of an accept set.

‎content/docs/automation/jobs.mdx‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ export const CloseStaleTasksJob = defineJob({
176176
package whose enabled job has no `body`, or a `body` that does not bind (an
177177
expression body, or one carrying `body.timeoutMs`), with `422 VALIDATION_ERROR`
178178
and the remedy: give the job a valid `body`, or boot it with `os start --artifact`.
179-
A [`pull`](#pulling-a-mapping) job is data too, and is not refused.
179+
A [`pull`](#pulling-a-mapping) job is data too: it installs when its `pull`
180+
binds, and is refused with the same `422` when it does not.
180181
Uninstalling a package stops its scheduled jobs at once, and a reinstall whose
181182
new version drops a job stops that job.
182183
</Callout>
@@ -249,7 +250,10 @@ export const OrdersPullJob = defineJob({
249250
- **Checked when you build.** `defineStack` — and so `os validate` — refuses a
250251
`pull` that names a mapping the stack does not declare, or one with no
251252
`connectorSource`. The binder checks the same reference against the artifact
252-
before it schedules the job, on every door.
253+
before it schedules the job, on every door, and `os package install` refuses a
254+
package whose enabled job's `pull` fails that check, with `422 VALIDATION_ERROR`
255+
naming the job and the reason. A package an earlier version installed still
256+
loads after a restart; such a job of it is not scheduled, and a warning names it.
253257
- **Each run is one pull.** It calls the connector's read action once, reads one
254258
response, and writes the records through the import runner with the mapping's
255259
`mode` and `upsertKey` — see

‎packages/cli/test/package-install-local-jobs-pull.integration.test.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@
2727
* no `connectorSource` is refused the same way; the control — a pull job
2828
* naming a declared mapping — installs and is scheduled (its `sys_job`
2929
* row, and a `sys_job_run` row per run: every run reaches the automation
30-
* service's pull door, which this host answers with a refusal because it
31-
* composes no `rest` connector provider — the run's verdict, not the
32-
* install's); a DISABLED unbindable pull job does not block its install;
30+
* service's pull door, which refuses it because the package declares no
31+
* `connectors[]` entry for the connector the mapping names — the run's
32+
* verdict, `failed`, not the install's: the install door judges the job's
33+
* `pull` as the binder does, never the connector a run will read); a
34+
* DISABLED unbindable pull job does not block its install;
3335
* 2. RESTART — a ledger entry an earlier build wrote, carrying an unbindable
3436
* pull job beside a bindable one, rehydrates: the bindable job is
3537
* scheduled, the unbindable one is not, and a warn names it.

0 commit comments

Comments
 (0)