Skip to content

automation: cloning a packaged flow writes the sys_metadata row and its audit row with a null actor, so nothing records which admin created the clone #27414

Closed
1 of 1 issue completed

Description

@objectstack-fleet

domain:devx#1 · sunhaolin · session_017xRBcScm27Q3R5mkYLPWdB

QA-source: #27409 · automation.setup-packaged-automation-board · observed beside the clauses

Found by an off-clause probe in the 18.3.0 release-candidate QA run #27409, at framework c4c515ad, on the stock showcase.

#26474 fixed the null actor on the packaged flow and action toggle doors, and this run confirmed that fix holds. When #26474 was closed, the clone door was explicitly left out of its scope with a request for a card of its own. Searching open and closed issues found none, so this is that card.

Reproduction

  1. Boot the stock showcase on a fresh file DB and sign in as admin@objectos.ai.
  2. POST /api/v1/automation/showcase_task_completed/clone with {"name":"qa_task_completed_clone","label":"QA clone"}. It answers 200.
  3. Read the new sys_metadata row for qa_task_completed_clone with GET /api/v1/data/sys_metadata/ID?select=created_by,updated_by. Both fields are null.
  4. GET /api/v1/data/sys_audit_log?top=40&$orderby=created_at desc. The create row on sys_metadata for that id has user_id null and actor null, while the toggle rows beside it carry the admin's id.

Expected vs actual

Mechanism

