Repository navigation
Commit ecb6ca0
Fixes #17306
Clause-②: yes (widening)
The restart shape pre-written on the card (comment 5908590307, unlocked
by 5947573357), checked against `origin/main` before any edit. **Part 2
lands. Part 1's premise was falsified by measurement, and nothing was
flipped** (see *Premise check* below).
The rulings this executes are A (5643444726) and A′ (5651909056): the
screen-field keys ship together with their rendering (「声明即强制」). The
rendering is objectui#9248 → objectui `81778b955`, and this repo's
`.objectui-sha` pin `31971ff1e28f` carries it: REST `compare
81778b955575...31971ff1e28f` on objectui answers `ahead`, `ahead_by
100`, `behind_by 0`.
## What changes
- **`FLOW_SCREEN_FIELD_COPY_KEYS`** (`@objectstack/spec/system`) is now
`['label', 'placeholder', 'inlineHelpText']`, so a flow screen field's
help text gets a per-field translation key. The key is the screen
field's own spelling (`ScreenFieldConfig.inlineHelpText`, which is also
the object field's spelling). Both overlays write the translation back
onto that same key.
- **`TranslationDataSchema`**: `flows.FLOW.screens.NODE_ID.fields.FIELD`
declares `inlineHelpText`. Five help spellings (`help`, `helpText`,
`hint`, `tooltip`, `description`) used to get guidance saying the face
had no help key. They are now **aliases** onto `inlineHelpText`, so the
`.strict()` refusal names the rename. `options` / `choices` / `values`
keep their guidance. Nothing that parsed before is refused.
- **`FlowScreenFieldLike`** gains `inlineHelpText?: string`.
`translateFlow` needs no logic change, because `translateScreenField`
spreads whatever the constant resolved.
- **`liveness/translation.json`** (hand-kept): the `flows.screens` row
was already `live`. It is re-read at the pin `31971ff1e`: every objectui
pointer in `evidence` and `producer` is repinned from `f8a9d0fb`,
`overlayFieldCopy` and `ScreenView` are added as readers, and
`verifiedAt` is 2026-10-02. The `flows` container's `authorHint` and the
stale help sentence in its note are corrected. **No status moved**, so
`state-counts/` and the README count rows are unchanged
(`check:liveness`: current, 10 `planned` in total, as before).
- **`content/docs/ui/translations.mdx`**: the flows row now lists
`.inlineHelpText`. The boundary note no longer says that a screen field
has no help text, or that no runner reads the group.
- **`content/docs/automation/flows.mdx`** (patch round 2, head
`0b151ee532`): the screen-field paragraph that said `inlineHelpText` was
"not translatable yet" now says it is translated under the flows face,
beside `label` and `placeholder`, and links the flows row in
Translations. This PR made the old sentence false; the at-tier record
`5950219612` named it.
- **Changeset** `@objectstack/spec: minor`, carrying the same `Clause-②:
yes (widening)` line.
## Every reader of the constant, followed
| Reader | Where | What it needed |
|:---|:---|:---|
| spec resolver | `i18n-resolver.ts#lookupFlowScreenFieldCopy` /
`#translateScreenField` | Nothing. It walks the constant and spreads the
result. New pin in `i18n-resolver.test.ts`. |
| translation schema | the `flows` field node in `translation.zod.ts` |
The member. Without it `.strict()` would refuse the key the extractor
writes. The existing "declares exactly the keys" pin now iterates three
keys. |
| lint walk | `packages/lint/src/validate-translation-references.ts` |
Nothing. It resolves flow, screen and field **names** and never reads
copy keys. |
| CLI extractor and coverage |
`packages/cli/src/utils/i18n-extract.ts#walkScreenFlows` | Nothing in
source, because it imports the constant. Four CLI pins listed literal
keys and are updated. "Scaffolds a bundle the strict schema accepts" now
parses a skeleton that carries `inlineHelpText`. |
| objectui runner | objectui @31971ff1e
`packages/app-shell/src/views/FlowRunner.tsx#overlayFieldCopy` |
Nothing. It imports the constant (its header says the day the spec lists
the key it is translated with no edit there). `copy[key]` is typed from
the spec's `TranslationData`, which now carries the member. |
## Premise check: part 1 (the four `planned` liveness rows) does not
exist
The dispatch's mechanism assumption 1 was that some ledger holds
`planned` rows for the screen-field keys `min`, `max`, `inlineHelpText`
and `reference`. Measured at base `9360df4138`:
- No ledger under `packages/spec/liveness/` has a row for any of the
four. `git grep -n inlineHelpText -- packages/spec/liveness` hits only
`field.json` (the OBJECT field's row) and `translation.json`.
- `flow.json` stops at `nodes.config`, which is `z.record(z.string(),
z.unknown())` (`packages/spec/src/automation/flow.zod.ts`), so the
gate's walk cannot reach a node config key.
- **Probe.** `children: { min: … }` was added under `flow.json`'s
`nodes.config`, then `check:liveness` was run. It exited 1 with `✗ 1
UNCLASSIFIED … flow/nodes.config (declared children but property is not
a container)`. The file was restored with `git checkout HEAD --`, and
its blob hash equals HEAD's. A row for these keys cannot be added, so
there is nothing to flip.
- None of the four keys' `.describe()` carries a `planned` /
`experimental` marker either.
The rendering half the flip was meant to record is cited below instead.
All four readers exist at the pin, so no key is held back.
## objectui readers at the pin (`31971ff1e`)
- `min` / `max`: `ScreenView.tsx#ScreenFieldInput` puts the native `min`
/ `max` on the numeric input.
`ScreenView.tsx#screenFieldBoundViolations` is the submit-time
comparison (inclusive, present finite number, hidden fields skipped).
`FlowRunner.tsx#FlowRunner` refuses the submit through it and names the
field.
- `inlineHelpText`: `ScreenView.tsx#ScreenView` draws it under the
control, and the control names it in `aria-describedby`.
- `reference`: `ScreenView.tsx#ScreenFieldInput` renders the shared
`LookupField` widget over `field.reference` on a `type: 'lookup'` field.
## Tests (head `a7f3557b11`, after merging `origin/main` at
`3937ad2f32`)
- `pnpm --filter @objectstack/spec exec vitest run --project local
--maxWorkers=2`: **598 files, 17531 passed, 1 todo**.
- `pnpm --filter @objectstack/spec typecheck`: exit 0 (`tsc`, scripts,
and `check:test-typecheck`: 52 files, 246 errors, 135 pinned signatures
held).
- `pnpm --filter @objectstack/cli exec vitest run --project unit
--maxWorkers=2`: **245 files, 3465 passed**. The `integration` layer is
declared to CI: this diff touches no spawn entry and no integration
file.
- `pnpm --filter @objectstack/cli typecheck`: exit 0.
- `pnpm --filter @objectstack/spec check:generated`: all 15 generated
artifacts up to date, against a `dist` rebuilt after the merge.
- **Reverse verification** (one-off, not committed). A scratch module
was compiled against the rebuilt `@objectstack/spec` `.d.ts`. It
assigned `'inlineHelpText'` to `FlowScreenFieldCopyKey`, `{
inlineHelpText }` to the `TranslationData` flows field node, and
`'help'` to `FlowScreenFieldCopyKey`. Result: exactly one error, on the
`'help'` line (`TS2322 … not assignable to type '"label" | "placeholder"
| "inlineHelpText"'`). The module was deleted and `git status` is clean.
## Gates (union run on `a7f3557b11`)
`node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--commands` derived **109** commands with no paths passed. All 109 ran,
plus `check:i18n`, `check:i18n-coverage` and `check:i18n-stale-fill`,
measured for the dispatch's coverage question. **112 runs, all exit 0.**
`--ran` reconciliation: `109 derived, 109 run, 0 NOT-MEASURED, 0 UNRUN`.
- The first local run of three gates refused with exit 3, PREREQUISITE
NOT MET (`check:skill-examples`, `check:dual-build-cjs-loads`,
`check:i18n-walk-parity`). They were re-run after the build and are
green on the final head.
- **Translation coverage** (dispatch assumption 4):
`check:i18n-coverage` OK (13 configs, 621 baselined, none new).
`check:i18n` OK (9 packages in sync). No example app or platform bundle
authors `inlineHelpText` on a flow screen field. The only hit in
`examples/` is an object field, `app-showcase` `contact.object.ts`. The
CLI's whole `flows.*` demand is also still held back by the `flows`
row's `authorWarn`, which waits on the flow-label reader.
- **Lint, narrowed.** Population read from eslint itself: all 6 changed
`.ts` files report `isPathIgnored: false`. The changed `.md`, `.mdx` and
`.json` files are outside the config's
`**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}` files. `eslint --no-inline-config
--format json` on the 6 files: **6 files, 0 errors, 0 warnings** on
`a7f3557b11`. Invariance: `eslint.config.mjs` enables no type-aware
linting (no `parserOptions.project`, no typed rules, as its own comment
states), so this diff cannot move a verdict on any untouched file. The
repo-wide `pnpm lint` is CI's run.
## NOT MEASURED
- **Console Pin Gate** (objectui at the pin built against this spec):
NOT MEASURED locally, because building objectui does not fit this
container's foreground budget. CI does not run it either: the `console`
path filter in `ci.yml` excludes `packages/spec/**` by design, so the
check is `skipped` on this PR. What stands is the static reading at the
pin, which the at-tier record `5950219612` verified independently:
`FlowRunner.tsx` imports the constant, `overlayFieldCopy` indexes
`copy[key]` with the widened key, `ScreenView.tsx` draws
`inlineHelpText` under the control, and `scripts/build-console.sh`
bundles this tree's spec into the console.
## Acceptance notes
- The four screen-field keys have no liveness-ledger seat at all,
because `nodes.config` is opaque to the walk (probe above). Their
declared-vs-read reconciliation lives in `service-automation`'s
`builtin-node-form-zod-ledger.test.ts` and
`screen-input-contract.test.ts`, not in `liveness/`. Noted, not filed:
this is the design of the ledger's one-drill-level boundary, not a
defect.
- `content/docs/ui/translations.mdx`'s next paragraph ("The day the
runner lands and the row flips to `live` …") still describes the
flow-label half correctly and is unchanged.
- `origin/main` moved again after the merge (`d78bd011ea`, `11905a4f8b`:
CI-filter parity and `os generate`). Neither touches this diff's files.
The re-derivation printed the same 109 commands.
## Review round 1
- At-tier contract review **PASS** on `a7f3557b11` (comment
`5950219612`). Its ③ escalated one sentence this PR made false, in
`content/docs/automation/flows.mdx`. Patch round 2 (`0b151ee532`, +5 /
-3 in that file alone) corrects it. No code moved.
- The same record judged the five help spellings REFUSED with a rename
to `inlineHelpText` (no second spelling admitted), `minor` / `Clause-②:
yes (widening)` right, and part 1's falsification verified by reading
the ledger and `check-liveness`'s source.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3b4efa7 commit ecb6ca0
10 files changed
Lines changed: 167 additions & 104 deletions
File tree
- .changeset
- content/docs
- automation
- ui
- packages
- cli/test
- spec
- liveness
- src/system
Lines changed: 13 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
489 | 489 | | |
490 | 490 | | |
491 | 491 | | |
492 | | - | |
493 | | - | |
494 | | - | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
495 | 497 | | |
496 | 498 | | |
497 | 499 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
80 | 80 | | |
81 | 81 | | |
82 | 82 | | |
83 | | - | |
| 83 | + | |
84 | 84 | | |
85 | 85 | | |
86 | 86 | | |
| |||
382 | 382 | | |
383 | 383 | | |
384 | 384 | | |
385 | | - | |
| 385 | + | |
386 | 386 | | |
387 | | - | |
388 | | - | |
389 | | - | |
390 | | - | |
391 | | - | |
392 | | - | |
393 | | - | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
394 | 395 | | |
395 | 396 | | |
396 | 397 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
193 | 193 | | |
194 | 194 | | |
195 | 195 | | |
| 196 | + | |
196 | 197 | | |
197 | 198 | | |
| 199 | + | |
198 | 200 | | |
199 | 201 | | |
200 | 202 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
90 | 90 | | |
91 | 91 | | |
92 | 92 | | |
93 | | - | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
94 | 101 | | |
95 | 102 | | |
96 | 103 | | |
| |||
137 | 144 | | |
138 | 145 | | |
139 | 146 | | |
| 147 | + | |
140 | 148 | | |
141 | 149 | | |
142 | 150 | | |
| |||
166 | 174 | | |
167 | 175 | | |
168 | 176 | | |
169 | | - | |
| 177 | + | |
170 | 178 | | |
171 | 179 | | |
172 | 180 | | |
| |||
192 | 200 | | |
193 | 201 | | |
194 | 202 | | |
| 203 | + | |
195 | 204 | | |
196 | 205 | | |
| 206 | + | |
197 | 207 | | |
198 | 208 | | |
199 | 209 | | |
| |||
276 | 286 | | |
277 | 287 | | |
278 | 288 | | |
| 289 | + | |
279 | 290 | | |
280 | 291 | | |
281 | 292 | | |
| |||
424 | 435 | | |
425 | 436 | | |
426 | 437 | | |
| 438 | + | |
427 | 439 | | |
428 | 440 | | |
429 | 441 | | |
430 | 442 | | |
431 | 443 | | |
| 444 | + | |
432 | 445 | | |
433 | 446 | | |
| 447 | + | |
434 | 448 | | |
435 | 449 | | |
436 | 450 | | |
| |||
0 commit comments