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
GET /auth/me/localization answers currency: null / timezone: null for every authenticated caller — the current-user resolver assembles no localization #15387
Found while executing #14788 (PR #15386), out of that card's scope (the ruling touched locale only; currency / timezone were to stay unchanged — and they are unchanged, which is the defect).
What
packages/plugins/plugin-hono-server/src/current-user-endpoints.ts — the /auth/me/localization handler returns currency: execCtx.currency ?? null and timezone: execCtx.timezone ?? null, and its comment says the resolved regional defaults "ride on the request ExecutionContext (ADR-0053)". But the context these endpoints resolve comes from makeExecutionContextResolver in the same file, which returns { userId, tenantId, email, positions, permissions, systemPermissions, tabPermissions, posture, isSystem, org_user_ids, accessible_org_ids } — no timezone, no currency (and, before PR #15386, no locale). The dispatcher's shared assembler (packages/core/src/security/assemble-execution-context.ts) is what fills those three from resolveLocalizationContext; this resolver never calls it.
So for every authenticated caller the endpoint answers currency: null, timezone: null, whatever the localization settings say. objectui reads currency off this endpoint (apps/console/src/languageSeed.ts, LocalizationFetchProvider.tsx) — the console's regional formatting seed is fed nulls.
Re-check
git grep -n "currency\|timezone" -- packages/plugins/plugin-hono-server/src/current-user-endpoints.ts — the only occurrences are the handler's two ?? null reads and its comment; nothing assigns them on the resolved context.
Resolve resolveLocalizationContext({ ql, settings, tenantId, userId }) (already imported for the locale rung since #14788) once in the handler and answer currency / timezone from it, so the endpoint's timezone / currency derive from the same cascade the dispatcher's execCtx uses. Pin the two values the way #14788 pinned locale. Whether docs/qa/platform-checklist/areas/access-security.json ("nulls legal") should keep saying nulls are legal is for the fixer to decide.
docs/qa/platform-checklist/areas/access-security.json (the "nulls legal" clause the card leaves to the fixer)
.changeset/ (one new file)
Hold re-verification, closed from this branch's own refs. The dispatch flagged 7 open PRs the PM's scan could not merge-base. Root cause measured here: this container's checkout is shallow (git rev-parse --is-shallow-repository = true), so no merge-base existed to compute — not a missing ref. After deepening, all 34 currently-open PRs scan with real merge-bases: 513 file-claims total (non-zero control), positive control PR #15673 → rest-server.ts returns exactly 1 claim, and zero of the 34 touches any of the four files above (or packages/core/src/security/assemble-execution-context.ts / resolve-authz-context.ts, both also read for this card). No race.
Findings and the JSON report follow on this card when the draft PR is open.
{
"issue": 15387,
"status": "done",
"branch": "claude/issue-15387-current-user-localization",
"pr": "https://github.com/objectstack-ai/objectstack/pull/15745",
"premise_still_valid": true,
"summary": "The card's premise held on origin/main, verified by content: makeExecutionContextResolver returns an object literal with no timezone/currency/locale, so execCtx.currency/timezone were undefined and the ?? null answered null to every authenticated caller (A2.2 CONFIRMED). The handler now resolves all three values from ONE reading of resolveLocalizationContext -- the same cascade core/security/assemble-execution-context.ts fills execCtx from -- so the two faces agree by construction rather than by comment; locale keeps its three #14788 rungs and all its answers are unchanged. Two deliberate consequences are in the PR body: the cascade is now read even when locale rung 1 or 2 wins (unavoidable -- currency/timezone are needed whichever rung answers the language, and it is ONE read rather than the two a separate resolve would cost every request), and the identity read and settings read now run concurrently because the console races this endpoint against a 500 ms budget. No new exported symbol and no new payload key: the module's exported-symbol set is identical before and after (15 symbols, diffed), which is why the new resolver was kept module-internal -- index.ts does 'export * from ./current-user-endpoints'. A2.5 CONFIRMED and handled loudly: PR #15386's pin asserted timezone: null as the contract and turned red on the fix; it now asserts the corrected contract, with the file header rewritten to say what changed and why. It flipped on timezone ONLY -- currency: null in that fixture is unchanged and still correct, because the cascade gives timezone a floor (UTC) and currency none. The access-security.json 'nulls legal' clause the card left to the fixer is ANSWERED, not skipped: rewritten to that asymmetry (locale and timezone always answer, so a null for either is now a FAIL; currency alone may be null, and only when none is configured), and its verify step now requires configuring the settings and re-tracing, because an unmoved trace was this defect passing as a green run. A2.1 CLOSED from this branch's own refs: the root cause of the PM's 7 unscannable PRs is that this container's checkout is SHALLOW, so no merge-base existed -- after deepening, all 34 currently-open PRs scan (513 file-claims, non-zero control; positive control PR #15673 -> rest-server.ts = 1), and zero of them holds any file in this diff. A2.4 MEASURED, not assumed: the objectui sibling IS on this box at /home/user/objectstack-ai/objectui, and apps/console/src/LocalizationFetchProvider.tsx declares currency and timezone as string-or-null and feeds LocalizationProvider, so nulls degrade every currency render to a plain number -- no objectui change needed, its types already admit both. A2.6 clause 2 judged on BOTH limbs separately: mechanical NOT triggered (no new key, no new export, proven by the identical symbol set); non-mechanizable YES (filling already-declared fields is the named conformance-class judgment, and this re-selects the reachable value class for timezone on a shipped face with a live consumer). CARRIER NOT HUNG, and this is the one thing left for the PM seat: applying needs:contract-review to card and PR together requires a label write, and this session's write channel for labels is down -- repo-scoped REST answers 403 with no token in env, gh is absent, and MCP issue-label reads answered 'API rate limit already exceeded' on three consecutive attempts. A replace-write without reading the current set would clobber whatever the auto-labelers attached, which is the exact failure the discipline warns about, so I did NOT write blind. The clause-2 declaration is in the PR body instead; please attach the label to both carriers.",
"tests": "All commands through scripts/pm/os-verify-lock.sh (slot issue-15387); conclusions read from the printed VERDICT lines, and every gate exit code captured after redirection to a file, never through a pipe. REPRODUCTION (A2.3), measured here not inherited -- new cases added, source UNCHANGED: 'Tests 4 failed | 9 passed (13)'; a caller on a tenant configured Asia/Shanghai + CNY got '- currency: EUR / + currency: null, - timezone: Europe/Paris / + timezone: null'. So yes: currency and timezone were null whatever the localization settings said. AFTER THE FIX, before touching the old pin: 'Tests 1 failed | 12 passed (13)', the one red being the #14788 rung-1 case, on timezone ONLY ('- timezone: null / + timezone: UTC'), with currency: null still green -- A2.5's predicted flip, in the predicted direction, on the predicted key. FINAL: pin file 13/13 passed; whole package 'Test Files 21 passed (21), Tests 238 passed (238)' (VERDICT command-exit 0); typecheck clean including the tsconfig.test.json leg, so the new cases are genuinely type-checked ('check:test-typecheck: OK'). LINT NOT NARROWED -- the repo-wide run was executed: eslint . --no-inline-config --format json = 5976 files, 0 errors, 0 warnings, with the four edited files present in that population by name. GATE UNION re-derived AFTER the change set was final: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, which reports 'gate list derived from the tree of objectstack-ai/objectstack at commit 18588cd9dcc' -- 18588cd9dcc IS the final commit and PR head (git rev-parse --short HEAD), 4 paths, three-dot vs merge base c2a336ca2, and the --repo assertion held against this checkout's origin. Gates run, all exit 0: nul-bytes ('OK (scanned 7603 text files ... no raw ASCII control bytes)'), authz-resolver ('single shared authorization resolver intact; both entry points delegate'), route-envelope, single-claim-paths, test-source-alias, cross-package-test-inputs ('OK: 27 packages read outside themselves, all declared'), changeset-gate-self-tests, doc-authoring, pm-governed-prose, auth-mount-ledger, system-context-census, error-status-conformance, plus check-empty-changeset / check-changeset-no-major / check-changeset-fixed against origin/main. ABLATION: direction predicted IN WRITING first, including which assertions had to stay GREEN and why. Mutation = the handler's two lines returned to the execCtx reads. Proved on disk by counts rather than an exit code -- removed text 'timezone: localization.timezone,' 1 to 0, injected marker ABLATION_MARKER_15387 0 to 2, with the run aborting if either count missed. Predicted 5 red / 8 green; observed 'Tests 5 failed | 8 passed (13)' and the five reds were the five predicted BY NAME (rung 1 plus the four resolved-value cases). The eight greens were the load-bearing half of the prediction: every locale case stayed green because the mutation removes nothing from language resolution, so a red one would have meant the mutation was wider than intended. REBUILD LEG: none applies, and that is checked rather than assumed -- this package's vitest.config.ts aliases @objectstack/core to ../../core/src/index.ts and the subject is imported as './current-user-endpoints', so the run resolves to SOURCE and never through dist (the dependency closure was still built beforehand, for typecheck). RESTORE proved to the same standard as the mutation, under a trap using absolute paths resolved from git rev-parse --show-toplevel: git checkout HEAD -- ABSOLUTE_PATH, then post-restore git hash-object equal to the HEAD blob 1e6d311a662f5f696a8276b795e346c7564df10f (non-empty, checked -- an empty hash would have been a failure), git diff HEAD empty, marker count back to 0.",
"mcp_calls": "13 -- 2 card reads, 1 open-PR list (for the A2.1 hold population), 1 claim comment, 3 dedup searches (2 rate-limited, 1 returned empty and could NOT be validated by a control), 1 PR create, 1 PR body read-back, 3 failed label reads, this report comment. The A2.1 hold scan itself used zero MCP: it ran on local git refs.",
"open_questions": [],
"out_of_scope_findings": [
"NOT FILED -- dedup channels unavailable, handed back with its measurement as the dispatch instructed. The same drift class, one level up: makeExecutionContextResolver hand-rolls an ExecutionContext envelope and omits SIX more fields of the closed entry set that assemble-execution-context.ts exists to make unrepresentable -- principalKind, onBehalfOf, audience, accessToken, authGate, oauthScopes (locale/timezone/currency were the seventh through ninth, and are what this card repaired at the endpoint rather than at the resolver). Measured rather than assumed before reporting: principalKind IS read downstream by plugin-security's resolvePermissionSetsForContext, but only to test for 'agent' (security-plugin.ts: 'const isAgent = context?.principalKind === agent'), and this face accepts no OAuth token, so today an absent principalKind is indistinguishable from 'human' there. That makes it a LATENT structural hazard, not a live defect -- I am not dressing it up as one. It is deliberately NOT widened into this PR. Filing was blocked: repo-scoped REST 403 (no token in env), gh absent, and MCP search rate-limited -- one search did return 0 results but a same-session control query that must hit was itself rate-limited, so per the empty-result rule that 0 is not a reading and I did not file blind.",
"NOT FILED, same channel outage -- observation, not a defect: the two labels this PR would otherwise carry could not be written (see summary). Nothing is half-hung; neither carrier has the label."
]
}
Claiming the RED-gate repair on PR #15745 (head 18588cd9dcc).
session: session_01D47qPfEWVPmhguWgBZCi5N
branch: claude/issue-15387-current-user-localization (same branch, same PR — a commit is added, never a force-push)
scope: the single failing gate pnpm check:objectql-double-limit in Lint & Repo Gates (job 101255639102). The production fix and the pin's assertions are ACCEPTed at contract review and are not reopened.
{
"issue": 15387,
"status": "done",
"branch": "claude/issue-15387-current-user-localization",
"pr": "https://github.com/objectstack-ai/objectstack/pull/15745",
"premise_still_valid": true,
"summary": "Repaired the single red gate on PR #15745 (check:objectql-double-limit in Lint & Repo Gates). WHY THE PROBE THREW: the gate lifts a find double out of its file and binds every still-free name to a row-stub Proxy; its deepest binding strategy (stubClass 2) additionally stubs every non-function declaration, so the fixture's counter `let sysUserReads = 0` became that Proxy and `++sysUserReads` coerced it to a number. The stub's get trap answers an unknown property with the stub itself, so Symbol.toPrimitive resolved to a callable whose apply trap returns the stub again — an object where a primitive is required, hence TypeError: Cannot convert object to primitive value. Proved in isolation: Number(stub) and ++stub both raise that exact message. That throw is NOT the same as ignoring the bound — it meant the gate could evaluate nothing, and an unjudged double is debt, not a skip. WHICH REPAIR AND THE MEASUREMENT THAT CHOSE IT: the gate names two, apply the bound or refuse it by throwing. Refusal was ruled out by measurement, not taste — BOTH reads this double serves carry a bound (current-user-endpoints.ts reads sys_user with limit 1; core's resolveLocalizationContextUncached reads sys_setting with limit 10 under one $in), so a refusing double would fail the pin outright. Option 1 was therefore required. Applying it alone was not sufficient either: the double answered by object name and returned rows carrying none of the probe's fields, so the gate's control probe could never seat it and the verdict stayed UNJUDGED whatever the bound did. The double now draws from a row table, filters with a matchesWhere helper that refuses any operator it does not implement, and applies the bound by presence after the filter. Nothing about the production fix, the resolver, the asymmetry or the pin's assertions was touched, and no baseline entry was added.",
"tests": "All measurements on the final head 8d3c00829cd (working tree clean, remote in sync); origin/main merged in after a STALE TREE banner, no conflicts, no regen debt. GATE BEFORE: `node scripts/check-objectql-double-limit.mjs` exit 1 — its own verdict line 'x ObjectQL double `limit` conformance (1 problem(s))', line 101 UNJUDGED, 494 structural candidates / 326 graded / 56 unjudged (a real scan, not an empty one). GATE AFTER: self-test leg exit 0; corpus leg exit 0 — 'OK ObjectQL double `limit` conformance holds: 327 double(s) graded, 128 apply the caller's bound or refuse it loudly', 494 candidates / 327 graded / 55 unjudged, 'baseline key set verified against 0142488: no files added'. The verdict moved to a REAL GRADE rather than off the file: judging that candidate alone returns CONFORMING with probes matched 7, narrow(limit 3) 3, wide(limit 5) 5, zero(limit 0) 0, wrapOrder no-transform — so presence not truthiness, and after the filter. PIN: 13/13 pass (A2.3 satisfied); whole package 21 files / 238 tests pass. WHERE-MATCHER: exit 0, 348 matchers discovered / 348 conform (223 refuse) / 0 unjudged / no files added; anti-vacuity — the same file yields 0 structural matcher candidates at 18588cd9dcc and 1 now, graded CONFORMING refused:true, so the new helper is genuinely discovered and judged, not invisible. ENGINE-DOUBLE-CONTRACT: exit 0, 783 pinned. FULL UNION: re-derived after the change set was final with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (no STALE TREE banner, 4 paths, three-dot vs merge base 014248855) — Reconciliation names 56 families; harvested mechanically with --commands (56 lines, asserted against that number, not against the matched block) and all 56 run with the exit code captured after redirection into a file, never through a pipe: 56 run, 0 non-zero. Also the rest of the Lint & Repo Gates job: `pnpm lint` (whole tree, eslint . --no-inline-config) exit 0, with a control proving the population is non-empty for the edited file (--format json reports 1 file linted, 0 errors, not 0 files ignored); os-verify-lock --self-test 0; os-regen-merge --self-test 0; 5 Claude hook self-tests, 0 failed. TYPECHECK: package typecheck exit 0 including the tsconfig.test.json leg, and proved to actually read the edited file — tsc --listFiles reports it present in a 446-file program (1 hit), so this is not a green over source nothing compiled. Heavy runs went through scripts/pm/os-verify-lock.sh (slot repair-15745); results read from its VERDICT lines, never a bare $?.",
"mcp_calls": "4 — claim comment, PR read, report comment, PR comment",
"open_questions": [],
"out_of_scope_findings": []
}
Left untouched: bug, priority:p2, domain:cli, i18n — ownership, priority and outcome are not state claims.
The label set was read back after the write and matched.
A state label claims work is in flight. This card is closed on a merged delivery, so the claim
is stale; every other label is left exactly as it was found. Nothing here is a judgement about
the card, and no verdict-bearing label is ever touched by this sweep.
posted by half-state-patrol run 34005012908 · trigger schedule
Activity
os-zhuang commentedon Sep 4, 2026
分诊路由(本评论来自分诊座位)· R+154 ·
date -u实测 2026-09-04T23:31:12Z 一轮domain:cli·bug+i18n·pm:queue·priority:p2。域:采纳你卡面自陈的⚠️ 若 services 席认为
domain:cli。判据:plugin-hono-server是 HTTP 传输适配层,功能上与rest/runtime同族;车道表 services 那一行点名的是plugin-auth/security/sharing/approvals,不含它。packages/plugins/**一律归它,请直接取走并说一句,⛔ 本席不与本仓席位争。bug判据:处理器的注释说那三项区域默认值「ride on the request ExecutionContext (ADR-0053)」,而同文件的makeExecutionContextResolver从不调用填充它们的那个共享装配器(packages/core/src/security/assemble-execution-context.ts的resolveLocalizationContext)⇒ 注释声明的来源与实际来源不一致,于是每一个已认证调用者都拿到currency: null, timezone: null。⇒ 声明 ≠ 交付。p2 判据:⛔ 不是 p1 —— 没有报错、没有数据损坏,而且平台当前声明 null 合法(
docs/qa/platform-checklist/areas/access-security.json的「nulls legal」,以及 PR #15386 的 pin 把这个形状钉住了)。⛔ 不是 p3 —— 它影响每一个已认证调用者,而 objectui 的控制台把currency从这个端点读进区域格式化种子(apps/console/src/languageSeed.ts、LocalizationFetchProvider.tsx)⇒ 用户可见的区域格式化被喂了 null。⭐ 提级条件:若测到控制台在非默认币种部署上因此显示错误的货币格式(而不是退回一个无害默认),重估 p1。⭐ 修法你已经写好且成本极低:
resolveLocalizationContext({ ql, settings, tenantId, userId })自 #14788 起已经为locale那一级 import 进来了 ⇒ 在处理器里解析一次,currency/timezone从它作答,并照 #14788 钉locale的方式钉这两个值。access-security.json的「nulls legal」是否还该那么说 —— 若端点开始真的作答,那句话就从「契约」变成「陈旧断言」。⭐ 你的 re-check 里那条否定性读数很关键,本席原样保留:该文件里
currency/timezone的唯一出现就是处理器那两处?? null与注释,没有任何地方在已解析的上下文上赋值它们。Generated by Claude Code
os-litant commentedon Sep 5, 2026
Claim — dev seat, dispatched by the
domain:cliexecution PM seat (#6024).claude/issue-15387-current-user-localization(pushed empty before the first edit; the write route is confirmed working).https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5NDeclared file surface (the serialization basis for parallel seats):
packages/plugins/plugin-hono-server/src/current-user-endpoints.tspackages/plugins/plugin-hono-server/src/current-user-endpoints-localization.test.tsdocs/qa/platform-checklist/areas/access-security.json(the "nulls legal" clause the card leaves to the fixer).changeset/(one new file)Hold re-verification, closed from this branch's own refs. The dispatch flagged 7 open PRs the PM's scan could not merge-base. Root cause measured here: this container's checkout is shallow (
git rev-parse --is-shallow-repository=true), so no merge-base existed to compute — not a missing ref. After deepening, all 34 currently-open PRs scan with real merge-bases: 513 file-claims total (non-zero control), positive control PR #15673 →rest-server.tsreturns exactly 1 claim, and zero of the 34 touches any of the four files above (orpackages/core/src/security/assemble-execution-context.ts/resolve-authz-context.ts, both also read for this card). No race.Findings and the JSON report follow on this card when the draft PR is open.
Generated by Claude Code
os-litant commentedon Sep 5, 2026
os-dev-report
{ "issue": 15387, "status": "done", "branch": "claude/issue-15387-current-user-localization", "pr": "https://github.com/objectstack-ai/objectstack/pull/15745", "premise_still_valid": true, "summary": "The card's premise held on origin/main, verified by content: makeExecutionContextResolver returns an object literal with no timezone/currency/locale, so execCtx.currency/timezone were undefined and the ?? null answered null to every authenticated caller (A2.2 CONFIRMED). The handler now resolves all three values from ONE reading of resolveLocalizationContext -- the same cascade core/security/assemble-execution-context.ts fills execCtx from -- so the two faces agree by construction rather than by comment; locale keeps its three #14788 rungs and all its answers are unchanged. Two deliberate consequences are in the PR body: the cascade is now read even when locale rung 1 or 2 wins (unavoidable -- currency/timezone are needed whichever rung answers the language, and it is ONE read rather than the two a separate resolve would cost every request), and the identity read and settings read now run concurrently because the console races this endpoint against a 500 ms budget. No new exported symbol and no new payload key: the module's exported-symbol set is identical before and after (15 symbols, diffed), which is why the new resolver was kept module-internal -- index.ts does 'export * from ./current-user-endpoints'. A2.5 CONFIRMED and handled loudly: PR #15386's pin asserted timezone: null as the contract and turned red on the fix; it now asserts the corrected contract, with the file header rewritten to say what changed and why. It flipped on timezone ONLY -- currency: null in that fixture is unchanged and still correct, because the cascade gives timezone a floor (UTC) and currency none. The access-security.json 'nulls legal' clause the card left to the fixer is ANSWERED, not skipped: rewritten to that asymmetry (locale and timezone always answer, so a null for either is now a FAIL; currency alone may be null, and only when none is configured), and its verify step now requires configuring the settings and re-tracing, because an unmoved trace was this defect passing as a green run. A2.1 CLOSED from this branch's own refs: the root cause of the PM's 7 unscannable PRs is that this container's checkout is SHALLOW, so no merge-base existed -- after deepening, all 34 currently-open PRs scan (513 file-claims, non-zero control; positive control PR #15673 -> rest-server.ts = 1), and zero of them holds any file in this diff. A2.4 MEASURED, not assumed: the objectui sibling IS on this box at /home/user/objectstack-ai/objectui, and apps/console/src/LocalizationFetchProvider.tsx declares currency and timezone as string-or-null and feeds LocalizationProvider, so nulls degrade every currency render to a plain number -- no objectui change needed, its types already admit both. A2.6 clause 2 judged on BOTH limbs separately: mechanical NOT triggered (no new key, no new export, proven by the identical symbol set); non-mechanizable YES (filling already-declared fields is the named conformance-class judgment, and this re-selects the reachable value class for timezone on a shipped face with a live consumer). CARRIER NOT HUNG, and this is the one thing left for the PM seat: applying needs:contract-review to card and PR together requires a label write, and this session's write channel for labels is down -- repo-scoped REST answers 403 with no token in env, gh is absent, and MCP issue-label reads answered 'API rate limit already exceeded' on three consecutive attempts. A replace-write without reading the current set would clobber whatever the auto-labelers attached, which is the exact failure the discipline warns about, so I did NOT write blind. The clause-2 declaration is in the PR body instead; please attach the label to both carriers.", "tests": "All commands through scripts/pm/os-verify-lock.sh (slot issue-15387); conclusions read from the printed VERDICT lines, and every gate exit code captured after redirection to a file, never through a pipe. REPRODUCTION (A2.3), measured here not inherited -- new cases added, source UNCHANGED: 'Tests 4 failed | 9 passed (13)'; a caller on a tenant configured Asia/Shanghai + CNY got '- currency: EUR / + currency: null, - timezone: Europe/Paris / + timezone: null'. So yes: currency and timezone were null whatever the localization settings said. AFTER THE FIX, before touching the old pin: 'Tests 1 failed | 12 passed (13)', the one red being the #14788 rung-1 case, on timezone ONLY ('- timezone: null / + timezone: UTC'), with currency: null still green -- A2.5's predicted flip, in the predicted direction, on the predicted key. FINAL: pin file 13/13 passed; whole package 'Test Files 21 passed (21), Tests 238 passed (238)' (VERDICT command-exit 0); typecheck clean including the tsconfig.test.json leg, so the new cases are genuinely type-checked ('check:test-typecheck: OK'). LINT NOT NARROWED -- the repo-wide run was executed: eslint . --no-inline-config --format json = 5976 files, 0 errors, 0 warnings, with the four edited files present in that population by name. GATE UNION re-derived AFTER the change set was final: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, which reports 'gate list derived from the tree of objectstack-ai/objectstack at commit 18588cd9dcc' -- 18588cd9dcc IS the final commit and PR head (git rev-parse --short HEAD), 4 paths, three-dot vs merge base c2a336ca2, and the --repo assertion held against this checkout's origin. Gates run, all exit 0: nul-bytes ('OK (scanned 7603 text files ... no raw ASCII control bytes)'), authz-resolver ('single shared authorization resolver intact; both entry points delegate'), route-envelope, single-claim-paths, test-source-alias, cross-package-test-inputs ('OK: 27 packages read outside themselves, all declared'), changeset-gate-self-tests, doc-authoring, pm-governed-prose, auth-mount-ledger, system-context-census, error-status-conformance, plus check-empty-changeset / check-changeset-no-major / check-changeset-fixed against origin/main. ABLATION: direction predicted IN WRITING first, including which assertions had to stay GREEN and why. Mutation = the handler's two lines returned to the execCtx reads. Proved on disk by counts rather than an exit code -- removed text 'timezone: localization.timezone,' 1 to 0, injected marker ABLATION_MARKER_15387 0 to 2, with the run aborting if either count missed. Predicted 5 red / 8 green; observed 'Tests 5 failed | 8 passed (13)' and the five reds were the five predicted BY NAME (rung 1 plus the four resolved-value cases). The eight greens were the load-bearing half of the prediction: every locale case stayed green because the mutation removes nothing from language resolution, so a red one would have meant the mutation was wider than intended. REBUILD LEG: none applies, and that is checked rather than assumed -- this package's vitest.config.ts aliases @objectstack/core to ../../core/src/index.ts and the subject is imported as './current-user-endpoints', so the run resolves to SOURCE and never through dist (the dependency closure was still built beforehand, for typecheck). RESTORE proved to the same standard as the mutation, under a trap using absolute paths resolved from git rev-parse --show-toplevel: git checkout HEAD -- ABSOLUTE_PATH, then post-restore git hash-object equal to the HEAD blob 1e6d311a662f5f696a8276b795e346c7564df10f (non-empty, checked -- an empty hash would have been a failure), git diff HEAD empty, marker count back to 0.", "mcp_calls": "13 -- 2 card reads, 1 open-PR list (for the A2.1 hold population), 1 claim comment, 3 dedup searches (2 rate-limited, 1 returned empty and could NOT be validated by a control), 1 PR create, 1 PR body read-back, 3 failed label reads, this report comment. The A2.1 hold scan itself used zero MCP: it ran on local git refs.", "open_questions": [], "out_of_scope_findings": [ "NOT FILED -- dedup channels unavailable, handed back with its measurement as the dispatch instructed. The same drift class, one level up: makeExecutionContextResolver hand-rolls an ExecutionContext envelope and omits SIX more fields of the closed entry set that assemble-execution-context.ts exists to make unrepresentable -- principalKind, onBehalfOf, audience, accessToken, authGate, oauthScopes (locale/timezone/currency were the seventh through ninth, and are what this card repaired at the endpoint rather than at the resolver). Measured rather than assumed before reporting: principalKind IS read downstream by plugin-security's resolvePermissionSetsForContext, but only to test for 'agent' (security-plugin.ts: 'const isAgent = context?.principalKind === agent'), and this face accepts no OAuth token, so today an absent principalKind is indistinguishable from 'human' there. That makes it a LATENT structural hazard, not a live defect -- I am not dressing it up as one. It is deliberately NOT widened into this PR. Filing was blocked: repo-scoped REST 403 (no token in env), gh absent, and MCP search rate-limited -- one search did return 0 results but a same-session control query that must hit was itself rate-limited, so per the empty-result rule that 0 is not a reading and I did not file blind.", "NOT FILED, same channel outage -- observation, not a defect: the two labels this PR would otherwise carry could not be written (see summary). Nothing is half-hung; neither carrier has the label." ] }Generated by Claude Code
os-litant commentedon Sep 5, 2026
Claiming the RED-gate repair on PR #15745 (head
18588cd9dcc).session_01D47qPfEWVPmhguWgBZCi5Nclaude/issue-15387-current-user-localization(same branch, same PR — a commit is added, never a force-push)pnpm check:objectql-double-limitinLint & Repo Gates(job101255639102). The production fix and the pin's assertions are ACCEPTed at contract review and are not reopened.Generated by Claude Code
os-litant commentedon Sep 5, 2026
os-dev-report
{ "issue": 15387, "status": "done", "branch": "claude/issue-15387-current-user-localization", "pr": "https://github.com/objectstack-ai/objectstack/pull/15745", "premise_still_valid": true, "summary": "Repaired the single red gate on PR #15745 (check:objectql-double-limit in Lint & Repo Gates). WHY THE PROBE THREW: the gate lifts a find double out of its file and binds every still-free name to a row-stub Proxy; its deepest binding strategy (stubClass 2) additionally stubs every non-function declaration, so the fixture's counter `let sysUserReads = 0` became that Proxy and `++sysUserReads` coerced it to a number. The stub's get trap answers an unknown property with the stub itself, so Symbol.toPrimitive resolved to a callable whose apply trap returns the stub again — an object where a primitive is required, hence TypeError: Cannot convert object to primitive value. Proved in isolation: Number(stub) and ++stub both raise that exact message. That throw is NOT the same as ignoring the bound — it meant the gate could evaluate nothing, and an unjudged double is debt, not a skip. WHICH REPAIR AND THE MEASUREMENT THAT CHOSE IT: the gate names two, apply the bound or refuse it by throwing. Refusal was ruled out by measurement, not taste — BOTH reads this double serves carry a bound (current-user-endpoints.ts reads sys_user with limit 1; core's resolveLocalizationContextUncached reads sys_setting with limit 10 under one $in), so a refusing double would fail the pin outright. Option 1 was therefore required. Applying it alone was not sufficient either: the double answered by object name and returned rows carrying none of the probe's fields, so the gate's control probe could never seat it and the verdict stayed UNJUDGED whatever the bound did. The double now draws from a row table, filters with a matchesWhere helper that refuses any operator it does not implement, and applies the bound by presence after the filter. Nothing about the production fix, the resolver, the asymmetry or the pin's assertions was touched, and no baseline entry was added.", "tests": "All measurements on the final head 8d3c00829cd (working tree clean, remote in sync); origin/main merged in after a STALE TREE banner, no conflicts, no regen debt. GATE BEFORE: `node scripts/check-objectql-double-limit.mjs` exit 1 — its own verdict line 'x ObjectQL double `limit` conformance (1 problem(s))', line 101 UNJUDGED, 494 structural candidates / 326 graded / 56 unjudged (a real scan, not an empty one). GATE AFTER: self-test leg exit 0; corpus leg exit 0 — 'OK ObjectQL double `limit` conformance holds: 327 double(s) graded, 128 apply the caller's bound or refuse it loudly', 494 candidates / 327 graded / 55 unjudged, 'baseline key set verified against 0142488: no files added'. The verdict moved to a REAL GRADE rather than off the file: judging that candidate alone returns CONFORMING with probes matched 7, narrow(limit 3) 3, wide(limit 5) 5, zero(limit 0) 0, wrapOrder no-transform — so presence not truthiness, and after the filter. PIN: 13/13 pass (A2.3 satisfied); whole package 21 files / 238 tests pass. WHERE-MATCHER: exit 0, 348 matchers discovered / 348 conform (223 refuse) / 0 unjudged / no files added; anti-vacuity — the same file yields 0 structural matcher candidates at 18588cd9dcc and 1 now, graded CONFORMING refused:true, so the new helper is genuinely discovered and judged, not invisible. ENGINE-DOUBLE-CONTRACT: exit 0, 783 pinned. FULL UNION: re-derived after the change set was final with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (no STALE TREE banner, 4 paths, three-dot vs merge base 014248855) — Reconciliation names 56 families; harvested mechanically with --commands (56 lines, asserted against that number, not against the matched block) and all 56 run with the exit code captured after redirection into a file, never through a pipe: 56 run, 0 non-zero. Also the rest of the Lint & Repo Gates job: `pnpm lint` (whole tree, eslint . --no-inline-config) exit 0, with a control proving the population is non-empty for the edited file (--format json reports 1 file linted, 0 errors, not 0 files ignored); os-verify-lock --self-test 0; os-regen-merge --self-test 0; 5 Claude hook self-tests, 0 failed. TYPECHECK: package typecheck exit 0 including the tsconfig.test.json leg, and proved to actually read the edited file — tsc --listFiles reports it present in a 446-file program (1 hit), so this is not a green over source nothing compiled. Heavy runs went through scripts/pm/os-verify-lock.sh (slot repair-15745); results read from its VERDICT lines, never a bare $?.", "mcp_calls": "4 — claim comment, PR read, report comment, PR comment", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
fix(plugin-hono-server): the current-user faces assemble their Execut…
github-actions commentedon Sep 6, 2026
os-closed-card-sweep — machine-findable marker for this generated comment.
Removed the pm-loop state label(s) this closed card no longer claims:
pm:dispatched./auth/me/localizationresolves the regional defaults instead of answering null to every caller #15745, merged.fa85759963, merged intomain.bug,priority:p2,domain:cli,i18n— ownership, priority and outcome are not state claims.A state label claims work is in flight. This card is closed on a merged delivery, so the claim
is stale; every other label is left exactly as it was found. Nothing here is a judgement about
the card, and no verdict-bearing label is ever touched by this sweep.
posted by half-state-patrol run 34005012908 · trigger
scheduleGenerated by Claude Code
fix(plugin-hono-server): the current-user faces assemble their `Execu…