Filed by the repo:hotcrm execution seat (session session_019hUuCQStzXGMFSX4dzww5t). ⛔ Observation-level, unassigned, no domain:* / type — routing and grading belong to central triage. Filed here rather than pinned locally, per the hotcrm rulebook's Scope rule 3: "Lint, validation, gates and diagnostics belong to the platform, uniformly. A drift-class or validation-class gap you find is a platform problem and goes upstream."
Surfaced by hotcrm#1558 / PR #1571, which fixed the instance. This card is the mechanism that allowed it.
The gap
FlowSchema.safeParse() accepts a flow whose edges[] contains two edges carrying the same id.
Probed directly against @objectstack/spec 17.2.0 with a minimal two-edge flow, both edges id: 'dup':
success: true
parsed edge ids: ['dup', 'dup']
⭐ With a control, so the pass is not vacuous: the same schema instance rejects a flow whose edge carries an invalid type. ⇒ the schema was live and validating at the moment it accepted the duplicate — this is a missing rule, not a disabled validator.
Why it matters beyond tidiness
⚠️ It let a real duplicate ship and survive two releases of green CI. hotcrm's src/flows/lead-conversion.flow.ts declared id: 'e20' on two different edges. Nothing caught it — not pnpm validate, not the 3,300-test suite, not review — because nothing checks the property.
The instance was inert in that app only by luck of implementation: traversal filters out-edges by source and never by id. ⇒ the collision is invisible until something keys on edge ids, and then it is silently wrong rather than loudly broken. Candidates: a BPMN export, a flow designer, a flow-diff, or any future traversal that dedupes by id.
⚠️ And the id space is authored by hand. hotcrm's own file carries a comment explaining that surviving edges keep the ids they have always had — a convention that only means anything if ids are unique — and the duplicate arose from an edge reorder that reassigned one. The next author picking a "free" id from the sequence has no way to know it is taken.
Suggested shape
A uniqueness refinement on edges[] in the flow schema, so this fails at parse time for every ObjectStack app rather than per-app. ⛔ The seat is not proposing an implementation.
⚠️ Worth deciding deliberately: whether this is an error or a warning. An error is correct on the merits but would reject flows that parse today, so it wants a release note at minimum — hotcrm had one such flow and may not be alone.
Reproduction
Minimal, against @objectstack/spec 17.2.0: build a flow with two edges differing only in source/target, both id: 'dup', and call FlowSchema.safeParse(). It returns success: true. Swap one edge's type for an invalid value and the same call returns success: false — that is the control.
Real instance and its fix: objectstack-ai/hotcrm#1558, PR objectstack-ai/hotcrm#1571.
⚠️ One adjacent measurement, in case it shares a cause
The hotcrm dev's sweep across that app's flows found 168 edges and exactly one flow with a duplicate id — so this is a latent trap rather than a widespread one, at least in that app. ⇒ if the refinement lands as an error, the blast radius may be small; ⛔ but one app is not a survey, and the seat has not measured any other app.
Filed by the
repo:hotcrmexecution seat (sessionsession_019hUuCQStzXGMFSX4dzww5t). ⛔ Observation-level, unassigned, nodomain:*/type— routing and grading belong to central triage. Filed here rather than pinned locally, per the hotcrm rulebook's Scope rule 3: "Lint, validation, gates and diagnostics belong to the platform, uniformly. A drift-class or validation-class gap you find is a platform problem and goes upstream."Surfaced by hotcrm#1558 / PR #1571, which fixed the instance. This card is the mechanism that allowed it.
The gap
FlowSchema.safeParse()accepts a flow whoseedges[]contains two edges carrying the sameid.Probed directly against
@objectstack/spec17.2.0 with a minimal two-edge flow, both edgesid: 'dup':⭐ With a control, so the pass is not vacuous: the same schema instance rejects a flow whose edge carries an invalid
type. ⇒ the schema was live and validating at the moment it accepted the duplicate — this is a missing rule, not a disabled validator.Why it matters beyond tidiness
hotcrm'ssrc/flows/lead-conversion.flow.tsdeclaredid: 'e20'on two different edges. Nothing caught it — notpnpm validate, not the 3,300-test suite, not review — because nothing checks the property.The instance was inert in that app only by luck of implementation: traversal filters out-edges by
sourceand never byid. ⇒ the collision is invisible until something keys on edge ids, and then it is silently wrong rather than loudly broken. Candidates: a BPMN export, a flow designer, a flow-diff, or any future traversal that dedupes by id.Suggested shape
A uniqueness refinement on
edges[]in the flow schema, so this fails at parse time for every ObjectStack app rather than per-app. ⛔ The seat is not proposing an implementation.erroror awarning. An error is correct on the merits but would reject flows that parse today, so it wants a release note at minimum — hotcrm had one such flow and may not be alone.Reproduction
Minimal, against
@objectstack/spec17.2.0: build a flow with two edges differing only insource/target, bothid: 'dup', and callFlowSchema.safeParse(). It returnssuccess: true. Swap one edge'stypefor an invalid value and the same call returnssuccess: false— that is the control.Real instance and its fix: objectstack-ai/hotcrm#1558, PR objectstack-ai/hotcrm#1571.
The hotcrm dev's sweep across that app's flows found 168 edges and exactly one flow with a duplicate id — so this is a latent trap rather than a widespread one, at least in that app. ⇒ if the refinement lands as an error, the blast radius may be small; ⛔ but one app is not a survey, and the seat has not measured any other app.