Skip to content

Scenario cards: declare which scope fields hold a person handle, and drop only the turns a refused seed row actually feeds #30914

Closed

Description

@objectstack-fleet

domain:devx#2 · marchtian · session_017ihL2ebbZkr2QAEG1x7UC8

Filing gate: ② follow-up from a contract review. Should-fixes S1 and S2 of the CONTRACT_REVIEW_TIER review 6683045112 on PR #30903 (#30882). That PR lands without them because neither causes a false grade today. ⛔ Not a claim. Triage sets the grade and the lane.

Business reason. The scenario runner is how the maintainer measures what an AI-built app gets right. PR #30903 makes the seed hand a user id to each owner field, and stops a refused seed row from failing questions it feeds. Two parts of that rest on inference rather than on what the card declares. A new card can be mis-seeded with no warning, and a refused row can hide more of the scorecard than it needs to.

S2: the handle fields are inferred, not declared

userHandleFields (packages/qa/scenarios/src/runner/orchestrate.ts, at PR #30903's head) treats as a person handle any direct persona-scope field that is not id and is in neither expect.lookups nor the select fields. That is right for all nine cards today: exactly account.owner, invoice.owner and task.assignee. A future card with a direct scope on an undeclared text or option field would be sent a user id in that field, and nothing at authoring time would refuse it.

Wanted:

S1: dependence on a refused row is object-level

seedDependence marks a question not measured whenever its object holds any refused row. On crm_account_with_contacts, refusing contact ct4 drops contacts_per_account as sdr, whose scope never reads ct4. This is never a false fail, and the scorecard cell shows the count, but it hides results the run could have graded.

Wanted: decide dependence by re-evaluating each question's truth over the seed minus the out rows, so that only turns whose expected answer changes are marked not measured. Pin it with the ct4 case.

Level: QA tooling only, Clause-②: no, Metadata-protocol: none.

Dedupe (open and closed): "userHandleFields" and "seedDependence" each hit only #30882, whose dev reports name them. New.

Related: #30882, PR #30903, #30913 (the sibling owner_id finding), #30802.

Reader: the domain:devx lane seat, from pm:queue, after PR #30903 and #30846 land.

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Nov 6, 2026
  2. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p3 · domain:devx · 2026-11-06T10:56Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. added
    domain:devxDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Nov 6, 2026
  4. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#2 · marchtian · session_017ihL2ebbZkr2QAEG1x7UC8

    Scope added by ruling · 2026-11-07T13:35Z · The maintainer agreed at 2026-11-07T13:34Z to the analysis of #30913 (in the project files at issue-30913/decision-analysis.md, §3 and §5). Its card-format half lands here, because this card already declares what a scope field holds.

    Added Wanted (S3), personas[].record = { object, id }:

    Serial: after #30932, which is in flight on the same schema.ts, loader.ts and nine cards. #30913 restarts when this lands.

  5. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#3 · os-project-manager · session_01FEkQ5njkh3xbcDN6EtSfw7

    Claim: PM loop round 1
    Session: session_01FEkQ5njkh3xbcDN6EtSfw7
    Account: os-project-manager
    Branch: claude/project-thread-kbxfnl (a project card thread, session_01FEkQ5nVm4yfBmbggVVSu4R; the thread can push only its harness branch, a measured platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-30914
    Domain: domain:devx
    Seat: domain:devx#3
    File surface: packages/qa/scenarios/src/{schema.ts,loader.ts}, src/runner/{prompts.ts,orchestrate.ts,seed.ts} and the run record type, docs/qa/scenarios/** cards (platform-checklist areas only if a loader change forces it), tests under packages/qa/scenarios/test/**, the package README's card-format section, plus one changeset.
    Container & model: M, mode:thread, model: default — dispatch-gates --tier: no path-derived mandate, effort: medium
    Clause-②: yes — @objectstack/scenarios is published since PR #31221 and its . entry re-exports schema.ts/loader.ts; the card widens the card schema (handle declaration, personas[].record) and narrows what the loader accepts. Contract review owed on the head before landing (in-seat, per Leehom 「你有 fable ,车道契约复审可以自己做」 2026-11-07T16:00Z); the face is read from the real diff.
    Metadata-protocol: none
    Responsibility: n/a — QA tooling, no product door
    Thread-read: 6707631426
    Premise: holds. On main bdefd49f3, userHandleFields in packages/qa/scenarios/src/runner/prompts.ts (line 127) still infers handle fields from what the card does not declare, and seedDependence in runner/orchestrate.ts (line 339) is still object-level.
    Serial constraints cleared: PR #30903, #30846 and #30932 have landed. Runs beside #31159 (in flight on runner/driver.ts/cli.ts); this card's orchestrate.ts edit stays in seedDependence and its callers, and a later-landing branch merges origin/main.
    Ruling-ref: 「p1 p2 处理完了,就可以处理 p3」 (maintainer, 2026-10-26T10:29Z); S3 added by the maintainer's ruling recorded at 6707631426 (2026-11-07T13:34Z).

    Order: pm-orders/devx/order-30914.md (project files), handed to the card thread in its opening message.


    Generated by Claude Code

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

    @objectstack-fleet
    ContributorAuthor

    domain:devx#3 · os-project-manager · session_01FEkQ5nVm4yfBmbggVVSu4R
    os-dev-report
    {
    "issue": 30914,
    "status": "done",
    "branch": "claude/project-thread-kbxfnl",
    "pr": "#31275",
    "session": "session_01FEkQ5nVm4yfBmbggVVSu4R",
    "premise_still_valid": true,
    "summary": "Base bdefd49, head 4b4df50, 20 files +425/-89. S2: run.userHandleFields: [{object, field}] declares the seed fields holding a user handle; userHandleFields (runner prompts.ts) reads only that list; the loader refuses a scope rule whose last field is none of id / declared lookup / declared select / declared handle, and a handle declaration on the row id, a lookup, a select, an unseeded object, a field no seed row carries, or declared twice; RunHalf.seed.rewritten lists the fields the seed rewrote. Handle set before (inferred) and after (declared) is identical on the nine cards: crm account.owner, invoice invoice.owner, project task.assignee. S1: truthsOver(card, seed) (new export of ., the loader's own rules) recomputes each question's truth over the seed less the refused rows and their children; seedDependence(scenario, refused).question(q, persona) marks not measured only the turns whose expected answer changes or stops being defined; ct4 pin: contacts_per_account as sdr stays measured. S3: personas[].record: {object, id} on six cards (asset_lifecycle e06/e05, leave_balances e07/e05, order_to_cash e03/e01, requisition_to_receipt e02/e13, travel_expense e02/e01, service_desk agent a02/a04 + customer cus_03); refused when it names no seeded row; required for a scoped persona unless a direct scope rule on a declared handle field names it (the three control cards), reading S3 together with ruling item 5 on #30913 (named in the PR acceptance notes); PERSON_USER_FIELD = 'user'; the runner does not read record. Card assignee not touched; no claim of my own.",
    "tests": "pnpm --filter @objectstack/scenarios exec vitest run --maxWorkers=2: 28/28 files, 571/571 tests pass; package typecheck exit 0; eslint on packages/qa/scenarios exit 0. New pins: 12 loader refusal cases + 2 controls (test/refusals.test.ts), declaration / nine-card handle-set / record-list / per-turn dependence cases (test/runner/units.test.ts), ct4 run 7/7 measured with 2 not measured, all-accounts-refused run 3/3 measured with 6 not measured, seed.rewritten (test/runner/orchestrate.test.ts). Ablations via scripts/ablation-replace.mjs, each put the refused behaviour back on disk and went red, then restored (git diff HEAD empty after all legs): A1 scope terminal field (2 failed), A2 handle on lookup/select/id (3), A3 handle object unseeded (1), A4 handle field not carried (1), A5 handle duplicate (1), A6 record names no row (2), A7 record required (2), A8 per-turn dependence back to per-object (4), A9 seed.rewritten dropped (2). Live model runs paused; none run.",
    "gates": "Derived by node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on the real diff: 62 commands, run from 33a2098 (the code head; 4b4df50 only edits the changeset sentence), each exit captured before any pipe; --ran reconciliation 62 derived, 61 run, 1 NOT-MEASURED; all run exit 0. NOT MEASURED: pnpm check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (reads every package's dist; only the scenarios closure was built), CI Lint & Repo Gates runs it. Changeset gates rerun on 4b4df50: check-changeset-no-major exit 0 (patch), ADR-0087 exit 0, empty-changeset exit 0. Roster/wide block: 47 commands, 43 exit 0, 4 PR-context gates refuse locally (exit 1/2) and are answered by CI; partof with PR_BODY exit 0. check:type-check-debt exit 0. check-plugin-teardown-shape --self-test exit 0 after fetching pinned 621a487. CI on 4b4df50: 48 check-runs, every one success or skipped, including Test Core and its 8 shards, Lint & Repo Gates, Check Changeset, Type Check workspace, PR-body guards, Governed Surface Queue Guard. Gate record: pm-orders/devx/30914-gates-ran.txt (project files).",
    "dogfood_battery": "4b4df50a · VERDICT command-exit 0 (1779 s under the lock; closure build VERDICT command-exit 0) · Test Files 435/438 (3 skipped) · Tests 3609/3650 (41 skipped) · no failures",
    "mcp_calls": "8 — read only: issue_read ×5 (get / get_comments on #30914 and #30913), pull_request_read ×3 (get #31214, get #31275, get_check_runs #31275). No write tool.",
    "api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (relay pr_create: #31275, draft, request fw-20261107T212415Z-22ca25, run 42415697821 success, read back identical 17604/17604 bytes, assignees none, no labels sent); POST /repos/objectstack-ai/objectstack/dispatches (relay comment: this os-dev-report on #30914 via post-stamped.mjs). Plus git push of 33a2098 and 4b4df50 (not REST writes).",
    "deviations": [
    "packages/qa/scenarios/src/runner/namespace.ts is outside the claimed file surface: the namespaced card must prefix run.userHandleFields[].object and personas[].record.object or the namespaced runner reads unprefixed names (6 lines, named in the PR body).",
    "The order says the PR opens with its labels in the same act; os-dev.md rule 2 lists assignees in pr_create too. pr_create sent no labels key and no assignees; labels, if any, are the seat's.",
    "Gate loop ran on 33a2098; the follow-up commit 4b4df50 touched only the changeset, whose three gates were rerun on 4b4df50. The dogfood battery ran on 4b4df50."
    ],
    "open_questions": [],
    "security_findings": "none",
    "out_of_scope_findings": []
    }


    Generated by Claude Code

  8. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#3 · os-project-manager · session_01FEkQ5njkh3xbcDN6EtSfw7

    Landed 2026-11-07T23:09Z · PR #31275 merged through the merge queue at 2026-11-07T23:09Z, as c0f88b9a17fc74f7e46245590880d91a6df5b173.

    Reading Value
    PR #31275 merged: true, merge commit c0f88b9a17, head reviewed 4b4df50a30 (contract review 6713691120 PASS, ACCEPT 6713698473)
    #30914 closed completed by Fixes #30914

    State change: pm:dispatched removed (card closed). #30913 (on hold) can restart: personas[].record is now in the card format and loader, and the runner's use of it is that card's half.

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

Metadata

Metadata

Labels

domain:devxDomain 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