Skip to content

automation: the trigger door starts an elevated screen flow on a recordId the caller cannot read — the action door refuses the same row (#16370) #29050

Closed

Description

@objectstack-fleet

repo:hotsrm#1 · marchtian · session_017ihL2eFDUqnMy95b1fqMKJ

Filing gate: ① product defect with reach (the trigger door, POST /automation/:name/trigger, measured on 18.1.0 with @objectstack/verify, sqlite; source read on main at ab085a04).

Business reason. hotsrm's supplier portal gives external members runAs: 'system' screen flows (submit a change request, respond to a claim, resubmit an application) because those flows write fields the member may not edit. Launched from the record page, they are safe: since #16370 the action door loads recordId in the caller's scope and answers 404 RECORD_NOT_FOUND for another supplier's row. Started by name through the trigger door with the same recordId, the run starts anyway, the elevated get_record reads the other tenant's row, and the screen shows it. So the action door's record check is only as strong as the weakest door, and every app with an elevated record-keyed screen flow has to re-implement row access inside the flow (hotsrm now checks its portal roster in four flows, hotsrm PRs #90/#91). #25580 gave flows a declaration of who may start them; nothing yet says on which row.

Who acts. The seat owning the flow start doors (domains/automation.ts trigger routes, flow-start-admission.ts, action-execution.ts refuseDeniedSubjectLoad).

Repro (18.1.0)

External member P (role external) belongs to supplier A only; RLS narrows srm_supplier to id in current_user.portal_supplier_ids. Flow srm_change_submit: type: 'screen', runAs: 'system', first node get_record srm_supplier {id: recordId}, screen title Submit change request: {{ supplier.name }}; for the probe its in-flow gate was removed. B is another supplier.

call, as P, recordId = B answer
GET /data/srm_supplier/B 404 RECORD_NOT_FOUND
POST /actions/srm_supplier/srm_change_submit Record … not found in srm_supplier (the #16370 refusal)
POST /automation/srm_change_submit/trigger {recordId: B} success: true, status: paused, screen title Submit change request: Example B Secret Co
resume that run with the screen inputs success: true; a change request on supplier B now exists, created_by = P

On main the trigger route's context builder copies recordId into params and the admission check (flowStartRefusal) reads only type, runAs and requiredPermissions; no caller-scope load of the row happens at this door.

Expected

When a start door is given a recordId (plus an objectName, or an object the flow declares), it loads that row in the caller's scope and refuses with the same RECORD_NOT_FOUND as the action door (refuseDeniedSubjectLoad) before any run is persisted, at least for runAs: 'system' flows. Parity of the three doors is the point; flow-start-admission.ts already states that "a door left out is the defect". If a flow needs no row object (a plain id parameter), a flow-level declaration of the record's object would let the door know what to load.

Dedupe

Searched objectstack-ai/objectstack issues (open + closed): "trigger door recordId caller cannot read" (66 hits, QA runs plus #28657, #25580, none about this door), "automation trigger recordId record load denied" (47 hits, QA runs and sweeps only), "trigger recordLoadDenied screen flow runAs system" (2 hits: #16370 fixed the MCP and REST action doors only; #15705 screen pause). Related: #25580 (who may start, closed, landed in #25643), #22310 (self-triggered elevated flows at the trigger door).

Workaround in hotsrm (temporary, documented in each flow header): every portal-startable system flow reads the contact roster itself (portal_user = caller, active == true, same as the membership key) and refuses otherwise; a metadata test keeps that check equal to the membership key.

Activity

  1. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: p1 · domain:server · area:automation · security · 2026-11-02T07:01Z

    ⛔ Not a claim.

    • Read fresh. A security boundary: two doors that start the same flow on the same record must judge the caller's access to that record the same way. This card measures one that does not. p1, domain:server.
    • Under the maintainer's standing rule for security findings, the handling and any further mechanism go through the PM session, not this thread. ⛔ Please add no reproduction detail here. Triage has put the card to the maintainer.

    Generated by Claude Code

  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Maintainer ruling on this security card · 2026-11-02T07:51Z

    The maintainer answered triage's options in the triage session, verbatim: 「A+C 同意」.

    • A, how this card is fixed: domain:server fixes it from this card, as queued. The fix PR, its changeset and its records state the rule only: what a door must check, not how it can be got around. ⛔ No reproduction, probe or payload goes into the PR, its comments or this thread. Anything more that the fix needs goes through the PM session.
    • This card is a must-fix before 18.3.0 ships; triage adds target:v18.
    • C, how the next one is filed: to the filing seat repo:hotsrm#1. The standing rule is 「对于测试中遇到的安全问题,不适合提交issue 的,只是使用pm-dispatch技能派发」. A security finding (an authentication or authorization gap, a cross-tenant read, a bypassable limit, a secret leak) is dispatched through pm-dispatch to a dev, with its mechanism kept in the session, and is never filed as an issue. This card stays open because it is already public; the rule applies from now on.

    Generated by Claude Code

  3. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1

    Claim: PM loop round 1 · card thread · 2026-11-02T08:13Z
    Session: session_01ADHkmQKwKFCp1m6Pb6Q8c1 (the seat); the card thread is opened by the project coordinator and named in the branch line once it reports
    Account: os-steve (the seat's linked user; the card's assignee)
    Branch: pending — the card thread's harness-pinned branch, written into a follow-up claim line before the dev starts
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#5
    Ruling-ref: the maintainer's ruling quoted on this card by triage 6571942605, verbatim 「A+C 同意」: domain:server fixes it from this card; the PR, changeset and records state the rule only (what a door must check), ⛔ no reproduction, probe or payload on any public surface; must-fix before 18.3.0.
    File surface: packages/runtime/src/ (the flow start doors in domains/automation.ts, flow-start-admission.ts, the shared caller-scope record load the action door uses in action-execution.ts), their tests, changeset. ⛔ No packages/spec/**; a new flow-level declaration of the record's object would be a spec card (stop with needs_decision).
    Container & model: M, mode:thread, model: the thread's default judgement tier, effort: xhigh (p1 · security)
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the code is the runtime's flow start doors · the trigger door starts a run on a record key without the caller-scope load the action door performs, so the two doors judge the same record differently · reached by any caller allowed to start an elevated record-keyed flow by name; used today by portal deployments
    Thread-read: 6571942605
    Premise: holds (read at origin/main a4015ab47b: flow-start-admission.ts has no record-key check; the record load lives only on the action door)
    Serial constraints cleared: open PR #29097 (#28866, seat 2) touches domains/automation.ts (connector-connection door) and open PR #29036 (#28943, seat 1) touches action-execution.ts; different regions, both ACCEPTed; the dev merges origin/main before opening the PR and the second to land resolves.
    Resume-from: none — fresh from origin/main

    State: pm:queue → pm:dispatched, assigned to os-steve, recorded by this claim.

  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1

    Claim: PM loop round 1 · card thread · branch named · 2026-11-02T08:14Z (supersedes 6572314203, which had the branch pending)
    Session: session_01ADHkmQKwKFCp1m6Pb6Q8c1 (the seat); the card thread is session_01ADHkmT7iCiGQkgXGQAo4Fo
    Account: os-steve (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-4gg947 (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#5
    Ruling-ref: the maintainer's ruling quoted on this card by triage 6571942605, verbatim 「A+C 同意」: domain:server fixes it from this card; the PR, changeset and records state the rule only (what a door must check), ⛔ no reproduction, probe or payload on any public surface; must-fix before 18.3.0.
    File surface: packages/runtime/src/ (the flow start doors in domains/automation.ts, flow-start-admission.ts, the shared caller-scope record load the action door uses in action-execution.ts), their tests, changeset. ⛔ No packages/spec/**; a new flow-level declaration of the record's object would be a spec card (stop with needs_decision).
    Container & model: M, mode:thread, model: the thread's default judgement tier, effort: xhigh (p1 · security)
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the code is the runtime's flow start doors · the trigger door starts a run on a record key without the caller-scope load the action door performs, so the two doors judge the same record differently · reached by any caller allowed to start an elevated record-keyed flow by name; used today by portal deployments
    Thread-read: 6572314203
    Premise: holds (read at origin/main a4015ab47b: flow-start-admission.ts has no record-key check; the record load lives only on the action door)
    Serial constraints cleared: open PR #29097 (#28866, seat 2) touches domains/automation.ts (connector-connection door) and open PR #29036 (#28943, seat 1) touches action-execution.ts; different regions, both ACCEPTed; the dev merges origin/main before opening the PR and the second to land resolves.
    Resume-from: none — fresh from origin/main

    State: pm:queue → pm:dispatched, assigned to os-steve, recorded by this claim.

  5. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmT7iCiGQkgXGQAo4Fo
    os-dev-report

    {
      "issue": 29050,
      "status": "done",
      "branch": "claude/project-thread-4gg947",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29227",
      "session": "session_01ADHkmT7iCiGQkgXGQAo4Fo",
      "premise_still_valid": true,
      "summary": "Every door that starts a flow by name (both trigger spellings, a declared type: 'flow' endpoint, and dispatchFlowAction behind the REST action route and the MCP run_action tool) now asks one third admission question after flowStartRefusal and before execute(): each record key the door hands the run in a row slot it defines (recordId, the objectName alias, an action's recordIdParam) is loaded in the caller's own scope and refused with the action door's own 404 RECORD_NOT_FOUND (refuseDeniedSubjectLoad) before any run exists. The object is the one the start names; a record key with no object is refused 400 VALIDATION_FAILED for a runAs: 'system' flow and starts as before for a user flow; a key that is not one id is refused 400 naming it. The action door's routed-record load is unchanged; the system principal is unchanged. No packages/spec change.",
      "tests": "New packages/runtime/src/flow-start-record-admission.test.ts, 32 tests: the refusal at every door with no run created, one answer for unreadable and nonexistent records, a firing control per door, each row slot judged with the value the run receives, the 400 cases loading nothing, the unchanged cases, and trigger/action envelope parity. Full runtime test on 5fda11a5 (os-verify-lock): Test Files 515 passed (515), Tests 8350 passed | 19 skipped (8369), VERDICT command-exit 0. Typecheck (incl. check:test-typecheck): VERDICT command-exit 0. Before the 8 fixture edits the full run read Tests 12 failed | 8338 passed (all record launches against doubles that did not serve the row). Ablations (ablation-replace.mjs, anchor x1 -> x0, restored, git diff HEAD 0 bytes after each): trigger door handed no keys -> 17 failed | 15 passed ('expected 200 to be 404', 'expected 200 to be 400', parity 'expected { status: 200, ... } to deeply equal { status: 404, ... }'); endpoint handed no keys -> 1 failed ('expected 200 to be 404'); action door handed no caller-param keys -> 3 failed ('expected 200 to be 404', MCP 'promise resolved \"{ ok: true, ... }\" instead of rejecting'); no-object elevated start admitted -> 2 failed ('expected 200 to be 400'); non-id key loaded -> 2 failed ('expected 404 to be 400'); alias slot dropped -> 2 failed ('expected 200 to be 404').",
      "gates": "dispatch-gates --commands on 2020b6e5: 93 commands, each run with stdin closed after turbo build --filter='./packages/**' (78/78, VERDICT command-exit 0); all 93 exit 0 (check:nul-bytes included). --ran reconcile: '93 derived famil(ies) accounted for — 93 run, 0 NOT-MEASURED (a DERIVED zero)', exit 0. First pass on 5fda11a5: check:system-context-census exit 1 (new isSystem read had no owning row) -> row 57 updated in 2020b6e5, green; two exit-3 prerequisites cleared (pinned fixture commit fetched; full build). Lint: eslint packages/runtime --no-inline-config exit 0, no output. CI check-runs on #29227: not read yet (draft just opened); the seat reads them.",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "6 — mcp__github__issue_read x3 (get, get_comments x2), mcp__github__pull_request_read x3 (get_files #29036, get #29097, get #29227); no write tool",
      "api_writes": "2 — relay pr_create via POST /repos/objectstack-ai/objectstack/dispatches (fleet-write run 41423812203 -> PR #29227, body 13104 bytes sent = stored) and this os-dev-report comment via post-stamped.mjs (relay). Also git push of the branch (not an API write).",
      "open_questions": [
        {
          "question": "Assumption C: should the record check also run for flows that run as the user?",
          "options": [
            "A (implemented) every flow, when the start names an object: the same as the action door's routed-record load, which already runs for every flow; cost 8 fixture edits in existing tests",
            "B only runAs: 'system' flows (the card's minimum): no fixture edits, but the trigger door and the action door would answer a user flow differently"
          ],
          "recommendation": "A, because parity of the doors is the card's point and the action door already asks this of every flow."
        },
        {
          "question": "Clause-② value: the claim set `no`, and the change refuses starts the doors accepted before (an unreadable record key, an elevated start with a record key and no object, a non-id key).",
          "options": [
            "A keep `Clause-②: no` (closing an unintended accept; the claim's value)",
            "B `Clause-②: yes (narrowing)` with the BREAKING banner and an ADR-0087 disposition"
          ],
          "recommendation": "A, the claim's value; contract review to confirm."
        },
        {
          "question": "Should a flow declare the record object it addresses (a packages/spec change, spec lane), so every door can read the object from the flow's own definition?",
          "options": [
            "A the seat files a spec-lane card; mechanism notes were sent to the seat privately",
            "B no follow-up"
          ],
          "recommendation": "A, so the record object is part of the flow's own definition; this PR needed no spec change."
        }
      ],
      "out_of_scope_findings": [],
      "files_changed": [
        ".changeset/29050-flow-start-record-admission.md",
        "content/docs/permissions/system-context.mdx",
        "packages/runtime/src/action-execution.ts",
        "packages/runtime/src/domains/automation.ts",
        "packages/runtime/src/endpoint-executor.ts",
        "packages/runtime/src/flow-start-admission.ts",
        "packages/runtime/src/flow-start-record-admission.test.ts",
        "packages/runtime/src/dispatcher-plugin.endpoint-fallback.integration.test.ts",
        "packages/runtime/src/domain-handler-registry.test.ts",
        "packages/runtime/src/domains/automation-trigger-params-shape.test.ts",
        "packages/runtime/src/endpoint-executor.test.ts",
        "packages/runtime/src/flow-caller-param-keys.test.ts",
        "packages/runtime/src/flow-run-locale.test.ts",
        "packages/runtime/src/flow-run-posture.test.ts",
        "packages/runtime/src/http-dispatcher.actions-type-dispatch.test.ts"
      ],
      "line_budget": "+749 / -15 over 15 files vs merge base 4550091a1: source +263/-7 (4 files), tests +458/-7 (9 files: new pin file +424, 8 fixture edits +34/-7), changeset +26, docs +2/-1. Under dispatch-gates' human-merge threshold 3000.",
      "deviations": [
        "content/docs/permissions/system-context.mdx is outside the card's named files: check:system-context-census required the new isSystem read to be owned by a row (row 57 extended, register regenerated with --write).",
        "Commit df7f0e50 is titled test(...) but also carries the trigger door's errorFromThrown exit, and commit 68b89e6a's body starts 'WIP:'. Not rewritten: no force-push on the published head.",
        "The session harness asked to set the requester as PR assignee and reviewer through MCP writes; not done, because the standard order forbids GitHub MCP writes and PR assignees.",
        "Gate prerequisites on the first pass: git fetch --depth=1 of the teardown-shape self-test's pinned fixture commit 621a4876; full package build for check:dual-build-cjs-loads."
      ]
    }

    Stamped 2026-11-02T09:51Z.


    Generated by Claude Code

  6. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmT7iCiGQkgXGQAo4Fo
    os-dev-report

    {
      "issue": 29050,
      "status": "done",
      "branch": "claude/project-thread-4gg947",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29227",
      "session": "session_01ADHkmT7iCiGQkgXGQAo4Fo",
      "premise_still_valid": true,
      "summary": "Delta on the seat's word: one docs-only commit on top of the ACCEPTed head 2020b6e5. New head 0258ffd81f0ee054709dc272919c75c2db3daf40. content/docs/automation/flows.mdx, 'Run a flow via API': the trigger route's outcome table gains a rule-only 404 RECORD_NOT_FOUND row (with the 400 VALIDATION_FAILED cases named in it), 'seven outcomes' becomes eight, 'The first six' becomes 'The first seven', and 'Only 400 describes a run' is narrowed to 400 FLOW_FAILED. No code, test or changeset change.",
      "tests": "No test input changed. Code readings stand from the main report: runtime full suite on 5fda11a5, Test Files 515 passed (515), Tests 8350 passed | 19 skipped, VERDICT command-exit 0; six ablations red then restored.",
      "gates": "dispatch-gates --commands on 0258ffd8: 93 commands (the same list as on 2020b6e5), each run with stdin closed; all 93 exit 0. --ran reconcile: '93 derived famil(ies) accounted for — 93 run, 0 NOT-MEASURED (a DERIVED zero — all 93 recorded an exit code and none of them is 3)', exit 0. CI check-runs on 0258ffd8: not read yet (just pushed); the seat reads them.",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "6 in total, unchanged by this delta — all reads",
      "api_writes": "3 in total — the main report's 2 (relay pr_create, os-dev-report comment) plus this delta report comment. Also git push 2020b6e5..0258ffd8.",
      "open_questions": [],
      "out_of_scope_findings": [],
      "files_changed": [
        "content/docs/automation/flows.mdx"
      ],
      "line_budget": "this delta +6/-5 in 1 file; PR total +755/-20 over 16 files vs merge base 4550091a1, under the human-merge threshold 3000",
      "deviations": [
        "The PR body still cites head 2020b6e5 and 15 files. It was not re-edited: relay issue_patch requires the seat signature as the body's first line, which would displace the attribution block and Fixes #29050. This comment carries the new head and readings."
      ]
    }

    Stamped 2026-11-02T10:19Z.


    Generated by Claude Code

  7. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1

    Landed · #29050 · 2026-11-02T11:32Z

    reading value
    PR #29227 MERGED by the merge queue at 2026-11-02T11:32:02Z, merge commit c347d22d2b (head 0258ffd8, ACCEPT 6574701327)
    this card closed completed by the PR's Fixes line

    State: pm:dispatched removed; the type, security, target:v18, area and domain labels stay. A flow-level declaration of the record object a flow addresses would be a packages/spec change; it is handled by the seat under the maintainer's ruling C, not on this card.

  8. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1

    Claim: PM loop round 9 · card thread · follow-up dispatch under ruling C (the start-record rule, carried further) · 2026-11-02T11:42Z
    Session: session_01ADHkmQKwKFCp1m6Pb6Q8c1 (the seat); the card thread is named in a follow-up claim once it reports its branch
    Account: os-steve (the seat's linked user)
    Branch: pending — the card thread's harness-pinned branch, written into a follow-up claim before the dev starts
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#5
    Ruling-ref: the maintainer's ruling quoted by triage 6571942605, verbatim 「A+C 同意」: rule C dispatches a security finding through pm-dispatch and never files it as an issue, so this claim on the landed card is the dispatch's identity record; every public surface states the rule only.
    File surface: packages/runtime/src/ (the flow start doors and refuseUnreadableFlowStartRecords in action-execution.ts, domains/automation.ts, endpoint-executor.ts, flow-start-admission.ts), their tests, changeset. ⛔ No packages/spec/**: anything that needs a new flow-level declaration is reported to the seat, not built.
    Container & model: M, mode:thread, model: the thread's default judgement tier, effort: xhigh (security, target:v18)
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the code is the runtime's flow start doors · the rule PR #29227 states (a start's record is checked in the caller's scope before any run exists) is carried to what a flow's own definition already declares about the record it addresses · reached by any caller allowed to start a record-keyed flow by name
    Thread-read: 6576087313
    Premise: holds (read at origin/main after PR #29227 merged as c347d22d2b)
    Serial constraints cleared: the dev reads the open PRs touching these files right before opening its PR and merges origin/main again then.
    Resume-from: none — fresh from origin/main

    State: this card stays closed and unlabelled; the PR references it as Refs #29050.

  9. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1

    Claim: PM loop round 9 · card thread · follow-up dispatch under ruling C · branch named · 2026-11-02T11:45Z (supersedes 6576282264, which had the branch pending)
    Session: session_01ADHkmQKwKFCp1m6Pb6Q8c1 (the seat); the card thread is session_01ADHkmRp2YLTpRc6Vfw6TD3
    Account: os-steve (the seat's linked user)
    Branch: claude/project-thread-kkdbga (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#5
    Ruling-ref: the maintainer's ruling quoted by triage 6571942605, verbatim 「A+C 同意」: rule C dispatches a security finding through pm-dispatch and never files it as an issue, so this claim on the landed card is the dispatch's identity record; every public surface states the rule only.
    File surface: packages/runtime/src/ (the flow start doors and refuseUnreadableFlowStartRecords in action-execution.ts, domains/automation.ts, endpoint-executor.ts, flow-start-admission.ts), their tests, changeset. ⛔ No packages/spec/**: anything that needs a new flow-level declaration is reported to the seat, not built.
    Container & model: M, mode:thread, model: the thread's default judgement tier, effort: xhigh (security, target:v18)
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the code is the runtime's flow start doors · the rule PR #29227 states (a start's record is checked in the caller's scope before any run exists) is carried to what a flow's own definition already declares about the record it addresses · reached by any caller allowed to start a record-keyed flow by name
    Thread-read: 6576282264
    Premise: holds (read at origin/main after PR #29227 merged as c347d22d2b)
    Serial constraints cleared: the dev reads the open PRs touching these files right before opening its PR and merges origin/main again then.
    Resume-from: none — fresh from origin/main

    State: this card stays closed and unlabelled; the PR references it as Refs #29050.

  10. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1

    Claim: PM loop round 9 · card thread · follow-up dispatch under ruling C · file surface revised · 2026-11-02T12:00Z (supersedes 6576324843)
    Session: session_01ADHkmQKwKFCp1m6Pb6Q8c1 (the seat); the card thread is session_01ADHkmRp2YLTpRc6Vfw6TD3
    Account: os-steve (the seat's linked user)
    Branch: claude/project-thread-kkdbga (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#5
    Ruling-ref: the maintainer's ruling quoted by triage 6571942605, verbatim 「A+C 同意」: rule C dispatches a security finding through pm-dispatch and never files it as an issue, so this claim on the landed card is the dispatch's identity record; every public surface states the rule only.
    File surface: packages/runtime/src/ (the flow start doors and refuseUnreadableFlowStartRecords in action-execution.ts, domains/automation.ts, endpoint-executor.ts, flow-start-admission.ts, and the REST action door's body checks in domains/actions.ts), their tests, changeset. ⛔ No packages/spec/**: anything that needs a new flow-level declaration is reported to the seat, not built.
    Container & model: M, mode:thread, model: the thread's default judgement tier, effort: xhigh (security, target:v18)
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the code is the runtime's flow start doors · the rule PR #29227 states (a start's record is checked in the caller's scope before any run exists) is carried to what a flow's own definition already declares about the record it addresses · reached by any caller allowed to start a record-keyed flow by name
    Thread-read: 6576324843
    Premise: holds (read at origin/main after PR #29227 merged as c347d22d2b)
    Serial constraints cleared: the dev reads the open PRs touching these files right before opening its PR and merges origin/main again then.
    Resume-from: none — fresh from origin/main

    State: this card stays closed and unlabelled; the PR references it as Refs #29050.

  11. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmRp2YLTpRc6Vfw6TD3
    os-dev-report

    {
      "issue": 29050,
      "status": "done",
      "branch": "claude/project-thread-kkdbga",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29365",
      "session": "session_01ADHkmRp2YLTpRc6Vfw6TD3",
      "premise_still_valid": true,
      "summary": "Follow-up under the maintainer's ruling, built only from what a flow definition already declares, with no spec change and no new keys. A start input that a flow's definition reads as the id of a record of a fixed object (a get/update/delete_record filter.id, $eq or $in with a literal objectName, a screen node's recordId, and the same through subflow and map hand-offs) is now loaded in the caller's own scope from that object at every door that starts the flow by name: 404 RECORD_NOT_FOUND when the caller cannot read it, 400 VALIDATION_FAILED naming the input when it is not one id (or a list of ids where it is read as a list), before the run exists. The REST action route also refuses a body recordId that is not one id with 400 naming recordId. The rest of the follow-up needs a spec change or another lane, and went to the seat privately.",
      "tests": "New packages/runtime/src/flow-start-declared-record-reads.test.ts: Test Files 1 passed (1), Tests 48 passed (48). Full runtime suite through os-verify-lock on head 5f06db07: Test Files 517 passed (517), Tests 8419 passed | 19 skipped (8438), VERDICT command-exit 0. The 50 existing runtime test files that drive the changed doors: Tests 1326 passed (1326), no forced edits. pnpm typecheck (runtime, incl. check:test-typecheck): exit 0. Ablations via scripts/ablation-replace.mjs on the committed tree, each restored with blob == HEAD and an empty git diff HEAD: declared reads not judged 19 failed ('expected 200 to be 404'); any value as one id 3 failed ('expected 404 to be 400'); any value as an id list 2 failed ('expected 200 to be 400'); callee flows not followed 3 failed ('expected {} to deeply equal { target: [ … ] }'); REST action route takes any recordId 6 failed ('expected 404 to be 400'); loop/map items read as inputs 1 failed; action door hands no inputs 4 failed ('promise resolved \"{ ok: true, … }\" instead of rejecting').",
      "gates": "dispatch-gates --commands on 5f06db07 derived 62; each ran with < /dev/null, exit captured before any pipe: 62 exit 0 (check-plugin-teardown-shape --self-test first exit 3, prerequisite: pinned fixture commit absent from the shallow clone; after git fetch --depth=1 of it, exit 0). dispatch-gates --ran: 62 derived, 62 run, 0 NOT-MEASURED (DERIVED zero). eslint packages/runtime --no-inline-config: exit 0. CI check-runs on the PR: not yet reported at posting time.",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "6 — all reads: mcp__github__issue_read get_comments ×3 (one answered 'invalid session' and was retried), mcp__github__pull_request_read get_files ×2 (#29283, #29097 overlap re-read), mcp__github__pull_request_read get ×1 (#29365 read-back). No write tool.",
      "api_writes": "2 — both through the fleet relay (POST /repos/objectstack-ai/objectstack/dispatches): pr_create #29365; this comment on #29050 via post-stamped.mjs. Plus one git push of claude/project-thread-kkdbga. Reads not counted as writes: 5 GET search/issues (open-PR overlap search).",
      "files_changed": [
        "packages/runtime/src/flow-start-admission.ts (+192 −2: flowDeclaredRecordReads and its walk)",
        "packages/runtime/src/action-execution.ts (+113 −26: refuseUnreadableFlowStartRecords judges declared reads; flowActionStartInputs; one id / id list refusals)",
        "packages/runtime/src/domains/automation.ts (+6 −1: flowStartRecordKeys returns inputs)",
        "packages/runtime/src/domains/actions.ts (+12: body recordId must be one id)",
        "packages/runtime/src/flow-start-declared-record-reads.test.ts (+576, new, 48 tests)",
        ".changeset/29050-flow-declared-record-reads.md (+32, patch, rule-only)"
      ],
      "line_budget": "source +323 −29 across 4 files; tests +576 in 1 new file; changeset +32. No forced edits to existing tests.",
      "deviations": [
        "The ablation legs and the runtime typecheck ran directly, not through os-verify-lock (one test file each leg; typecheck is tsc). The full runtime suite ran through the lock.",
        "The REST action route's recordId shape check covers every action type, not only flow actions: the subject load it guards is shared by all of them."
      ],
      "open_questions": [
        {
          "question": "A loop or map item variable that shares a start input's name hides that input from the reading, and an assignment that overwrites a start input does not. Keep that split?",
          "options": [
            "A keep: an iterator binding is certain to replace the value; an assignment may not run, so the caller's value is still judged",
            "B also exclude names an assignment writes"
          ],
          "recommendation": "A, because judging a caller-supplied value the flow may overwrite refuses nothing a legitimate caller sends, while excluding it would leave the input unjudged on every path where the assignment does not run"
        }
      ],
      "security_findings": "withheld",
      "out_of_scope_findings": []
    }

    Stamped 2026-11-02T13:00Z.


    Generated by Claude Code

  12. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1

    Claim: PM loop round 9 · card thread · follow-up dispatch under ruling C · file surface revised · 2026-11-02T13:04Z (supersedes 6576613570: adds the two docs pages that restate the start-door rule)
    Session: session_01ADHkmQKwKFCp1m6Pb6Q8c1 (the seat); the card thread is session_01ADHkmRp2YLTpRc6Vfw6TD3
    Account: os-steve (the seat's linked user)
    Branch: claude/project-thread-kkdbga (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#5
    Ruling-ref: the maintainer's ruling quoted by triage 6571942605, verbatim 「A+C 同意」: rule C dispatches a security finding through pm-dispatch and never files it as an issue, so this claim on the landed card is the dispatch's identity record; every public surface states the rule only.
    File surface: packages/runtime/src/ (the flow start doors and refuseUnreadableFlowStartRecords in action-execution.ts, domains/automation.ts, endpoint-executor.ts, flow-start-admission.ts, and the REST action door's body checks in domains/actions.ts), their tests, changeset, and the rule's two docs rows: content/docs/automation/flows.mdx (the trigger route's 404 RECORD_NOT_FOUND row) and content/docs/ui/actions.mdx (the action body's recordId), rule-only. ⛔ No packages/spec/**: anything that needs a new flow-level declaration is reported to the seat, not built.
    Container & model: M, mode:thread, model: the thread's default judgement tier, effort: xhigh (security, target:v18)
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the code is the runtime's flow start doors · the rule PR #29227 states (a start's record is checked in the caller's scope before any run exists) is carried to what a flow's own definition already declares about the record it addresses · reached by any caller allowed to start a record-keyed flow by name
    Thread-read: 6577783972
    Premise: holds (read at origin/main after PR #29227 merged as c347d22d2b)
    Serial constraints cleared: the dev reads the open PRs touching these files right before opening its PR and merges origin/main again then.
    Resume-from: none — fresh from origin/main

    State: this card stays closed and unlabelled; the PR references it as Refs #29050.

  13. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1

    Claim: PM loop round 10 · card thread · second follow-up dispatch under ruling C · 2026-11-02T13:07Z
    Session: session_01ADHkmQKwKFCp1m6Pb6Q8c1 (the seat); the card thread is named in a follow-up claim once it reports its branch
    Account: os-steve (the seat's linked user)
    Branch: pending: the card thread's harness-pinned branch, written into a follow-up claim before the dev starts
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#5
    Ruling-ref: the maintainer's 「A+C 同意」; rule C dispatches a security finding through pm-dispatch and never files it as an issue, so this claim is the dispatch's identity record, and every public surface states the rule only.
    File surface: packages/services/service-automation/src/builtin/ (the record nodes' filter operand handling in crud-nodes.ts), their tests, changeset. ⛔ No packages/spec/**.
    Container & model: M, mode:thread, model: the thread's default judgement tier, effort: xhigh (security)
    Clause-②: yes (narrowing), if the dev's measurement confirms a public accept-set change; contract review at CONTRACT_REVIEW_TIER by this seat on the delivered head
    Metadata-protocol: none
    Responsibility: the code is the flow engine's record nodes · the rule: an evaluated value in a filter's operand position is a value, never a query operator · reached by every flow with a record node whose filter reads a variable
    Thread-read: 6577844505
    Premise: holds (read at origin/main 5780fa49d: setFilterOperand writes the evaluated value at the operand path as it is)
    Serial constraints cleared: the first follow-up's PR #29365 touches packages/runtime/src/ only; the dev reads the open PRs touching crud-nodes.ts right before opening its PR.
    Resume-from: none: fresh from origin/main

    State: the card's state is unchanged by this claim; the PR references it as Refs #29050.

  14. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1

    Claim: PM loop round 10 · card thread · second follow-up dispatch under ruling C · branch named · 2026-11-02T13:08Z (supersedes 6577916318, which had the branch pending)
    Session: session_01ADHkmQKwKFCp1m6Pb6Q8c1 (the seat); the card thread is session_01ADHkmR4vTcUse1dqBKZNXq
    Account: os-steve (the seat's linked user)
    Branch: claude/project-thread-otcnyl (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#5
    Ruling-ref: the maintainer's 「A+C 同意」; rule C dispatches a security finding through pm-dispatch and never files it as an issue, so this claim is the dispatch's identity record, and every public surface states the rule only.
    File surface: packages/services/service-automation/src/builtin/ (the record nodes' filter operand handling in crud-nodes.ts), their tests, changeset. ⛔ No packages/spec/**.
    Container & model: M, mode:thread, model: the thread's default judgement tier, effort: xhigh (security)
    Clause-②: yes (narrowing), if the dev's measurement confirms a public accept-set change; contract review at CONTRACT_REVIEW_TIER by this seat on the delivered head
    Metadata-protocol: none
    Responsibility: the code is the flow engine's record nodes · the rule: an evaluated value in a filter's operand position is a value, never a query operator · reached by every flow with a record node whose filter reads a variable
    Thread-read: 6577916318
    Premise: holds (read at origin/main 5780fa49d: setFilterOperand writes the evaluated value at the operand path as it is)
    Serial constraints cleared: the first follow-up's PR #29365 touches packages/runtime/src/ only; the dev reads the open PRs touching crud-nodes.ts right before opening its PR.
    Resume-from: none: fresh from origin/main

    State: the card's state is unchanged by this claim; the PR references it as Refs #29050.

  15. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1

    Landed · first follow-up under ruling C (claim 6577844505) · 2026-11-02T13:58Z

    reading value
    PR #29365 MERGED by the merge queue at 2026-11-02T13:56:15Z, merge commit dfbe34d887 (head 1ffeb77cc1, ACCEPT 6578496085)
    this card stays closed; the PR referenced it with Refs

    The first follow-up dispatch is complete. The second (claim 6577943359) is still running.

  16. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmR4vTcUse1dqBKZNXq
    os-dev-report

    {
      "issue": 29050,
      "status": "done",
      "branch": "claude/project-thread-otcnyl",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29404",
      "session": "session_01ADHkmR4vTcUse1dqBKZNXq",
      "premise_still_valid": true,
      "summary": "A flow's record nodes (get_record, update_record, delete_record) now treat an evaluated value in a filter's operand position as a value, never as a query operator: a value that is not a scalar (or a list of scalars where the operator takes a list: $in, $nin, $between) is refused before the query runs, at every operand position, whatever the value came from. The judgement sits in resolveNodeFilter (crud-nodes.ts), the one place the three nodes resolve their filter; the refusal is a guard like the existing nothing guard beside it, names the node, the expression and the operand's place, and never echoes the value. Author-written structure and literal operands are unchanged. The premise was measured before the fix; the readings went to the seat privately (rule C).",
      "tests": "Under os-verify-lock on head 37f60fd6: vitest run in packages/services/service-automation → \"Test Files 260 passed (260) · Tests 3368 passed (3368)\"; build closure 31/31 tasks; typecheck exit 0 (check:test-typecheck OK). New describe in crud-filter-guard.test.ts: 28 cases (file 65). Ablations via scripts/ablation-replace.mjs on 04dd52eb, each restored to the HEAD blob: value check short-circuited → \"Tests 19 failed | 46 passed (65)\"; list-position check removed → \"Tests 3 failed | 62 passed (65)\"; list-member check removed → \"Tests 2 failed | 63 passed (65)\". Each pin goes red with its check removed.",
      "gates": "node scripts/pm/dispatch-gates.mjs --commands derived 60 commands; all 60 run with stdin /dev/null, exit captured before any pipe: 60 exit 0 (check:dual-build-cjs-loads read exit 3 until a full-package build under the lock, then exit 0). --ran: \"60 derived famil(ies) accounted for — 60 run, 0 NOT-MEASURED (a DERIVED zero)\". Lint: eslint --no-inline-config over packages/services/service-automation exit 0 (repo-wide run is CI's). test:repo not run locally. CI check-runs on 37f60fd6: pending at report time, not read.",
      "dogfood_battery": "37f60fd6 · os-verify-lock: VERDICT command-exit 0 · held the lock 2092s (34m52s) · Test Files 393 passed | 3 skipped (396) · Tests 3243 passed | 28 skipped (3271) · no failures",
      "files_changed": [
        "packages/services/service-automation/src/builtin/crud-nodes.ts (+86/−6)",
        "packages/services/service-automation/src/builtin/crud-filter-guard.test.ts (+186/−0)",
        ".changeset/29050-record-node-operand-value.md (+18/−0)"
      ],
      "line_budget": "296 changed lines (+290/−6) in 3 files: source 92, test 186, changeset 18; no card budget named; under the 3000-line dispatch-gates threshold",
      "deviations": [
        "Clause-② spelled `no (narrowing)` (PR body and changeset), not the delta's conditional `yes (narrowing)`: no door's accept set or schema changes; what narrows is a run-time value a record node takes, and the lane spells a pure narrowing `no (narrowing)`.",
        "Refusal class is a guard (a fault edge does not route it; a try_catch region catches it), matching the nothing guard in the same function, where the delta's Shape says a flow fault. Raised in open_questions.",
        "PR body opens with the attribution block, then `Refs #29050` (per the delta: never Fixes or Part of).",
        "pnpm lint scoped to the touched package; test:repo not run locally (no file of that project changed).",
        "Ablations read on the fix commit 04dd52eb; the later merge of origin/main did not touch either file.",
        "Docs and skill text (content/docs/automation/flows.mdx, skills/objectstack-automation/SKILL.md) not updated: outside the file surface, skills/** is governed. Declared in the PR body as a follow-up owed."
      ],
      "mcp_calls": "7 — read-only: issue_read get, issue_read get_comments, list_pull_requests ×2, pull_request_read get_files (#29097), pull_request_read get (#29345), pull_request_read get (#29404, read-back). No write tool. (Plus one session subscribe_pr_activity on #29404, not a GitHub write.)",
      "api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (fleet-write request fw-20261102T141230Z-f4b565: pr_create → #29404 as objectstack-fleet[bot], read-back identical, no assignees, no labels); POST /repos/objectstack-ai/objectstack/dispatches (post-stamped.mjs, transport dispatch: this report as a comment on #29050). Branch pushes: git push to claude/project-thread-otcnyl only.",
      "open_questions": [
        {
          "question": "Should the refusal stay a guard, or become a routable runtime failure?",
          "options": [
            "A keep the guard: consistent with the nothing guard; the filter the author wrote is not the one that would run, so no fault edge should swallow it",
            "B a routable failure: a fault edge can route it, at the cost of a flow continuing past a node whose filter was not the authored one"
          ],
          "recommendation": "A, because it matches the existing guard in the same function and keeps the refusal from being silently routed"
        },
        {
          "question": "Who carries the docs and skill follow-up?",
          "options": [
            "A a separate docs card (flows.mdx, plus the governed skills/objectstack-automation/SKILL.md edit under GOVERNED_APPROVERS)",
            "B fold it into this PR (widens the file surface onto a governed path and makes the PR wait on governed approval)"
          ],
          "recommendation": "A, because it keeps this PR off the governed surface"
        },
        {
          "question": "Should the list-comparand operator set ($in, $nin, $between) come from spec instead of a local constant?",
          "options": [
            "A keep it local with the reconciliation test against FieldOperatorsSchema (red if the grammar adds a list operator)",
            "B a spec card exports the set from a public barrel"
          ],
          "recommendation": "A, because no packages/spec edit is in scope and the test already holds the two in step"
        }
      ],
      "security_findings": "withheld",
      "out_of_scope_findings": [
        "withheld — one adjacent observation, carried to the seat privately; noted, not filed (rule C)"
      ]
    }

    Stamped 2026-11-02T14:14Z.


    Generated by Claude Code

  17. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmR4vTcUse1dqBKZNXq
    os-dev-report

    {
      "issue": 29050,
      "status": "done",
      "round": 2,
      "branch": "claude/project-thread-otcnyl",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29404",
      "head": "cf1d52e8",
      "session": "session_01ADHkmR4vTcUse1dqBKZNXq",
      "premise_still_valid": true,
      "summary": "Round 2 answers the contract review's should-fix 1 at head cf1d52e8. An evaluated list at an $in / $nin / $between comparand is now refused, as a guard, when an entry is nothing, the same way an operand that is nothing is refused. The refusal text, the docblocks and the changeset name the value set the code enforces: a string, number, bigint, boolean or date (bigint is named, not folded into number), plus a list of them with no empty entry at a list operator. The PR body's Skills-docs line now names follow-up #29418; the list-operator export is #29419, no change here.",
      "tests": "Under os-verify-lock on cf1d52e8: vitest run in packages/services/service-automation → \"Test Files 260 passed (260) · Tests 3369 passed (3369)\"; build closure 31/31; typecheck exit 0. One new pin (delete_record: a list operator's list holding an empty entry is refused, located at the operand); crud-filter-guard.test.ts now 66 tests. Ablation via scripts/ablation-replace.mjs on cf1d52e8, empty-entry check removed → \"Tests 1 failed | 65 passed (66)\", the new pin; restored, blob == HEAD, git diff HEAD empty. The three round-1 ablations (04dd52eb) stand.",
      "gates": "dispatch-gates --commands on cf1d52e8 derived the same 60 commands; 60 run with stdin /dev/null, exit captured before any pipe: 60 exit 0 (check:type-check-debt first read exit 3, its closure build queue-timed out behind the dogfood run on the lock, nothing measured; re-run under the lock: exit 0, 0 in the DEBT ledger). --ran: \"60 derived famil(ies) accounted for — 60 run, 0 NOT-MEASURED (a DERIVED zero)\". eslint --no-inline-config over packages/services/service-automation exit 0. CI check-runs on cf1d52e8: not read at report time.",
      "dogfood_battery": "cf1d52e8 · os-verify-lock: VERDICT command-exit 0 · held the lock 1933s (32m13s) · Test Files 393 passed | 3 skipped (396) · Tests 3243 passed | 28 skipped (3271) · no failures",
      "files_changed": [
        "packages/services/service-automation/src/builtin/crud-nodes.ts (round 2: +25/−17; net +94/−6)",
        "packages/services/service-automation/src/builtin/crud-filter-guard.test.ts (round 2: +2/−0; net +188)",
        ".changeset/29050-record-node-operand-value.md (round 2: +2/−2; net +18)"
      ],
      "line_budget": "net 306 changed lines (+300/−6) in 3 files; round 2 is +29/−19; under the 3000-line dispatch-gates threshold",
      "deviations": [
        "bigint is named in the refusal text and the changeset rather than folded into number, so the declared set equals isAcceptedFilterComparand less null.",
        "The empty-entry refusal joins the not-a-value refusal (detail \"a list whose entry [i] is nothing\"), a guard like the nothing guard, rather than the nothing guard's own message, which speaks of an operand that is nothing.",
        "No re-merge of origin/main in round 2: main moved but touched neither changed file; PR mergeable_state clean.",
        "The contract-review comment was read once with gh api (a REST read), not an MCP tool."
      ],
      "mcp_calls": "9 cumulative, all reads: issue_read get, issue_read get_comments, list_pull_requests ×2, pull_request_read get_files (#29097), pull_request_read get (#29345), pull_request_read get (#29404) ×2, pull_request_read get_check_runs (#29404). No write tool.",
      "api_writes": "4 cumulative, all POST /repos/objectstack-ai/objectstack/dispatches through the fleet-write relay: pr_create → #29404; the round-1 os-dev-report on #29050; issue_patch #29404 body (round 2, read-back identical); this round-2 report on #29050. Branch pushes: git push to claude/project-thread-otcnyl only (37f60fd6..cf1d52e8).",
      "open_questions": [],
      "security_findings": "withheld",
      "out_of_scope_findings": [
        "withheld — carried to the seat privately; noted, not filed (rule C)"
      ]
    }

    Stamped 2026-11-02T15:24Z.


    Generated by Claude Code

  18. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-steve · session_01ADHkmQKwKFCp1m6Pb6Q8c1

    Landed · second follow-up under ruling C (claim 6577943359) · 2026-11-02T17:06Z

    reading value
    PR #29404 MERGED by the merge queue at 2026-11-02T16:02:48Z, merge commit 555645566c (head cf1d52e814, contract review PASS ×2, ACCEPT on the PR)
    this card stays closed; the PR referenced it with Refs
    follow-ups skills #29418, spec #29419; the A+C decision is #29425
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