feat(lessons): both records show the link in the web UI (milestone 440 step 7, #4635)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 57s
CI & Build / integration (push) Successful in 1m9s
CI & Build / Python tests (push) Successful in 1m53s
CI & Build / Build & push image (push) Successful in 41s

The lesson page gains an "Instance of" panel that holds one of three answers.
Unjudged is the fall-through, so it is stated rather than left blank:
- the rule(s) it was judged an instance of;
- "No rule — <why>";
- "Not yet judged".

Suggested links show what they rest on (distinct situations, and projects
when more than one), with Confirm / Not an instance for a reader who can
write. Rejected links stay listed with their reason.

The rule slide-over lists the lessons that are instances of it, plus the
suggestions waiting on a judgment. Each entry links through to the other
record, and kind and state wear the existing .rule-chip.

The write check moves to utils/permission.ts. The copy on the snippet page
looked for "edit", which the server never sends, so shared editors saw a
read-only page (#4640 "The snippet page hid its edit controls from shared
editors"). Guards pin the client's unions and write levels to the
service's, the model's and access.py's own values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 15:46:27 -04:00
co-authored by Claude Opus 5.5
parent 6d3dca0af5
commit 75cefe60e4
7 changed files with 368 additions and 6 deletions
+12
View File
@@ -0,0 +1,12 @@
/** Whether the reader may change a record, from the `permission` its read
* carried. The levels are services/access.py's PERMISSION_RANK — viewer,
* editor, admin, owner — and absent means the reader owns it: the server
* labels a record only when it reached the reader through a share. This only
* decides which controls a page shows; the server enforces the write. Kept in
* one place so every page agrees, with each other and with the server, about
* who can edit. */
export const WRITE_LEVELS: readonly string[] = ["editor", "admin", "owner"];
export function canWriteRecord(permission: string | undefined): boolean {
return permission === undefined || WRITE_LEVELS.includes(permission);
}