Skip to content

fix(flows): frontend residual minors — stale route, edge id collisions, cron clamp, unvalidated status - #5292

Merged
graycyrus merged 3 commits into
tinyhumansai:mainfrom
graycyrus:fix/flows-residual-minors
Jul 31, 2026
Merged

fix(flows): frontend residual minors — stale route, edge id collisions, cron clamp, unvalidated status#5292
graycyrus merged 3 commits into
tinyhumansai:mainfrom
graycyrus:fix/flows-residual-minors

Conversation

@graycyrus

@graycyrus graycyrus commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Stacked on #5289 (fix/flows-i18n-node-cron-summaries). This branch contains that PR's commit as its base, because these fixes edit cron.ts in the form #5289 introduced. Review only the second commit, and merge after #5289.

Summary

  • Four frontend residual defects found in the flows review, each small but user-visible.
  • Fixes a dead back-route that dumped users in notification settings.
  • Fixes editor-created edge ids using the exact collision-prone scheme edgeId() was written to avoid.
  • Stops the cron builder silently rewriting a user's out-of-range custom step.
  • Makes an unknown run status render a fallback instead of undefined in three more places.

Problem

F-m1WorkflowsRun.tsx's back-fallback navigated to /intelligence?tab=workflows, but AppRoutes.tsx redirects /intelligence to /settings/notifications. A cold deep-link to /workflows/run followed by Back landed the user in notification settings.

F-m6EditableFlowCanvas.tsx created edges with addEdge(connection, current), taking React Flow's default concatenated id — precisely the scheme graphAdapter.ts's edgeId() exists to avoid. That helper JSON-encodes the 4-tuple because plain concatenation collides: node "a-b" + port "c" produces the same id as node "a" + port "b-c". Editor-created edges could therefore collide and duplicate React keys until the next load regenerated ids.

F-m7parseCron accepted any numeric step (*/90 * * * *interval: 90, described as "Every 90 minutes"), but the visual editor's next patch() recompiled through buildCron's clamp (1–59 minutes / 1–23 hours) and silently changed the stored expression to */59 — without the user touching the interval field.

F-m8FlowRunStatus.tsx and its consumers indexed status Records directly with a value cast straight off the wire and never validated, so a future or unknown FlowRunStatus yielded an undefined class and an undefined i18n key. WorkflowRunsPage.tsx already defended with a fallback; the drawer, sidebar, and inspector did not.

Solution

  • F-m1 — points at the real /flows route.
  • F-m6 — adds an exported connectionEdgeId() in graphAdapter.ts that applies edgeId()'s collision-free tuple id to a live Connection, and onConnect now passes it explicitly. Editor-created ids now match adapter-created ones.
  • F-m7parseCron rejects a step outside the range buildCron clamps to, so the expression is preserved as an opaque custom cron rather than being silently rewritten later. Chosen over surfacing the clamp in the UI because it is purely local, needs no new UI or i18n surface, and never rewrites what the user actually wrote.
  • F-m8 — adds flowRunStatusLabel / flowRunStatusAccentClass / flowRunStatusDotClass mirroring the fallback pattern WorkflowRunsPage.tsx already uses, and applies them at every direct-index site. This includes FlowRunInspectorDrawer.tsx, which had the identical bug though the review had not named it.

F-m4 (file sizes) was evaluated and deliberately skipped. EditableFlowCanvas.tsx (~875 lines) and nodeConfigFields.tsx (~577) exceed the ~500-line preference, but their state is tightly coupled (undo/redo history, drag/connect callbacks) and a clean extraction would have ballooned this diff and risked behaviour regressions for a line-count win. A smaller verifiable diff was judged worth more.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • Diff coverage ≥ 80% — every fix has a behavioural test (corrected back-route, distinct ids for the colliding tuples, out-of-range cron surviving a round-trip, unknown status rendering the fallback); 852 tests passed across 80 files
  • Coverage matrix updated — N/A: bug fixes to existing UI, no feature rows added/removed/renamed
  • All affected feature IDs from the matrix are listed under ## RelatedN/A: no matrix feature rows affected
  • No new external network dependencies introduced
  • Manual smoke checklist updated if this touches release-cut surfaces — N/A: no release-cut surface affected
  • Linked issue closed via Closes #NNNN/A: found by code review, no tracking issue filed yet

