Skip to content

docs(core): replace the dangling SECURITY_FIX_SUMMARY @see with its rationale - #6339

Merged
os-support-ai merged 1 commit into
mainfrom
claude/issue-6275-validation-engine-dangling-see
Aug 25, 2026
Merged

docs(core): replace the dangling SECURITY_FIX_SUMMARY @see with its rationale#6339
os-support-ai merged 1 commit into
mainfrom
claude/issue-6275-validation-engine-dangling-see

Conversation

@os-support-ai

Copy link
Copy Markdown
Collaborator

Fixes #6275

SimpleExpressionEvaluator's doc block in packages/core/src/validation/validators/object-validation-engine.ts pointed at a SECURITY_FIX_SUMMARY.md that has not existed since January. This replaces the dead @see with the load-bearing reasoning that file carried.

Premise table — re-measured on origin/main @ 79ebf30d1 (the card was written against c38162d7c; main moved 30+ commits)

# assumption verdict how measured
M1 the @see sits at object-validation-engine.ts:221 ✅ held line number re-derived, not trusted: grep -n SECURITY_FIX_SUMMARY <file>221: * @see https://github.com/objectstack-ai/objectui/blob/main/SECURITY_FIX_SUMMARY.md
M2 SECURITY_FIX_SUMMARY.md exists nowhere in the tree ✅ held git ls-tree -r --name-only HEAD | grep -i SECURITY_FIX_SUMMARY → 0 hits
M3 exactly one hit across packages/ and apps/ ✅ held grep -rn over both, excluding node_modules/dist → count 1; git grep over all tracked files agrees

All three held. One subsidiary claim in the card body did not — see "Two card claims that did not survive measurement" below.

The three-way choice, and the evidence from ea72f1886

Chosen: fold the load-bearing sentence into the doc block, then drop the dead @see. (Not "repoint" — nothing to repoint at. Not "just delete" — the reference was not redundant.)

Evidence the content was dropped, not moved:

$ git show --shortstat --format="" ea72f1886
 20 files changed, 6742 deletions(-)

Zero insertions. The commit — "Delete obsolete files and documentation related to PR #300 and v0.4.0 release; remove security fix summary and CRM app fix scripts" — is a bulk removal of 20 root-level summary docs (ARCHITECTURE_EVALUATION.md, RELEASE_SUMMARY_v0.4.0.md, SECURITY_FIX_SUMMARY.md, …). Nothing was written anywhere in the same commit, so there is no successor path to repoint at, and no ADR or docs page anywhere in the tree covers it (git grep -il codeql -- content/ docs/ → empty).

Evidence the reference was not redundant — from git show ea72f1886^:SECURITY_FIX_SUMMARY.md:

Most of what it carried is already in the doc block — the operator list, and the LIMITATIONS list reproduced there verbatim. One thing is not, and is the reason the parser has the shape it has:

Alert: Unsafe code constructed from library input
Issue: Use of new Function() constructor with user-provided expressions, enabling potential code injection attacks

The doc block said what the code does ("without using eval() or new Function()") but never why it must stay that way. A maintainer who wanted chained comparisons or more speed had, in the file, no reason not to reach for new Function() — the ban read as taste. That is the sentence now inlined. The provenance line points at git show ea72f1886^:SECURITY_FIX_SUMMARY.md, an immutable git object, rather than another main blob path that can rot the same way.

Two card claims that did not survive measurement

Neither changes the disposition — both are recorded so the record is accurate.

  1. The card says the @see was "the only pointer the doc block offers for the security rationale behind this evaluator's fail-open polarity". The deleted file contains zero mention of polarity or fail-open — its whole subject is the injection fix. The polarity rationale was already inline in the doc block (the NOT CEL paragraph, ending "…fails OPEN — the same outcome the server produces for an un-evaluable predicate"), and is untouched here.

  2. The card says "this dead link ships in the released package's type declarations". It ships, but not there. Measured by building the package (tsc) and reading the emitted output:

    artifact carries the doc block?
    dist/validation/validators/object-validation-engine.d.ts noSimpleExpressionEvaluator is not exported, so tsc emits no declaration for it; the only occurrence of the name is a passing mention inside the module-level doc block
    dist/validation/validators/object-validation-engine.js yestsc preserves comments, and package.json files: ["dist", …] publishes it

    So the dead link did reach consumers, in the published JavaScript. This is what decides the changeset form below.

Changeset

node scripts/check-changeset-presence.mjs is followed by its own verdict line, not by memory:

