Skip to content

merge: preview → feature/lark-oauth-provider (promote to lark-stable image lane)#11

Merged
JOBYINC merged 8 commits into
feature/lark-oauth-providerfrom
preview
May 20, 2026
Merged

merge: preview → feature/lark-oauth-provider (promote to lark-stable image lane)#11
JOBYINC merged 8 commits into
feature/lark-oauth-providerfrom
preview

Conversation

@JOBYINC
Copy link
Copy Markdown
Owner

@JOBYINC JOBYINC commented May 20, 2026

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

  1. `Build Lark Feature Images (GHCR)` workflow auto-triggers on push (~5-10 min)
  2. New `lark-stable` image appears in GHCR
  3. On prod: `docker compose pull && docker compose up -d migrator` (runs migration 0128)
  4. `docker compose up -d api worker beat-worker` to restart with new image
  5. Agent ops can then add `JOBY_PLANE_SYSTEM_KEY` to Infisical + restart Tom listener per the CUTOVER card

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.

Marcus Cheung and others added 8 commits May 19, 2026 15:32
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.
@JOBYINC JOBYINC merged commit 97c200d into feature/lark-oauth-provider May 20, 2026
7 of 13 checks passed
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