Skip to content

feat(spec): cover all three fold-and-drop aliases, not just execute (#3743 follow-up) - #3854

Merged
os-zhuang merged 1 commit into
mainfrom
claude/action-target-alias-discard-ahpgl5
Jul 28, 2026
Merged

feat(spec): cover all three fold-and-drop aliases, not just execute (#3743 follow-up)#3854
os-zhuang merged 1 commit into
mainfrom
claude/action-target-alias-discard-ahpgl5

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Follow-up to #3838 (closed #3743).

Why

#3838 introduced lintDeprecatedAliases — the pre-parse pass that reports an alias the parse itself is about to consume — with exactly one rule, for action.execute. #3743 predicted the pass would earn its keep beyond that rule:

a pre-parse hook is the only place any "author wrote a deprecated alias that parse will consume" warning can live, so the pass is likely to earn its keep beyond this one rule.

It does. execute was never special. Grepping the spec for fold-and-drop transforms returns exactly three, and the other two are structurally identical:

Transform Alias → canonical Drop condition
ui/action.zod.ts executetarget (#3713/#3742) if (execute && !rest.target)covered by #3838
data/field.zod.ts conditionalRequiredrequiredWhen (#3754/#3764) if (conditionalRequired && !rest.requiredWhen)
ai/agent.zod.ts knowledge.topicsknowledge.sources (#1878/#1891) if (rest.sources === undefined && topics !== undefined)

All three: canonical wins, alias vanishes from the parsed output. Declare both slots with different values and one is discarded with no signal — and no downstream check can report it, because the parse already erased the evidence. That is the whole reason the pass exists as a rule set rather than one hard-coded rule.

What's added

Two rules, same shape, same advisory severity, same two surfaces (defineStack at authoring time; os build / os validate for stacks that skip strict defineStack). Neither fails the build; both name the two values and give the one-line fix.

field-requiredwhen-conditionalrequired-conflict — the discarded predicate never gates the field.

  • Covers fields on objects and on object extensions (same FieldSchema, same transform, same silent discard).
  • Compares the predicate text, so 'record.paid' and { dialect: 'cel', source: 'record.paid' } are recognised as the same predicate and stay quiet — the envelope is what a bare string lowers into, so neither form loses anything.

agent-knowledge-sources-topics-conflict — the discarded list names RAG sources the agent never recruits from.

  • Compares by set: sources: ['a','b'] beside topics: ['b','a'] recruits the same context, so nothing is lost and the rule stays quiet. Order and repetition are not meaningful for either slot.

The three findings now share one builder, so they phrase the common half identically (which slot wins, that the alias is dropped, delete it) while each still names what its own discarded value would have done.

A docs bug found on the way

content/docs/ai/agents.mdx documented knowledge as { topics: string[], indexes: string[] } and used topics: in all three code examples — teaching the deprecated alias as if it were the canonical key, and contradicting skills/objectstack-ai/SKILL.md, which already said "sources (canonical; topics is a deprecated alias)". The human docs and the AI-authoring skill disagreed, and the human docs were the wrong one. Examples now use sources; the field table names both and says which is which.

content/docs/data-modeling/fields.mdx gains the same warning note #3838 added to the objectui actions reference.

Changes

  • packages/spec/src/shared/deprecated-aliases.ts — two rules + the shared finding builder and the expression/list helpers they need
  • packages/spec/src/index.ts, packages/spec/api-surface.json — two new rule-id exports (0 breaking, 2 added)
  • content/docs/ai/agents.mdx, content/docs/data-modeling/fields.mdx
  • changeset

No CLI change: both call sites already loop over whatever the pass returns, so the new rules reach os build / os validate and defineStack with no rewiring — which is what "severity modelled from day one" bought.

Testing

  • packages/spec/src/shared/deprecated-aliases.test.ts — 26 cases (was 13). New: both-declared for each rule, one-slot-only, envelope-vs-string equivalence, envelope on both slots, object extensions, set-equality in any order, empty list treated as undeclared, missing knowledge block, all three rules firing independently in one pass, and a canonical-keys-only stack returning nothing.
  • packages/spec/src/stack.test.ts — a field alias surfaces through defineStack on the same terms as an action alias, proving the wiring is rule-agnostic and not action-shaped, and the parse still drops the alias.
  • @objectstack/spec 6796 passed · @objectstack/cli 738 passed · all example apps typecheck and pass.
  • Every ESLint-job and typecheck-job gate run locally: lint, check:nul-bytes, check:doc-authoring, check:role-word, check:org-identifier, check:authz-resolver, check:release-notes, spec tsc --noEmit, check:docs, check:skill-refs, check:react-blocks, check:api-surface, check:liveness, examples typecheck, downstream-contract typecheck.

All three rules against the built spec:

• action 'convert' on object 'crm_task'
    Action declares both 'target' ('preferredHandler') and the deprecated alias 'execute'
    ('legacyHandler'). 'target' wins: … so 'legacyHandler' never runs.
    rule: action-target-execute-conflict

• field 'due_date' on object 'crm_task'
    Field declares both 'requiredWhen' (`record.stage == "closed"`) and the deprecated alias
    'conditionalRequired' (`record.amount > 0`). 'requiredWhen' wins: … so `record.amount > 0`
    never gates the field.
    rule: field-requiredwhen-conditionalrequired-conflict

• agent 'support_bot' knowledge
    Agent knowledge declares both 'sources' (['faq', 'policies']) and the deprecated alias
    'topics' (['legacy_kb']). 'sources' wins: … so ['legacy_kb'] is never recruited into RAG context.
    rule: agent-knowledge-sources-topics-conflict

Generated by Claude Code

…#3743 follow-up)

#3838 introduced `lintDeprecatedAliases` — the pre-parse pass that reports an
alias the parse itself is about to consume — with one rule, for
`action.execute`. #3743 predicted the pass would earn its keep beyond that rule.
It does: `execute` was never special. The spec has exactly THREE transforms that
fold an alias into its canonical key and then drop it from the parsed output,
and all three share one failure mode — declare both slots with different values
and one is discarded with no signal, invisible to every downstream check because
the parse already erased the evidence.

Two more rules, same shape, same advisory severity, same two surfaces:

  field-requiredwhen-conditionalrequired-conflict — `FieldSchema` folds
  `conditionalRequired` into `requiredWhen` (#3754/#3764); the discarded
  predicate never gates the field. Covers fields on objects AND on object
  extensions, and compares the predicate TEXT so a bare string and the
  `{ dialect, source }` envelope it lowers into read as the same predicate.

  agent-knowledge-sources-topics-conflict — `AIKnowledgeSchema` folds
  `knowledge.topics` into `knowledge.sources` (#1878/#1891); the discarded list
  names RAG sources the agent never recruits from. Compares by SET, so the same
  sources in a different order stay quiet.

The three findings now share one builder, so they phrase the common half
identically (which slot wins, that the alias is dropped, delete it) while each
still names what its own discarded value would have done.

Also corrects `content/docs/ai/agents.mdx`, which documented `knowledge` as
`{ topics, indexes }` and used `topics` in all three examples — teaching the
deprecated alias as the canonical key, and contradicting
`skills/objectstack-ai/SKILL.md`, which already had it right.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqC9ckaLbmhmytt65Gxi96
@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 28, 2026 11:53am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

104 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review July 28, 2026 12:07
@os-zhuang
os-zhuang merged commit f35cdc5 into main Jul 28, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/action-target-alias-discard-ahpgl5 branch July 28, 2026 12:07
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 tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P3] action target + execute both declared: the alias is discarded silently — no author-facing warning

2 participants