Skip to content

spec: retire the two inert personal declarations — a view marked personal is visible to everyone, and the docs promise personal views that do not exist #29037

Closed

Description

@objectstack-fleet

epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff

Filed by the record-page epic seat as an execution step of the ruling on #28971. The maintainer answered 「同意」 (2026-11-02) to the seat's recommendation of option C. Readings are on main ab085a0482. No domain:* label is set: triage routes this card.

Business reason

An author, usually AI, writes scope: 'personal' or sharing: { type: 'personal' } on a view. It saves, everyone who can read the object still sees the view, and the toolbar badge says personal. That is a silent promise about who can see the data, and it is false.

Real personal views, ruled on #28971, will be a user-owned data object. They will not use these keys.

Reading

  • packages/spec/src/ui/view.zod.ts:5788: ViewScopeSchema lists personal, and its docblock says nothing restricts such an item to one user. It is read at lines 5870 and 6542.
  • view.zod.ts, ViewSharingSchema: type: z.enum(['personal', 'collaborative']).default('collaborative').
  • ui/packages/plugin-list/src/ListView.tsx:6364: the only reader of sharing.type, a read-only badge.
  • packages/spec/src/conversions/registry.ts:13135, 13164: scope: 'personal' appears in conversion material. Read whether these need updating.
  • content/docs/ui/audience-based-interfaces.mdx:51: "personal views".
  • In examples/ and skills/, a search for these two values finds nothing. HotCRM's view files have no sharing.type and no scope: 'personal'; its two sharing blocks are public form links, which this card doesn't touch.

Target

  1. personal leaves ViewScopeSchema. An authored value is refused with a prescription, not silently accepted, under ADR-0049 and ADR-0087. Use the spec-property-retirement skill.
  2. personal leaves sharing.type, the same way. That leaves one value, so read whether type and the badge still earn their place, and retire them with it if not. State the reading in the PR.
  3. Update the docblocks that call this a parked direction, pointing at [Decision] Named personal list views: can a reader save their own named list, and what is the end state? #28971.
  4. audience-based-interfaces.mdx:51 says per-user list preferences instead of personal views.

Not in this card

The personal views themselves and the ADR note. Both are tracked from #28971.

Acceptance

  • Saving a view with either value is refused, and the message says what to do.
  • A stored or older artifact follows whatever the retirement route requires, stated in the PR.
  • The badge no longer says personal.
  • The generated baselines, forms and pins the retirement touches are updated.
  • The docs line is corrected.

