Repository navigation
An app package cannot ship an inbound api flow: config.secret must be a literal in source, and there is no deployment-time seam for a package-authored flow #25675
Description
Ruled: 6386630545 · batch #12 item 3 · letter A · 2026-10-24T16:24:48Z (an api flow's start node may declare a secret reference the deployment fills; os validate accepts it, the engine resolves per post via the #20790 store / resolveSecret; spec S p1 + server M)
Filing gate ② — a maintainer decision (a new authoring surface for package-shipped inbound hooks).
Filed for the hotsrm app (objectstack-ai/hotsrm, a public metadata-only SRM app on 18.0.0) by session session_01EGq2qAS1FBx325AAkDLPDH, while building its SCAR overdue sweep (hotsrm issue #37).
维护者速读
A public app package that wants an external scheduler to call one of its flows has no way to ship that flow. type: 'api' needs a non-blank literal config.secret on the start node or os validate refuses the package (flow-api-trigger-secret-missing) and the engine refuses the flow at registration. A literal secret in a public repository is a published credential. The write-only flow credential store (#20790) is reached only through the /meta save door, so it does not help a package built with os build. The decision asked for: how a package-authored api flow declares "the secret is supplied by the deployment".
What happens (measured)
- hotsrm declares
srm_scar_overdue_heartbeat(type: 'api',runAs: 'system', start node withoutconfig.secret) that calls the hourly schedule flow as a sub-flow, so an external scheduler can run the sweep every 10 minutes. os validate(18.0.0) refuses it:flow "srm_scar_overdue_heartbeat" › start node: binds the inbound api trigger (type: 'api') but its start node declares no config.secret, so the engine refuses to register the flow(flow-api-trigger-secret-missing).- The alternatives are closed too:
POST /api/v1/automation/:name/triggeron therunAs: 'system'schedule or autolaunched flow answers 403 (refusesElevatedSelfTriggeredStart,SELF_TRIGGERED_FLOW_TYPES= autolaunched / record_change / schedule). - The showcase flow says "Demo secret — real deployments inject this from configuration" (
examples/app-showcase/src/automation/flows/index.ts), but no injection seam exists for a package (also noted in trigger-api arms a flow's inbound hook without a secret and accepts unsigned posts; ADR-0041's trigger-api acceptance criteria name a per-flow secret and HMAC verification #20529, closed).
Business reason
- Who benefits: every app package with a time-critical scan that its deployment wants to drive from an external scheduler (ERP middleware, ops cron). hotsrm has three by design: SCAR overdue (now), bid-close (v0.7), PO reminder (after ERP integration). The design rule is "a scheduled job may be late or skipped, so time-critical sweeps also expose an api trigger as a heartbeat".
- Without it: the package ships only the hourly schedule, and
on hosted cloud scheduled work is off by default (service-automation: a hosted environment has no way to enable(corrected by the director 2026-10-24: hosted Team+ plans already run scheduled flows per plan, service-automation: a hosted environment has no way to enableschedule/time_relativeflows for an installed app (OS_AUTOMATION_SCHEDULED_WORK_ENABLED is deployment-wide and off on cloud) #25442), so the heartbeat is the only reliable driver thereschedule/time_relativeflows for an installed app (OS_AUTOMATION_SCHEDULED_WORK_ENABLED is deployment-wide and off on cloud) #25442 ruled D; only Free is off, so the heartbeat is a reliability extra, not the only driver).
Options (for the maintainer, not a ruling)
- A. The start node may declare a secret reference instead of a value (for example a
sys_secret/ flow-credential name, or an env var name), which the deployment fills;os validateaccepts the reference and the engine resolves it per post, likeresolveSecretalready does for flows saved through/meta. - B. Allow an authenticated, permission-gated start of an elevated flow through
/automation/:name/triggerwhen the flow opts in (a declared capability the caller must hold), so a service account can drive it without HMAC. - C. Keep today's rule and document that package-shipped inbound hooks are unsupported; packages publish the flow without the api trigger and each deployment adds its own signed wrapper in Studio.
四棱
- 实际业务需求:公开应用包需要「外部调度器心跳」驱动时效敏感的扫描(
托管云定时任务默认关闭,service-automation: a hosted environment has no way to enable托管云付费套餐已放行定时流程),今天包里无法声明。schedule/time_relativeflows for an installed app (OS_AUTOMATION_SCHEDULED_WORK_ENABLED is deployment-wide and off on cloud) #25442 - 项目长远合理性:密钥属于部署,不属于包;包与部署之间缺一个引用缝,后续任何包内 api flow 都会撞同一堵墙。
- 防 AI 写错:AI 作者按示例照抄字面密钥最省事,公开仓库会因此泄露凭据;给出引用写法即可堵住。
- 创业阶段不扩散:复用 security(flows): move a flow's inbound-hook secret out of flow metadata into the write-only secret seam #7799 established — no read, the generic data door included, returns it #20790 已有的写入式凭据存储与
resolveSecret,只加一个引用键。
Reader who acts: the triage seat grades it and puts it to the maintainer as a decision (needs-user-decision).
Dedupe: search_issues on objectstack-ai/objectstack, open and closed — api trigger secret env (1 hit: #20529, closed, unsigned posts), "config.secret" flow inject (5 hits: #24530, #20790, #20552, #20529, #20611 — all about storing/redacting a secret for flows saved through /meta, none about package-shipped flows), api flow secret package deployment credential store literal (8 hits, none on this). No carrier.
hotsrm keeps the gap as a documented, temporary omission (hourly schedule only) until this is decided.
Activity
objectstack-fleet commentedon Oct 24, 2026
Triage: first touch —
enhancement·security·priority:p2·domain:spec·area:workflow·needs-user-decision· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-24T16:00Z⛔ Not a claim, ⛔ not a dispatch.
apiflow says "the deployment supplies the secret". It is a new authoring key on a security boundary, so it is on the human floor.resolveSecret. A secret belongs to the deployment, not to the package. A publishes no credential and reuses what exists. B opens an authenticated start on elevated flows, which is a wider door. C leaves every public package without a heartbeat on hosted cloud, where scheduled work is off on Free (service-automation: a hosted environment has no way to enableschedule/time_relativeflows for an installed app (OS_AUTOMATION_SCHEDULED_WORK_ENABLED is deployment-wide and off on cloud) #25442). Workload for A: spec S (one reference key, protocol addition, p1 if ruled) plus server M (resolve per post,os validateaccepts the reference).Generated by Claude Code
os-elon-musk commentedon Oct 24, 2026
Ruling: batch #12 item 3 · letter A · maintainer 「25635 25647 25675 同意」 2026-10-24T16:24:48Z
A =
api型流程的起始节点可以声明密钥引用(流程凭据名 / 环境变量名)而不是字面值;部署方填值,os validate接受引用,引擎每次入站请求按引用解析,复用 #20790 已有的写入式流程凭据存储与resolveSecret。公开仓库里不再出现任何凭据。工作量:
domain:specS(一个引用键,协议新增 → p1 按分诊口径)+domain:serverM(校验接受引用、按次解析、引用缺值时的拒绝)。前提修正已记在简报:托管云 Team 及以上套餐今天已按套餐放行定时流程(#25442 改裁 D),所以 hotsrm 心跳的紧迫性低于卡面;hotsrm 在此之前维持小时级定时。B(能力门控的 trigger 启动)与 C(维持不支持)未选。维护者动作已完成:
needs-user-decision→pm:queue;裁不派。批 #12 简报:https://claude.ai/artifact/YLrSzMXcnWVmbkZCxgxcQtGenerated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Claim: PM loop round 2
Session:
session_01PC4NPXPgPmpDRG9DdDnyXTAccount:
sunhaolin(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-25675-api-flow-secret-refWorktree:
objectstack-issue-25675Domain:
domain:specSeat:
domain:spec#2(seat post #18549)File surface: one cross-domain PR. The server charter's red line ("触
packages/spec的卡一律归 spec 席") puts it in this lane; the cross-lane note goes to seat post #6021.(1)
packages/spec: on anapiflow's start node, a secret reference the deployment fills (a flow-credential name or an environment-variable name) in place of a literalconfig.secret. The dev locates the declaring schema and names it. Also its tests and regenerated shards.(2)
packages/lint:flow-api-trigger-secret-missing(authoring-rules.tsabout:1409–:1472,validate-flow-trigger-readiness.ts) accepts the reference.(3)
domain:server: the engine resolves the reference per inbound post through the existing write-only flow credential store (#20790) andresolveSecret(packages/services/service-automation/src/flow-credential-channel.ts,engine.ts, the api-trigger secret channel), and refuses a post whose reference has no value.(4) The showcase "Demo secret" comment (
examples/app-showcase/src/automation/flows/index.ts) if the reference replaces it, plus tests and one changeset.changeset/25675-api-flow-secret-ref.md.(stop on breach; explain in the report)
Container & model:
L,mode:cloud(spec plus lint plusservice-automation: build-heavy, one card per container),model: default (TIER_DEFAULT; dispatch-gates --tier for this surface: no path-derived mandate),effort: xhigh(security; a credential boundary).Clause-②: yes⇒ aCONTRACT_REVIEW_TIERcontract review is owed before the queue.Clause-②: yes (widening)
Metadata-protocol: added
Responsibility: n/a — not a defect card
Ruling-ref:
6386630545(batch #12 item 3, letter A, maintainer 「25635 25647 25675 同意」)Thread-read: 6386630545
Serial constraints cleared: none. No open PR touches these files (all 15 open PRs' file lists read 2026-10-24T16:43Z), and no open
pm:dispatchedclaim declares them. PR #25643 (#25580,f1ddace887) changed the flow start doors inruntime, and it has landed.Premise: holds on
origin/main6021b5e837.flow-api-trigger-secret-missingrefuses anapistart node with no non-blank literalconfig.secret(validate-flow-trigger-readiness.ts,authoring-rules.tsabout:1409). The flow credential store is reached only through the/metasave door (flow-credential-channel.ts), so a package built withos buildcannot ship anapiflow without publishing a literal.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Dispatched · round 2 ·
domain:specseat 2 · 2026-10-24T16:48Zmode:cloud, cloud sessionsession_01NZJKktPWaz2EZJ95a9XZa8(created by this seat's session throughcreate_session), on branchclaude/issue-25675-api-flow-secret-ref. One cross-domain PR; the server surfaces are declared on seat post [PM seat] domain:services — ⏳ vacant · archive of domain:server seat 1 (#6367) #6021. The claim6386845783stays the identity.6386630545, letter A) with its premise correction, the lint and engine leads, the redaction question, the security floor (an unfilled reference refuses, no literal credential in the repository), the consumer-package test list, theWrites:line.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
os-dev-report
{ "issue": 25675, "status": "done", "branch": "claude/issue-25675-api-flow-secret-ref", "pr": "https://github.com/objectstack-ai/objectstack/pull/25703", "session": "session_01NZJKktPWaz2EZJ95a9XZa8 (dispatched by session_01PC4NPXPgPmpDRG9DdDnyXT)", "premise_still_valid": true, "summary": "Premise re-measured on origin/main 6021b5e: it holds. flow-api-trigger-secret-missing and the engine's validateApiTriggerSecret both require a literal config.secret, and the flow credential channel is keyed by position with the /meta save door as its only write door, while flow has allowEnvironmentOverlay:false, so a packaged flow cannot reach it. Implemented ruling A. (1) spec adds start-node config.secretRef (ApiTriggerSecretRefSchema: [A-Za-z_][A-Za-z0-9_]* and not OS_, both as patterns). FlowSchema refuses a malformed reference, one beside a literal secret, and one on a flow that binds no api trigger. apiTriggerSecretRefOf is the shared reader. (2) lint accepts a usable reference, and the remedy and os explain name it. (3) service-automation resolves the name at every post through the plugin's existing credentialResolver (the connector credentialRef seam, ADR-0097 §3: env var in the open tier), passed to the engine as the new AutomationEngineOptions.credentialResolver. An unfilled name rejects with FlowCredentialUnresolvableError, which trigger-api answers 503 and logs with the flow and the reference. The channel gains a 'referenced' position form, so saving a referencing flow removes a stale held hook secret. (4) trigger-api messages. (5) The showcase literal 'showcase-webhook-secret' is replaced by secretRef 'SHOWCASE_WEBHOOK_SECRET' (0 git grep hits left outside CHANGELOGs), and the QA checklist, docs, the automation skill row and the changeset are updated. Not subscribed to PR activity: the role file's contract overrides the platform's subscription posture.", "tests": "HEAD 8a23b3ad (merged origin/main b77bb37e). Package suites through os-verify-lock: spec test exit 0; spec test:repo exit 0 (79/79 files, 1269/1269 tests) after a fix: the first run was red, file-description.test.ts expected 31 demoted descriptions and got 32 because the new module's docblock opened with a level-1 heading, so the docblock was changed and the pin left alone; lint 0; trigger-api 0; service-automation 0; metadata-protocol 0; runtime test 0; runtime test:repo 0; rest test:repo 0. Typecheck: spec, lint, service-automation and trigger-api 0; example-showcase NOT MEASURED (10 TS2307 from unbuilt workspace packages service-print, plugin-approvals and connector-rest, none in a changed file), and the showcase start node parses with FlowSchema against the rebuilt spec (true). New pins: spec api-trigger-secret-ref.test.ts (8), service-automation api-trigger-secret-ref.test.ts (5: per-post resolution and rotation, unfilled giving SERVICE_UNAVAILABLE/503 for undefined, '' and whitespace, the default env resolver, the channel never asked, refused references never armed), a flow-credential-channel test (a referenced position drops its held row and heldPaths omits it), lint (2 cases) and trigger-api (1 case). No ablation run: the behaviour is new, so no existing tested behaviour changed.", "gates": "dispatch-gates --commands at 8a23b3ad: 128 families, each recorded as 'cmd :: exit N' with $? captured before any pipe. 124 exit 0. 4 exit 3 = NOT MEASURED (refused prerequisite): check-plugin-teardown-shape --self-test (shallow clone), spec check:skill-examples (client-react dist absent), check:dual-build-cjs-loads and check:i18n-coverage (full workspace build). --ran reconciliation: '128 derived, 124 run, 4 NOT-MEASURED, 0 UNRUN', exit 0. Fixed on the way: check-skills-token-ratchet (SKILL.md +8 over its ceiling, fixed by genuine deletion: 5755 -> 5771 tokens, ceiling 5780, 438 -> 438 lines; pack 4389 -> 4389 lines, ratcheted tokens 144196 -> 144212), check:spec-parsed-alias (ADR-0122: z.input alias plus an isomorphic pin, 789 -> 790), check:quick-reference-counts (Automation 13 -> 14), spec check:llms-txt (205 -> 206, automation 13 -> 14), and the spec dropped-refinement gate (refine replaced by a projecting pattern). spec check:generated: all 14 artifacts up to date after a build at the merged head. check-platform-checklist --changed-from origin/main: OK. check:nul-bytes: 0. CI check-runs on #25703: in_progress at report time, not awaited.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "3 — mcp__github__issue_read x2 (get, get_comments on #25675) and ToolSearch to load it; no MCP write", "api_writes": "2 relay writes: fleet-write pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft, with assignee sunhaolin; request fw-20261024T185308Z-bff548, run 40122756265, read-back identical, 9901 bytes) and this os-dev-report comment (post-stamped, POST /repos/objectstack-ai/objectstack/issues/25675/comments). git push is not counted.", "open_questions": [ { "question": "Which name the reference resolves as. The ruling allows a flow-credential name or an environment-variable name.", "options": [ "A: a name resolved through the automation plugin's CredentialResolver (env var in the open tier, a host-wired secrets service otherwise), the same seam as connector auth.credentialRef (implemented)", "B: a name in the #20790 flow credential store, which is keyed by position and whose only write door is the /meta save door, closed to packaged flows (allowEnvironmentOverlay:false), so it needs a new write surface" ], "recommendation": "A. Real business need: hotsrm's deployment fills secrets by environment, and B has no door a deployment can reach today. Long-term soundness: one reference dialect (credentialRef/secretRef) through one resolver, which enterprise can back with a vault. Guarding against AI mistakes: one closed name grammar, refused loudly at FlowSchema. Startup scope: no new store surface. The binding's per-post resolveSecret from #20790 is reused as ruled; the store itself is reused only in that a referencing save removes its stale row." }, { "question": "Is config.secretRef withheld on read like config.secret?", "options": [ "A: not withheld; it is a name, not a credential (implemented)", "B: withheld" ], "recommendation": "A. Withholding it breaks the GET -> edit -> PUT round trip and protects nothing, and only the literal stays in FLOW_NODE_CREDENTIAL_KEYS." }, { "question": "Refusing names in the OS_ namespace is an extra constraint the ruling does not state.", "options": [ "A: refuse OS_* (implemented): whoever signs posts holds the hook secret, so OS_AUTH_SECRET reused as a hook secret would be handed out", "B: accept any identifier, as connector credentialRef does" ], "recommendation": "A, a contract tightening on a new key with no producer yet. Say if it should be dropped." }, { "question": "Governed surface: this PR edits skills/objectstack-automation/SKILL.md (Tier H), which the claim's file surface did not list.", "options": [ "A: keep the one-row fix in this PR (done): the row said a non-blank secret is required, which this change made false, and the role file says a published sentence the change makes false is fixed in the same PR", "B: split it into a docs PR" ], "recommendation": "A. The PR is now Tier H and needs the maintainer's approval as well as the Clause-② contract review. The PR body carries the 维护者速读 section and the skill readings." } ], "out_of_scope_findings": [ "class: none (note) · reach: n/a · A stored flow row from before the #20790 migration that still holds a literal secret: if an author edits it to secretRef through /meta, the carry-forward would restore the literal beside the reference and FlowSchema refuses the save. Loud, not silent; the boot migration moves such literals out first. carrier: none — in the PR's Acceptance notes only.", "class: none (note) · reach: n/a · A reference left unfilled is reported only per post (a warn on each refused post), with no boot-time line saying the hook cannot verify. carrier: none — noted, not filed." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
os-dev-report — round 2 addendum (patch round 1 for #25703)
088e4423(ungoverned) and docs(skills): the automation skill's inbound-hook table names config.secretRef #25708 at44ca1a08(governed,skills/**, Tier H, first lineRefs #25675). docs(skills): the automation skill's inbound-hook table names config.secretRef #25708's body says it merges after feat(spec): an api flow's start node may name its hook secret (config.secretRef), resolved per post #25703.skills/objectstack-automation/SKILL.mdonly. It is restored toorigin/mainon feat(spec): an api flow's start node may name its hook secret (config.secretRef), resolved per post #25703 and carried by docs(skills): the automation skill's inbound-hook table names config.secretRef #25708, with its own changeset.changeset/25675-skills-secret-ref-row.md(@objectstack/skillspatch).skills/objectstack-automation/references/_index.md. It isgen:skill-refsoutput, and the spec change in feat(spec): an api flow's start node may name its hook secret (config.secretRef), resolved per post #25703 forces it (check:skill-refs), so it has to ride with feat(spec): an api flow's start node may name its hook secret (config.secretRef), resolved per post #25703. Moving it would turn feat(spec): an api flow's start node may name its hook secret (config.secretRef), resolved per post #25703 red, which is the measured failure the [finding]skills/*/references/_index.mdis generator-owned (gen:skill-refs) yet sits inside theskills/**governed-merge fence — every spec PR touching an indexed headline is forced to carry a governed-surface file #11705 carve-out incheck-governed-merges.mjsdescribes.check-governed-merges --pr 25703reads NOT governed with it in place: 1 generated-artifact lift, 780 changed lines.scripts/ablation-replace.mjsunder the verify lock; source-resolved, since both test files importsrc/):service-automationflow-definitions.ts: the unfilled-name refusal was replaced by a barereturn value.api-trigger-secret-ref.test.tswent from 5/5 green (control) to 2 failed / 3 passed: the 503 pin and the default env pin. Restored blob94b953b8equals HEAD, andgit diff HEADis empty.dist/, so it failed at the import and measured nothing. The closure was then built and the control and mutation legs run.api-trigger-secret-ref.zod.ts: the beside-a-literal refusal was disabled. 8/8 (control) became 1 failed / 7 passed ("refuses secretRef beside a literal secret"). Restored blobe92bc293equals HEAD, andgit diff HEADis empty.088e4423: 125 derived, 121 exit 0, 4 exit 3 NOT MEASURED (teardown-shape self-test on a shallow clone, speccheck:skill-examples,check:dual-build-cjs-loadsandcheck:i18n-coverage, the last three needing the full build).--ran: 125 accounted, 0 UNRUN.44ca1a08: 46 derived, 44 exit 0, 2 exit 3 NOT MEASURED (check:dual-build-cjs-loads,check:lean-entry-closure).--ran: 0 UNRUN. Token ratchet: SKILL.md 5771 of a 5780 ceiling.skills/**being Tier H. The proposed seat edit:## 维护者速读(草稿)section, the## Skill readingssection and the second Acceptance-notes bullet;objectstack-automationskill's start-node table" with "(the skill row is docs(skills): the automation skill's inbound-hook table names config.secretRef #25708, which merges after this PR)".The same text is now in docs(skills): the automation skill's inbound-hook table names config.secretRef #25708.
pr_createdocs(skills): the automation skill's inbound-hook table names config.secretRef #25708 and this comment. MCP calls this round: 0.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Claim addendum (amends the file surface of
6386845783, round 2) ·domain:specseat 2 · 2026-10-24T20:09ZThe claim's surfaces (1) to (4) held. PR #25703 also touches the following, each forced by the change:
packages/triggers/trigger-api/src/api-trigger.tsand its test: the arm-time refusal and the docblock nameconfig.secretRef. The trigger never reads the name; only the automation engine resolves it.packages/services/service-automation/src/credential-resolver.ts(new): the plugin's existingcredentialResolvertype and its env default, moved out ofplugin.tsunchanged and re-exported. It is the connectorauth.credentialRefseam (ADR-0097 §3).content/docs/automation/flows.mdx,content/docs/automation/connectors.mdx,content/docs/getting-started/quick-reference.mdx(the Automation schema count its gate pins). Plus the regenerated reference pages undercontent/docs/references/**.docs/qa/platform-checklist/areas/automation.json: the two items that signed with the removed showcase literal now exportSHOWCASE_WEBHOOK_SECRETat boot.skills/objectstack-automation/references/_index.md:gen:skill-refsoutput thatcheck:skill-refsrequires beside the spec change. The generated-surface exception ([finding]skills/*/references/_index.mdis generator-owned (gen:skill-refs) yet sits inside theskills/**governed-merge fence — every spec PR touching an indexed headline is forced to carry a governed-surface file #11705) lifts it: CI's Governed Surface Queue Guard recomputed it on an installed tree (job120964600320), and feat(spec): an api flow's start node may name its hook secret (config.secretRef), resolved per post #25703's net diff reads not governed.Split out, governed: the
objectstack-automationskill's start-node row (skills/objectstack-automation/SKILL.md, Tier H) is PR #25708 onclaude/issue-25675-skills-secret-ref-row, first lineRefs #25675, with its own@objectstack/skillspatch changeset. It merges after #25703; the maintainer merges it.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Landed ·
domain:specseat 2 (seat post #18549) ·sunhaolin· sessionsession_01PC4NPXPgPmpDRG9DdDnyXT· 2026-10-24T21:54Zfa44dd2559. ACCEPT6388986607; cross-account contract review PASS6389260006on head088e442390(the landing head). That review settled the open question this seat handed it: the env-var arm throughcredentialResolveris a reading of ruling6386630545that holdsgh-readonly-queue/main/pr-25703-*git ls-remote: 0 refs)origin/mainfa44dd2559;packages/spec/src/automation/api-trigger-secret-ref.zod.tsholdsAPI_TRIGGER_SECRET_REF_KEY(6 hits); the showcase flow carriessecretRef: 'SHOWCASE_WEBHOOK_SECRET'(1 hit)State: the card closed
completedon the merge (Fixes #25675).pm:dispatchedis removed in this stroke.skills/objectstack-automation/SKILL.md, Tier H,Refs #25675). It is now unblocked on its side because the key it documents is onmain. It waits only on aGOVERNED_APPROVERSapproval (hotlong/os-zhuang); seat record6388999892.session_01NZJKktPWaz2EZJ95a9XZa8) stays open until docs(skills): the automation skill's inbound-hook table names config.secretRef #25708 merges, in case the review asks for an edit.secretRefis refused loudly) stay in the PR's Acceptance notes with no carrier, as the contract review recorded.Generated by Claude Code
objectstack-fleet commentedon Oct 25, 2026
os-dev-report — round 3 addendum (PR #25708, merge-group token ratchet)
origin/main(4cc17db3) merged intoclaude/issue-25675-skills-secret-ref-rowas merge commitfa387380, with no rebase and no force-push.mainhad rewritten the capability-prerequisite table in the same file.skills/objectstack-automation/SKILL.mdwas 5793 tokens against a ceiling of 5780 (+13). That is the same reading as the merge-group failure (run 40141451659).61942712):secretRef/secretrow, from "HMAC-SHA256 shared secret, required — without one the flow is refused at registration (os validatetoo). ShipsecretRef: 'NAME'(an env var the deployment fills, notOS_*; unfilled ⇒503), never a literal" to "HMAC-SHA256 secret, one required (else refused at registration andos validate). ShipsecretRef: 'NAME': an env var the deployment fills, notOS_*; unfilled ⇒503. Never a literal". That saves 6 tokens, and every fact is kept: one of the two is required, an env var the deployment fills, notOS_*, unfilled ⇒503, never a literal.configis a free-form record with no Zod shape)" from the sentence above the table, which saves 16 tokens. It is no longer accurate, becauseconfig.secretRefis now validated byFlowSchema.61942712:check-skills-token-ratchet --self-testexit 0 (79 cases), and the ratchet itself exit 0.pnpm --filter @objectstack/spec check:skill-refsexit 0, after a build under the verify lock.dispatch-gates --commandsderived 46: 44 exit 0, and 2 exit 3 NOT MEASURED (check:dual-build-cjs-loads,check:lean-entry-closure, both needing the full build).--ran: 46 accounted, 0 UNRUN.os-zhuang's approval, so docs(skills): the automation skill's inbound-hook table names config.secretRef #25708 needs approving again. feat(spec): an api flow's start node may name its hook secret (config.secretRef), resolved per post #25703 was not touched.git pushplus this comment through the relay. MCP calls: 0.Generated by Claude Code
objectstack-fleet commentedon Oct 25, 2026
Landed, governed half ·
domain:specseat 2 (seat post #18549) ·sunhaolin· sessionsession_01PC4NPXPgPmpDRG9DdDnyXT· 2026-10-25T09:41Zskills/**, Tier H,Refs #25675)d0eac355c3. os-zhuang (GOVERNED_APPROVERS) approved the landing head6194271233after the patch round for the skills token ratchet.ci-failure: GREEN, 44/44.check-governed-merges --pr 25708read GOVERNED, landing tier H, "an authorized APPROVED review … and then the owning seat lands it". The seat then armed auto-merge through the relay (run 40180255856)gh-readonly-queue/main/pr-25708-*matching-refs: 0 refs)origin/maind0eac355c3; theskills/objectstack-automation/SKILL.mdrow reads`secretRef` / `secret`"HMAC-SHA256 secret, one required … ShipsecretRef: 'NAME'"State: the card closed
completedwith the code half (PR #25703, landing record6390012662). With this, both halves are inmain.pm:dispatchedis already absent. The dev's cloud sessionsession_01NZJKktPWaz2EZJ95a9XZa8is archived in this stroke.Generated by Claude Code