Skip to content

skills/objectstack-data/references/data-hooks.md: rewrite the reserved word "role" to ADR-0090 D3 vocabulary, keeping one retirement note — the residue is what PR #15073 baselines #15177

Closed

Description

@hotlong

Filed by the director seat (summon 13, session_01WXyGTWPbbreqXow7Z2pZCk) on the maintainer's ruling of 2026-09-04 on decision card #15075 (option B of the director's re-evaluation; verbatim reply in chat: 「按照你的建议,跟进并处理完这两个pr。」). Routed directly to domain:skills under the maintainer's direct-dispatch channel; triage may confirm. Governed surface (skills/**): draft PR, review requested from both os-zhuang and hotlong, human merge.

Why

PR #15073 widens check:role-word to the published catalog's references/ pages and reds on this page: 12 occurrences of the reserved word. The decision card proposed admitting all 12 to the shrink-only baseline as "quoted pre-rename history". Read line by line on origin/main, only part of that holds:

lines occurrences what they are disposition
350 (table row) 2 the retirement note: session.roles retired in 17.0.0, never produced keep (one retirement note is legitimate history)
574-575 (comment) 2 the same retirement note again, plus "never by a role name" fold into one sentence without the reserved word ("privilege is judged by the security service, never by a session claim")
733 (example) 1 where: { role: 'admin' } querying a user field named role rewrite the example to a real field or a permission-set check; no sys_user field is named role
822 (callout) 1 "always hidden/masked for a role" D3 vocabulary: "for a permission set or position"
837-841 (comment) 6 the retirement note a third time, plus "role name", "role list", "role-aware", "per-role exemption" keep the one useful warning (do not write ctx.session?.roles?.includes), spell the rest in D3 vocabulary

Target: the page teaches permission sets / positions / business units, states the session.roles retirement once (the table row) and warns once against the ctx.session?.roles pattern; expected residue 3 to 4 occurrences, all quoted history. This page ships verbatim to third parties via npx skills add, which is why the reserved-word ratchet reads it at all.

Definition of done

Blocks: #15073 (card #15061) — that PR lands after this page merges, with the baseline entry for the residue.

Refs: #15061 · PR #15073 · #15075 (ruling) · ADR-0090 D3.

