skills(automation): state the wall-clock unit on the escalation row, and teach the hook-side runAs - #15681
Conversation
…hook-side `runAs` paragraph Two localized additions to the published automation skill. 1. The `escalation` row of the per-node table now states the unit with the declaration: `timeoutHours` is calendar (wall-clock) hours — nights, weekends and holidays count, and the platform ships no business-hours calendar. Mirrors the `ApprovalEscalationSchema.timeoutHours` describe, so the schema and the skill read the same. 2. The "Failure routing & `runAs`" section carried flow-side `runAs` only. It now also states the hook-side rule: a hook declares its own `runAs: 'system' | 'user' | 'inherit'` (default `'inherit'`, the context of the write that fired it), scoping that hook's `ctx.api` data operations only on both the in-process handler and the sandboxed body; a `'user'` hook with no trigger user has its data operations refused rather than run unscoped; `sudo` is not a hook key. Every fact is read off `HookSchema.runAs` in `packages/spec/src/data/hook.zod.ts`. No other file moves; `skills/objectstack-data/references/data-hooks.md` is deliberately untouched. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
|
ACCEPT — governed in-seat review of PR #15681, head Implemented-by: Verified in a detached compare worktree at the PR head against merge-base Landing regime: governed ( Generated by Claude Code |
Part of #14483
Fixes #14966
Two localized additions to one published skill file —
skills/objectstack-automation/SKILL.md.skills/**is a governed surface, so this stays draft for a human merge.Part of #14483and not a closing keyword on purpose: that card carries two teaching points, and only point 1 lands here. Point 2 belongs inskills/objectstack-data/references/data-hooks.md, which is in flight on the spec lane's draft PR #15626 — the card stays open for it.Addition 1 — the wall-clock unit on the escalation row (#14483, teaching point 1)
Ruling of record: maintainer 2026-08-31, mechanism B — the wall-clock unit is part of the declaration, not ambient prose. One clause, appended to the
escalationcell of theApprovalNodeConfigSchematable, immediately after the declaration it qualifies.Before
After
Net lines: 0 — the row is one line before and one line after. Budget for this point was ≤2 lines net.
Every word is read off
ApprovalEscalationSchema.timeoutHours(packages/spec/src/automation/approval.zod.ts:633), whose describe already reads "Calendar (wall-clock) hours before escalation triggers — nights, weekends and holidays count. The platform ships no business-hours calendar…". Schema and skill now say the same thing.Addition 2 — the hook-side
runAsparagraph (#14966)The「Failure routing &
runAs」section taughtrunAsfor flows only. An author wanting to elevate a hook found no declared route in the published skill — and the only route it left visible,ctx.api.sudo(), is a TypeError in the sandbox. One paragraph, placed after the flow-siderunAsmaterial (thereadonly/ elevate-the-write / organization-pin blockquotes), in the neighbours' voice and wrap.Before — nothing between the organization-pin blockquote and
### Filter tokens (config.filter).After
Net lines: +9 (8 lines plus the blank separator).
Each fact verified against this tree, not against the card:
'inherit'packages/spec/src/data/hook.zod.ts—runAs: z.enum(['system','user','inherit']).default('inherit')'inherit'= the context of the write that fired the hookrunAsTSDoc and describectx.apidata operations onlyhandlerand the sandboxedbodypackages/objectql/src/hook-wrappers.tswrapDeclarativeHook)"'user'with no trigger user ⇒ data operations refusedpackages/objectql/src/hook-run-as.ts—HOOK_UNSCOPED_DATA_ACCESS, status403, thrown at the data door (the hook itself still runs)sudois not a hook keyHookSchemaguidance: "sudois not a hook key. DeclarerunAs: 'system'…"The paragraph states the rule and nothing else: no incident narrative, no PR or issue ids in the published text (
check:doc-authoringrule 2).Ratchet readings — the mechanical budget
The published catalog carries no line ratchet (
scripts/pm/check-skill-line-ratchet.mjsexcludes it by design); the token ratchet is the budget.skills/objectstack-automation/SKILL.mdtokens (ceiling 12768)skills/objectstack-automation/SKILL.mdlinesNo ceiling was raised and no other text was trimmed to pay for these.
Scope
skills/objectstack-data/references/data-hooks.mdis untouched — it is in flight on PR #15626, and #14483's teaching point 2 lands there afterwards. No other skill, and no other file of any kind, is touched: the diff is one file, 10 insertions / 1 deletion.skip-changeset: nothing is published from a package by this diff.Gates — every verdict, exit code captured before any pipe
Family derived on this branch with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(21 commands; the tool read the change set from git itself). All green.check-ci-filter-paritycheck-closing-keyword-paritycheck-closing-keyword-parity --self-testcheck-comment-mask-corpuscheck-skills-token-ratchetskills/objectstack-automation/SKILL.mdis 12491 tokens (ceiling 12768; headroom 277); 36 authored bundle file(s) within their ceilingscheck-skills-token-ratchet --self-test@objectstack/lint check:doc-formula-expressions@objectstack/formulaand@objectstack/lintunbuilt; re-run green after building both — the 3 measured nothing)@objectstack/spec check:skill-docsskills/README.md,content/docs/ai/skills-reference.mdxin synccheck:agent-test-spellingcheck:corpus-claim-driftcheck:cross-package-test-inputscheck:doc-authoringcheck:driver-memory-censuscheck:nul-bytescheck:pm-governed-mergescheck:refd-timer-probecheck:role-wordcheck:skill-compatibilitycheck:skill-frame-synccheck:skill-identifier-livenesscheck:watch-hint-literalThree more run beyond the derived family, because a
SKILL.mdbody changed:@objectstack/spec check:skill-refscheck:pm-skill-ratchetcheck:skill-frame-freshnessorigin/mainpnpm lint— a measured narrowing, not a skip. ESLint's own configuration supplies no matching configuration for this path:pnpm exec eslint --no-inline-config --format json skills/objectstack-automation/SKILL.mdreturns exactly one result whose only message is "File ignored because no matching configuration was supplied" — 1 file addressed, 0 rules applied. The diff adds no TS/JS file and edits no eslint config, tsconfig or source, and type-aware linting is not in play here, so no untouched file's verdict can move either. A repo-wideeslint .would read zero lines of this change; CI runs it regardless.All gate readings above were taken on
abbbc3cc8, which is this branch's head.🤖 Generated with Claude Code
Generated by Claude Code
Generated by Claude Code