Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/you-might-not-need-a-comment/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ This codebase's convention: **TSDoc for documentation; an inline `//` only for a
- A `//` comment that explains a **non-obvious why**: a workaround for an upstream bug, an ordering constraint, a perf reason, a spec/edge-case the code can't self-document (`// first-match wins — matches the old find() semantics`).
- Existing TSDoc `/** ... */` blocks on declarations — leave them (only tighten if verbose).
- `// boundary-raw-fetch:`, `// double-cast-allowed:`, `// boundary-raw-json:`, `// untyped-response:`, `-- migration-safe:` (SQL migrations), `// rq-lint-allow:`, `// client-boundary-allow:` and any other `<kebab-tag>: <reason>` annotation a script under `scripts/` greps for, in line-comment or block-comment form (e.g. the `/** svg-path-precision-exception: ... */` directive on icon paths) — these are load-bearing, never touch them.
- `// biome-ignore`, `// eslint-disable`, `// @ts-expect-error` and other tooling directives.
- `// biome-ignore`, `// @ts-expect-error` and other tooling directives. (Nothing runs ESLint, so `eslint-disable`/`eslint-enable` directives are dead; delete them — `check:comment-hygiene` rejects both.)
- `// TODO` / `// FIXME` that point at real, still-open work.

## Bias
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This file (also `AGENTS.md`) holds the repo-wide rules. Area detail lives in `.c

- **Package manager**: `bun` and `bunx`, never `npm` and `npx`.
- **Logging**: `createLogger` from `@sim/logger`; `logger.info` / `logger.warn` / `logger.error`, never `console.log`. Inside `withRouteHandler` the logger already carries the request ID — no manual `withMetadata({ requestId })`.
- **Comments**: name things so the code explains itself. TSDoc documents exported APIs and non-obvious modules. An inline `//` is only for a terse, non-obvious *why*, or for a script-enforced `// <tag>: <reason>` annotation (`boundary-raw-fetch`, `double-cast-allowed`, `boundary-raw-json`, `untyped-response`, `rq-lint-allow`, `client-boundary-allow`, `utils-lint-allow`, …). History belongs in the commit message. No `====` separators or commented-out code (`check:comment-hygiene` enforces this). The `/you-might-not-need-a-comment` skill applies this to a diff.
- **Comments**: name things so the code explains itself. TSDoc documents exported APIs and non-obvious modules. An inline `//` is only for a terse, non-obvious *why*, or for a script-enforced `// <tag>: <reason>` annotation (`boundary-raw-fetch`, `double-cast-allowed`, `boundary-raw-json`, `untyped-response`, `rq-lint-allow`, `client-boundary-allow`, `utils-lint-allow`, …). History belongs in the commit message. No `====` separators, commented-out code, or ESLint directives (`eslint-disable`/`eslint-enable`; nothing runs ESLint; `check:comment-hygiene` enforces all three). The `/you-might-not-need-a-comment` skill applies this to a diff.
- **ID generation**: `generateId()` (UUID v4, the default) or `generateShortId(size?)` (URL-safe, 21 chars by default) from `@sim/utils/id` — never `crypto.randomUUID()`, `nanoid`, or `uuid`. Both use `crypto.getRandomValues()`, so they also work in non-secure (HTTP) browsers. For other randomness, `@sim/utils/random` (`randomInt`, `randomFloat`, `randomItem`, `generateRandomBytes`, `generateRandomHex`) — never `Math.random()` or `crypto.randomBytes()`.
- **Common utilities**: use the shared helpers from the `@sim/utils/<module>` subpaths instead of inline implementations (`check:utils` bans most of the inline forms below):
- `sleep(ms)` from `@sim/utils/helpers` — never `new Promise(resolve => setTimeout(resolve, ms))`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -548,137 +548,133 @@ export default function ResumeExecutionPage({
}
}, [refetchExecutionDetail, selectedContextId])

