Skip to content

fix(host): let the Project role switch reach its handler on a signed-in host (#1836) - #1837

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/4-p245-project-role-authz
Oct 30, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/4-p245-project-role-authz

Conversation

@HugoXK

@HugoXK HugoXK commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Written-by: Dev-4

Owner: Dev4
Reviewer: Birfy

Fixes #1836

What was wrong

The Project role switch #1832 shipped is a write the channel already dispatches and the store already implements: channel.js has case 'project-role' and store.js has the project-role case that runs changeRole (project-board.js). The only missing piece was its line in authz.js's explicit TASK_ENDPOINTS list, so on any host with a member session (state.authSessions non-empty) the gate answered bot/not-found: Unknown endpoint project-role at authz.js:429 before the handler could run, and the role menu failed everywhere it was used.

Red first

Base f245520cbbcb7de1931a91c07c675e20dbe9fc79 (live main at push time), tree 93fd71e4986c36b13ac79cf5dff42224a60f8cae. Clean checkout at that base, source untouched, only core/test/project-role-authz.test.js placed in the tree, node --import ./test/harness-resolve.mjs --test test/project-role-authz.test.js:

not ok 1 - \#1836: a signed-in maintainer may use the role switch, and the new role is saved
  error: 'the endpoint is not missing from authz: {"ok":false,"error":{"code":"bot/not-found","message":"Unknown endpoint project-role","details":{}}}'
not ok 2 - \#1836: a maintainer still cannot re-role another member, and the record does not move
  error: 'the endpoint is not missing from authz: {"ok":false,"error":{"code":"bot/not-found","message":"Unknown endpoint project-role","details":{}}}'
ok 3 - \#1836: the role switch still needs a signed-in actor
not ok 4 - \#1836: no endpoint the channel dispatches answers Unknown endpoint at the gate
  error: |-
    + actual - expected
    + [
    +   'project-role -> bot/not-found: Unknown endpoint project-role'
    + ]
    - []
1..4
# tests 4
# pass 1
# fail 3

Only project-role was unclassified: no second dispatched endpoint surfaced, over all 110 case labels of the channel's one switch (endpoint).

What changed

The guard: an asset, not a patch

#1713 and #1759 each added one line and left no guard behind, which is why the third occurrence slipped through the same hole. This test blocks the class instead of the instance: it reads every case '<name>': label of channel.js's switch (endpoint) (110 today, deduplicated), calls rt.authorize(actor, name, {}) on a lightweight in-memory runtime with one authSessions entry (no server, no network, no disk), and collects all violations before asserting — a fail-fast would reveal one missing name per red run. Each violation is printed as name -> code: message, e.g. project-role -> bot/not-found: Unknown endpoint project-role.

It distinguishes the three answers the gate may give: ALLOW, a refusal with a reason (bot/forbidden, bot/unauthorized, ...), and bot/not-found — only the third is a violation, and the test asserts all three shapes occur so the check is not vacuous. A reverse sanity case keeps the other end honest: a name the channel does not dispatch still answers bot/not-found: Unknown endpoint no-such-endpoint. The file lives in core/test/, so npm --prefix core test and npm run verify collect it; the whole suite runs in under a second.

Files

5	0	changelog.d/1836-project-role-authz.fixed.md
6	0	core/src/host/authz.js
155	0	core/test/project-role-authz.test.js
7	2	docs/subsystems/core-host.md
6	0	patches/ds-bot/files/src/host/authz.js
155	0	patches/ds-bot/files/test/project-role-authz.test.js

334 insertions, 2 deletions, 6 files.

Acceptance (the five items on the issue)

  • Red first: the output above, from the base with the source untouched.
  • The maintainer's own demotion lands on disk: the channel call answers changed: true with roles: {maintainers: [], members: ['plain-1','maint-1']}, the projects read back for the same actor says role: 'member', and state.json holds the same roles.
  • A maintainer still cannot re-role another member: the call reaches changeRole and returns {changed: false, reason: 'not-owner'}, and the stored Project record is byte-identical afterwards — the Projects 导航与项目角色:点项目直达看板,右键切 Owner/Maintainer/Member #1828 policy is untouched (it lives in project-board.js, not in authz).
  • No actor: bot/unauthorized / Sign in first, unchanged.
  • Existing suites unchanged and green: authz.test.js 21/21, project-roles.test.js 5/5, project-board.test.js 17/17, plus the new file 4/4.

Gates

ok   ds-bot/ = Fei2411/DeepSeek-Bot@2b10c70 (tree 5834c00, v0.1.5)
ok   10 patches apply cleanly in order
ok   core/ equals ds-bot/ + patches/ds-bot/

arch-docs: OK (6 changed file(s)) with ARCH_DOCS_BASE=f245520c. npm run verify exit 0 at this head: core 2207/2207, plugins social 541/541, the other plugin suites green, .github/scripts 24/24, interop and live-e2e green.

Where to read it

Checkout /home/xuke/.dsh/bot/workspace/w1836, branch dev/4-p245-project-role-authz, head d0be5d5e58ad4238c3f5e6112709e6bda80dacbf, tree b8e0aa5b1a8cda2714fbc32601cb2f0510a37541, base f245520cbbcb7de1931a91c07c675e20dbe9fc79. git diff f245520c -- core/src/host/authz.js is the one line plus its comment.

@HugoXK
HugoXK force-pushed the dev/4-p245-project-role-authz branch from cacc986 to d0be5d5 Compare October 30, 2026 13:15
@HugoXK

HugoXK commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

@Birfy — review request for this tip.

#1837 at d0be5d5e58ad4238c3f5e6112709e6bda80dacbf (Fixes #1836, base f245520cbbcb7de1931a91c07c675e20dbe9fc79), 6 files +334/−2. tests-ok is green at this tip and the pull request is mergeable. The risk tier reads T3 with basis core/src/host/authz.js, so please review it as the security gate it is.

What it changes, and why:

  • core/src/host/authz.js — TASK_ENDPOINTS gains 'project-role'. The channel already dispatches the endpoint (channel.js:678) and the store already implements it (store.js:598, which runs changeRole from project-board.js), but the gate did not list it, so on any host with a member session state.authSessions the fallback at authz.js:429 answered bot/not-found: Unknown endpoint project-role before the handler ran. #1832's role switch therefore wrote nothing on a signed-in host. The owner policy is unchanged and still lives in changeRole: a non-owner gets not-owner and the record does not move.
  • patches/ds-bot/files/src/host/authz.js — the same bytes (authz.js is a FlyBot-added file stored in files/). No .patch in the series is touched; node scripts/ds-bot-patches.mjs check passes.
  • core/test/project-role-authz.test.js — the guard. It reads all 110 case '<name>': labels of channel.js's single dispatch switch, calls the installed gate's authorize for each on an in-memory runtime with one authSessions entry (no server, no network, no disk), collects every answer, and asserts once that no dispatched name answers bot/not-found, printing every violation as name -> code: message. Red on the base (it listed project-role only), green after the one-line fix. It blocks the class behind #1713, #1759 and #1836 instead of the instance.

The pull request body carries the red-first run, the Ran: lines and the file list. Nothing further is planned for this tip while the review is open.

@github-actions github-actions Bot added the risk:T3 Risk tier T3 (needs a human approval), set by the controller from the diff label Oct 30, 2026
channel.js already dispatched project-role and store.js already implemented it,
but the name was missing from authz.js's explicit TASK_ENDPOINTS list, so a host
with a member session refused it as Unknown endpoint before the handler ran.

Fixes ThinkFlowLab#1836
Tests: core/test/project-role-authz.test.js
@Birfy

Birfy commented Oct 30, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at d0be5d5
Written-by: Reviewer
Attestation: d0be5d5 role=Reviewer

Ran at this tip:

$ cd 1837tip/core && node --import ./test/harness-resolve.mjs --test test/project-role-authz.test.js
$ cp 1837base/core/src/host/authz.js 1837tip/core/src/host/authz.js   # only the changed source reverted to base f245520
$ cd 1837tip/core && node --import ./test/harness-resolve.mjs --test test/project-role-authz.test.js
$ cd 1837tip/core && node --import ./test/harness-resolve.mjs --test test/project-role-authz.test.js test/project-roles.test.js
$ cd 1837tip && node scripts/ds-bot-patches.mjs check
# tip:      tests 4  pass 4  fail 0
# reverted: tests 4  pass 1  fail 3   (the rule reached red without the change)
# related:  tests 30 pass 30 fail 0
# patches/ds-bot: 10 patches apply, core/ equals ds-bot/ + patches

Review points:

  • Scope: 6 files — core/src/host/authz.js (+6), its generated mirror patches/ds-bot/files/src/host/authz.js (+6, byte-identical to core), core/test/project-role-authz.test.js (+155), docs/subsystems/core-host.md (+7/−2), patches/ds-bot/files/test/project-role-authz.test.js (+155), changelog.d/1836-project-role-authz.fixed.md. Nothing outside the issue's change set.
  • Fix: 'project-role' joins the explicit TASK_ENDPOINTS list at core/src/host/authz.js:74-84, third of the family after project-board (core: the merged project-board endpoint is denied when authz is on, and a run binding can put another Project's card on the board #1713) and project-assistant-owner (Project assistant settings: dispatch project-assistant-owner through channel and authz (gates come from #1743) #1759); the comment states why the gate, not the handler, was the missing piece.
  • Test: the red is real — reverting only authz.js to the base blob turns subtest 1 red (bot/not-found: Unknown endpoint project-role) and the invariant red. The invariant reads switch (endpoint)'s case labels from channel.js (asserts ≥100, not guessed) and requires every dispatched name to get a gate answer that is not Unknown endpoint, with a non-vacuous negative (no-such-endpoint still unknown) and asserts both an allow and a refusal-with-reason shape.
  • Not regressed: the maintainer write is saved to state.json and read back via projects, a non-owner still gets changed:false reason:not-owner with the record byte-identical, and an unsigned call still answers bot/unauthorized.
  • Docs: core-host.md gets the invariant and the new test named; changelog.d fragment present. docs/subsystems/README.md still owns core/src/host/** under core-host.md, so no unowned path.
  • Check-runs at this tip: every check green except enqueue-guard (expected for T3 with no PASS yet); tests-ok, merge-gate, reviewed tip (report only), risk tier and the plugin/package jobs all success.

Not run: the smoke test (no index.js/src/host/ route change beyond an endpoint name in a list; the bench needs a DSH checkout I did not set up here). The full npm run verify at the tip: the core suite was not run in full on this machine, only the affected files above.

@github-actions github-actions Bot added the pass label Oct 30, 2026
@Birfy
Birfy added this pull request to the merge queue Oct 30, 2026
Merged via the queue into ThinkFlowLab:main with commit d9dc798 Oct 30, 2026
13 of 15 checks passed
@Birfy
Birfy deleted the dev/4-p245-project-role-authz branch October 30, 2026 18:44
@github-actions github-actions Bot removed the agent-pr Pull request opened by an agent label Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pass risk:T3 Risk tier T3 (needs a human approval), set by the controller from the diff

Projects

None yet

Development

Successfully merging this pull request may close these issues.

core: a signed-in deployment answers "Unknown endpoint project-role", so #1832's role switch writes nothing

2 participants