Activity

  1. claude commented on Sep 4, 2026

    @claude
    Contributor

    Claim: PM loop round 5 — ruled work (#15075 → B)
    Session: session_019RfFHiRCSs3JXLK4cwcfox
    Branch: claude/issue-15177-data-hooks-role-vocabulary
    Worktree: objectstack-issue-15177
    Domain: domain:skills
    File surface: skills/objectstack-data/references/data-hooks.md only (stop on breach; explain in the report). ⛔ Not scripts/role-word-baseline.json (the residue entry is PR #15073's, authorised for that PR), ⛔ no script, ⛔ no other page.
    Container & model: S, mode:subagent, model: opus (published skills/** reference page — the catalog face; opus construction with the seat's contract-tier review as the compensating control, per the 2026-08-20 tiering rule)
    Clause-②: no (prose vocabulary on a published page; the reserved-word ratchet's contract is unchanged)
    Serial constraints cleared: the page is free (PR #15073 touches two scripts and the baseline only, and it now waits on this card — #15061 pm:blocked, Blocked-by: #15177); no other open PR touches skills/objectstack-data/**. Governed (skills/**) ⇒ draft PR, in-seat contract-tier review, review requested from os-zhuang and hotlong, human merge.

    Decision re-read (05:02Z): maintainer ruling B on #15075 (director record 5535828546, corrected by 5535830096; verbatim 「按照你的建议,跟进并处理完这两个pr。」) as carried on this card's table — the :350 table-row retirement note is kept (one legitimate history statement); :574-575 folds into one sentence without the reserved word; the :733 example is rewritten to a real field or a permission-set check (no sys_user field is named role); :822 becomes D3 vocabulary ("for a permission set or position"); :837-841 keeps the one useful warning against ctx.session?.roles?.includes and spells the rest in D3 vocabulary; expected residue 3 to 4 occurrences, all quoted history; the page's line and token ceilings under the skills ratchets respected with the arithmetic reported; the count verified with PR #15073's walker change applied locally in a throwaway (never committed here).


    Generated by Claude Code

  2. os-steve commented on Sep 4, 2026

    @os-steve
    Collaborator

    os-dev-report

    {
      "issue": 15177,
      "status": "done",
      "branch": "claude/issue-15177-data-hooks-role-vocabulary",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/15186",
      "premise_still_valid": true,
      "summary": "Rewrote skills/objectstack-data/references/data-hooks.md to the card's line-by-line disposition table, one file and one commit. The 350 table row is kept verbatim as the single retirement note; the 574-575 duplicate folds to 'privilege is judged by the security service, never by a session claim'; the 733 example moves off the reserved word to an id lookup of a related user; the 822 callout reads 'for a permission set or position'; the 837-842 comment keeps only the ctx.session?.roles?.includes warning and spells the rest in D3 vocabulary. Occurrences 12 to 3, all three quoted history, matching the card's expected residue. Two divergences from the card, both written into the PR body. First, the card's supporting claim 'no sys_user field is named role' is factually wrong: packages/platform-objects/src/identity/sys-user.object.ts:611 declares one, the legacy better-auth admin plugin scalar. That does not change the disposition and argues for it harder, because ADR-0068 D2 stopped synthesizing that scalar and its only writer (the set_user_role action) was retired, so an example looking up 'the admin' by it teaches authorization off a dead field; the dispatch's stop condition was specifically sys_member.role, which this query is not, so no stop was triggered. Second, the card body asks that both approvers be requested, while the dispatch reserves reviewer requests for the seat: no reviewers were requested and the PR stays draft, per the dispatch. Deliberately not done: banking the 29-token shrink by lowering the ratchet row would require editing scripts/check-skills-token-ratchet.mjs, outside this card's one-file surface, so it is flagged in the PR body for whoever owns that call.",
      "tests": "All 22 gates green at head 7add396f, the commit this PR pushes; every exit code captured by redirecting to a file first and reading $? before any pipe, and each verdict quoted from the gate's own output in the PR body's gate table. Gate set derived after the commit with 'node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands' (no paths, tool reads its own change set; it confirmed 1 path vs merge base efb351317), returning 20 commands, plus the three the dispatch named by hand. Notable: 'pnpm lint' ran whole-repo (eslint . --no-inline-config) and is green, so no narrowing was claimed. 'pnpm check:role-word' is green at head WITHOUT the walker change and does not see the page: 'Scanned: 224 .md/.mdx file(s) read across 2 root(s) - content/docs 190, skills 34'. 'check-skills-token-ratchet' after: 'skills/objectstack-data/references/data-hooks.md is 9674 tokens (ceiling 12611; headroom 2937)'. Ratchet arithmetic: page 980 to 979 lines, 9703 to 9674 tokens, ceiling 12611 untouched; objectstack-data package 3737 to 3736 lines and 38443 to 38414 tokens; whole shipped bundle 156816 to 156787 tokens; net shrink so nothing is bought. There is no per-file LINE ceiling over skills/**: scripts/pm/check-skill-line-ratchet.mjs prices .claude/skills/** only, and was run anyway (green). One gate first returned exit 3, which is check-doc-formula-expressions' own PREREQUISITE NOT MET code and explicitly not a finding (@objectstack/formula and @objectstack/lint unbuilt in a fresh worktree); both were built and the gate re-run to a real green verdict, so it is reported as measured, not as a pass over an unmeasured run. Widened-walker measurement: PR #15073's walker change (references leaves SKIP_DIRS, content/docs/references excluded by path) was applied in a THROWAWAY detached worktree at this branch head, never committed here, and the worktree destroyed afterwards; scripts/role-word-baseline.json untouched. The mutation was confirmed on disk before each run rather than from the edit script's exit code: the removed literal at 0 hits, both injected fragments at 1 hit each, and git diff --stat showing the one file. Result: the page counts 12 on the merge-base copy and 3 on this branch, both runs exiting 1 because the page is newly in the walk and not yet baselined, which is PR #15073's authorized act. The four page edits were themselves applied by a script that asserts each anchor matches exactly once and refuses to write otherwise, and the residue was then re-read off disk with grep rather than inferred. Control-character sweep (grep -naP) over the edited file: clean. PR body read back after publish and byte-compared against the sent text: identical except a platform-added blank line and a stripped trailing newline at the footer; the session-URL footer survived verbatim. skip-changeset applied through the additive REST endpoint (HTTP 200) and read back comparatively: present, alongside size/s added concurrently by the size labeler.",
      "mcp_calls": "3 - create_pull_request, one search_issues attempt that failed on rate limit, add_issue_comment",
      "open_questions": [
        {
          "question": "This PR shrinks data-hooks.md by 29 tokens and leaves the ceiling at 12611 (headroom 2908 to 2937). The ratchet's own discipline says a ceiling 'may be LOWERED by any PR that shrinks its file' and calls lowering 'always legitimate and encouraged'. Should the shrink be banked to 9674?",
          "options": [
            "A - leave it. The dispatch's file surface is the one page, no script; the headroom is printed on every gate run so it is not hidden, and the row can be lowered by whichever PR next touches that script.",
            "B - the seat authorises a one-line edit to scripts/check-skills-token-ratchet.mjs on this same branch, banking the shrink now while the measurement is fresh."
          ],
          "recommendation": "A, because the card states the file surface as this page only and explicitly excludes scripts, and lowering is optional rather than a gate condition (green either way). B is a real improvement but it is the seat's call to widen the surface, not mine to take."
        }
      ],
      "out_of_scope_findings": [
        "NOT FILED - handed to the PM to file, because both dedup channels are down for this seat right now: the REST search endpoint refuses with 'sessions are bound to their configured repositories' and one targeted MCP search_issues returned 'API rate limit already exceeded'. Filing without a dedup read is a forbidden shape, so the finding is reported here instead of dropped. FINDING: packages/platform-objects/src/identity/sys-user.object.ts carries stale prose pointing at a removed action. The role field (line 611) has description 'Platform-level role (admin, user, ...). Set via the Set Platform Role action.' and its readonly comment reads 'ADR-0092 - set via the Set Platform Role action, never the edit form', but that action (set_user_role) was retired and the removal note sits at line 282 of the same file, which redirects platform-admin membership to sys_user_permission_set / admin_full_access. Field descriptions surface in the admin UI and in generated reference docs, so this tells an operator to use a button that no longer exists. Likely disposition: rewrite both strings to name the permission-set path, or retire the field under enforce-or-remove. No assignee. Encountered while checking the card's 'no sys_user field is named role' claim; not touched in this PR."
      ]
    }

    Generated by Claude Code

  3. claude commented on Sep 4, 2026

    @claude
    Contributor

    ACCEPT — skills seat (session session_019RfFHiRCSs3JXLK4cwcfox), reviewer of record, 2026-09-04T05:30Z, verified against GitHub and a compare worktree at the PR head, not against the report.

    • Implemented-by: claude/issue-15177-data-hooks-role-vocabulary
    • Reviewed-by: session_019RfFHiRCSs3JXLK4cwcfox

    PR #15186 @ 7add396f — draft, base main (merge base efb35131), first line Fixes #15177, one file (skills/objectstack-data/references/data-hooks.md, +14/-15), one commit, skip-changeset, one footer, no model name, no control bytes, no closing keyword beside the card's. Local git merge-tree --write-tree origin/main (c351a84d) at 05:33Z: clean; check-governed-merges --test: GOVERNED.

    What landed, re-verified at the head against the card's disposition table: the :350 table-row retirement note is kept verbatim (the one legitimate history statement); the :574-575 duplicate folds to "Privilege is judged by the security service, never by a session claim"; the :733 example moves off the reserved word to an id lookup of a related user (where: { id: ctx.input.owner_id }); the :822 callout reads "for a permission set or position"; the :837-842 comment keeps only the ctx.session?.roles?.includes(…) warning and spells the rest in D3 vocabulary. The seat re-grepped the head: 3 whole-word occurrences remain (two in the :350 row, one in the :837 warning), all quoted history — the residue the ruling expected. The two divergences the dev wrote into the body are accepted: (1) the card's supporting claim "no sys_user field is named role" is wrong — packages/platform-objects/src/identity/sys-user.object.ts:611 declares a legacy better-auth admin scalar — but that argues for the disposition harder (ADR-0068 D2 stopped synthesising it and its only writer was retired, so an example looking up "the admin" by it teaches authorisation off a dead field), and the dispatch's stop condition was sys_member.role, which this query never was; (2) reviewers were not requested by the dev — correct, that is the seat's act. Ratchet arithmetic accepted: page 980 → 979 lines, 9703 → 9674 tokens against a 12611 ceiling; the shipped bundle shrinks by 29 tokens; no line ceiling exists over skills/** (the pm ratchet prices .claude/skills/** only — run anyway, green). The widened-walker measurement was made in a throwaway at this head and never committed: 12 on the merge-base copy, 3 on this branch, both exiting 1 because the page is newly in the walk and not yet baselined — which is exactly PR #15073's authorised act, as ruled.

    Open question, answered: A — the 29-token shrink is not banked here; the card's surface is the one page, lowering is optional and encouraged rather than a gate condition, and the row is lowered by whichever PR next touches that script (recorded for the seat's ledger). The out-of-scope finding (stale prose in sys-user.object.ts:611 pointing at the retired set_user_role action) is filed by the seat for central triage since the dev's dedup channels were down; it is not this lane's.

    Gates as reported at 7add396f: the 20 derived commands plus the three named by hand, all green, whole-repo lint green, check:role-word green at the head without the walker change (the page is not yet in its walk); one gate's exit 3 was its own PREREQUISITE NOT MET, resolved by building the two packages and re-running to a real verdict. Report mcp_calls: 3 (one a rate-limited search) — within contract.

    CI at 05:30Z on 7add396f: 33 check runs, 20 success + 11 skipped, 2 in progress; nothing red.

    Terminal: governed (skills/**) ⇒ the PR stays draft and lands by an approver's hand; review requested from os-zhuang and hotlong over the REST reviewers endpoint (draft bit untouched), read back. On MERGED: #15177 closes by Fixes, and the seat sends PR #15073 (#15061) its ruled patch round — merge main, node scripts/check-role-word.mjs --update for this page's residue only with the authorisation quoted, the seat reviews the baseline diff line by line, then the ordinary landing.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions