You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Studio flow editor: a notify node's stored channels: 'email' (string form) reads as an empty list, and the next edit drops email #25999
Filing gate ①: a product defect with a named landing point and a reproduction. Filed by seat domain:ui#1 (seat post #25966) · os-steve · session session_017iXjUm1rujwDk7f3KT1Pkk, from the out-of-scope finding in the #25900 os-dev-report (comment 6411548050). ⛔ Not a claim.
reach: public door, the Studio flow editor (Console → Studio → a flow → a notify node → Channels). Measured by the #25900 card thread with a render probe at origin/main (probe deleted): rendered with value: 'email', the field showed no email row, Add gave one empty textbox, and the commit after typing inbox was ['inbox']. The stored email channel was gone.
The defect
NotifyConfigSchema.channels accepts a string or a string list. The spec's own refusal text tells authors to write the string form: packages/spec/src/automation/io-node-config.zod.ts ends the email-field rule with "give the address its own notify node, with channels: 'email'."
ui/packages/app-shell/src/views/metadata-admin/inspectors/FlowStringListField.tsx line 61 reads any non-array value as []. So a stored string channel draws as an empty list, and the first commit replaces it, silently dropping the channel the author chose. A flow that emailed will now only post to the inbox.
A stored string value reads as a one-entry list ('email' → ['email']), so the editor shows it and an edit keeps it. Whether the commit writes back the list form or keeps the string when one entry is left is the dev's call, stated in the PR; both are accepted by the schema.
Business reason
Who benefits: anyone who edits a notify flow in Studio after authoring it by hand, by AI build or from the spec's own advice. Scenario: an admin changes the title of a notification that emails applicants, saves, and the emails stop. Left undone, this is silent data loss in a saved flow, with no error at save time.
Who acts on it
The domain:ui seat 1 (app-shell), one dispatch. The fix is in FlowStringListField.tsx, plus its test.
Duplicate check
search_issues on objectstack-ai/objectstack, "notify channels stringList editor string value reads empty FlowStringListField drops email": 0 hits.
p2: a silent data loss on edit. A stored channels: 'email' (the string form the spec itself recommends) reads as empty, and the next save drops email. The fix: the channels field normalizes a string to a one-item list on read. There is no spec change.
Claim: PM loop round 1
Session: session_017iXjUm1rujwDk7f3KT1Pkk (seat); implementing card thread session_01T9TKf9xmHBvHQzFKLqxM6Q
Account: os-steve
Branch: claude/project-thread-anpn3q (platform-assigned card-thread branch; a card thread can push only its assigned branch, and the name carries no issue number, so this claim is the identity record)
Worktree: the card thread's own container
Domain: domain:ui
Seat: domain:ui#1
File surface: ui/packages/app-shell/src/views/metadata-admin/inspectors/FlowStringListField.tsx (the read at line 61 that turns any non-array value into []) and its test, scripts/sdui-manifest.record.json (derived hash). Fix per triage (08:57Z): a stored string reads as a one-entry list, so the editor shows it and an edit keeps it. Whether the commit writes back the list form or keeps the string for a single entry is the dev's call, stated in the PR; the schema accepts both. No spec change (stop on breach; explain in the report). ⛔ ui/.github/** and the root .github/workflows/ci.yml are out of bounds (maintainer's word, ui-merge epic in flight). Changeset in the root .changeset/.
Container & model: S, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate), effort: medium
Clause-②: no (one internal inspector field; no exported type change)
Metadata-protocol: none
Responsibility: n/a: renderer defect in ui/, the producing code is the cited ui file; no platform path covers it; reached through the public Console
Thread-read: 6412104020
Premise: holds (read-only check by the seat at origin/main: line 61 still reads a non-array value as an empty list 2026-10-26T09:27Z)
Serial constraints cleared: none: no open PR touches FlowStringListField.tsx (open-PR file scan 2026-10-26T09:27Z)
Dispatch order handed to the card thread by the project coordinator (three partitions: ruling / mechanism hypotheses / suggested route); the dev does not claim, and starts only once this claim names its branch.
{
"issue": 25999,
"status": "done",
"branch": "claude/project-thread-anpn3q",
"pr": "https://github.com/objectstack-ai/objectstack/pull/26038",
"session": "session_01T9TKf9xmHBvHQzFKLqxM6Q",
"premise_still_valid": true,
"summary": "FlowStringListField read any non-array value as []; it now reads a non-blank string as one trimmed entry (same normalisation as notify-node toStringList), so a stored channels: \"email\" shows and survives an edit. Write-back: an edit commits the list form; an untouched field commits nothing, so the stored string stays. No spec change. Card assignee untouched; PR assignee os-steve.",
"tests": "at 0a84c545 under os-verify-lock: cd ui && vitest run --maxWorkers=2 on FlowStringListField.stringValue-25999.test.tsx + FlowObjectListField.select.test.tsx + FlowNodeInspector.entryCondition.test.tsx -> Test Files 3 passed (3), Tests 16 passed (16), VERDICT command-exit 0. Ablation via scripts/ablation-replace.mjs --delete of the string branch in toList (anchor x1->x0, blob 55a44b94->528f79b8): 4 failed | 3 passed (7) (shows/keeps/removes/trims red; list/blank/no-commit green, as predicted); restore blob == HEAD 55a44b94, git diff HEAD empty. No dist build needed: test imports ./FlowStringListField from src. pnpm --filter @object-ui/app-shell type-check (after --filter @object-ui/app-shell^... build): VERDICT command-exit 0, 0 error TS; tsconfig.test.json compiles the tests. Narrowed: full app-shell suite left to CI Test Core.",
"gates": "local: dispatch-gates --commands --repo objectstack-ai/objectstack at 0a84c545 -> 34 families, each exit 0 (captured before any pipe); --ran reconciliation: 34 derived, 34 run, 0 NOT-MEASURED (derived zero). check-sdui-manifest.mjs exit 0 after gen-sdui-manifest-node.mjs (VERDICT command-exit 0): the record file came out byte-identical because app-shell is not a registry source package, so it is not in the diff. CI check-runs on PR 26038: in_progress at report time, not re-run locally.",
"dogfood_battery": "n/a — no ! in the PR title and Clause-② no (no narrowing arm)",
"mcp_calls": "2 — mcp__github__issue_read get_comments x2 (reads only)",
"api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/pulls + POST /issues/26038/assignees, one dispatch) and this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/25999/comments via post-stamped.mjs); plus git push (not REST)",
"deviations": [
"scripts/sdui-manifest.record.json named in the claim surface is unchanged: regenerated, byte-identical, check green",
"first push refused by check:commit-card-trailers (model name in Co-Authored-By from the harness reminder); amended to the model-free pair before any publish"
],
"files_changed": [
"ui/packages/app-shell/src/views/metadata-admin/inspectors/FlowStringListField.tsx",
"ui/packages/app-shell/src/views/metadata-admin/inspectors/FlowStringListField.stringValue-25999.test.tsx",
".changeset/25999-flow-string-list-stored-string.md"
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed — FlowObjectListField.tsx ~L764 reads a nested stringList/numberList column non-array value as [] the same way; no spec key known to render there accepts a bare string, so observation only (PR Acceptance notes)"
]
}
Activity
objectstack-fleet commentedon Oct 26, 2026
Triage: first grade —
bug·priority:p2·domain:ui·area:workflow·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-26T08:57Z⛔ Not a claim, ⛔ not a dispatch.
channels: 'email'(the string form the spec itself recommends) reads as empty, and the next save dropsemail. The fix: the channels field normalizes a string to a one-item list on read. There is no spec change.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 1
Session:
session_017iXjUm1rujwDk7f3KT1Pkk(seat); implementing card threadsession_01T9TKf9xmHBvHQzFKLqxM6QAccount:
os-steveBranch:
claude/project-thread-anpn3q(platform-assigned card-thread branch; a card thread can push only its assigned branch, and the name carries no issue number, so this claim is the identity record)Worktree: the card thread's own container
Domain:
domain:uiSeat:
domain:ui#1File surface:
ui/packages/app-shell/src/views/metadata-admin/inspectors/FlowStringListField.tsx(the read at line 61 that turns any non-array value into[]) and its test,scripts/sdui-manifest.record.json(derived hash). Fix per triage (08:57Z): a stored string reads as a one-entry list, so the editor shows it and an edit keeps it. Whether the commit writes back the list form or keeps the string for a single entry is the dev's call, stated in the PR; the schema accepts both. No spec change (stop on breach; explain in the report). ⛔ui/.github/**and the root.github/workflows/ci.ymlare out of bounds (maintainer's word, ui-merge epic in flight). Changeset in the root.changeset/.Container & model:
S,mode:thread,model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate),effort: mediumClause-②: no (one internal inspector field; no exported type change)
Metadata-protocol: none
Responsibility:
n/a: renderer defect in ui/, the producing code is the cited ui file; no platform path covers it; reached through the public ConsoleThread-read: 6412104020
Premise: holds (read-only check by the seat at
origin/main: line 61 still reads a non-array value as an empty list 2026-10-26T09:27Z)Serial constraints cleared: none: no open PR touches FlowStringListField.tsx (open-PR file scan 2026-10-26T09:27Z)
Dispatch order handed to the card thread by the project coordinator (three partitions: ruling / mechanism hypotheses / suggested route); the dev does not claim, and starts only once this claim names its branch.
objectstack-fleet commentedon Oct 26, 2026
os-dev-report
{ "issue": 25999, "status": "done", "branch": "claude/project-thread-anpn3q", "pr": "https://github.com/objectstack-ai/objectstack/pull/26038", "session": "session_01T9TKf9xmHBvHQzFKLqxM6Q", "premise_still_valid": true, "summary": "FlowStringListField read any non-array value as []; it now reads a non-blank string as one trimmed entry (same normalisation as notify-node toStringList), so a stored channels: \"email\" shows and survives an edit. Write-back: an edit commits the list form; an untouched field commits nothing, so the stored string stays. No spec change. Card assignee untouched; PR assignee os-steve.", "tests": "at 0a84c545 under os-verify-lock: cd ui && vitest run --maxWorkers=2 on FlowStringListField.stringValue-25999.test.tsx + FlowObjectListField.select.test.tsx + FlowNodeInspector.entryCondition.test.tsx -> Test Files 3 passed (3), Tests 16 passed (16), VERDICT command-exit 0. Ablation via scripts/ablation-replace.mjs --delete of the string branch in toList (anchor x1->x0, blob 55a44b94->528f79b8): 4 failed | 3 passed (7) (shows/keeps/removes/trims red; list/blank/no-commit green, as predicted); restore blob == HEAD 55a44b94, git diff HEAD empty. No dist build needed: test imports ./FlowStringListField from src. pnpm --filter @object-ui/app-shell type-check (after --filter @object-ui/app-shell^... build): VERDICT command-exit 0, 0 error TS; tsconfig.test.json compiles the tests. Narrowed: full app-shell suite left to CI Test Core.", "gates": "local: dispatch-gates --commands --repo objectstack-ai/objectstack at 0a84c545 -> 34 families, each exit 0 (captured before any pipe); --ran reconciliation: 34 derived, 34 run, 0 NOT-MEASURED (derived zero). check-sdui-manifest.mjs exit 0 after gen-sdui-manifest-node.mjs (VERDICT command-exit 0): the record file came out byte-identical because app-shell is not a registry source package, so it is not in the diff. CI check-runs on PR 26038: in_progress at report time, not re-run locally.", "dogfood_battery": "n/a — no ! in the PR title and Clause-② no (no narrowing arm)", "mcp_calls": "2 — mcp__github__issue_read get_comments x2 (reads only)", "api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/pulls + POST /issues/26038/assignees, one dispatch) and this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/25999/comments via post-stamped.mjs); plus git push (not REST)", "deviations": [ "scripts/sdui-manifest.record.json named in the claim surface is unchanged: regenerated, byte-identical, check green", "first push refused by check:commit-card-trailers (model name in Co-Authored-By from the harness reminder); amended to the model-free pair before any publish" ], "files_changed": [ "ui/packages/app-shell/src/views/metadata-admin/inspectors/FlowStringListField.tsx", "ui/packages/app-shell/src/views/metadata-admin/inspectors/FlowStringListField.stringValue-25999.test.tsx", ".changeset/25999-flow-string-list-stored-string.md" ], "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed — FlowObjectListField.tsx ~L764 reads a nested stringList/numberList column non-array value as [] the same way; no spec key known to render there accepts a bare string, so observation only (PR Acceptance notes)" ] }Generated by Claude Code