Repository navigation
core/test: lock the Owner's positive project-role path (regression lock, green before the change) - #2531
Merged
Birfy merged 1 commit intoNov 5, 2026
Conversation
ThinkFlowLab#1836/ThinkFlowLab#1837 pinned the refusal direction of the Project role switch at the endpoint; the positive direction — the Owner re-roles another member and the write lands — is pinned only at the policy function. A later gate tightened into "the actor must be the target" would leave every case on main green while the Owner silently loses the capability ThinkFlowLab#1832 shipped (ThinkFlowLab#1876). Test-only: no production change.
HugoXK
force-pushed
the
dev/4-p247-owner-role-positive-lock
branch
from
November 5, 2026 13:08
0a12049 to
499f056
Compare
HugoXK
marked this pull request as ready for review
November 5, 2026 13:20
Collaborator
|
Verdict: PASS at 499f056 Ran at this tip: Review points
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1876
Owner: Dev-4
Reviewer: Birfy
Written-by: FlyBot Dev-4
Claim-Id: dca6a744b00942ae8d8b6ac707a735ec
Risk tier: T1
Base: 90a5944
What changed — a regression lock, green before the change
The Project role switch is pinned at the endpoint only in the refusal direction:
core/test/project-role-authz.test.jsholds that amaintainerwho is not the owner cannot re-role another member and that the record does not move (#1836/#1837). The positive direction — the Owner re-roles another member and the write lands — is pinned only at the policy function (core/test/project-roles.test.js), not where the channel dispatches it.This pull request appends one
test(...)tocore/test/project-role-authz.test.jsand copies it byte-identically topatches/ds-bot/files/test/project-role-authz.test.js. With the Owner's session it callsproject-role{ id: 'project-p1', memberId: 'plain-1', role: 'maintainer' }and asserts:ok: true,changed: true,roles.maintainerscontainsplain-1, andstate.jsoncarries the samerolesobject as the reply. Thenplain-1's own session readsprojectsand getsrole: 'maintainer'for the project — the entry'srolefield is the caller's own rank (projectRoleOf(project, actorId)), so the target's own token is what proves the page shows them the new rank. Finally the switch goes back down (role: 'member'):changed: trueagain, andstate.jsonends at{ maintainers: ['maint-1'], members: ['plain-1'] }.Why the endpoint is the level that pins it:
core/test/project-roles.test.jspins whatchangeRoleanswers; the endpoint wires that answer intosetProjectRolesandsave(). A later gate tightened into "the actor must be the target" — a plausible reading of the same invariant — can keep the policy test and every current endpoint case green while the Owner silently loses the capability #1832 shipped. Only a case that sends the Owner's token through dispatch and reads the write back catches that.Test-only: no production code, no changelog entry, no docs. The mirror is a verbatim copy of
core/test/, andcheck's third line proves the two agree.Not a red-first change
On live
main90a594471b5886737b433e116f6397aa8ca2b6f2the file's four existing cases are green, and the new case is green too: 4/4 before, 5/5 after. Nothing here is broken and nothing is fixed; this is a regression lock that is green before the change, and the change adds no production code.Proof that the lock has teeth (a mutation in a scratch tree, not in this branch)
Applying the plausible tightening in a scratch
90a59447tree — the gate read as "the actor must be the target", inchangeRole:actor !== 'owner' || text(actorId) !== text(target)— turns exactly this case red while the four existing cases stay green, which is the silent-loss shape #1876 names:Restoring the gate makes the same tree green again. The mutation is not part of this branch:
src/is untouched, andgit diff --statlists only the two test files.Verification at the tip
Tip:
499f05646796a4b65c7f8b228b3bbefaceb07703(treee21452312acca468123b5bf4cd55e1b5988d6659) — the push path re-created local46dc2be759c9799490e7e47090de954a22ef7e34on the same base with the same tree.Ran:
TZ=UTC node --import ./test/harness-resolve.mjs --test test/project-role-authz.test.js(incore/) →# tests 5 / # pass 5 / # fail 0:ok 1–ok 4are the existing core: a signed-in deployment answers "Unknown endpoint project-role", so #1832's role switch writes nothing #1836 cases,ok 5is the new core/test: lock the Owner's positive project-role path so a tightened gate cannot take the capability away #1876 case.90a59447tree with the file asmainhas it →# tests 4 / # pass 4 / # fail 0(the state before the change).TZ=UTC npm --prefix core test→# tests 2928 / # pass 2928 / # fail 0, exit 0; on the clean90a59447tree the same suite is# tests 2927 / # pass 2927 / # fail 0.90a59447tree (above):# tests 5 / # pass 4 / # fail 1, the one failure being the new case; mutation reverted,src/host/project-board.jsbyte-identical, tree green again.node scripts/ds-bot-patches.mjs check→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/.node scripts/ds-bot-patches.mjs canonical→ok patches/ds-bot/ is what generate writes (generate is a no-op).ARCH_DOCS_BASE=90a594471b5886737b433e116f6397aa8ca2b6f2 node scripts/check-arch-docs.mjs(incore/) →arch-docs: OK (0 changed file(s)).git diff --numstat 90a59447…HEAD— 2 files,+52 / −0:Not run (and why):
generate: not needed for a test-only change (an existingfiles/mirror, neither a new file nor an upstream file), andcanonicalabove reports thatgeneratewould be a no-op; no bundle or patch-series content is touched.npm run verifyas one command: it starts with core lint andoxlintis not installed in this checkout (core/node_modules/.bin/oxlintis absent,npxcannot fetch it here), so it would fail on a missing tool rather than a defect; its relevant steps are listed above.core/index.jsorcore/src/host/).Checkpoint
Test advisory: PASS at
499f05646796a4b65c7f8b228b3bbefaceb07703— FlyBot Test, independent of the author: the file is 4/4 on live main before the change and 5/5 at this tip; the new case fails only when the plausible "actor must be the target" tightening is applied; thepatches/ds-bot/files/test/mirror is byte-identical (cmp). Advisory for a T1 change, not the merge gate.{ "pr": 2531, "claim": "Dev-4", "issue": 1876, "summary": "The Project role switch was pinned at the endpoint only in the refusal direction; the Owner's positive path — re-role another member and the write lands — lived only at the policy function. One endpoint-level case now drives the Owner's token through project-role, checks the reply and state.json, reads the project back as plain-1 to see the new rank, and demotes again. It is a regression lock, green before the change: 4/4 on live main before, 5/5 after, and it fails only when the plausible 'actor must be the target' tightening is applied. Test-only; no production change.", "files": ["core/test/project-role-authz.test.js", "patches/ds-bot/files/test/project-role-authz.test.js"], "checks": { "ran": "project-role-authz.test.js 5/5 at the tip (4/4 before); TZ=UTC npm --prefix core test 2928/2928 (2927/2927 before); mutation counter-proof 4 pass/1 fail on the new case; ds-bot-patches check three ok lines; canonical no-op; arch-docs OK (0 changed file(s))", "result": "green", "at": "499f05646796a4b65c7f8b228b3bbefaceb07703" }, "next": "none — ready for review" }