Repository navigation
[finding] sys_position.permissions is a declared "JSON-serialized array of permission strings" column and nothing writes or reads it #9885
Description
Activity
Triage first-touch grading: graded off
finding→needs-user-decision(enforce-or-remove channel — removing an authorable, published column is on the human floor, same as its class siblings; the patrol's own no-verdict rule binds the measurement, and this grading is the downstream step it feeds).- platform long-term coherence: grants flow through permission-set bindings only (
sys_position_permission_set); a parallel free-textpermissionscatalogue on Position is a second, never-implemented grant vocabulary — removing it shrinks the security model to the one real path. Same class as the ruled [finding]sys_user_permission_set.delegated_fromis declared and data-door-writable, but the runtime delegation gate structurally never reads it on this object #9730 (delegated_from, REMOVE 2026-08-18) andsys_permission_set.activeandsys_position.activeare unenforced too — both Deactivate dialogs promise access stops, and it does not #8613. - measured business pull: zero — 97
sys_position-naming files read; no producer, no reader; the only in-file reference isclone_positioncopying a value nothing writes. Nobody is using it. - AI-agent error-resistance: the declared textarea tells an AI author "grant permission strings directly on a position" — a capability the runtime does not have; an agent following the declaration authors security config that silently does nothing. Removal makes the wrong model unwritable.
- startup scope discipline: implementing position-level direct grants (the enforce arm) is a whole security feature with no pull; remove wins by default under the standing enforce-or-remove posture.
Recommendation: A — REMOVE
sys_position.permissions(and theclone_positioncopy entry) via the standard ADR-0049/ADR-0087 retirement flow, docs/table riders included; objectui designer-preview check (the stated evidenceScope gap) folded into the retirement PR's checklist. B — enforce: design and implement direct position-level permission grants (a real security feature — only if the maintainer wants that capability). C — leave declared-and-inert.Confidence gap:
objectuiwas not searched (evidenceScope: this repo only) — if the console's position designer renders or persists this column, removal needs the consumer-side rider; and this analysis cannot see customer deployments that may have hand-filled the column as documentation-of-intent.
Generated by Claude Code
- platform long-term coherence: grants flow through permission-set bindings only (
Maintainer ruling recorded (2026-08-20, live decision-inbox session with the triage seat, session
session_01PjAP6vbcsg2yMtvySPv1Qo)Ruled: remove
sys_position.permissionsunder ADR-0049 enforce-or-remove. Provenance: maintainer accepted the batch, verbatim: 「其他接受你的建议。」Zero producers, zero readers (object-scoped 97-file sweep with same-object positive controls); the declared surface reads as a capability catalogue the platform does not have, which is exactly the AI-misleading shape the patrol exists to catch. Scope includes theclone_positioncopy line ({ field: 'permissions', defaultFromRow: true }) — a copy of a value nothing writes.Route per the
spec-property-retirementplaybook (ledger verdict confirmation, ADR-0087 conversion, baselines/forms/docs/pin tests as applicable to a platform-object row column). Clause-②: yes (declared-surface removal) — contract-review tier. objectui was not searched (evidenceScope) — the dev's first step is the consumer check there before deleting.State transition in the same stroke:
needs-user-decision→pm:queue.
Generated by Claude Code
Claim: PM loop round 1
Session:session_01DdCnBGcHeufjrq7drTD3wt
Branch:claude/issue-9885-retire-sys-position-permissions
Worktree:objectstack-issue-9885
Domain:domain:services—⚠️ not this seat's lane; maintainer direct-dispatch, see below
Container & model:M,mode:subagent,model: claude-fable-5(CONTRACT_REVIEW_TIER, read live fromscripts/pm/dispatch-gates.mjs:1943)
File surface:packages/plugins/plugin-security/src/objects/sys-position.object.ts+ the retirement playbook's required riders (ledger, ADR-0087 conversion, generated baselines, forms, docs, pin tests) + changeset (stop on breach; explain in the report)
Clause-②: yes — declared-surface removal
Serial constraints cleared:No open PR touches packages/plugins/plugin-security — repo-wide open PRs at claim time: #10150, #10147, #10157. Sibling in-flight from this seat: #9930 (plugin-webhooks), #10091 (service-storage), #9877/#10138 (scripts/**) — all disjoint. ⚠️ #9930 is in the same batch and also runs a retirement-shaped flow; different package, different key, no file overlap.⛔ Cross-lane authorisation — maintainer direct-dispatch channel
domain:servicescard, dispatched by thedomain:devxseat. Maintainer's instruction to this session, verbatim:Per-card.
domain:*untouched. It comes here for the same reason as #10091 and #9930: Clause-② requires the contract-review tier, and that lane has no fable. This session does, so it runs at the ruled tier — no deviation, no exemption.裁决 (maintainer, 2026-08-20, 「其他接受你的建议。」 — recorded in
5353923232)REMOVE
sys_position.permissionsunder ADR-0049 enforce-or-remove. Scope includes theclone_positioncopy entry ({ field: 'permissions', defaultFromRow: true },sys-position.object.ts:118) — a copy of a value nothing writes. Route per thespec-property-retirementplaybook: ledger verdict confirmation, ADR-0087 conversion, and the baselines / forms / docs / pin tests that apply to a platform-object row column.⛔ Option B (implement position-level direct grants) is ruled out. ⛔ Option C (leave declared-and-inert) is ruled out.
⭐ Your first step is the objectui consumer check — and I have removed the excuse for skipping it
The ruling says: "objectui was not searched (evidenceScope) — the dev's first step is the consumer check there before deleting." This is the card's one real gap: a console position designer that renders or persists this column is a consumer under the 2026-08-10 ruling (though not enforcement, and not a producer), and it would need a consumer-side rider before deletion.
⚠️ You will find that the GitHub API 403s onobjectstack-ai/objectui. That is NOT "objectui is unreachable" — I measured both channels:GET https://api.github.com/repos/objectstack-ai/objectui -> 403 (API: repo not attached to this session) git clone --depth 1 https://github.com/objectstack-ai/objectui -> WORKS (public repo, anonymous git read via the proxy)⇒ Clone it and grep it. ⛔ Do not report the consumer check as "could not verify" on the strength of the API refusal — an absence measured with the wrong instrument is not an absence, and this exact conflation has now cost this seat three separate wrong conclusions today.
GIT_LFS_SKIP_SMUDGE=1 git clone --depth 1 https://github.com/objectstack-ai/objectui /workspace/objectstack-ai/objectui(generous timeout — a shallow pack through the proxy can take several minutes and
git index-packlooks stalled while unpacking; ⛔ do not interrupt or re-run it. If the path already exists, checkgit -C … rev-parse HEADbefore touching it — a concurrent clone may be live.)Then search for a
sys_position+permissionsconsumer, with same-object positive controls so a zero-hit is a real absence rather than a broken search — the card's own method, and the reason its in-repo claim is credible. ⛔ This session cannot push to objectui; if a consumer exists, that is a finding and aBlocked-by:relationship, not something you fix.PM 机制假设 (measure — falsifying these is a good run)
The card's in-repo measurement is unusually well-controlled (97
sys_position-naming files read; positive controlsactive,delegatable,is_default,nameall resolving to real readers; cross-object controlsvalid_until,granted_by). Re-derive it anyway — it was taken atorigin/main@07bfd31cband today is2d3860df9.⚠️ Note the card's own warning that the bare tokenpermissionscollides generically (441 files), so the object-scoped second pass is the load-bearing one; ⛔ do not "confirm" it with a whole-tree grep.⚠️ This container is a depth-50 shallow clone — usegit show origin/main:<path>/git grep … origin/main, ⛔ nevergit logfor history. Anchor by symbol, not line number (:196and:118are from the census ref).⛔ Two fences
- Governed surface: ⛔ do not write
docs/adr/**. If the ADR-0087 conversion needs an ADR entry authored, draft it in the PR body and flag it — the maintainer authors it. - Removing a published authorable column is a real retirement, not a deletion. If the playbook's ceremony turns out to exceed one card — or if the ledger verdict does not confirm
dead— reportneeds_decisionand stop rather than half-executing it. That is a complete delivery. (Precedent worth knowing: on FieldSchema acceptsdeleteBehavior: 'set_null'on amaster_detail, and the engine silently resolves it tocascade#9689 a dev was sent to this same playbook and correctly found §0 did not apply to its case; following the instruction literally would have red-gated. Read the playbook against this key's facts.)
Premise-first
The body is a clue, not a spec.
premise_still_valid: falseis a legitimate delivery — if a producer or reader has landed since the census, that finding outranks the ruling's shape and you should report it rather than deleting anyway.Final report: structured JSON report contract, returned AND posted here with first line
<!-- os-dev-report -->. Machine-checkable fields only.
Generated by Claude Code
- Governed surface: ⛔ do not write
- added a commit that references this issue
on Aug 20, 2026 { "issue": 9885, "status": "done", "branch": "claude/issue-9885-retire-sys-position-permissions", "pr": "https://github.com/objectstack-ai/objectstack/pull/10182", "premise_still_valid": true, "summary": "Removed sys_position.permissions and the clone_position defaultFromRow copy entry per the 2026-08-20 ruling. FIRST STEP as directed: objectui consumer check via git clone (HEAD fd227eae1d33, API 403 not treated as absence) — 31 sys_position-naming files, zero consumers of the column, positive controls name/label/managed_by resolve to real readers under the identical instrument; no curated field list or designer names it, clone_position absent there. Census re-derived at origin/main cc21aad8ed: zero producers, zero readers, controls resolve — premise valid. Route read against this key's facts (#9689 lesson): PositionSchema never declared the key, so this is a platform-object COLUMN retirement on the ups-delegated-from-column-retired precedent — D3 semantic entry position-permissions-column-retired under major 17, nothing in RETIRED_KEYS_BY_MAJOR, no liveness row (ledger walks PositionSchema's shape; a row would be an ORPHAN — that is the ledger-verdict confirmation, with the re-derived census as the dead measurement), surface ratchets byte-identical as the route expects. Riders: locale bundles regenerated via os i18n extract (exactly 4 leaves removed), regenerated registry/spec-changes/upgrade-guide projections, removal pin tests with a positive control, minor+BREAKING changeset for plugin-security+spec stating the accept-set narrowing plainly, adr-0087 marker registered. Judgment call flagged in the PR body: added a PositionSchema strict-parse guidance entry for `permissions` (the mechanism's documented purpose is tombstones for retired keys) — one hunk to drop if judged out of scope. docs/adr/** untouched; no new ADR entry needed. CI status at report time: in_progress (report delivered at draft-PR time per contract).", "tests": "All at head bba754b (final commit; nothing changed after). pnpm --filter @objectstack/spec test: 'Test Files 414 passed (414) / Tests 11041 passed (11041)'. pnpm --filter @objectstack/plugin-security test: 'Test Files 66 passed (66) / Tests 1303 passed (1303)'. Honesty note per #10166: the appended '-- --maxWorkers=2' was swallowed by pnpm, so both runs were FULL package suites — stronger coverage than intended, ~6.5min lock hold; lesson taken. Playbook gate battery, redirect-then-capture per gate (no pipes): check:migration-registry, check:spec-changes, check:upgrade-guide, check:authorable-surface, check:liveness, check:empty-state, check:docs, check:api-surface, check:variant-docs, check:skill-refs, check:skill-docs, check:skill-examples, spec typecheck, plugin-security typecheck = 14/14 PASS. dispatch-gates.mjs (no path args, derived from real diff incl. changeset) union = 22/22 PASS incl. convention-triggered check:query-options-erasure, check:type-check-coverage, check:type-check-debt (after the full packages closure build it requires), check:engine-double-contract, check:where-matcher, check:i18n, and check-adr-0087-registration / check-changeset-no-major / check-empty-changeset. check-nul-bytes: OK. Ratchet dirs authorable-surface/api-surface/json-schema.manifest: byte-identical (expected for this route per the delegated_from precedent). No ablation legs were run: the pins are direct structural assertions on in-package src imports (relative './index.js', no dist resolution), and no schema member with expression surface was deleted, so no rebuild-dependent leg exists in this diff.", "open_questions": [], "out_of_scope_findings": [] }
Generated by Claude Code
os-dev-report
(previous comment 5356238646 is this same report; its HTML-comment marker was eaten by the sanitizer, so this copy carries the literal marker line the scan needs){ "issue": 9885, "status": "done", "branch": "claude/issue-9885-retire-sys-position-permissions", "pr": "https://github.com/objectstack-ai/objectstack/pull/10182", "premise_still_valid": true, "summary": "Removed sys_position.permissions and the clone_position defaultFromRow copy entry per the 2026-08-20 ruling. FIRST STEP as directed: objectui consumer check via git clone (HEAD fd227eae1d33, API 403 not treated as absence) — 31 sys_position-naming files, zero consumers of the column, positive controls name/label/managed_by resolve to real readers under the identical instrument; no curated field list or designer names it, clone_position absent there. Census re-derived at origin/main cc21aad8ed: zero producers, zero readers, controls resolve — premise valid. Route read against this key's facts (#9689 lesson): PositionSchema never declared the key, so this is a platform-object COLUMN retirement on the ups-delegated-from-column-retired precedent — D3 semantic entry position-permissions-column-retired under major 17, nothing in RETIRED_KEYS_BY_MAJOR, no liveness row (ledger walks PositionSchema's shape; a row would be an ORPHAN — that is the ledger-verdict confirmation, with the re-derived census as the dead measurement), surface ratchets byte-identical as the route expects. Riders: locale bundles regenerated via os i18n extract (exactly 4 leaves removed), regenerated registry/spec-changes/upgrade-guide projections, removal pin tests with a positive control, minor+BREAKING changeset for plugin-security+spec stating the accept-set narrowing plainly, adr-0087 marker registered. Judgment call flagged in the PR body: added a PositionSchema strict-parse guidance entry for `permissions` (the mechanism's documented purpose is tombstones for retired keys) — one hunk to drop if judged out of scope. docs/adr/** untouched; no new ADR entry needed. CI status at report time: in_progress (report delivered at draft-PR time per contract).", "tests": "All at head bba754b (final commit; nothing changed after). pnpm --filter @objectstack/spec test: 'Test Files 414 passed (414) / Tests 11041 passed (11041)'. pnpm --filter @objectstack/plugin-security test: 'Test Files 66 passed (66) / Tests 1303 passed (1303)'. Honesty note per #10166: the appended '-- --maxWorkers=2' was swallowed by pnpm, so both runs were FULL package suites — stronger coverage than intended, ~6.5min lock hold; lesson taken. Playbook gate battery, redirect-then-capture per gate (no pipes): check:migration-registry, check:spec-changes, check:upgrade-guide, check:authorable-surface, check:liveness, check:empty-state, check:docs, check:api-surface, check:variant-docs, check:skill-refs, check:skill-docs, check:skill-examples, spec typecheck, plugin-security typecheck = 14/14 PASS. dispatch-gates.mjs (no path args, derived from real diff incl. changeset) union = 22/22 PASS incl. convention-triggered check:query-options-erasure, check:type-check-coverage, check:type-check-debt (after the full packages closure build it requires), check:engine-double-contract, check:where-matcher, check:i18n, and check-adr-0087-registration / check-changeset-no-major / check-empty-changeset. check-nul-bytes: OK. Ratchet dirs authorable-surface/api-surface/json-schema.manifest: byte-identical (expected for this route per the delegated_from precedent). No ablation legs were run: the pins are direct structural assertions on in-package src imports (relative './index.js', no dist resolution), and no schema member with expression surface was deleted, so no rebuild-dependent leg exists in this diff.", "open_questions": [], "out_of_scope_findings": [] }
Generated by Claude Code
✅ ACCEPT — PR #10182. Reviewer of record: seat #6023, session
session_01DdCnBGcHeufjrq7drTD3wt, 2026-08-20T13:0xZ (date -u).Ruling on the flagged judgement call first: KEEP the
PositionSchemaguidance hunk. ⛔ Do not drop it. Reasoning below.⭐ The objectui consumer check was done properly, and I re-ran it
This was the ruling's named first deliverable and the card's one real evidence gap. It was done by clone, not by treating the API's 403 as an absence:
/workspace/objectstack-ai/objectui HEAD fd227ea origin …/objectui ← matches the reported clone 31 files name sys_position ← matches the reported count exactlyI re-derived the negative myself, and it took two attempts, which is worth recording: my first instrument returned zero for
permissionsAND zero for the positive controlsname/label/managed_by— a broken regex, so that negative proved nothing. Re-run as "greppermissionsinside the 31 files that namesys_position", with a control that fires: 17 files co-mention both, and every hit is unrelated —@object-ui/permissionspackage imports,usePermissions/useFieldPermissions/MePermissionsProvider- the
system/permissionsroute, whichAppContent.tsx:137explicitly maps tosys_permission_set SystemHubPage'spermissions:count bucket (countOrUnknown(permsRes)— permission sets)requiredPermissions, an action-bar prop; and one prose comment
⇒ Zero consumers of
sys_position.permissionsin objectui. Confirmed. ⭐ And the console's own routing is corroborating evidence for the card's thesis: it sends "permissions" to permission sets, because that is the only real grant path.⭐ The route was read against THIS key's facts, not by analogy
I warned about the #9689 precedent — a dev sent to this playbook that found §0 did not apply, where following the instruction literally would have red-gated. That warning paid off in the opposite direction: the playbook was followed, but only after establishing which route this key is on.
PositionSchemanever declaredpermissions(verified: zero hits onorigin/main). So this is a platform-object column retirement on theups-delegated-from-column-retiredprecedent, not a spec-key retirement:- D3 semantic entry
position-permissions-column-retiredunder major 17 ✅ - nothing in
RETIRED_KEYS_BY_MAJOR— correct, the key was never a schema key - no liveness row — the ledger walks
PositionSchema's shape, so a row would be an orphan. ⭐ That reasoning is the ledger-verdict confirmation the ruling asked for, with the re-derived census as the dead measurement - surface ratchets byte-identical, as that route predicts
The removal is complete and tombstoned
sys-position.object.tsdrops exactly the two things the ruling named — thepermissions: Field.textarea({…})declaration and the{ field: 'permissions', defaultFromRow: true }clone_positioncopy entry (that params block is nowlabel/name/descriptiononly). The three survivingpermissionsmentions in the file are: one unrelatedconfirmTextphrase and two dated tombstone comments. ⛔ No residue.Ruling: the guidance hunk stays
The dev offered to drop it. It is the strongest part of the retirement and it is in scope. The text is honest about its own status — it opens "
permissionsis not a Position field", so it does not imply the schema ever accepted the key — and it carries the prescription: capability reaches a position only throughsys_position_permission_setbindings; delete the key and bind a set instead.Why it belongs: the card's own four-axis analysis says the harm is that "the declared textarea tells an AI author 'grant permission strings directly on a position' — a capability the runtime does not have." Removing the column closes the declaration, but an agent working from a cached example, an older app definition, or training data will still write
permissions— and would get a bare strict-parse rejection with no explanation. The guidance entry is what makes the removal teach instead of merely refuse, which is the "响亮拒绝" axis rather than a silent one. It also sits in an existing guidance map beside a sibling entry, so it follows the file's pattern rather than inventing one.Gates and honesty
14/14 playbook battery + 22/22
dispatch-gatesunion (self-derived from the real diff, so it reached beyond my dispatch list — includingcheck:type-check-debtafter the full closure build,check-adr-0087-registration,check-changeset-no-major). Locale bundles regenerated viaos i18n extractwith exactly 4 leaves removed.minor+ BREAKING changeset acrossplugin-security+specstating the accept-set narrowing plainly.docs/adr/**untouched, as fenced.⚠️ Docs: no rider needed, and that is measured. The drift bot flagged three hand-written permission pages. I read all three: everypermissionsoccurrence is a permission set,systemPermissions,stack.permissions, orrequiredPermissions. No page ever documented the removed column — the rows fired on thesys_positionsymbol. The four release-owned pages were correctly left read-only.⭐ No ablation legs, with a structural reason rather than a shrug: the pins are direct structural assertions over in-package relative
srcimports with no dist resolution, and no schema member with expression surface was deleted — so there is no rebuild-dependent leg for an ablation to defeat. Declaring why a verification step does not apply is the right move; silently omitting it is not.⚠️ #10166 hit again, self-reported: the appended-- --maxWorkers=2was swallowed, so both suites ran full (~6.5 min lock hold). Stronger coverage than intended, and the honesty note is the right response — but this is now the third independent reproduction today, which strengthens #10166's case considerably.⚠️ Sanitizer ate the report marker again (5356238646→ re-posted as5356243897with a literal marker). Second confirmed instance today. Any collector grepping for that marker will miss reports; marker-absence ≠ report-absence.Disposition
ACCEPT.
⚠️ This PR does trip Clause ②'s path limb —packages/spec/src/identity/position.zod.tsand three siblings underpackages/spec/src/**. It is still enqueueable, because the gate fires only when a limb hits and the dispatch tier is belowCONTRACT_REVIEW_TIER; this ran at it (claude-fable-5). ⇒ Noneeds:contract-reviewowed. ⭐ Third card this round whose legality rests on that dispatch decision.⛔ Not flipping ready until CI converges; then ready → auto-merge → enqueue verified against
refs/heads/gh-readonly-queue/main/*.⚠️ Note for thedomain:specseat (#6017): this lands 4 files inpackages/spec/src/**— a semantic migration entry, the registry,position.zod.tsand its test. Filed under adomain:servicescard by maintainer direct-dispatch; flagging rather than assuming it is unwelcome.
Generated by Claude Code
- added a commit that references this issue
on Aug 23, 2026 - added a commit that references this issue
on Aug 25, 2026 - added a commit that references this issue
on Oct 7, 2026
Measured by the recurring no-verdict enumeration patrol established in the #9054 ruling (2026-08-16): "run the no-verdict enumeration periodically … and report 'keys with zero readers' as findings — enumeration without verdicts, never a red gate, never a ledger row." Filed unassigned by the triage seat (session
session_01JEhkB6ShK2HHDzozy7Qwgz); observation-class. ⛔ No verdict attached and none should be added — this card records a measurement, not a liveness claim.The key
packages/plugins/plugin-security/src/objects/sys-position.object.ts:196declares onsys_position:permissions—Field.textarea, "JSON-serialized array of permission strings", group Configuration.The measurement (at origin/main
07bfd31cb)A whole-tree word search (
git grep -lw permissionsoverpackages/,apps/,examples/, all.ts/.tsx, tests included) is inconclusive for this token by itself — 441 files, which is exactly the generic-token collision #8894's measurement documented (ExecutionContext.permissions,object_permissions, route paths, …). So the second pass is object-scoped, per that measurement's own lesson: all 97 files namingsys_positionin code were read for apermissionsrow-column access. Result:bootstrap-builtin-positions.ts(writeslabel,description,managed_by,active,is_default) andbootstrap-declared-positions.ts(writeslabel,description,active,is_default). Neither writespermissions.packages/core/src/security/resolve-authz-context.tsresolves position-bound sets throughsys_position_permission_setrows (:474-476) andsys_position.name(seepackages/spec/liveness/position.json: "sys_position.name keys position-bound permission-set resolution");delegated-admin-gate.tsreadsname/delegatable/is_default/business_unit_id;sharing-rule-service.tsreadsactive. Every otherpermissionsoccurrence in those 97 files isExecutionContext.permissions(caller-context set names), asys_permission_setcolumn (object_permissionsetc.), or route/prose text.clone_positionaction copies it ({ field: 'permissions', defaultFromRow: true },sys-position.object.ts:118, comment "permissions JSON is copied wholesale") — a copy of a value no code ever writes or evaluates.Why this is the ADR-0049 class
The declared surface reads as a capability catalogue: a
Permissionstextarea on Position tells an admin — and an AI agent authoring against this model — that granting permission strings directly on a position is a thing the platform does. It is not: grants flow only through permission-set bindings. Same shape assys_permission_set.activepre-#8613 and the #9046 D5 pair, on the same ungoverned row-column half of the ADR-0049 surface. Notably, #8894's read-oracle scored this key green for the same reason it stayed green onsys_permission_set.active— the token is generic — which is precisely the blind spot the no-verdict patrol exists to cover by hand.Corroboration that this is not a search artifact
Same-object positive controls under the identical search shapes:
sys_position.activeresolves to real readers (sharing-rule-service.ts:65,92;row-active.tsper #8710),delegatableandis_defaulttodelegated-admin-gate.ts(:758for is_default),nametopermission-evaluator.ts:380. Cross-object controls:valid_until→delegated-admin-gate.ts:476;granted_by→sharing-plugin.ts:873. Onlypermissionscomes back empty on both sides.evidenceScopediscipline (packages/spec/liveness/README.md).objectuiwas not searched; a designer preview rendering the column would count as a consumer under the 2026-08-10 ruling — though not as enforcement, and not as a producer.Patrol coverage note for the record: this round enumerated the full #8894 surface — 88 candidate keys across the 10 plugin-security/plugin-sharing platform objects; 85 live, 4 already covered by #9046 (the D5 pair ×2 tables), 1 already adjudicated (#8535,
sys_capability.active), and this key is the only unfiled zero/zero. Dedup checked (key-name, symptom, patrol phrasing): only #8710 surfaces, which concernssys_position.active— a different key.Refs: #9054 (the ruling that runs this patrol) · #8894 (the measurement + oracle) · #9046 (the D5 pair, same patrol) · #8613 · #8535 · ADR-0049 · ADR-0090.