Skip to content

service-settings: a map specifier admits any key when its option source answers an empty list; two missing shape pins; a stale json comment (#31386 review nits) #31422

Closed

Description

@objectstack-fleet

domain:server#5 · os-project-manager · session_01FEkQ5o8PE7ZtVjcor51wBZ

Filing gate: ② the three non-blocking nits from the Fable contract review of PR #31386 (#30208 row 5 phase 1, review 6717755004), recorded on #30208 (6717781477) as "owed as a small follow-up after #31386 merges". #31386 merged at 2026-11-08T06:50Z. ⛔ Not a claim.
Reader: the seat that owns packages/services/service-settings (SettingsService.mapShapeVerdict, map-shape.ts) and packages/objectql/src/lifecycle/.
Dedupe (open AND closed): mapShapeVerdict empty, map specifier fail closed → only #30208 / PR #31386.
reach: (1) every host whose lifecycle-class option source answers an empty list, where a map key admits any entry key instead of refusing it.
class: a · type: Bug
Grade: p3. The server seat graded it itself (Ruling-ref: the maintainer, 「定级你可以自己决定」). Nothing ships wrong today: with no lifecycle objects, no map row has a key that could be right.

维护者速读

#31386 新加的 map 设置类型有三处小尾巴,都是复审时记下的非阻塞项:选项来源答了空列表时,键的校验会放行任何键(应当拒绝);少两个测试;一处注释还把这些键叫 json。

Items

  1. mapShapeVerdict admits every key when a reachable option source answers an empty list. It should fail closed whenever the source answered at all, and keep the allowed.length > 0 gate only for the declared-table fallback. Pin: a registry with no lifecycle objects gives invalid_option.
  2. Missing pins: an array value (not_a_map gives invalid_type), and a scalar entry in a several-column map (not_an_entry gives invalid_shape).
  3. The comment at packages/objectql/src/lifecycle/lifecycle-service.ts (~l.1161) still calls these keys json specifiers.

Item 1 refuses a save that was accepted before, so it is Clause-②: no (narrowing), and the changeset says so.

Refs #30208, PR #31386

Activity

  1. added
    bugSomething isn't working
    pm:queueReady for the PM dispatch loop
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 8, 2026
  2. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p3 · domain:server · confirmed · 2026-11-08T07:53Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-project-manager · session_01FEkQ5pXkgNXVpCn2GZDjyy

    Claim: PM loop round 1 · #31422 · 2026-11-08T12:51Z · fix (p3; the thread freed by #31399's merge)
    Session: session_01FEkQ5pXkgNXVpCn2GZDjyy
    Account: os-project-manager
    Branch: claude/project-thread-2n2fmy (the card thread's harness-pinned branch, freed by PR #31480's merge; platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-31422
    Domain: domain:server
    Seat: domain:server#3
    File surface: packages/services/service-settings/src/settings-service.ts (mapShapeVerdict) and its map-shape tests, packages/objectql/src/lifecycle/lifecycle-service.ts (the comment at ~l.1162 only), a changeset (stop on breach; explain in the report)
    Container & model: S, mode:thread, model: default tier, effort: default (coordinator default)
    Clause-②: no (narrowing) — item 1 refuses a save that was accepted before, so the seat runs a TIER_DEFAULT contract review before landing
    Metadata-protocol: none
    Responsibility: non-blocking nits of PR #31386's Fable review 6717755004, recorded on #30208 (6717781477) → this card.
    Thread-read: 6718962862
    Premise: holds (read at main: mapShapeVerdict lives in settings-service.ts; the comment at lifecycle-service.ts l.1162 still says "json specifiers")
    Serial constraints cleared: the seat's de PR #31511 touches only service-settings/src/setup-nav-hub-parity.test.ts there, a different file; no other open PR touches service-settings or lifecycle-service.ts.
    Order: pm-orders/server/order-31422.md (project files).

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 8, 2026
  5. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-project-manager · session_01FEkQ5nBSzc6ktJyjdFcDb3
    os-dev-report
    {
    "issue": 31422,
    "status": "done",
    "branch": "claude/project-thread-2n2fmy",
    "pr": "#31539",
    "session": "session_01FEkQ5nBSzc6ktJyjdFcDb3",
    "premise_still_valid": true,
    "summary": "Item 1: SettingsService.optionTableOf now reports sourced (a registered source answered, even with no options); mapShapeVerdict judges every key whenever sourced, keeping the allowed.length > 0 gate only for the declared-table fallback. Both doors call this one verdict, so a source answering an empty list refuses each key at the save door (invalid_option) and keeps an OS_* override out of force (one error line). Item 2: pins for an array value (not_a_map / invalid_type) and a scalar entry in the several-column retention_overrides (not_an_entry / invalid_shape). Item 3: the lifecycle-service.ts docblock (~l.1162) now says map specifiers (json before #30208); comment only.",
    "tests": "Head 66137fb. settings-map-shape-30208.test.ts +6 rows (16 -> 22): on main's source 2 failed | 20 passed (the save row 'expected 200 to be 400', the env row 'expected env not to be env'); controls (empty map saves; an unanswered source with no declared keys admits any key) and the two coverage pins green; with the fix 22 passed. Ablation (ablation-replace, settings-service.ts, admitsKey ignores sourced via String('ABL31422').length < 0): anchor 1 -> 0, blob 07699b278e33 -> e0d112878a7f, the same 2 rows red; restored blob == HEAD, git diff HEAD empty; the suite reads its own src. @objectstack/service-settings vitest: Test Files 73 passed (73), Tests 1407 passed.",
    "gates": "dispatch-gates --commands at 7d88feb: 70 commands, timeout 1500 each, stdin /dev/null, exit codes captured before any pipe, after a runtime closure + service-settings build. 69 exit 0; check-adr-0087-registration --base origin/main exit 1 (the narrowing changeset had no ADR-0087 disposition) -> fixed in 66137fb with adr-0087: not-required (no-migration-prescription); the ten changeset/citation/closing-keyword gates re-run at 66137fb, all exit 0. dispatch-gates --ran: 70 derived, 70 run, 0 NOT-MEASURED, 0 UNRUN. Outside: service-settings and objectql typecheck exit 0; eslint --no-inline-config packages/services/service-settings packages/objectql/src/lifecycle exit 0. Record: pm-orders/server/31422-gates-ran.txt. CI: just opened at hand-back.",
    "dogfood_battery": "n/a — no ! in the PR title; the narrowing is one runtime refusal pinned over the real settings door testkit",
    "mcp_calls": "3 this card — issue_read x2 (#31422 get, get_comments); no write tool",
    "api_writes": "2 — relay pr_create (run 42486241078, PR #31539, draft, read-back identical 7082 bytes, assignees none, labels none); this os-dev-report comment on #31422 via post-stamped",
    "files_changed": [
    ".changeset/31422-map-key-source-empty-fails-closed.md (+12)",
    "packages/services/service-settings/src/settings-service.ts (+15/-6)",
    "packages/services/service-settings/src/settings-map-shape-30208.test.ts (+91)",
    "packages/objectql/src/lifecycle/lifecycle-service.ts (+3/-2, comment only)"
    ],
    "line_budget": "4 files, about +121/-8 vs origin/main (under the 3000-line human-merge threshold)",
    "deviations": [
    "optionTableOf (the helper mapShapeVerdict reads) gains a sourced member: same file, the other two callers ignore it and are unchanged.",
    "The changeset carries an adr-0087 not-required (no-migration-prescription) marker: the gate reads Clause-② narrowing as a declared breaking change.",
    "Worktree: the claim names objectstack-issue-31422; the chain kept its worktree objectstack-issue-31317 on the same claimed branch."
    ],
    "open_questions": [],
    "security_findings": "none",
    "out_of_scope_findings": [
    "carrier: PR #31539 Acceptance notes · noted, not filed — the option-bearing save branch (validatePatch) and the env override for non-map keys keep their own allowed.length > 0 gate, so a select key whose registered source answers an empty list still admits any value: item 1's shape on another family."
    ]
    }


    Generated by Claude Code

  6. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-project-manager · session_01FEkQ5pXkgNXVpCn2GZDjyy

    ACCEPT → PR #31539 comment 6722625300 · 2026-11-08T14:21Z · head 66137fbc · at-tier narrowing review 6722607950 PASS (one nit → follow-up #31550) · the seat marked it ready and turned on auto-merge.

  7. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-project-manager · session_01FEkQ5pXkgNXVpCn2GZDjyy

    Landed · 2026-11-08T14:44Z

    Probe Reading
    PR #31539 MERGED 2026-11-08T14:44Z through the merge queue as dc8479e0 (head 66137fbc, seat ACCEPT 6722625300, at-tier narrowing review 6722607950)
    This card closed by Fixes #31422

    Status change: pm:dispatched removed. The review's one nit (the select family's empty-source gap) and its observation (map dedupe in reportRejectedEnvOverride) are follow-up #31550.

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

Metadata

Metadata

Labels

bugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions