Intro

Maintenance & Codebase Checks

Codebase wiring audits, event-system delivery and topology checks, 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 six 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.

ds script find-missing-eventbridge-target-dlq
ds script find-missing-sns-subscription-dlq

The two infrastructure scans: every EventBridge rule target and every SNS subscription in stacks/ must have a dead-letter queue, or a delivery that fails is lost with no trace. Both print nothing when they pass.

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).

Event System Verification

The wiring checks above are static — they read source and confirm handlers, events, and stack entries are registered. Two further commands verify the event system at runtime, against real AWS infrastructure on a deployed stage.

ds script event-contract-test

An executable event-delivery contract test. It publishes one synthetic standard event and one synthetic FIFO event through the real EventBridge bus and SNS FIFO topic on a live stage, then asserts they're consumed through the real envelope-parsing chain — a green run proves envelope delivery end-to-end. It's an independent check, not a self-test: it re-implements the emitter's wire format byte-for-byte rather than importing DevStrideEventEmitter, so it catches infra-level delivery regressions the emitter's own unit tests can't. Its negative path deliberately drives an unroutable record and an unregistered-handler event to assert the sentinel alarm metrics fire.

  • --skip-negative — run only the positive delivery smoke test (skip the sentinel-alarm path).
  • --wait-alarm — also wait for the sentinel alarm to reach ALARM (slow).
  • --allow-prod-alarms — permit the negative path (which fires sentinel alarms) on prod/dev.
  • --timeout <seconds> — poll timeout (default 300).
ds script snapshot-event-topology

Captures a normalized snapshot of the live event topology — SNS topics, SQS queues, and EventBridge rules/attributes — into infra/event-topology/{stage}.baseline.json, with account IDs and stage-specific names tokenized out so the baseline is stable across stages. It's the drift detector for the event infrastructure: with --check it diffs the live topology against the committed baseline and exits 1 on any drift, so a stage whose queues/subscriptions have silently changed fails loudly instead of surprising you in production.

  • --check — diff the live topology against the committed baseline and exit 1 on drift (instead of writing a new baseline).
  • --out <path> — override the output/baseline path.

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.

ds script backfill-description-asset-items
DEVSTRIDE_BACKFILL_DRY_RUN=true ds script backfill-description-asset-items   # report only

The half of the description-asset repair a SQL migration could not do: parses the assets embedded in item descriptions and unfurls their metadata into asset items. Set DEVSTRIDE_BACKFILL_DRY_RUN=true to see what it would write first.

Operational Toggles

ds script maintenance-on
ds script maintenance-off
ds script api-maintenance-on
ds script api-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. The api- pair does the same for the public API's Lambdas only, so the app keeps working while integrations are held.

ds script sync-identity-providers

Restores Google sign-in on the stage's Cognito app client. Every deploy resets the client's provider list to [COGNITO], because the Google client secret lives inside the DEVSTRIDE_CONFIG secret where CloudFormation cannot read it; this idempotent step puts the provider back (or removes it when the keys are withdrawn). Seed runs it in after_deploy; run it by hand after deploying a stage any other way.

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.

Billing and canary rehearsals

ds script billing-create-account --org <organizationId> [--status trial|active|trial_expired|suspended|closed] [--pays-by card|invoice] [--trial-days 14]
ds script provision-canary-org --owner <account-id>

billing-create-account is a rehearsal of DevStride taking over one organization's billing, on a dev stage, through the real command; it refuses production by stage name and by the database it would write to. provision-canary-org creates the hidden canary organization and its inbound address once, on production only (the single stage that probes it); it is idempotent and refuses a run whose stage and database disagree. Both belong to owner-run procedures described in the repository's ds-billing-ops skill and the canary epic's checklist, not to the everyday loop.

One-Off Migrations

ds script create-slack-workflows
ds script create-teams-workflows --app-origin <url> [--org <organizationId>] [--dry-run]
ds script migrate-workitem-timespent-to-timeentry
ds script add-default-sidebar-form-group

These 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, Teams reminder workflows, 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. Never run the Slack and Teams backfills at the same time, and run the Teams one only after the settings API and membership consumer it depends on are deployed and old revisions have drained; its --app-origin must match the origin the process prints first, because it is baked into every created link.

Next Steps