#+TITLE: Pearl Scripts #+AUTHOR: Craig Jennings * Purpose This file is the single home for documentation about the helper scripts under =scripts/=. These scripts aren't part of pearl.el itself. They're development and setup tooling that sits alongside the package: workspace seeding, coverage reporting, and whatever else lands here later. Each script gets its own heading below with what it's for, how to run it, and any notes worth knowing before you use it. When you add a script, add a heading here too. * seed_pearl_workspace.py ** Purpose Brings a Linear workspace up to Pearl's dogfooding conventions in one run: - a "Pearl" team whose columns are the six Agile states (Icebox, Triage, Backlog, In Progress, Done, Canceled), - a "Pearl" project inside that team, - three Custom Views: Pearl Open Issues, Pearl Icebox, Pearl Inbox. It's idempotent. It reconciles against whatever's already there by name, so a second run changes nothing. It's reusable for us and for anyone standing up the same setup. ** Usage The script reads the Linear API key from the =LINEAR_API_KEY= environment variable. #+begin_src bash # preview the plan without changing anything LINEAR_API_KEY=lin_api_... python3 scripts/seed_pearl_workspace.py --dry-run # run it LINEAR_API_KEY=lin_api_... python3 scripts/seed_pearl_workspace.py #+end_src Run the tests with pytest: #+begin_src bash pytest scripts/tests/test_seed_pearl_workspace.py #+end_src The personal key lives in a gitignored =apikey.txt= at the repo root, never in the repo. A convenient way to pass it: #+begin_src bash LINEAR_API_KEY="$(tr -d '[:space:]' < apikey.txt)" python3 scripts/seed_pearl_workspace.py #+end_src ** Notes - *Reconcile, not create-from-scratch.* Linear's =teamCreate= seeds its own default columns, and Linear dedups state names case-insensitively, so a naive "create all six" run collides with the defaults. The script instead renames the defaults into the six Agile columns (keeping Backlog, In Progress, Done, Canceled, and repurposing the unstarted Todo as Triage) and creates only the genuinely-new Icebox. - *It never archives.* A Linear team must keep at least one unstarted state, and the Duplicate state is reserved, so neither can be removed by API. The script leaves any column it doesn't claim (Duplicate, which Linear hides in the board UI) and reports it rather than failing. - *Positions are set in a second pass with distinct nonzero values.* Linear ignores a 0.0 position and appends a freshly-created state at a high one, so the script updates positions after the states exist. - *Grouping isn't set at view-create time.* Linear's =CustomViewCreateInput= has no grouping field, so Pearl Open Issues is created with its filter only. Group it by category in pearl with =pearl-set-grouping=, or in the Linear UI. - *Triage is the unstarted type* (it took over the default Todo). pearl's grouped view orders sections by state type and then position, so Triage lists after the backlog columns in pearl even though the Linear board keeps your column order. - *New issues default to Triage, not Backlog.* The script points the team's default new-issue state at Triage (the intake column) after the columns reconcile, so a fresh issue lands there. * import_org_backlog.py ** Purpose Imports an org-mode backlog into the Pearl Linear workspace. It reads the level-2 =TODO=/=DOING= headings under the "Pearl Open Work" section and creates one Linear issue per task, carrying the priority, tags, and body across. The mapping: - priority cookie =[#A]= .. =[#D]= becomes Linear priority Urgent .. Low, - a =[#D]= "someday" task lands in Icebox, everything above it in Backlog, - tags become labels (missing ones are created); the =discuss=, =next=, =cleanup=, and =pearl= workflow markers are dropped rather than turned into labels, - the heading body becomes the issue description, minus the property drawer and any sub-entries. It's idempotent: a task whose title already exists is skipped, so a re-run only fills in what's missing. The two umbrella headings (the dogfooding parent and the manual-testing checklist) and everything under the Resolved section are left out. ** Usage The script reads the Linear API key from =LINEAR_API_KEY= and takes the org file as its argument. #+begin_src bash # preview the tasks without creating anything python3 scripts/import_org_backlog.py todo.org --dry-run # run it LINEAR_API_KEY="$(tr -d '[:space:]' < apikey.txt)" python3 scripts/import_org_backlog.py todo.org #+end_src Run the tests with pytest: #+begin_src bash pytest scripts/tests/test_import_org_backlog.py #+end_src ** Notes - *Dry-run first.* =--dry-run= parses the file and prints each task with its target state and surviving labels, plus the full label set it would create. It touches the network only on a real run, so it's the safe way to confirm scope before importing. - *Idempotent by title.* The skip check matches on exact issue title. Editing a task's title in the org file and re-running creates a second issue rather than updating the first, so treat titles as stable once imported. - *Tags are matched case-insensitively.* An existing =Bug= label is reused for a =:bug:= tag; only genuinely new labels are created. * coverage-summary.el ** Purpose The batch helper behind =make coverage-summary=. It parses the SimpleCov JSON that =make coverage= writes and prints a terminal summary: per-file covered and total executable lines with a percent (worst-covered first), plus a line-weighted project figure. Without it, =make coverage= reports little more than the HTML report's file size. ** Usage #+begin_src bash make coverage # run tests with coverage, then print the summary make coverage-summary # just re-print the summary from the last report #+end_src It's invoked from the Makefile (=coverage= and =coverage-summary= targets) with =emacs -batch -L scripts -l coverage-summary=, so there's no need to call it directly.