You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Member detail page — open one person's duties without asking them #13
The screen that removes the "can you send me a status update" mail. A manager opens a person and sees everything, having entered nothing.
Blocked-by: #8 (the scopes that decide what they may see).
Files you own
src/pages/member.page.ts (new), pushed into dulyPages in src/pages/index.ts
a nav entry in the Team group of src/apps/duly.app.ts
test/member-page.test.ts (new)
Content, in reading order
Header — name, position, unit, manager.
Right now — open tasks, of which late, of which not moving. Three numbers, the third emphasised.
This period — their governed duties and the state of each. Grouped by frequency so a monthly rhythm reads differently from an annual one.
Standing duties — listed, with no tick affordance anywhere near them. They never complete; a control that implies otherwise is a bug.
Recent activity — last 20 task events by last_update_at.
Assigned to them — tasks with source: 'assigned', showing who assigned each.
Not on this page
Their work log.duly_log_entry is readScope: 'own' for every position; the only widening is a record's own visibility: 'manager' flag via a sharing rule. Do not query the object here at all — not filtered, not counted, not "0 entries". A manager seeing a log count learns the log exists and that it is being watched, which is enough to stop people keeping one.
Any editable control. Read-only page. A manager's only write in this product is assigning, and the assign action lives on its own object.
Any comparison to other people. No percentile, no "vs team average", no rank. Their own trend over time is fine and useful; a position in a distribution is a performance score.
Rules
Everything must read correctly for a manager whose scope resolves through the ADR-0057 depths. In this open-edition checkout those fall back to owner-only, so the page will look empty for anyone but yourself — expected, not a bug (see Security model — positions, permission sets, sharing rules #8). Assert authored scopes and filters in tests rather than resolved rows.
Standing duties come from duly_duty where form = 'standing'; they have no tasks by construction, so do not join through tasks to find them.
Acceptance
the page renders from duly_task / duly_duty only; grep the file — no reference to duly_log_entry anywhere
worktree: ../duly-issue-13 off origin/main @ dd3c619
Files I intend to own: src/pages/member.page.ts (new), src/pages/index.ts, the Team nav group in src/apps/duly.app.ts, test/member-page.test.ts (new), plus zh-CN/en entries for the new page + nav labels (shared with #107/#108/#109 — whoever lands second merges main and re-runs the gates).
#8 landed (PR #53), so the pm:blocked premise is lifted; I'll verify that against origin/main before writing code.
{
"issue": 13,
"status": "done",
"branch": "claude/issue-13-member-page",
"pr": "https://github.com/objectstack-ai/duly/pull/112",
"premise_still_valid": true,
"summary": "`src/pages/member.page.ts` ships `duly_member`, a read-only `type: 'record'` page over `sys_user` carrying the card's six sections in reading order, wired into `dulyPages` and reachable from a new `People` entry in the Team nav group. The three forbidden things are pinned by `test/member-page.test.ts` (25 assertions): the work-log object's machine name is greped out of the page source AND walked out of the parsed page, no writing component type is declared, every highlight chip is `readonly`, and no aggregate or peer comparison exists. The card asked for the page schema to be MEASURED — seven gaps were found against installed 17.2.0 and all seven are filed at objectstack-ai/objectui rather than worked around. Two were found only in the browser. ONE LINE OF THE CARD IS NOT DELIVERED: 'showing who assigned each' is unauthorable — the assigner is one lookup hop from the task (related-list columns cannot cross a lookup) and the alternative binding on `duly_assignment.assignees` was authored, run, and refused by the driver with 400 INVALID_FILTER because `RelatedList` composes its parent filter as bare equality and `assignees` is `multiple: true`. That list was deleted rather than left rendering an error; the task list carries the `assignment` column, one click from the answer. Separately, an auto-appended discussion panel with a comment box, reactions and threaded replies was found on the rendered page — `sys_user` is a locked platform object and the renderer's `disableDiscussion` opt-out is not a `PageSchema` key, so the page now declares an explicit `record:discussion` with all three writes off, which is the only authorable way to shut it (verified: 0 composers on the page). Reached `main` at 56b6aae (#110) via merge and re-ran all four gates after it; also converged onto #110's `node:fs` shim, deleting this branch's Vite `?raw` declaration rather than shipping two ways to read a file in a test.",
"tests": "All four gates re-run ON THE FINAL PUSHED COMMIT e399e78 (`git status --porcelain` = 0 lines), each captured with the exit code taken before any pipe:\n\n pnpm validate -> 0 \"✓ Validation passed (483ms)\" · \"UI: 1 Apps 5 Views 1 Pages 1 Dashboards 6 Actions\"\n one warning, the expected open-edition state naming @objectstack/security-enterprise (AGENTS.md rule 7 — not silenced)\n pnpm typecheck -> 0 tsc --noEmit, no diagnostics\n pnpm test -> 0 \"Test Files 31 passed (31)\" · \"Tests 775 passed (775)\"\n pnpm build -> 0 \"✓ Build complete (1003ms)\"\n\nABLATION — both new guards were proven able to fail. Script carried `trap '<restore>' EXIT INT TERM`; each mutation's anchor was asserted present exactly once by the editing script BEFORE the edit (a python `assert s.count(old) == 1`, so a zero-hit edit aborts instead of reporting a green no-op); no rebuild step applies (vitest reads src directly, no dist in the resolution path).\n A. injected a `duly_log_entry` related list -> on-disk proof `grep -c duly_log_entry` = 1 -> \"Tests 3 failed | 22 passed (25)\", red on `is not named anywhere in the source`, `binds no component to it`, `reads in the order the card asks for`.\n B. dropped `readonly: true` from one highlight chip -> \"Tests 1 failed | 24 passed (25)\", red on `marks every highlight chip readonly` and nothing else. (The shell-echoed grep counts for leg B were mangled by heredoc quoting and are NOT evidence; the anchor assertion and the targeted red are.)\n RESTORE leg verified byte-identical: `diff -q member.page.ts.orig src/pages/member.page.ts` => identical, `grep -c 'readonly: true'` = 5, `grep -c duly_log_entry` = 0.\n\nBROWSER — `pnpm demo -- --port 4913` (own port; killed by recorded PID only, never by name; port confirmed closed afterwards). 465 seeded rows, signed in as the demo admin, Playwright 1.56.1 at 1400x1000 @2x. Two seeded people (Rosa Delgado; Priya Raman, who has a standing duty). Measured on the rendered page: all six sections present with real data; counts Open 5 / Late 1 / Not moving 1, Governed duties 3 sorted Fortnightly-Monthly-Monthly, Held permanently 1, Last touched 28, Assigned work 2; `duly_log_entry`/'Work log'/'percentile'/'rank'/'vs team' all ABSENT; 0 textareas or contenteditables (no comment box); 0 console errors and 0 4xx on /api after the fix. The ADR-0065 emphasis was confirmed by computed style, not by eye: objectui emitted `.os-s-right_now_stalled { border-left: 3px solid #8C6512 !important; padding-left: 12px !important; }` and the node computes `3px rgb(140, 101, 18)` / `padding-left: 12px`. Page-assignment collision measured live: `GET /api/v1/meta/page` returns BOTH `duly_member` and the platform's `sys_user_detail` for `sys_user`, `duly_member` first, and it wins by list position and a stable sort only. Screenshots pushed to throwaway branch `shots-issue-13` and embedded in the PR.",
"open_questions": [],
"out_of_scope_findings": [
"filed as objectstack-ai/objectui#7297: no record-context token for filter values — CONTEXT_TOKENS is {current_user_id} + {current_org_id}, both the VIEWER, so element:number cannot be scoped to the record and would silently count the whole org",
"filed as objectstack-ai/objectui#7298: two page keys the renderer reads that PageSchema (a strictObject) does not declare — `priority` in usePageAssignment and `disableDiscussion` in RecordDetailView; both are unwritable, and the second leaves a comment box on a read-only page",
"filed as objectstack-ai/objectui#7299: record:related_list cannot bind to a multi-value relationship field — RelatedList composes bare equality, the driver refuses with 400 INVALID_FILTER and prescribes $contains, which is unreachable from RecordRelatedListProps",
"filed as objectstack-ai/objectui#7300: a page cannot declare its related lists read-only — '+ New'/edit/delete are host-resolved from the CHILD object, so this read-only page renders 8 '+ New' buttons it has no key to decline",
"filed as objectstack-ai/objectui#7301: record:related_list has no groupBy, and its columns cannot cross a lookup — the two limits that kept 'grouped by frequency' and 'who assigned each' from being written",
"NOT filed, recorded here for PM triage: `PageTranslation.components` (@objectstack/spec 17.2.0) declares title|description|label|placeholder|emptyText|submitLabel and has NO key for `content` — the one string `element:text` renders. Its own comment says the key face was derived from the copy props components declare and names its two deliberate exclusions (`help`, `subtitle`), so this reads as a missed one. The blessed alternative (an inline `{en,'zh-CN'}` I18nLabel map, which the platform's own sys-user.page.ts uses) is what this page does, and the 16 strings are declared in the walk's untranslatable ledger with that reason and pinned by count. It belongs at objectstack-ai/objectstack, not objectui, which is why I did not file it under the card's stated destination without a PM call."
]
}
Supersedes the previous comment on this issue. Two things the GitHub body sanitizer ate there, both worth knowing about: the leading <!-- os-dev-report --> HTML-comment marker was stripped entirely (so that comment is invisible to a marker scan), and inside the evidence the short <restore> fragment was eaten from trap '...' EXIT INT TERM, which made the ablation's restore leg read as an empty trap. Same mechanism both times — short angle-bracket fragments do not survive, backticks included. This comment leads with the marker as literal text and spells the trap out.
{
"issue": 13,
"status": "done",
"branch": "claude/issue-13-member-page",
"pr": "https://github.com/objectstack-ai/duly/pull/112",
"premise_still_valid": true,
"summary": "`src/pages/member.page.ts` ships `duly_member`, a read-only `type: record` page over `sys_user` carrying the card's six sections in reading order, wired into `dulyPages` and reachable from a new `People` entry in the Team nav group. The three forbidden things are pinned by `test/member-page.test.ts` (25 assertions): the work-log object's machine name is greped out of the page source AND walked out of the parsed page, no writing component type is declared, every highlight chip is `readonly`, and no aggregate or peer comparison exists. The card asked for the page schema to be MEASURED — seven gaps were found against installed 17.2.0 and all seven are filed at objectstack-ai/objectui rather than worked around. Two were found only in the browser. ONE LINE OF THE CARD IS NOT DELIVERED: 'showing who assigned each' is unauthorable — the assigner is one lookup hop from the task (related-list columns cannot cross a lookup) and the alternative binding on `duly_assignment.assignees` was authored, run, and refused by the driver with 400 INVALID_FILTER because `RelatedList` composes its parent filter as bare equality and `assignees` is `multiple: true`. That list was deleted rather than left rendering an error; the task list carries the `assignment` column, one click from the answer. Separately, an auto-appended discussion panel with a comment box, reactions and threaded replies was found on the rendered page — `sys_user` is a locked platform object and the renderer's `disableDiscussion` opt-out is not a `PageSchema` key, so the page now declares an explicit `record:discussion` with all three writes off, which is the only authorable way to shut it (verified: 0 composers on the page). Reached `main` at 56b6aae (#110) via merge and re-ran all four gates after it; also converged onto #110's `node:fs` shim, deleting this branch's Vite `?raw` declaration rather than shipping two ways to read a file in a test.",
"tests": "All four gates re-run ON THE FINAL PUSHED COMMIT e399e78 (`git status --porcelain` = 0 lines), each exit code captured before any pipe:\n\n pnpm validate -> 0 '✓ Validation passed (483ms)' · 'UI: 1 Apps 5 Views 1 Pages 1 Dashboards 6 Actions'\n one warning, the expected open-edition state naming @objectstack/security-enterprise (AGENTS.md rule 7 — not silenced)\n pnpm typecheck -> 0 tsc --noEmit, no diagnostics\n pnpm test -> 0 'Test Files 31 passed (31)' · 'Tests 775 passed (775)'\n pnpm build -> 0 '✓ Build complete (1003ms)'\n\nABLATION — both new guards were proven able to fail. The script carried a restore trap on EXIT, INT and TERM (a `cp` of the pristine file back over the page, registered before the first mutation), so a foreground-cap SIGTERM mid-mutation could cost a reading but never leave a mutated tree. Each mutation's anchor was asserted present exactly once by the editing script BEFORE the edit (a python `assert s.count(old) == 1`), so a zero-hit edit aborts instead of reporting a green no-op. No rebuild step applies here: vitest reads src directly, with no dist in the resolution path.\n A. injected a `duly_log_entry` related list -> on-disk proof `grep -c duly_log_entry` = 1 -> 'Tests 3 failed | 22 passed (25)', red on `is not named anywhere in the source`, `binds no component to it`, `reads in the order the card asks for`.\n B. dropped `readonly: true` from one highlight chip -> 'Tests 1 failed | 24 passed (25)', red on `marks every highlight chip readonly` and nothing else. (The shell-echoed grep counts for leg B were mangled by heredoc quoting and are NOT evidence; the anchor assertion and the targeted red are.)\n RESTORE leg verified byte-identical: `diff -q member.page.ts.orig src/pages/member.page.ts` => identical, `grep -c 'readonly: true'` = 5, `grep -c duly_log_entry` = 0.\n\nBROWSER — `pnpm demo -- --port 4913` (own port; killed by recorded PID only, never by name; port confirmed closed afterwards). 465 seeded rows, signed in as the demo admin, Playwright 1.56.1 at 1400x1000 @2x. Two seeded people (Rosa Delgado; Priya Raman, who has a standing duty). Measured on the rendered page: all six sections present with real data; counts Open 5 / Late 1 / Not moving 1, Governed duties 3 sorted Fortnightly-Monthly-Monthly, Held permanently 1, Last touched 28, Assigned work 2; `duly_log_entry` / 'Work log' / 'percentile' / 'rank' / 'vs team' all ABSENT; 0 textareas or contenteditables (no comment box); 0 console errors and 0 4xx on /api after the fix. The ADR-0065 emphasis was confirmed by computed style, not by eye: objectui emitted `.os-s-right_now_stalled { border-left: 3px solid #8C6512 !important; padding-left: 12px !important; }` and the node computes `3px rgb(140, 101, 18)` / `padding-left: 12px`. Page-assignment collision measured live: `GET /api/v1/meta/page` returns BOTH `duly_member` and the platform's `sys_user_detail` for `sys_user`, `duly_member` first, and it wins by list position and a stable sort only. Screenshots pushed to throwaway branch `shots-issue-13` and embedded in the PR.",
"open_questions": [],
"out_of_scope_findings": [
"filed as objectstack-ai/objectui#7297: no record-context token for filter values — CONTEXT_TOKENS is {current_user_id} + {current_org_id}, both the VIEWER, so element:number cannot be scoped to the record and would silently count the whole org",
"filed as objectstack-ai/objectui#7298: two page keys the renderer reads that PageSchema (a strictObject) does not declare — `priority` in usePageAssignment and `disableDiscussion` in RecordDetailView; both are unwritable, and the second leaves a comment box on a read-only page",
"filed as objectstack-ai/objectui#7299: record:related_list cannot bind to a multi-value relationship field — RelatedList composes bare equality, the driver refuses with 400 INVALID_FILTER and prescribes $contains, which is unreachable from RecordRelatedListProps",
"filed as objectstack-ai/objectui#7300: a page cannot declare its related lists read-only — '+ New' / edit / delete are host-resolved from the CHILD object, so this read-only page renders 8 '+ New' buttons it has no key to decline",
"filed as objectstack-ai/objectui#7301: record:related_list has no groupBy, and its columns cannot cross a lookup — the two limits that kept 'grouped by frequency' and 'who assigned each' from being written",
"NOT filed, recorded here for PM triage: PageTranslation.components (@objectstack/spec 17.2.0) declares title|description|label|placeholder|emptyText|submitLabel and has NO key for `content` — the one string element:text renders. Its own comment says the key face was derived from the copy props components declare and names its two deliberate exclusions (help, subtitle), so this reads as a missed one. The blessed alternative (an inline en/zh-CN I18nLabel map, which the platform's own sys-user.page.ts uses) is what this page does, and the 16 strings are declared in the walk's untranslatable ledger with that reason and pinned by count. It belongs at objectstack-ai/objectstack, not objectui, which is why I did not file it under the card's stated destination without a PM call."
]
}
Activity
os-warren commentedon Sep 2, 2026
Claiming this card.
01SqkTcrxUFci7nqXdbBSe2pclaude/issue-13-member-page../duly-issue-13offorigin/main@dd3c619Files I intend to own:
src/pages/member.page.ts(new),src/pages/index.ts, the Team nav group insrc/apps/duly.app.ts,test/member-page.test.ts(new), pluszh-CN/enentries for the new page + nav labels (shared with #107/#108/#109 — whoever lands second mergesmainand re-runs the gates).#8 landed (PR #53), so the
pm:blockedpremise is lifted; I'll verify that againstorigin/mainbefore writing code.Generated by Claude Code
os-warren commentedon Sep 2, 2026
{ "issue": 13, "status": "done", "branch": "claude/issue-13-member-page", "pr": "https://github.com/objectstack-ai/duly/pull/112", "premise_still_valid": true, "summary": "`src/pages/member.page.ts` ships `duly_member`, a read-only `type: 'record'` page over `sys_user` carrying the card's six sections in reading order, wired into `dulyPages` and reachable from a new `People` entry in the Team nav group. The three forbidden things are pinned by `test/member-page.test.ts` (25 assertions): the work-log object's machine name is greped out of the page source AND walked out of the parsed page, no writing component type is declared, every highlight chip is `readonly`, and no aggregate or peer comparison exists. The card asked for the page schema to be MEASURED — seven gaps were found against installed 17.2.0 and all seven are filed at objectstack-ai/objectui rather than worked around. Two were found only in the browser. ONE LINE OF THE CARD IS NOT DELIVERED: 'showing who assigned each' is unauthorable — the assigner is one lookup hop from the task (related-list columns cannot cross a lookup) and the alternative binding on `duly_assignment.assignees` was authored, run, and refused by the driver with 400 INVALID_FILTER because `RelatedList` composes its parent filter as bare equality and `assignees` is `multiple: true`. That list was deleted rather than left rendering an error; the task list carries the `assignment` column, one click from the answer. Separately, an auto-appended discussion panel with a comment box, reactions and threaded replies was found on the rendered page — `sys_user` is a locked platform object and the renderer's `disableDiscussion` opt-out is not a `PageSchema` key, so the page now declares an explicit `record:discussion` with all three writes off, which is the only authorable way to shut it (verified: 0 composers on the page). Reached `main` at 56b6aae (#110) via merge and re-ran all four gates after it; also converged onto #110's `node:fs` shim, deleting this branch's Vite `?raw` declaration rather than shipping two ways to read a file in a test.", "tests": "All four gates re-run ON THE FINAL PUSHED COMMIT e399e78 (`git status --porcelain` = 0 lines), each captured with the exit code taken before any pipe:\n\n pnpm validate -> 0 \"✓ Validation passed (483ms)\" · \"UI: 1 Apps 5 Views 1 Pages 1 Dashboards 6 Actions\"\n one warning, the expected open-edition state naming @objectstack/security-enterprise (AGENTS.md rule 7 — not silenced)\n pnpm typecheck -> 0 tsc --noEmit, no diagnostics\n pnpm test -> 0 \"Test Files 31 passed (31)\" · \"Tests 775 passed (775)\"\n pnpm build -> 0 \"✓ Build complete (1003ms)\"\n\nABLATION — both new guards were proven able to fail. Script carried `trap '<restore>' EXIT INT TERM`; each mutation's anchor was asserted present exactly once by the editing script BEFORE the edit (a python `assert s.count(old) == 1`, so a zero-hit edit aborts instead of reporting a green no-op); no rebuild step applies (vitest reads src directly, no dist in the resolution path).\n A. injected a `duly_log_entry` related list -> on-disk proof `grep -c duly_log_entry` = 1 -> \"Tests 3 failed | 22 passed (25)\", red on `is not named anywhere in the source`, `binds no component to it`, `reads in the order the card asks for`.\n B. dropped `readonly: true` from one highlight chip -> \"Tests 1 failed | 24 passed (25)\", red on `marks every highlight chip readonly` and nothing else. (The shell-echoed grep counts for leg B were mangled by heredoc quoting and are NOT evidence; the anchor assertion and the targeted red are.)\n RESTORE leg verified byte-identical: `diff -q member.page.ts.orig src/pages/member.page.ts` => identical, `grep -c 'readonly: true'` = 5, `grep -c duly_log_entry` = 0.\n\nBROWSER — `pnpm demo -- --port 4913` (own port; killed by recorded PID only, never by name; port confirmed closed afterwards). 465 seeded rows, signed in as the demo admin, Playwright 1.56.1 at 1400x1000 @2x. Two seeded people (Rosa Delgado; Priya Raman, who has a standing duty). Measured on the rendered page: all six sections present with real data; counts Open 5 / Late 1 / Not moving 1, Governed duties 3 sorted Fortnightly-Monthly-Monthly, Held permanently 1, Last touched 28, Assigned work 2; `duly_log_entry`/'Work log'/'percentile'/'rank'/'vs team' all ABSENT; 0 textareas or contenteditables (no comment box); 0 console errors and 0 4xx on /api after the fix. The ADR-0065 emphasis was confirmed by computed style, not by eye: objectui emitted `.os-s-right_now_stalled { border-left: 3px solid #8C6512 !important; padding-left: 12px !important; }` and the node computes `3px rgb(140, 101, 18)` / `padding-left: 12px`. Page-assignment collision measured live: `GET /api/v1/meta/page` returns BOTH `duly_member` and the platform's `sys_user_detail` for `sys_user`, `duly_member` first, and it wins by list position and a stable sort only. Screenshots pushed to throwaway branch `shots-issue-13` and embedded in the PR.", "open_questions": [], "out_of_scope_findings": [ "filed as objectstack-ai/objectui#7297: no record-context token for filter values — CONTEXT_TOKENS is {current_user_id} + {current_org_id}, both the VIEWER, so element:number cannot be scoped to the record and would silently count the whole org", "filed as objectstack-ai/objectui#7298: two page keys the renderer reads that PageSchema (a strictObject) does not declare — `priority` in usePageAssignment and `disableDiscussion` in RecordDetailView; both are unwritable, and the second leaves a comment box on a read-only page", "filed as objectstack-ai/objectui#7299: record:related_list cannot bind to a multi-value relationship field — RelatedList composes bare equality, the driver refuses with 400 INVALID_FILTER and prescribes $contains, which is unreachable from RecordRelatedListProps", "filed as objectstack-ai/objectui#7300: a page cannot declare its related lists read-only — '+ New'/edit/delete are host-resolved from the CHILD object, so this read-only page renders 8 '+ New' buttons it has no key to decline", "filed as objectstack-ai/objectui#7301: record:related_list has no groupBy, and its columns cannot cross a lookup — the two limits that kept 'grouped by frequency' and 'who assigned each' from being written", "NOT filed, recorded here for PM triage: `PageTranslation.components` (@objectstack/spec 17.2.0) declares title|description|label|placeholder|emptyText|submitLabel and has NO key for `content` — the one string `element:text` renders. Its own comment says the key face was derived from the copy props components declare and names its two deliberate exclusions (`help`, `subtitle`), so this reads as a missed one. The blessed alternative (an inline `{en,'zh-CN'}` I18nLabel map, which the platform's own sys-user.page.ts uses) is what this page does, and the 16 strings are declared in the walk's untranslatable ledger with that reason and pinned by count. It belongs at objectstack-ai/objectstack, not objectui, which is why I did not file it under the card's stated destination without a PM call." ] }Generated by Claude Code
os-warren commentedon Sep 2, 2026
os-dev-report
Supersedes the previous comment on this issue. Two things the GitHub body sanitizer ate there, both worth knowing about: the leading
<!-- os-dev-report -->HTML-comment marker was stripped entirely (so that comment is invisible to a marker scan), and inside the evidence the short<restore>fragment was eaten fromtrap '...' EXIT INT TERM, which made the ablation's restore leg read as an empty trap. Same mechanism both times — short angle-bracket fragments do not survive, backticks included. This comment leads with the marker as literal text and spells the trap out.{ "issue": 13, "status": "done", "branch": "claude/issue-13-member-page", "pr": "https://github.com/objectstack-ai/duly/pull/112", "premise_still_valid": true, "summary": "`src/pages/member.page.ts` ships `duly_member`, a read-only `type: record` page over `sys_user` carrying the card's six sections in reading order, wired into `dulyPages` and reachable from a new `People` entry in the Team nav group. The three forbidden things are pinned by `test/member-page.test.ts` (25 assertions): the work-log object's machine name is greped out of the page source AND walked out of the parsed page, no writing component type is declared, every highlight chip is `readonly`, and no aggregate or peer comparison exists. The card asked for the page schema to be MEASURED — seven gaps were found against installed 17.2.0 and all seven are filed at objectstack-ai/objectui rather than worked around. Two were found only in the browser. ONE LINE OF THE CARD IS NOT DELIVERED: 'showing who assigned each' is unauthorable — the assigner is one lookup hop from the task (related-list columns cannot cross a lookup) and the alternative binding on `duly_assignment.assignees` was authored, run, and refused by the driver with 400 INVALID_FILTER because `RelatedList` composes its parent filter as bare equality and `assignees` is `multiple: true`. That list was deleted rather than left rendering an error; the task list carries the `assignment` column, one click from the answer. Separately, an auto-appended discussion panel with a comment box, reactions and threaded replies was found on the rendered page — `sys_user` is a locked platform object and the renderer's `disableDiscussion` opt-out is not a `PageSchema` key, so the page now declares an explicit `record:discussion` with all three writes off, which is the only authorable way to shut it (verified: 0 composers on the page). Reached `main` at 56b6aae (#110) via merge and re-ran all four gates after it; also converged onto #110's `node:fs` shim, deleting this branch's Vite `?raw` declaration rather than shipping two ways to read a file in a test.", "tests": "All four gates re-run ON THE FINAL PUSHED COMMIT e399e78 (`git status --porcelain` = 0 lines), each exit code captured before any pipe:\n\n pnpm validate -> 0 '✓ Validation passed (483ms)' · 'UI: 1 Apps 5 Views 1 Pages 1 Dashboards 6 Actions'\n one warning, the expected open-edition state naming @objectstack/security-enterprise (AGENTS.md rule 7 — not silenced)\n pnpm typecheck -> 0 tsc --noEmit, no diagnostics\n pnpm test -> 0 'Test Files 31 passed (31)' · 'Tests 775 passed (775)'\n pnpm build -> 0 '✓ Build complete (1003ms)'\n\nABLATION — both new guards were proven able to fail. The script carried a restore trap on EXIT, INT and TERM (a `cp` of the pristine file back over the page, registered before the first mutation), so a foreground-cap SIGTERM mid-mutation could cost a reading but never leave a mutated tree. Each mutation's anchor was asserted present exactly once by the editing script BEFORE the edit (a python `assert s.count(old) == 1`), so a zero-hit edit aborts instead of reporting a green no-op. No rebuild step applies here: vitest reads src directly, with no dist in the resolution path.\n A. injected a `duly_log_entry` related list -> on-disk proof `grep -c duly_log_entry` = 1 -> 'Tests 3 failed | 22 passed (25)', red on `is not named anywhere in the source`, `binds no component to it`, `reads in the order the card asks for`.\n B. dropped `readonly: true` from one highlight chip -> 'Tests 1 failed | 24 passed (25)', red on `marks every highlight chip readonly` and nothing else. (The shell-echoed grep counts for leg B were mangled by heredoc quoting and are NOT evidence; the anchor assertion and the targeted red are.)\n RESTORE leg verified byte-identical: `diff -q member.page.ts.orig src/pages/member.page.ts` => identical, `grep -c 'readonly: true'` = 5, `grep -c duly_log_entry` = 0.\n\nBROWSER — `pnpm demo -- --port 4913` (own port; killed by recorded PID only, never by name; port confirmed closed afterwards). 465 seeded rows, signed in as the demo admin, Playwright 1.56.1 at 1400x1000 @2x. Two seeded people (Rosa Delgado; Priya Raman, who has a standing duty). Measured on the rendered page: all six sections present with real data; counts Open 5 / Late 1 / Not moving 1, Governed duties 3 sorted Fortnightly-Monthly-Monthly, Held permanently 1, Last touched 28, Assigned work 2; `duly_log_entry` / 'Work log' / 'percentile' / 'rank' / 'vs team' all ABSENT; 0 textareas or contenteditables (no comment box); 0 console errors and 0 4xx on /api after the fix. The ADR-0065 emphasis was confirmed by computed style, not by eye: objectui emitted `.os-s-right_now_stalled { border-left: 3px solid #8C6512 !important; padding-left: 12px !important; }` and the node computes `3px rgb(140, 101, 18)` / `padding-left: 12px`. Page-assignment collision measured live: `GET /api/v1/meta/page` returns BOTH `duly_member` and the platform's `sys_user_detail` for `sys_user`, `duly_member` first, and it wins by list position and a stable sort only. Screenshots pushed to throwaway branch `shots-issue-13` and embedded in the PR.", "open_questions": [], "out_of_scope_findings": [ "filed as objectstack-ai/objectui#7297: no record-context token for filter values — CONTEXT_TOKENS is {current_user_id} + {current_org_id}, both the VIEWER, so element:number cannot be scoped to the record and would silently count the whole org", "filed as objectstack-ai/objectui#7298: two page keys the renderer reads that PageSchema (a strictObject) does not declare — `priority` in usePageAssignment and `disableDiscussion` in RecordDetailView; both are unwritable, and the second leaves a comment box on a read-only page", "filed as objectstack-ai/objectui#7299: record:related_list cannot bind to a multi-value relationship field — RelatedList composes bare equality, the driver refuses with 400 INVALID_FILTER and prescribes $contains, which is unreachable from RecordRelatedListProps", "filed as objectstack-ai/objectui#7300: a page cannot declare its related lists read-only — '+ New' / edit / delete are host-resolved from the CHILD object, so this read-only page renders 8 '+ New' buttons it has no key to decline", "filed as objectstack-ai/objectui#7301: record:related_list has no groupBy, and its columns cannot cross a lookup — the two limits that kept 'grouped by frequency' and 'who assigned each' from being written", "NOT filed, recorded here for PM triage: PageTranslation.components (@objectstack/spec 17.2.0) declares title|description|label|placeholder|emptyText|submitLabel and has NO key for `content` — the one string element:text renders. Its own comment says the key face was derived from the copy props components declare and names its two deliberate exclusions (help, subtitle), so this reads as a missed one. The blessed alternative (an inline en/zh-CN I18nLabel map, which the platform's own sys-user.page.ts uses) is what this page does, and the 16 strings are declared in the walk's untranslatable ledger with that reason and pinned by count. It belongs at objectstack-ai/objectstack, not objectui, which is why I did not file it under the card's stated destination without a PM call." ] }Generated by Claude Code