feat(jobs): the restricted sheets for a child reader (§7) #231

Merged
nalum merged 1 commit from feat/restricted-job-sheets into main 2026-08-26 18:35:22 +00:00
Owner

Closes #178. The last piece of the job sheet.

Which shape a reader gets comes from the per-verb matrix, not a role bucket, so
a Child gets one of two: §7a when they own the job, §7b when they do not. Every
control is gated on the specific verb it performs, and where a verb is denied the
control is absent, never disabled and greyed.

A live permission leak, found while building it. The shared jobOptions()
gated Make it part of another job on canSetParent — which a Child has — and
never on manager. So a Child owning a repeat-free, sub-job-free job with a
destination available got exactly the one-entry ⋯ menu §7a exists to prevent.
Fixed on both surfaces.

Two more gating faults went with it: the sub-job expansion chevron rendered
unconditionally, so §7b could open an expansion it may not use, and the footer's
Waiting on Ali line showed for readers whose footer §7b closes to two states.

It also found two Android tests synthesising an impossible caps object — a Child
holding canDeleteItem and canSetRepeat — to isolate an unrelated condition.
Those verbs never co-occur with a non-manager in the real matrix, so the tests
were asserting against a state that cannot exist. Rebased onto a manager base.

One capability goes with §7a's blessing, and it is worth naming. The Who
chevron was a child-owner's only route to putting a job back to Anyone. §7a
says subtract it, so it is gone from this sheet. The server still allows the
put-back (assignuser.go), and the design offers no other affordance for an
owned job — take-and-put-back is scoped to the up-for-grabs pool in §11.C. So a
child who no longer wants their own job now has nowhere to say so. Either §7
gains an exception or the pool's put-back widens; flagged rather than invented.

One ambiguity deferred rather than guessed. §5's "the doer sheet, §7" could
be read as denying a Child reader the progress block. The shipped conformance
fixtures already pin it present for Child readers, and tested shipped behaviour
is the stronger authority than a possibly-stale cross-reference. Reported, not
changed.

Eight new golden cases ride with the code; no existing case moved.

🤖 Generated with Claude Code

Closes #178. The last piece of the job sheet. Which shape a reader gets comes from the per-verb matrix, not a role bucket, so a Child gets one of two: §7a when they own the job, §7b when they do not. Every control is gated on the specific verb it performs, and where a verb is denied the control is **absent**, never disabled and greyed. **A live permission leak, found while building it.** The shared `jobOptions()` gated `Make it part of another job` on `canSetParent` — which a Child has — and never on manager. So a Child owning a repeat-free, sub-job-free job with a destination available got exactly the one-entry `⋯` menu §7a exists to prevent. Fixed on both surfaces. Two more gating faults went with it: the sub-job expansion chevron rendered unconditionally, so §7b could open an expansion it may not use, and the footer's `Waiting on Ali` line showed for readers whose footer §7b closes to two states. It also found two Android tests synthesising an impossible caps object — a Child holding `canDeleteItem` and `canSetRepeat` — to isolate an unrelated condition. Those verbs never co-occur with a non-manager in the real matrix, so the tests were asserting against a state that cannot exist. Rebased onto a manager base. **One capability goes with §7a's blessing, and it is worth naming.** The Who chevron was a child-owner's only route to putting a job back to `Anyone`. §7a says subtract it, so it is gone from this sheet. The server still allows the put-back (`assignuser.go`), and the design offers no other affordance for an *owned* job — take-and-put-back is scoped to the up-for-grabs pool in §11.C. So a child who no longer wants their own job now has nowhere to say so. Either §7 gains an exception or the pool's put-back widens; flagged rather than invented. **One ambiguity deferred rather than guessed.** §5's "the doer sheet, §7" could be read as denying a Child reader the progress block. The shipped conformance fixtures already pin it present for Child readers, and tested shipped behaviour is the stronger authority than a possibly-stale cross-reference. Reported, not changed. Eight new golden cases ride with the code; no existing case moved. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(jobs): the restricted sheets for a child reader (§7)
Some checks failed
check / commits (pull_request) Successful in 16s
check / go (pull_request) Successful in 3m0s
check / report (pull_request) Successful in 4s
check / web (pull_request) Failing after 3m41s
android / build (pull_request) Successful in 6m41s
android / report (pull_request) Successful in 3s
4a0a36fb74
Admin and Member always got the full job sheet (§5); a Child got the
same rows because every affordance was already gated on the matrix,
but three gaps let a control through that §7 says must be absent:

