Scope: Phase 1. Comments on dockets only (content_type = DOCKET, value 1).
Tier map: A = SYS_ADMIN, B = ADMIN, C = EDITOR. Policy: src/constants/comment_permissions.py. Web mirror: keystone-web/src/features/comments/permissions.ts.
Action
SYS_ADMIN
ADMIN
EDITOR
AI JWT
CREATE
yes
yes
yes
yes
EDIT_OWN
yes
yes
yes
n/a
EDIT_ANY
yes
no
no
n/a
DELETE_OWN
yes
yes
yes
n/a
DELETE_ANY
yes
no
no
n/a
RESOLVE_ANY
yes
yes
no
n/a
REOPEN_ANY
yes
yes
no
n/a
AI JWT: create only; other calls FORBIDDEN. AI-authored comments: no PATCH; delete needs DELETE_ANY (system admin).
API: handled routes return HTTP 200; failures use JSON status failure and error.code.
Backend create POST /comments
ID
Case
Expect
TC-BE-C01
Human JWT, valid docket, body_type=1 (TEXT), body within limit
HTTP 200 success; author_type human, status OPEN, created_by matches sub; row + audit created
TC-BE-C02
Same, body_type=2 (PLATE), valid JSON
HTTP 200 success; body_type 2 echoed
TC-BE-C03
AI JWT, valid docket
HTTP 200 success; author_type ai, created_by matches sub
TC-BE-C10
Empty body
HTTP 200 failure; INVALID_PARAMETERS
TC-BE-C11
Body over 10000 chars
HTTP 200 failure; INVALID_PARAMETERS
TC-BE-C12
Invalid or unknown content_type
HTTP 200 failure; INVALID_PARAMETERS
TC-BE-C13
DOCKET but content_id not valid ObjectId hex
HTTP 200 failure; INVALID_PARAMETERS
TC-BE-C14
Valid hex, docket missing for course_id
HTTP 200 failure; RESOURCE_NOT_FOUND
TC-BE-C15
AI JWT calls PATCH, resolve, reopen, delete
HTTP 200 failure; FORBIDDEN each
Backend list GET /comments
ID
Case
Expect
TC-BE-L01
Default status=open
Only OPEN, not soft-deleted
TC-BE-L02
status=resolved / all
Matches contract
TC-BE-L03
author_type human / ai
Filters on stored value
TC-BE-L04
sort_order asc vs desc
Stable order (created_at + _id tie-break per repo)
TC-BE-L05
next_cursor / prev_cursor
No dupes or skips
TC-BE-L06
Soft-deleted comment
Excluded
TC-BE-L07
limit 1..100
Respected; invalid limit rejected at validation
Backend get one GET /comments/{id}
ID
Case
Expect
TC-BE-G01
Existing visible comment
HTTP 200 success; response shape + author_name when resolvable
TC-BE-G02
Wrong course_id
HTTP 200 failure; RESOURCE_NOT_FOUND
TC-BE-G03
Deleted or hidden
HTTP 200 failure; RESOURCE_NOT_FOUND
Backend patch PATCH /comments/{id}
ID
Case
Expect
TC-BE-P01
EDITOR patches own human comment
HTTP 200 success; body updated; audit edited
TC-BE-P02
SYS_ADMIN patches another human comment
HTTP 200 success (EDIT_ANY)
TC-BE-P10
EDITOR patches someone else’s
HTTP 200 failure; FORBIDDEN
TC-BE-P11
ADMIN patches someone else’s
HTTP 200 failure; FORBIDDEN
TC-BE-P12
Any role patches AI-authored comment
HTTP 200 failure; FORBIDDEN
TC-BE-P13
Patch DELETED comment
HTTP 200 failure; pin RESOURCE_NOT_FOUND vs OPERATION_NOT_ALLOWED
TC-BE-P14
Empty or oversize body
HTTP 200 failure; INVALID_PARAMETERS
TC-BE-P15
body_type in PATCH body
Ignored or rejected (pin contract)
Backend resolve and reopen
ID
Case
Expect
TC-BE-R01
EDITOR tries resolve on OPEN
HTTP 200 failure; FORBIDDEN
TC-BE-R02
ADMIN resolves OPEN
HTTP 200 success; RESOLVED; audit resolved
TC-BE-R03
SYS_ADMIN resolves OPEN
HTTP 200 success; same
TC-BE-R04
Resolve already RESOLVED
HTTP 200 success; idempotent (no second audit row per current repo)