Impact

  • Runtime/platform: desktop frontend only (app/src). No Rust, no Tauri.
  • Behaviour: Back from a cold-loaded run page now returns to Workflows. A hand-written cron step outside the builder's range is preserved instead of quietly rewritten. Unknown run statuses degrade gracefully.
  • i18n: no new keys — every fix reuses existing keys/patterns. pnpm i18n:check exit 0, 0 missing/extra.
  • Compatibility: connectionEdgeId is additive. Existing persisted edge ids are unchanged; only newly created editor edges get the corrected scheme.

Related


AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

Validation Run

  • pnpm --filter openhuman-app format:check — lint clean on touched files (0 errors; pre-existing warnings elsewhere untouched)
  • pnpm typecheck — passes
  • Focused tests: vitest run src/lib/flows src/components/flows src/pages852 passed, 80 files, 0 failed
  • Rust fmt/check (if changed): N/A, no Rust changed
  • Tauri fmt/check (if changed): N/A, app/src-tauri untouched

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: out-of-range cron steps are preserved as opaque expressions rather than silently clamped on a later edit; unknown run statuses render a fallback.
  • User-visible effect: Back returns to Workflows; a hand-written */90 cron is no longer rewritten to */59 behind the user's back.

Parity Contract

  • Legacy behavior preserved: in-range cron parsing/building is unchanged, as is every known run status's label and styling; existing edge ids are untouched.
  • Guard/fallback/dispatch parity checks: the new status helpers mirror WorkflowRunsPage.tsx's existing fallback rather than introducing a second pattern.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none
  • Canonical PR: this one
  • Resolution: N/A

Summary by CodeRabbit

  • Bug Fixes

    • Unknown flow-run statuses now display readable labels with neutral styling instead of undefined.
    • Cron schedules reject invalid minute, hour, weekday, and step values without silently changing user input.
    • Newly created workflow connections receive stable, collision-resistant identifiers.
    • Workflow run back navigation now correctly returns to the flows page when no history is available.
  • Tests

    • Added coverage for unknown statuses, invalid cron expressions, connection identifiers, schedule fields, and navigation behavior.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds safe fallbacks for unknown flow-run statuses, stricter cron validation, stable React Flow connection IDs, and /flows back-navigation fallback behavior. Tests cover each change.

Changes

Flow-run status fallbacks

