Skip to content

[SILO-1276] feat: add work item page links, page listing, intake status, and archive methods#36

Open
akhil-vamshi-konam wants to merge 6 commits into
mainfrom
chore-page-to-work-item-attachment
Open

[SILO-1276] feat: add work item page links, page listing, intake status, and archive methods#36
akhil-vamshi-konam wants to merge 6 commits into
mainfrom
chore-page-to-work-item-attachment

Conversation

@akhil-vamshi-konam
Copy link
Copy Markdown

@akhil-vamshi-konam akhil-vamshi-konam commented May 18, 2026

Description:

Description

New sub-resource: client.work_items.pages

  • create(workspace_slug, project_id, work_item_id, data) — link a page to a work item
  • list(workspace_slug, project_id, work_item_id) — list all linked pages
  • retrieve(workspace_slug, project_id, work_item_id, work_item_page_id) — retrieve a specific link
  • delete(workspace_slug, project_id, work_item_id, work_item_page_id) — remove a page link

Pages resource (client.pages)

  • list_workspace_pages(workspace_slug) — list all workspace pages
  • list_project_pages(workspace_slug, project_id) — list all pages in a project

Intake resource (client.intake)

  • update_status(workspace_slug, project_id, work_item_id, data) — triage an intake work item (accept, decline, snooze, duplicate)

Work items resource (client.work_items)

  • list_archived(workspace_slug, project_id) — list archived work items
  • archive(workspace_slug, project_id, work_item_id) — archive a work item
  • unarchive(workspace_slug, project_id, work_item_id) — restore an archived work item

Type of Change

  • Feature (non-breaking change which adds functionality)

Test Scenarios

  • tests/unit/test_work_item_pages.py — create, list, retrieve, delete page links
  • tests/unit/test_pages.py — list workspace/project pages
  • tests/unit/test_intake.py — accept and decline intake work items via update_status
  • tests/unit/test_work_items.py — archive, unarchive, list archived, create/update links with title

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

📝 Walkthrough

Walkthrough

This PR adds work-item page linking to the Plane Python SDK: new Pydantic models, a WorkItemPages CRUD resource, Pages list methods, WorkItems integration and archived listing, Intake status update, related model/enum adjustments, tests across the surface, and a version bump.

Changes

Work Item Page Linking

Layer / File(s) Summary
Work Item Page Data Models
plane/models/work_item_pages.py
Defines WorkItemPageLite, WorkItemPage, CreateWorkItemPage, and PaginatedWorkItemPageResponse for work-item ↔ page relationships.
WorkItemPages CRUD Resource
plane/api/work_items/pages.py
New WorkItemPages BaseResource with list(), retrieve(), create(), and delete() methods; serializes requests and validates responses against the new models.
Pages API List Methods
plane/api/pages.py
Adds list_workspace_pages and list_project_pages that accept optional PaginatedQueryParams, build query params, GET the appropriate endpoint, and return PaginatedPageResponse; removes unused Mapping import.
WorkItems Sub-Resource & Archived Listing
plane/api/work_items/base.py
Imports and initializes WorkItemPages as self.pages and adds list_archived() to fetch a project's archived work items.
Intake Status Patch
plane/api/intake.py
Adds Intake.update_status to PATCH an intake issue's status and return a validated IntakeWorkItem.
Model & Enum Updates
plane/models/enums.py, plane/models/projects.py, plane/models/work_item_properties.py, plane/models/work_items.py
Adds FORMULA/RELEASE enum values, an optional network in UpdateProject, makes property/relation serializers return `str
Tests and Version
tests/unit/test_work_item_pages.py, tests/unit/test_pages.py, tests/unit/test_intake.py, tests/unit/test_work_items.py, pyproject.toml
Adds WorkItemPages CRUD tests, pages listing tests, intake status tests, work-items link/title and archive lifecycle tests; bumps package version to 0.2.12.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant WorkItemPages
  participant API
  Client->>WorkItemPages: list(workspace_slug, project_id, work_item_id, params?)
  WorkItemPages->>API: GET /workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/pages
  API-->>WorkItemPages: PaginatedWorkItemPageResponse
  Client->>WorkItemPages: create(workspace_slug, project_id, work_item_id, CreateWorkItemPage)
  WorkItemPages->>API: POST /workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/pages (body CreateWorkItemPage)
  API-->>WorkItemPages: WorkItemPage
  Client->>WorkItemPages: retrieve(..., work_item_page_id)
  WorkItemPages->>API: GET /.../pages/{work_item_page_id}
  API-->>WorkItemPages: WorkItemPage
  Client->>WorkItemPages: delete(..., work_item_page_id)
  WorkItemPages->>API: DELETE /.../pages/{work_item_page_id}
  API-->>WorkItemPages: (no content)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

A rabbit bounds through pages new,
Work-item links now breaking through,
Models sketched and endpoints wired,
Tests run clean, resources inspired,
Version bumped — the hop is true! 🐇📄

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 79.07% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main changes: adding work item page links, page listing methods, intake status update, and archive list functionality to the SDK.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 chore-page-to-work-item-attachment

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 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/pages.py`:
- Around line 24-25: The endpoint URL strings for the pages list calls are
missing a trailing slash, causing redirects; update the calls that invoke
self._get to use f"{workspace_slug}/pages/" (and the other similar call around
the block that returns PaginatedPageResponse.model_validate) so the URL ends
with '/' per convention; search for uses of self._get with the "pages" path in
this file and add the trailing slash to each occurrence.

