Skip to content

feat: add workspace governance resources (workspace states, workspace workflows, type governance) - #66

Open
sunder-ch wants to merge 4 commits into
mainfrom
feat/workspace-governance
Open

feat: add workspace governance resources (workspace states, workspace workflows, type governance)#66
sunder-ch wants to merge 4 commits into
mainfrom
feat/workspace-governance

Conversation

@sunder-ch

@sunder-ch sunder-ch commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Description

Plane's workspace governance (makeplane/plane-ee#8288) moves states, workflows, and work item type governance from per-project scope to a workspace-level catalog, with per-workspace dual-mode opt-in. This PR adds full SDK coverage for the new public API surface, plus parity housekeeping for the existing project workflows resource.

New resources

client.workspace_states/workspaces/{slug}/states/

  • list / retrievedual-mode: the workspace states catalog when governed, the cross-project aggregate otherwise; retrieve_by_external_id supported
  • create / update / delete — governed workspaces only (400 workspace_not_managed otherwise); catalog rules typed into the DTOs (5 lifecycle groups only, no default flag, workspace-unique names → 409 state_name_in_use)
  • Not to be confused with workspace_project_states (project lifecycle states)

client.workspace_workflows/workspaces/{slug}/workflows/...

  • Catalog CRUD (paginated list with search/is_active/sort params), usage report, activities log
  • .states: add / update / remove (orphan-gated, with state_mapping) / mark_default
  • .transitions: full CRUD (with approvers via member_ids, required_approvals)
  • .hooks: transition hooks CRUD + regenerate_secret + executions

client.work_item_type_governance/workspaces/{slug}/work-item-types/{type_id}/governance/...

  • retrieve / update governance mode (any / constrained / required), with preview dry-runs
  • Pins: list_pins / create_pins / delete_pin
  • Project-side: list_project_type_workflows, retrieve_project_type_workflow, get_project_pick / set_project_pick, preview_project_workflow_fallback

Model updates

  • WorkspaceFeature: typed work_item_types, releases, and read-only states_owned_by_workspace — the governed-mode detection flag for API clients (GET /workspaces/{slug}/features/)
  • New CatalogGroupEnum (the 5 lifecycle groups), workspace state DTOs, workspace workflow models, governance models, shared workflow hook/activity/state-membership models

Housekeeping — project workflows parity

The existing client.workflows resource lagged the public API; added: retrieve, delete, activities, submit_work_item_approval, .states.list / .states.update / .states.transfer, .transitions.retrieve, and the .hooks sub-resource.

Notes

  • Version bumped to 0.2.22
  • Unit tests are governance-aware: dual-mode reads assert in every workspace; catalog writes run only when states_owned_by_workspace is true (skip otherwise), and the ungoverned rejection contract (400) is asserted explicitly
  • ruff and black clean on all changed files; the three new mypy valid-type notes on list-named methods match the pre-existing pattern in the repo (milestones/cycles/modules)

Summary by CodeRabbit

  • New Features
    • Added workspace catalog state APIs (create/retrieve/update/delete, lookup by external ID).
    • Added workspace workflow catalog management plus state-chain, transitions, activities, and transition hooks APIs.
    • Added project workflow transition hooks and typed workflow approval handling.
    • Added work item type governance enhancements, including workflow pin management and typed project workflow pick results.
  • Bug Fixes / Improvements
    • Standardized request routing to be tolerant of trailing-slash differences and improved response typing/validation.
    • Added workspace/workflow state and transition retrieval/update/transfer capabilities.
  • Documentation
    • Updated README workflow and governance examples to match the latest API naming.
  • Chores
    • Bumped package version to 0.2.22.

… workflows, type governance)

Plane's workspace governance moves states, workflows, and work item type
governance to a workspace-level catalog (dual-mode, per-workspace opt-in).
This adds the corresponding public-API coverage:

- workspace_states: list/retrieve (dual-mode reads), create/update/delete
  against the workspace states catalog, plus external-id lookup
- workspace_workflows: catalog CRUD, usage report, activity log, chain
  state management (add/update/remove/mark-default), state transitions,
  and transition hooks (incl. webhook secret regeneration and executions)
- work_item_type_governance: governance mode (any/constrained/required),
  change previews, project pins, project-side effective workflows and
  workflow picks, and workflow fallback previews
- WorkspaceFeature: type the new work_item_types, releases, and read-only
  states_owned_by_workspace fields (the governed-mode detection flag)

Project workflows parity housekeeping: retrieve/delete/activities, chain
state list/update/transfer, transition retrieve, transition hooks, and
work item workflow approval.
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The SDK adds typed workspace state and workflow clients, project workflow governance APIs, workflow hooks and approval models, expanded public exports, updated examples, smoke tests, and version 0.2.22.

Changes

