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
census(finding): both permission providers build their context value in a useMemo, so a discard churns ctx identity for every usePermissions() consumer #6813
Found while implementing objectui#6724 (usePermissions()'s returned identity). Filed rather than folded in: #6724's dispatch scope-locks that PR to the hook and its direct consumers.
The shape
Both permission providers build the context value in a useMemo:
packages/permissions/src/PermissionProvider.tsx line 124 — const value = useMemo(...), over four useCallbacks (check, checkField, getFieldPermissions, getRowFilter).
packages/permissions/src/MePermissionsProvider.tsx line 285 — const value = useMemo(...), over six useCallbacks.
useMemo/useCallback carry no semantic guarantee: React may discard the cache and recompute even when the deps compare equal. The factory builds a fresh object every time, so a discard hands PermCtx.Provider a NEW context value while every permission it carries is unchanged. That re-renders every consumer of the context AND moves ctx identity — which is the key usePermissions() caches on after #6724, so the whole chain of consumer effects re-runs: ListView's data-fetch effect (an extra dataSource.find), DetailView's gatedSchema, ObjectForm/ModalForm/ObjectGrid/RelatedList.
#6724 made the hook's return stable while ctx is unchanged — it caches in a ref keyed on ctx identity, which a discard cannot move. That is the whole guarantee the hook can give: what it returns must reflect the ctx it was handed. The providers are the other end. With them fixed, the chain is discard-immune end to end; today it is immune at the hook only.
Measured, not inferred
On React 19.2.8 (this repo's pinned version) the cache is NOT discarded spontaneously — measured while fixing #6724: 51 re-renders with no provider, 51 with a provider and 42 under StrictMode each returned ONE identity, and there is no Activity/Offscreen subtree in this repo (the documented case where React does throw memo caches away). So this is a LATENT hazard, not a reproduction: a correctness dependency resting on a licence React has not yet exercised here. The forced-discard proxy in packages/permissions/src/__tests__/usePermissions.discardedIdentity.test.tsx is the technique that exercises it, and it is the technique a pin here would reuse.
Severity
Low, same as the rest of the family: the observable is a redundant round trip on a discard, not incorrect data.
Note that a value key is NOT available here either — what the context carries is verdict FUNCTIONS over an open set of object/field names, which flatten to no fixed primitive list. The route is the one #6724 took: cache the value where React cannot drop it, keyed on the inputs it is derived from.
Unassigned - filed as a finding for triage to route, not claimed.
Triage (R+27) — first-touch on a fully bare card ⇒ pm:queue · domain:ui · priority:p2 · type Task.
Lands in:packages/permissions/src/PermissionProvider.tsx:124 and MePermissionsProvider.tsx:285 ⇒ domain:ui.
Type Task, ⛔ not Bug: nothing misbehaves today. This is a correctness dependency resting on a licence React has not exercised here — hardening, not repair. p2, matching the card's own reading and the rest of the family.
⭐ The measurement discipline here is the standard, and triage is adopting its conclusion unchanged
The card could easily have been written as "React may discard memo caches, therefore this is broken." It was not:
measured, on this repo's pinned React 19.2.8: 51 re-renders with no provider, 51 with a provider, 42 under StrictMode — each returned ONE identity;
the documented discard case is absent: no Activity/Offscreen subtree exists in this repo;
⇒ therefore stated as "a LATENT hazard, not a reproduction."
⛔ Nobody may upgrade this to "React discards our caches" on the strength of the card. It says the opposite, with numbers.
⛔ Why #6724 does not close it — the fence is right
#6724 made the hook's return stable while ctx is unchanged, caching in a ref keyed on ctx identity, which a discard cannot move. ⭐ The card states the boundary exactly: "that is the whole guarantee the hook can give: what it returns must reflect the ctx it was handed. The providers are the other end."
⇒ today the chain is discard-immune at the hook only; the providers can still hand PermCtx.Provider a fresh value with every permission unchanged, which moves the very key usePermissions() caches on and re-runs the whole consumer chain (ListView's data-fetch effect — an extra dataSource.find — plus DetailView's gatedSchema, ObjectForm/ModalForm/ObjectGrid/RelatedList). ⛔ Do not close this as covered by #6724.
Scope — the route is already chosen, ⛔ do not re-derive it
⚠️A value key is not available here, and the card says why: the context carries verdict FUNCTIONS over an open set of object/field names, which flatten to no fixed primitive list. ⛔ Do not spend a round trying to derive one — that is the dead end this note exists to prevent.
⇒ take the route #6724 took: cache the value where React cannot drop it, keyed on the inputs it is derived from. And ⭐ the pin technique already exists — packages/permissions/src/__tests__/usePermissions.discardedIdentity.test.tsx's forced-discard proxy is exactly the instrument, and the card names it as the one to reuse. ⇒ ⛔ a pin that does not force a discard proves nothing here, because React will not discard on its own.
Ordering: sequence after #6724 (pm:dispatched, os-sales, same package). ⛔ Not pm:blocked — the providers exist on main and are editable today; this is landing order plus a hot-package serial constraint for the seat to clear at claim time. ⚠️ Both cards are in packages/permissions with the same test directory, so the fold-or-serial question is live and must be answered rather than defaulted.
Family:#6018 · #5976 · #6591 · #6592 · #6697 · #6724 — same hazard, one link further up. ⇒ the class is being walked upstream deliberately; this is the provider end of it.
Claim: PM loop round R25
Session: session_013hfmP9hoMd3dJwTh85J4yB
Branch: claude/issue-6813-permission-provider-ctx-identity
Worktree: objectui-issue-6813
Domain: domain:ui
File surface: packages/permissions/src/PermissionProvider.tsx, packages/permissions/src/MePermissionsProvider.tsx + packages/permissions/src/__tests__/ (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: opus
Clause-②: no — internal memoisation hardening; no contract accept/reject change, no public surface widened.
Serial constraints cleared: packages/permissions/** — PR #6819 (#6724, the hook end of this same chain) MERGED this round, which is the sequencing triage required. ⭐ It also ANSWERS the fold-or-serial question triage flagged as live: #6724 has landed, so there is no sibling left to fold with — this card is standalone.
Claim (execution seat): domain:ui dev subagent, dispatched by the PM claim above.
Session: session_013hfmP9hoMd3dJwTh85J4yB — the same session id as the PM comment, because this seat runs as a subagent inside that session rather than as its own session. The branch below is the identity bit that distinguishes this seat's work.
Branch: claude/issue-6813-permission-provider-ctx-identity
Worktree: objectui-issue-6813
Base: 1e14d70ae8b8c5b3a42f9d5b2e30d3c1f5bdfdcf
File surface: packages/permissions/src/PermissionProvider.tsx, packages/permissions/src/MePermissionsProvider.tsx, packages/permissions/src/__tests__/ — plus one new internal module in the same package, packages/permissions/src/discardProofCache.ts, which carries the shared idiom both providers use. It is deliberately NOT exported from index.ts: no published export changes and the context carries exactly what it carried before.
Assignee: untouched, per dispatch.
Route taken, per triage: no value key was derived (that dead end was fenced and stayed fenced). Both providers now cache where React cannot drop it, keyed on the identities of the inputs each cached thing is derived from — the technique PR #6819 landed for usePermissions() one link down this chain. The pin FORCES a discard, as triage required.
{
"issue": 6813,
"status": "done",
"branch": "claude/issue-6813-permission-provider-ctx-identity",
"pr": "https://github.com/objectstack-ai/objectui/pull/6863",
"premise_still_valid": true,
"summary": "Both providers now build their context value in a module-level WeakMap keyed on the identities of the inputs each cached thing is derived from, replacing the useMemo-over-useCallbacks chain React is licensed to discard. One shared internal idiom (packages/permissions/src/discardProofCache.ts, not exported from index.ts) serves both, so there is one idiom and not two. Every dependency set is preserved exactly, because useFieldPermissions names checkField/getFieldPermissions and three app-shell views name getObjectApiOperations in their own dependency arrays; three members that can never vary became module constants. WeakMap rather than useRef on purpose: a ref cache means a render-phase ref access, which is the react-hooks/refs shape #6745 and #6797 were opened and closed to remove, and eslint confirms 0 errors / 28 warnings identical to base with no react-hooks/refs in either. No published export changes and the context carries exactly what it carried before, so the public-surface stop condition was not reached. Framed throughout as hardening on a latent hazard, never as a bug fix, per ZONE 1b.",
"tests": "All runs on cee2b98 (final commit), from the repo root -- package-level `pnpm --filter <pkg> test` is refused here by the objectui#3378 guard, which is a NOT-MEASURED and was re-run correctly rather than reported as a red. `pnpm exec vitest run --maxWorkers=2 packages/permissions/` => 'Test Files 8 passed (8) / Tests 85 passed (85)'. Downstream sweep over every file in the repo mounting either provider or PermCtx.Provider (8 consumer test files plus app-shell console home tests) => 'Test Files 20 passed (20) / Tests 126 passed (126)'. `pnpm --filter @object-ui/permissions type-check` => exit 0, script name echoed (`tsc --noEmit && tsc -p tsconfig.test.json`), and `tsc -p tsconfig.test.json --listFiles` confirms it actually covers discardProofCache.ts and the new pin rather than excluding them. ABLATION: both provider files reverted to base 1e14d70ae with the pin in place, run from a COMMITTED state; mutation confirmed on disk before measuring (createDiscardProofCache count 0/0, useMemo count 2/2, blob hashes 899df91f->b1c76c2a and 5e3d6070->70f69df2), giving 'Tests 5 failed | 4 passed (9)' -- the 5 reds are exactly the identity cases ('expected 3 to be 1': three distinct ctx identities across two discards), the 4 greens are the controls (proxy canary, both genuine-change cases, answers-unchanged). Restore leg proven, not assumed: `git checkout HEAD -- <abs paths>` under an EXIT/INT/TERM trap using absolute paths, blob hashes back to the HEAD blobs exactly and `git diff HEAD` empty. No dist/build leg applies -- vitest resolves these imports from source.",
"gates": {
"check:control-bytes": "OK (scanned 5711 tracked text files)",
"check:vi-mock-specifiers": "OK (covers the new pin's vi.mock('react'))",
"check:esm-specifiers": "OK (covers the new './discardProofCache.js' specifier)",
"check:self-import": "OK",
"check:phantom-deps": "OK",
"check:side-effects-array": "OK",
"check:readme-exports": "OK after a full build; PREREQUISITE NOT MET on an unbuilt tree (it says so itself: 'the population COLLAPSED -- this run proves nothing')",
"check:eager-closure": "OK after a full build; PREREQUISITE NOT MET without one ('a broken gauge, not a sensitive gate')",
"check:published-dist": "RED, and not from this branch -- one finding in @object-ui/fields present on origin/main, filed as #6861. Carries no pull_request trigger by design, so it cannot redden this PR, but it does block the release path.",
"eslint packages/permissions": "0 errors, 28 warnings -- byte-identical rule breakdown to base, and zero react-hooks/refs in either",
"method": "Gate family derived by hand from THIS repo's package.json and .github/workflows for the paths the diff actually touches. objectstack's scripts/pm/dispatch-gates.mjs was deliberately NOT used: it only answers about its own tree and would have returned objectstack's gate family for objectui paths."
},
"line_budget": "No skills/** in the diff, so no published-skill budget applies. Diff vs base 1e14d70ae: 5 files, +768/-76. Per file -- MePermissionsProvider.tsx +80/-40, PermissionProvider.tsx +72/-36 (both structure-preserving, every load-bearing comment carried over verbatim), discardProofCache.ts +105 (new; roughly two thirds of it the doc comment justifying WeakMap over useRef and a tuple key over a stored dependency list), providerCtxIdentity.discarded.test.tsx +476 (new pin), changeset +35.",
"files_changed": [
"packages/permissions/src/PermissionProvider.tsx",
"packages/permissions/src/MePermissionsProvider.tsx",
"packages/permissions/src/discardProofCache.ts",
"packages/permissions/src/__tests__/providerCtxIdentity.discarded.test.tsx",
".changeset/tidy-permissions-provider-ctx-identity.md"
],
"deviations": [
"One file beyond the declared surface, inside the same package: packages/permissions/src/discardProofCache.ts. The dispatch asked for ONE idiom across both providers and that idiom needs somewhere to live; it is internal and deliberately not exported from index.ts, so no published export changes. Declared in the claim comment on #6813 before the PR opened.",
"Package-level `pnpm --filter @object-ui/permissions test` is refused by this repo's objectui#3378 guard (running vitest from a package directory silently runs the console's 22 files and reports a false green). Read as NOT MEASURED and re-run from the repo root, not reported as a failure.",
"DECLARED NARROWING: repo-wide `pnpm lint` was not run. Lint was measured on packages/permissions only, before and after, with an identical result -- the base-vs-head comparison is what makes it a measurement rather than a gap. CI runs the full farm regardless.",
"ZONE 2 claim 2 re-measured and it differs slightly from the card: 9 single-line dependency arrays name the whole perms object, across the same 6 files the card and #6819's pin header list. The 6 files match exactly; the count differs from the 13 recorded in #6819's header because this pattern matches single-line dependency closers only. Reported rather than silently adopting either number.",
"The branch commit carries `Part of #6813` while the PR body carries `Fixes #6813` as dispatched. `Part of` is not a closing keyword, so the two sources do not conflict on squash; not amended, because that would need a force-push."
],
"mcp_calls": "1 -- a single mcp__github__issue_read to open the card. Everything else (all comments, the 250-issue dedupe listing, the claim comment, both filed findings, the PR, and every read-back) went through repo-scoped REST, which answers 200/201 for this seat.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #6861: @object-ui/fields emits dist/__tests__/numberInputBrowserReadings.d.ts into its published tarball -- its tsconfig excludes tooling by NAME (**/*.test.ts) and not by DIRECTORY, the exact name-vs-directory mismatch that shipped in #4006 and #4836. Pre-existing on origin/main; blocks the release path, not this PR.",
"filed as #6862: MePermissionsProvider's fetchPermissions is a useCallback the fetch effect depends on, so a discard costs a redundant /me/permissions round trip and flips isLoaded. Same family, the EFFECT end rather than the value end. Deliberately not folded in: the input-keyed idiom this PR introduces does not transfer unchanged to four dependencies that include an optional fetcher and three primitives, so it fails the mechanical-fix test for an in-place bounded repair."
],
"zone_checks": {
"no_value_key_derived": "Confirmed. No round was spent on one; the fenced dead end stayed fenced.",
"pin_forces_a_discard": "Yes. Module-level proxy patching useMemo AND useCallback, discarding every armed cache. Ablation proves it falsifiable: 5 of 9 red on the pre-fix providers.",
"not_closed_as_covered_by_6724": "Correct -- #6819/#6724 hardened the hook end only; this hardens the provider end, and the chain is now discard-immune end to end.",
"latent_not_overstated": "Held. Changeset, code comments, pin header and PR body all state it as hardening on a licence React has not exercised here, with the 51/51/42 numbers and the absent Activity/Offscreen subtree.",
"public_surface_stop_condition": "Not reached. No published export changes; the context carries exactly the same keys, types and semantics.",
"incorrect_data_escalation": "Not triggered. The observable remains a redundant round trip on a discard, never incorrect data."
}
}
Activity
os-project-manager commentedon Aug 29, 2026
Triage (R+27) — first-touch on a fully bare card ⇒
pm:queue·domain:ui·priority:p2· typeTask.Lands in:
packages/permissions/src/PermissionProvider.tsx:124andMePermissionsProvider.tsx:285⇒domain:ui.Type
Task, ⛔ notBug: nothing misbehaves today. This is a correctness dependency resting on a licence React has not exercised here — hardening, not repair. p2, matching the card's own reading and the rest of the family.⭐ The measurement discipline here is the standard, and triage is adopting its conclusion unchanged
The card could easily have been written as "React may discard memo caches, therefore this is broken." It was not:
StrictMode— each returned ONE identity;Activity/Offscreen subtree exists in this repo;⛔ Nobody may upgrade this to "React discards our caches" on the strength of the card. It says the opposite, with numbers.
⛔ Why #6724 does not close it — the fence is right
#6724 made the hook's return stable while
ctxis unchanged, caching in a ref keyed onctxidentity, which a discard cannot move. ⭐ The card states the boundary exactly: "that is the whole guarantee the hook can give: what it returns must reflect the ctx it was handed. The providers are the other end."⇒ today the chain is discard-immune at the hook only; the providers can still hand
PermCtx.Providera fresh value with every permission unchanged, which moves the very keyusePermissions()caches on and re-runs the whole consumer chain (ListView's data-fetch effect — an extradataSource.find— plusDetailView's gatedSchema,ObjectForm/ModalForm/ObjectGrid/RelatedList). ⛔ Do not close this as covered by #6724.Scope — the route is already chosen, ⛔ do not re-derive it
⇒ take the route #6724 took: cache the value where React cannot drop it, keyed on the inputs it is derived from. And ⭐ the pin technique already exists —
packages/permissions/src/__tests__/usePermissions.discardedIdentity.test.tsx's forced-discard proxy is exactly the instrument, and the card names it as the one to reuse. ⇒ ⛔ a pin that does not force a discard proves nothing here, because React will not discard on its own.Ordering: sequence after #6724 (⚠️ Both cards are in
pm:dispatched,os-sales, same package). ⛔ Notpm:blocked— the providers exist onmainand are editable today; this is landing order plus a hot-package serial constraint for the seat to clear at claim time.packages/permissionswith the same test directory, so the fold-or-serial question is live and must be answered rather than defaulted.Family: #6018 · #5976 · #6591 · #6592 · #6697 · #6724 — same hazard, one link further up. ⇒ the class is being walked upstream deliberately; this is the provider end of it.
Generated by Claude Code
claude commentedon Aug 30, 2026
Claim: PM loop round R25
Session:
session_013hfmP9hoMd3dJwTh85J4yBBranch:
claude/issue-6813-permission-provider-ctx-identityWorktree:
objectui-issue-6813Domain:
domain:uiFile surface:
packages/permissions/src/PermissionProvider.tsx,packages/permissions/src/MePermissionsProvider.tsx+packages/permissions/src/__tests__/(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opusClause-②: no — internal memoisation hardening; no contract accept/reject change, no public surface widened.
Serial constraints cleared:
packages/permissions/**— PR #6819 (#6724, the hook end of this same chain) MERGED this round, which is the sequencing triage required. ⭐ It also ANSWERS the fold-or-serial question triage flagged as live: #6724 has landed, so there is no sibling left to fold with — this card is standalone.Generated by Claude Code
claude commentedon Aug 30, 2026
Claim (execution seat):
domain:uidev subagent, dispatched by the PM claim above.Session:
session_013hfmP9hoMd3dJwTh85J4yB— the same session id as the PM comment, because this seat runs as a subagent inside that session rather than as its own session. The branch below is the identity bit that distinguishes this seat's work.Branch:
claude/issue-6813-permission-provider-ctx-identityWorktree:
objectui-issue-6813Base:
1e14d70ae8b8c5b3a42f9d5b2e30d3c1f5bdfdcfFile surface:
packages/permissions/src/PermissionProvider.tsx,packages/permissions/src/MePermissionsProvider.tsx,packages/permissions/src/__tests__/— plus one new internal module in the same package,packages/permissions/src/discardProofCache.ts, which carries the shared idiom both providers use. It is deliberately NOT exported fromindex.ts: no published export changes and the context carries exactly what it carried before.Assignee: untouched, per dispatch.
Route taken, per triage: no value key was derived (that dead end was fenced and stayed fenced). Both providers now cache where React cannot drop it, keyed on the identities of the inputs each cached thing is derived from — the technique PR #6819 landed for
usePermissions()one link down this chain. The pin FORCES a discard, as triage required.Generated by Claude Code
Generated by Claude Code
claude commentedon Aug 30, 2026
os-dev-report
{ "issue": 6813, "status": "done", "branch": "claude/issue-6813-permission-provider-ctx-identity", "pr": "https://github.com/objectstack-ai/objectui/pull/6863", "premise_still_valid": true, "summary": "Both providers now build their context value in a module-level WeakMap keyed on the identities of the inputs each cached thing is derived from, replacing the useMemo-over-useCallbacks chain React is licensed to discard. One shared internal idiom (packages/permissions/src/discardProofCache.ts, not exported from index.ts) serves both, so there is one idiom and not two. Every dependency set is preserved exactly, because useFieldPermissions names checkField/getFieldPermissions and three app-shell views name getObjectApiOperations in their own dependency arrays; three members that can never vary became module constants. WeakMap rather than useRef on purpose: a ref cache means a render-phase ref access, which is the react-hooks/refs shape #6745 and #6797 were opened and closed to remove, and eslint confirms 0 errors / 28 warnings identical to base with no react-hooks/refs in either. No published export changes and the context carries exactly what it carried before, so the public-surface stop condition was not reached. Framed throughout as hardening on a latent hazard, never as a bug fix, per ZONE 1b.", "tests": "All runs on cee2b98 (final commit), from the repo root -- package-level `pnpm --filter <pkg> test` is refused here by the objectui#3378 guard, which is a NOT-MEASURED and was re-run correctly rather than reported as a red. `pnpm exec vitest run --maxWorkers=2 packages/permissions/` => 'Test Files 8 passed (8) / Tests 85 passed (85)'. Downstream sweep over every file in the repo mounting either provider or PermCtx.Provider (8 consumer test files plus app-shell console home tests) => 'Test Files 20 passed (20) / Tests 126 passed (126)'. `pnpm --filter @object-ui/permissions type-check` => exit 0, script name echoed (`tsc --noEmit && tsc -p tsconfig.test.json`), and `tsc -p tsconfig.test.json --listFiles` confirms it actually covers discardProofCache.ts and the new pin rather than excluding them. ABLATION: both provider files reverted to base 1e14d70ae with the pin in place, run from a COMMITTED state; mutation confirmed on disk before measuring (createDiscardProofCache count 0/0, useMemo count 2/2, blob hashes 899df91f->b1c76c2a and 5e3d6070->70f69df2), giving 'Tests 5 failed | 4 passed (9)' -- the 5 reds are exactly the identity cases ('expected 3 to be 1': three distinct ctx identities across two discards), the 4 greens are the controls (proxy canary, both genuine-change cases, answers-unchanged). Restore leg proven, not assumed: `git checkout HEAD -- <abs paths>` under an EXIT/INT/TERM trap using absolute paths, blob hashes back to the HEAD blobs exactly and `git diff HEAD` empty. No dist/build leg applies -- vitest resolves these imports from source.", "gates": { "check:control-bytes": "OK (scanned 5711 tracked text files)", "check:vi-mock-specifiers": "OK (covers the new pin's vi.mock('react'))", "check:esm-specifiers": "OK (covers the new './discardProofCache.js' specifier)", "check:self-import": "OK", "check:phantom-deps": "OK", "check:side-effects-array": "OK", "check:readme-exports": "OK after a full build; PREREQUISITE NOT MET on an unbuilt tree (it says so itself: 'the population COLLAPSED -- this run proves nothing')", "check:eager-closure": "OK after a full build; PREREQUISITE NOT MET without one ('a broken gauge, not a sensitive gate')", "check:published-dist": "RED, and not from this branch -- one finding in @object-ui/fields present on origin/main, filed as #6861. Carries no pull_request trigger by design, so it cannot redden this PR, but it does block the release path.", "eslint packages/permissions": "0 errors, 28 warnings -- byte-identical rule breakdown to base, and zero react-hooks/refs in either", "method": "Gate family derived by hand from THIS repo's package.json and .github/workflows for the paths the diff actually touches. objectstack's scripts/pm/dispatch-gates.mjs was deliberately NOT used: it only answers about its own tree and would have returned objectstack's gate family for objectui paths." }, "line_budget": "No skills/** in the diff, so no published-skill budget applies. Diff vs base 1e14d70ae: 5 files, +768/-76. Per file -- MePermissionsProvider.tsx +80/-40, PermissionProvider.tsx +72/-36 (both structure-preserving, every load-bearing comment carried over verbatim), discardProofCache.ts +105 (new; roughly two thirds of it the doc comment justifying WeakMap over useRef and a tuple key over a stored dependency list), providerCtxIdentity.discarded.test.tsx +476 (new pin), changeset +35.", "files_changed": [ "packages/permissions/src/PermissionProvider.tsx", "packages/permissions/src/MePermissionsProvider.tsx", "packages/permissions/src/discardProofCache.ts", "packages/permissions/src/__tests__/providerCtxIdentity.discarded.test.tsx", ".changeset/tidy-permissions-provider-ctx-identity.md" ], "deviations": [ "One file beyond the declared surface, inside the same package: packages/permissions/src/discardProofCache.ts. The dispatch asked for ONE idiom across both providers and that idiom needs somewhere to live; it is internal and deliberately not exported from index.ts, so no published export changes. Declared in the claim comment on #6813 before the PR opened.", "Package-level `pnpm --filter @object-ui/permissions test` is refused by this repo's objectui#3378 guard (running vitest from a package directory silently runs the console's 22 files and reports a false green). Read as NOT MEASURED and re-run from the repo root, not reported as a failure.", "DECLARED NARROWING: repo-wide `pnpm lint` was not run. Lint was measured on packages/permissions only, before and after, with an identical result -- the base-vs-head comparison is what makes it a measurement rather than a gap. CI runs the full farm regardless.", "ZONE 2 claim 2 re-measured and it differs slightly from the card: 9 single-line dependency arrays name the whole perms object, across the same 6 files the card and #6819's pin header list. The 6 files match exactly; the count differs from the 13 recorded in #6819's header because this pattern matches single-line dependency closers only. Reported rather than silently adopting either number.", "The branch commit carries `Part of #6813` while the PR body carries `Fixes #6813` as dispatched. `Part of` is not a closing keyword, so the two sources do not conflict on squash; not amended, because that would need a force-push." ], "mcp_calls": "1 -- a single mcp__github__issue_read to open the card. Everything else (all comments, the 250-issue dedupe listing, the claim comment, both filed findings, the PR, and every read-back) went through repo-scoped REST, which answers 200/201 for this seat.", "open_questions": [], "out_of_scope_findings": [ "filed as #6861: @object-ui/fields emits dist/__tests__/numberInputBrowserReadings.d.ts into its published tarball -- its tsconfig excludes tooling by NAME (**/*.test.ts) and not by DIRECTORY, the exact name-vs-directory mismatch that shipped in #4006 and #4836. Pre-existing on origin/main; blocks the release path, not this PR.", "filed as #6862: MePermissionsProvider's fetchPermissions is a useCallback the fetch effect depends on, so a discard costs a redundant /me/permissions round trip and flips isLoaded. Same family, the EFFECT end rather than the value end. Deliberately not folded in: the input-keyed idiom this PR introduces does not transfer unchanged to four dependencies that include an optional fetcher and three primitives, so it fails the mechanical-fix test for an in-place bounded repair." ], "zone_checks": { "no_value_key_derived": "Confirmed. No round was spent on one; the fenced dead end stayed fenced.", "pin_forces_a_discard": "Yes. Module-level proxy patching useMemo AND useCallback, discarding every armed cache. Ablation proves it falsifiable: 5 of 9 red on the pre-fix providers.", "not_closed_as_covered_by_6724": "Correct -- #6819/#6724 hardened the hook end only; this hardens the provider end, and the chain is now discard-immune end to end.", "latent_not_overstated": "Held. Changeset, code comments, pin header and PR body all state it as hardening on a licence React has not exercised here, with the 51/51/42 numbers and the absent Activity/Offscreen subtree.", "public_surface_stop_condition": "Not reached. No published export changes; the context carries exactly the same keys, types and semantics.", "incorrect_data_escalation": "Not triggered. The observable remains a redundant round trip on a discard, never incorrect data." } }Generated by Claude Code
Generated by Claude Code
perf(permissions): build both providers' context value where React ca…