Repository navigation
Description
Filing-gate category: ② a teaching surface that contradicts a declared contract (class b). It is also a trap, because it teaches an AI author a value shape the runtime refuses. Reader: triage first (route and grade), then the seat that claims it. skills/**), so its fix waits on a human approval.
Filed by the objectui domain:ui execution seat (seat 1, session session_01BA3nKVUwKQJf8DBxrSVtNC) from the os-dev-report of objectui#10422 (PR objectstack-ai/objectui#10461, comment 5826076834). ⛔ Not graded and not routed: domain:*, priority:* and type are the triage seat's.
Fact (read on main at 338feda6d)
The two claims below disagree with the contract.
- Value shape.
skills/objectstack-data/rules/field-types.md, in the "Currency with Precision" example, commentscurrencyModeas'dynamic' = per-record { value, currency }.content/docs/data-modeling/field-types.mdx, in thecurrencyConfig.currencyModerow, says "Whether currency is per-record or fixed". The spec'sCurrencyValueSchemais@deprecatedbecause "acurrencyfield's value is a BARE NUMBER everywhere (validator, SQL driverfloatcolumn, import coercion, field-zoo oracle); the currency code lives in field config". An author who follows either teaching writes an object value into a number column. - Resolution order. In
field-types.md, the "Currency resolution (ADR-0053)" paragraph says "the field's owncurrencyConfig.defaultCurrency→ the tenantlocalization.currencydefault", with no mode condition. The spec'sFieldSchemaguidance says a fixed currency is declared ascurrencyConfig: { currencyMode: 'fixed', defaultCurrency: … }and that "A field without one uses the tenant default at runtime".currencyConfig.defaultCurrencyalso DEFAULTS to'CNY'(CurrencyConfigSchema), so an unconditioned read makes every dynamic field show CNY. PR fix(i18n): a dynamic-mode currency field shows the tenant currency, not its defaultCurrency (#10422) objectui#10461 (in review) moves the renderer'sresolveFieldCurrencyto readdefaultCurrencyonly undercurrencyMode: 'fixed'. Once it lands, the skill's sentence describes behaviour no renderer has.
Grading notes (for triage, not a grade)
- Seam:
spec:CurrencyConfigSchema.currencyMode→renderer:resolveFieldCurrency(objectui). Both the skill and the docs page are consumers of it as documentation. - The shape of the fix is text only, re-derived from the spec: the value is a bare number in both modes.
fixedpinsdefaultCurrencyas the field's one currency. Otherwise the tenant default applies. ⛔ No spec change is asked for by this card. - An observation for whoever takes it, ⛔ not a claim: the spec's own
currencyModedescribe reads "dynamic (user selectable)". What "selectable" means for a bare-number value is worth one read against ADR-0053 before the text is rewritten.
Dedupe
REST page walk over the 1000 most recently updated objectstack items (oldest updated_at 2026-09-20). field-types.md|mdx near currenc ⇒ only objectstack#19847 (a closed PR about format, not currency). Currency resolution ⇒ 0. currencyMode near skill|docs|field-types ⇒ 0. per-record { value, currency } ⇒ 0. Must-hit control currency ⇒ 60 hits.
Dedupe words: field-types skill currency resolution · currencyMode dynamic per-record value currency · currency field bare number docs · defaultCurrency tenant default mode
Generated by Claude Code
Activity
objectstack-fleet commentedon Sep 25, 2026
Addendum for whoever carries this card, from the objectui
domain:uiseat #1 (session_01BA3nKVUwKQJf8DBxrSVtNC), 2026-09-25T03:33Z. ⛔ No label, grade or state is changed.The contract review of PR objectstack-ai/objectui#10461 (boundary flag ③2) names a third page with the same drift:
content/docs/data-modeling/fields.mdx. Its example authorscurrencyConfig: { currencyMode: 'dynamic', defaultCurrency: 'USD' }without saying that, underdynamic, USD is not the displayed currency. Once objectstack-ai/objectui#10461 lands, the renderer shows the tenant currency there. The runtime half of the same mode rule is filed separately as #20091 (service-analytics).domain:uiseat #1 · addendum · 2026-09-25T03:33Zobjectstack-fleet commentedon Sep 25, 2026
Path: business objects, records and views | records-forms.field-type-matrix | P2
Triage: first grade —
bug·documentation·priority:p2·domain:skills·area:records·pm:queue(findingremoved — graded)Triage: lands in
skills/objectstack-data/rules/field-types.md(:183, 「'dynamic' = per-record{ value, currency }」, and the "Currency resolution (ADR-0053)" paragraph at:189, onorigin/maina8bcce6) ⇒domain:skills, withcontent/docs/data-modeling/field-types.mdx(:155, "Whether currency is per-record or fixed") andcontent/docs/data-modeling/fields.mdx(the addendum5826233021) taken by the same claim as a declared cross-domain surface; rationale: teaching text that contradicts the spec — acurrencyvalue is a bare number in both modes (CurrencyValueSchemais@deprecatedfor exactly this), anddefaultCurrencyis the field's currency only undercurrencyMode: 'fixed'. The North Star counts a wrong sentence in AI-facing docs and skills as a product defect; runs but teaches wrong ⇒ p2.Triage seat #6015 ·
session_01CRZSc7dU8oDStbTbSwhuZe· 2026-09-25T04:54Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card and its one comment, andorigin/main. Dedupe over 1,113 cards (open, plus closed since 2026-09-18):currencyMode→ 4 hits (this card; #20091, its runtime sibling; #8345 and #20011, other questions).Execution notes
skills/**is a governed surface (Tier H, human approval), the twocontent/docspages are not. Land them separately so the docs fix does not wait on the approval.fixedpinsdefaultCurrency; otherwise the tenant default applies. Read "dynamic (user selectable)" in the spec's own describe against ADR-0053 first, as the card says.currencyMode, so adynamicfield'sdefaultCurrencyshows on analytics faces while field faces show the tenant currency #20091 (service-analytics, the runtime half of the same rule) is a separate lane; no serial constraint between them.objectstack-fleet commentedon Sep 25, 2026
Claim: PM loop round 1
Session:
session_0148fenvVvyQV9HYxgVDQ33qAccount:
os-tesla(the seat's linked user asGET /useranswers it; = the card's assignee)Branch:
claude/issue-20090-currency-mode-teachingWorktree:
objectstack-issue-20090Domain:
domain:skillsSeat:
domain:skills#1File surface:
skills/objectstack-data/rules/field-types.md(the "Currency with Precision" example :175–:186 and the "Currency resolution (ADR-0053)" paragraph :189–:193 atfa00ebf) on the branch above — Tier H (publishedskills/**); and, as the declared cross-domain surface triage named,content/docs/data-modeling/field-types.mdx(:155, thecurrencyModerow) +content/docs/data-modeling/fields.mdx(thecurrencyMode: 'dynamic', defaultCurrency: 'USD'example) on a second branchclaude/issue-20090-currency-mode-docs(worktreeobjectstack-issue-20090-docs), not governed — two draft PRs, bothPart of #20090, the seat closes the card after both land (stop on breach; explain in the report)Container & model:
S,mode:subagent,model: claude-fable-5-1—node scripts/pm/dispatch-gates.mjs --tier --repo objectstack-ai/objectstackon the three paths answers 「Model tier — MANDATORY: claude-fable-5-1」 onskills/**(the published catalog ships whole, no line-level exemption); the docs half rides the same dev; no exit takenClause-②: no
Thread-read: 5826979821
Serial constraints cleared: no in-flight claim declares these files (lane
pm:dispatchedset read at 2026-09-25T05:46Z: #20084 · #20088 · #20103 — all.claude/**; the devx in-flight card #19879 iserror-catalog.mdx); noissue-20090head onorigin; no open PR names the card. Sibling #20091 (service-analytics, the runtime half of the mode rule) is another lane, no serial constraint (triage note 3). Same-day churn:a666e949(#19847, 2026-09-24) touchedfield-types.mdx⇒ churn line in the dispatch.skills/**net-line budget set by the seat: ≤ +2 lines infield-types.md, whole-file and whole-package before/after readings owed.Ruling on this card (implemented as written, ⛔ not re-ruled): none from the maintainer — triage's first grade 5826979821 and its execution notes 1–3 apply; the contract is the spec (
packages/spec/src/data/field-value.zod.ts:23 / :63 「currencyIS a bare number」;field.zod.ts:439–:440currencyMode/defaultCurrency), ⛔ no spec change. Written by the seat 2026-09-25T05:49Z; the dev leaves no second claim — this comment is its identity, and it verifies the newestClaim:on this card names its branch before writing.Generated by Claude Code
objectstack-fleet commentedon Sep 25, 2026
os-dev-report
{ "issue": 20090, "status": "done", "branch": "claude/issue-20090-currency-mode-teaching", "pr": "https://github.com/objectstack-ai/objectstack/pull/20118", "session": "session_0148fenvVvyQV9HYxgVDQ33q", "premise_still_valid": true, "summary": "Two draft PRs, both `Part of #20090`, both base `main`, both authored by `objectstack-fleet[bot]` via the relay: PR A https://github.com/objectstack-ai/objectstack/pull/20118 (branch `claude/issue-20090-currency-mode-teaching`, head 7bf1503b, `skills/objectstack-data/rules/field-types.md` only, Tier H, stays draft, no ready flip / enqueue / auto-merge / review request; body carries `## 维护者速读(草稿)` with 席位意见 empty) and PR B https://github.com/objectstack-ai/objectstack/pull/20120 (branch `claude/issue-20090-currency-mode-docs`, head c982ebfa, `content/docs/data-modeling/field-types.mdx` + `fields.mdx` only, no meta.json change). Both carry `Clause-②: no` at line start, `skip-changeset` (measured: no package `files[]` names a `skills` path, positive control `packages/spec` files[] names `dist`; create-objectstack installs the catalog with `npx skills add objectstack-ai/objectstack/skills` from the repo, never its tarball; content/docs is docs-only) and assignee `os-tesla` (= the card's), each label write read back MATCHES. Teaching now, re-derived from the spec: value is a bare number in both modes (ADR-0104 D1), never `{ value, currency }`; `fixed` pins the column to `defaultCurrency`; `dynamic` (default) displays the tenant `localization.currency` setting and `defaultCurrency` is not the displayed currency; neither ⇒ plain number; a measure's own `currency` wins. Mechanism assumptions measured: (a) HELD — at fa00ebf4 the example comments sit at :182–:183 and the paragraph at :189–:193 (diff hunk `@@ -179,18 +179,18 @@`); (b) HELD, resolved — `docs/adr/0053-*` is `0053-date-and-datetime-semantics.md`, 0 currency hits; of 14 ADRs matching `currency` only ADR-0104 governs a currency rule (D1 value shape); no ADR and no `cloud ADR-NNNN` governs the display order, so the paragraph cites ADR-0104 D1 for the shape and DROPS the ADR-0053 citation (the tenant default is named by its producer, the `localization.currency` setting in `packages/services/service-settings/src/manifests/localization.manifest.ts`, whose header calls the manifest an 'ADR-0053 Phase 2 follow-up' — which is how the number was borrowed); (c) ADR silent — no ADR, key or runtime seam names where a per-record currency would be selected, so the skill teaches only bare number + tenant default and the 'dynamic (user selectable)' gap is in PR A `## Acceptance notes` with carrier; (d) PARTLY FALSIFIED — `check:skill-examples` is opt-in by an `os:check` marker above the fence and `field-types.md` carries zero markers, so it type-checks nothing in that file (green regardless: '259 prose examples type-check across 3 surface(s)'); the token ratchet read 3158/3158 (headroom 0) before, 3157/3158 after — the file had zero headroom, so the rewrite was paid inside the same sentences; (e) HELD — docs gates run, all green. Budget readings: field-types.md 328 → 328 lines (net 0, budget ≤ +2), package `skills/objectstack-data/**/*.md` 3717 → 3717 lines (15 files), bytes 12629 → 12625, tokens 3158 → 3157 (ceiling 3158), bundle 153973 → 153972. Deviations declared: ① the docs worktree took lock slot `OS_VERIFY_LOCK_SLOT=issue-20090-docs` (the named slot `issue-20090` served the skill worktree; one name for two concurrent queue entries would collide); ② `field-types.mdx` :156 (`defaultCurrency` row, 'Default currency code') and the section lead at :146 were edited beside the named :155 row — same table, same contradiction the card's item 2 argues (the CNY default); ③ 4 git pushes, not 2: an empty-branch probe plus one content push per branch; ④ `check:skill-examples` run in the skill worktree outside its derivation (it reads `skills/**`). Same-day churn checked: a666e949 (#19847) touched only the `format` row of `field-types.mdx`; the currency rows were unchanged at fa00ebf4. No maintainer ruling on the card; triage's execution notes 1–3 followed; no spec change.", "tests": "Gates derived per worktree with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no paths), every command run in the foreground via a runner that captures `$?` before any pipe, then reconciled with `--ran`. PR A worktree, head `7bf1503b` (`git rev-parse --short HEAD`): 23 derived, 23 run, all exit 0; `--ran` verdict 「23 derived, 23 run, 0 NOT-MEASURED, 0 UNRUN」 exit 0. Families: check-ci-filter-parity · check-closing-keyword-parity (+--self-test) · check-comment-mask-corpus · check-doc-route-spelling --advisory (+--self-test) · check-skills-token-ratchet (+--self-test) · lint check:doc-formula-expressions · check:agent-test-spelling · check:corpus-claim-drift · check:cross-package-test-inputs · check:doc-authoring · check:driver-memory-census · check:gitlink-declared · check:nul-bytes · check:pm-governed-merges · check:refd-timer-probe · check:role-word · check:skill-compatibility · check:skill-frame-sync · check:skill-identifier-liveness · check:watch-hint-literal; plus outside the derivation `pnpm --filter @objectstack/spec run check:skill-examples` exit 0 「✅ 259 prose examples type-check across 3 surface(s) — every marked block parsed」. Token ratchet before/after: 「skills/objectstack-data/rules/field-types.md is 3158 tokens (ceiling 3158; headroom 0)」 → 「is 3157 tokens (ceiling 3158; headroom 1)」, 「54 authored bundle file(s) within their ceilings」 both runs, exit 0. PR B worktree, head `c982ebfa`: 40 derived, 40 run, all exit 0; `--ran` verdict 「40 derived, 40 run, 0 NOT-MEASURED, 0 UNRUN」 exit 0. Families: check-ci-filter-parity · check-closing-keyword-parity (+--self-test) · check-comment-mask-corpus · check-doc-frontmatter (+--self-test) · check-doc-route-spelling --advisory (+--self-test) · check-docs-section-name (+--self-test) · check-section-landing-index (+--self-test) · lint check:doc-formula-expressions · check:doc-security-posture · spec check:docs · check:empty-state · check:liveness · check:skill-examples · check:strictness-ledger · check:variant-docs · check:yaml-examples · check:corpus-claim-drift · check:cross-package-test-inputs · check:doc-anchors · check:doc-authoring · check:docs-audit-scope · check:docs-redirects · check:docs-single-h1 · check:docs-spec-enumerations · check:docs-transcript-drift · check:driver-memory-census · check:gitlink-declared · check:nul-bytes · check:published-readme-links · check:react-page-adapter-contract · check:refd-timer-probe · check:role-word · check:skill-identifier-liveness · check:vendor-version-stamps · check:watch-hint-literal. Two exit-3 prerequisite refusals (「Nothing was measured」) were cleared by building the named packages and re-running to exit 0: check:doc-formula-expressions (formula + lint) and check:skill-examples (client-react). Builds under `bash scripts/pm/os-verify-lock.sh -c` with NODE_OPTIONS=--max-old-space-size=4096: spec build A 「VERDICT command-exit 0 · held the lock 172s」, spec build B 「VERDICT command-exit 0 · held the lock 188s」, formula+lint (turbo, cache hit) A/B 「VERDICT command-exit 0 · held the lock 1s」, client-react closure A 「VERDICT command-exit 0 · held the lock 177s」 (34 tasks), B 「held the lock 2s」 (34 cached); dist dirs verified present after each. Repo-wide `pnpm lint` not run (CI-owned). On-disk edit proof: anchor-guarded Python replacement (exact-count assertions), `grep -c per-record` 1 → 0 and `grep -c ADR-0104` 0 → 1 in field-types.md; `grep -c per-record` → 0 in field-types.mdx; `currencyMode: 'dynamic'` → 0 in fields.mdx. No ablation / reverse verification: prose only, no consumer parses it (a product that has no meaning here, said rather than templated). PR bodies read back after write: both byte-identical to the sent file (trailing newline aside), one footer each. CI on both PRs: in_progress at report time (not waited on).", "mcp_calls": "0 — no MCP GitHub tool called; all reads were REST GET via curl (card, comments, two pulls-by-head lists, two pull reads); all writes via scripts/pm/ tools on the fleet-write relay.", "api_writes": "5 relay dispatches carrying 7 REST writes, all as objectstack-fleet[bot]: pr_create → POST /repos/objectstack-ai/objectstack/pulls ×2 (run 36102823694 → #20118; run 36102881777 → #20120); label-write ×2, each one dispatch of two actions (POST .../issues/20118/labels + POST .../issues/20118/assignees, run 36102933184; POST .../issues/20120/labels + POST .../issues/20120/assignees, run 36102976254), read-back MATCHES both; os-dev-report comment → POST .../issues/20090/comments ×1 via post-stamped. Not REST: git push ×4 (empty-branch probe + content push per branch, both probes 200-class, no 403). No PATCH on any body, no card assignee write, no second claim, no ready flip, no review request.", "open_questions": [], "out_of_scope_findings": [ "class: b (candidate; the seat files or declines) · the spec describe `currencyMode: … 'dynamic (user selectable)'` (`packages/spec/src/data/field.zod.ts`) advertises a per-record selection no schema key, ADR or runtime seam provides — ADR-0104 D1 says the code 'lives in field config, not per value'; it surfaces verbatim in the auto-generated `content/docs/references/data/field.mdx` (2 rows, `gen:docs`). Seam: spec:CurrencyConfigSchema.currencyMode (describe) → renderer: none | consumer: content/docs/references/data/field.mdx. Not touched here (this card forbids a spec change; the page is auto-gen). carrier: spec lane (one-word describe change + `gen:docs`) · dedupe words: `currencyMode user selectable`, `dynamic user selectable describe`, `CurrencyConfigSchema describe currencyMode`, `references/data/field.mdx currencyMode`", "class: b · `skills/objectstack-ui/rules/dashboards.md` :304–:308 teaches the same unconditioned chain this card corrects (「resolves measure `currency` → the aggregated field's `currencyConfig.defaultCurrency` → the tenant `localization.currency` default (ADR-0053)」) — `defaultCurrency` is the field's currency only under `currencyMode: 'fixed'` per `field.zod.ts`, and ADR-0053 never names currency. Seam: spec:CurrencyConfigSchema.currencyMode → runtime: packages/services/service-analytics/src/analytics-service.ts (the measure chain, #20091's lane) | renderer: none. Governed `skills/**` (Tier H); a sibling teaching surface of #20091's runtime rule ⇒ report it as a sub-issue of #20091. carrier: #20091 · dedupe words: `dashboards.md currency chain`, `measure currency defaultCurrency tenant`, `objectstack-ui skill currency resolution ADR-0053`", "carrier: #20091 · noted, not filed — wrong ADR citation 'ADR-0053 currency chain' (ADR-0053 = date/datetime) in `content/docs/api/data-api.mdx` :467, `packages/services/service-analytics/src/plugin.ts` :1070 and `analytics-service.ts` :1792; the analytics chain there reads `defaultCurrency` unconditionally (`mc.currency ?? meta?.defaultCurrency ?? context?.currency`), which is exactly #20091's runtime half. Recorded in both PR bodies' Acceptance notes." ] }Generated by Claude Code
objectstack-fleet commentedon Sep 25, 2026
Review — ACCEPT · PR #20118 (skills, Tier H, head
7bf1503b) + PR #20120 (docs, headc982ebfa)Reviewed by the seat (
session_0148fenvVvyQV9HYxgVDQ33q, os-tesla) against GitHub and the fetched heads (three-dot diffs againstorigin/main7a13e05), 2026-09-25T06:40Z.Checklist, verified on both PR objects: draft ✓ · base
main✓ · first body linePart of #20090on both (neither PR alone completes the card; the seat closes it after both land) ✓ ·Clause-②: noat line start ✓ · labelsdocumentation·size/s·skip-changeset(measured by the dev: no packagefiles[]names askills/path, positive controlpackages/specnamesdist; docs-only for PR B) ✓ · assigneeos-tesla= the card's ✓ · PR A: 1 file, +7 / −7,field-types.mdonly; PR B: 2 files, +12 / −5, the twocontent/docs/data-modelingpages only (defaultCurrencyrow and the section lead edited beside the named row — same table, same contradiction, declared) ✓ · report comment 5827983271 first lineos-dev-report✓ ·mcp_calls0, seven REST writes in five relay strokes, inside the budget ✓ · newestClaim:still 5827510912 naming the primary branch ✓ · no closing keyword near any card number, no model identifier, one footer each ✓ · PR A carries## 维护者速读(草稿)✓.Spot-check readings (seat's own): every new sentence is re-derived from the spec, not the renderer — bare number in both modes (
field-value.zod.ts:23 / :63; ADR-0104 :60 「all treat currency as a bare number」, D1 = the spec owns the value-shape contract, verified indocs/adr/0104-field-runtime-value-shape-contract.md);fixedpins the column todefaultCurrency,dynamic(default) displays the tenantlocalization.currencysetting anddefaultCurrencyis not the displayed currency (field.zod.ts:439–:440 + the FieldSchema guidance); neither ⇒ plain grouped number; a measure's owncurrencywins. The ADR-0053 citation is dropped —docs/adr/0053-*is the date / datetime record (0 currency hits); the number had been borrowed from the localization manifest's 「ADR-0053 Phase 2 follow-up」 header. The renderer half objectui#10461 is MERGED, so the text now describes both the spec and a shipping renderer. Budget held:field-types.md328 / 328 (≤ +2 allowed), package 3717 / 3717 lines, tokens 3158 → 3157 against the token ratchet ceiling 3158. Mechanism assumption (d) was partly false and the dev measured it:check:skill-examplestype-checks onlyos:check-marked blocks andfield-types.mdcarries none — an observation recorded, not a defect of this PR.Out-of-scope findings (three, all class b) — under the closure rule (#20088, landing as PR #20110) they are one family, not three cards:
filed #20126— the currency-mode class-closure card, enumerating the spec describe 「dynamic (user selectable)」 (site 1),skills/objectstack-ui/rules/dashboards.md:304–:308 (site 2),content/docs/api/data-api.mdx:467 and the analytics comments / chain (sites 3–4, #20091's runtime half), with an enumerating pin; barefinding, for triage to route or split. Nothing dropped.Landing: PR B (#20120, not governed) —
## Contract reviewPASS 5828034280 on the PR; the seat lands it through the relay once every check onc982ebfais green. PR A (#20118, Tier H,skills/**) — the four-piece terminal is in place: this review of record; the PR left DRAFT withneeds-user-decisionand the seat's 终稿「维护者速读」 5828044359;## Contract reviewPASS 5828039071; reviews requested fromos-zhuangandhotlong— awaiting a human APPROVED review, then the owning seat lands it through the relay. The card closes when both PRs are MERGED (Part ofcloseout by the seat).Generated by Claude Code
objectstack-fleet commentedon Sep 25, 2026
Closeout — both
Part ofPRs MERGED · card closed completedPR #20120 (docs half,
content/docs/data-modeling/field-types.mdx+fields.mdx) MERGED 2026-09-25T06:54Z through the queue on the seat's## Contract reviewPASS 5828034280; PR #20118 (skills half,skills/objectstack-data/rules/field-types.md, Tier H) APPROVED by os-zhuang and MERGED by the maintainer's hand — the two halves of this card are onorigin/main(both readings taken by the seat 2026-09-25T08:44Z:git log origin/maincarries(#20118)and(#20120); both PR objects readmerged). The out-of-scope family is carried by the class-closure card #20126. Closed as completed by the seat (session_0148fenvVvyQV9HYxgVDQ33q, os-tesla);pm:dispatchedoff and assignee cleared in the same stroke.Generated by Claude Code
docs(data-modeling): currency is a bare number in both modes; default…
docs(skills): currency is a bare number in both modes; the resolution…
test(spec): the currency-mode family's enumerating closure pin (objec…