Skip to content

docs(skills): teach the calendar-hours rule where a hook stamps a deadline - #16591

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-14483-data-hooks-calendar-hours
Sep 7, 2026
Merged

docs(skills): teach the calendar-hours rule where a hook stamps a deadline#16591
os-zhuang merged 2 commits into
mainfrom
claude/issue-14483-data-hooks-calendar-hours

Conversation

@os-musk

@os-musk os-musk commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Fixes #14483

The last outstanding teaching point of that card: the hook-deadline rule in the published
data skill's hooks reference. Teaching point 1 (the escalation row's calendar-hours clause)
landed earlier in PR #15681; this branch is point 2 and touches nothing else.

What changed

skills/objectstack-data/references/data-hooks.md+4 lines, 0 deletions, one file.
A three-line note closing the Sandbox restrictions subsection of
## Sandboxed Hook Bodies (body) — What the Sandbox ctx Can Call:

A deadline stamped from an hours table is calendar hours — nights, weekends and
holidays count. Name it *_CALENDAR_HOURS (SLA_CALENDAR_HOURS, or its camelCase)
and add elapsed milliseconds (hours * 3_600_000); never label it "business hours".

It carries all four elements the card specifies for point 2: the unit (calendar hours),
the naming convention (the *_CALENDAR_HOURS suffix pattern, with one concrete table as
the parenthesised example), the arithmetic (hours * 3_600_000), and the refusal to label
wall-clock arithmetic "business hours".

Review round 1 moved the naming rule from a concrete name to the pattern. A published
skill is copied verbatim by the agents that read it, so leading with SLA_CALENDAR_HOURS
would have handed that same name to hours tables that have nothing to do with an SLA. The
pattern now leads and the concrete name is demoted to an example — same three lines, same
+4 net, no ceiling touched.

Ruling of record

Maintainer 2026-08-31, mechanism B (quoted verbatim, untranslated): 「其他同意」 — the
clock unit is part of the declaration, not ambient prose; the platform ships no
business-hours calendar; the rule is taught where an AI writes the next hours table.
The wording deliberately mirrors what the schema already says, so the two read the same:
ApprovalEscalationSchema.timeoutHours in packages/spec/src/automation/approval.zod.ts
describes "Calendar (wall-clock) hours … nights, weekends and holidays count. The platform
ships no business-hours calendar", and its TSDoc spells the arithmetic hours × 3_600_000.

Placement — measured on this tree, not taken from the card

Three readings decided where the note goes:

  1. No schema reaches this surface. packages/spec/src/data/hook.zod.ts declares no
    hours, deadline or calendar key at all — its only time key is timeoutMs, the
    millisecond budget after which the hook run is aborted. So there is no producer-side
    describe that could carry this rule and no generated _index.md that already states
    it; a hand-authored line in this reference is the only place an author meets it. The
    card's premise holds.
  2. timeoutMs is a different concept and was rejected as the anchor. Putting a
    business-deadline rule beside the VM's abort budget invites exactly the conflation the
    rule exists to prevent.
  3. The chosen anchor is where a body's clock primitives are named. The restrictions
    list says Date is available, setTimeout is not, and any hours table must be inlined
    because free identifiers at module scope are rejected by objectstack build. A hook
    that stamps a deadline therefore does Date arithmetic over an inlined table — the note
    sits directly under that list.

Readings (governed-surface convention: whole-file and whole-package, before → after)

Tokens are the ratchet's own convention, ceil(utf8 bytes / 4), measured from git at both
revisions; before = ffca0df9 (branch base), after = 3fcc79e1 (final commit, review
round 1 included).

Reading Before After Note
data-hooks.md — tokens 9676 9741 ceiling 12611; headroom 2935 → 2870
data-hooks.md — lines 979 983 +4 net, the whole budget, nothing left over
skills/objectstack-data bundle, 11 authored .md — tokens 36365 36430 references/_index.md excluded: generator-owned, measured but not ratcheted
skills/objectstack-data bundle — lines 3596 3600
all 11 published SKILL.md — tokens 79687 79687 unchanged — no SKILL.md is touched
all 11 published SKILL.md — lines 6858 6858 unchanged

check:skills-token-ratchet prints the file's own verdict line:
skills/objectstack-data/references/data-hooks.md is 9741 tokens (ceiling 12611; headroom 2870), and 36 authored bundle file(s) within their ceilings. No ceiling was raised, and
the change is paid entirely out of existing headroom. The review-round reword cost 5 tokens
and 0 lines.

One baseline in the card does not reproduce: it cites 10183 for "all SKILL.md files", where
today's measurement over the 11 published SKILL.md files is 6858 lines / 79687 tokens.
Reported as drift in the card's stated baseline; no action taken on it here.