Layer / File(s) Summary
Safe status helpers and rendering
app/src/components/flows/FlowRunStatus.tsx, app/src/components/flows/FlowRunInspectorDrawer.tsx, app/src/components/flows/FlowRunsDrawer.tsx, app/src/components/flows/FlowRunsSidebar.tsx
Status labels now humanize unknown values. Status dot and accent classes now use neutral fallbacks.
Unknown-status regression coverage
app/src/components/flows/*test.tsx, app/src/components/flows/__tests__/FlowRunInspectorDrawer.test.tsx
Tests verify labels render without undefined text or CSS classes.

Cron validation

Layer / File(s) Summary
Bounded cron parsing and UI handling
app/src/lib/flows/cron.ts, app/src/components/flows/canvas/nodeConfig/__tests__/ScheduleField.test.tsx
Cron parsing rejects unsupported ranges. Out-of-range expressions remain in the advanced field without mount-time rewriting.
Cron regression coverage
app/src/lib/flows/cron.test.ts
Tests cover invalid ranges, valid boundaries, round trips, opaque expressions, and weekday labels.

Stable connection IDs

Layer / File(s) Summary
Connection ID generation and canvas integration
app/src/lib/flows/graphAdapter.ts, app/src/components/flows/canvas/EditableFlowCanvas.tsx
New connections use normalized, collision-resistant IDs based on the existing edge ID format.
Connection ID tests
app/src/lib/flows/graphAdapter.test.ts
Tests cover equivalent tuples, hyphenated identifiers, and default main handles.

Run-page navigation

Layer / File(s) Summary
Back-navigation fallback
app/src/pages/WorkflowsRun.tsx, app/src/pages/WorkflowsRun.test.tsx
Cold deep links now navigate to /flows; existing in-app history still uses navigate(-1). Tests verify both paths.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested labels: bug

Suggested reviewers: senamakel

Poem

A rabbit checks each status sign,
Unknown words now render fine.
Cron bounds hold, edges keep their names,
Back buttons choose the proper lanes.
Hop, hop—clean flows align! 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the four frontend defects addressed: stale routing, edge ID collisions, cron clamping, and unvalidated statuses.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@graycyrus
graycyrus force-pushed the fix/flows-residual-minors branch 2 times, most recently from 80a8f65 to 2e94aaa Compare July 31, 2026 06:23
@graycyrus
graycyrus marked this pull request as ready for review July 31, 2026 09:59
@graycyrus
graycyrus requested a review from a team July 31, 2026 09:59

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

graycyrus has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2e94aaa04f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread app/src/lib/flows/cron.ts Outdated
// Every N hours: `M */N * * dow`
const hourStep = parseStep(hour);
if (hourStep && /^\d+$/.test(min)) {
if (hourStep && /^\d+$/.test(min) && hourStep.step >= 1 && hourStep.step <= 23) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject out-of-range hourly cron minutes

In the hourly cron path, a custom expression like 90 */2 * * * still parses as a visual hours spec with minute: 90; the next unrelated ScheduleField edit then recompiles through buildCron() and silently rewrites it to 59 */2 * * *. This leaves the same data-loss behavior this patch is trying to avoid for step values, so this branch should also require the minute field to be within 0..59 before returning a CronSpec.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — thanks for catching this. The same data-loss hole existed in three more places than the hourly minute field you flagged:

  • Hourly minute (the one you named): 90 */2 * * * now returns null instead of parsing to minute: 90 and getting silently rewritten to 59 */2 * * * on the next unrelated edit.
  • Daily minute and hour: same shape — buildCron clamps both (0-59 / 0-23) but parseCron was not validating either before this fix. 75 9 * * * and 30 25 * * * now both return null.
  • Weekday list: normalizeWeekdays silently drops any day outside 0-6 (after mapping 7→Sun), so a mixed list like 1,8 used to parse to just [1] — same "narrow and move on" bug for the dow field. parseCron now rejects the whole field (→ null) if any entry is outside cron's valid 0-7 range.

Added a shared parseBoundedInt(field, max) helper used by both the hourly and daily branches so the range check lives in one place.

Verified the caller degrades correctly: ScheduleField seeds advanced from parsed === null, so a rejected expression still renders in the raw/editable cron text field with a "custom schedule" summary from describeCron — confirmed by reading ScheduleField.tsx (no test needed there since the wiring was already covered by F-m7's existing test).

Tests added in cron.test.ts (app/src/lib/flows/cron.ts:131, app/src/lib/flows/cron.test.ts):

  • out-of-range hourly minute (90 */2 * * *, 60 */2 * * *)
  • out-of-range daily minute/hour, individually and together
  • out-of-range weekday, both standalone (8) and mixed into a valid list (1,8)
  • boundary values (0/23/59/7) still accepted
  • a battery of in-range hourly/daily/weekly crons proven unaffected (round-trip unchanged)

Verified real counts: tsc --noEmit clean, vitest run on src/lib/flows src/components/flows src/pages — 81 files / 867 tests passed (cron.test.ts alone: 35 tests), prettier --check . clean. Amended into the existing top commit and pushed.

describeNode/describeCron/describeEveryMs/describeSchedule returned
hardcoded English literals rendered on every canvas node card and in
the schedule field, violating the project's useT() rule. Thread `t`
(and `locale`, for weekday names) through these pure modules mirroring
the existing runStepSummary.ts pattern, add flows.nodeSummary.*/
flows.cron.* keys to en.ts plus real translations across all 14
locales, and derive weekday names from Intl.DateTimeFormat against the
active locale instead of hand-translated WEEKDAY_SHORT/WEEKDAY_INITIAL
arrays.

Allowlist two pure-placeholder strings (HTTP method+URL, quoted
prompt+model) in the i18n-find-english leftover-English checker, same
as existing entries like channels.activeRouteValue.
@graycyrus
graycyrus force-pushed the fix/flows-residual-minors branch from 2e94aaa to f7485fa Compare July 31, 2026 10:41

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

graycyrus has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

…s, cron clamp, unvalidated status (F-m1/F-m6/F-m7/F-m8)

- F-m1: WorkflowsRun's cold-deep-link back-fallback pointed at
  /intelligence?tab=workflows, which redirects to /settings/notifications.
  Point it at the real workflows route (/flows).
- F-m6: EditableFlowCanvas's onConnect used React Flow's default
  concatenated edge id, the exact collision-prone scheme graphAdapter's
  edgeId() exists to avoid. Add connectionEdgeId() and use it so
  editor-created edges match adapter-created ones.
- F-m7: parseCron accepted any numeric cron step (e.g. */90), which the
  visual editor's next unrelated patch() would silently recompile through
  buildCron's clamp, rewriting the stored expression to */59. Reject
  out-of-range steps so they round-trip untouched through the advanced
  cron field instead.
- F-m8: FlowRunStatus/FlowRunsDrawer/FlowRunsSidebar/FlowRunInspectorDrawer
  indexed status maps with an unvalidated wire status, rendering
  "undefined" for an unrecognized FlowRunStatus. Add
  flowRunStatusLabel/AccentClass/DotClass with the same fallback pattern
  WorkflowRunsPage.tsx already used, applied consistently everywhere the
  status maps are read.

F-m4 (file-size rule) evaluated and deliberately skipped — see PR/task notes.
@graycyrus
graycyrus force-pushed the fix/flows-residual-minors branch from f7485fa to 3c95c94 Compare July 31, 2026 10:50

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

graycyrus has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

…minors

# Conflicts:
#	app/src/lib/i18n/es.ts
#	app/src/lib/i18n/pt.ts

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

graycyrus has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai coderabbitai Bot added the bug label Jul 31, 2026
@graycyrus
graycyrus merged commit 54ca06d into tinyhumansai:main Jul 31, 2026
23 of 28 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in Team Openhuman Jul 31, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
app/src/components/flows/FlowRunStatus.test.tsx (1)

124-129: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the mapped translation key in the recognized-status test.

The mock t returns the fallback for every key. Therefore, Line 128 passes even if flowRunStatusLabel skips FLOW_RUN_STATUS_KEY or uses the wrong key. Use a spy and assert that t receives flowRuns.status.completed, then assert the localized result.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/components/flows/FlowRunStatus.test.tsx` around lines 124 - 129,
Update the recognized-status case in flowRunStatusLabel so the translation mock
spies on its invocation and verifies it receives the key
flowRuns.status.completed with the expected fallback, then assert the returned
localized value. Keep the unknown-status fallback assertion unchanged.
app/src/pages/WorkflowsRun.test.tsx (1)