Workspace workflow governance

Layer / File(s) Summary
Governance and workflow contracts
plane/models/*
Adds typed models for catalog states, workflow chains, transitions, activities, hooks, approvals, governance settings, pins, workflow picks, and fallback previews.
Workspace state and workflow API flow
plane/api/workspace_states.py, plane/api/workspace_workflows/*, plane/client/plane_client.py
Adds workspace state and workflow CRUD, state-chain, transition, hook, usage, and activity operations, then exposes them through PlaneClient.
Project workflow governance and workflow extensions
plane/api/work_item_type_governance/*, plane/api/workflows/*
Adds governance retrieval, updates, previews, pins, project workflow picks, transition hooks, workflow activities, state operations, transition retrieval, and typed approval submission.
Public exports and release validation
plane/__init__.py, README.md, tests/unit/*, pyproject.toml
Updates exports and examples, adds governance-gated smoke tests, and bumps the package version to 0.2.22.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: prashant-surya

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding workspace governance resources for states, workflows, and type governance.
Docstring Coverage ✅ Passed Docstring coverage is 83.54% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/workspace-governance

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plane/__init__.py`:
- Around line 86-97: Update the package-root workspace workflow exports around
the existing models import and __all__ definitions to include
WorkspaceWorkflowState, WorkspaceWorkflowTransition, and
WorkspaceWorkflowUsageProject. Preserve the current exports and ensure all three
response models are importable from the package root.

In `@plane/api/work_item_type_governance.py`:
- Around line 150-188: Rename get_project_pick to retrieve_project_pick and
set_project_pick to update_project_pick in the governance API class, preserving
their existing parameters, request behavior, return values, and documentation.
Update all references to the old method names so callers use the CRUD-standard
names.
- Around line 33-205: Append a trailing slash to every endpoint URL constructed
in the resource methods retrieve, update, preview, list_pins, create_pins,
delete_pin, list_project_type_workflows, retrieve_project_type_workflow,
get_project_pick, set_project_pick, and preview_project_workflow_fallback,
including URLs with path parameters. Preserve each existing endpoint path and
request method while ensuring all generated URLs end with “/”.

In `@plane/api/workflows/base.py`:
- Around line 131-146: The submit_work_item_approval method must follow the DTO
and response-validation contract used by the other write methods. Update its
action parameter to accept the appropriate Pydantic request DTO, serialize it
with model_dump(exclude_none=True) for _post, and validate the response with the
corresponding response model’s model_validate method; preserve the existing
workflow-approval endpoint.

In `@plane/api/workflows/hooks.py`:
- Around line 17-132: Update the hooks URL construction in _base to return an
endpoint ending with a trailing slash, and update retrieve, update, and delete
to append hook_id with a trailing slash as well. Ensure list and create use the
corrected collection URL while preserving the existing method behavior.

In `@plane/api/workspace_states.py`:
- Around line 40-110: Update all endpoint paths in workspace_states.py methods
retrieve_by_external_id, create, retrieve, update, and delete to include a
trailing slash for both collection and item routes; apply the same
trailing-slash convention to every collection and item endpoint in
workspace_workflows/transitions.py lines 24-93. Preserve the existing request
methods and parameters.
- Around line 43-57: Remove the public retrieve_by_external_id method and fold
its lookup into the CRUD-only API by extending retrieve with mutually exclusive
external_id and external_source parameters, or by supporting the same filters
through list(..., params=...). Preserve the existing endpoint and State
validation behavior while ensuring callers use only the established CRUD method
names.

In `@plane/api/workspace_workflows/base.py`:
- Around line 46-122: Normalize every workspace-workflow API route to end with
exactly one trailing slash: update list, CRUD, usage, and activities paths in
plane/api/workspace_workflows/base.py (lines 46-122),
add/update/remove/mark-default paths in plane/api/workspace_workflows/states.py
(lines 41-110), and the hooks collection plus every hook child or operation path
in plane/api/workspace_workflows/hooks.py (lines 19-149). Ensure child routes do
not introduce double slashes.

In `@plane/models/enums.py`:
- Around line 20-26: Update the module’s __all__ declaration to include the
public CatalogGroupEnum alias, preserving the existing exports and ordering
conventions.

In `@README.md`:
- Around line 787-808: Update the README workflow examples to define all
referenced identifiers before use: capture the created state IDs, workflow ID,
and transition ID, or assign explicit placeholder IDs. Apply this consistently
to the workflow setup and the related examples covering usage, activities, and
hooks, including the symbols state_a, state_b, workflow_id, and transition_id.

In `@tests/unit/test_workspace_workflows.py`:
- Around line 42-52: Guard workspace test resources with try/finally cleanup
before any subsequent work: in tests/unit/test_workspace_workflows.py lines
42-52, initialize state_a, state_b, and workflow, then enter try before creating
state_a; in tests/unit/test_workspace_states.py lines 58-70, enter try/finally
immediately after assigning created and place assertions inside the guarded
block, ensuring cleanup runs when setup or assertions fail.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b4609632-f1f8-4f91-b4d0-28aa84daa575

📥 Commits

Reviewing files that changed from the base of the PR and between a789ece and 77d581d.

📒 Files selected for processing (25)
  • README.md
  • plane/__init__.py
  • plane/api/work_item_type_governance.py
  • plane/api/workflows/__init__.py
  • plane/api/workflows/base.py
  • plane/api/workflows/hooks.py
  • plane/api/workflows/states.py
  • plane/api/workflows/transitions.py
  • plane/api/workspace_states.py
  • plane/api/workspace_workflows/__init__.py
  • plane/api/workspace_workflows/base.py
  • plane/api/workspace_workflows/hooks.py
  • plane/api/workspace_workflows/states.py
  • plane/api/workspace_workflows/transitions.py
  • plane/client/plane_client.py
  • plane/models/enums.py
  • plane/models/states.py
  • plane/models/work_item_type_governance.py
  • plane/models/workflows.py
  • plane/models/workspace_workflows.py
  • plane/models/workspaces.py
  • pyproject.toml
  • tests/unit/test_work_item_type_governance.py
  • tests/unit/test_workspace_states.py
  • tests/unit/test_workspace_workflows.py

Comment thread plane/__init__.py
Comment thread plane/api/work_item_type_governance.py Outdated
Comment thread plane/api/work_item_type_governance.py Outdated
Comment thread plane/api/workflows/base.py
Comment thread plane/api/workflows/hooks.py
Comment thread plane/api/workspace_states.py
Comment thread plane/api/workspace_workflows/base.py Outdated
Comment thread plane/models/enums.py
Comment thread README.md Outdated
Comment thread tests/unit/test_workspace_workflows.py
- Append trailing slashes to every new endpoint path (URL convention)
- Rename get_project_pick/set_project_pick to retrieve_project_pick/
  update_project_pick (CRUD verb convention)
- submit_work_item_approval now takes a SubmitWorkItemApproval DTO and
  returns a typed WorkItemApprovalResult
- Export WorkspaceWorkflowState, WorkspaceWorkflowTransition,
  WorkspaceWorkflowUsageProject, and the new approval models from the
  package root; export CatalogGroupEnum from plane.models
- README: capture created IDs in the workspace-workflow example and
  document placeholder IDs in the governance example
- Tests: enter try/finally immediately after each resource creation so
  failed setup or assertions cannot leak catalog data
Match the resource-package convention used by collections/ and releases/:
a base.py for the top-level class plus one file per sub-resource, each
exposing plain CRUD verbs instead of prefixed method families.

    work_item_type_governance/
      base.py               -> WorkItemTypeGovernance: retrieve/update/preview
      pins.py               -> WorkItemTypeWorkflowPins: list/create/delete
      project_workflows.py  -> ProjectTypeWorkflows: list/retrieve/
                               retrieve_pick/update_pick/preview_fallback

Call sites become client.work_item_type_governance.pins.list(...) and
.project_workflows.update_pick(...), replacing list_pins/create_pins/
delete_pin/list_project_type_workflows/retrieve_project_type_workflow/
retrieve_project_pick/update_project_pick.

Also narrow the new packages' __init__ exports to the top-level resource
class only, as collections/ and releases/ do; sub-resources are reached
through the parent (the pre-existing workflows/ package keeps its own
exports). Response models stay exported from the package root.
@sunder-ch

sunder-ch commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Restructured work_item_type_governance to match the resource-package convention used by the two newest resources (collections/, releases/) — pushed in 7a7e165.

It was the one addition in this PR that did not follow the pattern: a flat module holding three distinct endpoint families, papered over with prefixed method names. It is now a package with a base.py plus one file per sub-resource, each exposing plain CRUD verbs:

plane/api/work_item_type_governance/
    base.py               → WorkItemTypeGovernance: retrieve / update / preview
    pins.py               → WorkItemTypeWorkflowPins: list / create / delete
    project_workflows.py  → ProjectTypeWorkflows: list / retrieve / retrieve_pick / update_pick / preview_fallback

Call sites read client.work_item_type_governance.pins.list(...) and .project_workflows.update_pick(...), replacing list_pins / create_pins / delete_pin / list_project_type_workflows / retrieve_project_type_workflow / retrieve_project_pick / update_project_pick. This also resolves the CRUD-verb naming feedback at its root rather than by renaming.

The other two additions already matched: workspace_states.py is flat because it has no sub-resources (like states.py / workspace_project_states.py), and workspace_workflows/ mirrors the existing workflows/ package.

Also narrowed the two new packages' __init__.py to export only the top-level resource class, as collections/ and releases/ do — sub-resources are reached through the parent. Response models remain exported from the package root. The pre-existing workflows/ package keeps exporting its own sub-resources, unchanged.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
plane/api/workflows/base.py (1)

116-136: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Use a typed query-parameter DTO for workflow activities.

Accepting Mapping[str, Any] bypasses input validation and the SDK’s query-model contract. Define a WorkflowActivityQueryParams request model with extra="ignore" and serialize it with model_dump(exclude_none=True) before calling _get.

Proposed fix
-        params: Mapping[str, Any] | None = None,
+        params: WorkflowActivityQueryParams | None = None,
...
-            params=params,
+            params=params.model_dump(exclude_none=True) if params else None,

As per coding guidelines, "Resource methods must accept Pydantic DTOs, serialize with model_dump(exclude_none=True), and validate responses with Model.model_validate()"; Pydantic query param models use extra='ignore'.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plane/api/workflows/base.py` around lines 116 - 136, Update the activities
method to accept a WorkflowActivityQueryParams Pydantic DTO instead of
Mapping[str, Any], define that model with extra="ignore", and pass
params.model_dump(exclude_none=True) to _get when provided. Preserve the
existing response extraction and WorkflowActivity.model_validate() behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plane/api/work_item_type_governance/project_workflows.py`:
- Around line 65-88: Update update_pick to validate its response with a Pydantic
DTO containing workflow_id, reusing an existing response model or adding one if
needed. Normalize scalar responses into a mapping with workflow_id, then return
the DTO’s model_validate(payload) result instead of an unvalidated dict.

---

Outside diff comments:
In `@plane/api/workflows/base.py`:
- Around line 116-136: Update the activities method to accept a
WorkflowActivityQueryParams Pydantic DTO instead of Mapping[str, Any], define
that model with extra="ignore", and pass params.model_dump(exclude_none=True) to
_get when provided. Preserve the existing response extraction and
WorkflowActivity.model_validate() behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f4aa62f1-cf95-4771-a3a8-111c82d7aa28

📥 Commits

Reviewing files that changed from the base of the PR and between 77d581d and 7a7e165.

📒 Files selected for processing (20)
  • README.md
  • plane/__init__.py
  • plane/api/work_item_type_governance/__init__.py
  • plane/api/work_item_type_governance/base.py
  • plane/api/work_item_type_governance/pins.py
  • plane/api/work_item_type_governance/project_workflows.py
  • plane/api/workflows/base.py
  • plane/api/workflows/hooks.py
  • plane/api/workspace_states.py
  • plane/api/workspace_workflows/__init__.py
  • plane/api/workspace_workflows/base.py
  • plane/api/workspace_workflows/hooks.py
  • plane/api/workspace_workflows/states.py
  • plane/api/workspace_workflows/transitions.py
  • plane/models/__init__.py
  • plane/models/enums.py
  • plane/models/workflows.py
  • tests/unit/test_work_item_type_governance.py
  • tests/unit/test_workspace_states.py
  • tests/unit/test_workspace_workflows.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • README.md
  • tests/unit/test_work_item_type_governance.py

Comment thread plane/api/work_item_type_governance/project_workflows.py Outdated
update_pick was the only resource method returning an unvalidated dict.
Add ProjectWorkflowPickResult (mirroring WorkItemApprovalResult for the
analogous {"state_id": ...} approval response), normalize the scalar
fallback into a mapping, and validate through it.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
plane/__init__.py (1)

129-138: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve the existing workspace-workflow root exports.

Removing WorkspaceWorkflowStates, WorkspaceWorkflowTransitions, and WorkspaceWorkflowTransitionHooks from __all__ breaks callers using the previously public package-root API. Keep compatibility aliases and exports, or explicitly document this breaking change and provide a migration path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plane/__init__.py` around lines 129 - 138, Update the package root’s __all__
exports to retain WorkspaceWorkflowStates, WorkspaceWorkflowTransitions, and
WorkspaceWorkflowTransitionHooks, preserving their existing public API
availability and compatibility aliases where applicable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@plane/__init__.py`:
- Around line 129-138: Update the package root’s __all__ exports to retain
WorkspaceWorkflowStates, WorkspaceWorkflowTransitions, and
WorkspaceWorkflowTransitionHooks, preserving their existing public API
availability and compatibility aliases where applicable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 338c0888-1ce6-45ff-8278-6ab2c748b061

📥 Commits

Reviewing files that changed from the base of the PR and between 7a7e165 and a857baa.

📒 Files selected for processing (3)
  • plane/__init__.py
  • plane/api/work_item_type_governance/project_workflows.py
  • plane/models/work_item_type_governance.py

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