Trade-offs, along the four axes

Only one call needed the frame: what to cut to fit three lines of prose into the +4-line
budget. The dropped clause is "the platform ships no business-hours calendar"; what stayed is
the unit, the naming rule, the arithmetic and the prohibition.

  • Real business need — measured, not assumed: the misread this prevents has already
    happened once in a reference app's hook body, and it happened as arithmetic, not as a
    missing sentence about platform capabilities. The surviving text is the half an author
    acts on while typing.
  • Long-term soundness — the sentence that was cut is not lost to the platform: it is
    already carried by the schema describe and by the automation skill's escalation row,
    where a capability question belongs. Repeating it here would have been a third copy to
    keep in sync.
  • Stopping AI from writing bad metadata — the naming rule is the structural half, and
    review round 1 sharpened exactly this axis: a suffix pattern generalises to every hours
    table an agent writes, where a single fixed name would have been copied verbatim onto
    tables it does not describe. A table matching *_CALENDAR_HOURS cannot be silently read
    as business hours by the next author or the next agent, which prose elsewhere cannot
    achieve.
  • Startup-stage focus — the budget is the ruling (「不能为了一个小功能扩写很多」,
    maintainer 2026-08-21, the ruling this ratchet enforces). Four lines were granted and four
    were used; no ceiling was raised, and no wording was expanded to make room.

Two smaller calls, both measured: the note uses the file's own idiom for a prohibition
("never", bold lead-in) rather than the ⛔ marker, because this file contains zero ⛔ in 979
lines; and 3_600_000 keeps the separators the schema's own TSDoc uses, so the skill text
and the contract text are byte-comparable — the file had no ms-arithmetic precedent of its
own to follow.

Verification