12-17: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Create the shared mock with vi.hoisted.

Vitest hoists vi.mock before imports and documents vi.hoisted for values referenced by mock factories. The current factory captures navigateMock in a returned function, so it does not show a confirmed immediate TDZ failure. Move the mock creation to vi.hoisted to avoid relying on deferred evaluation. (vitest.dev)

Suggested fix
-const navigateMock = vi.fn();
+const navigateMock = vi.hoisted(() => vi.fn());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/pages/WorkflowsRun.test.tsx` around lines 12 - 17, Move the
navigateMock creation in WorkflowsRun.test.tsx into a vi.hoisted callback, then
have the react-router-dom vi.mock factory reference that hoisted mock while
preserving the existing useNavigate behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/components/flows/FlowRunStatus.tsx`:
- Around line 48-54: Update flowRunStatusAccentClass, flowRunStatusDotClass, and
the corresponding third status-map lookup to verify each status is an own key
before returning its mapped value, otherwise use the neutral fallback. Add
regression tests covering toString, constructor, and __proto__ for all three
lookup paths.

In `@app/src/lib/flows/cron.test.ts`:
- Around line 212-214: Update the test for weekdayShortLabel to assert the exact
English fallback label for the unsupported locale, not only that the call does
not throw. Keep the existing unsupported locale input and weekday index, and
verify the returned value matches the expected English weekday abbreviation.

In `@app/src/lib/flows/graphAdapter.test.ts`:
- Around line 434-448: The connectionEdgeId normalization test currently covers
only explicit null handles and omits endpoint cases. Update the test around
connectionEdgeId to add a case with sourceHandle and targetHandle omitted, plus
cases with null source or target endpoints, and assert each normalizes
consistently with the corresponding explicit main/default representation.

In `@app/src/pages/WorkflowsRun.test.tsx`:
- Around line 43-67: Update the two back-navigation tests around the cold-link
and in-app history cases to explicitly set window.history.state via
History.replaceState rather than relying on ambient state or redefining the
property. Save the prior history state before the history-entry test and restore
it in its finally block, while ensuring the cold-link test uses an explicit
no-entry state and both tests retain their existing navigation assertions.

In `@app/src/pages/WorkflowsRun.tsx`:
- Line 41: Update the fallback navigation in WorkflowsRun to call
navigate('/flows', { replace: true }) when window.history.state?.idx is missing
or zero, while preserving navigate(-1) for positive indices. Update WorkflowsRun
tests to assert the replace options object for the fallback.

---

Nitpick comments:
In `@app/src/components/flows/FlowRunStatus.test.tsx`:
- Around line 124-129: Update the recognized-status case in flowRunStatusLabel
so the translation mock spies on its invocation and verifies it receives the key
flowRuns.status.completed with the expected fallback, then assert the returned
localized value. Keep the unknown-status fallback assertion unchanged.

In `@app/src/pages/WorkflowsRun.test.tsx`:
- Around line 12-17: Move the navigateMock creation in WorkflowsRun.test.tsx
into a vi.hoisted callback, then have the react-router-dom vi.mock factory
reference that hoisted mock while preserving the existing useNavigate behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d8826104-d2d1-4eea-926f-5449efafa9ad

📥 Commits

Reviewing files that changed from the base of the PR and between fe08b05 and 3b685dd.

📒 Files selected for processing (16)
  • app/src/components/flows/FlowRunInspectorDrawer.tsx
  • app/src/components/flows/FlowRunStatus.test.tsx
  • app/src/components/flows/FlowRunStatus.tsx
  • app/src/components/flows/FlowRunsDrawer.test.tsx
  • app/src/components/flows/FlowRunsDrawer.tsx
  • app/src/components/flows/FlowRunsSidebar.test.tsx
  • app/src/components/flows/FlowRunsSidebar.tsx
  • app/src/components/flows/__tests__/FlowRunInspectorDrawer.test.tsx
  • app/src/components/flows/canvas/EditableFlowCanvas.tsx
  • app/src/components/flows/canvas/nodeConfig/__tests__/ScheduleField.test.tsx
  • app/src/lib/flows/cron.test.ts
  • app/src/lib/flows/cron.ts
  • app/src/lib/flows/graphAdapter.test.ts
  • app/src/lib/flows/graphAdapter.ts
  • app/src/pages/WorkflowsRun.test.tsx
  • app/src/pages/WorkflowsRun.tsx

Comment on lines +48 to +54
export function flowRunStatusAccentClass(status: FlowRunStatusValue): string {
return FLOW_RUN_STATUS_ACCENT[status] ?? UNKNOWN_STATUS_ACCENT;
}

/** Runtime-safe dot class lookup — falls back for an unrecognized status. */
export function flowRunStatusDotClass(status: FlowRunStatusValue): string {
return FLOW_RUN_STATUS_DOT[status] ?? UNKNOWN_STATUS_DOT;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
rg -n -C 8 'FLOW_RUN_STATUS_(ACCENT|DOT|KEY)' app/src/components/flows/FlowRunStatus.tsx

Repository: tinyhumansai/openhuman

Length of output: 3768


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- FlowRunStatus.tsx ---'
cat -n app/src/components/flows/FlowRunStatus.tsx | sed -n '1,130p'

printf '%s\n' '--- FlowRunStatus and status type references ---'
rg -n -C 5 'type FlowRunStatus|interface FlowRunStatus|FlowRunStatusValue|flowRunStatus(Label|AccentClass|DotClass)' app/src

printf '%s\n' '--- prototype-key behavior of the current lookup shape ---'
node - <<'JS'
const accent = {
  running: 'running-class',
  completed: 'completed-class',
};
const dot = {
  running: 'running-dot',
  completed: 'completed-dot',
};
const key = {
  running: 'flowRuns.status.running',
  completed: 'flowRuns.status.completed',
};

for (const status of ['toString', 'constructor', '__proto__', 'missing']) {
  const accentValue = accent[status] ?? 'unknown-accent';
  const dotValue = dot[status] ?? 'unknown-dot';
  const labelKey = key[status];
  const label = labelKey ? labelKey : status.replace(/_/g, ' ');
  console.log(JSON.stringify({
    status,
    accentType: typeof accentValue,
    accentValue: String(accentValue),
    dotType: typeof dotValue,
    dotValue: String(dotValue),
    labelKeyType: typeof labelKey,
    label,
  }));
}
JS

Repository: tinyhumansai/openhuman

Length of output: 31144


Guard status-map lookups against inherited keys.

Statuses such as toString, constructor, and __proto__ return inherited values from all three maps instead of the neutral fallback. Use own-property checks or null-prototype maps, and add regression tests for these statuses.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/components/flows/FlowRunStatus.tsx` around lines 48 - 54, Update
flowRunStatusAccentClass, flowRunStatusDotClass, and the corresponding third
status-map lookup to verify each status is an own key before returning its
mapped value, otherwise use the neutral fallback. Add regression tests covering
toString, constructor, and __proto__ for all three lookup paths.

Comment on lines +212 to +214
it('falls back to English for an unsupported locale tag rather than throwing', () => {
expect(() => weekdayShortLabel(0, 'not-a-real-locale')).not.toThrow();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the fallback result.

This test only checks that weekdayShortLabel does not throw. It passes if the function returns a non-English default locale instead of the required English fallback. Assert the returned label.

Proposed fix
-    expect(() => weekdayShortLabel(0, 'not-a-real-locale')).not.toThrow();
+    expect(weekdayShortLabel(0, 'not-a-real-locale')).toBe('Sun');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('falls back to English for an unsupported locale tag rather than throwing', () => {
expect(() => weekdayShortLabel(0, 'not-a-real-locale')).not.toThrow();
});
it('falls back to English for an unsupported locale tag rather than throwing', () => {
expect(weekdayShortLabel(0, 'not-a-real-locale')).toBe('Sun');
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/lib/flows/cron.test.ts` around lines 212 - 214, Update the test for
weekdayShortLabel to assert the exact English fallback label for the unsupported
locale, not only that the call does not throw. Keep the existing unsupported
locale input and weekday index, and verify the returned value matches the
expected English weekday abbreviation.

Comment on lines +434 to +448
it('defaults null/undefined handles to the "main" port, matching isValidFlowConnection', () => {
const withNullHandles = connectionEdgeId({
source: 'a',
sourceHandle: null,
target: 'b',
targetHandle: null,
});
const withExplicitMain = connectionEdgeId({
source: 'a',
sourceHandle: 'main',
target: 'b',
targetHandle: 'main',
});
expect(withNullHandles).toBe(withExplicitMain);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Test omitted handles and endpoints.

Line 434 states that the test covers undefined handles, but both handles are null. The suite also does not test the new missing-endpoint normalization. Add cases with omitted handle fields and null endpoints.

Proposed test additions
       const withExplicitMain = connectionEdgeId({
         source: 'a',
         sourceHandle: 'main',
         target: 'b',
         targetHandle: 'main',
       });
       expect(withNullHandles).toBe(withExplicitMain);
+
+      const withOmittedHandles = connectionEdgeId({ source: 'a', target: 'b' });
+      expect(withOmittedHandles).toBe(withExplicitMain);
+
+      expect(
+        connectionEdgeId({
+          source: null,
+          sourceHandle: null,
+          target: null,
+          targetHandle: null,
+        })
+      ).toBe(edgeId({ from_node: '', from_port: 'main', to_node: '', to_port: 'main' }));

As per coding guidelines, “Untested code is incomplete; add tests for new or changed behavior.”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('defaults null/undefined handles to the "main" port, matching isValidFlowConnection', () => {
const withNullHandles = connectionEdgeId({
source: 'a',
sourceHandle: null,
target: 'b',
targetHandle: null,
});
const withExplicitMain = connectionEdgeId({
source: 'a',
sourceHandle: 'main',
target: 'b',
targetHandle: 'main',
});
expect(withNullHandles).toBe(withExplicitMain);
});
it('defaults null/undefined handles to the "main" port, matching isValidFlowConnection', () => {
const withNullHandles = connectionEdgeId({
source: 'a',
sourceHandle: null,
target: 'b',
targetHandle: null,
});
const withExplicitMain = connectionEdgeId({
source: 'a',
sourceHandle: 'main',
target: 'b',
targetHandle: 'main',
});
expect(withNullHandles).toBe(withExplicitMain);
const withOmittedHandles = connectionEdgeId({ source: 'a', target: 'b' });
expect(withOmittedHandles).toBe(withExplicitMain);
expect(
connectionEdgeId({
source: null,
sourceHandle: null,
target: null,
targetHandle: null,
})
).toBe(edgeId({ from_node: '', from_port: 'main', to_node: '', to_port: 'main' }));
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/lib/flows/graphAdapter.test.ts` around lines 434 - 448, The
connectionEdgeId normalization test currently covers only explicit null handles
and omits endpoint cases. Update the test around connectionEdgeId to add a case
with sourceHandle and targetHandle omitted, plus cases with null source or
target endpoints, and assert each normalizes consistently with the corresponding
explicit main/default representation.

Source: Coding guidelines

Comment on lines +43 to +67
it('falls back to /flows (not the dead /intelligence?tab=workflows route) on a cold deep-link with no history', () => {
// No `window.history.state.idx` — matches a fresh deep-link with no
// in-app history entry to go back to (F-m1: /intelligence redirects to
// /settings/notifications, so the runner must not target it).
navigateMock.mockClear();
render_();
fireEvent.click(screen.getByRole('button', { name: 'common.back' }));
expect(navigateMock).toHaveBeenCalledWith('/flows');
expect(navigateMock).not.toHaveBeenCalledWith(expect.stringContaining('/intelligence'));
});

it('goes back in history instead when an in-app history entry exists', () => {
navigateMock.mockClear();
const originalDescriptor = Object.getOwnPropertyDescriptor(window.history, 'state');
Object.defineProperty(window.history, 'state', { configurable: true, value: { idx: 1 } });
try {
render_();
fireEvent.click(screen.getByRole('button', { name: 'common.back' }));
expect(navigateMock).toHaveBeenCalledWith(-1);
} finally {
if (originalDescriptor) {
Object.defineProperty(window.history, 'state', originalDescriptor);
}
}
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
file="app/src/pages/WorkflowsRun.test.tsx"
printf '%s\n' "== file outline =="
ast-grep outline "$file" --lang tsx
printf '%s\n' "== relevant source =="
sed -n '1,130p' "$file"
printf '%s\n' "== history.state references in the test file =="
rg -n -C 3 'history\.state|replaceState|defineProperty|navigateMock|render_' "$file"
printf '%s\n' "== neighboring test setup and history references =="
rg -n -C 3 'history\.state|replaceState|defineProperty' app/src --glob '*.{test,spec}.{ts,tsx}' | head -250

Repository: tinyhumansai/openhuman

Length of output: 27939


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' "== WorkflowsRun implementation =="
ast-grep outline app/src/pages/WorkflowsRun.tsx --lang tsx
sed -n '1,180p' app/src/pages/WorkflowsRun.tsx
printf '%s\n' "== Vitest configuration and setup files =="
fd -i 'vitest|vite|setup' . -t f | head -100
printf '%s\n' "== test environment declarations =="
rg -n -C 3 'environment|jsdom|happy-dom|setupFiles|history' --glob 'vitest*.{ts,js,mjs,cjs}' --glob 'vite*.{ts,js,mjs,cjs}' --glob '*setup*.{ts,tsx,js,ts}' . | head -250
printf '%s\n' "== dependency metadata =="
rg -n -C 2 '"(jsdom|happy-dom|vitest|react-router-dom)"' package.json package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null | head -200 || true
printf '%s\n' "== ECMAScript property-restoration probe =="
node - <<'JS'
const prototype = {};
Object.defineProperty(prototype, 'state', {
  configurable: true,
  get() { return this._state; },
});
const history = Object.create(prototype);
const originalDescriptor = Object.getOwnPropertyDescriptor(history, 'state');
Object.defineProperty(history, 'state', { configurable: true, value: { idx: 1 } });
if (originalDescriptor) {
  Object.defineProperty(history, 'state', originalDescriptor);
}
console.log(JSON.stringify({
  originalDescriptorWasUndefined: originalDescriptor === undefined,
  ownDescriptorRemains: Object.prototype.hasOwnProperty.call(history, 'state'),
  state: history.state,
}));
JS

Repository: tinyhumansai/openhuman

Length of output: 8528


Set and restore window.history.state explicitly.

The cold-link test depends on ambient history state. The history-entry test can leave an own state property when no own descriptor existed. Use History.replaceState to set explicit states and restore the previous state in finally.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/pages/WorkflowsRun.test.tsx` around lines 43 - 67, Update the two
back-navigation tests around the cold-link and in-app history cases to
explicitly set window.history.state via History.replaceState rather than relying
on ambient state or redefining the property. Save the prior history state before
the history-entry test and restore it in its finally block, while ensuring the
cold-link test uses an explicit no-entry state and both tests retain their
existing navigation assertions.

(window.history.state?.idx ?? 0) > 0
? navigate(-1)
: navigate('/intelligence?tab=workflows')
(window.history.state?.idx ?? 0) > 0 ? navigate(-1) : navigate('/flows')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- target file outline ---'
ast-grep outline app/src/pages/WorkflowsRun.tsx --view expanded
printf '%s\n' '--- target implementation ---'
cat -n app/src/pages/WorkflowsRun.tsx | sed -n '1,100p'
printf '%s\n' '--- related tests ---'
fd -i 'WorkflowsRun' app/src | xargs -r -n1 sh -c 'echo "--- $0"; cat -n "$0" | sed -n "1,240p"'
printf '%s\n' '--- route and navigation references ---'
rg -n --glob '*.{ts,tsx}' "WorkflowsRun|/flows|navigate\\(-1\\)|history\\.state|replace: true" app/src

Repository: tinyhumansai/openhuman

Length of output: 47426


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- router dependency versions ---'
python3 - <<'PY'
import json
from pathlib import Path
for name in ('package.json', 'app/package.json'):
    p = Path(name)
    if p.exists():
        data = json.loads(p.read_text())
        deps = {**data.get('dependencies', {}), **data.get('devDependencies', {})}
        print(name, 'react-router-dom=', deps.get('react-router-dom'))
PY
printf '%s\n' '--- navigation guard implementation ---'
cat -n app/src/components/settings/hooks/useSettingsNavigation.ts | sed -n '120,190p'
printf '%s\n' '--- deterministic history model ---'
python3 - <<'PY'
def navigate(history, index, destination, replace=False):
    if replace:
        history[index] = destination
    else:
        history = history[:index + 1] + [destination] + history[index + 1:]
        index += 1
    return history, index

initial = ['external-referrer', '/flows/run?workflow=w1']
push_history, push_index = navigate(initial[:], 1, '/flows')
replace_history, replace_index = navigate(initial[:], 1, '/flows', replace=True)
print('push:', push_history, 'index=', push_index, 'back=', push_history[push_index - 1])
print('replace:', replace_history, 'index=', replace_index, 'back=', replace_history[replace_index - 1])
assert push_history[push_index - 1] == '/flows/run?workflow=w1'
assert replace_history[replace_index - 1] == 'external-referrer'
PY
printf '%s\n' '--- current fallback and test assertions ---'
rg -n -C 3 "navigate\\('/flows'\\)|toHaveBeenCalledWith\\('/flows'\\)" app/src/pages/WorkflowsRun.tsx app/src/pages/WorkflowsRun.test.tsx

Repository: tinyhumansai/openhuman

Length of output: 4825


Replace the cold-link entry instead of pushing a new entry.

When idx is missing or 0, call navigate('/flows', { replace: true }) to prevent Back from returning to the runner. Update app/src/pages/WorkflowsRun.test.tsx to assert the options object.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/pages/WorkflowsRun.tsx` at line 41, Update the fallback navigation in
WorkflowsRun to call navigate('/flows', { replace: true }) when
window.history.state?.idx is missing or zero, while preserving navigate(-1) for
positive indices. Update WorkflowsRun tests to assert the replace options object
for the fallback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

1 participant