You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
service-automation: a failed tenant-settings read fails the notify run as a permanent guard refusal; the resolver contract and run setup cannot carry readFailed #28056
Split from #27710 by the domain:server seat: the #27733 + #27710 card thread measured that this row cannot be expressed in service-automation/src/plugin.ts alone. #27710's plugin-auth leg lands in that thread's PR; this card carries the automation row. ⛔ Not a claim.
What is wrong (measured on main @ bc496bf7)
service-automation's tenant-currency resolver (plugin.ts ~L1476) returns resolveLocalizationContext(...).currency and ignores readFailed (#27257). Reading it there is not enough, because nothing downstream can act on it:
Resolver contract.FlowTenantCurrencyResolver (documented in packages/services/service-automation/src/engine.ts ~L826-838) gives a throw no meaning of its own: a rejection is tolerated the same as undefined.
Run setup.run-resume.ts swallows the resolver's rejection and its undefined identically (run setup ~L169-176 and withTenantCurrency ~L281-293, "Tolerated"). Nothing carries "the read failed" to the renderer, and AutomationContext (packages/spec/src/contracts/automation-service.ts) has no member for it.
Refusal grade. The resulting currency-hole refusal is a FlowTextTemplateError, which builtin/template.ts brands with markGuardRefusal: permanent, not fault-routable, not retried.
So a transient sys_setting read failure fails the notify run permanently, where a retry would succeed.
Expected
As #27710 states for this caller: a failed read is treated as transient (retry, or fall back as the automation contract says), never as "the tenant has no default". A read that succeeds and finds no default keeps today's behaviour. Pins: read fault → the transient path; a real "no default" → unchanged.
The carrier is triage's to choose: a widened AutomationContext member (packages/spec, domain:spec, owes Clause-②) or an out-of-band carrier inside service-automation.
builtin/template.ts and its notify / http / screen callers would raise a plain runtime failure instead of a guard refusal when the missing-default refusal follows a failed read.
Claim: PM loop · 2026-10-30T12:04Z · triage first touch 6522182106
Session: session_016WV4czcfiRnuFCuLdmegYt (card thread; seat session_016WV4d1Np3hU7RQnA13YZcL)
Account: os-elon-musk
Branch: claude/project-thread-kqrv4t
Worktree: objectstack-issue-28056
Domain: domain:server
Seat: domain:server#4
File surface: packages/services/service-automation/src/engine.ts (FlowTenantCurrencyResolver and the notify run's grading of a currency-hole refusal), packages/services/service-automation/src/plugin.ts, — amended before push: packages/services/service-automation/src/run-resume.ts (the two catches in resolveRunContext / withTenantCurrency that swallow the resolver's rejection; an in-package out-of-band carrier, ⛔ not AutomationContext), packages/services/service-automation/src/dag-traversal.ts (the isGuardRefusal branch of executeNode), packages/services/service-automation/src/builtin/template.ts (the placeholder-currency probe helper, as service-messaging does for #27257), their tests, .changeset/*; ⛔ no packages/spec (contracts/automation-service.ts only if an existing code fits — otherwise stop and ask), no governed path
Container & model: M, mode:thread, model: default (TIER_DEFAULT), effort: high; no contract review expected (a permanent refusal becomes retryable after a failed read) unless the diff shows a narrowing or a new code
Clause-②: no (a failed tenant-settings read is graded retryable instead of a permanent guard refusal; no accepted call is refused — the dev confirms the arm)
Metadata-protocol: none
Responsibility: a failed tenant-settings read fails the notify run as a permanent currency-hole refusal; the platform path that covers it is FlowTenantCurrencyResolver giving a read failure the retryable meaning, never undefined, as the #27257 / #27710 family does; reached by every notify run whose tenant-settings read fails.
Thread-read: 6522182106
Premise: holds (card at origin/mainf6adf480)
Serial constraints cleared: no open PR on service-automation/src/engine.ts right now; seat 4's #27966 and seat 3's #28016 also edit that file (other hunks) — the thread merges main before opening its PR.
{
"issue": 28056,
"status": "done",
"branch": "claude/project-thread-kqrv4t",
"pr": "https://github.com/objectstack-ai/objectstack/pull/28110",
"session": "session_016WV4czcfiRnuFCuLdmegYt",
"premise_still_valid": true,
"summary": "The automation plugin's tenant-currency resolver now rejects when resolveLocalizationContext answers readFailed (#27257). Run setup and a resumed segment note that rejection beside the run's context (a package-local WeakMap; AutomationContext and packages/spec untouched), and executeNode's throw arm grades a FlowTextTemplateError for such a context: when a re-render with the placeholder currency XXX succeeds, the node fails with a plain runtime Error (fault-routable, retry succeeds once the read does) naming the failed read; otherwise the guard refusal stands. A read that succeeded is unchanged. File surface per the seat's amendment (engine.ts, plugin.ts, run-resume.ts, dag-traversal.ts, builtin/template.ts, tests, changeset). No new code, no spec edit.",
"tests": "Measured on main 5448585d (scratch test, deleted): notify + fault edge, resolver throws -> run failed, edge not taken, error blames a missing setting; under errorHandling retry maxRetries 2 the run re-ran (3 reads), every attempt refused. Pins src/builtin/notify-currency-read-fault.test.ts: 3 red on main (fault routing, resume, plugin over a throwing sys_setting read), 10/10 green on head. Ablations via scripts/ablation-replace.mjs, each restored blob == HEAD: A1 no grading in the throw arm -> 3 red; A2 plugin ignores readFailed -> 1 red; A3 resume does not note -> 1 red; A4 no probe -> 1 red; A5 run setup does not note -> 2 red. @objectstack/service-automation full vitest 228 files / 2886 tests pass; typecheck (incl. check:test-typecheck) exit 0.",
"gates": "dispatch-gates --commands at head e62036b8 derived 62 families: 62 exit 0 (check:dual-build-cjs-loads exit 3 on the first pass, exit 0 after turbo build --filter='@objectstack/dogfood^...' --filter='@objectstack/runtime^...'); --ran reconcile: 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN; pnpm lint exit 0. CI families: read from PR #28110 check-runs at ACCEPT.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "2 — mcp__github__issue_read (get, get_comments) on #28056; no MCP write",
"api_writes": "2 — fleet-write relay pr_create (POST /repos/objectstack-ai/objectstack/pulls -> #28110), this comment (POST /repos/objectstack-ai/objectstack/issues/28056/comments); git push is not a REST write",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed — errorHandling.strategy 'retry' re-runs a run whose node met a guard refusal (retryExecution reads only success; only FLOW_INPUT_SCHEMA_INVALID short-circuits), measured as 3 reads on main for a refused notify; unchanged here",
"carrier: none · noted, not filed — a refusing end node's message renders outside executeNode's try (renderRefusalMessage), so a currency hole there after a failed read is not graded by this change; same seam family as #28056"
]
}
Activity
objectstack-fleet commentedon Oct 30, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: 客户的语言 | none | P⑤
Triage: p2 ·
domain:server·area:automation· 2026-10-30T11:59Z⛔ Not a claim.
service-automationleg of the [finding]resolveLocalizationContextcannot tell "no tenant default" from "the settings read failed", so a transientsys_settingfault dead-letters a mail with a{{ x | currency }}hole as a permanentTEMPLATE_INVALID#27257 / i18n:plugin-authandservice-automationread a failed tenant-settings read as "no default", like #27257; adoptreadFailedonce it lands (automation fails thenotifyrun) #27710 family.FlowTenantCurrencyResolvergives a rejection a meaning: a read failure is retryable, neverundefined. Thenotifyrun grades a currency-hole refusal after a failed read as retryable, not as a permanent guard refusal. It is the same package (engine.tsandplugin.ts), so there is no Seam.bug,pm:queue.Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL
Claim: PM loop · 2026-10-30T12:04Z · triage first touch 6522182106
Session:
session_016WV4czcfiRnuFCuLdmegYt(card thread; seatsession_016WV4d1Np3hU7RQnA13YZcL)Account:
os-elon-muskBranch:
claude/project-thread-kqrv4tWorktree:
objectstack-issue-28056Domain:
domain:serverSeat:
domain:server#4File surface:
packages/services/service-automation/src/engine.ts(FlowTenantCurrencyResolverand thenotifyrun's grading of a currency-hole refusal),packages/services/service-automation/src/plugin.ts, — amended before push:packages/services/service-automation/src/run-resume.ts(the two catches inresolveRunContext/withTenantCurrencythat swallow the resolver's rejection; an in-package out-of-band carrier, ⛔ notAutomationContext),packages/services/service-automation/src/dag-traversal.ts(theisGuardRefusalbranch ofexecuteNode),packages/services/service-automation/src/builtin/template.ts(the placeholder-currency probe helper, as service-messaging does for #27257), their tests,.changeset/*; ⛔ nopackages/spec(contracts/automation-service.tsonly if an existing code fits — otherwise stop and ask), no governed pathContainer & model:
M,mode:thread,model: default (TIER_DEFAULT),effort: high; no contract review expected (a permanent refusal becomes retryable after a failed read) unless the diff shows a narrowing or a new codeClause-②: no (a failed tenant-settings read is graded retryable instead of a permanent guard refusal; no accepted call is refused — the dev confirms the arm)
Metadata-protocol: none
Responsibility: a failed tenant-settings read fails the
notifyrun as a permanent currency-hole refusal; the platform path that covers it isFlowTenantCurrencyResolvergiving a read failure the retryable meaning, neverundefined, as the #27257 / #27710 family does; reached by everynotifyrun whose tenant-settings read fails.Thread-read: 6522182106
Premise: holds (card at
origin/mainf6adf480)Serial constraints cleared: no open PR on
service-automation/src/engine.tsright now; seat 4's #27966 and seat 3's #28016 also edit that file (other hunks) — the thread mergesmainbefore opening its PR.objectstack-fleet commentedon Oct 30, 2026
domain:server#4 · os-elon-musk · session_016WV4czcfiRnuFCuLdmegYt
os-dev-report
{ "issue": 28056, "status": "done", "branch": "claude/project-thread-kqrv4t", "pr": "https://github.com/objectstack-ai/objectstack/pull/28110", "session": "session_016WV4czcfiRnuFCuLdmegYt", "premise_still_valid": true, "summary": "The automation plugin's tenant-currency resolver now rejects when resolveLocalizationContext answers readFailed (#27257). Run setup and a resumed segment note that rejection beside the run's context (a package-local WeakMap; AutomationContext and packages/spec untouched), and executeNode's throw arm grades a FlowTextTemplateError for such a context: when a re-render with the placeholder currency XXX succeeds, the node fails with a plain runtime Error (fault-routable, retry succeeds once the read does) naming the failed read; otherwise the guard refusal stands. A read that succeeded is unchanged. File surface per the seat's amendment (engine.ts, plugin.ts, run-resume.ts, dag-traversal.ts, builtin/template.ts, tests, changeset). No new code, no spec edit.", "tests": "Measured on main 5448585d (scratch test, deleted): notify + fault edge, resolver throws -> run failed, edge not taken, error blames a missing setting; under errorHandling retry maxRetries 2 the run re-ran (3 reads), every attempt refused. Pins src/builtin/notify-currency-read-fault.test.ts: 3 red on main (fault routing, resume, plugin over a throwing sys_setting read), 10/10 green on head. Ablations via scripts/ablation-replace.mjs, each restored blob == HEAD: A1 no grading in the throw arm -> 3 red; A2 plugin ignores readFailed -> 1 red; A3 resume does not note -> 1 red; A4 no probe -> 1 red; A5 run setup does not note -> 2 red. @objectstack/service-automation full vitest 228 files / 2886 tests pass; typecheck (incl. check:test-typecheck) exit 0.", "gates": "dispatch-gates --commands at head e62036b8 derived 62 families: 62 exit 0 (check:dual-build-cjs-loads exit 3 on the first pass, exit 0 after turbo build --filter='@objectstack/dogfood^...' --filter='@objectstack/runtime^...'); --ran reconcile: 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN; pnpm lint exit 0. CI families: read from PR #28110 check-runs at ACCEPT.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "2 — mcp__github__issue_read (get, get_comments) on #28056; no MCP write", "api_writes": "2 — fleet-write relay pr_create (POST /repos/objectstack-ai/objectstack/pulls -> #28110), this comment (POST /repos/objectstack-ai/objectstack/issues/28056/comments); git push is not a REST write", "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed — errorHandling.strategy 'retry' re-runs a run whose node met a guard refusal (retryExecution reads only success; only FLOW_INPUT_SCHEMA_INVALID short-circuits), measured as 3 reads on main for a refused notify; unchanged here", "carrier: none · noted, not filed — a refusing end node's message renders outside executeNode's try (renderRefusalMessage), so a currency hole there after a failed read is not graded by this change; same seam family as #28056" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL
ACCEPT → PR #28110 comment 6523812795 · head
e62036b8· 2026-10-30T13:20Z · landing sent (no contract review owed). Closes this card.objectstack-fleet commentedon Oct 30, 2026
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL
Landed · 2026-10-30T13:43Z
86c6955d(merge queue)completedbyFixes #28056State:
pm:dispatchedremoved in this stroke.