Activity

  1. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: domain:spec · p2 · pm:queue · 2026-11-02T07:19Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS

    Claim: PM loop round 1 · 2026-11-02T13:55Z
    Session: session_01ADHkmPi25XXKx92Xmg4viS (seat); dev = card thread session_01ADHkmTHDhPHuivpyvN8gfF
    Account: os-steve
    Branch: claude/project-thread-if04ti
    Worktree: the card thread's own checkout of objectstack-ai/objectstack
    Domain: domain:spec
    Seat: domain:spec#1
    File surface: packages/spec/src/ui/view.zod.ts (ViewScopeSchema, ViewSharingSchema, the "parked direction" docblocks) and its tests; packages/spec/src/conversions/registry.ts only if the retirement route needs a D2 conversion; the generated baselines, forms, pins and references the retirement touches; content/docs/ui/audience-based-interfaces.mdx; ui/packages/plugin-list/src/ListView.tsx badge only if sharing.type itself retires; root .changeset/ (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default — the tier is the PM's per-card call (floor sonnet · default opus · ceiling fable); one CONTRACT_REVIEW_TIER review (a narrowing on the published schema face) before landing, effort: high
    Clause-②: no (narrowing)
    Metadata-protocol: narrowed
    Responsibility: packages/spec view.zod.ts ViewScopeSchema / ViewSharingSchema | scope 'personal' and sharing.type 'personal' save but restrict nothing, while the badge says personal | authors and agents who believe a view is private to one user
    Thread-read: 6571434336
    Premise: holds (the card's readings are on main ab085a0482; the dev re-reads them on the base)
    Serial constraints cleared: PR #29287 merged (b5e7ba5552, same file); #28522 follows this card on view.zod.ts

    Ruling-ref: #28971 letter C (record 6570568131, 「同意」) — named personal views are a user-owned D3 object and do not reuse these keys, so both inert personal values retire with a prescription (ADR-0049, ADR-0087). If sharing.type is left with one value, it retires too unless a reader other than the badge needs it; the PR states the reading. The ADR notes ride PR #29321.

  3. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-steve · session_01ADHkmTHDhPHuivpyvN8gfF
    os-dev-report

    {
      "issue": 29037,
      "status": "done",
      "branch": "claude/project-thread-if04ti",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29447",
      "session": "session_01ADHkmTHDhPHuivpyvN8gfF",
      "premise_still_valid": true,
      "summary": "Both inert personal declarations retire per #28971 letter C. view.scope 'personal' is refused with a prescription by enumWithRetiredValues at every door that reads a view item's scope. view.list.sharing.type retires whole (retiredKey tombstone): once 'personal' goes it has one value left, and its only reader is the console badge (git grep over packages/ and ui/ finds no server or runtime reader). The emitted default 'collaborative' is accepted and stripped through acceptRetiredDefaultResidue (#12840 house rule). Route: D2 conversion view-personal-declarations-removed deletes both declarations on stored rows, assembled artifacts and os migrate meta. D3 entry ui-view-personal-declarations-retired, RETIRED_KEYS_BY_MAJOR ui/ViewSharing:type, one ADR-0087 marker. Console (in surface): the ListView badge becomes a lock-only 'Locked by' indicator, normalize-list-view drops the legacy visibility pair instead of writing type, and list.sharingPersonal/list.sharingCollaborative leave all 10 locales. The docs line changes to per-user list preferences.",
      "tests": "head 92ac574d (main merged at 96fb7b5d via os-regen-merge.sh, authorable-surface regenerated): spec test 791 files passed, 23468 passed | 1 todo (lock VERDICT exit 0); spec typecheck exit 0 (pre-merge 933882e0); spec check:generated exit 0 (14 artifacts current, dist built this round); metadata-protocol protocol.graft-folded-form-sections 47/47; ui consumers (plugin-list ListView + inertKeys-13074 + specInputs-13047, core normalize-list-view, plugin-view 8980, types p1-spec-alignment, console registry-inputs-spec-parity) 7 files, 642/642 (a796462b, ui sources unchanged since); new file view-personal-declarations-retirement.test.ts in the repo project.",
      "gates": "dispatch-gates --commands: 110 derived; each run with < /dev/null and its exit captured; --ran reconciliation: 110 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. Two first-pass reds were environment: check-plugin-teardown-shape --self-test exit 3 (shallow clone, cleared after fetching its fixture commit) and pnpm --dir ui exec vitest run scripts/ exit 1 (4 tests timed out at 15s while dogfood shared the box); both exit 0 on re-run (ui scripts 169 files, 4921 passed). check:skill-examples and check:dual-build-cjs-loads ran after building their 7 extra packages. PR check-runs: not yet read (CI just started).",
      "dogfood_battery": "92ac574d · os-verify-lock: VERDICT command-exit 1 · held the lock 2866s · Test Files 4 failed | 389 passed | 3 skipped (396) · Tests 6 failed | 3237 passed | 28 skipped. Failures: external-import-code-datasource-namespace (control) and wall-package-refusal (single CONTROL) timed out at 5000ms and pass when re-run alone; flow-runas x3 (404 RECORD_NOT_FOUND) and declarative-endpoint-anonymous-guest x1 come from main: #29365 (dfbe34d8, in my merge) added the flow-start record-read refusal, and #29451 (6af3d295, landed on main after my merge) re-points those pins. With #29451's three test files checked out on my tree: 2 files, 17/17 passed (VERDICT exit 0). None touches a view.",
      "files_changed": 39,
      "line_budget": "n/a: no skills/** lines; diff vs main +975 / -292 across 39 files (486 of the added lines are the new retirement test)",
      "deviations": [
        "The first dogfood run was killed before merging main so the battery would run on the final head; the battery reading above is the post-merge head.",
        "Before the fix, the pre-merge closure build passed --concurrency=2 into the cli build script (sh syntax error); the post-merge build used --workspace-concurrency=2 and is green.",
        "main moved 27 commits after my merge, so the PR does not re-merge; #29451 is among them."
      ],
      "mcp_calls": "7 — reads only: mcp__github__issue_read x4, mcp__github__pull_request_read x3",
      "api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches, run 41468778199, created #29447) + this comment via post-stamped.mjs",
      "open_questions": [
        { "question": "sharing.type residue: should the old emitted default 'collaborative' still parse?", "options": ["A accept and strip it (shipped): views built by a released toolchain parse with no edit; 'personal' is refused", "B refuse 'collaborative' too: every stored list view with the default needs the D2 conversion or an edit"], "recommendation": "A, because #12840 rules a retired defaulted key's emitted default as residue, not an authoring choice, and the ruling's 'both values' names the two personal declarations" },
        { "question": "Conversion identifier: personalViewDeclarationsRemoved (id view-personal-declarations-removed) instead of viewPersonal..., because an identifier that sorts last trips scripts/conversions-major18-merge.test.ts", "options": ["A keep the identifier (shipped)", "B rename to viewPersonal... and fix the merge test's last-sort assumption in a separate card"], "recommendation": "A; the id string is unchanged, so nothing outside the registry reads the identifier" }
      ],
      "security_findings": "withheld",
      "out_of_scope_findings": [
        "class: c · reach: scripts/conversions-major18-merge.test.ts synthetic definitions-end scenario assumes no rule-placed conversion sorts last; a new last-sorting identifier yields a 3rd finding · evidence: round-1 red on 5d20b513 · dedupe words: conversions-major18-merge last sort · noted, not filed",
        "class: b · reach: view.manageShared gate has no enforcing reader (git grep) · dedupe words: manageShared inert · noted, not filed",
        "class: b · reach: view.form.ts section describe still says 'who can see it' for a non-visibility key · dedupe words: form section who can see · noted, not filed",
        "carrier: PR #29447"
      ]
    }

    Stamped 2026-11-02T16:16Z.


    Generated by Claude Code

  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS

    Claim amendment on #29037 (amends claim 6578962562) · 2026-11-02T16:18Z

    PR #29447 retires sharing.type whole, which the claim allowed. Its only reader was the ListView badge. That retirement forces edits outside the named surface, and the seat accepts all of them:

    • The ui emitter and its strings. ui/packages/core/src/utils/normalize-list-view.ts no longer writes type, and its test follows. The two badge i18n keys leave the 10 locale files under ui/packages/i18n/src/locales/.
    • ui pins of the spec surface. These follow the key's removal:
      • registry-inputs-spec-parity
      • ListView / ListViewBlock tests
      • ObjectView protocol-keys test
      • p1-spec-alignment
    • The retirement mechanics ADR-0087 requires:
      • one retired-keys entry
      • one semantic D3 entry
      • migrations/registry.ts.template
      • vitest.repo-tests.json
      • the retired-key removal-version pin
    • One metadata-protocol test fixture: protocol.graft-folded-form-sections.test.ts.

    The open questions are answered A and A:

    The lines stay Clause-②: no (narrowing) / Metadata-protocol: narrowed. The narrowing is ruled: #28971 letter C, record 6570568131, 「同意」.

  5. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS

    Landed · 2026-11-02T17:15Z

    Probe Reading
    PR #29447 MERGED 2026-11-02T16:55Z, merge commit 85ea12f475, through the merge queue
    #29037 closed completed by Fixes #29037

    pm:dispatched is removed. The seat ACCEPTed, with a claim amendment for the ui/i18n/migration edits the retirement forces, and the CONTRACT_REVIEW_TIER review passed on 92ac574d.

    What changed:

    Non-blocking residue, not filed: the useViewSharing.ts docblock in ui/packages/react still claims alignment with ViewSharingSchema. Next on view.zod.ts is #28522, which is free for another spec seat.

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

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:specenhancementNew feature or requestpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions