docs(console): rewrite error-tracking guide for the two-key telemetry gate - #6601

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-6599-error-tracking-doc
Aug 27, 2026
Merged

docs(console): rewrite error-tracking guide for the two-key telemetry gate#6601
os-zhuang merged 1 commit into
mainfrom
claude/issue-6599-error-tracking-doc

Conversation

@claude

@claudeclaudeBot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Fixes#6599

apps/console/docs/error-tracking.md predated the #5522 fix chain. Followed literally it told a reader to pnpm add @sentry/react, hand-roll src/lib/sentry.ts, and initialise before React renders — bolting a second, ungated Sentry init onto an app that already has a gated one, and reinstating the report-before-permission ordering that PR #5982 eliminated. The committed-telemetry-endpoint.test.ts ratchet would not have caught it: nothing gets committed, the init is just wrong.

What changed

One file, apps/console/docs/error-tracking.md (+156 / -131). No code, no dependencies, no changeset owed.

  • Installation section and the hand-rolled recipe are gone. Sentry is built in; the doc now says so and warns explicitly against adding a second init.
  • The two-key gate is stated as a conjunction, each half with its variable names and where it lives: build-time VITE_SENTRY_DSN (plus VITE_SENTRY_SEND_DEFAULT_PII, VITE_SENTRY_ENABLED, VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_RELEASE, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY, mirrored from the authoritative comment block in apps/console/.env.production) AND the runtime permission telemetry.allowClientErrorReporting, granted with OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED or new RuntimeConfigPlugin({ allowClientErrorReporting: true }), linked to the canonical row in objectstack content/docs/deployment/environment-variables.mdx.
  • Fail-closed contract in one line, plus the closed truthy vocabulary (1/true/on/yes), the mount-time refusal of an unrecognised spelling, and the OS_CLOUD_URL=off interaction that lowers a well-spelled grant to denied.
  • Ordering documented for anyone embedding app-shell in their own host: initSentry() memoizes its verdict, so it must run after initRuntimeConfig() settles.
  • "Option 2: Custom Error Boundary" removed. It was a second ungated exfiltration path presented as an alternative. Replaced by the built-in captureError() / setSentryUser(), which route through the same gate and no-op when it denies, plus a pointer to the app-shell ErrorBoundary that already calls them.
  • Verification steps now start with the half an operator can inspect from outside: curl /api/v1/runtime/config | jq .telemetry, then the vendor-sentry chunk in the Network tab, then a thrown test error.

Two corrections beyond the card table

Both found while verifying the doc against the tree, both in the same file:

  1. The CSP section was fabricated. It claimed the console "includes a Content Security Policy (CSP) meta tag" whose default "already includes https://*.sentry.io in the connect-src directive". Measured: apps/console/index.html sets no CSP meta tag, and git grep -rlni "content-security-policy" over the repo excluding CHANGELOGs returns exactly one file — the doc itself. Rewritten to state the console ships no CSP, and to give the connect-src a hosting layer would need.
  2. VITE_ENVIRONMENT and VITE_ERROR_ENDPOINT had zero read sites anywhere in the tree. Both removed.

The source-maps CI note is kept — sourcemap: false at apps/console/vite.config.ts:702 is still accurate — now with --release pinned to VITE_SENTRY_RELEASE so uploaded maps match the events.

Acceptance criteria

  • One file changed: apps/console/docs/error-tracking.md. No changeset (check-changeset-presence verdict: "No source of a released package changed in this range, so no changeset is owed.")
  • Every env var named in the doc greps to a real read site — 11/11 verified mechanically (VITE_SENTRY_DSN, VITE_SENTRY_ENABLED, VITE_SENTRY_SEND_DEFAULT_PII, VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_RELEASE, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY, VITE_APP_VERSION, VITE_SERVER_URL in objectui; OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED, OS_CLOUD_URL in objectstack).
  • No instruction in the doc, followed literally, produces an ungated telemetry path. Every reporting entry point it now names (captureError, setSentryUser, ErrorBoundary) routes through resolveSentryGate.

Verification, at cca3b38

Gate union re-run after the final commit:

gateverdict line
check:control-bytesOK (scanned 5451 tracked text file(s); skipped 85 binary)
check:doc-fencesevery TypeScript block in 223 document(s) is fenced ts/tsx/typescript
check:shell-escape-residueOK (4/4 root(s) resolved ... 0 occurrence(s) outside a fence)
check-changeset-presenceNo source of a released package changed in this range

