feat(entity-client): publish the attribute-type allowlists - #469
Merged
Conversation
RELATION_ATTRIBUTE_TYPES and OVERRIDABLE_ATTRIBUTE_TYPES are hand-copied in entity-api, epilot360-entity-builder, epilot360-entity-builder-v2 and epilot360-integration-hub, kept in sync by a comment. Publishing them from entity-client — and so through @epilot/sdk/entity, via the schema-model.ts mechanism — lets those four copies become an import. Both are ReadonlySet<string> so a caller can test an unvalidated attribute.type without narrowing first, which is how every consumer uses them today. The literal-typed arrays are exported alongside, and both are `satisfies readonly AttributeType[]` against the union of every type the Attribute variants declare, so a member the spec does not have is a compile error. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXhwfYV419Bodt42E3aw2C
Conditional pricing is the only consumer of the `overridable_attribute` type allowlist, so it ships from pricing-client rather than entity-client. RELATION_ATTRIBUTE_TYPES stays in entity-client: three of its four call sites are unrelated to pricing (edit_mode_config in both entity builders, relation mapping validation in integration-hub), and moving it would make those repos depend on pricing-client. The moved list loses its compile-time anchor — it holds entity types, and pricing-client has no entity schema to `satisfies` against and must not import one, since @epilot/sdk depends on no client package. models.test.ts checks both lists against the entity spec instead, which covers it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXhwfYV419Bodt42E3aw2C
Cut the block comments to one line each. The rationale for where the lists live and why the pricing one has no compile-time anchor belongs in the PR, not repeated above every export. Comments only; no behaviour change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXhwfYV419Bodt42E3aw2C
josecarneiro
pushed a commit
that referenced
this pull request
Sep 1, 2026
The auto-release gate only fired on `clients/*/openapi.json`, so a change to the SDK's own runtime (proxy.ts, retry.ts, client-factory.ts), to a hand-written client module (schema-model.ts, additional-types.ts), or to the generator itself never published. #464 and #469 both landed on main unpublished and only shipped when an unrelated spec PR swept them up. The gate now matches those paths too, and excludes the generated output under src/{apis,types,models,definitions,docs} — the auto-release commit rewrites those itself, so matching them would make it re-trigger this job and bump versions in a loop. The release commit also now carries `[skip release]`, which the job's own `if` already honours, as a second layer in case the exclusion is later widened carelessly. Verified the new expression against real commit file lists: a spec change, #464, #469, an SDK runtime fix and a generator change all trigger; the auto-release commit 5e39d9c, docs-only, changeset-only and generated-output-only changes all do not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXhwfYV419Bodt42E3aw2C
2 tasks
josecarneiro
pushed a commit
that referenced
this pull request
Sep 1, 2026
Merging main brought in the environments spec reshape (#463), the entity/pricing attribute-type allowlists (#469) and the @epilot/cli@0.1.142 release. That release again staged only packages/cli/package.json, so main had definitions/environments.json stale against the spec it is copied from and src/index.ts reporting 0.1.51 against a package.json at 0.1.142. Regenerating brings both in line: all 51 definitions match their client specs and src/index.ts reports 0.1.142. Third occurrence of this drift since the PR was opened — once per release. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MhYbcfHQfe7bk2R37P75zV
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
RELATION_ATTRIBUTE_TYPESand theoverridable_attributetype allowlist are currently hand-copied across four repos, kept in sync by a comment:entity-api.../schema/schema-attribute-service.ts:11,:98epilot360-entity-builder.../custom-configs/editModeUtils.ts:13epilot360-entity-buildersrc/entity-builder/conditions/conditions-config.ts:321epilot360-entity-builder-v2src/lib/edit-mode.ts:33epilot360-integration-hub.../config-schema-validation.ts:41conditions-config.tseven carries a comment saying it was "verified member-for-member" againstentity-api— a manual sync contract, which is the thing worth deleting.This is the follow-up to #464, which built the mechanism (a client's
schema-model.tsreaching consumers as real runtime values) but didn't yet use it for the constants that motivated it.What changed
clients/entity-client/src/schema-model.tsgains:These reach consumers as
@epilot/entity-clientexports and, through thecopyModelsgenerator step from #464, as@epilot/sdk/entityexports too.Three decisions worth a look
ReadonlySet<string>, notReadonlySet<AttributeType>. Every consumer calls.has(attribute.type)on an unnarrowedstring. ASet<AttributeType>would reject that and force a cast at all four call sites; typing the set asstringkeeps the migration import-only. The type safety lives on the arrays instead, where it costs nothing.The anchor is
Attribute['type'], not a hand-written union.Attributeis ananyOfof 35 variants, so that resolves to every type literal the spec declares (39 in total). Verified the guard bites — adding'not_a_real_type'fails withTS1360.Renamed
VARIANT_OVERRIDABLE_ATTRIBUTE_TYPES→OVERRIDABLE_ATTRIBUTE_TYPES, matching theoverridable_attributeflag it gates and whatepilot360-entity-builderalready calls its own copy. Easy to revert if you'd rather keep the old name.Test plan
pnpm build(tsup + dts) clean across all 51 APIsmodels.test.ts(2 new)satisfies. It also pinsordered_listas declared-but-not-overridable, the case that made an allowlist necessary rather than a denylistdist/apis/entity.cjsexports both sets with the right members, andRelationAffinityModestill resolvesTS1360; the spec-parsing test is non-vacuous (39 types found, rejects a fake one)pnpm lintclean (524 files)Note on releasing
This touches no
clients/*/openapi.json, so the spec-gatedauto-releasejob will not fire — the same thing that happened to #464, which only shipped when an unrelated spec PR swept it up ~90 minutes later. It needs the changesets flow (pnpm version-packages && publish-packages) or a manual@epilot/sdk@x.y.ztag. A changeset is included.Worth considering separately whether the gate should also trip on
clients/*/src/schema-model.tsandpackages/epilot-sdk-v2/src/**.Follow-up (not in this PR)
Once released, delete the four copies.
entity-apiis the awkward one — it has no@epilot/sdkdependency and is pinned to@epilot/entity-client@^4.25.1while 7.4.0 is published, so it needs either a new dep or a three-major bump. Those are GitLab repos, so they'll be MRs rather than PRs.Generated by Claude Code