Re-run in full on the final head 3fcc79e1 after the review-round reword — not carried over
from the first sweep.

  • Gate family derived, not recalled: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack → 21 commands (byte-identical to the round-1
    derivation); all 21 run, all exit 0 on the first sweep this round, exit codes captured
    before any pipe. --ran reconciliation: 21 derived, 21 run, 0 NOT-MEASURED, 0 UNRUN.
  • Named verdict lines: check-skills-token-ratchet as quoted above; check:doc-authoring
    46 published skill files clean — no internal issue-id references and 15062 customer-facing string(s) across 865 spec sources clean; check:skill-identifier-liveness,
    check:skill-compatibility, check:skill-frame-sync, check:nul-bytes,
    check:pm-governed-merges all exit 0. Beyond the derived family: check:skill-docs (no
    regeneration owed), check:pm-skill-ratchet and check:skill-frame-freshness also exit 0.
  • Governed predicate probed deliberately: node scripts/pm/check-governed-merges.mjs --test skills/objectstack-data/references/data-hooks.md exits 3 — GOVERNED — a human merge is the review record.
  • Control bytes: grep -naP over the edited file for the C0/DEL range exits 1, no match.
  • pnpm lint is a measured narrowing, not a skip, on three pieces of evidence: (1) the
    population comes from eslint's own config — eslint.config.mjs matches only
    ts,tsx,mts,cts,js,jsx,mjs,cjs and contains no markdown entry at all; (2) the count comes
    from --format json on the changed file — 1 result, 0 errors, 0 rules applied, the single
    warning being "File ignored because no matching configuration was supplied"; (3) the
    invariance claim — the diff adds no TS/JS file and edits no eslint config, tsconfig or
    source, and type-aware linting is not in play (eslint.config.mjs states it carries no
    parserOptions.project and no typed rules), so no untouched file's verdict can move.
  • Serial constraints: a driver-less bare-clone git merge-tree --write-tree --name-only
    against the current origin/main (b4abb0a9, re-fetched after the reword) is clean. At
    round 1 all 21 open PRs were fetched by refs/pull/N/head and diffed against their own
    merge bases for skills/objectstack-data/** — zero hits, so no open PR holds this file.
  • The reword landed as a fast-forward push (270145f6..3fcc79e1) — no amend, no force.
  • No ablation applies: nothing executable changed.

维护者速读(草稿)

改了什么

发布给客户的数据技能包里,写 hook 沙盒那一节末尾加了三行规则:从小时表盖出来的截止时间是
日历小时
(夜里、周末、节假日都算),表名要落在 *_CALENDAR_HOURS 这个后缀模式上
(SLA_CALENDAR_HOURS 只是括号里的例子),算术是加毫秒(hours * 3_600_000),并且不许把
这种墙钟算术标成 "business hours"。一个文件、净增 4 行、没删任何东西、没动别的技能文本。

为什么改

这条规则的另一半(审批升级的 timeoutHours 是日历小时)已经在 schema 和自动化技能包里落
地了。但真实事故发生在 hook 体里 —— 参考应用把日历小时当工作小时写进了 hook 代码,而
hook 体是没有任何 schema 能管到的地方:平台的 hook 契约里根本不存在"小时"这个键。于是这条
规则只能教在"AI 下一次写小时表的那个位置",也就是这份沙盒参考文档。这是维护者 2026-08-31
裁的机制 B(消费端收敛,零契约变更)的最后一块。

风险与代价(含回滚)

风险极低:纯文档,没有代码、没有 schema、没有运行期行为变化。代价是客户上下文窗口里多了 65
个 token(该文件 9741 / 上限 12611,余量还有 2870),全部用既有余量支付,没有抬高任何棘轮
上限
。回滚就是 revert 这两个 commit,不牵连任何其它文件或 PR。唯一的判断题是措辞取舍:为
了守住 +4 行预算,砍掉了"平台不发运工作日历"这半句 —— 它在 schema 的 describe 和自动化技能
包里都还在,这里不重复第三遍。

席位意见

(留空,待席位 ACCEPT 时填写)

你要做的

这是受管面(skills/**),只能人工合并:本 PR 保持 draft、未入队、未挂 auto-merge、未
提交任何 review。请审阅这三行文案是否是你要教的口径,尤其是 *_CALENDAR_HOURS 这个命名模式
和被砍掉的那半句;认可后由你手动合并。合并即关闭该卡(两个教学点都已落地)。

…dline

The data-hooks sandbox section now carries the hook-deadline rule: a deadline
stamped from an hours table is calendar hours, the table is named
`*_CALENDAR_HOURS` (or its camelCase), the arithmetic adds elapsed milliseconds,
and wall-clock arithmetic is never labelled "business hours".

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018dxq7YqsLDMeZDZ5AzsgJX
…not one name

Review point: the rule read `SLA_CALENDAR_HOURS`, a concrete table name, where the
spec is a naming pattern. A published skill is copied verbatim by agents, so a
non-SLA hours table would have inherited the SLA name. The pattern now leads and
the concrete name is the parenthesised example. Same three lines, same +4 net.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018dxq7YqsLDMeZDZ5AzsgJX

os-musk commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator Author

维护者速读(终稿 · skills 席,2026-09-07T11:4xZ)

  • 改了什么:发布给客户的数据技能包 skills/objectstack-data/references/data-hooks.md,沙盒限制清单末尾加三行规则:从小时表盖出来的截止时间是日历小时(夜里、周末、节假日都算);表名用 *_CALENDAR_HOURS 模式(示例 SLA_CALENDAR_HOURS,或其 camelCase);算术是加毫秒(hours * 3_600_000);不许把墙钟算术标成 "business hours"。一个文件,净增 4 行,没删任何东西。
  • 为什么改:这是你 2026-08-31 裁的机制 B(消费端收敛、零契约变更)的最后一块。审批升级的 timeoutHours 那一半已经在 schema 与自动化技能包里落地;真实事故发生在 hook 体里 —— 参考应用把日历小时当工作小时写进了 hook 代码,而 hook 契约里根本没有"小时"这个键,没有任何 schema 能管到,只能教在 AI 写下一张小时表的位置。
  • 风险与代价(含回滚):纯文档、零运行期行为。代价是客户上下文多 65 token(该文件 9741 / 12611,余量 2870),全部用既有余量支付,没有抬任何棘轮上限;整包 total 36365 → 36430。回滚 = revert 两个 commit。措辞取舍:为守 +4 行预算砍掉"平台不发运工作日历"半句 —— 它在 schema describe 与自动化技能包里都在,这里不重复第三遍。
  • 席位意见:席位亲核了 diff(一个文件 +4/−0)、行数与字节(38700 → 38962 字节)、受管判定(exit 3)、与 origin/main b4abb0a9 的 merge-tree 干净。补丁轮把规则从一个具体表名改成后缀模式(*_CALENDAR_HOURS),这样非 SLA 的小时表不会被 agent 逐字照抄成 SLA_CALENDAR_HOURS。三行与 schema 的 describe 同口径。CI 31 项 11:47Z 无红(Lint & Repo Gates、Type Check 与两片 Test Core 仍在跑,合并前会收敛)。建议直接合并。
  • 你要做的:确认 *_CALENDAR_HOURS 这个命名口径与被砍掉的那半句,然后人工合并本 draft PR(受管面不进合并队列);合并即关闭 skills: teach the wall-clock rule where the next hours table is written — one clause on the approval escalation row, a hook-deadline rule in the data-hooks reference (+4 lines; skill limb of objectstack#13801, mechanism B) #14483(两个教学点都已落地)。

Generated by Claude Code

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

Labels

documentation Improvements or additions to documentation needs-user-decision size/xs skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants