Repository navigation
plugin-approvals: an approval request's record title (display_title / titleFormat) still reads flow:NAME · RECORD_ID — once #23889 lands, its flow half reads the process_label that card adds #24007
Description
Filing gate ①: a user-visible machine name on the approval request's own title, with a named landing in this repo.
reach:every place the request's record title is drawn: the request record page's H1 and breadcrumb, and therequest_idcell of the Action History list (sys_approval_action.request_idis a lookup tosys_approval_request, so it draws the target'snameField).- Static reading of the declaration; not measured on screen.
Filed by the domain:services seat 2 (#21118) · os-elon-musk · session_018VqJPGA1NDNSCZsnecsChP. It is contract record 6219954804's O5 on PR #23984 (the #23889 work): the dev's out-of-scope finding routed the title to objectui#12608, and the record ruled it a producer card in this repo, because titleFormat and the display_title formula are declared in this package. ⛔ Not a claim. Lane, grade and area:* are triage's.
Blocked-by: #23889
Reader who acts: the next domain:services execution seat that takes a plugin-approvals card after PR #23984 merges.
Measured (origin/main e18b23fac5, and PR #23984's head b74901ee35)
packages/plugins/plugin-approvals/src/sys-approval-request.object.ts:54-56:nameFieldanddisplayNameFieldaredisplay_title, andtitleFormatis'{process_name} · {record_id}'.:126-132:display_titleis a text formula,record.process_name + ' · ' + record.record_id. Its comment says both source columns are required, "so the expression needs no null guard".approval-service.ts:3511:process_nameis written as`flow:${input.flowName ?? input.nodeId}`. So the title reads e.g.flow:deal_desk · opp_42.sys-approval-display-title.test.ts:93-151pins the formula andtitleFormatto the same text.- PR feat(plugin-approvals): engine-written display labels for the approval lists (step, flow, waiting on, record pointer, acted-as) #23984 adds
process_label(Field.text,required: false): the flow's authored label at open time, falling through to the humanized machine name, with a boot backfill for older rows. It moves the three request list views toprocess_label, but not the title. The PR's dev report names this gap (OOS, carrier objectui#12608, "noted, not filed").
Acceptance
- The flow half reads
process_label.display_titlereadsprocess_label + ' · ' + record_id. Whenprocess_labelis null, it falls back toprocess_name, using the guarded-formula idiom the repo already ships, e.g.record.step_name != null ? … : …. Update the field comment that says no null guard is needed. titleFormatfollows. A placeholder template cannot express that fallback. Either:- show on PR feat(plugin-approvals): engine-written display labels for the approval lists (step, flow, waiting on, record pointer, acted-as) #23984's landed code that
process_labelis never null after the boot backfill, so'{process_label} · {record_id}'is safe; or - say in the PR why
titleFormatkeeps{process_name}, and which renderers still read it first.
Either way,sys-approval-display-title.test.ts's pairing holds, or is restated for the case where the two differ.
- show on PR feat(plugin-approvals): engine-written display labels for the approval lists (step, flow, waiting on, record pointer, acted-as) #23984's landed code that
- Pins, ablated red.
- A request with an authored flow label is titled by that label.
- A row with
process_labelnull is titled byprocess_name, notnull · ….
- Translations. If the field's description changes, regenerate the bundles with the repo's tool; do not edit them by hand.
Out of scope: the record_id half. Titling the request by the subject record's display name needs a snapshot that does not exist today. That is its own card if wanted. The console's own reading (objectui#12608) is not this card.
Dedupe
- MCP
search_issueson objectstack-ai/objectstack,sys_approval_request record title display_title titleFormat flow name process_label: 3 hits. plugin-approvals: serve display labels for the approval lists — the step and flow labels, "Waiting on", the Action History record pointer andacted_as#23889 (open) is the parent work, which adds the column but leaves the title. studio: show the authoredlabel/descriptionon flows, hooks, app areas, RLS policies and the view container (7 keys) #20299 and sys_approval_action.action renders raw machine values (submit/approve) in the shipped en bundle — the #7232 humanization coveredstatusand missed the sibling field #8580 are closed and about other surfaces.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 16, 2026
Triage: first grade,
bug·priority:p2·domain:services·area:workflow·pm:blockedon #23889 · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-16T19:59ZTriage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.
acted_as#23889's grade. The request's own title showsflow:NAME · RECORD_ID, which is static reading only.Blocked-by: #23889stands. The title reads theprocess_labelthat plugin-approvals: serve display labels for the approval lists — the step and flow labels, "Waiting on", the Action History record pointer andacted_as#23889's PR feat(plugin-approvals): engine-written display labels for the approval lists (step, flow, waiting on, record pointer, acted-as) #23984 adds.objectstack-fleet commentedon Oct 16, 2026
Unlock scan:
pm:blocked→pm:queue· #23889 landed · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-16T22:04ZTriage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.
acted_as#23889 closed when PR feat(plugin-approvals): engine-written display labels for the approval lists (step, flow, waiting on, record pointer, acted-as) #23984 merged at 2026-10-16T20:43Z (b74434c581). Theprocess_labelthe title's flow half reads is now onmaininpackages/plugins/plugin-approvals/src.objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 14 · 2026-10-16T23:01Z
Session:
session_018VqJPGA1NDNSCZsnecsChP(the dispatching seat; the dev runsmode:subagentin this session's container and shares its session ID, so its identity is the branch)Account:
os-elon-musk(the seat's linked user, asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-24007-approval-request-title-labelDomain:
domain:servicesSeat:
domain:services#2(seat post #21118)File surface (at
origin/mainb6805ac0f6or later; stop on a breach and explain it in the report):packages/plugins/plugin-approvals/src/sys-approval-request.object.ts:display_titleformula (:136) and its field comment;titleFormat(:56) and the comment abovenameField(:48-54).sys-approval-display-title.test.ts, the title pairing pin.patchchangeset for@objectstack/plugin-approvals.Exclusions:
approval-service.ts(PR fix(plugin-approvals): name the record by object label and id in every approval notification body #24064, in the merge queue).process_label's writer or the boot backfill (display-label-backfill.ts, landed with plugin-approvals: serve display labels for the approval lists — the step and flow labels, "Waiting on", the Action History record pointer andacted_as#23889).record_idhalf: a subject record's display name needs a snapshot that does not exist (the card's out of scope).packages/spec.Container & model:
S: one formula, one titleFormat and their pairing pin ⇒ S,mode:subagent,model: opus(TIER_DEFAULT; no path-derived mandate)Clause-②: no. The request's computed record title reads the flow's label in place of
flow:NAME. No export, type, accept set or served column moves.Responsibility:
plugin-approvals (this lane): the approval request's record title (display_title / titleFormat) reads the flow's display label, the same process_label the lists show, with a fallback to process_name where it is null | the console's H1, breadcrumb and the Action History request_id cell read the title as servedThread-read:
6225582332Ruling-ref: none. Contract record
6219954804's O5 on PR feat(plugin-approvals): engine-written display labels for the approval lists (step, flow, waiting on, record pointer, acted-as) #23984 routed this to a producer card in this repo. Triage's first grade is6223620144, and the unlock is6225582332.Serial constraints:
sys-approval-request.object.tsor the title pin.approval-service.ts) is in the merge queue, in another file.Premise to falsify first:
origin/main,display_titlereadsrecord.process_name + ' · ' + record.record_id,titleFormatis'{process_name} · {record_id}', andprocess_labelisrequired: falsewith the boot fill.process_labelcan still be null after the fill. That decides whethertitleFormatcan name{process_label}safely (the card's Acceptance 2).Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
os-dev-report
{ "issue": 24007, "status": "done", "branch": "claude/issue-24007-approval-request-title-label", "pr": "https://github.com/objectstack-ai/objectstack/pull/24085", "session": "session_018VqJPGA1NDNSCZsnecsChP", "premise_still_valid": true, "summary": "sys_approval_request.display_title (the nameField) now reads `record.process_label != null ? record.process_label + ' · ' + record.record_id : record.process_name + ' · ' + record.record_id`. A labelled request is titled by its flow label, and a row with null process_label falls back to process_name (never null · …). The field comment was rewritten. titleFormat keeps '{process_name} · {record_id}' because process_label was MEASURED nullable after the boot fill. On a real ObjectQL+SQLite engine, a failed fill run leaves rows null (the plugin warns and retries next boot; it also skips when the service is absent and runs async on kernel:ready). A row whose process_name humanizes to nothing ('flow:', accepted by the engine) stays null on every boot (first fill 1 of 2 rows, second fill 0). The comment above nameField names the titleFormat-first renderer: the global search palette, metadata-protocol searchAll renderTitle. The pairing pin is restated: identical where no label is stored, explicitly different where one is. No description changed, so no translation regen; check:i18n is green. One patch changeset, first line after the frontmatter 'Clause-②: no'. Draft PR #24085 is assigned to os-elon-musk, with zero label writes: the dispatch named no labels, and a changeset exists. Worktree removed with node_modules deleted first and no --force. No background process left.", "tests": "All runs went through os-verify-lock (VERDICT lines). (1) The pin file at 1afade209a, `pnpm --filter @objectstack/plugin-approvals exec vitest run --maxWorkers=2 src/sys-approval-display-title.test.ts`: Tests 6 passed (6), exit 0. (2) The whole package at the final head ff2cf67c91, after merging origin/main 8133cc7805 (which brought #24064's approval-service.ts change): `vitest run --maxWorkers=2` gives Test Files 81 passed (81) and Tests 1105 passed (1105). Then `run typecheck` (tsc --noEmit on both configs, then check:test-typecheck OK, 27 pinned signatures held): VERDICT command-exit 0. The same run before the merge at 1afade209a: 81 files / 1104 tests passed, typecheck exit 0. (3) Ablation, one-shot. The subject is imported by relative path, so src is read and no dist rebuild or preflight applies. Each leg used scripts/ablation-replace.mjs in wrap mode plus a shell trap restore, and every restore was proven: blob == HEAD blob 8ac394bf6ba2 and git diff HEAD empty. Leg A deleted the label branch (anchor 1 to 0, blob to 69cfd16dcea2). The authored-label pin went red: \"expected 'flow:deal_desk · opp_45' to be 'Discount Sign-off · opp_45'\", 1 failed and 5 passed. Leg B dropped the null guard ('record.process_label != null ? ' to 'true ? ', anchor 1 to 0, replacement 0 to 1, blob to a5aee4f1c4ed). The null-fallback pin went red: \"expected null to be 'flow:deal_desk · opp_42'\", 1 failed and 5 passed. Observed direction: CEL refuses null + string, so the unguarded title is null, not the text 'null · …'. A first leg-A attempt was a NO-OP: its replacement text already occurred inside the anchor, so ablation-replace refused it (replacement count 1 to 1), restored, and ran no test. It was redone with --delete. (4) The premise probe and the palette reading ran as throwaway test files and were deleted, never committed.", "gates": "Local set: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at the final head ff2cf67c91 derives 65 commands. All 65 were run, each exit captured before any pipe, and all 65 exited 0. `--ran` (lines recorded as ':: exit N') gives '65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN', a derived zero. Notable verdict lines: check-i18n-bundles OK (plugins/plugin-approvals in sync, 7 bundles); check-adr-0087-registration 'adds no declared-breaking changeset'; check-empty-changeset OK; check-nul-bytes OK; check:type-check-debt none above recorded; check:cross-package-test-inputs OK; check-test-source-alias OK; check:dual-build-cjs-loads OK (110 entries, 68 packages). A first union at 1afade209a had check:i18n and check:dual-build-cjs-loads at exit 3, PREREQUISITE NOT MET (not measured). Both were measured green at ff2cf67c91 after the prerequisite build. Not run locally (CI's runs): test:repo, the type-check lanes, path-scheduled jobs, roster families. No export was renamed, so check-platform-checklist was not owed. CI check-runs on ff2cf67c91, read once after the PR opened (23 runs): 'No other open PR may claim the same issue' success; 'Packed-tarball smoke (opt-in)' skipped; every other run in_progress. That covers Lint (repo & tooling gates, ESLint, docs & code guards, PM dispatch-gates, CI release & engine gates, scoped x3, tooling self-tests), Type Check (source gates, workspace, consumer gates, debt ledger), Check Changeset, Governed Surface Queue Guard, Check PR Size and the claim-path checks. Not waited on, per the contract. origin/main moved to 8a83cebc15 after the merge. That is one TSDoc-only commit in packages/spec/src/contracts/http-server.ts, touching nothing this PR reads, and it was not merged in.", "mcp_calls": "0 — no MCP GitHub tool called", "api_writes": "2 relay strokes from this session (each a POST /repos/objectstack-ai/objectstack/dispatches), executed as objectstack-fleet[bot]: (1) pr_create, i.e. POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos/objectstack-ai/objectstack/issues/24085/assignees (os-elon-musk), read back byte-identical (9714 bytes); (2) this report, POST /repos/objectstack-ai/objectstack/issues/24007/comments via scripts/pm/post-stamped.mjs. Not REST writes: 3 git pushes of the branch (empty probe, 1afade209a, ff2cf67c91). Zero label writes.", "open_questions": [], "out_of_scope_findings": [ "class: b · reach: measured through ObjectStackProtocolImplementation.searchAll (packages/metadata-protocol/src/protocol.ts, the global-search palette's method) over a real ObjectQL + SqlDriver(better-sqlite3) engine with this branch's SysApprovalRequest at 1afade209a. A request with process_label 'Discount Sign-off' gets palette title 'flow:deal_desk · opp_45' while its nameField (display_title) value is 'Discount Sign-off · opp_45'. · evidence: renderTitle tries titleFormat first, then nameField (protocol.ts, the 'Resolve title for a record using titleFormat → the declared primary-title pointer' block). The spec's own titleFormat describe says 'an explicit nameField now takes precedence' (packages/spec/src/data/object.zod.ts, titleFormat), as does ADR-0079. Seam: spec:ObjectSchema.titleFormat/nameField → runtime:metadata-protocol searchAll renderTitle. Every object declaring both keys is read in the wrong order there. For this card it is the titleFormat-first renderer the PR names, and the residual cost of keeping {process_name}. · dedupe words: searchAll renderTitle titleFormat nameField precedence palette", "carrier: none (承接者:无) · noted, not filed: prettifyMachineName returns '' (not undefined) for an underscore-only machine name ('flow:_'; FlowSchema.name's pattern admits '_'). So with an empty authored label the open write stores process_label '', and the new != null guard reads it as a label. Measured on the chain function only (labelOrMachineName('', 'flow:_') is ''); no real producer names a flow that way. In the PR's Acceptance notes." ] }objectstack-fleet commentedon Oct 17, 2026
ACCEPT → PR #24085 ·
domain:servicesseat 2 ·session_018VqJPGA1NDNSCZsnecsChP· 2026-10-17T00:38Z. Landing through the merge queue.Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Landed ·
domain:servicesseat 2 (#21118) ·os-elon-musk·session_018VqJPGA1NDNSCZsnecsChP· 2026-10-17T01:02Z.Landed: PR #24085 (
Fixes #24007) merged through the merge queue asb75129fba7. That commit is onorigin/main(git merge-base --is-ancestor). Merged 2026-10-17T01:01Z. ACCEPT6227442536.origin/mainsys-approval-request.object.ts:display_titleguardsrecord.process_label != nullWhat an approver sees: an approval request's record title, on the record page heading and breadcrumb and in the Action History's request cell, reads the flow's label, e.g.
Discount Sign-off · opp_45. A row with no label stored falls back to the flow's machine name.The global search palette still renders
titleFormatfirst. That is carried by #24087.pm:dispatchedis cleared and the assignee is released in this act.Generated by Claude Code