✅  1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s):
    .changeset/issue-6275-validation-engine-dangling-see.md.
    Every one of them has an EMPTY frontmatter — declared as releasing nothing, which
    is the explicit exemption and a complete answer to this gate.

Empty frontmatter, deliberately. The change is comment-only on a class that is not exported: no behaviour, no type surface, no API. Nothing a consumer can import, call or depend on moves. The counterweight is concrete — .changeset/config.json puts all 43 packages in one fixed group, so any non-empty bump versions the entire monorepo for a comment. If a reviewer reads the published-.js comment as user-visible enough to warrant a release line, this flips to patch with a one-line edit; the measurement above is the fact that decides it. Never major (check-changeset-no-major.mjs ✅).

Gates

Gate set derived by reading the CI job step lists under .github/workflows/ — every workflow's on: block parsed for a pull_request trigger and its path filter, then each firing job's run: steps read. Verdict lines below are the gates' own; exit codes captured before any pipe (cmd > file 2>&1; EXIT=$?), never through tail.

Union re-run after the final commit, at ad39293c9:

gate (CI workflow) exit the gate's own verdict line
check-changeset-presence (changeset-presence.yml) 0 ✅ 1 source file(s) … declares 1 changeset(s) … EMPTY frontmatter … a complete answer to this gate.
check-changeset-no-major (changeset-guard.yml) 0 ✅ No changeset declares a `major` bump.
check-changeset-fixed (ci.yml) 0 ✅ All workspace packages are in the changeset fixed group.
check-control-bytes (control-bytes.yml) 0 ✅ check-control-bytes: OK (scanned 5225 tracked text file(s); skipped 85 binary).
check-doc-component-types (doc-component-types.yml) 0 ✅ Every documented component type is registered.
check-doc-fence-languages (doc-fence-languages.yml) 0 ✅ check:doc-fences — every TypeScript block in 223 document(s) is fenced ts/tsx/typescript …
check-doc-links (docs-links.yml) 0 Links are valid across 17 scan roots.blind, see below
check-shell-escape-residue (shell-escape-residue.yml) 0 ✅ check-shell-escape-residue: OK (4/4 root(s) resolved …)
check-skills-paths (skills-paths.yml) 0 ✅ check-skills-paths: OK (93/94 stated path(s) resolve …)
check-vi-mock-specifiers (vi-mock-specifiers.yml) 0 ✅ check-vi-mock-specifiers: OK (3748 tracked source file(s) …)
check-lint-coverage (lint.yml) 0 ✅ lint coverage: 46/46 packages linted, 0 with outstanding errors (0 total).
check-entry-guard (lint.yml) 0 ✓ check:entry-guard: 47 scripts/ file(s) — no entry guard outside the baseline …
check-cross-repo-closer-outcome (lint.yml) 0 check-cross-repo-closer-outcome: OK (105 assertions over 18 scenarios …)
check-pre-install-import-graph (pre-install-import-graph.yml) 0 ✅ check-pre-install-import-graph: OK — 16 pre-install step(s) in 15 job(s) …
check-type-check-coverage (ci.yml) 0 ✅ test type-check coverage: 41/41 packages compile their tests, 0 declared debt …
turbo run lint --filter=@object-ui/core (lint.yml) 0 ✖ 515 problems (0 errors, 515 warnings) — all warnings pre-existing, none on the changed lines
turbo run type-check --filter=@object-ui/core (ci.yml) 0 Tasks: 5 successful, 5 total
vitest run packages/core/ (ci.yml test shards) 0 Test Files 100 passed (100) · Tests 2023 passed (2023)

Gates that are structurally blind to this change

  • check-doc-links.mjs (docs-links.yml) — blind, and its green run above is not evidence for this PR. Not a judgement call: its walker is
    scripts/check-doc-links.mjs:650if (!/\.(md|mdx)$/.test(entry.name)) continue;
    so a .ts file cannot enter the population at all. Worth noting the near-miss the issue flagged: the gate does already know how to decide this exact URL shape — https://github.com/objectstack-ai/objectui/(blob|tree)/main/<path> is a case it handles for markdown (check-doc-links.mjs:154). The link was undecidable only because of where it was written, not what it was. Widening the population to @see URLs in TS source is explicitly out of scope here (triage: "separate call, not minted this round") and is not attempted.
  • Every test gate. vitest run packages/core/ (100 files, 2023 tests) passes, but a comment-only diff cannot move any assertion — it is a regression guard, not evidence the link is fixed.
  • readme-exports, performance-budget, doc-snippet-types. They fire on these paths and are left to CI: none can observe a comment. The bundle budget sees no byte change in any minified artifact; the export surface is unchanged (nothing was exported before or after).