- The Who row kept its chevron for a Child who owns the job, limited
  to themselves and Anyone (ADR-0014's put-back-your-own-job taker
  half). §7a blesses subtracting it: the server still allows the put-
  back (assignuser.go's putBackOwn branch), but the sheet stops
  offering the row that reaches it. A Child who no longer wants a job
  they own now has no way to give it back from this sheet — that is
  the capability this citation removes. The unowned-job exception
  (§11.C's taker mechanism) is unaffected: Who still chevrons there.
- jobOptions()'s canMakePart checked canSetParent (Child-allowed) but
  not isManager, so a Child owning a repeat-free, sub-job-free job
  with a destination available got a one-entry ⋯ menu. §7a: "⋯ —
  absent, always" — a one-item menu is worse than none.
- The sub-job expansion chevron rendered unconditionally, so §7b's
  "no expansion chevrons" reader could still open a sub-job's own
  Who/Worth/note/act rows. Gated on canEditJob, same test as every
  other owner/manager-only affordance on this sheet.

Two smaller footer/row fixes follow from the same reading: a sub-
job's own Who row never carves the unowned-taker exception the JOB's
does (§7a: "sub-jobs, their Who and Worth as read rows" — no
exception written, unlike the job-level bullet) — the take/put-back
mechanism lives at the job/pool level (§11.C), not nested inside a
sub-job's expansion. And the "Waiting on Ali" footer line is written
for a reader who can act on the job (owner, manager, or an unowned
job); §7b's footer is a closed two-state list — the reader's own next
sub-job, or no footer — so the line is now gated on canEditJob too.

Two existing Android §6 specs synthesized a "Child" caps object with
canDeleteItem/canSetRepeat forced on to isolate canMakePart's repeat
condition from its role gate — a shape the real matrix never
produces (both are Admin/Member-only rows). Rebased them on a
manager caps object varying only canSetRepeat, since isManager is now
its own hard gate.

New RTL/Compose specs pin every subtraction, the unowned-job
exception, the inert-vs-tickable sub-job split, and each footer
state; two new conformance/render-job-sheet.json cases (shoot: true)
carry matching goldens on both surfaces.

Test report

Suite Tests Result Skipped
Unit 1435 ✅ pass 1
Integration 131 ✅ pass —

Coverage: 27.0%

Updated by the check workflow · commit ed14823165

<!-- ci-test-report --> ## Test report | Suite | Tests | Result | Skipped | | --- | --: | --- | --: | | Unit | 1435 | ✅ pass | 1 | | Integration | 131 | ✅ pass | — | **Coverage:** 27.0% <sub>Updated by the check workflow · commit ed148231654a69dadc8dfa96e800e545478f7da9</sub>

Android test report

Suite Tests Result Skipped
Unit (debug) ❌ 1 failed

Coverage:

Updated by the android workflow · commit ed14823165

<!-- android-test-report --> ## Android test report | Suite | Tests | Result | Skipped | | --- | --: | --- | --: | | Unit (debug) | | ❌ 1 failed | | **Coverage:** <sub>Updated by the android workflow · commit ed148231654a69dadc8dfa96e800e545478f7da9</sub>
nalum force-pushed feat/restricted-job-sheets from 4a0a36fb74
Some checks failed
check / commits (pull_request) Successful in 16s
check / go (pull_request) Successful in 3m0s
check / report (pull_request) Successful in 4s
check / web (pull_request) Failing after 3m41s
android / build (pull_request) Successful in 6m41s
android / report (pull_request) Successful in 3s
to ed14823165
Some checks failed
check / commits (pull_request) Successful in 15s
check / go (pull_request) Successful in 3m10s
android / build (pull_request) Failing after 5m28s
check / web (pull_request) Successful in 4m17s
check / report (pull_request) Successful in 4s
android / report (pull_request) Successful in 4s
android / report (push) Has been cancelled
android / build (push) Has been cancelled
check / commits (push) Has been cancelled
check / go (push) Has been cancelled
check / report (push) Has been cancelled
check / web (push) Has been cancelled
tag / tag (push) Has been cancelled
2026-08-26 07:03:48 +00:00
Compare
nalum changed target branch from feat/job-create-flow to main 2026-08-26 18:35:18 +00:00
nalum merged commit ed14823165 into main 2026-08-26 18:35:22 +00:00
nalum deleted branch feat/restricted-job-sheets 2026-08-26 18:35:23 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
eagraiclainne/app!231
No description provided.