aboutsummaryrefslogtreecommitdiff
path: root/docs
Commit message (Collapse)AuthorAgeFilesLines
* docs: spec for selecting multiple statuses in a filterCraig Jennings2026-06-011-0/+148
| | | | | | I wrote a spec for letting a filter match several workflow states at once. The builder picks one state today, so "Todo or In Review" can't be built. The design makes :state polymorphic, a name or a list of names, mirroring how :state-type already works, so (:state ("Todo" "In Review")) compiles to state.name.in. It covers the four filter-side readers (compiler, builder, validator, reverse-compile), reconciles the views spec's representability boundary, and notes the issue's own state field is a separate, unaffected use. Codex review incorporated. Status Ready.
* feat(views): reshape the keymap and transient around the view nounCraig Jennings2026-06-011-3/+5
| | | | | | | | I put the view operations under the verb a user would reach for. Create, edit, and delete local views are c v, e v, and k v. The fetch group gains run-local-view at f l and keeps run-linear-view at f v, with publish at f u and save-locally reserved at f d. Since f p already runs issues by project, publish and save-locally take u and d, which read as up and down and mirror the copy-up and copy-down lifecycle. The transient now has a Views column grouping create, edit, run-local, run-linear, publish, and delete, separate from the source-fetching commands. I updated the spec's keymap to the implemented letters so the two stay in sync. Phase 3 of the views spec. Keymap tests assert the new bindings. Suite, compile, and lint are green.
* docs: finalize local/Linear views spec for implementationCraig Jennings2026-06-011-82/+366
| | | | | | | | The spec is implementation-ready. It pins the design the earlier drafts left open, across the parts that turned out to need real contracts rather than prose. Copy-down's reverse-compile is the load-bearing piece. A live probe verified that CustomView.filterData is readable but stored as Linear's and/or view-filter tree, not the IssueFilter shape Pearl's compiler emits, so the reverse-compile is a normalize-then-match: flatten the tree, unwrap single-branch or/and, read one-element in as a scalar, then match each conjunct to a Pearl authoring key. The per-dimension contract is explicit, including the cases where the compiled filter loses information: :state and :state-type stay distinct, :priority canonicalizes to the integer, :open t maps from the exact open-state nin while generic nin refuses, and a multi-value in on any singular key refuses rather than silently narrow the view. Anything outside the model refuses with a structured reason instead of producing a lossy local view. The rest of the surface is settled too. A tracked local view runs the local filter, the source of truth, not its Linear mirror. Edit preserves all non-edited metadata through a dedicated writer. One account guard covers every read and mutate, with delete and publish refusing before any Linear call on a mismatch. Local and cross-store name collisions have a defined policy. Vocabulary is direct-renamed to filter / local view / Linear view, with intent-first publish and save-locally commands and no aliases. Agreed decisions, acceptance criteria, a phased plan, and the dispositions are recorded inline.
* docs: add bidirectional copy to the local/Linear views specCraig Jennings2026-06-011-26/+72
| | | | | | | | I expanded the spec to make copying a view between the two stores a first-class operation in both directions, and deliberately asymmetric. Copy up (local view to Linear view) is publish: it creates a tracked Linear view, and re-copying updates it in place. Copy down (Linear view to local view) is a fork: it duplicates the Linear view's filter into a new, independent local view you can edit and rename, with no sync link back. The principle behind the asymmetry is that the local view is the editable source of truth. Copy down is the one direction that needs real engine work. Because the result has to be editable, it can't store opaque filter JSON. It has to reverse-compile Linear's IssueFilter back into Pearl's authoring plist. The spec carries the dimension table the reverse-compile inverts, and draws the representability boundary: when a Linear view's filter uses OR logic, an unmodeled dimension, or an operator Pearl doesn't emit, copy down refuses with a clear message rather than dropping conditions silently. A local view that looks right but matches a different issue set, diverging on every refresh, is the worse failure. I also added the two decisions the change forces (refuse-don't-guess on unrepresentable filters, fork-not-track on copy down), the copy-up naming question, keymap slots, acceptance criteria for both round-trips, and two implementation phases. The spec is still a Draft, now with eight open questions.
* docs: spec for local views and Linear viewsCraig Jennings2026-06-011-0/+197
| | | | | | | | I drafted a spec that renames "saved query" to one noun with a location qualifier: a local view (private, on disk) and a Linear view (published, shared, the CustomView Linear users already know). The lifecycle is git-shaped: a local view is a local branch, a Linear view a remote branch, publish is push, and the stored view-id is the tracking link. The model already exists in the code. What's missing is a coherent name and command shape. Today the saved-query operations scatter across the verb-first keymap: create is hidden inside the filter builder and filed under fetch, delete has a home, edit doesn't exist. The spec covers the rename map (obsolete aliases so no init.el breaks), new create and edit commands, the keymap/transient/picker reshape, and the config migration. One piece isn't a rename: pulling a Linear view back into a local view needs a reverse-compile of IssueFilter, deferred to vNext. Status is Draft with six open questions. No code changed.
* feat(accounts): multi-account support with testsCraig Jennings2026-05-311-2/+2
| | | | | | | | | | | | I added a named-account layer for working more than one Linear workspace from one Emacs. Before this, everything that identified a workspace was a single global: pearl-api-key, pearl-graphql-url, pearl-org-file-path, and the lookup caches. Nothing stopped a work command from running under personal credentials or a work fetch from landing in the personal file, and switching accounts meant re-customizing the key, team, and file by hand and clearing the cache. pearl-accounts maps a name to a per-workspace plist (credential source, org file, default team, optional endpoint), and pearl-switch-account makes one active. Account state flows through an explicit context rather than mutating globals. Every request snapshots its account at dispatch through pearl--graphql-request-async and re-establishes it around the callbacks. A switch mid-fetch can't bleed into a request already in flight: the result finishes into the account it was dispatched under. I centralized this in the one request primitive, so the leak surface is a single function instead of every call site. Rendered files carry a #+LINEAR-ACCOUNT marker, and a buffer guard refuses a command run from one account's file while another is active, naming both, so a work edit can't push under personal credentials. An unmarked legacy file lets reads and refreshes through and acquires its marker on the first refresh. Mutations wait until then. Credentials resolve through auth-source, an env var, or an inline literal, and a resolved key is never persisted through Customize or logged. The active account shows in the mode line. A saved query can carry an :account so it refuses to run under the wrong workspace before any lookup. With pearl-accounts unset, everything behaves exactly as before, off the legacy globals.
* docs(view-sync): README sync-up section + spec status to DoneCraig Jennings2026-05-281-1/+1
| | | | | | | | | | | | | | This is Phase 6 of docs/saved-query-sync-spec.org, the docs commit that lands the saved-query → Linear view sync work. I added a new "Publishing a saved query as a Linear view" subsection under Commands → Sources. It walks through the two publish commands (sync-saved-query-to-linear and publish-current-source) with their full bindings, the scope-and-visibility prompt the user sees on first sync, the Replace/Rename/Cancel collision flow, the four :linear-view-* metadata keys + :linear-view-url that get persisted, the one-way push semantics, the v1 caveat that :sort and :order don't sync up, and the two-stage delete-saved-query prompt for synced entries. I updated the existing Sources example block to include the new [saved → <scope>] picker labels (team-scoped, Personal, and the stale-team `?` case), with a paragraph explaining how the dispatch routes those through customView(id:) on Linear rather than re-running the local filter. I extended the prefix-keymap quick-reference table with f S (sync), f P (publish), and k q (delete saved query) because they were bound in pearl-mode but missing from the table. The Fetching and refreshing command table gained rows for sync-saved-query, publish-current-source, and delete-saved-query. I flipped docs/saved-query-sync-spec.org Status from Ready to Done with the four implementation-phase commit hashes (60a026b, c8b9ad1, fd94237, 0975e7d). No code changes. 660 ert tests still green. make compile and make lint clean.
* docs(view-sync): phase 1 verified, pearl filter output works as filterDataCraig Jennings2026-05-281-0/+15
| | | | | | Ran a live round-trip against api.linear.app to close phase 1 of `docs/saved-query-sync-spec.org`. Pearl's existing `pearl--build-issue-filter` produces a structure that `customViewCreate` accepts as `filterData` verbatim, the view created with the expected shape, `customView(id:)` read back the identical structure, and `customViewDelete` cleaned up. No new mapping helper required for phase 2; the compiler is the mapping. Recorded the probe results in the spec's Review dispositions section. Phase 1's todo entry can move to DONE; phase 2 (the user-facing `pearl-sync-saved-query-to-linear` command) can consume the existing compiler output directly.
* docs(spec): saved-query-sync to Linear views, Ready after sprint reviewCraig Jennings2026-05-281-0/+294
| | | | | | | | | | New spec at `docs/saved-query-sync-spec.org` for promoting local `pearl-saved-queries` entries to Linear custom views (`customViewCreate` / `Update` / `Delete`). Written end to end on 2026-05-28 and walked through a six-question sprint review the same day. Status moves Draft to Ready. The live-API probe drove two substantive simplifications. `CustomViewCreateInput.filterData` is type `IssueFilter`, the exact type pearl's existing `pearl--build-issue-filter` already produces, so the proposed filter-mapping helper collapses to a pass-through and implementation phase 1 becomes a sanity-test commit rather than new code. Linear's API has no `customViewArchive` (only Delete), so the delete-or-unlink prompt on the extended `pearl-delete-saved-query` is the actual choice rather than archive-or-unlink. No sort-order input on the view mutations either, so syncing pearl's `:sort`/`:order` moves to vNext. The review itself surfaced one further design move beyond resolving questions: collapsing the two prompts the spec originally proposed (team scope plus shared flag) into a single enriched prompt where each candidate is a complete end-state (`[ Team: Engineering, visible to the team ]` etc.). The same enrichment pattern then applies to `pearl-pick-source`'s label for synced entries (`[saved → Engineering] Name`). Two pattern-catalog candidates noted for the running rulesets discussion. Implementation decomposes into seven independently-shippable phases, enumerated in the spec's `Implementation tasks (drop-in for todo.org)` section per the spec-review workflow update sent to rulesets earlier in the session. Tasks land in `todo.org`'s Pearl Open Work alongside this commit.
* feat(sources): favorites, views, and filters via pick-sourceCraig Jennings2026-05-271-4/+5
| | | | | | | | | | Pearl now treats Linear sources (favorites, custom views, ad-hoc filters, saved queries) as one surface. `pearl-pick-source` is the unified picker: it lists every Linear favorite alongside every locally saved query, and dispatches by kind. View / project / cycle / label / user favorites resolve to runnable filter or view sources; issue / document / dashboard favorites open in the browser since they aren't lists. The filter compiler grows id-based forms (`:label-id` and `:assignee-id`), so favorites that name a Linear entity (a user, a label) compile to rename-proof queries rather than fragile name matching. The ad-hoc builder gets a matching member-assignee option ("me / member / any"), so a one-off "what's a teammate working on?" doesn't require building a saved query first. Two pre-existing builder bugs surfaced during verify and got fixed in the same pass. The builder previously passed display names straight to the compiler, but `:team` compiles to `team.key.eq` and `:project` to `project.id.eq`, so Linear rejected every builder run that pinned a team or project. Now the builder resolves the picked name to its key / id before assembling. And `pearl-list-issues-by-project` previously injected `:assignee :me` into the filter, hiding every teammate's work in the project. It now returns the whole project. The save prompt on the builder was rewritten so the local-only nature is unambiguous, and the README grew a Sources section plus a keymap-table entry for `f s`.
* docs: add the issue-sources spec (favorites, views, filters)Craig Jennings2026-05-271-0/+143
| | | | I drafted a design for unifying Pearl's fetch surface around sources: read the viewer's Linear favorites and run them by type-dispatch behind one pearl-pick-source picker, with the per-dimension filters as the primitives favorites resolve into and the ad-hoc builder as the exploratory fallback. It fixes the by-project me-lock and resolves label and user favorites by stable id, which needs two small filter-compiler forms (:label-id, :assignee-id). The Linear favorites schema is verified against the live API and Codex's review is incorporated, so the spec is Ready.
* docs: supersede the keyword spec's sync-back half with save-model-v2Craig Jennings2026-05-261-1/+5
| | | | The spec predates save-model-v2, which removed the org-sync push path its "sync-back: cycled keyword → Linear state" section relied on. Mark that section obsolete: state now reconciles at save through pearl--save-state-field, and the cycled-keyword → state-id resolution is the c8 task. Record the invariant c8 leans on — every fresh render sets the keyword to slugify(state-name) — and the one stale-render caveat a refresh closes.
* docs: add the ticket save-model v2 specCraig Jennings2026-05-252-0/+229
| | | | | | The v2 spec folds the structured fields — priority, state, assignee, labels — onto the save engine and removes immediate push, so the package has one write path instead of two. It extends the shipped v1 save-model spec and supersedes the "field setters stay immediate" half of v1 decision 8. I incorporated two Codex review rounds. I also added the supersession pointer to the top of the v1 spec so the v1/v2 relationship is legible from either file.
* docs: record the WorkflowState.position verification in the TODO-keywords specCraig Jennings2026-05-251-2/+6
| | | | I ran the live check the spec named as its one prerequisite: WorkflowState.position exists and returns a float (Dev Review 947.14, In Progress 2, Planning 0), so the per-team position ordering works as designed and the no-position fallback is unnecessary. Recorded it in the status and prerequisites and folded in the round-5 review (the only finding was that status/rubric hygiene). The spec is now cleanly READY with no open prerequisites.
* docs: incorporate the spec reviews and move four specs to ReadyCraig Jennings2026-05-254-104/+411
| | | | | | | | | | | | I folded the Codex reviews into four draft specs and brought each to Ready. The reviews surfaced real failure modes, not nits, so the changes are substantive. The comment-deletion spec is Ready: I verified Linear's commentDelete contract against the live API. The mutation is commentDelete(id: String!) returning success, and the comment is not found immediately after, so there's no restore path and undo is impossible. The dirty-local-comment policy is allow-with-discard-wording, the prompt names both the remote delete and the local removal, and the d c keymap / K transient slots are settled. The multi-account spec is Ready. The original "set the globals on switch and let downstream stay oblivious" model leaked across accounts in the async code, so it's redesigned around an account-context layer: dispatch-time context snapshots, #+LINEAR-ACCOUNT file ownership with wrong/unmarked-buffer guards, a runtime (not persisted) active-account, a mode-line indicator, and an exact startup rule. The five safety calls are settled as the safer defaults. The sort-order spec is Ready. Client-side sort now moves whole issue subtrees by LINEAR-ID byte-for-byte rather than reparsing and rewriting, which would have dropped unsaved edits. Header persistence is atomic with the reorder, and Custom Views refuse server-side sort in v1 since customView.issues has no verified orderBy. The labels-as-org-tags spec is Ready: collisions render the shared tag once with the drawer authoritative, Pearl owns the whole issue-heading tag set, manual tag edits are overwritten and never pushed, and Unicode alphanumerics are preserved.
* docs: title-case the section headings in the save-model specCraig Jennings2026-05-241-8/+8
|
* docs: add the ticket-save-model and multi-account specsCraig Jennings2026-05-242-0/+375
| | | | Two design specs for upcoming work. ticket-save-model-spec covers the unified "save the ticket" model — diff title/description/comments against their provenance hashes and push only what changed, with a sequential conflict-aware engine and an opt-in keymap — and went through two review rounds (Codex). multi-account-spec covers switching between work and personal Linear workspaces over the existing globals, with auth-source credentials and per-account cache isolation; it's still pre-review draft.
* feat: pearl — manage Linear issues from org-modeCraig Jennings2026-05-247-0/+1034
Pearl fetches Linear issues into an org file and syncs edits back. It covers list / custom views / saved queries, per-issue and bulk rendering with comments inline, conflict-aware sync of descriptions, titles, and comments, field commands for priority / state / assignee / labels, and a transient dispatch menu. The render folds to a scannable outline and nests issues under a sortable parent. Based on and inspired by Gael Blanchemain's linear-emacs.