Repository navigation
fix(platform-objects,plugin-audit): nav entries for the console's Audit Log and Integrations & APIs pages - #20699
Conversation
…it-log, AI-approvals and integrations pages Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 11 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 282451af4831aa1bf93e3e943abd65ca67baee8b && git checkout 282451af4831aa1bf93e3e943abd65ca67baee8b
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ed54768703b393cec280c249b549b01b14b44080 d4744797b9c99714e475160eafd7afcc4d85552d && git checkout -B drift-repro ed54768703b393cec280c249b549b01b14b44080 && git merge --no-ff d4744797b9c99714e475160eafd7afcc4d85552d
node scripts/docs-audit/affected-docs.mjs --json ed54768703b393cec280c249b549b01b14b44080
|
…ability's to contribute (ADR-0029 D7) Restores platform-objects.test.ts's K2.b slot pin and the Connect-an-Agent Setup tree count to main, removes nav_ai_approvals with its label leaves and source hashes, and narrows the new pins to the two entries this repo ships. Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs: card #20142 (body and its six comments, seat answer 5896257587 and both Check-runs at read time: every one of the seven required contexts is ① Derived judgmentsAccept set: unchanged — right. The diff adds no Zod key, no closed-set member, no Setup Studio
Both Three new pins, each on the half its package owns — right. Shipped prose, the four locales — right, with one observation. en Docblocks on exported symbols — right. The Hand-written docs the drift bot listed — no sentence goes false. Hand-written doc the drift bot could not list — one row goes false: FAIL item. PR body and title, judged as the squash message — three statements the diff falsifies: FAIL item. (a) The title still reads "nav entries for the console's Audit Log, AI Approvals and Integrations & APIs pages"; the head ships no AI Approvals entry, the body itself says "adds two of the three", and the title is the first line that lands in ② Semver level
③ Boundary flags
FAIL items, both fixable in one patch round on this branch:
Implemented-by: VERDICT: FAIL Generated by Claude Code |
Claude-Session: https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Delta record, superseding 5896816094 (FAIL at What moved since the prior record. One commit, Check-runs at read time: ① Derived judgmentsPrior FAIL item 2 — Prior FAIL item 1 — title and body as the squash message: discharged, right. (a) Title now Every sentence the seat's edit added, judged.
Every other factual sentence in the body — carried from 5896816094 and spot-read here. H1 ( Accept set and public surface: unchanged — carried. No Zod key, closed-set member, ② Semver levelCarried: ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
…tradict their current en source (objectstack-ai#20707) Fixes objectstack-ai#20666 Clause-②: no ## What changed A translated leaf in `packages/platform-objects/src/apps/translations/{ja-JP,es-ES,zh-CN}.metadata-forms.generated.ts` that a translator wrote by hand keeps its value when its `en` source changes later. This PR measures that set first, with PR objectstack-ai#20652's instrument and the two widening checks the services seat named, then re-translates the leaves whose meaning now **contradicts** the current `en`: **12 leaves** (ja-JP 4, es-ES 4, zh-CN 4). - Values only. No key is added or dropped, no `en` file is edited, and no provenance companion or echo-decision ledger changes (measured below; the tooling requires none). - `.changeset/20666-stale-authored-metadata-form-leaves.md`: `@objectstack/platform-objects` `patch`. - No new gate, per triage. No test pins any of the old or new strings. - File surface held: the three translated `metadata-forms` bundles and the changeset. Not the object bundles, not the plugin bundles, and not `{en,ja-JP,es-ES,zh-CN}.ts` or `*.source-hashes.ts` (PR objectstack-ai#20699). ## The measurement (base `a8acee28d`, before any edit) ### Instruments The clone is not shallow (`git rev-parse --is-shallow-repository` answers `false`, 15122 commits), so every walk below reads full history. 1. **Since last edit** (PR objectstack-ai#20652's condensed instrument, ported with two edits: the bundle kind `objects.generated.ts` becomes `metadata-forms.generated.ts`, and the recorded-copy test reads `"metadataForms.PATH"` in both `LOCALE.source-hashes.generated.ts` and `LOCALE.source-hashes.ts`). Population: every string leaf of the locale's metadata-forms bundle, minus echoes of the current `en` and recorded byte copies. Last edit: the first-parent commit where the parsed value last changed. A leaf is a candidate when `en` at that commit differs from `en` today. Meaning is then judged by hand. 2. **Renamed key.** A leaf whose path was born at its last edit, while the same value sat under a path (any path in the bundle, not only a sibling) that disappeared in that commit. For each, `en` at the old path's last edit is compared with `en` at the new path today. 3. **Penultimate edit.** For each non-candidate, `en` at its previous edit, and whether `en` moved in the last-edit commit itself. Every flag was read by hand, including the ones where `en` moved in the same commit. 4. **Merge side.** For a leaf last edited by a merge commit, `en` is also read at the second parent. 5. **Retired term.** The locale's word for `project`, `department`, `role` (ADR-0090) or `profile` (ADR-0090 D2), where `en` at the same path does not carry the English term. The raw ja-JP `role` pattern hit four leaves, all false positives (ロールアップ roll-up three times, コントロール control once); the refined pattern excludes both and hits 0. 6. **Retired concept** (added here; the concept-level twin of PR objectstack-ai#20652's retired-term scan). For each re-model the contradicting candidates surfaced, the locale's words for the retired concept, flagged where `en` at the same path does not carry it: agent tools (ADR-0109 removed `agent.tools[]`, scope `agent.*`); `PermissionSet.contextVariables` (removed by the ADR-0105 commit `879ea1304`); sharing rules named for RLS; an email template sent by `id` with a content type (`47a92f427` re-modelled the type); a report block that joins objects (ADR-0021 dataset-bound reports, `18178454c`, scope `report.*`). 7. **Carve-in continuity.** The zh-CN leaves last edited at `e0077ea36` were carried in from a hand overlay, `packages/platform-objects/src/metadata-translations/zh-CN.ts`, born at `dc721729a` and consolidated at `e0077ea36` the same day. So the instrument dates them at the carve-in. Between those two commits `packages/spec/src` changed no form label, description or help string (`git diff dc72172 e0077ea -- packages/spec/src`: two changed `label` / `description` lines, both schema declarations, no string). The carve-in dating therefore hides no source move inside the overlay's lifetime. ### Known-positive controls - `permission.sections.tab_and_row_level_security.description` (line 2251 of all four bundles) and `agent.sections.capabilities.description` (line 2347). The since-last-edit instrument flags both in **all three** locales. In ja-JP and es-ES both promise what `en` dropped (custom context variables; tools). zh-CN's agent leaf promises tools too, but zh-CN's permission leaf never promised context variables: it names the section after sharing rules, read below. - Renamed-key check: `dashboard.fields.refreshIntervalSeconds.label`, renamed from `refreshInterval` at `e9fcd6bbd` with the value carried. It fires in all three locales. - Retired-concept scan: it fires on every one of the four contradicting paths in ja-JP and es-ES, and on the three in zh-CN. - Provenance (for H2): with ja-JP `agent.sections.capabilities.description` set to the `en` string, `check-i18n-bundles --filter=platform-objects` goes red, `platform-objects DRIFTED (1)` (through `scripts/ablation-replace.mjs`: anchor 1 to 0, blob `1b912e07c279` to `70cb7a0e1ba3`, restored: blob equals HEAD and `git diff HEAD` is empty). In hold mode, `--write` adds exactly one row, `"metadataForms.agent.sections.capabilities.description"`, to `ja-JP.source-hashes.generated.ts`. Both files were restored with `git checkout HEAD --`, and their blobs equal HEAD's (`1b912e07c279`, `2429910c839c`). ### Population and radius - **Covered:** the `metadata-forms` bundles of `@objectstack/platform-objects` for ja-JP, es-ES and zh-CN. There are 1116 leaves per locale. Echoes of the current `en` number 0 / 0 / 0, recorded byte copies 0, and authored leaves **1116 / 1116 / 1116**. History: every first-parent commit since the bundles were created at `ae2da1e7d` (69 / 70 / 70 commits touching each locale file, 56 for `en`), plus the zh-CN overlay's lifetime (instrument 7). - **Not covered:** a leaf that was wrong when it was authored and whose `en` never moved, outside the vocabularies of instruments 5 and 6; where the zh-CN overlay's strings came from before `dc721729a` (the file was born with them); the object bundles, the plugin bundles and the other packages' bundle sets. ### Counts per locale | | ja-JP | es-ES | zh-CN | |---|---|---|---| | authored leaves | 1116 | 1116 | 1116 | | candidates (`en` moved since last edit), before | 9 | 9 | 6 | | of which contradicting, before | 4 | 4 | 3 | | penultimate-edit flags, before (contradicting) | 18 (0) | 18 (0) | 22 (0) | | renamed-key flags, before (contradicting) | 1 (0) | 1 (0) | 1 (0) | | merge-side flags, before | 0 | 0 | 0 | | retired-term flags, before | 0 | 0 | 0 | | retired-concept flags outside the candidates, before (contradicting) | 0 | 0 | 1 (1) | | **re-translated here** | **4** | **4** | **4** | | candidates after (at `aaee46778`) | 5 | 5 | 3 | | contradicting after | 0 | 0 | 0 | | retired-concept flags after | 0 | 0 | 0 | The raw candidate counts equal the objectstack-ai#20653 dev's 9 / 9 / 6 exactly. After, both walks re-run at `aaee46778`: each after-candidate set is exactly the before set minus the re-translated paths (0 added), the echo count is still 0 (no new value equals its `en` leaf), and the renamed-key flag is the same one. The penultimate-edit flags after are the before set plus the 11 re-translated candidates (their last edit is now this PR, after `en` moved), which is the expected shape. ### The judgment rule As in PR objectstack-ai#20652 and PR objectstack-ai#20684. A leaf **contradicts** the current `en` when a reader who acts on it would believe something about the current form that `en` now says is false: `en` now denies what the leaf asserts; the type was re-modelled, so the leaf describes a different thing; or the leaf names a mechanism the section does not hold. Added detail, narrowing, rewording, punctuation and title-casing are not contradictions. ## Re-translated (12): before and after **`agent.sections.capabilities.description`**, all three. ADR-0109 removed `agent.tools[]` (`e2616e0cf`); an agent's tools come from its skills. `en`: `Skills and knowledge sources the agent can use.` - ja-JP: 「エージェントが使用できるスキル、ツール、ナレッジソース。」 → 「エージェントが使用できるスキルとナレッジソース。」 - es-ES: 「Skills, herramientas y fuentes de conocimiento que puede usar el agente.」 → 「Skills y fuentes de conocimiento que puede usar el agente.」 - zh-CN: 「代理可使用的技能、工具与知识来源」 → 「代理可使用的技能与知识来源」 **`permission.sections.tab_and_row_level_security.description`**, all three. `879ea1304` removed `PermissionSet.contextVariables` (enforce-or-remove, zero consumers) and its form row. `en`: `Tab visibility and RLS policies.` - ja-JP: 「タブ表示、RLS ポリシー、述語評価用カスタムコンテキスト変数。」 → 「タブ表示と RLS ポリシー。」 - es-ES: 「Visibilidad de pestañas, políticas RLS y variables de contexto personalizadas para evaluar predicados.」 → 「Visibilidad de pestañas y políticas RLS.」 - zh-CN: 「导航可见性与共享规则」 → 「标签页可见性与行级安全策略」. The old value named the section after **sharing rules**. In this repository 共享规则 is the zh-CN name of a different mechanism, the `sys_sharing_rule` object (`plugin-sharing`) and its own Setup entry `nav_sharing_rules`. The section holds `tabPermissions` and `rowLevelSecurity`. The new value uses the bundle's own words: 标签页 (this section's label), 行级安全策略 (the bundle's rendering of an RLS policy, as in `object.fields["access.default"].helpText`). **`report.fields.blocks.helpText`**, all three. The 9.0 single-form cutover (ADR-0021, `18178454c`) made every report block a dataset-bound sub-report; no block joins objects. `en`: `Dataset-bound sub-reports (joined report only)` - ja-JP: 「複数オブジェクトを結合(joined レポートのみ)」 → 「データセットにバインドされたサブレポート(joined レポートのみ)」 - es-ES: 「Une varios objetos (solo informe joined)」 → 「Subinformes vinculados a un conjunto de datos (solo informe joined)」 - zh-CN: 「joined 报表的联合查询块」 → 「绑定数据集的子报表(仅 joined 报表)」 **`email_template.sections.identity.description`**, ja-JP and es-ES. `47a92f427` promoted `email_template` to a first-class metadata type and rewrote its form. The Identity section now holds `name` / `label` / `category` / `locale` / `description`, with no `id` and no content type, and `IEmailService.sendTemplate` resolves the template by `name`. A reader who copied the old sample would pass `template: id`. zh-CN already said what `en` says. `en`: `Template identifier resolved by IEmailService.sendTemplate({ template: name, locale, ... }).` - ja-JP: 「識別子とコンテンツ型。id は sendTemplate({ template: id, ... }) で参照される。」 → 「IEmailService.sendTemplate({ template: name, locale, ... }) が解決するテンプレート識別子。」 - es-ES: 「Identificador y tipo de contenido. El id se referencia con sendTemplate({ template: id, ... }).」 → 「Identificador de plantilla que resuelve IEmailService.sendTemplate({ template: name, locale, ... }).」 **`report.sections.joined_blocks.label`**, zh-CN only. It was found by the retired-concept scan, not by the since-last-edit instrument, because `en` (`Joined blocks`) never moved. The zh-CN label said 关联对象 ("related objects"), the pre-9.0 model in which blocks joined objects. The section holds the `blocks` repeater of dataset-bound sub-reports. ja-JP 結合ブロック and es-ES Bloques unidos render `en` literally and are unchanged. - zh-CN: 「关联对象」 → 「joined 报表分块」 (分块 is `report.fields.blocks.label`, and `joined` stays the report-type token, as in 仅 joined 报表). ## Stale but not contradicting (listed, left as written) | path | locales | what `en` did | |---|---|---| | `object.sections.fields.description` | all three | "each row becomes a column" became "each entry becomes a column" (`9f8ec35c8`). Rewording. | | `hook.fields.condition.helpText` | all three | "Optional formula — skip the hook when this evaluates to false" became "CEL predicate — the hook runs only when TRUE" (`48efe915b`, which changed the row's editor language from `javascript` to `expression`). The runtime gate is `if (!conditionFn(ctx))` in `packages/objectql/src/hook-wrappers.ts`, whose own docblock says "skip when the formula evaluates FALSE", and the leaf makes no JavaScript claim. This is the closest call among the leaves left alone. | | `app.fields.defaultAgent.helpText` | ja-JP, es-ES | "AI agent" became "Platform agent", with the `ask` / `build` defaults added. Narrowing and detail. zh-CN already says what `en` says. | | `report.sections.joined_blocks.description` | ja-JP, es-ES | "blocks joined into a single report" became "dataset-bound blocks stacked into a single report". Combined into one report is still true, and the `en` label still says Joined blocks. The second closest call. zh-CN already says what `en` says. | | `report.sections.filter_and_chart.description` | all three | "Report-level filters" became "Render-time scope filter". The filter is still the report's. | | `dashboard.fields.refreshIntervalSeconds.label` (renamed key) | all three | The rename added the unit to the label. Each locale's `helpText` on the same field already says seconds (自動更新(秒), Actualización automática (segundos), 自动刷新间隔(秒)). | Penultimate-edit flags, all read by hand. Every one already says what `en` says: - `en` moved EARLIER than the last edit: `object.fields.isSystem.helpText`, `object.fields.fields.reference.helpText`, `object.fields.fields.trackHistory.helpText`, `view.fields.filter.helpText`, `page.fields.type.helpText`, `dashboard.fields.gap.helpText`, `action.sections.advanced.description`, `action.fields.body.helpText`, `report.sections.basics.description`, `report.fields.columns.helpText` and `dataset.fields.measures.helpText` (all three locales); plus, zh-CN only, `app.fields.defaultAgent.helpText`, `report.sections.joined_blocks.description`, `email_template.sections.identity.description` and `email_template.fields.variables.helpText`. - `en` moved IN the last-edit commit: `object.fields.indexes.fields.helpText`, `field.fields.precision.helpText`, `field.fields.maskingRule.helpText`, `page.fields.variables.source.label`, `app.sections.content.description`, `app.sections.access_and_sharing.description` and `permission.sections.identity.description` (all three locales). ## Dispatch hypotheses, measured - **H1 held, and widened.** The targets are exactly the three translated bundles against `en.metadata-forms.generated.ts`. Both known leaves are candidates in all three locales, and the raw counts reproduce as 9 / 9 / 6 at `a8acee28d`. Judged, 4 / 4 / 3 contradict. Two more paths besides the known two contradict: `report.fields.blocks.helpText` (all three) and `email_template.sections.identity.description` (ja-JP, es-ES). zh-CN's permission leaf contradicts for a different reason than the known one (sharing rules). The retired-concept scan added one zh-CN label. - **H2 held: values only.** The companions work as they do for the object bundles. `collectFilledFromHashes` records a `metadataForms` leaf only while it is a byte copy of the current `en` or of a recorded digest. Today that is 0 rows in all three locales, and a hand translation records nothing. At `aaee46778`, `node scripts/check-i18n-bundles.mjs --write --filter=platform-objects` printed `regenerated` and rewrote all eleven bundle and companion files on disk (every mtime moved), and `git status --porcelain` stayed empty. The control that shows the tool really reads these leaves is in "Known-positive controls" above. No echo-decision ledger row and no test names any of the 12 paths (`git grep` over the ledgers). `metadata-forms-vocabulary.test.ts` asserts group keys only. - **H3 held: both checks ran, and their hits are reported separately.** Renamed key: 1 / 1 / 1 hit (`dashboard.fields.refreshIntervalSeconds.label`), stale but not contradicting. Penultimate edit: 18 / 18 / 22 flags, 0 contradicting. ## Verification (all at `aaee46778`) - Build: `turbo run build --filter=@objectstack/cli... --filter=@objectstack/platform-objects... --concurrency=2`: 59/59 tasks, `VERDICT command-exit 0`. - `pnpm --filter @objectstack/platform-objects test`: 58 files, 942 tests passed. `pnpm --filter @objectstack/platform-objects typecheck`: exit 0, `check:test-typecheck: OK`. - The new values ship: the built `dist/metadata-translations/index.mjs` and `index.js` were imported, and `MetadataFormsTranslations` was read by path after `withSourceFallback`. In both, 12 of 12 re-translated rows equal the HEAD source and differ from the base, and 9 of 9 unchanged-leaf controls equal both. - Gates: `node scripts/pm/dispatch-gates.mjs --commands` (no paths) derived 55 commands from the real diff, and all 55 exit 0. `--ran` printed "55 derived famil(ies) accounted for — 55 run, 0 NOT-MEASURED (a DERIVED zero — all 55 recorded an exit code and none of them is 3)". `pnpm check:dual-build-cjs-loads` first refused with exit 3 (`PREREQUISITE NOT MET`: nine packages outside this diff had no `dist/`). Those were built and the gate re-ran: exit 0. - Named in the verdicts: `check:i18n` "OK (9 package(s) — all bundles in sync, no undeclared authoring keys)"; `check:i18n-stale-fill` "OK (10 bundle set(s) — no new stale fills, 0 baselined)"; `check:nul-bytes` exit 0. - Lint, narrowed to the three edited bundles: `eslint --no-inline-config --format json` read 3 files, 0 errors, 0 warnings. `ESLint#isPathIgnored` answers `false` for all three, read from the repo's own config. The config enables no type-aware linting (no `parserOptions.project` or `projectService`, `eslint.config.mjs` states it at line 328), so a change to string values in these files cannot move the verdict on any other file. The full `pnpm lint` is left to CI. - `origin/main` moved two commits past the base (`defc7f7b5`). Neither touches a form declaration or a translation bundle, so the measurement stands on it. ## Acceptance notes - `dataset-panel-echo-decisions.test.ts`, the reason on the `include` label row, cites "report.sections.joined_blocks.label is 关联对象" as a precedent for the word 关联. After this PR that label reads joined 报表分块, so the citation is out of date. It is prose: no assertion reads it, and the `include` decision stands on its own, since 关联 keeps authored precedents such as `sys_email.fields.related_object.label` 关联对象 in the objects bundle. The ledger is outside this card's file surface and is not edited here. - zh-CN `page.sections.data_context.description` reads 关联对象与变量 ("related objects and variables") against `en` "Record binding and page-local state." It is loose but does not contradict: the bound record's object is the related object, and the variables are the page-local state. No instrument flags it, and it is left as written. - The since-last-edit instrument cannot see a leaf that was wrong when it was authored while `en` never moved. Instruments 5 and 6 cover only the vocabularies they name. Instruments were run from the session scratchpad. --- _Generated by [Claude Code](https://claude.ai/code/session_01DEvba2nBuD4tWzfq8r8NFY)_ Co-authored-by: Claude <noreply@anthropic.com>
Fixes #20142
Clause-②: no
Summary
The console retired its System Hub card wall and its Developer Hub (objectui#10520, PR objectui#10576). Those had been the only in-app links to three console pages, so the pages were reachable by typed URL only. The console now registers each page under a component-registry key. This PR adds two of the three navigation entries that name those keys:
componentRefnav_audit_log_browser("Audit Log Browser")group_diagnostics, directly undernav_audit_logsaudit:log@objectstack/plugin-auditnavigationContributionssys_audit_lognav_integrations("Integrations & APIs")group_developer, afternav_public_formsdeveloper:integrations@objectstack/platform-objectsSTUDIO_APPstudio.accessThe third key,
ai:approvals(the AI pending-action queue), is the AI capability's entry to contribute under ADR-0029 D7, so this repo ships none. The seat answered the dev's placement question C (5896257587) and handed the entry to cloud's@objectstack/service-ai(knock 5896271533 on #6026).content/docs/ui/setup-app.mdx's "Group / Filled by" table now readsAudit Logs · Audit Log Browser — plugin-auditon its Diagnostics row (patch round 2,d4744797b, after contract review 5896816094). No other hand-written page enumerates these slots' entries.Labels are in
packages/platform-objects/src/apps/translations/{en,zh-CN,ja-JP,es-ES}.ts, with source hashes recorded in the three hand-maintained*.source-hashes.tstables. Noplugin-audit/src/translations/*file is touched: Setup nav labels live in platform-objects'apps.setup.navigationsubtree, the same asnav_audit_logs's.Measured first: the three keys at the console pin
Read-only from objectui over git, at the objectstack
.objectui-shapin:git init, thengit fetch --depth 1 https://github.com/objectstack-ai/objectui.git dd3f7e1be3561d63267d7162f3fc0ac52e72834d(exit 0,FETCH_HEAD= that sha), thengit grep.audit:logapps/console/src/registerSystemComponents.tsx:80registerAppComponent({ ref: 'audit:log', label: 'Audit Log', ... AuditLogPage })apps/console/src/main.tsx:45import './registerSystemComponents'ai:approvalsapps/console/src/registerSystemComponents.tsx:91registerAppComponent({ ref: 'ai:approvals', label: 'AI Approvals', ... AiPendingActionsPage })developer:integrationsapps/console/src/registerDeveloperComponents.tsx:86registerAppComponent({ ref: 'developer:integrations', label: 'Integrations & APIs', ... IntegrationsPage })apps/console/src/main.tsx:33import './registerDeveloperComponents'approvals:inbox(the refnav_account_approvalsnames)apps/console/src/registerApprovalsComponents.tsx:59registerAppComponent({ ref: 'approvals:inbox', ... })apps/console/src/main.tsx:42SystemHubPage.tsxis absent fromapps/console/src/pages/system/at the pin.Mechanism hypotheses: which held
type: 'component'plus a registry key.requiresServiceis the item-level gate, stripped server-side byfilterAppForUserWithReason/filterNavinpackages/rest/src/meta-item-read-gate.ts(ADR-0057 D10). It is variant-agnostic, so it gates a component item.group_approvals.platform-objects.test.tspins "does not contribute slots owned by capability plugins" over['group_integrations', 'group_approvals'](ADR-0029 K2.b, commit4cc2ceddce). Round 1 split that pin to placeai:approvalsthere. The seat answered C (5896257587) by ADR-0029 D7: no platform-objects entry ingroup_approvals. Patch round 1 (085c4ddb1) removed the entry, andplatform-objects.test.tsis byte-identical tomain.packages/platform-objects/src/apps/translations/*.ts, not generated bundles.check:i18nreports all nine packages' generated bundles in sync, so a--writerun had nothing to write. Translated values: zh-CN审计日志浏览器/集成与 API, ja-JP監査ログブラウザー/連携と API, es-ESExplorador de registros de auditoría/Integraciones y API. None is a byte copy of the English source.The two open choices
Keep both audit entries: kept
The two surfaces were compared at the pin and on this tree, and neither is a superset of the other.
nav_audit_logs, unchanged) has six named list views onsys_audit_log:recent,writes_only,auth_events,record_views,config_changesandall_events. It is searchable (enable.searchable: true), and it rendersuser_id/tenant_idas lookups. Its record page showsold_value/new_valueasField.textarea, which is raw text.audit:log, AuditLogPage.tsx) is one table (50 per page) with an action / object / actor-id / date filter bar. Its drawer pretty-prints Before and After JSON (tryPrettyJson) and the metadata. The actor is shown as a raw user id. Its action filter (ACTION_OPTIONSinauditLogActions.ts) listscreate, update, delete, login, logout, config_change, import, and has noread. So thereadrows are listed only by the object view'srecord_viewsview.The page is added; it does not replace the object entry. The page's label, "Audit Log Browser", differs from "Audit Logs" so the two rows are distinguishable in the same group.
FOLLOW-UPS.md row K2: re-read, not edited
Row K2 is left as written. This repo ships no entry that links the AI Approvals page, so the row's "ungated" half is for the cloud-side entry (the knock on #6026) to close. The "error-blind inbox" half is untouched by this PR.
Tests (at head
085c4ddb1, afterturbo run buildover./packages/*)packages/platform-objects/src/apps/console-page-nav-entries.test.ts(new): pinsnav_integrations, its group and position, and checks its ref against the measured key list and the key shape.packages/plugins/plugin-audit/src/audit-nav-contribution.test.ts(new): runs the realAuditPlugin.init, and asserts item order['nav_audit_logs', 'nav_audit_log_browser'],componentRef: 'audit:log', and that no gate is set.packages/cli/test/console-page-nav-entries.pin.test.ts(new): a wire pin over the realSTUDIO_APPfold,SchemaRegistry, andRestServerGET /api/v1/meta/:type/:name. The Studio entry is served last ingroup_developer.packages/platform-objects/src/platform-objects.test.tsandpackages/cli/test/connect-agent-both-halves-wire.pin.test.tsare unchanged frommain. The Setup tree count re-measures 34.pnpm test59 files / 947 tests pass; plugin-auditpnpm test26 / 366 pass; the two cli pin files, 4 tests pass. The full cli unit tier last ran at round 1's1d5520a076(235 files / 3358 tests). The cli integration tier is declared to CI. Typecheck exits 0 for platform-objects, plugin-audit and cli.aigate is moot: that gate no longer exists.Gates
node scripts/pm/dispatch-gates.mjs --commandsatd4744797bderived 93 commands:085c4ddb1's 64 plus 29 docs, link and spec-docs families that the docs row brings in. All 93 exit 0, and--ranreads93 derived, 93 run, 0 NOT-MEASURED, 0 UNRUN. At085c4ddb1the 64 all exited 0. That includescheck:i18n(9 packages in sync),check:test-source-alias,check:type-check-debt,check:dual-build-cjs-loads,check:nul-bytes,check:doc-authoringandcheck:cross-package-test-inputs. The--ranreconciliation reads64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN.pnpm check:app-nav-i18nreports OK with setup at 55 merged nav ids, every id labelled in 4 locales.pnpm check:i18n-coveragewas run in round 1 only.One gate went red mid-branch and was answered by narrowing, not by widening a ledger.
check:test-source-aliasrefused a first draft of the cli pin that imported@objectstack/plugin-auditand@objectstack/plugin-approvals(new unaliaseddist/imports). The cli pin now folds only platform-objects' entries. The audit ref is pinned in plugin-audit's own test.Lint, as a proven narrowing:
eslint --no-inline-config --format jsonover the branch's 12 changed.tsfiles reported 0 errors and 0 warnings at085c4ddb1(the 13th changed file is the.mdchangeset). ① Population:eslint.config.mjs'sfiles: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}']minusNEVER_LINTEDcovers all 12. ② The count is read from the JSON. ③ Invariance:eslint.config.mjsstates that it "never enables type-aware linting (noparserOptions.project, no typed@typescript-eslintrules) for ANY file". So this diff cannot move any untouched file's verdict. The repo-widepnpm lintis CI's.Acceptance notes
readoption, althoughsys_audit_log.actiondeclares it (auditLogActions.tsat the pin). Its docblock still points the object atplatform-objects/src/audit/sys-audit-log.object.ts, which moved to plugin-audit. Carrier: none.file-diff,plug-zap) are lucide names; no gate validates icon names.content/docs/ui/setup-app.mdx's Apps row names Packages only, whilesetup-nav.contributions.tsonmainalso contributes Packaged Automation (nav_packaged_automation) togroup_apps. Carrier: none.Generated by Claude Code