Skip to content

[Decision] os build cannot lower a job's handler into the new JobSchema.body as ruling E wrote it — withdraw the build lowering (C), or change the functions contract (A) or the job handler form (B)? #21540

Description

@objectstack-fleet

Ruled: 5968961157 · letter C · 2026-10-03T12:02Z

This card carries #21515's scope item 2, the build lowering. #21515 keeps the spec half, which is draft PR #21538 (Part of #21515).

Filing gate: ② a maintainer decision. Executing ruling E (5964305303 on #21489, 「jobs同意」) falsified one of its premises, so the change of part of a recorded ruling goes back to the maintainer. Filed by domain:spec seat 2 (session_01YDt3PzwfrkuFzUBF89WPmM, seat post #18549), from the #21515 dev report 5965564062 (open_questions[0]). ⛔ Not a claim. Nothing waits on this card: the spec half lands without it, and #21489's runtime binder needs only the spec half.

维护者速读

上一轮你批准「jobs 同意」(E):job 像 hook 一样带沙箱代码体 body,并由 os build 把 job 的处理函数自动转成 body。spec 那一半已做完(PR #21538:JobSchema.body、handler 标为弃用、超时只在一处声明)。自动转换这一半做不成:job 的处理函数写在 defineStack({ functions }) 里,defineStack 解析时会把每个函数包一层 zod 外壳,构建时拿到的是外壳、读不到作者源码;文档里 job 函数的标准写法 ({ jobId, ql, logger }) 即使读得到,转出来的 body 运行时也会报错,而且 body 优先,会顶掉原本能跑的函数。C=撤回自动转换:job 的 body 直接按数据写(和 Studio、AI、JSON 包写法一致),构建只校验;A=改 functions 的契约让构建读得到源码;B=给 job 加 hook 那样的内联函数写法。推荐 C。要不要撤回这一项?(C/A/B)

Background

Governing text:

  • Ruling E (above): the lowering clause is the item at issue.
  • ADR-0088: a function is code, never a metadata row. That is why a JSON artifact cannot carry a job handler, and why E exists.
  • automation/flow-function.zod.ts:237: "z.function() wraps callables". The docblock says the CLI lowers callables BEFORE the stack is parsed, but defineStack in the author's own config parses first (premise 2).

Premises (each with its re-check, at origin/main 49161683fb)

  1. Jobs have no inline handler form. An inline function was never accepted for a job.
    • git grep -n "handler: z.string()" origin/main -- packages/spec/src/system/job.zod.ts → :193, 1 hit.
  2. The callable the build sees for a functions entry is zod's wrapper, not the author's function.
    • git grep -n -E "z\.function\(" origin/main -- packages/spec/src/automation/flow-function.zod.ts → FlowFunctionDeclarationSchema.handler (:183) and FlowFunctionEntrySchema's first arm (:266).
    • Measured by the dev with the pipeline pin defineStack → normalizeStackInput → lowerCallables → ObjectStackDefinitionSchema at 781609d2f7. The parse succeeded, job.body was undefined, and the recorded extraction warning was free-identifiers [func, inst, parse]: zod's identifiers, not the author's.
  3. The documented job handler form would lower into a body that throws.
    • git grep -n "JobHandlerContext" origin/main -- content/docs/automation/jobs.mdx → :156 to :170: async function sweepProjectHealth({ ql, jobId, logger }: JobHandlerContext).
    • A sandbox L2 body runs as (async (ctx) => { … }) with ctx.api / ctx.log. A destructured { ql, jobId, logger } extracts into a body reading unbound names. Because body wins over handler, it would replace a working handler.
  4. Zero measured pull.
    • git grep -l -E "^\s*jobs\s*:" origin/main -- 'examples/**/*.ts' 'packages/apps/**/*.ts' → examples/app-showcase/objectstack.config.ts only. Its function uses module-scope helpers and ql, so it would not lower under any option.
    • The director's E record measured zero jobs in cloud, hotcrm and objectui.

The question

The ruled lowering cannot run as written. Should it be withdrawn, so that a job body is authored as data, or kept by changing a public contract so the build can see the job's function?

Options

What it does What a customer or author sees
C. Withdraw the build lowering A job body is written as data ({ language: 'js', source, capabilities }), the form Studio, an AI author and a JSON artifact all write. os build only validates. handler stays deprecated until #21489 binds bodies. One way to write a portable job: a body. A job still on handler keeps working on os start --artifact, and install-local refuses it loudly (C of the ruling, in #21489).
A. Make the named function readable FlowFunctionEntrySchema / FlowFunctionDeclarationSchema.handler switch from z.function() to an identity-preserving function check (same accept set, no wrapper). The build mints a job body from the named function behind a job-context guard (in branch history) and still refuses JobHandlerContext-shaped functions. A job function written for the sandbox's ctx is converted. The documented ({ jobId, ql, logger }) form is refused at build, so the author rewrites it anyway. One function now serves two contexts, in-process and sandbox.
B. Give jobs the hook mechanism literally JobSchema.handler also accepts an inline function (DEPRECATED, as hooks). The build lowers it to body plus a ref, the runtime module bundles it, and AppPlugin binds inline functions on a config boot. Mirrors hooks. It adds an authoring form to a deprecated key, and a runtime binding change that overlaps #21489. It has the same two-context trap as A.

Business meaning:

  • C: like a spreadsheet macro stored as text in the file, so it travels with the file. You write the macro text, not a pointer to code on your own machine.
  • A / B: the platform tries to copy code from your machine into the file at save time. It only works when that code was written to run in the file's sandbox, and the common way to write it today is not.

四维分析

os-decision-facets

  • ① 项目长远合理性: C 只留一种可移植写法——body 即数据,与 hook/动作的元数据形态一致,符合 ADR-0088「函数是代码、不是元数据」;A、B 都要让同一个函数同时满足进程内 JobHandlerContext 与沙箱 ctx 两种上下文,这是结构性陷阱,守卫只能拒绝、不能修复。
  • ② 实际业务拉动: 零——四仓只有 examples/app-showcase 声明 job,且其函数用模块作用域辅助函数与 ql,任何方案都转不了;自动转换没有一个可被它服务的实测作者。
  • ③ 防 AI 犯错: C 下严格 schema(仅 L2、超时一处、二选一必填)就是全部契约,写错在解析时响亮拒收;A、B 引入构建期代作者生成的 body 与一条拒绝路径,AI 要理解「何种函数可被转换」,出错面更大。
  • ④ 创业阶段不扩散: C 什么都不加;A 改动流程函数契约(无拉动),B 新增一种已弃用键的写法外加运行时绑定改动,是三者中最宽的。

Prior rulings read: job body,job handler,lowercallables,build lowering,inline handler,sandboxed body → 0 hits; none; thread: 1 ruling(s) (5964305303); repo: objectstack-ai/objectstack

推荐:C。 终态句:两年后,应用包里任何服务端代码(hook、动作、定时任务)都是作者写下的沙箱代码体数据,任何安装门都能跑;构建不替作者从本机代码里「抠」出代码体。只看①选 C;②③④ 是否翻转:否(②零拉动、③响亮拒收、④零新增,均同向)。回退: A,且须等 #21489 先决定进程内 job 处理函数是否获得 ctx.api / ctx.log,否则 A 转出的代码体与进程内函数仍是两套上下文。置信缺口: 前提 2 由 dev 的管线测试在 781609d2f7 实测,本席未独立复跑;仓外(cloud、hotcrm)无 job,由 E 裁决记录实测,本席未重测。

After the ruling

Related

Ruling E 5964305303 · #21515 · PR #21538 · the dev report 5965564062 · #21489 (the runtime binder, domain:cli) · ADR-0088 · automation/flow-function.zod.ts.

Dedupe: the 100 most recently updated open issues here were listed by REST and grepped for job body, lowerCallables and job handler with lower: the only hits are #21515, PR #21538 and #21489, the source. No card asks this question.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #273 item 1 · letter C · maintainer 「同意c」 2026-10-03T12:01Z

    Director seat, summon #32, session_016tKoy8NJa35Yih1FdzrVmn. Written as objectstack-fleet[bot] through the relay.

    The ruling

    C: the build lowering is withdrawn. A job body is authored as data.

    Not taken:

    四棱(本裁决新记录)

    • ① 长远:应用包里的服务端代码只有一种可移植写法——沙箱代码体数据;构建不替作者从本机代码里抠代码体,与 ADR-0088「函数是代码、不是元数据」一致。
    • ② 拉动:零——四仓只有 examples/app-showcase 声明 job,且其函数在任何方案下都转不了。
    • ③ 防 AI:严格 schema(仅 L2、超时一处、二选一必填)即全部契约,写错在解析时响亮拒收;没有「哪种函数可被转换」的隐含规则。
    • ④ 不扩散:不加机制、不改 functions 契约、不给弃用键加写法。
    • 只看①选 C;②③④ 是否翻转:否。

    Execution (ruled here; no further decision card)


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions