Skip to content

record — the option-B reader by-shape sweep: method, derived 37-key collection set, triaged false positives, and the two packages it found #15210

Description

@hotlong

Part of #14122 · related #14512 (the emitter half) and #15004 (the acceptance pin).

Filed from the by-shape sweep card #15007 requires before implementing — the sweep the card asks for precisely because the two earlier enumerations scoped themselves by package name up front and each missed a package. This one turns up two more packages, neither of which the program scopes: @objectstack/verify and @objectstack/plugin-dev.

Not folded into #15007 per that card's instruction ("If another package turns up, file it as 5/4 rather than folding it in").

The sites

Package file:line Reads What an option-B artifact does to it
@objectstack/verify packages/verify/src/derive.ts:176 config.objects deriveCrudCases derives ZERO CRUD round-trip cases
@objectstack/verify packages/verify/src/derive.ts:180 config.datasources the datasource-by-name map is empty, so every derived case loses its datasource
@objectstack/verify packages/verify/src/rls.ts:134 config.positions declaredPositionNames returns [] — no persona is built for any declared position
@objectstack/verify packages/verify/src/rls.ts:346 config.objects rlsProbePermissionSet builds an EMPTY probe permission set — the persona that makes an RLS run a probe grants nothing and narrows nothing
@objectstack/plugin-dev packages/plugins/plugin-dev/src/dev-plugin.ts:526 options.stack.translations the I18nServicePlugin auto-detect never fires, so a multi-package app boots os dev with the in-memory i18n fallback instead of the file-based service

Reached with a composed multi-package config today:

  • packages/cli/src/commands/verify.ts:155 calls declaredPositionNames(config) on the loaded config; rlsProbeSecurity / deriveCrudCases run off the same object.
  • new DevPlugin({ stack: config }) is the documented construction (packages/plugins/plugin-dev/src/index.ts:36).

Why this is worse than a missing collection, not merely equal to one

The other reader cards lose a capability. @objectstack/verify loses the verification itself, and reports success while doing it: zero derived cases and an empty RLS probe set are a green run that measured nothing. A multi-package app under option B would get a passing os verify that asserts nothing about any of its objects — the failure mode #15004 exists to make loud, arriving in the one place where a false green is most expensive.

Acceptance (suggested — the ruling's shape, not re-adjudicated here)

May well want splitting into 5/4 and 6/4 — one card per package, per the ruling's "each site separately reviewable and revertible". Filed as one so the finding is not lost; the split is the PM's call.

The sweep this came from, stated so the next one can be compared against it

By SHAPE, across all of packages/, never against a package list. The collection key set was derived, not transcribed: ObjectStackDefinitionSchema shape keys intersected with AssembledPackageBodySchema shape keys = the 37 package-owned collections (actions agents analyticsCubes apis apps books capabilities connectors dashboards data datasets datasourceMapping datasources devPlugins docs emailTemplates flows functions hooks jobs mappings objectExtensions objects pages permissions plugins positions reports requires sharingRules skills tiers tools translations viewItems views webhooks), complement = the 7 envelope keys.

Four shapes searched over every packages/** source file (tests, fixtures and dist/ excluded):

  1. property access on a stack-ish identifier — (config|cfg|bundle|artifact|stack|stackConfig|appConfig|projectConfig|loadedConfig|resolvedConfig|definition|objectstackConfig|osConfig|conf)?.KEY
  2. destructuring — const { KEY, ... } = ...(config|bundle|stack|artifact)
  3. bracket access — identifier['KEY']
  4. this./options. member reads — (this|options).(stack|bundle|artifact|config)?.KEY

Plus two structural passes: every parameter typed ObjectStackDefinition / ObjectStackConfig / StackDefinition / AssembledPackageBody, and every site that opens the artifact itself (objectstack.json + readFileSync / JSON.parse).

Triaged as NOT findings, so the next sweep does not re-litigate them:

  • @objectstack/plugin-auththis.config.plugins is AuthPluginConfig.plugins (better-auth plugin options), not the stack's plugins collection.
  • @objectstack/objectql engine.ts:13946-13965config.datasources / config.objects / config.hooks on the ENGINE config, a name-keyed record, not the stack's arrays.
  • @objectstack/service-analytics analytics-service.ts:734config.datasets on AnalyticsServiceConfig (the same object carrying getObjectFieldNames), not a stack.
  • @objectstack/metadata-protocol build-probes.ts:171config there is a VIEW body's config field.
  • @objectstack/metadata-core form-predicate-root-policy.ts:445definition.views on an authoring-time definition, reached from lint rather than from a boot.
  • @objectstack/lint (201 hits) and packages/spec (95) — the linter's authoring-time surface and the schemas themselves.
  • plugin-email / plugin-webhooks / plugin-security bootstrap-declared-* — the stack.KEY spellings there are in PROSE; those readers take their items from the metadata service.
  • loadArtifactBundle has callers in packages/runtime only.
  • The i18n-extract configs under packages/*/scripts/ are single-package authoring stacks with no packages[].

Generated by Claude Code

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions