Skip to content

fix(spec): published agent prompts reference real exports — Object no longer binds the JS global (#9545) - #9615

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-9545-prompt-import-fabrications
Aug 18, 2026
Merged

os-steve merged 1 commit into
mainfrom
claude/issue-9545-prompt-import-fabrications

Conversation

@claude

@claude claude Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Fixes #9545

Three published agent-authoring prompts in packages/spec/prompts/ told agents to import five symbols @objectstack/spec does not export. Four failed loudly. The fifth did not — and that is the one this card exists for.

Verified at 00ad76978 (the final commit; the gate union below was re-run on that tree).

Why the fifth one is different — demonstrated, not asserted

import { Object } from '@objectstack/spec/data' does not resolve, so the annotation in export const AccountObject: Object = { ... } binds to the JavaScript global Object. I compiled that exact shape against the built package:

Probe Result
import { type Object } from '@objectstack/spec/data' TS2305: Module '"@objectstack/spec/data"' has no exported member 'Object'
export const AccountObject: Object = { this_is_not_a_field: 'x', neither_is_this: 12345 } exit 0 — accepts arbitrary garbage

That second row is the defect: metadata authored from this prompt type-checks against a type that constrains nothing, and every signal says it passed.

Resolution proofs

Every substitution was measured against the built .d.ts (pnpm --filter @objectstack/spec build first), then compiled from packages/objectql, which is both a real consumer and the literal audience of implement-objectql.md.

Form Valid input Bad input
ObjectSchema.create({ ... }) exit 0 TS2322 on a bogus field type
z.infer< typeof ObjectSchema > exit 0 TS2353 on an unknown key
ObjectStackDefinitionSchema.parse(...) from @objectstack/spec exit 0 —
RLSUserContextSchema + RowLevelSecurityPolicySchema from @objectstack/spec/security exit 0 —

The corrected create-new-project.md snippet was extracted verbatim from the committed file and compiled: exit 0.

Per-symbol before / after, with referent justification

1. Object — create-new-project.md:52

-import { Object } from '@objectstack/spec/data';
-export const AccountObject: Object = {
+import { ObjectSchema } from '@objectstack/spec/data';
+export const AccountObject = ObjectSchema.create({

Referent: not a name-swap — ObjectSchema is a Zod const, unusable as an annotation. The house authoring convention, measured rather than chosen: ObjectSchema.create({ ... }) appears 34 times across the example apps, and the annotation form appears zero times.

⚠️ Two things this fix must not break, both measured:

  • create-new-project.md:87-89 legitimately calls Object.values(objects) — the JS global. Importing anything named Object would shadow it. That alone rules out introducing a local Object alias.
  • Making the form genuinely constrain exposed that the prompt's own example was already invalid: enable.audit and enable.workflow do not exist. The fabricated annotation had been hiding it. They are now trackHistory / files — the exact pair the enable docstring in data/object.zod.ts uses, with trackHistory documented as the audit-trail flag. Declared here because it is a same-class fix beyond a pure substitution.

2. type Object — implement-objectql.md:17

-import { type Object, ObjectSchema } from '@objectstack/spec/data';
+import { z } from 'zod';
+import { ObjectSchema, type Field, QuerySchema } from '@objectstack/spec/data';
+type ObjectMetadata = z.infer< typeof ObjectSchema >;
+type Query = z.infer< typeof QuerySchema >;

Only the fabricated type Object half was dropped. Referent: Field and QuerySchema are real exports on ./data, kept as-is. No bare Object type is exported — and the omission is deliberate, since every sibling has the full triple (Field / FieldParsed / FieldSchema) while Object has only ObjectSchema. Deriving it matches prompts/instructions.md ("interfaces must be inferred from Zod, z.infer< typeof X >") and spec's own src/contracts/schema-driver.ts, which writes type DataObject = z.infer< typeof ObjectSchema > for exactly this reason.

3. ManifestSchema — implement-objectos.md:16

-import { ManifestSchema } from '@objectstack/spec/system';
+import { ObjectStackDefinitionSchema } from '@objectstack/spec';

Referent: the card framed this as AppManifestSchema vs DeployManifestSchema; both are wrong. The prompt names its own subject — objectstack.config.ts — whose authoring surface is defineStack / ObjectStackDefinitionSchema, on the root entry, not /system. AppManifestSchema installs an app into a running stack; DeployManifestSchema describes a deploy bundle. The subpath changes with the symbol.

4. IdentitySchema + PolicySchema — implement-objectos.md:25

-import { IdentitySchema, PolicySchema } from '@objectstack/spec/system';
+import {
+  RLSUserContextSchema,
+  RowLevelSecurityPolicySchema,
+} from '@objectstack/spec/security';

Referent: /system returns zero matches for Identity — there is no IdentitySchema and no bare Identity either, so the card's own "measured reality" column was itself a fabrication. Substituting it as the card instructed would have written a new fabricated symbol into a published prompt.

Rule #2's intent is "all request handlers validate identity; no operation proceeds without checking policy". The honest referents live on ./security:

  • RLSUserContextSchema — documented in security/rls.zod.ts as "the current user's context for RLS evaluation", i.e. the per-request security context.
  • RowLevelSecurityPolicySchema — the same file calls per-policy RLS "the live, enforced surface".

A sentence was added stating plainly that no bare Identity/Policy schema exists and that broader posture lives in the qualified schemas, so the next reader does not re-derive it.

5. Three dead source paths — implement-objectos.md "Key Files to Watch"

-- `system/manifest.zod.ts`: The "Kernel Configuration".
-- `system/identity.zod.ts`: The "Security Context".
-- `system/events.zod.ts`: The "System Bus".
+- `stack.zod.ts`: The "Kernel Configuration" (`ObjectStackDefinitionSchema`, `defineStack`).
+- `security/rls.zod.ts`: The "Security Context" (`RLSUserContextSchema`, `RowLevelSecurityPolicySchema`).
+- `kernel/events.zod.ts`: The "System Bus" (`EventSchema`; ...).

Each now serves the same watch-purpose with a file that exists (all nine paths across both prompts re-verified present). kernel/events.zod.ts is chosen over kernel/events/core.zod.ts where EventSchema is physically declared, because that barrel documents itself as the stable entrypoint and the events/* sub-modules as internal — the prompt now says so.

Baseline reconciliation

The five prompt entries are deleted in this same PR (16 → 11). The diff is deletion-only — no reformatting.

✓ check:published-readme-exports — 60 published document(s) across 77 workspace package(s);
  167 import statement(s), 47 workspace type entr(ies).
  11 known instance(s) still in the baseline; 2 of the findings are call sites.

⚠️ The gate needs the whole workspace built, not just spec — an unbuilt package is a hard error by design. Full pnpm build (71/71) precedes the run.

Reverse verification

Re-added one fixed entry to the baseline. Predicted red on a stale entry (the baseline reconciles in both directions); observed exactly that, naming the entry:

✗ check:published-readme-exports — 1 stale baseline entr(ies)
    @objectstack/spec|packages/spec/prompts/create-new-project.md|import|@objectstack/spec/data|Object
Good news, and it is still a failure: these README claims now resolve, so their
entries ... are dead text.

Removed; green again, diff back to deletion-only.

Gates

Derived from the actual changed paths via node scripts/pm/dispatch-gates.mjs, all green at 00ad76978:

check:published-readme-exports · check:nul-bytes (+ manual control-char scan) · spec check:liveness · spec check:strictness-ledger · spec check:empty-state · spec check:variant-docs · check:merge-driver · check:type-source-resolution · check-dev-prereqs · docs-audit/check-affected-docs

Plus @objectstack/spec test 409 files / 10937 tests passed, and typecheck OK.

Out of scope

Rule #3 names RequestEnvelope and ResponseEnvelope in prose; neither exists as an export (only ResponseEnvelopeConfig*). Prose symbols are the gate's structural blind spot and were explicitly kept in #9532's lane by the PM ruling, so they are filed separately rather than folded in here. #9544 is a separate card and its README files are untouched.


Generated by Claude Code

Three published agent-authoring prompts in packages/spec/prompts told agents to
import five symbols @objectstack/spec does not export. Four failed loudly. The
fifth did not: `import { Object } from '@objectstack/spec/data'` does not
resolve, so `export const AccountObject: Object = { ... }` bound to the
JavaScript global instead, and metadata authored from that prompt type-checked
against a type that constrains nothing -- false assurance handed to an
automated author.

Every substitution was measured against the built .d.ts, not guessed:

- Object -> ObjectSchema.create({ ... }), the house authoring convention (34
  uses across the example apps; zero uses of the annotation form). Making the
  form genuinely constrain exposed that the prompt's own example set
  enable.audit / enable.workflow, neither of which exists; they are now
  trackHistory / files, the pair the schema's own docstring uses.
- implement-objectql.md keeps the real Field and QuerySchema imports and
  derives the metadata type as z.infer of typeof ObjectSchema, matching
  prompts/instructions.md and spec's own src/contracts/schema-driver.ts. No
  bare Object type is exported -- it would shadow the JS global.
- ManifestSchema -> ObjectStackDefinitionSchema on the package root. The
  prompt's subject is objectstack.config.ts, which is neither /system manifest.
- IdentitySchema / PolicySchema have no bare referent anywhere in spec. Rule #2
  now names RLSUserContextSchema and RowLevelSecurityPolicySchema from
  @objectstack/spec/security.
- The three dead "Key Files to Watch" paths now point at stack.zod.ts,
  security/rls.zod.ts and kernel/events.zod.ts.

The five baseline entries are deleted in the same commit; the gate is
reconciled in both directions and green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fs18A2DdXLVN2h8PaaFBcP
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 3 changed file(s) yielded no anchor (packages/spec/prompts/create-new-project.md, packages/spec/prompts/implement-objectos.md, packages/spec/prompts/implement-objectql.md) — pages documenting those are invisible to this run

Coarse fallback — 113 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json b057e53f46e47daa337608d4c6d249a76400b91f → packageMentionDocs.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling labels Aug 18, 2026
@os-steve
os-steve marked this pull request as ready for review August 18, 2026 13:51
@os-steve
os-steve added this pull request to the merge queue Aug 18, 2026
Merged via the queue into main with commit 890b38f Aug 18, 2026
26 checks passed
@os-steve
os-steve deleted the claude/issue-9545-prompt-import-fabrications branch August 18, 2026 14:26
os-steve added a commit that referenced this pull request Aug 18, 2026
…tract, not a seed count (#9649)

The block claimed the file was "seeded from the five instances #9532
measured". It shipped with 16 entries (PR #9546) and has since shrunk to
0 through #9602, #9615 and #9581, so the sentence was wrong on day one
and the gap kept changing meaning as the ledger shrank.

Rewritten to state the contract rather than a number: what an entry is
(one known instance still awaiting repair, debt not exemption), that the
count is read from `entries` and never asserted in prose, that
`entries: []` is the success state rather than a corrupt or deletable
file, and that absence from the file means measured-and-clean rather
than unscanned -- which is what the plugin-audit negative control now
says for every package.

No baseline entry is added, removed or edited.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 23, 2026
…tract, not a seed count (objectstack-ai#9649) (objectstack-ai#9764)

The block claimed the file was "seeded from the five instances objectstack-ai#9532
measured". It shipped with 16 entries (PR objectstack-ai#9546) and has since shrunk to
0 through objectstack-ai#9602, objectstack-ai#9615 and objectstack-ai#9581, so the sentence was wrong on day one
and the gap kept changing meaning as the ledger shrank.

Rewritten to state the contract rather than a number: what an entry is
(one known instance still awaiting repair, debt not exemption), that the
count is read from `entries` and never asserted in prose, that
`entries: []` is the success state rather than a corrupt or deletable
file, and that absence from the file means measured-and-clean rather
than unscanned -- which is what the plugin-audit negative control now
says for every package.

No baseline entry is added, removed or edited.


Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling

Projects

None yet

2 participants