merge: preview → feature/lark-oauth-provider (promote 2026-05-21 cleanup to lark-stable lane)#25
Merged
JOBYINC merged 6 commits intoMay 21, 2026
Conversation
…tent custom-field token API (#20) * feat(api): external_source/external_id idempotency on custom-field token API The public token custom-field API (/api/v1/.../projects/<id>/fields/) already existed but lacked the external-id idempotency convention every other tick_client write uses (Issue: GET-then-create / 409+id). External agents' ensure_* therefore couldn't safely create field/option schema. Mirror the Issue pattern exactly: - WorkItemField + WorkItemFieldOption: add external_source/external_id CharField(255, null, blank) (identical decl to Issue/IssueType). Migration 0127, additive + nullable, no data migration. (Value layer unchanged: PUT upsert is already idempotent via the (issue,field) unique key.) - Token serializers expose external_source/external_id (writable). - WorkItemFieldListAPIEndpoint / WorkItemFieldOptionListAPIEndpoint: GET ?external_source=&external_id= single lookup (mirrors Issue list); POST returns 409 + {error, id:<existing>} when the external keys already exist (mirrors Issue create), scoped to project / field. Pre-existing automation RenameIndex drift detected by makemigrations is deliberately excluded from 0127 (not ours; documented in the migration). Verified end-to-end on the local stack: 15/15 acceptance probes pass (LD_Offset number create/list/set-value/read + idempotent 409+id; Tier single_select + PS/S/A/B options idempotent). No new migration on the prod line beyond 0127; frontend untouched. * style(work-item-field): wrap E501 line in 409 error message ruff check enforces 120-char max on apps/api; the inline 123-char error string in WorkItemFieldOptionListAPIEndpoint POST tripped Lint API CI. Wrap into a multi-line string literal — no behavior change. --------- Co-authored-by: Marcus Cheung <marcusm5@Marcuss-MacBook-Pro.local>
Working-notes that have stabilized into reference material the JOBY agent team and engineering rely on day-to-day; promoting them out of untracked limbo on local working trees into the repo so they version with the code they document. - asana-list-architecture.md — Asana-style list view architecture decision (2026-05-17) - asana-list-plan.md — F1+F2 implementation plan, ratified 2026-05-17 - custom-fields-token-api.md — public token-API reference for the JOBY agents writing structured Tick data - spreadsheet-recon.md — recon notes for feature/custom-fields-spreadsheet - tick-primer.md — paste-into-system-prompt primer for any agent that needs to talk to Tick - tick-agent-reference.md — full Tick API reference companion to tick-primer.md No secrets: scan confirms only secret NAMES (PLANE_API_KEY, X-Api-Key header) appear, never values. Co-authored-by: Marcus Cheung <marcusm5@Marcuss-MacBook-Pro.local>
…oxlint warnings) (#23) * chore(preview): fix pre-existing CI drift on web format + i18n types + oxlint warnings Two pre-existing problems on preview were blocking any PR touching apps/web or @plane/i18n from going green: 1. oxfmt --check flagged 5 web files as unformatted drift (none owned by a single PR). Ran oxfmt --write on: - apps/web/app/(all)/lark-quick-create/page.tsx - apps/web/ce/components/automations/root.tsx - apps/web/core/components/profile/overview/workload.tsx - apps/web/core/components/workspace/settings/lark-invite-modal.tsx - apps/web/core/services/project/project-automation.service.ts Pure whitespace / line-wrapping; no behavior change. 2. @plane/i18n tsc --noEmit failed at src/store/index.ts:68 with "Property 'env' does not exist on type 'ImportMeta'". The repo has no vite-env.d.ts ambient declaration anywhere, and this is the only import.meta.env usage in the package. Inline cast to a minimal typed shape — single line, no new file, no `any`. 3. The 5 web files also had 16 pre-existing oxlint warnings flagged the moment lint-staged ran on them. CI tolerates 11957 warnings on web, but the pre-commit hook uses --deny-warnings (0 tolerance). Fixed in place: - 8 unused vars → underscore-prefixed (the rule's own suggested fix) - 2 s.onload= / s.onerror= → addEventListener (mechanical) - 2 await-in-loop (sequential SDK fallback) → eslint-disable-next-line with reason ("intentional sequential CDN fallback") - 2 key={idx} on local-state lists with no stable id → eslint-disable-next-line with reason - 1 <a> with no href on a non-link wrapper → replaced with <div> (closest non-anchor element) - 1 .then() missing return → added explicit `return;` Effect: any PR touching apps/web or @plane/i18n will pass check:format, check:types, and pre-commit lint-staged again. Unblocks PR #21. * chore(preview): fix 11 pre-existing apps/web type errors exposed once i18n no longer short-circuits tsc After the i18n fix in the previous commit, tsc no longer fails fast on @plane/i18n, so check:types finally runs against apps/web — and surfaces 11 type errors that have been sitting on preview unnoticed: 1. Button variant="neutral-primary" (5 occurrences). The propel Button variant union was narrowed to remove neutral-primary; consumers were never updated. Closest semantic = "secondary" (bordered neutral button used for lower-emphasis actions next to a "primary" Add/Submit). - apps/web/.../members/page.tsx (2x: "Sync from Lark", "Invite from Lark") - apps/web/.../lark-invite-modal.tsx (1x: Cancel) - apps/web/.../lark-quick-create/page.tsx (1x: Cancel) Plus the 2nd "Invite from Lark" instance. 2. Button size="md" (2 occurrences). Same story for size — "md" was removed, the previous default-equivalent is "base". - apps/web/.../lark-invite-modal.tsx (Cancel + Invite N members) 3. `catch (err: unknown)` chains that produce `unknown | string` instead of `string` (2 occurrences). The pattern const message = (err && typeof err === "object" && "error" in err && (err as {error?: string}).error) || "fallback" returns `unknown | string` (because `err && X` returns `err` when `err` is falsy, and `err` is `unknown`). Rewrote with explicit narrowing + a `string` accumulator. Behavior unchanged. - apps/web/.../members/page.tsx (line 174-178) - apps/web/.../lark-invite-modal.tsx (line 120-124) 4. TLarkContact verbatim-import error (1 occurrence). TypeScript 5+ with verbatimModuleSyntax requires `import type` for type-only symbols. - apps/web/.../lark-invite-modal.tsx (line 14): split into `import type { TLarkContact }` and `import { WorkspaceService }`. 5. MemberDropdown multiple={false} prop mismatch (1 issue, 2 lines). The MemberDropdownProps union narrows by `multiple`: multiple: false → value: string|null, onChange: (string|null)=>void multiple: true → value: string[], onChange: (string[])=>void lark-quick-create was passing `value={[id]}` and `onChange: (ids: string[])` under `multiple={false}`, which only the `true` branch accepts. Switched to the correct `multiple: false` shape. - apps/web/.../lark-quick-create/page.tsx (line 658-659) 6. fetchWorkspaceMembers callback signature mismatch (1 occurrence). The store returns `IWorkspaceMember[]` but the call site treats each member as a defensive `Record<string, unknown>` (member-object vs flat — depends on API shape evolution). Added an explicit boundary cast and let `.map` infer the parameter type. Behavior unchanged. - apps/web/.../lark-quick-create/page.tsx (line 391-392) apps/web `tsc --noEmit` now passes (exit 0), apps/web `oxlint --deny-warnings` passes on the 3 modified files, and `oxfmt --check` passes. --------- Co-authored-by: Marcus Cheung <marcusm5@Marcuss-MacBook-Pro.local>
…ty) (#21) Custom fields could be filtered but not sorted; built-in sort is a hardcoded closed enum. Wire the pre-built (but gated) custom-field order parser end to end so a custom column sorts like a built-in one. Backend: - filters.py: add apply_custom_field_order — mirrors the labels/assignees Min-annotate branch of order_issue_queryset but filters the aggregate to the target field_id (the shipped parse_custom_field_order_by returns a bare field_values__value_* path; applying that directly fans the reverse-FK join out and sorts by an arbitrary field — wrong). Rewrites order_by_param to the annotation name so the grouper/paginator are unchanged. - app IssueListEndpoint + IssueViewSet.list and the token issue list else-branch: custom-field sort takes precedence, else built-in. Frontend: - TIssueOrderByOptions admits custom_field__<id> / -custom_field__<id>. - CustomColumnHeaderCell: asc/desc + clear-sort menu mirroring ListSortHeaderCell; list-header-row threads the display-filter props. - base-issues.store issuesSortWithOrderBy: a custom_field key now returns the server order unchanged (root-cause fix — the client re-sort fell through to workItemSortWithOrderByExtended which returns a -created_at-sorted array, silently discarding the correct server order; the value isn't on the client TIssue to re-sort). - i18n en/zh-CN: sort_ascending / sort_descending. Also fixes the 15 pre-existing oxlint warnings in base-issues.store.ts (no-shadow / no-unused-expressions, NOT introduced here). They had to be cleared: the un-bypassable pre-commit hook (oxlint --deny-warnings) blocks staging this file otherwise, and the root-cause fix must live here. All zero-behavior (cond && expr -> if (cond) expr; local renames orderBy->orderByValue, update->updateItem/updateAction, action->groupedAction, groupId/subGroupId->grpId/subGrpId). Verified: backend token probe 10/10 (asc/desc, nulls last, no fan-out, built-in not hijacked); oxlint 0/0 on changed files; tsc no new errors (11 pre-existing lark/members unrelated); user-verified List sort + grouping/counts unaffected. Co-authored-by: Marcus Cheung <marcusm5@Marcuss-MacBook-Pro.local>
…commit warnings (#24) Three independent dev-experience problems found during the 2026-05-21 preview hygiene cleanup. All three are root-cause fixes, not workarounds. 1. apps/api had 5 pre-existing F401 unused-import warnings that ruff --fix in CI silently auto-fixed (in-CI working copy) without committing back. Every PR touching apps/api saw "Found N errors (N-1 fixed, 1 remaining)" noise that masked the real failure. Applied `ruff check --fix` and committed: - apps/api/plane/app/views/issue/sub_issue.py (Func, Q) - apps/api/plane/db/management/commands/lark_long_poll.py (build_url_preview_card) - apps/api/plane/tests/contract/api/test_assigned_work_items.py (uuid) - apps/api/plane/tests/contract/api/test_personal_tasks.py (APIClient) 2. CI's `pnpm turbo run check:types --affected` (and check:format, check:lint) was short-circuiting on the first failing package. The 2026-05-21 cleanup discovered that a single @plane/i18n type error was hiding 11 latent type errors in apps/web for an unknown duration. Added `--continue` to all three turbo runs in `.github/workflows/pull-request-build-lint-web-apps.yml` so every package surfaces its own failures in one CI run, regardless of any prior package's outcome. 3. The husky pre-commit hook ran `pnpm exec oxlint --fix --deny-warnings` on staged files. apps/web carries 964 pre-existing oxlint warnings (within the CI budget of 11957), but `--deny-warnings` is 0-tolerance — so any author running `oxfmt --write` or otherwise re-staging a warning-laden file got their commit refused. CI's `--max-warnings=11957` already catches NEW warnings (any increase fails CI); pre-commit doesn't need to be stricter than that. Changed to `--fix --quiet` (only fail pre-commit on actual lint ERRORS, not warnings). Co-authored-by: Marcus Cheung <marcusm5@Marcuss-MacBook-Pro.local>
…nup to lark-stable image lane) Brings the 5 PRs merged into preview since the last lark-stable promotion (PR #11) into the production image lane: - PR #20 fix(migrations): missing 0127_workitemfield_external_ids + custom-field token API idempotency - PR #21 feat(custom-fields): sort the issue list by a custom field (List parity) (lark-oauth-provider already carried this work at SHA 315ed1c as the un-squashed original; preview has the cherry-pick squash 62b6054. Conflict-free except for the wrapped 409 error string.) - PR #22 docs(agent-notes): commit 6 standing reference docs - PR #23 chore(preview): fix pre-existing CI drift on web format + i18n types + oxlint warnings - PR #24 chore(dx): apps/api unused-import cleanup + turbo --continue + relaxed pre-commit Conflict resolved (1 file): apps/api/plane/api/views/work_item_field.py:280 HEAD (lark-oauth-provider): one-line 123-char "error" string (would re-fail Lint API) origin/preview: wrap into multi-line literal (PR #20's E501 ruff fix) Resolution: take preview's wrapped version. ruff check clean. No behavior change beyond what the 5 individual PRs already shipped to preview. Production image (lark-stable tag) will rebuild on push; production hosts will only pick it up on the next deliberate redeploy (docker-compose pull && up -d).
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.
What this PR does
Promotes the 5 PRs merged into `preview` since the last lark-stable promotion (PR #11) into the production image lane.
On merge: `build-lark-feature.yml` workflow triggers and pushes new `ghcr.io/jobyinc/plane-{backend,frontend,admin,space}:lark-stable` images.
Production hosts: NOT auto-deployed. Only pick up the new image on the next explicit `docker-compose pull && docker-compose up -d` on the production host.
Promoted PRs
Conflict resolution (1 file)
`apps/api/plane/api/views/work_item_field.py` line 280:
Resolution: took preview's wrapped version. Local `ruff check` clean.
Note on PR #21
`lark-oauth-provider` already carried PR #21's work at the original SHA `315ed1c00d` (pre-PR cherry-pick). `preview` has the squash-merged cherry-pick at `62b605471b`. Content is identical; no conflicts on the actual sort-by-custom-field code.
Test plan