In `@plane/api/work_items/pages.py`:
- Around line 20-21: The list method currently accepts a raw dict parameter
`params: dict | None` (in the `list` function returning
`PaginatedWorkItemPageResponse`); change it to accept a Pydantic query DTO
(e.g., a new or existing QueryModel type) and before calling `_get` serialize it
with `query_model.model_dump(exclude_none=True)` instead of passing a raw dict,
then validate the response with
`PaginatedWorkItemPageResponse.model_validate()`; apply the same change (replace
raw dict params with the DTO + model_dump serialization and response
model_validate) to the other affected resource methods referenced around lines
30-33.
- Around line 31-33: The WorkItemPages endpoints are missing required trailing
slashes; update each endpoint string in the WorkItemPages resource to end with a
"/" (e.g. change
f"{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/pages" to
f"{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/pages/"), and
make the same change for the three other similar endpoint strings in the
WorkItemPages class so all requests follow the
`{base_path}/api/v1{resource_base_path}/{endpoint}/` convention.

In `@tests/unit/test_work_item_pages.py`:
- Around line 31-40: The page fixture creates a workspace page via
client.pages.create_workspace_page but never deletes it; convert the fixture to
a teardown-style (use yield) so after tests run you call the pages deletion API
(e.g., client.pages.delete_workspace_page or the appropriate client.pages.delete
method) with the created page's identifier to remove the page; update the
fixture named page to return (yield) the created CreatePage result and then call
the deletion in the finally/after-yield block to avoid leaking test pages.
- Around line 26-29: Replace the bare "except Exception: pass" around the
cleanup call to client.work_items.delete(workspace_slug, project.id, wi.id) with
targeted exception handling: import PlaneError from plane.errors and catch only
PlaneError (e.g., "except PlaneError: pass" or better "except PlaneError as e:
log.warning(...)") for expected SDK/cleanup failures, and allow all other
exceptions to propagate so unexpected errors fail the test; apply the same
change for the other cleanup calls at the same pattern (the other
client.work_items.delete and similar blocks).
🪄 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

Run ID: d8a5716a-deaa-4f76-a6a7-6bf003cd21e7

📥 Commits

Reviewing files that changed from the base of the PR and between 6e5389f and 85b2800.

📒 Files selected for processing (5)
  • plane/api/pages.py
  • plane/api/work_items/base.py
  • plane/api/work_items/pages.py
  • plane/models/work_item_pages.py
  • tests/unit/test_work_item_pages.py

Comment thread plane/api/pages.py
Comment thread plane/api/work_items/pages.py Outdated
Comment thread plane/api/work_items/pages.py
Comment thread tests/unit/test_work_item_pages.py
Comment thread tests/unit/test_work_item_pages.py
@akhil-vamshi-konam akhil-vamshi-konam changed the title chore: add page-to-work-item attachment methods to SDK feat: add work item page links, page listing, intake status, and archive methods May 19, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (3)
plane/models/work_item_properties.py (3)

134-135: ⚡ Quick win

Align parameter type annotations with field optionality.

Both property_type and relation_type are optional in UpdateWorkItemProperty, so their serializer parameters should reflect that with | None.

♻️ Proposed fix for type annotation consistency
 `@field_serializer`("property_type")
-def serialize_property_type(self, value: PropertyType) -> str | None:
+def serialize_property_type(self, value: PropertyType | None) -> str | None:
     return value.value if value else None

 `@field_serializer`("relation_type")
-def serialize_relation_type(self, value: RelationType) -> str | None:
+def serialize_relation_type(self, value: RelationType | None) -> str | None:
     return value.value if value else None

Also applies to: 138-139

🤖 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/models/work_item_properties.py` around lines 134 - 135, The serializer
parameter annotations must match the optional fields: update the signatures of
serialize_property_type and serialize_relation_type so they accept PropertyType
| None and RelationType | None respectively (instead of non-optional types), and
ensure their return logic remains the same (return value.value if value else
None) to handle None inputs correctly.

47-48: ⚡ Quick win

Align parameter type annotations with field optionality.

The serializer parameter types should match the field types. Since relation_type is optional (RelationType | None), the parameter should be RelationType | None. The property_type field is required in this model, so its serializer parameter type PropertyType is correct, but the return type should remain str (not str | None) unless property_type can actually be None at serialization time.

♻️ Proposed fix for type annotation consistency
 `@field_serializer`("property_type")
-def serialize_property_type(self, value: PropertyType) -> str | None:
+def serialize_property_type(self, value: PropertyType) -> str:
-    return value.value if value else None
+    return value.value

 `@field_serializer`("relation_type")
-def serialize_relation_type(self, value: RelationType) -> str | None:
+def serialize_relation_type(self, value: RelationType | None) -> str | None:
     return value.value if value else None

Also applies to: 51-52

🤖 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/models/work_item_properties.py` around lines 47 - 48, The
serialize_property_type signature and body must match the model field
optionality: update serialize_property_type(self, value: PropertyType) -> str to
return value.value (no None branch) since property_type is required, and update
serialize_relation_type to accept RelationType | None (or
Optional[RelationType]) and return str | None, preserving the conditional return
(value.value if value else None); ensure you reference the property_type and
relation_type fields and the methods serialize_property_type and
serialize_relation_type when making the edits.

75-76: ⚡ Quick win

Align parameter type annotations with field optionality.

The serializer parameter types should match the field types. Since relation_type is optional (RelationType | None), the parameter should be RelationType | None. The property_type field is required in this model, so its serializer parameter type PropertyType is correct, but the return type should remain str (not str | None) unless property_type can actually be None at serialization time.

♻️ Proposed fix for type annotation consistency
 `@field_serializer`("property_type")
-def serialize_property_type(self, value: PropertyType) -> str | None:
+def serialize_property_type(self, value: PropertyType) -> str:
-    return value.value if value else None
+    return value.value

 `@field_serializer`("relation_type")
-def serialize_relation_type(self, value: RelationType) -> str | None:
+def serialize_relation_type(self, value: RelationType | None) -> str | None:
     return value.value if value else None

Also applies to: 79-80

🤖 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/models/work_item_properties.py` around lines 75 - 76, The serializer
type hints are inconsistent with field optionality: update
serialize_property_type so its signature reflects that property_type is required
(keep parameter type as PropertyType and change the return type to str, not
Optional) and update serialize_relation_type to accept RelationType | None and
return str | None (since relation_type is optional); adjust the type annotations
on the serialize_property_type and serialize_relation_type functions to match
the property_type and relation_type field optionality.
🤖 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/intake.py`:
- Around line 113-115: The endpoint path passed to self._patch in the
update_status flow is missing a trailing slash; update the f-string used there
(the call that builds
f"{workspace_slug}/projects/{project_id}/intake-issues/{work_item_id}/status")
to include a trailing slash ("/status/") so it conforms to the SDK URL
convention used across plane/api/*.py and ensure any related callers still work
with the updated path.

In `@plane/api/work_items/base.py`:
- Around line 257-259: The archived-work-items endpoint URL is missing the
required trailing slash; update the f-string passed to self._get (the call
producing response) from
f"{workspace_slug}/projects/{project_id}/archived-work-items" to include a
trailing "/" so it becomes
f"{workspace_slug}/projects/{project_id}/archived-work-items/"; ensure this
change is applied in the method containing the response = self._get(...) call so
the SDK follows the "{base_path}/api/v1{resource_base_path}/{endpoint}/"
convention.
- Around line 21-23: Remove the duplicated import of WorkItemPages: in the
import block where WorkItemPages is imported twice alongside WorkItemRelations
(from .pages import WorkItemPages; from .relations import WorkItemRelations),
keep only a single import statement for WorkItemPages and remove the redundant
duplicate to satisfy Ruff/isort rules and clean up imports.

In `@tests/unit/test_intake.py`:
- Around line 120-121: The current tests silently return when the fixture
`intake_work_item` lacks an `issue`, causing false passes; replace the early
`return` that checks `if not (hasattr(intake_work_item, "issue") and
intake_work_item.issue): return` with an explicit `pytest.skip("fixture setup
incomplete: missing issue")` (or a hard `assert` that `intake_work_item.issue`
is present) so test outcomes are explicit; apply the same change in both
affected test functions that perform this `hasattr`/`issue` check.

In `@tests/unit/test_pages.py`:
- Around line 48-60: Wrap the page-creation assertions in a try/finally in the
tests that create pages (e.g., test_list_workspace_pages_contains_created_page
and the similar test at lines 71-83) so created pages are always deleted: after
calling client.pages.create_workspace_page (and the analogous
create_project_page), capture the created.id, run assertions in try, and in
finally call the appropriate deletion method (client.pages.delete_page or
client.pages.delete_workspace_page / delete_project_page as applicable) using
the captured id to ensure cleanup even if assertions fail; ensure you reference
the created variable and response/results to locate the creation and listing
calls.

In `@tests/unit/test_work_items.py`:
- Line 8: The import line with AdvancedSearchWorkItem, CreateWorkItem,
CreateWorkItemLink, UpdateWorkItem, and UpdateWorkItemLink exceeds the
100-character limit; split or wrap the import to multiple lines (e.g., use
parentheses and one symbol per line or separate imports) so the line length
complies with Black/Ruff formatting while keeping the same symbols imported from
plane.models.work_items.
- Around line 358-361: The teardown currently swallows all exceptions from
client.work_items.delete(workspace_slug, project.id, wi.id) which hides cleanup
failures; update the teardown to either let the exception propagate (remove the
try/except) so failures surface, or catch only the specific expected exception
(e.g., a NotFound or HTTPError) and re-raise or log unexpected exceptions;
locate the delete call in the test teardown and change the try/except block
accordingly so you do not silently pass on all Exception types.

---

Nitpick comments:
In `@plane/models/work_item_properties.py`:
- Around line 134-135: The serializer parameter annotations must match the
optional fields: update the signatures of serialize_property_type and
serialize_relation_type so they accept PropertyType | None and RelationType |
None respectively (instead of non-optional types), and ensure their return logic
remains the same (return value.value if value else None) to handle None inputs
correctly.
- Around line 47-48: The serialize_property_type signature and body must match
the model field optionality: update serialize_property_type(self, value:
PropertyType) -> str to return value.value (no None branch) since property_type
is required, and update serialize_relation_type to accept RelationType | None
(or Optional[RelationType]) and return str | None, preserving the conditional
return (value.value if value else None); ensure you reference the property_type
and relation_type fields and the methods serialize_property_type and
serialize_relation_type when making the edits.
- Around line 75-76: The serializer type hints are inconsistent with field
optionality: update serialize_property_type so its signature reflects that
property_type is required (keep parameter type as PropertyType and change the
return type to str, not Optional) and update serialize_relation_type to accept
RelationType | None and return str | None (since relation_type is optional);
adjust the type annotations on the serialize_property_type and
serialize_relation_type functions to match the property_type and relation_type
field optionality.
🪄 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

Run ID: b509d5f5-52f1-4967-b0fd-80f912f4c8d1

📥 Commits

Reviewing files that changed from the base of the PR and between 85b2800 and 2d54793.

📒 Files selected for processing (11)
  • plane/api/intake.py
  • plane/api/work_items/base.py
  • plane/api/work_items/pages.py
  • plane/models/enums.py
  • plane/models/projects.py
  • plane/models/work_item_properties.py
  • plane/models/work_items.py
  • pyproject.toml
  • tests/unit/test_intake.py
  • tests/unit/test_pages.py
  • tests/unit/test_work_items.py
✅ Files skipped from review due to trivial changes (1)
  • pyproject.toml
🚧 Files skipped from review as they are similar to previous changes (1)
  • plane/api/work_items/pages.py

Comment thread plane/api/intake.py
Comment thread plane/api/work_items/base.py Outdated
Comment thread plane/api/work_items/base.py
Comment thread tests/unit/test_intake.py Outdated
Comment thread tests/unit/test_pages.py Outdated
Comment thread tests/unit/test_work_items.py Outdated
Comment thread tests/unit/test_work_items.py
@akhil-vamshi-konam akhil-vamshi-konam changed the title feat: add work item page links, page listing, intake status, and archive methods [SIL0-1276] feat: add work item page links, page listing, intake status, and archive methods May 19, 2026
@akhil-vamshi-konam akhil-vamshi-konam changed the title [SIL0-1276] feat: add work item page links, page listing, intake status, and archive methods [SILO-1276] feat: add work item page links, page listing, intake status, and archive methods May 19, 2026
@makeplane
Copy link
Copy Markdown

makeplane Bot commented May 19, 2026

Linked to Plane Work Item(s)

This comment was auto-generated by Plane

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