merge: preview → feature/lark-oauth-provider (promote to lark-stable image lane)#11
Merged
Merged
Conversation
Lift the My Tasks bucket bootstrap out of ProjectViewSet.personal() into plane.app.services so an upcoming token-API endpoint can reuse the same logic to create personal projects on behalf of other workspace members. The session-API wrapper passes only owner (actor defaults to it), preserving prior behavior. Uses project.save(created_by_id=actor.id) instead of the original Project.objects.create(..., created_by=actor) so the helper honors the passed actor regardless of crum's current-user context. The inline predecessor relied on a request being in flight; that assumption would not hold for out-of-band callers (celery tasks, the upcoming token endpoint, or unit tests).
Gate token-API endpoints that act on behalf of workspace members other than the token's own owner (cross-user writes into personal projects, workspace-wide assignee scans). Reuses the existing DB-only is_service flag on APIToken — no new schema, no new self-grant API path. Wired by the upcoming personal-tasks and assigned-work-items viewsets.
APIKeyAuthentication.authenticate returns request.auth = api_token.token (the raw string), not the APIToken model. The original mock-based unit tests passed but the production check would have silently denied every service-tier token. Re-query the row from the header value instead — same pattern already in BaseAPIView.get_throttles. Also tighten with is_active=True to mirror the auth class. Tests rewritten against real APIToken rows.
System-tier endpoint at POST/PATCH /api/v1/workspaces/{slug}/personal-tasks/
that creates and updates work items in any workspace member's personal
"My Tasks" project on behalf of that member. Gated by IsSystemToken;
ordinary tokens are 403'd. Idempotency reuses the existing Issue
contract — duplicate (project, external_source, external_id) returns
409 + the existing work item id. PATCH may only touch work items whose
external_source matches the body, enforcing the §5 rule 2 privacy
boundary.
Adds APIActivityLog.acting_on_behalf_of (nullable UUID, indexed,
migration 0128) wired by APITokenLogMiddleware so every audit row for
these calls records the target member, making "system-on-behalf-of-X"
queryable downstream. The viewset sets the attribute on the underlying
Django request (not the DRF wrapper) so the middleware — which runs at
the Django layer — can read it.
The bucket get-or-create path comes from the C1 helper, which resolves
spec §12.4 lazy-create from the server side: a token call works even
when the target member has never opened "My Tasks" in the web UI.
11 contract tests cover: non-system 403, anon 401/403, missing-owner
400, missing-external-keys 400, owner-not-in-workspace 403, lazy-create
+ 201, duplicate 409+id, PATCH wrong-source 403, PATCH matching-source
200, PATCH nonexistent 404, audit row carries owner UUID as
acting_on_behalf_of.
System-tier endpoint at
GET /api/v1/workspaces/{slug}/assigned-work-items/?assignee=<uuid>
that returns the target user's assigned work items across the entire
workspace — both their personal "My Tasks" project AND any shared
project they're a member of. Privacy boundary is the assignee filter:
only items where the target is assigned appear; other members' items
and items in shared projects the target is not on do not leak.
This is the read side of spec §6 Q1 (a-extended) — no external_source
filter on the read path. The §5 rule 2 source-restricted boundary
stays on the write side (PATCH /personal-tasks/{id}/) where it
matters; reads need to be "everything assigned" so the agent-side
"today" view doesn't miss tasks the user created themselves.
Optional filters: state_group (CSV) and target_date_before (ISO).
Pagination uses BasePaginator cursor envelope. Audit row records the
query.assignee UUID via the same _acting_on_behalf_of hook the
personal-tasks endpoint uses.
8 contract tests cover non-system 403, anon 401, missing/malformed
assignee 400, cross personal+shared completeness, privacy boundary
(no leak), state_group filter, empty case, and audit row UUID.
Adds operation_id / summary / description / tags annotations to the three new endpoints (POST + PATCH personal-tasks, GET assigned-work-items) so drf-spectacular surfaces polished entries in the OpenAPI schema served at /api/v1/schema/. No CHANGELOG file in this fork — skipping that part of the original plan. Cross-repo skill mirror sync (skills-shared/joby-plane/tick-agent-reference.md §12.4 + SKILL.md bump) is a post-ship step, intentionally out of scope here.
Adds /personal-tasks/ POST+PATCH and /assigned-work-items/ GET under IsSystemToken gate. APIActivityLog.acting_on_behalf_of column (migration 0128) for audit. get_or_create_personal_project helper extracted from session API. 28 contract+unit tests passing.
…ields + system identity, 33 commits) Roll up of 4 feature epics: Asana-style personal My Tasks projects, custom-fields token API, list-view ergonomics + UI cleanup, system-identity personal-tasks endpoints (PR #9). Migrations 0127 + 0128 are nullable column additions + indexes (safe). No breaking API changes.
This was referenced May 20, 2026
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
Promote the current `preview` state into the image-build lane so the
next `lark-stable` GHCR image includes PR #9 / PR #10 work. Image-build
workflow (`build-lark-feature.yml`) only triggers on push to
`feature/lark-oauth-provider` → produces `lark-stable` tag → prod
docker compose pulls that tag.
What this brings in (8 commits from preview not yet in lark-oauth-provider)
Deploy sequence after merge
Known reverse-direction divergence
`feature/lark-oauth-provider` has ~7 commits not on `preview` (pre-existing
diverge). This merge does NOT resolve that — both branches will continue
to have content the other doesn't. Marcus to decide if a reverse PR is
needed.