The only evidence that the defect is fixed is the direct measurement: blob/main/SECURITY_FIX_SUMMARY.md occurrences across packages/ + apps/ went 1 → 0, and the injected text (CodeQL, Unsafe code constructed from library input, ea72f1886) is present exactly once each on disk.

Narrowing declared

lint, type-check and test were run filtered to @object-ui/core rather than across all 46 packages. Three pieces of evidence that the narrowing excludes nothing:

  1. Population from the tool's own config, not a guesspnpm exec eslint . --format json inside packages/core reports 195 files linted, and the changed file is in that set (checked by path, not assumed).
  2. Counts read from --format json — 0 errors, 515 warnings, of which 17 are on the changed file and all pre-date this PR.
  3. Invariance for untouched files — no eslint config in the repo enables type-aware linting (no project: / projectService in the single root eslint.config.js), so this diff cannot move any untouched file's verdict. Independently, the diff changes only comment bytes in a non-exported class, so no type, value or module-graph fact crosses a package boundary.

check-lint-coverage and check-type-check-coverage above still ran repo-wide and confirm 46/46 and 41/41 clean.

Scope

Fix confined to packages/core (T3 — sibling agents hold packages/plugin-form #5034 and packages/plugin-map #5977; no file overlap). The sweep was not widened (T1): the repo-wide grep found this to be the only SECURITY_FIX_SUMMARY reference, and no other dangling @see or doc link was repaired here. content/docs/releases/ untouched.


Generated by Claude Code

…ationale

`SimpleExpressionEvaluator`'s doc block pointed at
https://github.com/objectstack-ai/objectui/blob/main/SECURITY_FIX_SUMMARY.md.
That file was deleted in ea72f18 — a 20-file bulk removal of the root-level
PR #300 / v0.4.0 summaries, 6742 deletions and zero insertions, so the content
was dropped outright rather than migrated. The link has 404'd since.

Reading `git show ea72f18^:SECURITY_FIX_SUMMARY.md` shows the doc block
already repeats most of what it carried (the operator list, the LIMITATIONS
list verbatim). One thing it carried is NOT in the doc block and is
load-bearing: the parser exists because CodeQL flagged the previous
`new Function(...contextKeys, "'use strict'; return (…)")` as "Unsafe code
constructed from library input". That makes the ban on dynamic code execution
a remediation constraint a future maintainer must not trade away for
expressiveness or speed, not a style preference. That sentence is now inline,
and the provenance points at the immutable git object instead of a `main` blob
path that no longer resolves.

Comment-only: `SimpleExpressionEvaluator` is not exported, so nothing changes
in the emitted `.d.ts`, and no behaviour, type or API surface moves.
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 52 chunks) 3223.5 KB 3266.6 KB
Main entry chunk (gzip) 154.2 KB 350 KB
Entry file index-CVFVnCcX.js
Status PASS

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 10.96KB 4.16KB
app-shell (runtime-config.js) 18.10KB 6.51KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 506.08KB 114.67KB
core (index.js) 5.30KB 2.13KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 171.74KB 47.48KB
fields (index.js) 238.89KB 60.02KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.44KB 1.39KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 33.40KB 8.71KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.95KB 10.97KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.55KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 9.53KB 3.38KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 4.64KB 1.50KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 1.93KB 0.88KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.62KB 12.83KB
plugin-charts (index.js) 64.66KB 18.32KB
plugin-chatbot (index.js) 188.21KB 44.67KB
plugin-dashboard (index.js) 133.35KB 34.45KB
plugin-designer (index.js) 211.95KB 42.75KB
plugin-detail (index.js) 245.10KB 62.31KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 126.92KB 30.85KB
plugin-gantt (index.js) 164.14KB 39.87KB
plugin-grid (index.js) 201.21KB 54.43KB
plugin-kanban (index.js) 52.83KB 14.55KB
plugin-list (index.js) 111.94KB 27.24KB
plugin-map (index.js) 20.09KB 6.62KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.49KB 11.93KB
plugin-timeline (index.js) 26.49KB 7.59KB
plugin-tree (index.js) 9.26KB 3.13KB
plugin-view (index.js) 84.55KB 20.74KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 54.84KB 18.43KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.35KB 0.70KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 12.13KB 3.65KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 7.54KB 2.63KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.49KB 2.14KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

object-validation-engine.ts @see points at SECURITY_FIX_SUMMARY.md, deleted in ea72f1886

2 participants