feat(schedules): register agent_schedule in authz on create/delete#259
Merged
Conversation
rpatel-scale
approved these changes
Jun 2, 2026
Register an agent_schedule resource (with the parent_agent edge) in the authorization service before the Temporal create, and deregister it on delete. The register/deregister calls live in ScheduleService, alongside the Temporal write, so the compensation boundary can be scoped to the Temporal create only: registration is unconditional and fail-closed; a Temporal create failure after a successful register triggers a compensating deregister so no orphan tuple is left behind. A post-create read-back (describe) failure does NOT compensate, since the schedule was actually created. Deregister on delete is best-effort. Registration is skipped with a warning when no creator identity is resolvable on the principal context (agent-bypass / internal paths). Mirrors the agent_api_key register/deregister pattern. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2b22285 to
a478b29
Compare
asherfink
added a commit
that referenced
this pull request
Jun 3, 2026
…strings (#268) Scrubs internal references (Linear ticket IDs, internal service names, internal flag names) from comments and docstrings across already-landed authorization code. Fixes issues introduced by #248, #252, #255, #257, #259, and #260. No behavior changes. <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR is a pure housekeeping change that scrubs internal references — Linear ticket IDs (e.g. `AGX1-263`, `AGX1-290`), internal service names (`SpiceDB`, `Spark`, `agentex-auth`), and internal flag/PR references — from comments and docstrings across 13 authorization-related source and test files. No executable code is touched. - Comments and docstrings across `port.py`, `authorization_service.py`, `agent_api_keys.py`, and related utilities have internal service names replaced with generic terms like \"authorization service\" or \"authorization schema\". - TODO comments in `agent_api_key_authorization.py`, `agent_authorization.py`, and `authorization_shortcuts.py` have internal ticket IDs stripped, with file-level context substituted where the ticket reference previously identified the work location. - Module docstrings in all six affected test files have ticket-prefixed deliverable descriptions replaced with plain behavioral descriptions. <details><summary><h3>Confidence Score: 5/5</h3></summary> All changes are confined to comments and docstrings; no executable code is modified. Every diff hunk touches only comment text, docstrings, or module-level strings. There are no logic, control-flow, or interface changes anywhere in the PR, making regression risk essentially zero. No files require special attention. </details> <h3>Important Files Changed</h3> | Filename | Overview | |----------|----------| | agentex/src/adapters/authorization/port.py | Docstring-only: replaced "SpiceDB" with "authorization" in register_resource docstring. | | agentex/src/api/routes/agent_api_keys.py | Comment-only: removed "SpiceDB" references from two inline comments in create/delete route handlers. | | agentex/src/domain/services/authorization_service.py | Docstring-only: replaced "SpiceDB schema" with "authorization schema" in register_resource docstring. | | agentex/src/domain/use_cases/agent_api_keys_use_case.py | Comment-only: removed unconditional-routing comment referencing internal service names and ticket numbers; also cleaned docstring referencing internal routing logic. | | agentex/src/utils/agent_api_key_authorization.py | Comment-only: replaced TODO(AGX1-290) with plain TODO, removing the Linear ticket reference. | | agentex/src/utils/agent_authorization.py | Comment-only: replaced TODO(AGX1-290) with plain TODO, removing the Linear ticket reference. | | agentex/src/utils/authorization_shortcuts.py | Comment-only: updated TODO to reference the file name instead of internal ticket/PR numbers. | | agentex/tests/integration/services/test_agent_api_key_service_dual_write.py | Docstring-only: removed references to "Spark AuthZ", internal ticket numbers, and internal service names throughout module docstring and inline comments. | | agentex/tests/integration/services/test_schedule_service_dual_write.py | Docstring-only: removed "Spark AuthZ" and "SpiceDB" references across the module docstring and inline comments. | | agentex/tests/integration/api/checkpoints/test_checkpoints_authz_api.py | Docstring-only: removed AGX1-302 ticket reference and internal terminology from module docstring. | | agentex/tests/integration/api/events/test_events_authz_api.py | Docstring-only: replaced "AGX1-244" ticket reference in module docstring with a plain description. | | agentex/tests/integration/api/messages/test_messages_authz_api.py | Docstring-only: removed AGX1-277 ticket reference from module docstring. | | agentex/tests/unit/api/test_agent_api_keys_authz.py | Docstring-only: removed AGX1-263 ticket reference and "Spark AuthZ" mention from module docstring. | </details> <details><summary><h3>Flowchart</h3></summary> ```mermaid %%{init: {'theme': 'neutral'}}%% flowchart TD A["PR #268: Scrub internal references"] --> B["Source files\n(port.py, agent_api_keys.py,\nauthorization_service.py,\nagent_api_keys_use_case.py,\n3 utils files)"] A --> C["Test files\n(6 integration + unit tests)"] B --> D["Replace: SpiceDB → authorization service/schema\nRemove: agentex-auth, Spark, internal PR refs\nStrip: AGX1-xxx ticket IDs from TODOs"] C --> E["Replace: AGX1-xxx deliverable labels → plain descriptions\nRemove: Spark AuthZ, scaleapi/agentex#353, etc."] D --> F["No executable code changed"] E --> F ``` </details> <sub>Reviews (2): Last reviewed commit: ["chore(authz): scrub internal project ref..."](62581f7) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=35250665)</sub> <!-- /greptile_comment -->
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.
Summary
Registers an
agent_scheduleresource in the authorization service on schedulecreate (with the owning agent as its parent edge) and deregisters it on delete,
so schedules participate in fine-grained authorization.
Schedules have no Postgres row — Temporal is the store and the authz selector is
the schedule id (
{agent_id}--{schedule_name}). The register/deregister callstherefore live in
ScheduleService, alongside the Temporal write, so thecompensation boundary can be scoped to the Temporal create only.
Behavior
parent=agent) before theTemporal create. Unconditional and fail-closed — a register failure
aborts the create. If the Temporal create fails after a successful register,
a compensating deregister removes the tuple so no orphan is left. The
post-create read-back (describe) is outside the compensation scope: a describe
failure must not deregister a schedule that was actually created.
never blocks the delete).
nor service-account identity): registration is skipped with a warning and the
schedule is still created. Interim behavior until on-behalf-of-user identity
is threaded.
Per-account routing (which authz backend the call lands on) is owned by the
authorization service; this repo calls register/deregister unconditionally.
Follows the
agent_api_keyregister/deregister pattern (#248), which this PR isstacked on.
Requires
A companion change in the authorization service mapping the
scheduleresourcetype to its SpiceDB
agent_scheduledefinition; without it theregister/deregister call is rejected before reaching the backend.
Changes
schedule_service.py: register-before-create + compensate-on-temporal-failure;best-effort deregister on delete;
_register_schedule_in_auth/_deregister_schedule_from_authhelpers; inject the authorization service.authorization_types.py: add thescheduleresource type + factory.tests/integration/services/test_schedule_service_dual_write.py: 7 cases(parent-edge contract, fail-closed create, compensating deregister on Temporal
failure, read-back failure does NOT compensate, best-effort deregister on
delete, no-creator skip).
tests/unit/services/test_schedule_service.py: fixture updated for the newconstructor arg.
Test plan
pytest tests/integration/services/test_schedule_service_dual_write.py— 7/7pytest tests/unit/services/test_schedule_service.py— 27/27pytest tests/unit/use_cases/test_schedules_use_case.py— 18/18Stacked on
#248 (base branch). Rebase onto the default branch once #248 merges.
🤖 Generated with Claude Code
Greptile Summary
This PR integrates
agent_scheduleresources into the authorization service on create and delete, following the existingagent_api_keypattern. Register-before-create is fail-closed with a compensating deregister on Temporal failure; delete is best-effort post-Temporal.schedule_service.py: InjectsDAuthorizationService, adds_register_schedule_in_auth(fail-closed, returns bool to drive compensation) and_deregister_schedule_from_auth(best-effort, never propagates). The post-create read-back is intentionally outside the compensation scope, which is correctly verified by a dedicated test case.authorization_types.py: Adds thescheduleenum value and corresponding factory methods to bothAgentexResourceandAgentexResourceOptionalSelector, matching all existing resource types.Confidence Score: 5/5
Safe to merge; the compensation and bypass logic are correct and all edge cases are covered by the new test suite.
The register-before-create / compensate-on-Temporal-failure contract is correctly implemented: orphan tuples are prevented on create failures, and delete is best-effort so auth never blocks a Temporal delete. The read-back exclusion from compensation scope is explicitly tested. The no-creator skip and auth-bypass paths are both handled without leaving orphan state. The 7-case integration test suite directly mirrors the documented behavior contracts.
No files require special attention.
Important Files Changed
scheduletoAgentexResourceTypeenum and factory methods to bothAgentexResourceandAgentexResourceOptionalSelector; directly mirrors theapi_keypattern, no issues.DAuthorizationService, adds register/deregister helpers with correct fail-closed/best-effort semantics, and scopes compensation to the Temporal create only (read-back is outside scope); logic is sound.authorization_serviceconstructor argument; no behavioral changes to the existing 27 unit tests.Sequence Diagram
sequenceDiagram participant Caller participant ScheduleService participant AuthzService participant Temporal Note over ScheduleService: create_schedule() ScheduleService->>AuthzService: "register_resource(schedule, parent=agent)" alt register fails AuthzService-->>ScheduleService: raises Exception ScheduleService-->>Caller: raises (fail-closed, Temporal never called) else "register succeeds (registered=True)" AuthzService-->>ScheduleService: None ScheduleService->>Temporal: create_schedule(schedule_id) alt Temporal create fails Temporal-->>ScheduleService: raises Exception ScheduleService->>AuthzService: deregister_resource(schedule) [compensate] Note over ScheduleService: best-effort, swallows errors ScheduleService-->>Caller: re-raises original exception else Temporal create succeeds Temporal-->>ScheduleService: None ScheduleService->>Temporal: describe_schedule (read-back) Note right of ScheduleService: Outside compensation scope ScheduleService-->>Caller: ScheduleResponse end else no creator identity Note over ScheduleService: Skips register (warning logged) ScheduleService->>Temporal: create_schedule(schedule_id) Temporal-->>ScheduleService: None ScheduleService-->>Caller: ScheduleResponse end Note over ScheduleService: delete_schedule() ScheduleService->>Temporal: delete_schedule(schedule_id) Temporal-->>ScheduleService: None ScheduleService->>AuthzService: deregister_resource(schedule) [best-effort] ScheduleService-->>Caller: NoneReviews (2): Last reviewed commit: "feat(schedules): register agent_schedule..." | Re-trigger Greptile