⚠️Declared narrowing, and an honest coverage statement. Of the gates above, only check:control-bytes actually contains the edited file in its scan surface — it enumerates git ls-files, and the file is tracked (positive control run). The three documentation gates all root at content/docs (check:doc-fences adds packages/*/README.md) and do not descend into apps/, so their green is real but says nothing about this file. eslint cannot judge it either: every files: glob in eslint.config.js is **/*.{ts,tsx}, so markdown is outside eslint entirely and a full pnpm lint could not change this verdict. That gap is recorded as #6600 (filed unassigned, finding) — not addressed here, out of scope for this card.

This is a prose change to an operator guide with no mechanical checker; the substantive verification is the read-site audit above and the line-by-line check of every claim against resolveSentryGate, runtime-config.ts, main.tsx, .env.production, vite.config.ts, and the objectstack telemetry-posture.ts / runtime-config-plugin.ts.

Refs: #5522 · PR #5559 · PR #5982 · objectstack#10805 · objectstack PR #11382 · objectstack-ai/cloud#1508


Generated by Claude Code

… gate
`apps/console/docs/error-tracking.md` predated the #5522 fix chain and every
load-bearing instruction in it contradicted the shipped design. Followed
literally it told a reader to `pnpm add @sentry/react`, hand-roll
`src/lib/sentry.ts`, and call it before React renders — bolting a second,
ungated Sentry init onto an app that already has a gated one, which is exactly
the "decision frozen at build time, no operator switch" shape #5522 removed.
Rewritten to describe the system that exists:
- Sentry is built in; the install + hand-rolled-init recipe is gone.
- Enabling is the CONJUNCTION of two independent opt-ins: build-time
`VITE_SENTRY_DSN` (plus the optional knobs mirrored from the authoritative
comment block in `apps/console/.env.production`) AND the runtime permission
`telemetry.allowClientErrorReporting`, granted server-side with
`OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED` or
`new RuntimeConfigPlugin({ allowClientErrorReporting: true })`.
- The fail-closed contract stated once: either half missing => no reporting,
silently, by design; every "cannot determine" state reads denied.
- Ordering documented for host embedders: `initSentry()` must run after
`initRuntimeConfig()` settles, because it memoizes its verdict.
- "Option 2: Custom Error Boundary" removed. It was a second ungated
exfiltration path presented as an alternative; replaced by the built-in
`captureError()` / `setSentryUser()`, which route through the same gate, plus
a pointer to the app-shell `ErrorBoundary` that already calls them.
- Verification steps now check the half an operator can inspect from outside:
`curl /api/v1/runtime/config | jq .telemetry`, then the vendor-sentry chunk,
then a thrown test error.
Two corrections beyond the card's table, both verified against the tree:
- The CSP section was fabricated. It claimed the console "includes a Content
Security Policy meta tag" whose default "already includes https://*.sentry.io
in connect-src". `apps/console/index.html` sets no CSP meta tag and the doc
was the only file in the repo mentioning CSP at all. Rewritten to say the
console ships no CSP, and to give the connect-src a *hosting layer* would
need.
- `VITE_ENVIRONMENT` and `VITE_ERROR_ENDPOINT` had zero read sites anywhere in
the tree; both are gone. Every env var the doc now names greps to a real read
site (11/11 verified).
The source-maps CI note is kept — `sourcemap: false` in
`apps/console/vite.config.ts` is still accurate — with the release pinned to
`VITE_SENTRY_RELEASE` so uploaded maps match the events.
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 52 chunks)3235.4 KB3266.6 KB
Main entry chunk (gzip)157.0 KB350 KB
Entry fileindex-BEKllrlV.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)11.71KB4.46KB
app-shell (runtime-config.js)18.10KB6.51KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)506.01KB114.64KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)173.10KB47.96KB
fields (index.js)238.89KB60.02KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.95KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)9.53KB3.38KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.64KB1.50KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)1.93KB0.88KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.85KB12.89KB
plugin-charts (index.js)64.66KB18.32KB
plugin-chatbot (index.js)188.60KB44.82KB
plugin-dashboard (index.js)133.48KB34.49KB
plugin-designer (index.js)212.80KB43.15KB
plugin-detail (index.js)245.29KB62.39KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)131.78KB32.19KB
plugin-gantt (index.js)165.16KB40.33KB
plugin-grid (index.js)201.66KB54.58KB
plugin-kanban (index.js)53.11KB14.62KB
plugin-list (index.js)112.86KB27.54KB
plugin-map (index.js)20.09KB6.62KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)26.72KB7.71KB
plugin-tree (index.js)9.26KB3.13KB
plugin-view (index.js)85.87KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)63.21KB21.05KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)2.44KB1.21KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)12.13KB3.65KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)7.54KB2.63KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuangos-zhuang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PM review (dispatching seat for #6599): read the full diff against the shipped implementation.

  • Every instruction now describes the code that exists: the two-key gate (resolveSentryGate), the fail-closed contract with all indeterminate states reading denied, the #5982 init-after-runtime-config ordering with the memoization trap spelled out, and the closed truthy vocabulary with loud refusal.
  • The dev's mechanical acceptance check (11/11 named variables grep to real read sites; the two ghosts VITE_ENVIRONMENT / VITE_ERROR_ENDPOINT removed) matches the card's acceptance criterion exactly.
  • Two corrections beyond the card, both verified measurements, both accepted: the old CSP section was fabricated (no CSP meta tag exists in apps/console/index.html), and Option 2 was a second ungated exfiltration path — replaced with the gated captureError/setSentryUser helpers.
  • Heads-up recorded, not a blocker: objectstack#12681 (maintainer-ruled, queued) will move the DSN source server-side; this doc's runtime half gets updated again as part of that change. Landing the accurate current-shape doc first is the ruled sequencing.

Landing via the normal queue — apps/console/docs/** is not a governed surface.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 27, 2026 08:42
@os-zhuang
os-zhuang added this pull request to the merge queueAug 27, 2026
Merged via the queue into main with commit 9363ad0Aug 27, 2026
27 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-6599-error-tracking-doc branch August 27, 2026 08:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(console): error-tracking.md predates the #5522 two-key telemetry gate — following it as written recreates the exact shape #5522 removed

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

docs(console): rewrite error-tracking guide for the two-key telemetry gate - #6601

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-6599-error-tracking-doc
Aug 27, 2026
Merged

docs(console): rewrite error-tracking guide for the two-key telemetry gate#6601
os-zhuang merged 1 commit into
mainfrom
claude/issue-6599-error-tracking-doc

Conversation

@claude

@claudeclaudeBot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Fixes#6599

apps/console/docs/error-tracking.md predated the #5522 fix chain. Followed literally it told a reader to pnpm add @sentry/react, hand-roll src/lib/sentry.ts, and initialise before React renders — bolting a second, ungated Sentry init onto an app that already has a gated one, and reinstating the report-before-permission ordering that PR #5982 eliminated. The committed-telemetry-endpoint.test.ts ratchet would not have caught it: nothing gets committed, the init is just wrong.

What changed

One file, apps/console/docs/error-tracking.md (+156 / -131). No code, no dependencies, no changeset owed.

  • Installation section and the hand-rolled recipe are gone. Sentry is built in; the doc now says so and warns explicitly against adding a second init.
  • The two-key gate is stated as a conjunction, each half with its variable names and where it lives: build-time VITE_SENTRY_DSN (plus VITE_SENTRY_SEND_DEFAULT_PII, VITE_SENTRY_ENABLED, VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_RELEASE, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY, mirrored from the authoritative comment block in apps/console/.env.production) AND the runtime permission telemetry.allowClientErrorReporting, granted with OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED or new RuntimeConfigPlugin({ allowClientErrorReporting: true }), linked to the canonical row in objectstack content/docs/deployment/environment-variables.mdx.
  • Fail-closed contract in one line, plus the closed truthy vocabulary (1/true/on/yes), the mount-time refusal of an unrecognised spelling, and the OS_CLOUD_URL=off interaction that lowers a well-spelled grant to denied.
  • Ordering documented for anyone embedding app-shell in their own host: initSentry() memoizes its verdict, so it must run after initRuntimeConfig() settles.
  • "Option 2: Custom Error Boundary" removed. It was a second ungated exfiltration path presented as an alternative. Replaced by the built-in captureError() / setSentryUser(), which route through the same gate and no-op when it denies, plus a pointer to the app-shell ErrorBoundary that already calls them.
  • Verification steps now start with the half an operator can inspect from outside: curl /api/v1/runtime/config | jq .telemetry, then the vendor-sentry chunk in the Network tab, then a thrown test error.

Two corrections beyond the card table

Both found while verifying the doc against the tree, both in the same file:

  1. The CSP section was fabricated. It claimed the console "includes a Content Security Policy (CSP) meta tag" whose default "already includes https://*.sentry.io in the connect-src directive". Measured: apps/console/index.html sets no CSP meta tag, and git grep -rlni "content-security-policy" over the repo excluding CHANGELOGs returns exactly one file — the doc itself. Rewritten to state the console ships no CSP, and to give the connect-src a hosting layer would need.
  2. VITE_ENVIRONMENT and VITE_ERROR_ENDPOINT had zero read sites anywhere in the tree. Both removed.

The source-maps CI note is kept — sourcemap: false at apps/console/vite.config.ts:702 is still accurate — now with --release pinned to VITE_SENTRY_RELEASE so uploaded maps match the events.

Acceptance criteria

  • One file changed: apps/console/docs/error-tracking.md. No changeset (check-changeset-presence verdict: "No source of a released package changed in this range, so no changeset is owed.")
  • Every env var named in the doc greps to a real read site — 11/11 verified mechanically (VITE_SENTRY_DSN, VITE_SENTRY_ENABLED, VITE_SENTRY_SEND_DEFAULT_PII, VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_RELEASE, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY, VITE_APP_VERSION, VITE_SERVER_URL in objectui; OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED, OS_CLOUD_URL in objectstack).
  • No instruction in the doc, followed literally, produces an ungated telemetry path. Every reporting entry point it now names (captureError, setSentryUser, ErrorBoundary) routes through resolveSentryGate.

Verification, at cca3b38

Gate union re-run after the final commit:

gateverdict line
check:control-bytesOK (scanned 5451 tracked text file(s); skipped 85 binary)
check:doc-fencesevery TypeScript block in 223 document(s) is fenced ts/tsx/typescript
check:shell-escape-residueOK (4/4 root(s) resolved ... 0 occurrence(s) outside a fence)
check-changeset-presenceNo source of a released package changed in this range

⚠️Declared narrowing, and an honest coverage statement. Of the gates above, only check:control-bytes actually contains the edited file in its scan surface — it enumerates git ls-files, and the file is tracked (positive control run). The three documentation gates all root at content/docs (check:doc-fences adds packages/*/README.md) and do not descend into apps/, so their green is real but says nothing about this file. eslint cannot judge it either: every files: glob in eslint.config.js is **/*.{ts,tsx}, so markdown is outside eslint entirely and a full pnpm lint could not change this verdict. That gap is recorded as #6600 (filed unassigned, finding) — not addressed here, out of scope for this card.

This is a prose change to an operator guide with no mechanical checker; the substantive verification is the read-site audit above and the line-by-line check of every claim against resolveSentryGate, runtime-config.ts, main.tsx, .env.production, vite.config.ts, and the objectstack telemetry-posture.ts / runtime-config-plugin.ts.

Refs: #5522 · PR #5559 · PR #5982 · objectstack#10805 · objectstack PR #11382 · objectstack-ai/cloud#1508


Generated by Claude Code

… gate
`apps/console/docs/error-tracking.md` predated the #5522 fix chain and every
load-bearing instruction in it contradicted the shipped design. Followed
literally it told a reader to `pnpm add @sentry/react`, hand-roll
`src/lib/sentry.ts`, and call it before React renders — bolting a second,
ungated Sentry init onto an app that already has a gated one, which is exactly
the "decision frozen at build time, no operator switch" shape #5522 removed.
Rewritten to describe the system that exists:
- Sentry is built in; the install + hand-rolled-init recipe is gone.
- Enabling is the CONJUNCTION of two independent opt-ins: build-time
`VITE_SENTRY_DSN` (plus the optional knobs mirrored from the authoritative
comment block in `apps/console/.env.production`) AND the runtime permission
`telemetry.allowClientErrorReporting`, granted server-side with
`OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED` or
`new RuntimeConfigPlugin({ allowClientErrorReporting: true })`.
- The fail-closed contract stated once: either half missing => no reporting,
silently, by design; every "cannot determine" state reads denied.
- Ordering documented for host embedders: `initSentry()` must run after
`initRuntimeConfig()` settles, because it memoizes its verdict.
- "Option 2: Custom Error Boundary" removed. It was a second ungated
exfiltration path presented as an alternative; replaced by the built-in
`captureError()` / `setSentryUser()`, which route through the same gate, plus
a pointer to the app-shell `ErrorBoundary` that already calls them.
- Verification steps now check the half an operator can inspect from outside:
`curl /api/v1/runtime/config | jq .telemetry`, then the vendor-sentry chunk,
then a thrown test error.
Two corrections beyond the card's table, both verified against the tree:
- The CSP section was fabricated. It claimed the console "includes a Content
Security Policy meta tag" whose default "already includes https://*.sentry.io
in connect-src". `apps/console/index.html` sets no CSP meta tag and the doc
was the only file in the repo mentioning CSP at all. Rewritten to say the
console ships no CSP, and to give the connect-src a *hosting layer* would
need.
- `VITE_ENVIRONMENT` and `VITE_ERROR_ENDPOINT` had zero read sites anywhere in
the tree; both are gone. Every env var the doc now names greps to a real read
site (11/11 verified).
The source-maps CI note is kept — `sourcemap: false` in
`apps/console/vite.config.ts` is still accurate — with the release pinned to
`VITE_SENTRY_RELEASE` so uploaded maps match the events.
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 52 chunks)3235.4 KB3266.6 KB
Main entry chunk (gzip)157.0 KB350 KB
Entry fileindex-BEKllrlV.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)11.71KB4.46KB
app-shell (runtime-config.js)18.10KB6.51KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)506.01KB114.64KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)173.10KB47.96KB
fields (index.js)238.89KB60.02KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.95KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)9.53KB3.38KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.64KB1.50KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)1.93KB0.88KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.85KB12.89KB
plugin-charts (index.js)64.66KB18.32KB
plugin-chatbot (index.js)188.60KB44.82KB
plugin-dashboard (index.js)133.48KB34.49KB
plugin-designer (index.js)212.80KB43.15KB
plugin-detail (index.js)245.29KB62.39KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)131.78KB32.19KB
plugin-gantt (index.js)165.16KB40.33KB
plugin-grid (index.js)201.66KB54.58KB
plugin-kanban (index.js)53.11KB14.62KB
plugin-list (index.js)112.86KB27.54KB
plugin-map (index.js)20.09KB6.62KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)26.72KB7.71KB
plugin-tree (index.js)9.26KB3.13KB
plugin-view (index.js)85.87KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)63.21KB21.05KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)2.44KB1.21KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)12.13KB3.65KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)7.54KB2.63KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuangos-zhuang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PM review (dispatching seat for #6599): read the full diff against the shipped implementation.

  • Every instruction now describes the code that exists: the two-key gate (resolveSentryGate), the fail-closed contract with all indeterminate states reading denied, the #5982 init-after-runtime-config ordering with the memoization trap spelled out, and the closed truthy vocabulary with loud refusal.
  • The dev's mechanical acceptance check (11/11 named variables grep to real read sites; the two ghosts VITE_ENVIRONMENT / VITE_ERROR_ENDPOINT removed) matches the card's acceptance criterion exactly.
  • Two corrections beyond the card, both verified measurements, both accepted: the old CSP section was fabricated (no CSP meta tag exists in apps/console/index.html), and Option 2 was a second ungated exfiltration path — replaced with the gated captureError/setSentryUser helpers.
  • Heads-up recorded, not a blocker: objectstack#12681 (maintainer-ruled, queued) will move the DSN source server-side; this doc's runtime half gets updated again as part of that change. Landing the accurate current-shape doc first is the ruled sequencing.

Landing via the normal queue — apps/console/docs/** is not a governed surface.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 27, 2026 08:42
@os-zhuang
os-zhuang added this pull request to the merge queueAug 27, 2026
Merged via the queue into main with commit 9363ad0Aug 27, 2026
27 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-6599-error-tracking-doc branch August 27, 2026 08:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(console): error-tracking.md predates the #5522 two-key telemetry gate — following it as written recreates the exact shape #5522 removed

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(console): rewrite error-tracking guide for the two-key telemetry gate - #6601

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-6599-error-tracking-doc
Aug 27, 2026
Merged

docs(console): rewrite error-tracking guide for the two-key telemetry gate#6601
os-zhuang merged 1 commit into
mainfrom
claude/issue-6599-error-tracking-doc

Conversation

@claude

@claudeclaudeBot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Fixes#6599

apps/console/docs/error-tracking.md predated the #5522 fix chain. Followed literally it told a reader to pnpm add @sentry/react, hand-roll src/lib/sentry.ts, and initialise before React renders — bolting a second, ungated Sentry init onto an app that already has a gated one, and reinstating the report-before-permission ordering that PR #5982 eliminated. The committed-telemetry-endpoint.test.ts ratchet would not have caught it: nothing gets committed, the init is just wrong.

What changed

One file, apps/console/docs/error-tracking.md (+156 / -131). No code, no dependencies, no changeset owed.

  • Installation section and the hand-rolled recipe are gone. Sentry is built in; the doc now says so and warns explicitly against adding a second init.
  • The two-key gate is stated as a conjunction, each half with its variable names and where it lives: build-time VITE_SENTRY_DSN (plus VITE_SENTRY_SEND_DEFAULT_PII, VITE_SENTRY_ENABLED, VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_RELEASE, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY, mirrored from the authoritative comment block in apps/console/.env.production) AND the runtime permission telemetry.allowClientErrorReporting, granted with OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED or new RuntimeConfigPlugin({ allowClientErrorReporting: true }), linked to the canonical row in objectstack content/docs/deployment/environment-variables.mdx.
  • Fail-closed contract in one line, plus the closed truthy vocabulary (1/true/on/yes), the mount-time refusal of an unrecognised spelling, and the OS_CLOUD_URL=off interaction that lowers a well-spelled grant to denied.
  • Ordering documented for anyone embedding app-shell in their own host: initSentry() memoizes its verdict, so it must run after initRuntimeConfig() settles.
  • "Option 2: Custom Error Boundary" removed. It was a second ungated exfiltration path presented as an alternative. Replaced by the built-in captureError() / setSentryUser(), which route through the same gate and no-op when it denies, plus a pointer to the app-shell ErrorBoundary that already calls them.
  • Verification steps now start with the half an operator can inspect from outside: curl /api/v1/runtime/config | jq .telemetry, then the vendor-sentry chunk in the Network tab, then a thrown test error.

Two corrections beyond the card table

Both found while verifying the doc against the tree, both in the same file:

  1. The CSP section was fabricated. It claimed the console "includes a Content Security Policy (CSP) meta tag" whose default "already includes https://*.sentry.io in the connect-src directive". Measured: apps/console/index.html sets no CSP meta tag, and git grep -rlni "content-security-policy" over the repo excluding CHANGELOGs returns exactly one file — the doc itself. Rewritten to state the console ships no CSP, and to give the connect-src a hosting layer would need.
  2. VITE_ENVIRONMENT and VITE_ERROR_ENDPOINT had zero read sites anywhere in the tree. Both removed.

The source-maps CI note is kept — sourcemap: false at apps/console/vite.config.ts:702 is still accurate — now with --release pinned to VITE_SENTRY_RELEASE so uploaded maps match the events.

Acceptance criteria

  • One file changed: apps/console/docs/error-tracking.md. No changeset (check-changeset-presence verdict: "No source of a released package changed in this range, so no changeset is owed.")
  • Every env var named in the doc greps to a real read site — 11/11 verified mechanically (VITE_SENTRY_DSN, VITE_SENTRY_ENABLED, VITE_SENTRY_SEND_DEFAULT_PII, VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_RELEASE, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY, VITE_APP_VERSION, VITE_SERVER_URL in objectui; OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED, OS_CLOUD_URL in objectstack).
  • No instruction in the doc, followed literally, produces an ungated telemetry path. Every reporting entry point it now names (captureError, setSentryUser, ErrorBoundary) routes through resolveSentryGate.

Verification, at cca3b38

Gate union re-run after the final commit:

gateverdict line
check:control-bytesOK (scanned 5451 tracked text file(s); skipped 85 binary)
check:doc-fencesevery TypeScript block in 223 document(s) is fenced ts/tsx/typescript
check:shell-escape-residueOK (4/4 root(s) resolved ... 0 occurrence(s) outside a fence)
check-changeset-presenceNo source of a released package changed in this range

⚠️Declared narrowing, and an honest coverage statement. Of the gates above, only check:control-bytes actually contains the edited file in its scan surface — it enumerates git ls-files, and the file is tracked (positive control run). The three documentation gates all root at content/docs (check:doc-fences adds packages/*/README.md) and do not descend into apps/, so their green is real but says nothing about this file. eslint cannot judge it either: every files: glob in eslint.config.js is **/*.{ts,tsx}, so markdown is outside eslint entirely and a full pnpm lint could not change this verdict. That gap is recorded as #6600 (filed unassigned, finding) — not addressed here, out of scope for this card.

This is a prose change to an operator guide with no mechanical checker; the substantive verification is the read-site audit above and the line-by-line check of every claim against resolveSentryGate, runtime-config.ts, main.tsx, .env.production, vite.config.ts, and the objectstack telemetry-posture.ts / runtime-config-plugin.ts.

Refs: #5522 · PR #5559 · PR #5982 · objectstack#10805 · objectstack PR #11382 · objectstack-ai/cloud#1508


Generated by Claude Code

… gate
`apps/console/docs/error-tracking.md` predated the #5522 fix chain and every
load-bearing instruction in it contradicted the shipped design. Followed
literally it told a reader to `pnpm add @sentry/react`, hand-roll
`src/lib/sentry.ts`, and call it before React renders — bolting a second,
ungated Sentry init onto an app that already has a gated one, which is exactly
the "decision frozen at build time, no operator switch" shape #5522 removed.
Rewritten to describe the system that exists:
- Sentry is built in; the install + hand-rolled-init recipe is gone.
- Enabling is the CONJUNCTION of two independent opt-ins: build-time
`VITE_SENTRY_DSN` (plus the optional knobs mirrored from the authoritative
comment block in `apps/console/.env.production`) AND the runtime permission
`telemetry.allowClientErrorReporting`, granted server-side with
`OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED` or
`new RuntimeConfigPlugin({ allowClientErrorReporting: true })`.
- The fail-closed contract stated once: either half missing => no reporting,
silently, by design; every "cannot determine" state reads denied.
- Ordering documented for host embedders: `initSentry()` must run after
`initRuntimeConfig()` settles, because it memoizes its verdict.
- "Option 2: Custom Error Boundary" removed. It was a second ungated
exfiltration path presented as an alternative; replaced by the built-in
`captureError()` / `setSentryUser()`, which route through the same gate, plus
a pointer to the app-shell `ErrorBoundary` that already calls them.
- Verification steps now check the half an operator can inspect from outside:
`curl /api/v1/runtime/config | jq .telemetry`, then the vendor-sentry chunk,
then a thrown test error.
Two corrections beyond the card's table, both verified against the tree:
- The CSP section was fabricated. It claimed the console "includes a Content
Security Policy meta tag" whose default "already includes https://*.sentry.io
in connect-src". `apps/console/index.html` sets no CSP meta tag and the doc
was the only file in the repo mentioning CSP at all. Rewritten to say the
console ships no CSP, and to give the connect-src a *hosting layer* would
need.
- `VITE_ENVIRONMENT` and `VITE_ERROR_ENDPOINT` had zero read sites anywhere in
the tree; both are gone. Every env var the doc now names greps to a real read
site (11/11 verified).
The source-maps CI note is kept — `sourcemap: false` in
`apps/console/vite.config.ts` is still accurate — with the release pinned to
`VITE_SENTRY_RELEASE` so uploaded maps match the events.
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 52 chunks)3235.4 KB3266.6 KB
Main entry chunk (gzip)157.0 KB350 KB
Entry fileindex-BEKllrlV.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)11.71KB4.46KB
app-shell (runtime-config.js)18.10KB6.51KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)506.01KB114.64KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)173.10KB47.96KB
fields (index.js)238.89KB60.02KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.95KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)9.53KB3.38KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.64KB1.50KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)1.93KB0.88KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.85KB12.89KB
plugin-charts (index.js)64.66KB18.32KB
plugin-chatbot (index.js)188.60KB44.82KB
plugin-dashboard (index.js)133.48KB34.49KB
plugin-designer (index.js)212.80KB43.15KB
plugin-detail (index.js)245.29KB62.39KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)131.78KB32.19KB
plugin-gantt (index.js)165.16KB40.33KB
plugin-grid (index.js)201.66KB54.58KB
plugin-kanban (index.js)53.11KB14.62KB
plugin-list (index.js)112.86KB27.54KB
plugin-map (index.js)20.09KB6.62KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)26.72KB7.71KB
plugin-tree (index.js)9.26KB3.13KB
plugin-view (index.js)85.87KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)63.21KB21.05KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)2.44KB1.21KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)12.13KB3.65KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)7.54KB2.63KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuangos-zhuang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PM review (dispatching seat for #6599): read the full diff against the shipped implementation.

  • Every instruction now describes the code that exists: the two-key gate (resolveSentryGate), the fail-closed contract with all indeterminate states reading denied, the #5982 init-after-runtime-config ordering with the memoization trap spelled out, and the closed truthy vocabulary with loud refusal.
  • The dev's mechanical acceptance check (11/11 named variables grep to real read sites; the two ghosts VITE_ENVIRONMENT / VITE_ERROR_ENDPOINT removed) matches the card's acceptance criterion exactly.
  • Two corrections beyond the card, both verified measurements, both accepted: the old CSP section was fabricated (no CSP meta tag exists in apps/console/index.html), and Option 2 was a second ungated exfiltration path — replaced with the gated captureError/setSentryUser helpers.
  • Heads-up recorded, not a blocker: objectstack#12681 (maintainer-ruled, queued) will move the DSN source server-side; this doc's runtime half gets updated again as part of that change. Landing the accurate current-shape doc first is the ruled sequencing.

Landing via the normal queue — apps/console/docs/** is not a governed surface.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 27, 2026 08:42
@os-zhuang
os-zhuang added this pull request to the merge queueAug 27, 2026
Merged via the queue into main with commit 9363ad0Aug 27, 2026
27 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-6599-error-tracking-doc branch August 27, 2026 08:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(console): error-tracking.md predates the #5522 two-key telemetry gate — following it as written recreates the exact shape #5522 removed

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(console): rewrite error-tracking guide for the two-key telemetry gate - #6601

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-6599-error-tracking-doc
Aug 27, 2026
Merged

docs(console): rewrite error-tracking guide for the two-key telemetry gate#6601
os-zhuang merged 1 commit into
mainfrom
claude/issue-6599-error-tracking-doc

Conversation

@claude

@claudeclaudeBot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Fixes#6599

apps/console/docs/error-tracking.md predated the #5522 fix chain. Followed literally it told a reader to pnpm add @sentry/react, hand-roll src/lib/sentry.ts, and initialise before React renders — bolting a second, ungated Sentry init onto an app that already has a gated one, and reinstating the report-before-permission ordering that PR #5982 eliminated. The committed-telemetry-endpoint.test.ts ratchet would not have caught it: nothing gets committed, the init is just wrong.

What changed

One file, apps/console/docs/error-tracking.md (+156 / -131). No code, no dependencies, no changeset owed.

  • Installation section and the hand-rolled recipe are gone. Sentry is built in; the doc now says so and warns explicitly against adding a second init.
  • The two-key gate is stated as a conjunction, each half with its variable names and where it lives: build-time VITE_SENTRY_DSN (plus VITE_SENTRY_SEND_DEFAULT_PII, VITE_SENTRY_ENABLED, VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_RELEASE, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY, mirrored from the authoritative comment block in apps/console/.env.production) AND the runtime permission telemetry.allowClientErrorReporting, granted with OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED or new RuntimeConfigPlugin({ allowClientErrorReporting: true }), linked to the canonical row in objectstack content/docs/deployment/environment-variables.mdx.
  • Fail-closed contract in one line, plus the closed truthy vocabulary (1/true/on/yes), the mount-time refusal of an unrecognised spelling, and the OS_CLOUD_URL=off interaction that lowers a well-spelled grant to denied.
  • Ordering documented for anyone embedding app-shell in their own host: initSentry() memoizes its verdict, so it must run after initRuntimeConfig() settles.
  • "Option 2: Custom Error Boundary" removed. It was a second ungated exfiltration path presented as an alternative. Replaced by the built-in captureError() / setSentryUser(), which route through the same gate and no-op when it denies, plus a pointer to the app-shell ErrorBoundary that already calls them.
  • Verification steps now start with the half an operator can inspect from outside: curl /api/v1/runtime/config | jq .telemetry, then the vendor-sentry chunk in the Network tab, then a thrown test error.

Two corrections beyond the card table

Both found while verifying the doc against the tree, both in the same file:

  1. The CSP section was fabricated. It claimed the console "includes a Content Security Policy (CSP) meta tag" whose default "already includes https://*.sentry.io in the connect-src directive". Measured: apps/console/index.html sets no CSP meta tag, and git grep -rlni "content-security-policy" over the repo excluding CHANGELOGs returns exactly one file — the doc itself. Rewritten to state the console ships no CSP, and to give the connect-src a hosting layer would need.
  2. VITE_ENVIRONMENT and VITE_ERROR_ENDPOINT had zero read sites anywhere in the tree. Both removed.

The source-maps CI note is kept — sourcemap: false at apps/console/vite.config.ts:702 is still accurate — now with --release pinned to VITE_SENTRY_RELEASE so uploaded maps match the events.

Acceptance criteria

  • One file changed: apps/console/docs/error-tracking.md. No changeset (check-changeset-presence verdict: "No source of a released package changed in this range, so no changeset is owed.")
  • Every env var named in the doc greps to a real read site — 11/11 verified mechanically (VITE_SENTRY_DSN, VITE_SENTRY_ENABLED, VITE_SENTRY_SEND_DEFAULT_PII, VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_RELEASE, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY, VITE_APP_VERSION, VITE_SERVER_URL in objectui; OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED, OS_CLOUD_URL in objectstack).
  • No instruction in the doc, followed literally, produces an ungated telemetry path. Every reporting entry point it now names (captureError, setSentryUser, ErrorBoundary) routes through resolveSentryGate.

Verification, at cca3b38

Gate union re-run after the final commit:

gateverdict line
check:control-bytesOK (scanned 5451 tracked text file(s); skipped 85 binary)
check:doc-fencesevery TypeScript block in 223 document(s) is fenced ts/tsx/typescript
check:shell-escape-residueOK (4/4 root(s) resolved ... 0 occurrence(s) outside a fence)
check-changeset-presenceNo source of a released package changed in this range

⚠️Declared narrowing, and an honest coverage statement. Of the gates above, only check:control-bytes actually contains the edited file in its scan surface — it enumerates git ls-files, and the file is tracked (positive control run). The three documentation gates all root at content/docs (check:doc-fences adds packages/*/README.md) and do not descend into apps/, so their green is real but says nothing about this file. eslint cannot judge it either: every files: glob in eslint.config.js is **/*.{ts,tsx}, so markdown is outside eslint entirely and a full pnpm lint could not change this verdict. That gap is recorded as #6600 (filed unassigned, finding) — not addressed here, out of scope for this card.

This is a prose change to an operator guide with no mechanical checker; the substantive verification is the read-site audit above and the line-by-line check of every claim against resolveSentryGate, runtime-config.ts, main.tsx, .env.production, vite.config.ts, and the objectstack telemetry-posture.ts / runtime-config-plugin.ts.

Refs: #5522 · PR #5559 · PR #5982 · objectstack#10805 · objectstack PR #11382 · objectstack-ai/cloud#1508


Generated by Claude Code

… gate
`apps/console/docs/error-tracking.md` predated the #5522 fix chain and every
load-bearing instruction in it contradicted the shipped design. Followed
literally it told a reader to `pnpm add @sentry/react`, hand-roll
`src/lib/sentry.ts`, and call it before React renders — bolting a second,
ungated Sentry init onto an app that already has a gated one, which is exactly
the "decision frozen at build time, no operator switch" shape #5522 removed.
Rewritten to describe the system that exists:
- Sentry is built in; the install + hand-rolled-init recipe is gone.
- Enabling is the CONJUNCTION of two independent opt-ins: build-time
`VITE_SENTRY_DSN` (plus the optional knobs mirrored from the authoritative
comment block in `apps/console/.env.production`) AND the runtime permission
`telemetry.allowClientErrorReporting`, granted server-side with
`OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED` or
`new RuntimeConfigPlugin({ allowClientErrorReporting: true })`.
- The fail-closed contract stated once: either half missing => no reporting,
silently, by design; every "cannot determine" state reads denied.
- Ordering documented for host embedders: `initSentry()` must run after
`initRuntimeConfig()` settles, because it memoizes its verdict.
- "Option 2: Custom Error Boundary" removed. It was a second ungated
exfiltration path presented as an alternative; replaced by the built-in
`captureError()` / `setSentryUser()`, which route through the same gate, plus
a pointer to the app-shell `ErrorBoundary` that already calls them.
- Verification steps now check the half an operator can inspect from outside:
`curl /api/v1/runtime/config | jq .telemetry`, then the vendor-sentry chunk,
then a thrown test error.
Two corrections beyond the card's table, both verified against the tree:
- The CSP section was fabricated. It claimed the console "includes a Content
Security Policy meta tag" whose default "already includes https://*.sentry.io
in connect-src". `apps/console/index.html` sets no CSP meta tag and the doc
was the only file in the repo mentioning CSP at all. Rewritten to say the
console ships no CSP, and to give the connect-src a *hosting layer* would
need.
- `VITE_ENVIRONMENT` and `VITE_ERROR_ENDPOINT` had zero read sites anywhere in
the tree; both are gone. Every env var the doc now names greps to a real read
site (11/11 verified).
The source-maps CI note is kept — `sourcemap: false` in
`apps/console/vite.config.ts` is still accurate — with the release pinned to
`VITE_SENTRY_RELEASE` so uploaded maps match the events.
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 52 chunks)3235.4 KB3266.6 KB
Main entry chunk (gzip)157.0 KB350 KB
Entry fileindex-BEKllrlV.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)11.71KB4.46KB
app-shell (runtime-config.js)18.10KB6.51KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)506.01KB114.64KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)173.10KB47.96KB
fields (index.js)238.89KB60.02KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.95KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)9.53KB3.38KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.64KB1.50KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)1.93KB0.88KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.85KB12.89KB
plugin-charts (index.js)64.66KB18.32KB
plugin-chatbot (index.js)188.60KB44.82KB
plugin-dashboard (index.js)133.48KB34.49KB
plugin-designer (index.js)212.80KB43.15KB
plugin-detail (index.js)245.29KB62.39KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)131.78KB32.19KB
plugin-gantt (index.js)165.16KB40.33KB
plugin-grid (index.js)201.66KB54.58KB
plugin-kanban (index.js)53.11KB14.62KB
plugin-list (index.js)112.86KB27.54KB
plugin-map (index.js)20.09KB6.62KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)26.72KB7.71KB
plugin-tree (index.js)9.26KB3.13KB
plugin-view (index.js)85.87KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)63.21KB21.05KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)2.44KB1.21KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)12.13KB3.65KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)7.54KB2.63KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuangos-zhuang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PM review (dispatching seat for #6599): read the full diff against the shipped implementation.

  • Every instruction now describes the code that exists: the two-key gate (resolveSentryGate), the fail-closed contract with all indeterminate states reading denied, the #5982 init-after-runtime-config ordering with the memoization trap spelled out, and the closed truthy vocabulary with loud refusal.
  • The dev's mechanical acceptance check (11/11 named variables grep to real read sites; the two ghosts VITE_ENVIRONMENT / VITE_ERROR_ENDPOINT removed) matches the card's acceptance criterion exactly.
  • Two corrections beyond the card, both verified measurements, both accepted: the old CSP section was fabricated (no CSP meta tag exists in apps/console/index.html), and Option 2 was a second ungated exfiltration path — replaced with the gated captureError/setSentryUser helpers.
  • Heads-up recorded, not a blocker: objectstack#12681 (maintainer-ruled, queued) will move the DSN source server-side; this doc's runtime half gets updated again as part of that change. Landing the accurate current-shape doc first is the ruled sequencing.

Landing via the normal queue — apps/console/docs/** is not a governed surface.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 27, 2026 08:42
@os-zhuang
os-zhuang added this pull request to the merge queueAug 27, 2026
Merged via the queue into main with commit 9363ad0Aug 27, 2026
27 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-6599-error-tracking-doc branch August 27, 2026 08:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(console): error-tracking.md predates the #5522 two-key telemetry gate — following it as written recreates the exact shape #5522 removed

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

docs(console): rewrite error-tracking guide for the two-key telemetry gate - #6601

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-6599-error-tracking-doc
Aug 27, 2026
Merged

docs(console): rewrite error-tracking guide for the two-key telemetry gate#6601
os-zhuang merged 1 commit into
mainfrom
claude/issue-6599-error-tracking-doc

Conversation

@claude

@claudeclaudeBot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Fixes#6599

apps/console/docs/error-tracking.md predated the #5522 fix chain. Followed literally it told a reader to pnpm add @sentry/react, hand-roll src/lib/sentry.ts, and initialise before React renders — bolting a second, ungated Sentry init onto an app that already has a gated one, and reinstating the report-before-permission ordering that PR #5982 eliminated. The committed-telemetry-endpoint.test.ts ratchet would not have caught it: nothing gets committed, the init is just wrong.

What changed

One file, apps/console/docs/error-tracking.md (+156 / -131). No code, no dependencies, no changeset owed.

  • Installation section and the hand-rolled recipe are gone. Sentry is built in; the doc now says so and warns explicitly against adding a second init.
  • The two-key gate is stated as a conjunction, each half with its variable names and where it lives: build-time VITE_SENTRY_DSN (plus VITE_SENTRY_SEND_DEFAULT_PII, VITE_SENTRY_ENABLED, VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_RELEASE, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY, mirrored from the authoritative comment block in apps/console/.env.production) AND the runtime permission telemetry.allowClientErrorReporting, granted with OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED or new RuntimeConfigPlugin({ allowClientErrorReporting: true }), linked to the canonical row in objectstack content/docs/deployment/environment-variables.mdx.
  • Fail-closed contract in one line, plus the closed truthy vocabulary (1/true/on/yes), the mount-time refusal of an unrecognised spelling, and the OS_CLOUD_URL=off interaction that lowers a well-spelled grant to denied.
  • Ordering documented for anyone embedding app-shell in their own host: initSentry() memoizes its verdict, so it must run after initRuntimeConfig() settles.
  • "Option 2: Custom Error Boundary" removed. It was a second ungated exfiltration path presented as an alternative. Replaced by the built-in captureError() / setSentryUser(), which route through the same gate and no-op when it denies, plus a pointer to the app-shell ErrorBoundary that already calls them.
  • Verification steps now start with the half an operator can inspect from outside: curl /api/v1/runtime/config | jq .telemetry, then the vendor-sentry chunk in the Network tab, then a thrown test error.

Two corrections beyond the card table

Both found while verifying the doc against the tree, both in the same file:

  1. The CSP section was fabricated. It claimed the console "includes a Content Security Policy (CSP) meta tag" whose default "already includes https://*.sentry.io in the connect-src directive". Measured: apps/console/index.html sets no CSP meta tag, and git grep -rlni "content-security-policy" over the repo excluding CHANGELOGs returns exactly one file — the doc itself. Rewritten to state the console ships no CSP, and to give the connect-src a hosting layer would need.
  2. VITE_ENVIRONMENT and VITE_ERROR_ENDPOINT had zero read sites anywhere in the tree. Both removed.

The source-maps CI note is kept — sourcemap: false at apps/console/vite.config.ts:702 is still accurate — now with --release pinned to VITE_SENTRY_RELEASE so uploaded maps match the events.

Acceptance criteria

  • One file changed: apps/console/docs/error-tracking.md. No changeset (check-changeset-presence verdict: "No source of a released package changed in this range, so no changeset is owed.")
  • Every env var named in the doc greps to a real read site — 11/11 verified mechanically (VITE_SENTRY_DSN, VITE_SENTRY_ENABLED, VITE_SENTRY_SEND_DEFAULT_PII, VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_RELEASE, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY, VITE_APP_VERSION, VITE_SERVER_URL in objectui; OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED, OS_CLOUD_URL in objectstack).
  • No instruction in the doc, followed literally, produces an ungated telemetry path. Every reporting entry point it now names (captureError, setSentryUser, ErrorBoundary) routes through resolveSentryGate.

Verification, at cca3b38

Gate union re-run after the final commit:

gateverdict line
check:control-bytesOK (scanned 5451 tracked text file(s); skipped 85 binary)
check:doc-fencesevery TypeScript block in 223 document(s) is fenced ts/tsx/typescript
check:shell-escape-residueOK (4/4 root(s) resolved ... 0 occurrence(s) outside a fence)
check-changeset-presenceNo source of a released package changed in this range

⚠️Declared narrowing, and an honest coverage statement. Of the gates above, only check:control-bytes actually contains the edited file in its scan surface — it enumerates git ls-files, and the file is tracked (positive control run). The three documentation gates all root at content/docs (check:doc-fences adds packages/*/README.md) and do not descend into apps/, so their green is real but says nothing about this file. eslint cannot judge it either: every files: glob in eslint.config.js is **/*.{ts,tsx}, so markdown is outside eslint entirely and a full pnpm lint could not change this verdict. That gap is recorded as #6600 (filed unassigned, finding) — not addressed here, out of scope for this card.

This is a prose change to an operator guide with no mechanical checker; the substantive verification is the read-site audit above and the line-by-line check of every claim against resolveSentryGate, runtime-config.ts, main.tsx, .env.production, vite.config.ts, and the objectstack telemetry-posture.ts / runtime-config-plugin.ts.

Refs: #5522 · PR #5559 · PR #5982 · objectstack#10805 · objectstack PR #11382 · objectstack-ai/cloud#1508


Generated by Claude Code

… gate
`apps/console/docs/error-tracking.md` predated the #5522 fix chain and every
load-bearing instruction in it contradicted the shipped design. Followed
literally it told a reader to `pnpm add @sentry/react`, hand-roll
`src/lib/sentry.ts`, and call it before React renders — bolting a second,
ungated Sentry init onto an app that already has a gated one, which is exactly
the "decision frozen at build time, no operator switch" shape #5522 removed.
Rewritten to describe the system that exists:
- Sentry is built in; the install + hand-rolled-init recipe is gone.
- Enabling is the CONJUNCTION of two independent opt-ins: build-time
`VITE_SENTRY_DSN` (plus the optional knobs mirrored from the authoritative
comment block in `apps/console/.env.production`) AND the runtime permission
`telemetry.allowClientErrorReporting`, granted server-side with
`OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED` or
`new RuntimeConfigPlugin({ allowClientErrorReporting: true })`.
- The fail-closed contract stated once: either half missing => no reporting,
silently, by design; every "cannot determine" state reads denied.
- Ordering documented for host embedders: `initSentry()` must run after
`initRuntimeConfig()` settles, because it memoizes its verdict.
- "Option 2: Custom Error Boundary" removed. It was a second ungated
exfiltration path presented as an alternative; replaced by the built-in
`captureError()` / `setSentryUser()`, which route through the same gate, plus
a pointer to the app-shell `ErrorBoundary` that already calls them.
- Verification steps now check the half an operator can inspect from outside:
`curl /api/v1/runtime/config | jq .telemetry`, then the vendor-sentry chunk,
then a thrown test error.
Two corrections beyond the card's table, both verified against the tree:
- The CSP section was fabricated. It claimed the console "includes a Content
Security Policy meta tag" whose default "already includes https://*.sentry.io
in connect-src". `apps/console/index.html` sets no CSP meta tag and the doc
was the only file in the repo mentioning CSP at all. Rewritten to say the
console ships no CSP, and to give the connect-src a *hosting layer* would
need.
- `VITE_ENVIRONMENT` and `VITE_ERROR_ENDPOINT` had zero read sites anywhere in
the tree; both are gone. Every env var the doc now names greps to a real read
site (11/11 verified).
The source-maps CI note is kept — `sourcemap: false` in
`apps/console/vite.config.ts` is still accurate — with the release pinned to
`VITE_SENTRY_RELEASE` so uploaded maps match the events.
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 52 chunks)3235.4 KB3266.6 KB
Main entry chunk (gzip)157.0 KB350 KB
Entry fileindex-BEKllrlV.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)11.71KB4.46KB
app-shell (runtime-config.js)18.10KB6.51KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)506.01KB114.64KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)173.10KB47.96KB
fields (index.js)238.89KB60.02KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.95KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)9.53KB3.38KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.64KB1.50KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)1.93KB0.88KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.85KB12.89KB
plugin-charts (index.js)64.66KB18.32KB
plugin-chatbot (index.js)188.60KB44.82KB
plugin-dashboard (index.js)133.48KB34.49KB
plugin-designer (index.js)212.80KB43.15KB
plugin-detail (index.js)245.29KB62.39KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)131.78KB32.19KB
plugin-gantt (index.js)165.16KB40.33KB
plugin-grid (index.js)201.66KB54.58KB
plugin-kanban (index.js)53.11KB14.62KB
plugin-list (index.js)112.86KB27.54KB
plugin-map (index.js)20.09KB6.62KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)26.72KB7.71KB
plugin-tree (index.js)9.26KB3.13KB
plugin-view (index.js)85.87KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)63.21KB21.05KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)2.44KB1.21KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)12.13KB3.65KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)7.54KB2.63KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuangos-zhuang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PM review (dispatching seat for #6599): read the full diff against the shipped implementation.

  • Every instruction now describes the code that exists: the two-key gate (resolveSentryGate), the fail-closed contract with all indeterminate states reading denied, the #5982 init-after-runtime-config ordering with the memoization trap spelled out, and the closed truthy vocabulary with loud refusal.
  • The dev's mechanical acceptance check (11/11 named variables grep to real read sites; the two ghosts VITE_ENVIRONMENT / VITE_ERROR_ENDPOINT removed) matches the card's acceptance criterion exactly.
  • Two corrections beyond the card, both verified measurements, both accepted: the old CSP section was fabricated (no CSP meta tag exists in apps/console/index.html), and Option 2 was a second ungated exfiltration path — replaced with the gated captureError/setSentryUser helpers.
  • Heads-up recorded, not a blocker: objectstack#12681 (maintainer-ruled, queued) will move the DSN source server-side; this doc's runtime half gets updated again as part of that change. Landing the accurate current-shape doc first is the ruled sequencing.

Landing via the normal queue — apps/console/docs/** is not a governed surface.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 27, 2026 08:42
@os-zhuang
os-zhuang added this pull request to the merge queueAug 27, 2026
Merged via the queue into main with commit 9363ad0Aug 27, 2026
27 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-6599-error-tracking-doc branch August 27, 2026 08:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(console): error-tracking.md predates the #5522 two-key telemetry gate — following it as written recreates the exact shape #5522 removed

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(console): rewrite error-tracking guide for the two-key telemetry gate - #6601

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-6599-error-tracking-doc
Aug 27, 2026
Merged

docs(console): rewrite error-tracking guide for the two-key telemetry gate#6601
os-zhuang merged 1 commit into
mainfrom
claude/issue-6599-error-tracking-doc

Conversation

@claude

@claudeclaudeBot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Fixes#6599

apps/console/docs/error-tracking.md predated the #5522 fix chain. Followed literally it told a reader to pnpm add @sentry/react, hand-roll src/lib/sentry.ts, and initialise before React renders — bolting a second, ungated Sentry init onto an app that already has a gated one, and reinstating the report-before-permission ordering that PR #5982 eliminated. The committed-telemetry-endpoint.test.ts ratchet would not have caught it: nothing gets committed, the init is just wrong.

What changed

One file, apps/console/docs/error-tracking.md (+156 / -131). No code, no dependencies, no changeset owed.

  • Installation section and the hand-rolled recipe are gone. Sentry is built in; the doc now says so and warns explicitly against adding a second init.
  • The two-key gate is stated as a conjunction, each half with its variable names and where it lives: build-time VITE_SENTRY_DSN (plus VITE_SENTRY_SEND_DEFAULT_PII, VITE_SENTRY_ENABLED, VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_RELEASE, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY, mirrored from the authoritative comment block in apps/console/.env.production) AND the runtime permission telemetry.allowClientErrorReporting, granted with OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED or new RuntimeConfigPlugin({ allowClientErrorReporting: true }), linked to the canonical row in objectstack content/docs/deployment/environment-variables.mdx.
  • Fail-closed contract in one line, plus the closed truthy vocabulary (1/true/on/yes), the mount-time refusal of an unrecognised spelling, and the OS_CLOUD_URL=off interaction that lowers a well-spelled grant to denied.
  • Ordering documented for anyone embedding app-shell in their own host: initSentry() memoizes its verdict, so it must run after initRuntimeConfig() settles.
  • "Option 2: Custom Error Boundary" removed. It was a second ungated exfiltration path presented as an alternative. Replaced by the built-in captureError() / setSentryUser(), which route through the same gate and no-op when it denies, plus a pointer to the app-shell ErrorBoundary that already calls them.
  • Verification steps now start with the half an operator can inspect from outside: curl /api/v1/runtime/config | jq .telemetry, then the vendor-sentry chunk in the Network tab, then a thrown test error.

Two corrections beyond the card table

Both found while verifying the doc against the tree, both in the same file:

  1. The CSP section was fabricated. It claimed the console "includes a Content Security Policy (CSP) meta tag" whose default "already includes https://*.sentry.io in the connect-src directive". Measured: apps/console/index.html sets no CSP meta tag, and git grep -rlni "content-security-policy" over the repo excluding CHANGELOGs returns exactly one file — the doc itself. Rewritten to state the console ships no CSP, and to give the connect-src a hosting layer would need.
  2. VITE_ENVIRONMENT and VITE_ERROR_ENDPOINT had zero read sites anywhere in the tree. Both removed.

The source-maps CI note is kept — sourcemap: false at apps/console/vite.config.ts:702 is still accurate — now with --release pinned to VITE_SENTRY_RELEASE so uploaded maps match the events.

Acceptance criteria

  • One file changed: apps/console/docs/error-tracking.md. No changeset (check-changeset-presence verdict: "No source of a released package changed in this range, so no changeset is owed.")
  • Every env var named in the doc greps to a real read site — 11/11 verified mechanically (VITE_SENTRY_DSN, VITE_SENTRY_ENABLED, VITE_SENTRY_SEND_DEFAULT_PII, VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_RELEASE, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY, VITE_APP_VERSION, VITE_SERVER_URL in objectui; OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED, OS_CLOUD_URL in objectstack).
  • No instruction in the doc, followed literally, produces an ungated telemetry path. Every reporting entry point it now names (captureError, setSentryUser, ErrorBoundary) routes through resolveSentryGate.

Verification, at cca3b38

Gate union re-run after the final commit:

gateverdict line
check:control-bytesOK (scanned 5451 tracked text file(s); skipped 85 binary)
check:doc-fencesevery TypeScript block in 223 document(s) is fenced ts/tsx/typescript
check:shell-escape-residueOK (4/4 root(s) resolved ... 0 occurrence(s) outside a fence)
check-changeset-presenceNo source of a released package changed in this range

⚠️Declared narrowing, and an honest coverage statement. Of the gates above, only check:control-bytes actually contains the edited file in its scan surface — it enumerates git ls-files, and the file is tracked (positive control run). The three documentation gates all root at content/docs (check:doc-fences adds packages/*/README.md) and do not descend into apps/, so their green is real but says nothing about this file. eslint cannot judge it either: every files: glob in eslint.config.js is **/*.{ts,tsx}, so markdown is outside eslint entirely and a full pnpm lint could not change this verdict. That gap is recorded as #6600 (filed unassigned, finding) — not addressed here, out of scope for this card.

This is a prose change to an operator guide with no mechanical checker; the substantive verification is the read-site audit above and the line-by-line check of every claim against resolveSentryGate, runtime-config.ts, main.tsx, .env.production, vite.config.ts, and the objectstack telemetry-posture.ts / runtime-config-plugin.ts.

Refs: #5522 · PR #5559 · PR #5982 · objectstack#10805 · objectstack PR #11382 · objectstack-ai/cloud#1508


Generated by Claude Code

… gate
`apps/console/docs/error-tracking.md` predated the #5522 fix chain and every
load-bearing instruction in it contradicted the shipped design. Followed
literally it told a reader to `pnpm add @sentry/react`, hand-roll
`src/lib/sentry.ts`, and call it before React renders — bolting a second,
ungated Sentry init onto an app that already has a gated one, which is exactly
the "decision frozen at build time, no operator switch" shape #5522 removed.
Rewritten to describe the system that exists:
- Sentry is built in; the install + hand-rolled-init recipe is gone.
- Enabling is the CONJUNCTION of two independent opt-ins: build-time
`VITE_SENTRY_DSN` (plus the optional knobs mirrored from the authoritative
comment block in `apps/console/.env.production`) AND the runtime permission
`telemetry.allowClientErrorReporting`, granted server-side with
`OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED` or
`new RuntimeConfigPlugin({ allowClientErrorReporting: true })`.
- The fail-closed contract stated once: either half missing => no reporting,
silently, by design; every "cannot determine" state reads denied.
- Ordering documented for host embedders: `initSentry()` must run after
`initRuntimeConfig()` settles, because it memoizes its verdict.
- "Option 2: Custom Error Boundary" removed. It was a second ungated
exfiltration path presented as an alternative; replaced by the built-in
`captureError()` / `setSentryUser()`, which route through the same gate, plus
a pointer to the app-shell `ErrorBoundary` that already calls them.
- Verification steps now check the half an operator can inspect from outside:
`curl /api/v1/runtime/config | jq .telemetry`, then the vendor-sentry chunk,
then a thrown test error.
Two corrections beyond the card's table, both verified against the tree:
- The CSP section was fabricated. It claimed the console "includes a Content
Security Policy meta tag" whose default "already includes https://*.sentry.io
in connect-src". `apps/console/index.html` sets no CSP meta tag and the doc
was the only file in the repo mentioning CSP at all. Rewritten to say the
console ships no CSP, and to give the connect-src a *hosting layer* would
need.
- `VITE_ENVIRONMENT` and `VITE_ERROR_ENDPOINT` had zero read sites anywhere in
the tree; both are gone. Every env var the doc now names greps to a real read
site (11/11 verified).
The source-maps CI note is kept — `sourcemap: false` in
`apps/console/vite.config.ts` is still accurate — with the release pinned to
`VITE_SENTRY_RELEASE` so uploaded maps match the events.
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 52 chunks)3235.4 KB3266.6 KB
Main entry chunk (gzip)157.0 KB350 KB
Entry fileindex-BEKllrlV.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)11.71KB4.46KB
app-shell (runtime-config.js)18.10KB6.51KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)506.01KB114.64KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)173.10KB47.96KB
fields (index.js)238.89KB60.02KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.95KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)9.53KB3.38KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.64KB1.50KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)1.93KB0.88KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.85KB12.89KB
plugin-charts (index.js)64.66KB18.32KB
plugin-chatbot (index.js)188.60KB44.82KB
plugin-dashboard (index.js)133.48KB34.49KB
plugin-designer (index.js)212.80KB43.15KB
plugin-detail (index.js)245.29KB62.39KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)131.78KB32.19KB
plugin-gantt (index.js)165.16KB40.33KB
plugin-grid (index.js)201.66KB54.58KB
plugin-kanban (index.js)53.11KB14.62KB
plugin-list (index.js)112.86KB27.54KB
plugin-map (index.js)20.09KB6.62KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)26.72KB7.71KB
plugin-tree (index.js)9.26KB3.13KB
plugin-view (index.js)85.87KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)63.21KB21.05KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)2.44KB1.21KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)12.13KB3.65KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)7.54KB2.63KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuangos-zhuang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PM review (dispatching seat for #6599): read the full diff against the shipped implementation.

  • Every instruction now describes the code that exists: the two-key gate (resolveSentryGate), the fail-closed contract with all indeterminate states reading denied, the #5982 init-after-runtime-config ordering with the memoization trap spelled out, and the closed truthy vocabulary with loud refusal.
  • The dev's mechanical acceptance check (11/11 named variables grep to real read sites; the two ghosts VITE_ENVIRONMENT / VITE_ERROR_ENDPOINT removed) matches the card's acceptance criterion exactly.
  • Two corrections beyond the card, both verified measurements, both accepted: the old CSP section was fabricated (no CSP meta tag exists in apps/console/index.html), and Option 2 was a second ungated exfiltration path — replaced with the gated captureError/setSentryUser helpers.
  • Heads-up recorded, not a blocker: objectstack#12681 (maintainer-ruled, queued) will move the DSN source server-side; this doc's runtime half gets updated again as part of that change. Landing the accurate current-shape doc first is the ruled sequencing.

Landing via the normal queue — apps/console/docs/** is not a governed surface.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 27, 2026 08:42
@os-zhuang
os-zhuang added this pull request to the merge queueAug 27, 2026
Merged via the queue into main with commit 9363ad0Aug 27, 2026
27 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-6599-error-tracking-doc branch August 27, 2026 08:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(console): error-tracking.md predates the #5522 two-key telemetry gate — following it as written recreates the exact shape #5522 removed

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(console): rewrite error-tracking guide for the two-key telemetry gate - #6601

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-6599-error-tracking-doc
Aug 27, 2026
Merged

docs(console): rewrite error-tracking guide for the two-key telemetry gate#6601
os-zhuang merged 1 commit into
mainfrom
claude/issue-6599-error-tracking-doc

Conversation

@claude

@claudeclaudeBot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Fixes#6599

apps/console/docs/error-tracking.md predated the #5522 fix chain. Followed literally it told a reader to pnpm add @sentry/react, hand-roll src/lib/sentry.ts, and initialise before React renders — bolting a second, ungated Sentry init onto an app that already has a gated one, and reinstating the report-before-permission ordering that PR #5982 eliminated. The committed-telemetry-endpoint.test.ts ratchet would not have caught it: nothing gets committed, the init is just wrong.

What changed

One file, apps/console/docs/error-tracking.md (+156 / -131). No code, no dependencies, no changeset owed.

  • Installation section and the hand-rolled recipe are gone. Sentry is built in; the doc now says so and warns explicitly against adding a second init.
  • The two-key gate is stated as a conjunction, each half with its variable names and where it lives: build-time VITE_SENTRY_DSN (plus VITE_SENTRY_SEND_DEFAULT_PII, VITE_SENTRY_ENABLED, VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_RELEASE, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY, mirrored from the authoritative comment block in apps/console/.env.production) AND the runtime permission telemetry.allowClientErrorReporting, granted with OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED or new RuntimeConfigPlugin({ allowClientErrorReporting: true }), linked to the canonical row in objectstack content/docs/deployment/environment-variables.mdx.
  • Fail-closed contract in one line, plus the closed truthy vocabulary (1/true/on/yes), the mount-time refusal of an unrecognised spelling, and the OS_CLOUD_URL=off interaction that lowers a well-spelled grant to denied.
  • Ordering documented for anyone embedding app-shell in their own host: initSentry() memoizes its verdict, so it must run after initRuntimeConfig() settles.
  • "Option 2: Custom Error Boundary" removed. It was a second ungated exfiltration path presented as an alternative. Replaced by the built-in captureError() / setSentryUser(), which route through the same gate and no-op when it denies, plus a pointer to the app-shell ErrorBoundary that already calls them.
  • Verification steps now start with the half an operator can inspect from outside: curl /api/v1/runtime/config | jq .telemetry, then the vendor-sentry chunk in the Network tab, then a thrown test error.

Two corrections beyond the card table

Both found while verifying the doc against the tree, both in the same file:

  1. The CSP section was fabricated. It claimed the console "includes a Content Security Policy (CSP) meta tag" whose default "already includes https://*.sentry.io in the connect-src directive". Measured: apps/console/index.html sets no CSP meta tag, and git grep -rlni "content-security-policy" over the repo excluding CHANGELOGs returns exactly one file — the doc itself. Rewritten to state the console ships no CSP, and to give the connect-src a hosting layer would need.
  2. VITE_ENVIRONMENT and VITE_ERROR_ENDPOINT had zero read sites anywhere in the tree. Both removed.

The source-maps CI note is kept — sourcemap: false at apps/console/vite.config.ts:702 is still accurate — now with --release pinned to VITE_SENTRY_RELEASE so uploaded maps match the events.

Acceptance criteria

  • One file changed: apps/console/docs/error-tracking.md. No changeset (check-changeset-presence verdict: "No source of a released package changed in this range, so no changeset is owed.")
  • Every env var named in the doc greps to a real read site — 11/11 verified mechanically (VITE_SENTRY_DSN, VITE_SENTRY_ENABLED, VITE_SENTRY_SEND_DEFAULT_PII, VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_RELEASE, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY, VITE_APP_VERSION, VITE_SERVER_URL in objectui; OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED, OS_CLOUD_URL in objectstack).
  • No instruction in the doc, followed literally, produces an ungated telemetry path. Every reporting entry point it now names (captureError, setSentryUser, ErrorBoundary) routes through resolveSentryGate.

Verification, at cca3b38

Gate union re-run after the final commit:

gateverdict line
check:control-bytesOK (scanned 5451 tracked text file(s); skipped 85 binary)
check:doc-fencesevery TypeScript block in 223 document(s) is fenced ts/tsx/typescript
check:shell-escape-residueOK (4/4 root(s) resolved ... 0 occurrence(s) outside a fence)
check-changeset-presenceNo source of a released package changed in this range

⚠️Declared narrowing, and an honest coverage statement. Of the gates above, only check:control-bytes actually contains the edited file in its scan surface — it enumerates git ls-files, and the file is tracked (positive control run). The three documentation gates all root at content/docs (check:doc-fences adds packages/*/README.md) and do not descend into apps/, so their green is real but says nothing about this file. eslint cannot judge it either: every files: glob in eslint.config.js is **/*.{ts,tsx}, so markdown is outside eslint entirely and a full pnpm lint could not change this verdict. That gap is recorded as #6600 (filed unassigned, finding) — not addressed here, out of scope for this card.

This is a prose change to an operator guide with no mechanical checker; the substantive verification is the read-site audit above and the line-by-line check of every claim against resolveSentryGate, runtime-config.ts, main.tsx, .env.production, vite.config.ts, and the objectstack telemetry-posture.ts / runtime-config-plugin.ts.

Refs: #5522 · PR #5559 · PR #5982 · objectstack#10805 · objectstack PR #11382 · objectstack-ai/cloud#1508


Generated by Claude Code

… gate
`apps/console/docs/error-tracking.md` predated the #5522 fix chain and every
load-bearing instruction in it contradicted the shipped design. Followed
literally it told a reader to `pnpm add @sentry/react`, hand-roll
`src/lib/sentry.ts`, and call it before React renders — bolting a second,
ungated Sentry init onto an app that already has a gated one, which is exactly
the "decision frozen at build time, no operator switch" shape #5522 removed.
Rewritten to describe the system that exists:
- Sentry is built in; the install + hand-rolled-init recipe is gone.
- Enabling is the CONJUNCTION of two independent opt-ins: build-time
`VITE_SENTRY_DSN` (plus the optional knobs mirrored from the authoritative
comment block in `apps/console/.env.production`) AND the runtime permission
`telemetry.allowClientErrorReporting`, granted server-side with
`OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED` or
`new RuntimeConfigPlugin({ allowClientErrorReporting: true })`.
- The fail-closed contract stated once: either half missing => no reporting,
silently, by design; every "cannot determine" state reads denied.
- Ordering documented for host embedders: `initSentry()` must run after
`initRuntimeConfig()` settles, because it memoizes its verdict.
- "Option 2: Custom Error Boundary" removed. It was a second ungated
exfiltration path presented as an alternative; replaced by the built-in
`captureError()` / `setSentryUser()`, which route through the same gate, plus
a pointer to the app-shell `ErrorBoundary` that already calls them.
- Verification steps now check the half an operator can inspect from outside:
`curl /api/v1/runtime/config | jq .telemetry`, then the vendor-sentry chunk,
then a thrown test error.
Two corrections beyond the card's table, both verified against the tree:
- The CSP section was fabricated. It claimed the console "includes a Content
Security Policy meta tag" whose default "already includes https://*.sentry.io
in connect-src". `apps/console/index.html` sets no CSP meta tag and the doc
was the only file in the repo mentioning CSP at all. Rewritten to say the
console ships no CSP, and to give the connect-src a *hosting layer* would
need.
- `VITE_ENVIRONMENT` and `VITE_ERROR_ENDPOINT` had zero read sites anywhere in
the tree; both are gone. Every env var the doc now names greps to a real read
site (11/11 verified).
The source-maps CI note is kept — `sourcemap: false` in
`apps/console/vite.config.ts` is still accurate — with the release pinned to
`VITE_SENTRY_RELEASE` so uploaded maps match the events.
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 52 chunks)3235.4 KB3266.6 KB
Main entry chunk (gzip)157.0 KB350 KB
Entry fileindex-BEKllrlV.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)11.71KB4.46KB
app-shell (runtime-config.js)18.10KB6.51KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)506.01KB114.64KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)173.10KB47.96KB
fields (index.js)238.89KB60.02KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.95KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)9.53KB3.38KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.64KB1.50KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)1.93KB0.88KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.85KB12.89KB
plugin-charts (index.js)64.66KB18.32KB
plugin-chatbot (index.js)188.60KB44.82KB
plugin-dashboard (index.js)133.48KB34.49KB
plugin-designer (index.js)212.80KB43.15KB
plugin-detail (index.js)245.29KB62.39KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)131.78KB32.19KB
plugin-gantt (index.js)165.16KB40.33KB
plugin-grid (index.js)201.66KB54.58KB
plugin-kanban (index.js)53.11KB14.62KB
plugin-list (index.js)112.86KB27.54KB
plugin-map (index.js)20.09KB6.62KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)26.72KB7.71KB
plugin-tree (index.js)9.26KB3.13KB
plugin-view (index.js)85.87KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)63.21KB21.05KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)2.44KB1.21KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)12.13KB3.65KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)7.54KB2.63KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuangos-zhuang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PM review (dispatching seat for #6599): read the full diff against the shipped implementation.

  • Every instruction now describes the code that exists: the two-key gate (resolveSentryGate), the fail-closed contract with all indeterminate states reading denied, the #5982 init-after-runtime-config ordering with the memoization trap spelled out, and the closed truthy vocabulary with loud refusal.
  • The dev's mechanical acceptance check (11/11 named variables grep to real read sites; the two ghosts VITE_ENVIRONMENT / VITE_ERROR_ENDPOINT removed) matches the card's acceptance criterion exactly.
  • Two corrections beyond the card, both verified measurements, both accepted: the old CSP section was fabricated (no CSP meta tag exists in apps/console/index.html), and Option 2 was a second ungated exfiltration path — replaced with the gated captureError/setSentryUser helpers.
  • Heads-up recorded, not a blocker: objectstack#12681 (maintainer-ruled, queued) will move the DSN source server-side; this doc's runtime half gets updated again as part of that change. Landing the accurate current-shape doc first is the ruled sequencing.

Landing via the normal queue — apps/console/docs/** is not a governed surface.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 27, 2026 08:42
@os-zhuang
os-zhuang added this pull request to the merge queueAug 27, 2026
Merged via the queue into main with commit 9363ad0Aug 27, 2026
27 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-6599-error-tracking-doc branch August 27, 2026 08:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(console): error-tracking.md predates the #5522 two-key telemetry gate — following it as written recreates the exact shape #5522 removed

2 participants

@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

docs(console): rewrite error-tracking guide for the two-key telemetry gate - #6601

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-6599-error-tracking-doc
Aug 27, 2026
Merged

docs(console): rewrite error-tracking guide for the two-key telemetry gate#6601
os-zhuang merged 1 commit into
mainfrom
claude/issue-6599-error-tracking-doc

Conversation

@claude

@claudeclaudeBot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Fixes#6599

apps/console/docs/error-tracking.md predated the #5522 fix chain. Followed literally it told a reader to pnpm add @sentry/react, hand-roll src/lib/sentry.ts, and initialise before React renders — bolting a second, ungated Sentry init onto an app that already has a gated one, and reinstating the report-before-permission ordering that PR #5982 eliminated. The committed-telemetry-endpoint.test.ts ratchet would not have caught it: nothing gets committed, the init is just wrong.

What changed

One file, apps/console/docs/error-tracking.md (+156 / -131). No code, no dependencies, no changeset owed.

  • Installation section and the hand-rolled recipe are gone. Sentry is built in; the doc now says so and warns explicitly against adding a second init.
  • The two-key gate is stated as a conjunction, each half with its variable names and where it lives: build-time VITE_SENTRY_DSN (plus VITE_SENTRY_SEND_DEFAULT_PII, VITE_SENTRY_ENABLED, VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_RELEASE, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY, mirrored from the authoritative comment block in apps/console/.env.production) AND the runtime permission telemetry.allowClientErrorReporting, granted with OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED or new RuntimeConfigPlugin({ allowClientErrorReporting: true }), linked to the canonical row in objectstack content/docs/deployment/environment-variables.mdx.
  • Fail-closed contract in one line, plus the closed truthy vocabulary (1/true/on/yes), the mount-time refusal of an unrecognised spelling, and the OS_CLOUD_URL=off interaction that lowers a well-spelled grant to denied.
  • Ordering documented for anyone embedding app-shell in their own host: initSentry() memoizes its verdict, so it must run after initRuntimeConfig() settles.
  • "Option 2: Custom Error Boundary" removed. It was a second ungated exfiltration path presented as an alternative. Replaced by the built-in captureError() / setSentryUser(), which route through the same gate and no-op when it denies, plus a pointer to the app-shell ErrorBoundary that already calls them.
  • Verification steps now start with the half an operator can inspect from outside: curl /api/v1/runtime/config | jq .telemetry, then the vendor-sentry chunk in the Network tab, then a thrown test error.

Two corrections beyond the card table

Both found while verifying the doc against the tree, both in the same file:

  1. The CSP section was fabricated. It claimed the console "includes a Content Security Policy (CSP) meta tag" whose default "already includes https://*.sentry.io in the connect-src directive". Measured: apps/console/index.html sets no CSP meta tag, and git grep -rlni "content-security-policy" over the repo excluding CHANGELOGs returns exactly one file — the doc itself. Rewritten to state the console ships no CSP, and to give the connect-src a hosting layer would need.
  2. VITE_ENVIRONMENT and VITE_ERROR_ENDPOINT had zero read sites anywhere in the tree. Both removed.

The source-maps CI note is kept — sourcemap: false at apps/console/vite.config.ts:702 is still accurate — now with --release pinned to VITE_SENTRY_RELEASE so uploaded maps match the events.

Acceptance criteria

  • One file changed: apps/console/docs/error-tracking.md. No changeset (check-changeset-presence verdict: "No source of a released package changed in this range, so no changeset is owed.")
  • Every env var named in the doc greps to a real read site — 11/11 verified mechanically (VITE_SENTRY_DSN, VITE_SENTRY_ENABLED, VITE_SENTRY_SEND_DEFAULT_PII, VITE_SENTRY_ENVIRONMENT, VITE_SENTRY_RELEASE, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY, VITE_APP_VERSION, VITE_SERVER_URL in objectui; OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED, OS_CLOUD_URL in objectstack).
  • No instruction in the doc, followed literally, produces an ungated telemetry path. Every reporting entry point it now names (captureError, setSentryUser, ErrorBoundary) routes through resolveSentryGate.

Verification, at cca3b38

Gate union re-run after the final commit:

gateverdict line
check:control-bytesOK (scanned 5451 tracked text file(s); skipped 85 binary)
check:doc-fencesevery TypeScript block in 223 document(s) is fenced ts/tsx/typescript
check:shell-escape-residueOK (4/4 root(s) resolved ... 0 occurrence(s) outside a fence)
check-changeset-presenceNo source of a released package changed in this range

⚠️Declared narrowing, and an honest coverage statement. Of the gates above, only check:control-bytes actually contains the edited file in its scan surface — it enumerates git ls-files, and the file is tracked (positive control run). The three documentation gates all root at content/docs (check:doc-fences adds packages/*/README.md) and do not descend into apps/, so their green is real but says nothing about this file. eslint cannot judge it either: every files: glob in eslint.config.js is **/*.{ts,tsx}, so markdown is outside eslint entirely and a full pnpm lint could not change this verdict. That gap is recorded as #6600 (filed unassigned, finding) — not addressed here, out of scope for this card.

This is a prose change to an operator guide with no mechanical checker; the substantive verification is the read-site audit above and the line-by-line check of every claim against resolveSentryGate, runtime-config.ts, main.tsx, .env.production, vite.config.ts, and the objectstack telemetry-posture.ts / runtime-config-plugin.ts.

Refs: #5522 · PR #5559 · PR #5982 · objectstack#10805 · objectstack PR #11382 · objectstack-ai/cloud#1508


Generated by Claude Code

… gate
`apps/console/docs/error-tracking.md` predated the #5522 fix chain and every
load-bearing instruction in it contradicted the shipped design. Followed
literally it told a reader to `pnpm add @sentry/react`, hand-roll
`src/lib/sentry.ts`, and call it before React renders — bolting a second,
ungated Sentry init onto an app that already has a gated one, which is exactly
the "decision frozen at build time, no operator switch" shape #5522 removed.
Rewritten to describe the system that exists:
- Sentry is built in; the install + hand-rolled-init recipe is gone.
- Enabling is the CONJUNCTION of two independent opt-ins: build-time
`VITE_SENTRY_DSN` (plus the optional knobs mirrored from the authoritative
comment block in `apps/console/.env.production`) AND the runtime permission
`telemetry.allowClientErrorReporting`, granted server-side with
`OS_TELEMETRY_CLIENT_ERROR_REPORTING_ENABLED` or
`new RuntimeConfigPlugin({ allowClientErrorReporting: true })`.
- The fail-closed contract stated once: either half missing => no reporting,
silently, by design; every "cannot determine" state reads denied.
- Ordering documented for host embedders: `initSentry()` must run after
`initRuntimeConfig()` settles, because it memoizes its verdict.
- "Option 2: Custom Error Boundary" removed. It was a second ungated
exfiltration path presented as an alternative; replaced by the built-in
`captureError()` / `setSentryUser()`, which route through the same gate, plus
a pointer to the app-shell `ErrorBoundary` that already calls them.
- Verification steps now check the half an operator can inspect from outside:
`curl /api/v1/runtime/config | jq .telemetry`, then the vendor-sentry chunk,
then a thrown test error.
Two corrections beyond the card's table, both verified against the tree:
- The CSP section was fabricated. It claimed the console "includes a Content
Security Policy meta tag" whose default "already includes https://*.sentry.io
in connect-src". `apps/console/index.html` sets no CSP meta tag and the doc
was the only file in the repo mentioning CSP at all. Rewritten to say the
console ships no CSP, and to give the connect-src a *hosting layer* would
need.
- `VITE_ENVIRONMENT` and `VITE_ERROR_ENDPOINT` had zero read sites anywhere in
the tree; both are gone. Every env var the doc now names greps to a real read
site (11/11 verified).
The source-maps CI note is kept — `sourcemap: false` in
`apps/console/vite.config.ts` is still accurate — with the release pinned to
`VITE_SENTRY_RELEASE` so uploaded maps match the events.
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 52 chunks)3235.4 KB3266.6 KB
Main entry chunk (gzip)157.0 KB350 KB
Entry fileindex-BEKllrlV.js
StatusPASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)11.71KB4.46KB
app-shell (runtime-config.js)18.10KB6.51KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)506.01KB114.64KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)173.10KB47.96KB
fields (index.js)238.89KB60.02KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.95KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)9.53KB3.38KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.64KB1.50KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)1.93KB0.88KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.85KB12.89KB
plugin-charts (index.js)64.66KB18.32KB
plugin-chatbot (index.js)188.60KB44.82KB
plugin-dashboard (index.js)133.48KB34.49KB
plugin-designer (index.js)212.80KB43.15KB
plugin-detail (index.js)245.29KB62.39KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)131.78KB32.19KB
plugin-gantt (index.js)165.16KB40.33KB
plugin-grid (index.js)201.66KB54.58KB
plugin-kanban (index.js)53.11KB14.62KB
plugin-list (index.js)112.86KB27.54KB
plugin-map (index.js)20.09KB6.62KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)26.72KB7.71KB
plugin-tree (index.js)9.26KB3.13KB
plugin-view (index.js)85.87KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)63.21KB21.05KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)2.44KB1.21KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)12.13KB3.65KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)7.54KB2.63KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuangos-zhuang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PM review (dispatching seat for #6599): read the full diff against the shipped implementation.

  • Every instruction now describes the code that exists: the two-key gate (resolveSentryGate), the fail-closed contract with all indeterminate states reading denied, the #5982 init-after-runtime-config ordering with the memoization trap spelled out, and the closed truthy vocabulary with loud refusal.
  • The dev's mechanical acceptance check (11/11 named variables grep to real read sites; the two ghosts VITE_ENVIRONMENT / VITE_ERROR_ENDPOINT removed) matches the card's acceptance criterion exactly.
  • Two corrections beyond the card, both verified measurements, both accepted: the old CSP section was fabricated (no CSP meta tag exists in apps/console/index.html), and Option 2 was a second ungated exfiltration path — replaced with the gated captureError/setSentryUser helpers.
  • Heads-up recorded, not a blocker: objectstack#12681 (maintainer-ruled, queued) will move the DSN source server-side; this doc's runtime half gets updated again as part of that change. Landing the accurate current-shape doc first is the ruled sequencing.

Landing via the normal queue — apps/console/docs/** is not a governed surface.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 27, 2026 08:42
@os-zhuang
os-zhuang added this pull request to the merge queueAug 27, 2026
Merged via the queue into main with commit 9363ad0Aug 27, 2026
27 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-6599-error-tracking-doc branch August 27, 2026 08:55
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(console): error-tracking.md predates the #5522 two-key telemetry gate — following it as written recreates the exact shape #5522 removed

2 participants

@os-zhuang@claude