Suggested acceptance


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: 真挡得住的权限 | none | P②

    Triage: p2 · domain:server · area:automation · 2026-10-29T06:03Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · sunhaolin · session_017xRBcScm27Q3R5mkYLPWdB

    Path: 真挡得住的权限 | none | P②

    Triage: lands in packages/runtime/src/domains/automation.ts (domain:server); rationale: this concurs with the first touch at 06:03Z (comment 6489286673) on class, domain, area and p2. The landing file is now located. Labels stay as they are (bug, priority:p2, pm:queue, area:automation, domain:server). Read at origin/main ded67bfb, 2026-10-29T09:55Z.

    Size/model suggestion: S, or S/M if create and update reproduce and get pins too.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · sunhaolin · session_017xRBcScm27Q3R5mkYLPWdB

    Claim: PM loop round 1
    Session: session_017xRBcScm27Q3R5mkYLPWdB
    Account: sunhaolin
    Branch: claude/issue-27414-flow-save-actor
    Worktree: objectstack-issue-27414
    Domain: domain:server
    Seat: domain:devx#1
    File surface: packages/runtime/src/domains/automation.ts (registerAndSaveFlow and its callers clone / create / update only), its tests under packages/runtime, one @objectstack/runtime changeset (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: default (dispatch-gates --tier at c4c515a: "no path-derived mandate"), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: runtime automation domain code: registerAndSaveFlow calls saveMetaItem without the caller as actor | no platform path stamps it; #26605 fixed the same omission on the toggle doors | reached by every admin who clones (and very likely creates or updates) a flow; the audit trail is used today
    Thread-read: 6494081779
    Premise: holds (triage first touch re-verified the mechanism on origin/main ded67bfb; the dev re-measures before editing)
    Serial constraints cleared: none — the 10 open domain:server + pm:dispatched claims were read; #27237 owns packages/services/service-automation/**, a different package from this card's packages/runtime file

    Maintainer direct dispatch (cross-lane claim authority): the maintainer, in session session_017xRBcScm27Q3R5mkYLPWdB on 2026-10-29, verbatim: 「你应该使用 pm-dispatch 技能处理 issue 和 pr」. This devx-seat session claims these domain:server cards on that instruction; the card stays domain:server.
    Scope note from triage: registerAndSaveFlow also serves POST /automation and PUT /automation/:name; the dev measures create and update first and fixes them in this PR only if they show the same null actor (same function, same fix).


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 27414,
    "status": "rework",
    "branch": "claude/issue-27414-flow-save-actor",
    "pr": "#27538",
    "session": "session_017xRBcScm27Q3R5mkYLPWdB",
    "premise_still_valid": true,
    "summary": "Partial delivery, PR body opens 'Part of #27414'. Delivered: registerAndSaveFlow (packages/runtime/src/domains/automation.ts) now passes the caller's userId as saveMetaItem's actor, read through activationCaller(context) (the #26605 helper), so POST /automation, PUT /automation/:name and POST /automation/:name/clone stamp sys_metadata created_by/updated_by and sys_metadata_history.recorded_by. Measured live on a fresh showcase before the change: create and update reproduce the clone null actor, so all three are in scope. NOT delivered: the sys_audit_log user_id/actor half of the card's acceptance. Measured live, it is null on every sys_metadata write door, including the PUT /meta/flow/:name control that already passes actor. Mechanism: SysMetadataRepository.put (packages/metadata-protocol/src/sys-metadata-repository.ts) inserts/updates under { ...ctx, isSystem: true } with no session.userId and no provenance.attributedUserId, the only two fields plugin-audit reads for user_id. That file is outside the claimed surface, so I stopped there (see open_questions).",
    "tests": "Live HTTP, stock showcase, fresh file DB, admin signed in. Before (base ded67bf): clone/create/update rows created_by=null updated_by=null; /meta control row = admin; all 4 sys_audit_log rows on sys_metadata user_id=null actor=null. After (head d325afa, runtime rebuilt; dist marker grep 1): clone/create/update rows = admin id; audit rows still null on all 4 doors. New real-store pin automation-flow-save-actor.test.ts: 3/3 (create plus update by another user, clone, and a no-user control on all three doors that records null). Durable pins updated to expect actor. Ablation via scripts/ablation-replace.mjs, fix committed first: the actor spread was removed (anchor 1->0, blob 14a061fa->540e6524). Result 4 failed / 10 passed: 2 durable pins and the create/update and clone row pins failed, the control stayed green. Restored: blob == HEAD 14a061fa, git diff HEAD empty. Runtime imports automation.ts from src, so no rebuild was needed. At 8551475 under the lock: runtime typecheck exit 0 (including check:test-typecheck); runtime test 472/472 files, 7671 passed, 19 skipped, VERDICT command-exit 0. After the origin/main merge (f1f7ce3, incoming objectql change, runtime^... rebuilt): 6 related files 47/47, VERDICT command-exit 0.",
    "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at 8551475 derived 61. --ran reconciliation exit 0: '61 derived, 59 run, 2 NOT-MEASURED, 0 UNRUN'. NOT MEASURED (exit 3, PREREQUISITE NOT MET, left to CI): node scripts/check-plugin-teardown-shape.mjs --self-test (shallow clone, pinned control commit unreachable; the scan run itself exited 0) and pnpm check:dual-build-cjs-loads (no full workspace build). All other 59 exited 0. eslint --no-inline-config on the 3 touched .ts files: exit 0. PR check-runs: in_progress at report time, not waited on.",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "mcp_calls": "0",
    "api_writes": "3 REST requests across 2 relay dispatches: pr_create (POST /repos/objectstack-ai/objectstack/pulls, then POST /repos//issues/27538/assignees), and this report (POST /repos//issues/27414/comments). git push is not counted.",
    "line_budget": "n/a — no skills/** or ledger file touched",
    "files_changed": [
    "packages/runtime/src/domains/automation.ts",
    "packages/runtime/src/domains/automation-flow-save-actor.test.ts",
    "packages/runtime/src/domains/automation-authoring-doors-durable.test.ts",
    ".changeset/27414-flow-save-actor.md"
    ],
    "deviations": [
    "Showcase booted with 'pnpm --filter @objectstack/example-showcase dev -- --fresh -p 38714' instead of 'pnpm dev'. Only the showcase closure was built, and check-dev-prereqs refuses a partial build. The server was stopped by its port afterwards.",
    "A stray file /build.pid (the pid of the build process) was written at the filesystem root by a mis-scoped shell variable. Removing it was refused by the harness safety check, so it is still there for a person to delete.",
    "The gate runner's while-read loop lost stdin after 57 gates. The remaining 4 were rerun with stdin from /dev/null, and all 4 exited 0. A first exit-3 record for check:type-check-debt came from that loop and was removed from the record before reconciliation.",
    "The PR body footer uses the AGENTS.md session-URL form, not the harness reminder's emoji form (AGENTS.md takes precedence)."
    ],
    "open_questions": [
    {
    "question": "How should the sys_audit_log half of #27414 be finished? The audit row names nobody on every metadata save door, /meta included.",
    "options": [
    "A: widen this card's surface to packages/metadata-protocol/src/sys-metadata-repository.ts and add a second PR there. The put/delete engine writes would carry opts.actor as provenance.attributedUserId, the existing channel plugin-audit already reads for a human credited on a system-authorized write. One edit covers every metadata door. Cost: a metadata-protocol pin plus a check:system-context-census reading. S/M.",
    "B: close this card on the sys_metadata half and file the audit half as its own class (b) card across all metadata doors. Cost: one more card. This card's acceptance text would then need amending."
    ],
    "recommendation": "A, as the card's own acceptance names the audit row. The mechanism is one function in one package. The channel already exists and is the one the audit writer documents for exactly this case, so no second mechanism is needed."
    }
    ],
    "out_of_scope_findings": [
    "class: b · reach: PUT /api/v1/meta/flow/qa_meta_before as admin on the stock showcase -> sys_audit_log create row on sys_metadata has user_id null, actor null (measured live) · evidence: SysMetadataRepository.put writes under { ...ctx, isSystem: true } with no userId/attributedUserId; ADR-0073 D3 'Attribution is always concrete' · Seam: runtime:SysMetadataRepository.put -> plugin-audit audit-writers user_id · this is the undelivered half of #27414 itself; it widens to every metadata door · dedupe words: sys_audit_log sys_metadata user_id null actor, metadata save audit attribution, attributedUserId SysMetadataRepository"
    ]
    }

  5. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · sunhaolin · session_017xRBcScm27Q3R5mkYLPWdB

    ACCEPT → PR #27538 (partial: the sys_metadata item, Part of #27414), reviewed 2026-10-29T11:46Z. The sys_audit_log item is sub-issue #27539, claimed by this seat and dispatched. This card closes when both have landed.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · sunhaolin · session_017xRBcScm27Q3R5mkYLPWdB

    Handover: PR #27538 → PM session session_017xRBcVy4VXmkQjn2MGeaWk (review and landing)


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · sunhaolin · session_017xRBcVy4VXmkQjn2MGeaWk

    Closed as delivered · 2026-10-29T17:52Z

    Both halves named by this card have merged:

    Each PR carried Part of #27414, so nothing closed this card automatically. Nothing is left on it, so it is closed and pm:dispatched is off. The retest is automation.setup-packaged-automation-board in the next checklist wave.


    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

Labels

area:automationbugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions