Skip to content

Commit c40506a

Browse files
docs(qa): RUNNER.md reads the connector self-URL from /meta/connector and states the scheduled-work switch (#24156)
Fixes #24119 Clause-②: no Docs only: `docs/qa/platform-checklist/RUNNER.md`, the two runner facts the card names, both inside "Environment facts the runner should not re-derive". Nothing else in the file moved. The "The record title" section is untouched, because #24127 may change it. No area file, no product code, no changeset: the diff ships nothing from any package, and the seat applies `skip-changeset` at review. Authored by the os-dev dispatched for this card by the `domain:devx` seat 2, session `session_016PVjjuVPte5XEJvaQx1E4t`, branch `claude/issue-24119-runner-facts`. ## How both facts were re-measured Both were measured on `main` at `2889760a5`, the branch point, by reading the code and by two `os dev` boots of `examples/app-showcase`. The boots ran after a full `pnpm build`, on the RUNNER's canonical boot line (`objectstack dev --ui --seed-admin -p PORT -d file:...`): - **Boot A**: `-p 47311`, environment untouched. - **Boot B**: `OS_PORT=47312 OS_AUTOMATION_SCHEDULED_WORK_ENABLED=1`, `-p 47312`. Both boots served the same `examples/app-showcase/dist/objectstack.json`. The build compiled it with no `OS_PORT`, so its two self-pinging connectors hold `providerConfig.baseUrl: http://127.0.0.1:3000`. Nothing listened on 3000. ## Passage 1: the connector self-URL check **Old** (the bullet's `Check:` sentence; the rest of the bullet is unchanged): ```text **Check:** before scoring any connector clause, `GET /api/v1/automation/connectors` and read the instance `baseUrl` — if it says `` and your server does not listen there, the fixture is misconfigured, not blocked. ``` **New:** ```text **Check:** before scoring any connector clause, read the instance's own metadata — `GET /api/v1/meta/connector/showcase_status_api`, field `item.providerConfig.baseUrl`, which is the value `resolveShowcaseSelfUrl()` produced (`examples/app-showcase/src/system/connectors/index.ts`). ⛔ Not `GET /api/v1/automation/connectors`: that route lists registry descriptors — `name`, `label`, `type`, `description`, `icon`, `origin`, `state`, `degradedReason`, `actions` (`getConnectorDescriptors()`, `packages/services/service-automation/src/engine.ts`) — and carries no `baseUrl` at all. On `os dev` the value follows the serving process's environment, not the compiled artifact. Measured on `main` at `2889760a`: `-p 47311` alone answered `http://127.0.0.1:3000`, and `showcase_declarative_connector_ping` (fired by a `showcase_task` create) failed `fetch failed`; `OS_PORT=47312` with `-p 47312` answered `http://127.0.0.1:47312` and the same flow completed, while `dist/objectstack.json` still held `http://127.0.0.1:3000`. Only an artifact-only boot (`os start --artifact`) freezes the value at build time (the `self-url.ts` header). If it names a port your server does not listen on, the fixture is misconfigured, not blocked. ``` **Sources and measurements:** - `GET /api/v1/automation/connectors` is served by the `parts[0] === 'connectors'` branch of `packages/runtime/src/domains/automation.ts`. That branch answers `getConnectorDescriptors()` from `packages/services/service-automation/src/engine.ts`, whose projection is `name, label, type, description, icon, origin, state, degradedReason, actions`. It has no `baseUrl` and no `providerConfig`. Measured on both boots: every row's keys were exactly `actions, description, icon, label, name, origin, state, type`, and the string `baseUrl` does not occur in the body. - `GET /api/v1/meta/connector/showcase_status_api` answers the declared item. Its `item.providerConfig.baseUrl` is the value `resolveShowcaseSelfUrl()` produced (`examples/app-showcase/src/system/connectors/index.ts`). Boot A answered `http://127.0.0.1:3000`. Boot B answered `http://127.0.0.1:47312`, while the artifact on disk still said 3000. - The materialized connector dials that value. On boot A, creating a `showcase_task` fired `showcase_declarative_connector_ping`, which failed with `Node 'ping' failed: connector_action(showcase_status_api.request) failed: fetch failed`. On boot B the same create produced a `completed` run whose `ping.body` carried `status: 'ok'`. - The value is read at boot, not at build, for this reason. The showcase config is a host config: its `plugins:` array holds instances (`isHostConfig`, `packages/cli/src/utils/plugin-detection.ts`). So the `serve` child loads `objectstack.config.ts` itself and wraps it in `new AppPlugin(config)` (`packages/cli/src/commands/serve.ts`), and `resolveShowcaseSelfUrl()` reads the serving process's environment. `os dev` hands `-p` to that child as `--port` and never exports `OS_PORT` (its `localEnv`, `packages/cli/src/commands/dev.ts`). So the bullet's existing claim that `-p` alone is not enough still holds, and that part was left as written. **Disproved half of the card's reading.** The card recorded that "`GET /api/v1/meta/connector` shows the build-time `http://127.0.0.1:3000`". On an `os dev` boot that value is not build-time. It follows the serving process's environment: boot B answered 47312 over an artifact holding 3000. 3000 is what any boot without `OS_PORT` or `SHOWCASE_SELF_URL` answers. The new text says this, so the next runner does not reason from the artifact. ## Passage 2: scheduled work **Premise correction.** RUNNER.md had no scheduled-work passage, so there was nothing stale to rewrite: - `git show c91af8e:docs/qa/platform-checklist/RUNNER.md | grep -ci schedul` prints 0 (exit 1). - The file is byte-identical between `c91af8ed6`, the card's measurement commit, and `2889760a5`: `git diff --stat` prints nothing. - `git merge-base --is-ancestor c91af8e 2889760` exits 0. A positive answer proves itself, even on this shallow checkout. The fact was missing, not stale. The card's Done-when ("RUNNER.md states both facts as the runtime answers them") is met by adding it as one new environment-fact bullet, right after the connector bullet. Old text: none. **New:** ```text - **Package-authored scheduled work is OFF on a stock boot — no `schedule` or `time_relative` flow binds and no packaged `defineJob` is scheduled unless you export `OS_AUTOMATION_SCHEDULED_WORK_ENABLED=1`.** The switch is an environment read, `resolveScheduledWorkEnabled()` / `resolveScheduledWorkPolicy()` in `packages/types/src/env.ts`: unset, empty, or anything other than `true` / `1` / `on` / `yes` (case-insensitive) is off, in every tenancy posture, and `os dev` sets nothing for you. Every binder reads that one answer — the automation engine's trigger bind and its `/_status` row (`packages/services/service-automation/src/engine.ts`), the schedule trigger (`packages/triggers/trigger-schedule/src/schedule-trigger.ts`), and AppPlugin's declarative-job loop, which logs `declarative jobs NOT scheduled — …` at info level (`packages/runtime/src/app-artifact-handlers.ts`). It does not gate platform-internal jobs (approvals escalation, the lifecycle Reaper, the messaging dispatch loop). **Check:** the boot banner. Measured on `main` at `2889760a` with the canonical boot below: a default boot printed `32 flow(s) 19 bound` and `1 flow declares a 'schedule' trigger but is NOT bound — disabled by deployment policy …: showcase_scheduled_digest` (the same line for `time_relative`: `showcase_task_due_reminder`), and `GET /api/v1/automation/_status` answered `bound: false` with that reason for both; the same boot with the switch exported printed `21 bound`, `_status` answered `bound: true` for both, and `showcase_scheduled_digest` completed its first run (`trigger.type: 'schedule'`) within a minute. **Fix:** export the switch before the boot, beside `OS_PORT`. ⛔ Do not score a clock-driven clause on a default boot, and do not file the unbound flow: it is the deployment policy working, not a trigger defect. ``` **Sources and measurements:** - The reader is `resolveScheduledWorkEnabled()` in `packages/types/src/env.ts`. It reads `OS_AUTOMATION_SCHEDULED_WORK_ENABLED` and is truthy only for `1`, `true`, `on` or `yes`. Its docblock reads "Default OFF, in every posture and every kernel". `resolveScheduledWorkPolicy()` and `scheduledWorkDisabledReason()` in the same file are what the engine's bind, the `/_status` row and the schedule trigger read. AppPlugin's job loop logs `declarative jobs NOT scheduled — …` at info level (`packages/runtime/src/app-artifact-handlers.ts`, the `!resolveScheduledWorkEnabled()` branch). - `os dev` does not set it. Neither `packages/cli/src/commands/dev.ts` nor `serve.ts` names the variable, and `dev`'s `localEnv` does not carry it. - Boot A's banner printed `Flows: 32 flow(s) 19 bound …` and the two `NOT bound — disabled by deployment policy` lines, for `showcase_task_due_reminder` and `showcase_scheduled_digest`. `_status` answered `bound: false` for both, with the policy reason. - On boot B the banner printed `21 bound`, and `_status` answered `bound: true` for both. `showcase_scheduled_digest` completed its first run at 2026-10-17T05:51:55Z with `trigger.type: 'schedule'`, less than a minute after boot. ## Gates All gates below were run on the final head `4094dd433`, which is `d76548150` (the edit) plus a merge of `main` at `6dede7a51`. The merged commit touches none of the paths cited above. Each exit code was captured before any pipe. - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no paths) derived 13 commands from a 1-path change set. Every one exited 0: `check-ci-filter-parity`, `check-closing-keyword-parity` and its `--self-test`, `check-comment-mask-corpus`, `@objectstack/lint check:doc-formula-expressions`, `check:cross-package-test-inputs`, `check:doc-authoring`, `check:driver-memory-census`, `check:gitlink-declared`, `check:nul-bytes`, `check:platform-checklist`, `check:refd-timer-probe` and `check:watch-hint-literal`. - `check:platform-checklist` printed: `check-platform-checklist: OK — 16 areas, 327 items (322 active, 1 planned)`. - `--ran` reconciliation: `13 derived famil(ies) accounted for — 13 run, 0 NOT-MEASURED (a DERIVED zero — all 13 recorded an exit code and none of them is 3)`. - `node scripts/pm/check-governed-merges.mjs --branch claude/issue-24119-runner-facts`: `0 of 1 path(s) hit the register` and NOT governed, 42 changed lines. - CI check-runs had not concluded when this PR was opened. ## Acceptance notes - The area files already name the switch per item (`automation.json`, `integration-system.json`, `platform-core.json`, `approvals.json`). The new bullet states it once, for every item and every runner. - Not measured: `serve.ts` says that on an `os dev` boot of a host config, the dev artifact door's copy of the stack replaces the config wrap's copy on the first artifact reload. After a watch rebuild, `/meta/connector` would therefore follow the recompiled artifact. Whether that recompile sees an exported `OS_PORT` was not measured. Both boots above were cold boots with no rebuild. No carrier. --- _Generated by [Claude Code](https://claude.ai/code/session_016PVjjuVPte5XEJvaQx1E4t)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1fee13d commit c40506a

1 file changed

Lines changed: 39 additions & 3 deletions

File tree

‎docs/qa/platform-checklist/RUNNER.md‎

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,13 +229,49 @@ contradicts it, and correct it here when it does.
229229
one is the expensive mistake: the module's own header records that #7516 burned a run
230230
proving it was an address problem by putting a TCP forwarder on 3000, and #7538 fixed
231231
the resolution but could not fix a boot that never exports the variable. **Check:**
232-
before scoring any connector clause, `GET /api/v1/automation/connectors` and read the
233-
instance `baseUrl` — if it says `` and your server does not listen there, the
234-
fixture is misconfigured, not blocked. **Fix:** boot with `OS_PORT=<port>` exported (or
232+
before scoring any connector clause, read the instance's own metadata —
233+
`GET /api/v1/meta/connector/showcase_status_api`, field `item.providerConfig.baseUrl`,
234+
which is the value `resolveShowcaseSelfUrl()` produced
235+
(`examples/app-showcase/src/system/connectors/index.ts`). ⛔ Not
236+
`GET /api/v1/automation/connectors`: that route lists registry descriptors — `name`,
237+
`label`, `type`, `description`, `icon`, `origin`, `state`, `degradedReason`, `actions`
238+
(`getConnectorDescriptors()`, `packages/services/service-automation/src/engine.ts`) —
239+
and carries no `baseUrl` at all. On `os dev` the value follows the serving process's
240+
environment, not the compiled artifact. Measured on `main` at `2889760a`: `-p 47311`
241+
alone answered `http://127.0.0.1:3000`, and `showcase_declarative_connector_ping`
242+
(fired by a `showcase_task` create) failed `fetch failed`; `OS_PORT=47312` with
243+
`-p 47312` answered `http://127.0.0.1:47312` and the same flow completed, while
244+
`dist/objectstack.json` still held `http://127.0.0.1:3000`. Only an artifact-only boot
245+
(`os start --artifact`) freezes the value at build time (the `self-url.ts` header). If
246+
it names a port your server does not listen on, the fixture is misconfigured, not
247+
blocked. **Fix:** boot with `OS_PORT=<port>` exported (or
235248
`SHOWCASE_SELF_URL=http://127.0.0.1:<port>`) *in addition to* `-p <port>`. ⛔ Do not
236249
record a connector clause as `blocked(environment)` on the strength of `fetch failed`
237250
alone.
238251

252+
- **Package-authored scheduled work is OFF on a stock boot — no `schedule` or
253+
`time_relative` flow binds and no packaged `defineJob` is scheduled unless you export
254+
`OS_AUTOMATION_SCHEDULED_WORK_ENABLED=1`.** The switch is an environment read,
255+
`resolveScheduledWorkEnabled()` / `resolveScheduledWorkPolicy()` in
256+
`packages/types/src/env.ts`: unset, empty, or anything other than `true` / `1` / `on` /
257+
`yes` (case-insensitive) is off, in every tenancy posture, and `os dev` sets nothing for
258+
you. Every binder reads that one answer — the automation engine's trigger bind and its
259+
`/_status` row (`packages/services/service-automation/src/engine.ts`), the schedule
260+
trigger (`packages/triggers/trigger-schedule/src/schedule-trigger.ts`), and AppPlugin's
261+
declarative-job loop, which logs `declarative jobs NOT scheduled — …` at info level
262+
(`packages/runtime/src/app-artifact-handlers.ts`). It does not gate platform-internal
263+
jobs (approvals escalation, the lifecycle Reaper, the messaging dispatch loop).
264+
**Check:** the boot banner. Measured on `main` at `2889760a` with the canonical boot
265+
below: a default boot printed `32 flow(s) 19 bound` and `1 flow declares a 'schedule'
266+
trigger but is NOT bound — disabled by deployment policy …: showcase_scheduled_digest`
267+
(the same line for `time_relative`: `showcase_task_due_reminder`), and
268+
`GET /api/v1/automation/_status` answered `bound: false` with that reason for both; the
269+
same boot with the switch exported printed `21 bound`, `_status` answered
270+
`bound: true` for both, and `showcase_scheduled_digest` completed its first run
271+
(`trigger.type: 'schedule'`) within a minute. **Fix:** export the switch before the boot,
272+
beside `OS_PORT`. ⛔ Do not score a clock-driven clause on a default boot, and do not
273+
file the unbound flow: it is the deployment policy working, not a trigger defect.
274+
239275
- **A per-boot `OS_HOME` and `OS_STORAGE_LOCAL_ROOT` do NOT isolate a package install —
240276
boot every item that installs a package from a scratch copy of the app, never from the
241277
shared tree.** install-local caches each installed manifest under the server's

0 commit comments

Comments
 (0)