Developer Docs

Maintenance & Codebase Checks

Codebase wiring audits, data cleanup and repair scripts, operational toggles, and historical one-off migrations available via ds script.

Maintenance & Codebase Checks

The ds script command is a grab-bag of operational tooling — codebase wiring audits, data repair, feature toggles, and one-off historical migrations. Unlike ds run, ds migrations, ds data, ds golden, and ds stripe, these commands share no common option prefix; nearly every one is its own independent script under cli/commands/scripts/ (the one exception is remove-org, which is registered under ds script but actually implemented in cli/commands/data/remove-org-data.ts).

This page covers everything under ds script that isn't already documented on the Local Development, API Development, or Stripe Integration pages (API client/docs generation, set-config, and reset-db live there instead).

Codebase Wiring Checks

DevStride's DI container and event system rely on files being explicitly registered in module init.ts files. A handler, event, or stack entry that exists on disk but isn't registered will silently never run — in production. These four scans catch that class of bug before it ships:

ds script find-missing-inits

Its main check scans Command and Query handler files and reports any that aren't registered in their module's init file, but it actually runs three checks in sequence: that first Command/Query pass, a second per-file convention check over event-handler files under application/**, and a third "whole-bundle" transitive check that walks each Lambda entry point's import closure (discovered from stacks/**/*.ts) to catch dispatches hidden behind shared utilities.

ds script find-missing-set-correlation-ids

Checks command/query/domain-event/integration-event/fifo-integration-event handlers for a setCorrelationId call, so requests stay traceable through the event-driven system. This is pattern-matching, not a type check — it can false-positive on indirect calls (e.g. via a base class), so treat flagged files as "verify manually," not "definitely broken."

ds script find-missing-events-registration

For each module, diffs the event classes defined under application/domain-events, application/integration-events, application/fifo-integration-events, and application/github-webhooks against what's actually registered in the corresponding *.init.ts file.

ds script find-missing-events-in-stack

Checks that every class extending IntegrationEventHandler — the same base class used for both regular integration events and FIFO integration events, there's no separate FifoIntegrationEventHandler type — is also wired into the relevant CDK stack file. An event handler that's registered in the module's init file but missing from the stack will never receive traffic.

Two closely related commands audit CloudFormation drift rather than code wiring:

ds script check-orphan-stacks
ds script delete-orphan-stacks

SST doesn't delete a stack just because it was removed from sst.config.ts — the orphaned stack lingers, and its API Gateway routes can shadow the catch-all apiFunc, silently breaking routes that used to live there. delete-orphan-stacks auto-remediates by deleting any stack in the watched list (cli/commands/scripts/orphan-stack-names.ts) that's still deployed; it's idempotent and a no-op once a stage is clean. check-orphan-stacks is the read-only guard — it fails loudly if an orphan is still present after the delete step (e.g. a missing IAM permission blocked the deletion).

Data Cleanup and Repair

ds script delete-orphaned-items

Finds work items and folders whose parentNumber points at a parent that no longer exists, then deletes them along with their activity logs, comments, notifications, roadmap items, item transactions, sub-items, and GitHub PR/commit/branch links. Takes no flags — it operates across every organization in the bound database.

ds script remove-org -o <organizationId>

Deletes every row for one organization — memberships, comments, GitHub links, and all tables in organizationTables — in a single irreversible pass.

ds script backfill-access-permissions

Idempotent SQL backfill that appends any missing baseline ACCESS_* navigation permission keys onto non-Owner roles (Reports and Settings access are deliberately excluded — those stay admin-opt-in). It exists to repair dev databases where an earlier draft of the permissions-rewrite migration marked itself applied in the Drizzle journal without the consolidated backfill ever firing. Safe to re-run — it only appends missing keys, never removes existing ones.

Operational Toggles

ds script maintenance-on
ds script maintenance-off

Sets or clears the DEVSTRIDE_MAINTENANCE_MODE environment variable on every Lambda function in the bound stage (looked up via the SST Function bindings, in batches, with backoff on CallerRateLimitExceeded/ResourceConflictException). Toggle this around disruptive operations on a shared stage.

ds script get-active-emails

Walks every organization with an active subscription, collects the emails of active members, and writes them out — useful for announcements or audits.

ds script inspect-user-permissions <username>

Diagnostic dump: looks up every organization membership for a username and prints the role's permissions array and is_system_owner flag for each. Built to answer "is this an access-control bug or a stale frontend session?" — the username is a required positional argument, not a flag.

One-Off Migrations

ds script create-slack-workflows
ds script migrate-workitem-timespent-to-timeentry
ds script add-default-sidebar-form-group

These three are historical, one-shot scripts — each was written to backfill a specific data shape after a specific feature shipped (Slack notification workflows for existing users, converting legacy work-item time-spent fields into TimeEntry records, and seeding a default "Sidebar" custom-field form group for every existing custom field collection). They're kept in the CLI for reference and for re-running against a stage that never got the original backfill, but they are not everyday tools — don't reach for them unless you know exactly which historical gap you're filling.

Next Steps