Skip to content

feat!: retire the legacy org rules API, expose the policy reflections - #54

Merged
guyb1 merged 2 commits into
mainfrom
feat/retire-legacy-rules
Jul 25, 2026
Merged

feat!: retire the legacy org rules API, expose the policy reflections#54
guyb1 merged 2 commits into
mainfrom
feat/retire-legacy-rules

Conversation

@guyb1

@guyb1 guyb1 commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Companion follow-up to onecli-cloud#709 (policy step 10 — "drop the old policy/equipment model"). /v1/org/rules now answers 410 Gone, so these methods can only throw. Shipping them would be worse than removing them.

Breaking — release-please cuts 3.0.0.

Removed

org.listRules, org.getRule, org.createRule, org.updateRule, org.deleteRule, and the OrgRule, OrgRuleAction, OrgRuleMethod, OrgRuleRateLimitWindow, OrgRuleCondition, CreateOrgRuleInput, UpdateOrgRuleInput types.

Authoring moved to the *PolicyRule family, available since 2.8.0.

Added — so nothing goes with them

The read-only policy reflections. They report what the published policy allows, not a stored assignment list:

method endpoint
org.getEffectiveAppPermissions({ provider }) /v1/org/policy/effective-app-permissions
getEffectiveAppPermissions({ provider, agentId? }) /v1/policy/effective-app-permissions
getEffectiveCredentials(agentId) /v1/agents/{id}/effective-credentials
getConnectionAgentAccess(connectionId) /v1/connections/{id}/effective-agents
listAppPermissionDefinitions() /v1/apps/permission-definitions

The difference is load-bearing: the old reads returned stored assignments, so a credential granted by a policy rule was invisible to them. The reflections resolve the rules, so it is not.

Review

Walked through typescript-pro and typescript-advanced-types — no any, no enums, discriminated unions for the credential and provenance shapes, declaration files generated, strict mode. Three fixes:

  • The EffectiveToolVerdict union was wrong against the API: it is allow | approval | block | mixed | unmanaged, and rateLimited does not exist — a rate limit rides rateLimit/rateLimitWindow on an allow. Anyone narrowing on the shipped values would have matched nothing.
  • The reflection set was asymmetric — the CLI could read project-scope app permissions, the SDK only org. Both scopes now exist, sharing one type: the shape is identical either side and basis.scope reports which produced it, so OrgEffectiveAppPermissions was the wrong name.
  • A test reused a single Response, whose body reads once — a second call threw "Body has already been read" instead of exercising the code.

Verification

tsc, tsup build, 108 vitest. Tests pin the request paths (a wrong one silently returns another resource's shape), url-encoding, error mapping, and the absence of the removed methods and types from the public surface.

Related

Land last, after the docs and CLI PRs.

🤖 Generated with Claude Code

guyb1 added 2 commits July 24, 2026 21:03
The cloud API deleted the old policy/equipment surface; `/v1/org/rules` now
answers 410 Gone. Methods that can only throw are worse than no methods, so
they are removed rather than deprecated.

REMOVED from `org`: listRules, getRule, createRule, updateRule, deleteRule —
and the OrgRule, OrgRuleAction, OrgRuleMethod, OrgRuleRateLimitWindow,
OrgRuleCondition, CreateOrgRuleInput and UpdateOrgRuleInput types. Authoring
moved to the *PolicyRule family, which has been available since 2.8.0.

ADDED, so nothing is lost with them — the read-only policy reflections, which
report what the PUBLISHED policy actually allows rather than a stored
assignment list:

- org.getEffectiveAppPermissions({ provider })  replaces the retired
  /v1/org/rules/permissions/:provider
- getEffectiveCredentials(agentId)              replaces GET /v1/agents/:id/secrets
                                                and /connections
- getConnectionAgentAccess(connectionId)        replaces GET /v1/connections/:id/agents
- listAppPermissionDefinitions()                the tool catalog an app-target
                                                rule can name

The difference matters: the old reads returned stored assignments, so a
credential granted by a policy rule was invisible to them. The reflections
resolve the rules, so it is not.

Tests pin the request paths (a wrong one silently returns another resource's
shape), url-encoding, error mapping, and the absence of the removed methods and
types from the public surface.

BREAKING CHANGE: org.listRules, org.getRule, org.createRule, org.updateRule and
org.deleteRule are removed along with the OrgRule* and *OrgRuleInput types. Use
the *PolicyRule methods to author rules and org.getEffectiveAppPermissions to
read what they permit.
Three findings from review.

The verdict union was wrong against the cloud's own type: it is
allow | approval | block | mixed | unmanaged, not allowed/blocked/needsApproval,
and rateLimited does not exist — a rate limit rides rateLimit/rateLimitWindow on
an allow. Anyone narrowing on the shipped values would have matched nothing.

The reflection set was asymmetric: the CLI could read PROJECT app permissions
and the SDK only the org scope. getEffectiveAppPermissions now exists on both,
sharing one type — the shape is identical at either scope and basis.scope
reports which produced it, so OrgEffectiveAppPermissions was the wrong name and
is now just EffectiveAppPermissions.

The test mock reused a single Response, whose body can only be read once; a
second call in one test threw "Body has already been read" rather than
exercising the code. It mints a fresh Response per call now.
@guyb1
guyb1 merged commit 25596d7 into main Jul 25, 2026
3 checks passed
@guyb1
guyb1 deleted the feat/retire-legacy-rules branch July 25, 2026 04:41
@guyb1 guyb1 mentioned this pull request Jul 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant