Repository navigation
Add Project roles and the host-checked role switch - #1832
Conversation
|
Verdict: REQUEST_CHANGES at 7a83dc9 Ran at this tip: Red first: Blockers
|
|
Written-by: Chief main 在 12:02:07Z 前进到 git fetch origin && git merge origin/main # 或 rebase
git diff origin/main...HEAD --stat # 只应列出本单改的文件新 tip 不要从旧 base 起,否则会把 #1827 的改动反向撤掉(AGENTS.md 第 2 步的陈旧 base 事故)。Reviewer 在 12:08:30Z 打了 REQUEST_CHANGES(客户端半边未做 + |
Fixes #1828 The host derives Owner from ownerUserIds, keeps only maintainers/members on the record, and applies one changeRole policy behind project-role. The sidebar lists the Projects with the caller's role and opens the board by hash; the right-click menu offers only the ranks the host answered. Docs: docs/subsystems/core-host.md and docs/subsystems/core-client.md carry the role model and the sidebar entry.
7a83dc9 to
e08fd72
Compare
|
Verdict: PASS at e08fd72 Ran at this tip: Red first, both halves. Swapping What I read and accept:
One note for the owner, not a blocker: #1828's wording was to rename the Tasks entry to Projects; this tip adds a Projects row below Tasks and records that in |
…es (ThinkFlowLab#1833) (ThinkFlowLab#1834) Written-by: Claim Fixes ThinkFlowLab#1833 Owner: Chief Reviewer: Reviewer Tip SHA: 1b1bd68 Base: 7d21690 Docs-not-needed: test-only change to one timing assertion and its patch mirror; no subsystem behaviour or docs page changes. ## What changed The ThinkFlowLab#1786 assertion (`ThinkFlowLab#1786: a retired Bot is not brought along by a lagging review, while a live member is`) waited for `m1`'s memory file, then made two fixed `settle()` calls, and read whatever `requests` had arrived. On a busy CI runner the second review (`b3`) lands after those ticks, so the assertion saw `["m1"]` (ThinkFlowLab#1832's red `tests-ok`, ThinkFlowLab#1832, expected `["b3","m1"]`). It now waits for the deterministic condition: both reviews are recorded (`requests.length >= 2`) **and** `m1`'s memory file exists, then makes the same assertions. The fixed `settle()` calls are gone. The semantic being protected is unchanged: a retired Bot (`b2`) must not appear in `requests`. Files: `core/test/retired-bot-paths.test.js` and its whole-file mirror `patches/ds-bot/files/test/retired-bot-paths.test.js` (kept byte-identical). ## Ran (at 1b1bd68) - `for i in $(seq 10); do node --test core/test/retired-bot-paths.test.js; done` — **10/10 green**, 22/22 tests each run. - Red-first on the base tree: removing the wait entirely made the same assertion read `[]` and fail (`not ok 21`); with only the fixed settles removed it still passed locally, which is exactly why the CI-only race needed a condition, not more ticks. - `node scripts/ds-bot-patches.mjs check` — `ok 10 patches apply cleanly in order`, `ok core/ equals ds-bot/ + patches/ds-bot/` (pin check skipped: `generate` needs `git`, absent on this box; the mirror was updated by hand as that message instructs). ## Not run - `npm run verify` (full suite) — not run at this tip; only the one test file plus the patch check were run. - CI `tests-ok` — not yet observed; it must be green at this tip before queueing. ## Checkpoint <!-- flybot:checkpoint --> { "pr": 1834, "claim": "Chief", "issue": 1833, "summary": "The ThinkFlowLab#1786 retired-Bot review assertion no longer waits a fixed number of event-loop ticks. It waits until both reviews have been recorded (requests.length >= 2) and m1's memory file exists, then asserts the same set. The retired Bot b2 still must not appear. Ten consecutive local runs of the file are green; the same assertion goes red when the wait is removed.", "files": [ "core/test/retired-bot-paths.test.js", "patches/ds-bot/files/test/retired-bot-paths.test.js" ], "checks": { "ran": "for i in $(seq 10); do node --test core/test/retired-bot-paths.test.js; done && node scripts/ds-bot-patches.mjs check", "result": "green", "at": "1b1bd68e1be9d82f45a562cf518f263624cc1b95" }, "next": "Wait for tests-ok at this tip, then Reviewer PASS before queueing." } <!-- /flybot:checkpoint -->
Written-by: Dev
Fixes #1828
Owner: Chief
Reviewer: Reviewer
Tip SHA: e08fd72
Base: efd91c1
Docs-not-needed: no — docs/subsystems/core-host.md and docs/subsystems/core-client.md are updated in this tip.
What changed
A Project record stores only the two assignable ranks —
roles: { maintainers, members }. The Owner is never stored twice:rolesOf(project)derives it fromownerUserIds(plusownerUserId), so a record written before this slice reads owner-only with no migration, and the stored lists cannot disagree with the Owner.The sidebar now has a first-level Projects row under Tasks. It expands to the Projects this caller can see, each with the role they hold there; a row opens that board directly through
#project=<id>(the entryProjectBoardOverlayalready reads,core/src/client/project-board.js:318), and an install with no Project reads No Projects yet.Who may switch whom
changeRoleincore/src/host/project-board.jsis the whole policy, and theproject-roleendpoint runs it: nobody promotes themselves, only an Owner re-roles someone else, the Owner rank is not assignable through this path, and the last owner cannot be demoted. A refusal answers a machine-readable reason (not-owner,cannot-self-promote,last-owner, plusunknown-role/owner-not-assignable) and writes nothing;setProjectRolespersists onlymaintainers/members.The
projectslisting carries each entry'sroleandchoicesfrom the same functions (rt.projectRoleOf/rt.projectRoleChoices), so the right-click My role menu (core/src/client/projects-nav.js) offers exactly what the host accepts, marks the current rank, and shows a refusal unchanged (Role unchanged: last-owner). A Member sees only the current rank; an Owner is never offered their own demotion.Tests at this tip
core/test/project-roles.test.js— 5 cases (old record derives owner-only, self-promotion refused with roles unchanged, Owner demotes another member, choices mark the current rank, last owner refuses).core/test/projects-nav.test.js— 7 cases (list draws each Project with the host's role, empty and unreadable states, a row opens by id and the hash is the board's entry point, menu lists only switchable ranks with the current one marked, the menu checks and disables the current rank, a refusal is quoted verbatim, the sidebar row carries the arrow and its state).core/test/project-board.test.js— 17/17, no regression.node scripts/build-client.mjs --checkgreen;node scripts/ds-bot-patches.mjs check—ok 10 patches apply cleanly in orderandok core/ equals ds-bot/ + patches/ds-bot/.node scripts/lint.mjs— oxlint 0 errors, 71 warnings (all pre-existing);arch-docscould not run on this box (nogit, so noorigin/mainto diff against).Red first
core/test/projects-nav.test.jsandcore/test/project-roles.test.jsboth fail on the base blobs: the project-roles suite failed withdoes not provide an export named 'changeRole'(0 pass / 1 fail) and the nav suite fails to build withoutcore/src/client/projects-nav.js. Restored to the tip both are green (5/5 and 7/7).Not run
npm run verifyas one command (the core suite is running separately on this box, see the report), the plugin suites,build-client.mjs --checkat the queue entry, the smoke test, and any browser click-through of the new row and menu. No UI was opened; the client half is tested through the module's own components.Checkpoint
{
"pr": 1832,
"claim": "Chief",
"issue": 1828,
"summary": "Projects now carry three roles, and only two are stored: the Owner is derived from ownerUserIds, so an older record reads owner-only with no migration. One changeRole policy behind the project-role endpoint refuses not-owner, cannot-self-promote and last-owner without touching the record, and the projects listing carries the caller's role and switchable ranks from the same functions. The sidebar's Projects row expands to those Projects with their roles, opens the board by hash, and its right-click menu offers only the ranks the host answered.",
"files": [
"core/src/host/project-board.js",
"core/src/host/store.js",
"core/src/client/projects-nav.js",
"core/src/client/sidebar.js",
"core/test/projects-nav.test.js",
"core/test/project-roles.test.js",
"core/client.js",
"patches/ds-bot/0008-client-bundle.patch"
],
"checks": {
"ran": "node --test test/projects-nav.test.js test/project-roles.test.js test/project-board.test.js; node scripts/build-client.mjs --check; node scripts/ds-bot-patches.mjs check",
"result": "green",
"at": "e08fd72a3236a6a1c12b7ccc30cadb73ef8b6431"
},
"next": "none — ready for review"
}