const handleResume = useCallback(
async () => {
if (!selectedContextId || !selectedDetail) {
setError('No pause point is selected. Refresh and try again.')
return
}
setLoadingAction(true)
setError(null)
setMessage(null)
let resumePayload: any
try {
if (isHumanMode && hasInputFormat) {
const errors: Record<string, string> = {}
const submission: Record<string, any> = {}
for (const field of inputFormatFields) {
const rawValue = formValues[field.name] ?? ''
const hasValue =
field.type === 'boolean'
? rawValue === 'true' || rawValue === 'false'
: rawValue.trim().length > 0 && rawValue !== '__unset__'
if (!hasValue || rawValue === '__unset__') {
if (field.required) errors[field.name] = 'This field is required.'
continue
}
const { value, error: parseError } = parseFormValue(field, rawValue)
if (parseError) {
errors[field.name] = parseError
continue
}
if (value !== undefined) submission[field.name] = value
const handleResume = useCallback(async () => {
if (!selectedContextId || !selectedDetail) {
setError('No pause point is selected. Refresh and try again.')
return
}
setLoadingAction(true)
setError(null)
setMessage(null)
let resumePayload: any
try {
if (isHumanMode && hasInputFormat) {
const errors: Record<string, string> = {}
const submission: Record<string, any> = {}
for (const field of inputFormatFields) {
const rawValue = formValues[field.name] ?? ''
const hasValue =
field.type === 'boolean'
? rawValue === 'true' || rawValue === 'false'
: rawValue.trim().length > 0 && rawValue !== '__unset__'
if (!hasValue || rawValue === '__unset__') {
if (field.required) errors[field.name] = 'This field is required.'
continue
}
const { value, error: parseError } = parseFormValue(field, rawValue)
if (parseError) {
errors[field.name] = parseError
continue
}
if (Object.keys(errors).length > 0) {
setFormErrors(errors)
setError('Fix the highlighted fields before resuming.')
if (value !== undefined) submission[field.name] = value
}
if (Object.keys(errors).length > 0) {
setFormErrors(errors)
setError('Fix the highlighted fields before resuming.')
setLoadingAction(false)
return
}
setFormErrors({})
resumePayload = { submission }
} else {
let parsedInput: any
if (resumeInput && resumeInput.trim().length > 0) {
try {
parsedInput = JSON.parse(resumeInput)
} catch {
setError('Resume input must be valid JSON.')
setLoadingAction(false)
return
}
setFormErrors({})
resumePayload = { submission }
} else {
let parsedInput: any
if (resumeInput && resumeInput.trim().length > 0) {
try {
parsedInput = JSON.parse(resumeInput)
} catch {
setError('Resume input must be valid JSON.')
setLoadingAction(false)
return
}
}
resumePayload = parsedInput
}
} catch (err: any) {
setError(err?.message || 'Failed to prepare resume payload.')
setLoadingAction(false)
resumePayload = parsedInput
}
} catch (err: any) {
setError(err?.message || 'Failed to prepare resume payload.')
setLoadingAction(false)
return
}
try {
const { ok, payload } = await resumeMutation.mutateAsync({
workflowId,
executionId,
contextId: selectedContextId,
input: resumePayload,
})
if (!ok) {
setError(payload.error || 'Failed to resume execution.')
return
}
try {
const { ok, payload } = await resumeMutation.mutateAsync({
workflowId,
executionId,
contextId: selectedContextId,
input: resumePayload,
})
if (!ok) {
setError(payload.error || 'Failed to resume execution.')
return
}
const nextStatus = payload.status === 'queued' ? 'queued' : 'resuming'
const nextQueuePosition = payload.queuePosition ?? null
const fallbackContextId =
executionDetail?.pausePoints.find(
(point) => point.contextId !== selectedContextId && point.resumeStatus === 'paused'
)?.contextId ?? null
queryClient.setQueryData<PausedExecutionDetail>(
resumeKeys.execution(workflowId, executionId),
(prev) => {
if (!prev) return prev
return {
...prev,
pausePoints: prev.pausePoints.map((point) =>
point.contextId === selectedContextId
? { ...point, resumeStatus: nextStatus, queuePosition: nextQueuePosition }
: point
),
}
const nextStatus = payload.status === 'queued' ? 'queued' : 'resuming'
const nextQueuePosition = payload.queuePosition ?? null
const fallbackContextId =
executionDetail?.pausePoints.find(
(point) => point.contextId !== selectedContextId && point.resumeStatus === 'paused'
)?.contextId ?? null
queryClient.setQueryData<PausedExecutionDetail>(
resumeKeys.execution(workflowId, executionId),
(prev) => {
if (!prev) return prev
return {
...prev,
pausePoints: prev.pausePoints.map((point) =>
point.contextId === selectedContextId
? { ...point, resumeStatus: nextStatus, queuePosition: nextQueuePosition }
: point
),
}
)
queryClient.setQueryData<PauseContextDetail | null>(
resumeKeys.context(workflowId, executionId, selectedContextId),
(prev) => {
if (!prev || prev.pausePoint.contextId !== selectedContextId) return prev
return {
...prev,
pausePoint: {
...prev.pausePoint,
resumeStatus: nextStatus,
queuePosition: nextQueuePosition,
},
}
}
)
queryClient.setQueryData<PauseContextDetail | null>(
resumeKeys.context(workflowId, executionId, selectedContextId),
(prev) => {
if (!prev || prev.pausePoint.contextId !== selectedContextId) return prev
return {
...prev,
pausePoint: {
...prev.pausePoint,
resumeStatus: nextStatus,
queuePosition: nextQueuePosition,
},
}
)
setSelectedContextIdOverride((override) => {
const currentContextId = override === undefined ? (defaultContextId ?? null) : override
return currentContextId !== selectedContextId ? override : fallbackContextId
})
setMessage(
payload.status === 'queued' ? 'Resume request queued.' : 'Resume started successfully.'
)
} catch (err: any) {
setError(err.message || 'Unexpected error while resuming execution.')
} finally {
setLoadingAction(false)
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[
workflowId,
executionId,
selectedContextId,
isHumanMode,
hasInputFormat,
inputFormatFields,
formValues,
parseFormValue,
resumeInput,
selectedDetail,
executionDetail,
queryClient,
]
)
}
)
setSelectedContextIdOverride((override) => {
const currentContextId = override === undefined ? (defaultContextId ?? null) : override
return currentContextId !== selectedContextId ? override : fallbackContextId
})
setMessage(
payload.status === 'queued' ? 'Resume request queued.' : 'Resume started successfully.'
)
} catch (err: any) {
setError(err.message || 'Unexpected error while resuming execution.')
} finally {
setLoadingAction(false)
}
}, [
workflowId,
executionId,
selectedContextId,
isHumanMode,
hasInputFormat,
inputFormatFields,
formValues,
parseFormValue,
resumeInput,
selectedDetail,
executionDetail,
queryClient,
])

const isFormComplete = useMemo(() => {
if (!isHumanMode || !hasInputFormat) return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ export function useCsvTruncationImport(
}
)
// importFile.mutate and router are stable references
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [workspaceId, file.id, file.key, file.name])

// Surface the cap as a warning toast with an import action, once per file.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,6 @@ const TerminalView = memo(function TerminalView({
}
// Theme is applied by the effect below so the terminal is never torn down
// (and its buffer never lost) for a repaint.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [terminalId, scopeId])

// Runs after the effect above, which is what installs these.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,6 @@ export function ResourceTabs({
// The chat owner handles optimistic state and its single ordered persistence queue.
onAddResource(resource)
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[desktopScopeId, onAddResource, selectResource]
)

Expand Down Expand Up @@ -477,7 +476,6 @@ export function ResourceTabs({
anchorIdRef.current = null
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[
activeId,
chatId,
Expand Down Expand Up @@ -574,7 +572,6 @@ export function ResourceTabs({
).catch(() => toast.error('Could not reorder that terminal. Please try again.'))
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[chatId, desktopScopeId, resources, onReorderResources]
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export function PromptEditor({

useEffect(() => {
if (autoFocus && !readOnly) editor.focusAtEnd()
// eslint-disable-next-line react-hooks/exhaustive-deps -- mount-only focus
// mount-only focus
}, [])

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ const UserInputImpl = forwardRef<UserInputHandle, UserInputProps>(function UserI
textarea.setSelectionRange(caretText.length, caretText.length)
}
}
}, []) // eslint-disable-line react-hooks/exhaustive-deps -- intentional mount-only restore
}, []) // intentional mount-only restore

const isFirstSaveRef = useRef(true)
const draftSaveTimerRef = useRef<number | null>(null)
Expand Down
1 change: 0 additions & 1 deletion apps/sim/app/workspace/[workspaceId]/home/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,6 @@ function HomeContent({ chatId, userName, userId }: HomeProps) {
// listing it would re-run this drain on every render. Omitted deliberately to
// keep it one-shot — and harmless either way, since `consume` clears the entry
// atomically and any re-run would find nothing.
// eslint-disable-next-line react-hooks/exhaustive-deps -- see above
}, [chatId, workspaceId, prepareResourceViewForAgentTurn, sendMessage])

function resolveResourceFromContext(
Expand Down
Loading
Loading