From 3e77f64d6c09a0dd6100cdf1ceda96bcb1e34dc5 Mon Sep 17 00:00:00 2001 From: Danilo Alonso Date: Fri, 5 Jun 2026 22:52:15 -0400 Subject: [PATCH 1/7] ci: add example-llm-memory-db-mssql e2e job Mirrors example-todo-db: bootstrap state.enc via noorm ci, build schema, apply changes, run SDK suite against SQL Server 2022. Uses noorm run build (not db reset) because schema-bound validator UDFs lock referenced tables, and config 'test' with isTest=true to match the test-context bootstrap. --- .github/workflows/ci.yml | 119 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14f0be9..add281a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -276,6 +276,125 @@ jobs: NOORM_TEST_PREBUILT: '1' run: bun test + # ─────────────────────────────────────────────────────────────────── + # examples/llm-memory-db-mssql is a second e2e target: a 38-table + # LLM-memory schema on SQL Server 2022 that exercises the + # MSSQL-specific surface — table-valued parameters, schema-bound + # validators, role-scoped views, named-procedure calls — plus a + # meta-test that drives `noorm mcp serve` end-to-end. Same shape as + # example-todo-db: bootstrap state.enc with `noorm ci`, build the + # schema, apply the committed change, then run the SDK suite. + # + # Two notable deviations from the todo-db pattern: + # + # 1. We use `noorm run build` instead of `noorm db reset` because + # schema-bound validator UDFs in `sql/03_validators/` lock the + # tables they reference, blocking the teardown step (gap #5 in + # examples/llm-memory-db-mssql/mssql-problems.md). + # + # 2. The test bootstrap is `config: 'test'` with `requireTest: true`, + # so the CI config must be named `test` and carry isTest=true. + # ─────────────────────────────────────────────────────────────────── + example-llm-memory-db-mssql: + runs-on: ubuntu-24.04 + needs: build + + services: + mssql: + image: mcr.microsoft.com/mssql/server:2022-latest + ports: + - 11433:1433 + env: + ACCEPT_EULA: 'Y' + SA_PASSWORD: 'NoOrm_Test123!' + MSSQL_PID: Developer + options: >- + --health-cmd "/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P 'NoOrm_Test123!' -C -Q 'SELECT 1' || exit 1" + --health-interval 10s + --health-timeout 5s + --health-retries 10 + --health-start-period 30s + + # Job-level env shared by every CLI step + the SDK test process. + # NOORM_name=test + NOORM_isTest=true line up with the + # `config: 'test', requireTest: true` call in + # examples/llm-memory-db-mssql/tests/helpers/test-context.ts. + env: + NOORM_CONNECTION_DIALECT: mssql + NOORM_CONNECTION_HOST: localhost + NOORM_CONNECTION_PORT: '11433' + NOORM_CONNECTION_USER: SA + NOORM_CONNECTION_PASSWORD: 'NoOrm_Test123!' + NOORM_CONNECTION_DATABASE: noorm_llm_test + NOORM_name: test + NOORM_type: remote + NOORM_isTest: 'true' + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '24' + + - uses: oven-sh/setup-bun@v2 + with: + bun-version: '1.3.11' + + - run: bun install --frozen-lockfile + - run: bun run build:packages + + - name: Expose `noorm` CLI on PATH + run: | + BIN_DIR="$GITHUB_WORKSPACE/.ci-bin" + mkdir -p "$BIN_DIR" + cat > "$BIN_DIR/noorm" <> $GITHUB_PATH + + - name: noorm ci identity new — mint ephemeral keypair + working-directory: examples/llm-memory-db-mssql + run: | + OUT=$(noorm ci identity new \ + --name llm-memory-db-mssql-ci \ + --email ci@noorm.local \ + --json) + PRIV=$(echo "$OUT" | jq -r '.data.privateKey // .privateKey') + echo "::add-mask::$PRIV" + echo "NOORM_IDENTITY_PRIVATE_KEY=$PRIV" >> "$GITHUB_ENV" + echo "NOORM_IDENTITY_NAME=llm-memory-db-mssql-ci" >> "$GITHUB_ENV" + echo "NOORM_IDENTITY_EMAIL=ci@noorm.local" >> "$GITHUB_ENV" + + - name: noorm ci init — bootstrap state.enc from env + working-directory: examples/llm-memory-db-mssql + run: noorm ci init --name test --force --json + + - name: noorm db create — create target database + working-directory: examples/llm-memory-db-mssql + run: noorm db create --json + + - name: noorm run build — build schema (types, tables, validators, views, procs) + working-directory: examples/llm-memory-db-mssql + run: noorm run build --json + + - name: noorm change ff — apply forward changes + working-directory: examples/llm-memory-db-mssql + run: noorm change ff --yes --json + + - name: Typecheck example + working-directory: examples/llm-memory-db-mssql + run: bun run typecheck + + - name: Run example test suite + working-directory: examples/llm-memory-db-mssql + env: + CI: 'false' + FORCE_COLOR: 'true' + run: bun test + cli-e2e: runs-on: ubuntu-24.04 needs: build From 84afc49782ee0f893644f739baee3fa2a1636612 Mon Sep 17 00:00:00 2001 From: Danilo Alonso Date: Fri, 5 Jun 2026 22:52:20 -0400 Subject: [PATCH 2/7] chore: add atomic project signals and scaffolding Wire .claude/project/signals.md @-ref into CLAUDE.md, add generated signals files, .signalsignore, followups, and docs/design + docs/spec scaffolding. Gitignore scratchpad and prev-deterministic snapshot. --- .claude/project/deterministic-signals.md | 1059 +++++++++++++++++ .claude/project/followups/INDEX.md | 21 + .../config-module-scope-env-snapshot.md | 13 + .../debug-process-test-no-assertions.md | 13 + .claude/project/signals-steering.md | 21 + .claude/project/signals.md | 79 ++ .claude/project/signals/cli.md | 68 ++ .claude/project/signals/core-change.md | 42 + .claude/project/signals/core-db.md | 52 + .claude/project/signals/core-identity.md | 59 + .claude/project/signals/core-runner.md | 45 + .claude/project/signals/core-state.md | 62 + .claude/project/signals/infra.md | 54 + .claude/project/signals/mcp-rpc.md | 46 + .claude/project/signals/sdk.md | 64 + .claude/project/signals/tui.md | 62 + .claude/project/signals/worker-bridge.md | 36 + .gitignore | 2 + .signalsignore | 17 + CLAUDE.md | 9 + docs/design/.gitkeep | 0 docs/spec/.gitkeep | 0 22 files changed, 1824 insertions(+) create mode 100644 .claude/project/deterministic-signals.md create mode 100644 .claude/project/followups/INDEX.md create mode 100644 .claude/project/followups/config-module-scope-env-snapshot.md create mode 100644 .claude/project/followups/debug-process-test-no-assertions.md create mode 100644 .claude/project/signals-steering.md create mode 100644 .claude/project/signals.md create mode 100644 .claude/project/signals/cli.md create mode 100644 .claude/project/signals/core-change.md create mode 100644 .claude/project/signals/core-db.md create mode 100644 .claude/project/signals/core-identity.md create mode 100644 .claude/project/signals/core-runner.md create mode 100644 .claude/project/signals/core-state.md create mode 100644 .claude/project/signals/infra.md create mode 100644 .claude/project/signals/mcp-rpc.md create mode 100644 .claude/project/signals/sdk.md create mode 100644 .claude/project/signals/tui.md create mode 100644 .claude/project/signals/worker-bridge.md create mode 100644 .signalsignore create mode 100644 docs/design/.gitkeep create mode 100644 docs/spec/.gitkeep diff --git a/.claude/project/deterministic-signals.md b/.claude/project/deterministic-signals.md new file mode 100644 index 0000000..f2d8628 --- /dev/null +++ b/.claude/project/deterministic-signals.md @@ -0,0 +1,1059 @@ +--- +generated_at: 2026-06-01T02:30:22Z +atomic_version: 3.0.0 +--- +# Deterministic signals + +## Tree + +├── .agents/ (1) +│ └── skills/ (1) +│ └── opentui/ (2) +│ ├── references/ (0 files, 8 dirs) +│ └── SKILL.md (a62967f, 195L, 7253ch, 7427B) +├── .changeset/ (64) +│ ├── README.md (bf33c79, 8L, 510ch, 510B) +│ ├── binary-release-automation.md (b25adb3, 6L, 110ch, 110B) +│ ├── bold-wolves-call.md (a1927b2, 10L, 442ch, 442B) +│ ├── brave-foxes-modify.md (da2d15a, 8L, 345ch, 347B) +│ ├── brave-wolves-rest.md (b7910ac, 5L, 220ch, 220B) +│ ├── bright-foxes-glow.md (9638d52, 16L, 520ch, 520B) +│ ├── bundle-fix.md (09aaa39, 8L, 200ch, 200B) +│ ├── calm-rivers-flow.md (146df56, 7L, 175ch, 175B) +│ ├── change-dry-run-cli.md (563b623, 8L, 502ch, 504B) +│ ├── change-dry-run-sdk.md (5b3059e, 6L, 263ch, 263B) +│ ├── change-interactive-prompts.md (1f01ea9, 19L, 1977ch, 1995B) +│ ├── citty-cli-migration.md (af9eed7, 16L, 596ch, 596B) +│ ├── citty-sdk-next.md (3cace63, 7L, 143ch, 143B) +│ ├── cjs-shim-fix.md (ce8f94e, 8L, 127ch, 127B) +│ ├── config.json (64bb386, 11L, 307ch, 307B) +│ ├── crisp-taxis-pick.md (38ea970, 30L, 1170ch, 1170B) +│ ├── dry-run-error-display.md (9ab9e9d, 7L, 358ch, 358B) +│ ├── fix-binary-bun-pin.md (21262d2, 6L, 173ch, 175B) +│ ├── fix-bundle-version.md (20815f5, 5L, 107ch, 107B) +│ ├── fix-headless-error-output.md (e2fdf59, 15L, 883ch, 885B) +│ ├── fix-helpers-loading.md (12f7ed3, 12L, 591ch, 595B) +│ ├── fix-mssql-connection-hang.md (2219da2, 11L, 502ch, 508B) +│ ├── fix-mssql-dialect-support-sdk.md (164eaa5, 10L, 442ch, 442B) +│ ├── fix-mssql-dialect-support.md (dadf1da, 19L, 1407ch, 1409B) +│ ├── fix-mssql-mysql-schema-migration.md (befbcf0, 11L, 504ch, 512B) +│ ├── fix-sdk-bundle-deps.md (69ad2cc, 15L, 738ch, 744B) +│ ├── fix-shutdown-hang.md (a723a06, 8L, 367ch, 367B) +│ ├── fix-teardown-schema-qualify-cli.md (ebd436d, 7L, 223ch, 223B) +│ ├── fix-teardown-schema-qualify.md (4cc7e50, 7L, 230ch, 230B) +│ ├── fix-teardown-tvp-ordering-cli.md (65aa702, 7L, 203ch, 203B) +│ ├── fix-teardown-tvp-ordering.md (f5f4c97, 7L, 203ch, 203B) +│ ├── generic-tvp-value.md (8d46d7b, 6L, 223ch, 225B) +│ ├── gentle-birds-impersonate.md (d33a2be, 9L, 598ch, 600B) +│ ├── gold-items-feel.md (90a88cd, 35L, 1391ch, 1391B) +│ ├── hip-pigs-knock.md (d9759c9, 34L, 1504ch, 1524B) +│ ├── humble-emus-jam.md (083c2fc, 167L, 8541ch, 8613B) +│ ├── identity-ci-settings-edit-secret.md (9a339dc, 9L, 1071ch, 1077B) +│ ├── init-nested-projects.md (d0ceb9e, 10L, 578ch, 578B) +│ ├── keen-moons-glow.md (4dd2b64, 7L, 274ch, 274B) +│ ├── kind-dogs-cheer.md (45e31cb, 6L, 176ch, 176B) +│ ├── mcp-server.md (e4430f9, 8L, 582ch, 584B) +│ ├── mssql-go-splitter-sdk.md (1a4d376, 11L, 708ch, 710B) +│ ├── mssql-teardown-sdk.md (37fb6c1, 8L, 917ch, 929B) +│ ├── noorm-ci-namespace.md (0e98b5d, 28L, 2748ch, 2762B) +│ ├── noorm-init-sql-repl.md (19e9978, 8L, 373ch, 377B) +│ ├── pre.json (916cf1f, 74L, 1973ch, 1973B) +│ ├── quiet-pandas-sleep.md (a17d93c, 7L, 220ch, 220B) +│ ├── rebuild-fix.md (174afad, 5L, 69ch, 69B) +│ ├── rich-errors-templates-headless.md (fea62cb, 18L, 1337ch, 1345B) +│ ├── runner-observability-cli.md (aea99ea, 9L, 562ch, 562B) +│ ├── sdk-protected-config-hardblock.md (99d3624, 37L, 1477ch, 1489B) +│ ├── sharp-foxes-run.md (4f88802, 5L, 224ch, 224B) +│ ├── swift-clouds-drift.md (cbb537b, 7L, 175ch, 175B) +│ ├── tender-lions-enter.md (aae45fa, 32L, 1240ch, 1240B) +│ ├── tiny-dogs-yawn.md (7c0d0cc, 5L, 127ch, 127B) +│ ├── tty-yes-flag-cli.md (a93a4e3, 8L, 723ch, 723B) +│ ├── tvp-support.md (48e6ffe, 8L, 328ch, 330B) +│ ├── typed-tuples-sdk.md (c803a7b, 6L, 216ch, 218B) +│ ├── vault-init-idempotent-sdk.md (93cbe93, 8L, 545ch, 547B) +│ ├── version-command.md (379be83, 5L, 138ch, 138B) +│ ├── version-debug.md (268b067, 5L, 96ch, 96B) +│ ├── warm-apples-march.md (e8178e6, 13L, 754ch, 754B) +│ ├── warm-tables-stay.md (4440596, 9L, 327ch, 329B) +│ └── worker-bridge.md (5aebf7c, 14L, 1107ch, 1109B) +├── .claude/ (2) +│ ├── rules/ (4) +│ │ ├── documentation.md (69cdfde, 30L, 837ch, 837B) +│ │ ├── testing.md (3c3b98d, 58L, 1070ch, 1070B) +│ │ ├── tui-development.md (e73a146, 163L, 4197ch, 4199B) +│ │ └── typescript.md (56de6aa, 312L, 7494ch, 7522B) +│ └── skills/ (2) +│ ├── noorm-design/ (7) +│ │ ├── assets/ (5 files, 1 dir) +│ │ ├── preview/ (25 files, 0 dirs) +│ │ ├── ui_kits/ (0 files, 2 dirs) +│ │ ├── uploads/ (1 file, 0 dirs) +│ │ ├── README.md (0afc77c, 302L, 13445ch, 13558B) +│ │ ├── SKILL.md (0b38f2d, 29L, 1885ch, 1893B) +│ │ └── colors_and_type.css (9e653e9, 403L, 12946ch, 13014B) +│ └── opentui +├── .github/ (1) +│ └── workflows/ (4) +│ ├── ci.yml (1f9faff, 445L, 18826ch, 19874B) +│ ├── docs.yml (4c7bee9, 48L, 1371ch, 1371B) +│ ├── publish.yml (7f0ea77, 85L, 2128ch, 2128B) +│ └── release-binary.yml (5123f48, 38L, 1053ch, 1053B) +├── docs/ (15) +│ ├── .vitepress/ (2) +│ │ ├── theme/ (5) +│ │ │ ├── HeroEyebrow.vue (e5ca4ad, 15L, 356ch, 361B) +│ │ │ ├── HeroStats.vue (715ce16, 26L, 531ch, 533B) +│ │ │ ├── HeroTerminal.vue (c6d4dfd, 140L, 3400ch, 3401B) +│ │ │ ├── brand.css (861d0a6, 425L, 11555ch, 14635B) +│ │ │ └── index.ts (5ce55a8, 53L, 1207ch, 1207B) +│ │ └── config.mts (91137d2, 192L, 8350ch, 8354B) +│ ├── cli/ (9) +│ │ ├── flags.md (9b7f8eb, 94L, 3136ch, 3152B) +│ │ ├── help.md (bbb20fe, 47L, 1421ch, 1429B) +│ │ ├── identity.md (00a7538, 79L, 3191ch, 3209B) +│ │ ├── init.md (ca7879c, 70L, 2873ch, 2889B) +│ │ ├── run.md (654e92f, 123L, 3831ch, 3845B) +│ │ ├── settings-edit.md (56b510a, 21L, 588ch, 590B) +│ │ ├── settings-secret.md (c26ebf9, 23L, 675ch, 679B) +│ │ ├── sql-repl.md (10dc595, 28L, 785ch, 789B) +│ │ └── sql.md (18be39b, 70L, 2615ch, 2639B) +│ ├── design/ (1) +│ │ └── .gitkeep (e3b0c44, 0L, 0ch, 0B) +│ ├── dev/ (26) +│ │ ├── README.md (2b333a0, 201L, 6527ch, 8033B) +│ │ ├── change.md (1ebd1fc, 509L, 15457ch, 15519B) +│ │ ├── ci.md (2bd9b8a, 205L, 6796ch, 6804B) +│ │ ├── config-sharing.md (7d033e6, 269L, 8565ch, 8583B) +│ │ ├── config.md (97b3fc1, 432L, 11722ch, 11742B) +│ │ ├── datamodel.md (718afdb, 1028L, 28851ch, 28971B) +│ │ ├── explore.md (6895cb3, 325L, 8889ch, 8959B) +│ │ ├── headless.md (d1846fa, 756L, 17719ch, 17855B) +│ │ ├── identity.md (4a4ea8e, 350L, 12135ch, 12159B) +│ │ ├── index.md (3b3ccb3, 42L, 1430ch, 1430B) +│ │ ├── ink-cheatsheet.md (a188494, 1427L, 28669ch, 28689B) +│ │ ├── ink-testing-library-cheatsheet.md (67fa6e5, 737L, 14708ch, 14708B) +│ │ ├── lock.md (1325257, 330L, 8577ch, 8607B) +│ │ ├── logger.md (b8754a5, 521L, 15363ch, 16867B) +│ │ ├── project-discovery.md (ba6c27b, 128L, 4178ch, 4184B) +│ │ ├── runner.md (ec9317d, 516L, 18416ch, 18438B) +│ │ ├── sdk.md (9ee441d, 1094L, 26206ch, 26248B) +│ │ ├── secrets.md (2312d48, 297L, 10122ch, 10192B) +│ │ ├── settings.md (abd87a4, 746L, 18091ch, 18101B) +│ │ ├── sql-terminal.md (381dcaa, 321L, 8991ch, 10355B) +│ │ ├── state.md (79f8e8e, 362L, 9285ch, 9315B) +│ │ ├── teardown.md (f40246e, 359L, 11762ch, 11794B) +│ │ ├── template.md (e6310f7, 460L, 11515ch, 11603B) +│ │ ├── transfer.md (47b8c7b, 663L, 22886ch, 23134B) +│ │ ├── vault.md (3095975, 521L, 16055ch, 17667B) +│ │ └── version.md (c0f51c6, 644L, 17504ch, 17516B) +│ ├── getting-started/ (4) +│ │ ├── building-your-sdk.md (8389a71, 752L, 16897ch, 17383B) +│ │ ├── concepts.md (885c8ed, 346L, 10673ch, 11025B) +│ │ ├── first-build.md (6ea690f, 332L, 8338ch, 8434B) +│ │ └── installation.md (447f8bb, 114L, 2936ch, 2998B) +│ ├── guide/ (6) +│ │ ├── automation/ (3) +│ │ │ ├── ci.md (ee009ad, 345L, 11893ch, 11929B) +│ │ │ ├── mcp.md (bc7ea57, 100L, 2780ch, 3038B) +│ │ │ └── non-interactive.md (b320f7e, 120L, 4054ch, 4066B) +│ │ ├── changes/ (3) +│ │ │ ├── forward-revert.md (1754625, 285L, 8097ch, 8101B) +│ │ │ ├── history.md (91ab04d, 320L, 9917ch, 9917B) +│ │ │ └── overview.md (909a111, 348L, 13623ch, 13945B) +│ │ ├── database/ (5) +│ │ │ ├── create.md (c765f14, 142L, 4944ch, 4968B) +│ │ │ ├── explore.md (929a4ee, 481L, 17845ch, 22315B) +│ │ │ ├── teardown.md (709966b, 363L, 9314ch, 9316B) +│ │ │ ├── terminal.md (a88f37a, 225L, 7075ch, 9051B) +│ │ │ └── transfer.md (a6608ba, 369L, 10184ch, 10210B) +│ │ ├── environments/ (4) +│ │ │ ├── configs.md (fb44cf0, 340L, 10225ch, 10247B) +│ │ │ ├── secrets.md (bb92c69, 200L, 6828ch, 6840B) +│ │ │ ├── stages.md (1484f7d, 258L, 7298ch, 7300B) +│ │ │ └── vault.md (bd35325, 257L, 7681ch, 8229B) +│ │ ├── sql-files/ (3) +│ │ │ ├── execution.md (4a53b9a, 249L, 8166ch, 8308B) +│ │ │ ├── organization.md (1d17d3d, 327L, 7430ch, 7892B) +│ │ │ └── templates.md (a5a8ab9, 458L, 12870ch, 12996B) +│ │ └── troubleshooting.md (c99678d, 108L, 3639ch, 3647B) +│ ├── public/ (3) +│ │ ├── icons/ (10) +│ │ │ ├── bolt.svg (8880fa8, 1L, 586ch, 586B) +│ │ │ ├── code-branch.svg (027335b, 1L, 1063ch, 1063B) +│ │ │ ├── cubes.svg (477517a, 1L, 1009ch, 1009B) +│ │ │ ├── database.svg (72faaf5, 1L, 389ch, 389B) +│ │ │ ├── fast-forward.svg (fc62b4a, 1L, 302ch, 302B) +│ │ │ ├── flask.svg (ceb88e5, 1L, 676ch, 676B) +│ │ │ ├── lock.svg (57394ae, 1L, 546ch, 546B) +│ │ │ ├── terminal.svg (5102e98, 1L, 613ch, 613B) +│ │ │ ├── toolbox.svg (7cc582d, 1L, 373ch, 373B) +│ │ │ └── users.svg (7c6b4e2, 1L, 1099ch, 1099B) +│ │ ├── image/ (3) +│ │ │ ├── 0105.gif (fa282b2, 31773L, 7759553ch, 8187851B) +│ │ │ ├── logo.png (f273558, 100L, 44738ch, 46953B) +│ │ │ └── logo.svg (8d46c28, 6L, 2529ch, 2529B) +│ │ └── install.sh (0cc90a2, 116L, 2925ch, 2925B) +│ ├── reference/ (1) +│ │ └── sdk.md (4dfc194, 1426L, 41209ch, 41368B) +│ ├── spec/ (1) +│ │ └── .gitkeep (e3b0c44, 0L, 0ch, 0B) +│ ├── superpowers/ (1) +│ │ └── specs/ (1) +│ │ └── 2026-04-19-cli-ci-identity-design.md (6c9cc80, 938L, 32762ch, 32918B) +│ ├── bun.lockb (34225b2, 190L, 125711ch, 126677B) +│ ├── headless.md (9fd6327, 1592L, 36955ch, 36975B) +│ ├── index.md (4d92e08, 178L, 6324ch, 6362B) +│ ├── package.json (b4778f0, 24L, 657ch, 657B) +│ └── tui.md (c6668da, 407L, 13808ch, 17874B) +├── examples/ (3) +│ ├── llm-memory-db-mssql/ (16) +│ │ ├── .cursor/ (1) +│ │ │ └── rules/ (1 file, 0 dirs) +│ │ ├── .noorm/ (2) +│ │ │ ├── .gitignore (14188a3, 1L, 7ch, 7B) +│ │ │ └── settings.yml (46b45e4, 77L, 1844ch, 1844B) +│ │ ├── changes/ (1) +│ │ │ └── 2026-05-10-add-memory-tag-color/ (1 file, 2 dirs) +│ │ ├── sql/ (11) +│ │ │ ├── 00_types/ (4 files, 0 dirs) +│ │ │ ├── 01_reference/ (10 files, 0 dirs) +│ │ │ ├── 02_tables/ (9 files, 0 dirs) +│ │ │ ├── 03_validators/ (5 files, 0 dirs) +│ │ │ ├── 04_subtypes/ (8 files, 0 dirs) +│ │ │ ├── 05_binary/ (11 files, 0 dirs) +│ │ │ ├── 06_seeds/ (11 files, 0 dirs) +│ │ │ ├── 07_functions/ (8 files, 0 dirs) +│ │ │ ├── 08_views/ (18 files, 0 dirs) +│ │ │ ├── 09_procedures/ (73 files, 0 dirs) +│ │ │ └── $helpers.ts (2df21d3, 58L, 1722ch, 1722B) +│ │ ├── src/ (11) +│ │ │ ├── agent/ (5 files, 0 dirs) +│ │ │ ├── artifact/ (5 files, 0 dirs) +│ │ │ ├── audit/ (3 files, 0 dirs) +│ │ │ ├── core/ (3 files, 0 dirs) +│ │ │ ├── memory/ (5 files, 0 dirs) +│ │ │ ├── milestone/ (5 files, 0 dirs) +│ │ │ ├── note/ (5 files, 0 dirs) +│ │ │ ├── project/ (5 files, 0 dirs) +│ │ │ ├── tag/ (5 files, 0 dirs) +│ │ │ ├── task/ (5 files, 0 dirs) +│ │ │ └── index.ts (2fe92c8, 55L, 2610ch, 2612B) +│ │ ├── tests/ (4) +│ │ │ ├── domain/ (8 files, 0 dirs) +│ │ │ ├── helpers/ (1 file, 0 dirs) +│ │ │ ├── integration/ (5 files, 0 dirs) +│ │ │ └── sql/ (11 files, 0 dirs) +│ │ ├── .gitignore (ccb61cd, 40L, 446ch, 446B) +│ │ ├── .mcp.json (14f011f, 11L, 174ch, 174B) +│ │ ├── CHANGELOG.md (f40702f, 11L, 243ch, 243B) +│ │ ├── CLAUDE.md (1f39d31, 111L, 2676ch, 2676B) +│ │ ├── README.md (3b60a6a, 121L, 6801ch, 6837B) +│ │ ├── REPORT.md (4f3efcd, 161L, 12957ch, 13004B) +│ │ ├── mcp-config.json (14f011f, 11L, 174ch, 174B) +│ │ ├── mssql-problems.md (5083524, 328L, 23834ch, 23934B) +│ │ ├── package.json (a0b9242, 23L, 631ch, 631B) +│ │ └── tsconfig.json (7be3bae, 27L, 736ch, 736B) +│ ├── llm-memory-db-pg/ (16) +│ │ ├── .cursor/ (1) +│ │ │ └── rules/ (1 file, 0 dirs) +│ │ ├── .noorm/ (2) +│ │ │ ├── .gitignore (14188a3, 1L, 7ch, 7B) +│ │ │ └── settings.yml (9311372, 49L, 1117ch, 1117B) +│ │ ├── changes/ (2) +│ │ │ ├── 2026-05-10-add-memory-tag-color/ (1 file, 2 dirs) +│ │ │ └── .gitkeep (e3b0c44, 0L, 0ch, 0B) +│ │ ├── sql/ (12) +│ │ │ ├── 00_types/ (1 file, 0 dirs) +│ │ │ ├── 01_reference/ (10 files, 0 dirs) +│ │ │ ├── 02_tables/ (9 files, 0 dirs) +│ │ │ ├── 03_subtypes/ (8 files, 0 dirs) +│ │ │ ├── 04_binary/ (11 files, 0 dirs) +│ │ │ ├── 05_seeds/ (5 files, 0 dirs) +│ │ │ ├── 06_functions/ (10 files, 0 dirs) +│ │ │ ├── 07_views/ (18 files, 0 dirs) +│ │ │ ├── 08_procedures/ (0 files, 10 dirs) +│ │ │ ├── 09_triggers/ (2 files, 0 dirs) +│ │ │ ├── $helpers.ts (7bec322, 38L, 982ch, 982B) +│ │ │ └── .gitkeep (e3b0c44, 0L, 0ch, 0B) +│ │ ├── src/ (11) +│ │ │ ├── agent/ (5 files, 0 dirs) +│ │ │ ├── artifact/ (5 files, 0 dirs) +│ │ │ ├── audit/ (4 files, 0 dirs) +│ │ │ ├── core/ (4 files, 0 dirs) +│ │ │ ├── memory/ (5 files, 0 dirs) +│ │ │ ├── milestone/ (5 files, 0 dirs) +│ │ │ ├── note/ (5 files, 0 dirs) +│ │ │ ├── project/ (5 files, 0 dirs) +│ │ │ ├── tag/ (5 files, 0 dirs) +│ │ │ ├── task/ (5 files, 0 dirs) +│ │ │ └── index.ts (746abeb, 57L, 2636ch, 2640B) +│ │ ├── tests/ (5) +│ │ │ ├── domain/ (8 files, 0 dirs) +│ │ │ ├── helpers/ (1 file, 0 dirs) +│ │ │ ├── integration/ (4 files, 0 dirs) +│ │ │ ├── sql/ (13 files, 0 dirs) +│ │ │ └── mcp-discovery.test.ts (02d4035, 765L, 24559ch, 24603B) +│ │ ├── .gitignore (a94396a, 36L, 397ch, 397B) +│ │ ├── .mcp.json (14f011f, 11L, 174ch, 174B) +│ │ ├── CHANGELOG.md (4dd3e08, 11L, 240ch, 240B) +│ │ ├── CLAUDE.md (1f39d31, 111L, 2676ch, 2676B) +│ │ ├── README.md (18a81f1, 169L, 9519ch, 9823B) +│ │ ├── REPORT-PHASE-1.md (59b7d41, 103L, 9610ch, 9670B) +│ │ ├── REPORT.md (f22f51f, 140L, 17057ch, 17127B) +│ │ ├── mcp-config.json (14f011f, 11L, 174ch, 174B) +│ │ ├── package.json (21f45f9, 23L, 670ch, 670B) +│ │ └── tsconfig.json (4dc04b1, 30L, 735ch, 735B) +│ └── todo-db/ (10) +│ ├── .noorm/ (1) +│ │ └── settings.yml (edb7584, 18L, 270ch, 270B) +│ ├── changes/ (3) +│ │ ├── 2026-01-04-add-functions-and-views/ (1 file, 2 dirs) +│ │ ├── 2026-02-15-soft-delete-metadata-and-bulk-ops/ (1 file, 2 dirs) +│ │ └── .gitkeep (e3b0c44, 0L, 0ch, 0B) +│ ├── export/ (11) +│ │ ├── category.dt (5d47f0a, 1L, 295ch, 295B) +│ │ ├── change.dt (960d30c, 1L, 53ch, 53B) +│ │ ├── tag.dt (82d5afb, 1L, 317ch, 317B) +│ │ ├── todo.dt (2568fe0, 1L, 596ch, 596B) +│ │ ├── todo_item.dt (39ab7a9, 1L, 629ch, 629B) +│ │ ├── todo_item_tag.dt (759f217, 1L, 491ch, 491B) +│ │ ├── todo_tag.dt (402fa82, 1L, 419ch, 419B) +│ │ ├── user.dt (c2c0c98, 6L, 867ch, 867B) +│ │ ├── user.dtz (76c3b2a, 3L, 282ch, 294B) +│ │ ├── user_tag.dt (bd4000f, 1L, 256ch, 256B) +│ │ └── version.dt (cd68ba3, 1L, 54ch, 54B) +│ ├── sql/ (5) +│ │ ├── 00_tables/ (7 files, 0 dirs) +│ │ ├── 01_views/ (0 files, 4 dirs) +│ │ ├── 02_functions/ (0 files, 6 dirs) +│ │ ├── 10_seeds/ (5 files, 1 dir) +│ │ └── .gitkeep (e3b0c44, 0L, 0ch, 0B) +│ ├── tests/ (7) +│ │ ├── _helpers/ (4 files, 0 dirs) +│ │ ├── edge/ (1 file, 0 dirs) +│ │ ├── functions/ (7 files, 0 dirs) +│ │ ├── seeds/ (1 file, 0 dirs) +│ │ ├── tables/ (6 files, 0 dirs) +│ │ ├── views/ (2 files, 0 dirs) +│ │ └── preload.ts (0d97cd6, 25L, 658ch, 660B) +│ ├── .gitignore (81531bd, 5L, 57ch, 57B) +│ ├── CHANGELOG.md (f223e05, 17L, 300ch, 300B) +│ ├── bunfig.toml (e10e7cb, 5L, 89ch, 89B) +│ ├── package.json (174c9fc, 22L, 581ch, 581B) +│ └── tsconfig.json (efeae48, 17L, 484ch, 484B) +├── packages/ (2) +│ ├── cli/ (4) +│ │ ├── scripts/ (1) +│ │ │ └── postinstall.js (4ddeede, 155L, 4208ch, 4210B) +│ │ ├── CHANGELOG.md (6e5e36a, 649L, 34455ch, 34629B) +│ │ ├── noorm.js (e3d76e8, 41L, 1041ch, 1043B) +│ │ └── package.json (07ea87f, 31L, 540ch, 540B) +│ └── sdk/ (2) +│ ├── CHANGELOG.md (28d93a1, 495L, 22426ch, 22564B) +│ └── package.json (459a656, 60L, 1093ch, 1093B) +├── scripts/ (5) +│ ├── Dockerfile (5fe0d7a, 51L, 1595ch, 1595B) +│ ├── build-binary.mjs (f598b0c, 37L, 1284ch, 1288B) +│ ├── build.mjs (303daea, 40L, 1519ch, 1519B) +│ ├── install.sh (2fb9002, 175L, 3660ch, 3664B) +│ └── ralph-wiggum.sh (7182e0a, 319L, 8973ch, 8973B) +├── skills/ (1) +│ └── noorm/ (2) +│ ├── references/ (4) +│ │ ├── cli.md (bd58f75, 1011L, 29192ch, 29234B) +│ │ ├── config.md (5dd1eb1, 267L, 8600ch, 9296B) +│ │ ├── sdk.md (632ebcb, 646L, 21501ch, 21584B) +│ │ └── templates.md (20dbd54, 386L, 11061ch, 11161B) +│ └── SKILL.md (d49c384, 65L, 3837ch, 3845B) +├── src/ (8) +│ ├── cli/ (20) +│ │ ├── change/ (13) +│ │ │ ├── _prompt.ts (a32b90c, 140L, 3465ch, 3467B) +│ │ │ ├── add.ts (4356dc7, 110L, 3023ch, 3025B) +│ │ │ ├── edit.ts (578f3a7, 129L, 3842ch, 3846B) +│ │ │ ├── ff.ts (5dbcd50, 108L, 3047ch, 3049B) +│ │ │ ├── history-detail.ts (208c91f, 181L, 5310ch, 5318B) +│ │ │ ├── history.ts (b142e25, 72L, 1710ch, 1712B) +│ │ │ ├── index.ts (f87649d, 58L, 1518ch, 1522B) +│ │ │ ├── list.ts (a3b191d, 74L, 1777ch, 1781B) +│ │ │ ├── next.ts (55f3589, 110L, 3017ch, 3019B) +│ │ │ ├── revert.ts (af5236e, 133L, 3749ch, 3751B) +│ │ │ ├── rewind.ts (e47452b, 132L, 3682ch, 3688B) +│ │ │ ├── rm.ts (b986149, 137L, 3829ch, 3831B) +│ │ │ └── run.ts (5a0a0de, 133L, 3744ch, 3746B) +│ │ ├── ci/ (4) +│ │ │ ├── identity/ (3 files, 0 dirs) +│ │ │ ├── index.ts (edb5806, 26L, 673ch, 679B) +│ │ │ ├── init.ts (e29ca75, 216L, 6703ch, 6711B) +│ │ │ └── secrets.ts (5b51d3a, 231L, 6176ch, 6180B) +│ │ ├── config/ (10) +│ │ │ ├── add.ts (cce6bfa, 27L, 732ch, 736B) +│ │ │ ├── cp.ts (cc57e45, 81L, 2209ch, 2211B) +│ │ │ ├── edit.ts (76f315f, 34L, 888ch, 892B) +│ │ │ ├── export.ts (508008b, 81L, 2213ch, 2215B) +│ │ │ ├── import.ts (11eb6e3, 133L, 3468ch, 3470B) +│ │ │ ├── index.ts (54b770a, 22L, 611ch, 613B) +│ │ │ ├── list.ts (d6aad68, 72L, 1905ch, 1909B) +│ │ │ ├── rm.ts (a5c6a97, 34L, 865ch, 869B) +│ │ │ ├── use.ts (f307d0e, 79L, 2169ch, 2173B) +│ │ │ └── validate.ts (d832331, 130L, 3623ch, 3625B) +│ │ ├── db/ (16) +│ │ │ ├── create.ts (9d3ae5b, 107L, 2993ch, 2995B) +│ │ │ ├── drop.ts (89c3bff, 95L, 2496ch, 2498B) +│ │ │ ├── explore-fks.ts (3a624f7, 79L, 1936ch, 1940B) +│ │ │ ├── explore-functions.ts (cd47d2a, 132L, 3143ch, 3147B) +│ │ │ ├── explore-indexes.ts (69140a5, 76L, 1762ch, 1764B) +│ │ │ ├── explore-procedures.ts (3bfd32a, 125L, 2932ch, 2934B) +│ │ │ ├── explore-tables-detail.ts (1e74666, 81L, 1983ch, 1985B) +│ │ │ ├── explore-tables.ts (85ba1fe, 66L, 1430ch, 1432B) +│ │ │ ├── explore-types.ts (03dbb1a, 136L, 3224ch, 3226B) +│ │ │ ├── explore-views.ts (d002b8d, 128L, 3069ch, 3071B) +│ │ │ ├── explore.ts (f62b609, 82L, 2179ch, 2181B) +│ │ │ ├── index.ts (d0fc774, 28L, 610ch, 612B) +│ │ │ ├── reset.ts (c1abc6f, 59L, 1396ch, 1398B) +│ │ │ ├── teardown.ts (292a1a3, 77L, 2094ch, 2096B) +│ │ │ ├── transfer.ts (f45629b, 594L, 16502ch, 16506B) +│ │ │ └── truncate.ts (f272b7b, 63L, 1386ch, 1388B) +│ │ ├── dev/ (3) +│ │ │ ├── index.ts (a926fdd, 18L, 420ch, 422B) +│ │ │ ├── test-helpers.ts (68f7d8b, 162L, 5256ch, 5262B) +│ │ │ └── test-workers.ts (b7586cc, 251L, 7629ch, 7639B) +│ │ ├── identity/ (5) +│ │ │ ├── edit.ts (0fcb3f4, 82L, 2518ch, 2520B) +│ │ │ ├── export.ts (99921ac, 61L, 1779ch, 1781B) +│ │ │ ├── index.ts (8e2b3c8, 17L, 451ch, 453B) +│ │ │ ├── init.ts (0bd5325, 66L, 2198ch, 2200B) +│ │ │ └── list.ts (4857d87, 65L, 1853ch, 1855B) +│ │ ├── lock/ (5) +│ │ │ ├── acquire.ts (c2ec3c3, 64L, 1445ch, 1447B) +│ │ │ ├── force.ts (a218370, 55L, 1177ch, 1179B) +│ │ │ ├── index.ts (16e428d, 22L, 451ch, 453B) +│ │ │ ├── release.ts (1d574a0, 55L, 1126ch, 1128B) +│ │ │ └── status.ts (7ddbee9, 80L, 1985ch, 1987B) +│ │ ├── mcp/ (3) +│ │ │ ├── index.ts (79fb558, 15L, 348ch, 350B) +│ │ │ ├── init.ts (efa2243, 57L, 1514ch, 1516B) +│ │ │ └── serve.ts (08a9e57, 28L, 693ch, 697B) +│ │ ├── run/ (8) +│ │ │ ├── build.ts (27cfc8c, 91L, 2373ch, 2375B) +│ │ │ ├── dir.ts (9dd68cb, 95L, 2517ch, 2519B) +│ │ │ ├── exec.ts (dacee37, 170L, 4757ch, 4759B) +│ │ │ ├── file.ts (9f4b921, 79L, 2010ch, 2012B) +│ │ │ ├── files.ts (96be84c, 111L, 3152ch, 3154B) +│ │ │ ├── index.ts (5f4eb57, 28L, 591ch, 593B) +│ │ │ ├── inspect.ts (08320d4, 238L, 6786ch, 6788B) +│ │ │ └── preview.ts (c385471, 105L, 3087ch, 3089B) +│ │ ├── secret/ (4) +│ │ │ ├── index.ts (8648e62, 13L, 339ch, 341B) +│ │ │ ├── list.ts (731ce39, 64L, 1795ch, 1797B) +│ │ │ ├── rm.ts (f2d3075, 90L, 2523ch, 2525B) +│ │ │ └── set.ts (467aea4, 73L, 2130ch, 2132B) +│ │ ├── settings/ (5) +│ │ │ ├── build.ts (d3fc961, 56L, 1551ch, 1553B) +│ │ │ ├── edit.ts (3e373c3, 643L, 17492ch, 17498B) +│ │ │ ├── index.ts (8333365, 17L, 400ch, 402B) +│ │ │ ├── init.ts (984af2c, 67L, 1833ch, 1835B) +│ │ │ └── secret.ts (dd30d18, 384L, 9867ch, 9871B) +│ │ ├── sql/ (5) +│ │ │ ├── clear.ts (16b8b97, 89L, 2766ch, 2768B) +│ │ │ ├── history.ts (7c2048c, 139L, 3970ch, 3980B) +│ │ │ ├── index.ts (427fc50, 38L, 1298ch, 1300B) +│ │ │ ├── query.ts (612128e, 111L, 2835ch, 2837B) +│ │ │ └── repl.ts (4888c5d, 131L, 3817ch, 3819B) +│ │ ├── vault/ (7) +│ │ │ ├── cp.ts (2adf7aa, 195L, 5472ch, 5474B) +│ │ │ ├── index.ts (c96493f, 16L, 443ch, 445B) +│ │ │ ├── init.ts (747c923, 93L, 2340ch, 2342B) +│ │ │ ├── list.ts (1a7892b, 128L, 3447ch, 3449B) +│ │ │ ├── propagate.ts (b3b523d, 113L, 2877ch, 2879B) +│ │ │ ├── rm.ts (d13caa5, 91L, 2339ch, 2341B) +│ │ │ └── set.ts (e2d219e, 91L, 2372ch, 2374B) +│ │ ├── _utils.ts (6e7f28c, 454L, 11208ch, 11208B) +│ │ ├── index.ts (aacc1ea, 318L, 9027ch, 9031B) +│ │ ├── info.ts (63f9546, 351L, 10547ch, 10551B) +│ │ ├── init.ts (f11a60d, 176L, 5250ch, 5252B) +│ │ ├── ui.ts (c22c772, 65L, 1675ch, 1679B) +│ │ ├── update.ts (f53462e, 110L, 2930ch, 2934B) +│ │ └── version.ts (ca59073, 273L, 6880ch, 6890B) +│ ├── core/ (29) +│ │ ├── change/ (9) +│ │ │ ├── executor.ts (57455e1, 1095L, 28953ch, 30419B) +│ │ │ ├── history.ts (f0f083c, 1081L, 29641ch, 31253B) +│ │ │ ├── index.ts (edd765a, 128L, 3031ch, 4739B) +│ │ │ ├── manager.ts (3f21ad7, 616L, 15844ch, 18156B) +│ │ │ ├── parser.ts (917e642, 570L, 13337ch, 14801B) +│ │ │ ├── scaffold.ts (0f2d03f, 649L, 15170ch, 17122B) +│ │ │ ├── tracker.ts (da3742c, 245L, 7183ch, 7671B) +│ │ │ ├── types.ts (9f674de, 668L, 15169ch, 17853B) +│ │ │ └── validation.ts (ca6f086, 90L, 2247ch, 2247B) +│ │ ├── config/ (5) +│ │ │ ├── index.ts (8bacaa8, 132L, 3477ch, 3477B) +│ │ │ ├── protection.ts (e825813, 152L, 3648ch, 3648B) +│ │ │ ├── resolver.ts (23c7786, 380L, 9351ch, 9351B) +│ │ │ ├── schema.ts (9d69aa4, 263L, 6611ch, 7099B) +│ │ │ └── types.ts (b7fdc63, 129L, 2867ch, 3111B) +│ │ ├── connection/ (5) +│ │ │ ├── dialects/ (7 files, 0 dirs) +│ │ │ ├── factory.ts (7c5708c, 253L, 7370ch, 7370B) +│ │ │ ├── index.ts (29dcf6f, 8L, 268ch, 268B) +│ │ │ ├── manager.ts (8e53eb4, 360L, 8404ch, 8404B) +│ │ │ └── types.ts (873d17d, 75L, 1457ch, 1457B) +│ │ ├── db/ (5) +│ │ │ ├── dialects/ (5 files, 0 dirs) +│ │ │ ├── dual.ts (baaddd4, 174L, 4914ch, 4914B) +│ │ │ ├── index.ts (04bf6c4, 53L, 1283ch, 1283B) +│ │ │ ├── operations.ts (52dc2c3, 296L, 7247ch, 7249B) +│ │ │ └── types.ts (31a89d7, 87L, 1985ch, 1985B) +│ │ ├── debug/ (2) +│ │ │ ├── index.ts (6e232b4, 21L, 385ch, 385B) +│ │ │ └── operations.ts (05257cd, 446L, 11657ch, 12391B) +│ │ ├── dt/ (16) +│ │ │ ├── dialects/ (4 files, 0 dirs) +│ │ │ ├── constants.ts (8158c4b, 75L, 1797ch, 1797B) +│ │ │ ├── crypto.ts (d74cf8c, 107L, 3220ch, 3222B) +│ │ │ ├── deserialize.ts (1e84e10, 366L, 9011ch, 9017B) +│ │ │ ├── events.ts (360f2c4, 151L, 3984ch, 3984B) +│ │ │ ├── index.ts (bcd5dc6, 1169L, 30231ch, 30253B) +│ │ │ ├── modify.ts (bad43c7, 678L, 17964ch, 17994B) +│ │ │ ├── paths.ts (76dade7, 103L, 2874ch, 2886B) +│ │ │ ├── reader.ts (430fd8d, 208L, 5233ch, 5237B) +│ │ │ ├── schema.ts (e3a68b5, 385L, 9650ch, 9652B) +│ │ │ ├── serialize.ts (4c8ab00, 253L, 6001ch, 6011B) +│ │ │ ├── streamer.ts (453f365, 395L, 9296ch, 9296B) +│ │ │ ├── type-map.ts (4cf9141, 142L, 3882ch, 3886B) +│ │ │ ├── types.ts (52a9b23, 428L, 10149ch, 10149B) +│ │ │ ├── version.ts (6a13dc2, 216L, 5118ch, 5128B) +│ │ │ └── writer.ts (3525333, 233L, 5667ch, 5667B) +│ │ ├── explore/ (4) +│ │ │ ├── dialects/ (5 files, 0 dirs) +│ │ │ ├── index.ts (59b350c, 52L, 1222ch, 1222B) +│ │ │ ├── operations.ts (47770f3, 439L, 10129ch, 10133B) +│ │ │ └── types.ts (a373e84, 393L, 7861ch, 7861B) +│ │ ├── identity/ (9) +│ │ │ ├── crypto.ts (26f8fa9, 350L, 9858ch, 9858B) +│ │ │ ├── env.ts (b4c5d5f, 120L, 3024ch, 3032B) +│ │ │ ├── factory.ts (ffbf457, 357L, 8396ch, 8396B) +│ │ │ ├── hash.ts (751360b, 97L, 2350ch, 2350B) +│ │ │ ├── index.ts (70bbcd4, 219L, 5484ch, 5484B) +│ │ │ ├── resolver.ts (67fae4f, 229L, 5068ch, 5068B) +│ │ │ ├── storage.ts (29ef16f, 500L, 11805ch, 11805B) +│ │ │ ├── sync.ts (cd659cf, 438L, 11504ch, 11504B) +│ │ │ └── types.ts (7093b29, 244L, 5823ch, 5823B) +│ │ ├── lifecycle/ (4) +│ │ │ ├── handlers.ts (9847a66, 228L, 5414ch, 5414B) +│ │ │ ├── index.ts (2b435e9, 37L, 938ch, 938B) +│ │ │ ├── manager.ts (6910dac, 604L, 13368ch, 13368B) +│ │ │ └── types.ts (acdd08c, 155L, 3586ch, 3586B) +│ │ ├── lock/ (4) +│ │ │ ├── errors.ts (33152dd, 134L, 3411ch, 3411B) +│ │ │ ├── index.ts (57fe77e, 46L, 1049ch, 1049B) +│ │ │ ├── manager.ts (3e13178, 607L, 16367ch, 17343B) +│ │ │ └── types.ts (57e8f2b, 119L, 2831ch, 2831B) +│ │ ├── logger/ (11) +│ │ │ ├── classifier.ts (4fa4422, 179L, 3747ch, 3747B) +│ │ │ ├── color.ts (1dd10fa, 192L, 4033ch, 4045B) +│ │ │ ├── formatter.ts (07ecfee, 450L, 12942ch, 12944B) +│ │ │ ├── index.ts (7d627c2, 66L, 1513ch, 1513B) +│ │ │ ├── init.ts (fe47e04, 178L, 4739ch, 4739B) +│ │ │ ├── logger.ts (15c3907, 801L, 18791ch, 19523B) +│ │ │ ├── queue.ts (b5b3918, 298L, 6507ch, 7483B) +│ │ │ ├── reader.ts (50e90ff, 150L, 3536ch, 3536B) +│ │ │ ├── redact.ts (4314060, 389L, 9109ch, 10329B) +│ │ │ ├── rotation.ts (3579fb5, 250L, 5628ch, 5628B) +│ │ │ └── types.ts (5036c8b, 127L, 2773ch, 2773B) +│ │ ├── runner/ (6) +│ │ │ ├── checksum.ts (d21f46d, 99L, 2650ch, 2650B) +│ │ │ ├── index.ts (5156823, 55L, 1020ch, 1020B) +│ │ │ ├── mssql-batches.ts (6f7535b, 166L, 4825ch, 4835B) +│ │ │ ├── runner.ts (5d53e1f, 1413L, 36581ch, 38291B) +│ │ │ ├── tracker.ts (3547b6f, 704L, 20648ch, 20876B) +│ │ │ └── types.ts (5378590, 389L, 9507ch, 11459B) +│ │ ├── settings/ (7) +│ │ │ ├── defaults.ts (3fef652, 117L, 2748ch, 2748B) +│ │ │ ├── events.ts (f8588b1, 108L, 2257ch, 2257B) +│ │ │ ├── index.ts (f096e30, 78L, 1597ch, 1597B) +│ │ │ ├── manager.ts (66221e2, 1048L, 23331ch, 26017B) +│ │ │ ├── rules.ts (16f8633, 277L, 6610ch, 6610B) +│ │ │ ├── schema.ts (6de2494, 338L, 9153ch, 11227B) +│ │ │ └── types.ts (79b9fb0, 306L, 6964ch, 6964B) +│ │ ├── shared/ (5) +│ │ │ ├── dialect-quoting.ts (8f028eb, 66L, 1860ch, 1866B) +│ │ │ ├── errors.ts (9cb1445, 221L, 6038ch, 6528B) +│ │ │ ├── files.ts (d9f4a27, 98L, 3001ch, 3001B) +│ │ │ ├── index.ts (d39b0e1, 62L, 1331ch, 1331B) +│ │ │ └── tables.ts (06b23dd, 487L, 12754ch, 14716B) +│ │ ├── sql-terminal/ (4) +│ │ │ ├── executor.ts (ad0e460, 101L, 2589ch, 2589B) +│ │ │ ├── history.ts (0cdd43f, 407L, 9888ch, 9888B) +│ │ │ ├── index.ts (fabf166, 9L, 185ch, 185B) +│ │ │ └── types.ts (cb0a264, 123L, 2519ch, 2519B) +│ │ ├── state/ (6) +│ │ │ ├── encryption/ (2 files, 0 dirs) +│ │ │ ├── index.ts (ad7758c, 70L, 1373ch, 1373B) +│ │ │ ├── manager.ts (488e8c2, 712L, 16908ch, 18372B) +│ │ │ ├── migrations.ts (eef668b, 81L, 2489ch, 2489B) +│ │ │ ├── types.ts (7d1b252, 82L, 2168ch, 2168B) +│ │ │ └── version.ts (11a6a0b, 26L, 649ch, 649B) +│ │ ├── teardown/ (4) +│ │ │ ├── dialects/ (5 files, 0 dirs) +│ │ │ ├── index.ts (74cbb84, 27L, 549ch, 549B) +│ │ │ ├── operations.ts (236951c, 549L, 15035ch, 15067B) +│ │ │ └── types.ts (237ef4d, 271L, 6923ch, 6925B) +│ │ ├── template/ (7) +│ │ │ ├── loaders/ (7 files, 0 dirs) +│ │ │ ├── context.ts (a34f692, 245L, 6537ch, 6537B) +│ │ │ ├── engine.ts (5eb6f57, 214L, 6145ch, 6155B) +│ │ │ ├── helpers.ts (2c3adab, 182L, 4612ch, 4640B) +│ │ │ ├── index.ts (a611347, 69L, 1570ch, 1570B) +│ │ │ ├── types.ts (eeb0f2a, 214L, 4307ch, 4307B) +│ │ │ └── utils.ts (953ff32, 153L, 3408ch, 3438B) +│ │ ├── transfer/ (7) +│ │ │ ├── dialects/ (5 files, 0 dirs) +│ │ │ ├── events.ts (c8d31f3, 68L, 1652ch, 1652B) +│ │ │ ├── executor.ts (66a317b, 1099L, 26390ch, 26392B) +│ │ │ ├── index.ts (4da78aa, 219L, 5734ch, 5734B) +│ │ │ ├── planner.ts (916e6d7, 660L, 17836ch, 17838B) +│ │ │ ├── same-server.ts (365863e, 123L, 3024ch, 3024B) +│ │ │ └── types.ts (48073ce, 170L, 3917ch, 3917B) +│ │ ├── update/ (7) +│ │ │ ├── checker.ts (445cd79, 348L, 8642ch, 8642B) +│ │ │ ├── global-settings.ts (ced5668, 317L, 7229ch, 7229B) +│ │ │ ├── index.ts (1985bc9, 69L, 1446ch, 1446B) +│ │ │ ├── install-mode.ts (3f9823e, 116L, 2653ch, 2661B) +│ │ │ ├── registry.ts (d57d1b8, 182L, 4728ch, 4728B) +│ │ │ ├── types.ts (c65c26b, 127L, 4012ch, 4012B) +│ │ │ └── updater.ts (6d359a6, 279L, 7509ch, 7513B) +│ │ ├── vault/ (8) +│ │ │ ├── copy.ts (647dcc5, 226L, 6214ch, 6214B) +│ │ │ ├── events.ts (a5a6714, 58L, 1255ch, 1255B) +│ │ │ ├── index.ts (576eed0, 12L, 320ch, 320B) +│ │ │ ├── key.ts (e9e19ea, 263L, 6776ch, 6776B) +│ │ │ ├── propagate.ts (b458963, 251L, 6873ch, 6873B) +│ │ │ ├── resolve.ts (27db674, 195L, 5094ch, 5094B) +│ │ │ ├── storage.ts (8d3fb1c, 585L, 15883ch, 15889B) +│ │ │ └── types.ts (dff608b, 109L, 2465ch, 2465B) +│ │ ├── version/ (5) +│ │ │ ├── schema/ (1 file, 1 dir) +│ │ │ ├── settings/ (1 file, 1 dir) +│ │ │ ├── state/ (1 file, 1 dir) +│ │ │ ├── index.ts (8abf6c2, 302L, 7550ch, 8282B) +│ │ │ └── types.ts (0a0df3c, 253L, 6917ch, 8381B) +│ │ ├── worker-bridge/ (6) +│ │ │ ├── bridge.ts (e4d45b1, 126L, 3245ch, 3247B) +│ │ │ ├── index.ts (15b198a, 13L, 360ch, 360B) +│ │ │ ├── order-buffer.ts (83665ff, 45L, 723ch, 723B) +│ │ │ ├── paths.ts (ba286e4, 49L, 1673ch, 1677B) +│ │ │ ├── pool.ts (b8d2706, 108L, 2852ch, 2852B) +│ │ │ └── types.ts (e699925, 53L, 1983ch, 1989B) +│ │ ├── environment.ts (e138bc2, 131L, 2420ch, 2420B) +│ │ ├── index.ts (7418a76, 418L, 8895ch, 8895B) +│ │ ├── observer.ts (f715df0, 254L, 9767ch, 9767B) +│ │ ├── project-init.ts (40ae219, 166L, 4688ch, 4690B) +│ │ ├── project.ts (18af87e, 255L, 6533ch, 6533B) +│ │ └── theme.ts (f8e8f4a, 545L, 12937ch, 14702B) +│ ├── hooks/ (1) +│ │ └── observer.ts (cc4883f, 76L, 1791ch, 1791B) +│ ├── mcp/ (3) +│ │ ├── index.ts (931d3b5, 26L, 847ch, 849B) +│ │ ├── init.ts (84b9cbf, 85L, 1953ch, 1953B) +│ │ └── server.ts (3eea905, 195L, 6108ch, 6110B) +│ ├── rpc/ (6) +│ │ ├── commands/ (7) +│ │ │ ├── changes.ts (39633c7, 90L, 2508ch, 2508B) +│ │ │ ├── config.ts (b246033, 32L, 1048ch, 1048B) +│ │ │ ├── explore.ts (1341edb, 88L, 3158ch, 3158B) +│ │ │ ├── index.ts (1d30237, 30L, 734ch, 734B) +│ │ │ ├── query.ts (298d906, 42L, 1404ch, 1406B) +│ │ │ ├── run.ts (a6ff11e, 55L, 1610ch, 1610B) +│ │ │ └── session.ts (33c99fd, 51L, 1571ch, 1571B) +│ │ ├── index.ts (32e718c, 21L, 630ch, 630B) +│ │ ├── protection.ts (9eb736e, 338L, 6887ch, 6897B) +│ │ ├── registry.ts (02a1571, 109L, 2511ch, 2511B) +│ │ ├── session.ts (fad46fb, 160L, 3434ch, 3442B) +│ │ └── types.ts (37291b9, 70L, 1559ch, 1561B) +│ ├── sdk/ (11) +│ │ ├── impersonate/ (4) +│ │ │ ├── dialect-strategy.ts (51babcc, 101L, 2765ch, 3745B) +│ │ │ ├── index.ts (6a19d4e, 7L, 233ch, 233B) +│ │ │ ├── scope.ts (b139628, 103L, 3170ch, 3414B) +│ │ │ └── types.ts (ace8416, 67L, 2106ch, 2594B) +│ │ ├── namespaces/ (11) +│ │ │ ├── changes.ts (f9221bc, 479L, 12307ch, 13613B) +│ │ │ ├── db.ts (fd453fd, 365L, 9237ch, 10543B) +│ │ │ ├── dt.ts (e81b899, 106L, 2686ch, 3144B) +│ │ │ ├── index.ts (9ce85c2, 10L, 454ch, 454B) +│ │ │ ├── lock.ts (dcd30d9, 154L, 3875ch, 4333B) +│ │ │ ├── run.ts (8cda085, 207L, 5429ch, 6525B) +│ │ │ ├── secrets.ts (860ea8b, 42L, 967ch, 1213B) +│ │ │ ├── templates.ts (da4c186, 53L, 1492ch, 1738B) +│ │ │ ├── transfer.ts (bd1c052, 63L, 1604ch, 1850B) +│ │ │ ├── utils.ts (4f1abc0, 60L, 1491ch, 1737B) +│ │ │ └── vault.ts (5e75287, 364L, 9201ch, 10299B) +│ │ ├── stubs/ (1) +│ │ │ └── ansis.ts (16243d6, 19L, 488ch, 490B) +│ │ ├── context.ts (3e9d43b, 564L, 16926ch, 19032B) +│ │ ├── guards.ts (00b7989, 99L, 2252ch, 2742B) +│ │ ├── index.ts (00fbae2, 256L, 7480ch, 8214B) +│ │ ├── noorm-ops.ts (7b2144a, 186L, 4074ch, 4744B) +│ │ ├── sql.ts (397c9c9, 728L, 19985ch, 21507B) +│ │ ├── state.ts (4de0bfb, 29L, 1057ch, 1301B) +│ │ ├── tvp.ts (05b7f0a, 168L, 4695ch, 5433B) +│ │ └── types.ts (b2fc815, 161L, 4101ch, 5077B) +│ ├── tui/ (14) +│ │ ├── components/ (10) +│ │ │ ├── dialogs/ (6 files, 0 dirs) +│ │ │ ├── feedback/ (4 files, 0 dirs) +│ │ │ ├── forms/ (2 files, 0 dirs) +│ │ │ ├── layout/ (5 files, 0 dirs) +│ │ │ ├── lists/ (5 files, 0 dirs) +│ │ │ ├── overlays/ (2 files, 0 dirs) +│ │ │ ├── secrets/ (6 files, 0 dirs) +│ │ │ ├── status/ (3 files, 0 dirs) +│ │ │ ├── terminal/ (3 files, 0 dirs) +│ │ │ └── index.ts (5d55936, 96L, 2557ch, 2557B) +│ │ ├── hooks/ (14) +│ │ │ ├── index.ts (b106954, 71L, 1436ch, 1436B) +│ │ │ ├── useAsyncEffect.ts (9e1d1d5, 43L, 903ch, 903B) +│ │ │ ├── useChangeProgress.ts (12b8c8d, 111L, 3230ch, 3230B) +│ │ │ ├── useConnection.ts (516f590, 229L, 6526ch, 6526B) +│ │ │ ├── useLoadGuard.ts (e4d449c, 51L, 1054ch, 1054B) +│ │ │ ├── useLockStatus.ts (632d89f, 145L, 3655ch, 3655B) +│ │ │ ├── useObserver.ts (59f91e2, 200L, 5388ch, 5390B) +│ │ │ ├── useRunProgress.ts (8174572, 279L, 6494ch, 6494B) +│ │ │ ├── useSecretSource.ts (2004e16, 74L, 1995ch, 1995B) +│ │ │ ├── useSettingsOperation.ts (cdd5226, 104L, 2795ch, 2811B) +│ │ │ ├── useTransferProgress.ts (268d0bf, 583L, 14501ch, 14501B) +│ │ │ ├── useUpdateChecker.ts (eac4e68, 167L, 4040ch, 4040B) +│ │ │ ├── useVaultConnection.ts (993630d, 141L, 3774ch, 3784B) +│ │ │ └── useVaultSecretKeys.ts (851e11d, 113L, 3532ch, 3540B) +│ │ ├── providers/ (1) +│ │ │ └── ConnectionProvider.tsx (b4340b1, 231L, 5790ch, 5794B) +│ │ ├── screens/ (15) +│ │ │ ├── change/ (12 files, 0 dirs) +│ │ │ ├── config/ (11 files, 0 dirs) +│ │ │ ├── db/ (11 files, 1 dir) +│ │ │ ├── debug/ (4 files, 0 dirs) +│ │ │ ├── identity/ (6 files, 0 dirs) +│ │ │ ├── init/ (4 files, 0 dirs) +│ │ │ ├── lock/ (6 files, 0 dirs) +│ │ │ ├── run/ (7 files, 0 dirs) +│ │ │ ├── secret/ (4 files, 0 dirs) +│ │ │ ├── settings/ (17 files, 0 dirs) +│ │ │ ├── vault/ (5 files, 0 dirs) +│ │ │ ├── MoreScreen.tsx (8f8530e, 80L, 2503ch, 2503B) +│ │ │ ├── UpdateScreen.tsx (d71a562, 191L, 4892ch, 4892B) +│ │ │ ├── home.tsx (7f5fdf6, 635L, 21702ch, 21712B) +│ │ │ └── not-found.tsx (135b44e, 72L, 1958ch, 1958B) +│ │ ├── utils/ (12) +│ │ │ ├── change-context.ts (dbc7d53, 66L, 1991ch, 1991B) +│ │ │ ├── change-loader.ts (4ad58de, 252L, 6631ch, 6633B) +│ │ │ ├── clipboard.ts (93a7f8c, 95L, 1991ch, 1991B) +│ │ │ ├── config-validation.ts (ff30891, 151L, 3627ch, 3629B) +│ │ │ ├── connection.ts (e2bc3b6, 79L, 1950ch, 1950B) +│ │ │ ├── date.ts (ff07926, 20L, 391ch, 391B) +│ │ │ ├── error.ts (97c198b, 33L, 736ch, 736B) +│ │ │ ├── identity.ts (a44606a, 32L, 870ch, 870B) +│ │ │ ├── index.ts (d45254c, 25L, 883ch, 883B) +│ │ │ ├── paths.ts (ee0cf63, 53L, 1329ch, 1329B) +│ │ │ ├── run-context.ts (a8bf8c9, 63L, 1810ch, 1810B) +│ │ │ └── string.ts (8c22fe4, 39L, 1182ch, 1182B) +│ │ ├── app-context.tsx (9ad6408, 1196L, 29554ch, 30788B) +│ │ ├── app.tsx (e0768a4, 401L, 11536ch, 11592B) +│ │ ├── focus.tsx (342fe30, 250L, 5134ch, 5134B) +│ │ ├── keyboard.tsx (5d1144b, 401L, 9159ch, 9161B) +│ │ ├── observer-context.ts (305604b, 24L, 806ch, 806B) +│ │ ├── router.tsx (6e92c8a, 255L, 5954ch, 5954B) +│ │ ├── screens.tsx (0e4497b, 664L, 15342ch, 15342B) +│ │ ├── shutdown.tsx (8aa78da, 306L, 7951ch, 7955B) +│ │ └── types.ts (1767af6, 470L, 9688ch, 9690B) +│ └── workers/ (2) +│ ├── compute.ts (c7b828a, 46L, 1233ch, 1233B) +│ └── connection.ts (128339f, 241L, 5955ch, 5957B) +├── tests/ (11) +│ ├── cli/ (22) +│ │ ├── ci/ (4) +│ │ │ ├── identity-enroll.test.ts (daea467, 75L, 2166ch, 2168B) +│ │ │ ├── identity-new.test.ts (2462d0f, 88L, 2587ch, 2587B) +│ │ │ ├── init.test.ts (4834a97, 192L, 5286ch, 5286B) +│ │ │ └── secrets.test.ts (53fa24b, 216L, 6180ch, 6180B) +│ │ ├── components/ (7) +│ │ │ ├── DismissableAlert.test.tsx (ad0c85f, 340L, 9391ch, 9391B) +│ │ │ ├── dialogs.test.tsx (47f67ce, 274L, 7546ch, 7550B) +│ │ │ ├── form-navigation.test.tsx (7f6d5a4, 175L, 4812ch, 4816B) +│ │ │ ├── forms.test.tsx (d67738d, 171L, 4982ch, 4990B) +│ │ │ ├── layout.test.tsx (811eaec, 110L, 2675ch, 2683B) +│ │ │ ├── lists.test.tsx (3807cc8, 220L, 6656ch, 6670B) +│ │ │ └── status.test.tsx (345fc65, 191L, 5245ch, 5245B) +│ │ ├── hooks/ (3) +│ │ │ ├── useObserver.test.tsx (58bdf47, 496L, 12728ch, 12730B) +│ │ │ ├── useTransferProgress.test.tsx (5267f2e, 322L, 10736ch, 10744B) +│ │ │ └── useUpdateChecker.test.tsx (3073b2c, 283L, 8301ch, 8301B) +│ │ ├── run/ (9) +│ │ │ ├── _setup.ts (35ce510, 167L, 4749ch, 4753B) +│ │ │ ├── build.test.ts (a658b29, 125L, 3897ch, 3909B) +│ │ │ ├── change-ff-dryrun.test.ts (963e4be, 197L, 5697ch, 5701B) +│ │ │ ├── change-ff.test.ts (58aa688, 126L, 3522ch, 3526B) +│ │ │ ├── change-run.test.ts (049eec9, 111L, 2909ch, 2913B) +│ │ │ ├── dir.test.ts (b5724ae, 108L, 3146ch, 3152B) +│ │ │ ├── file.test.ts (bb58559, 115L, 3539ch, 3543B) +│ │ │ ├── files.test.ts (b4ef236, 182L, 5193ch, 5201B) +│ │ │ └── sql.test.ts (29d578f, 123L, 3370ch, 3372B) +│ │ ├── screens/ (1) +│ │ │ └── init/ (4 files, 0 dirs) +│ │ ├── app-context.test.tsx (e15ad92, 628L, 16709ch, 16711B) +│ │ ├── app.test.tsx (3a6f3d3, 241L, 6170ch, 6181B) +│ │ ├── change-edit.test.ts (8125b19, 103L, 2985ch, 2985B) +│ │ ├── change-prompts.test.ts (9be33db, 107L, 2822ch, 2822B) +│ │ ├── citty-help.test.ts (a81fe18, 61L, 1570ch, 1570B) +│ │ ├── debug-pid.test.tsx (d07de2e, 11L, 188ch, 188B) +│ │ ├── env-bootstrap.test.ts (048bf9c, 75L, 2314ch, 2314B) +│ │ ├── focus.test.tsx (77608ce, 662L, 15713ch, 15713B) +│ │ ├── init.test.ts (abbfba0, 124L, 3924ch, 3926B) +│ │ ├── keyboard.test.tsx (83d53c1, 573L, 14942ch, 14942B) +│ │ ├── router.test.tsx (952530c, 489L, 13626ch, 13626B) +│ │ ├── screens.test.tsx (6edec68, 277L, 7530ch, 7530B) +│ │ ├── settings-edit.test.ts (a5ebc82, 56L, 1416ch, 1416B) +│ │ ├── settings-secret.test.ts (8a6f206, 53L, 1365ch, 1365B) +│ │ ├── sql-repl.test.ts (37b9c5a, 32L, 864ch, 864B) +│ │ ├── types.test.ts (c9a86fb, 136L, 4786ch, 4786B) +│ │ └── yes-flag.test.ts (768cd0f, 476L, 12490ch, 12492B) +│ ├── core/ (25) +│ │ ├── change/ (5) +│ │ │ ├── fixtures/ (0 files, 3 dirs) +│ │ │ ├── executor.test.ts (e0369c0, 342L, 12372ch, 12376B) +│ │ │ ├── parser.test.ts (3ce9b9e, 394L, 12279ch, 12279B) +│ │ │ ├── scaffold.test.ts (c8b3e10, 364L, 10559ch, 10559B) +│ │ │ └── types.test.ts (6dac816, 150L, 4669ch, 4669B) +│ │ ├── config/ (5) +│ │ │ ├── debug-process.test.ts (48b3b6d, 12L, 300ch, 300B) +│ │ │ ├── env.test.ts (7c8ba98, 406L, 10934ch, 10934B) +│ │ │ ├── protection.test.ts (788faec, 232L, 6612ch, 6612B) +│ │ │ ├── resolver.test.ts (b24941d, 744L, 21240ch, 21240B) +│ │ │ └── schema.test.ts (5e937d9, 326L, 8795ch, 8795B) +│ │ ├── connection/ (2) +│ │ │ ├── factory.test.ts (d4ef387, 140L, 3916ch, 3916B) +│ │ │ └── manager.test.ts (6d87552, 192L, 5424ch, 5424B) +│ │ ├── dt/ (13) +│ │ │ ├── crypto.test.ts (396a0f4, 162L, 4548ch, 4548B) +│ │ │ ├── deserialize.test.ts (f86c304, 309L, 10375ch, 10375B) +│ │ │ ├── integration.test.ts (c419b58, 800L, 26994ch, 27020B) +│ │ │ ├── modify.test.ts (aaebadd, 930L, 32501ch, 32515B) +│ │ │ ├── paths.test.ts (34268ed, 135L, 3543ch, 3543B) +│ │ │ ├── reader.test.ts (88875d1, 261L, 6808ch, 6808B) +│ │ │ ├── roundtrip.test.ts (95212dc, 418L, 13038ch, 13050B) +│ │ │ ├── schema.test.ts (1add531, 107L, 3266ch, 3270B) +│ │ │ ├── serialize.test.ts (6536fd0, 348L, 10713ch, 10715B) +│ │ │ ├── streamer.test.ts (ce5296c, 311L, 10016ch, 10028B) +│ │ │ ├── type-map.test.ts (0d0c9d7, 508L, 15680ch, 15680B) +│ │ │ ├── worker-pipeline.test.ts (39f13d6, 323L, 10611ch, 10619B) +│ │ │ └── writer.test.ts (882ee19, 221L, 6611ch, 6611B) +│ │ ├── explore/ (2) +│ │ │ ├── dialects/ (4 files, 0 dirs) +│ │ │ └── operations.test.ts (64e9a5f, 589L, 16355ch, 16371B) +│ │ ├── identity/ (7) +│ │ │ ├── crypto.test.ts (8801b11, 311L, 8701ch, 8701B) +│ │ │ ├── env.test.ts (2e40161, 136L, 4113ch, 4113B) +│ │ │ ├── factory.test.ts (61019f8, 348L, 10115ch, 10115B) +│ │ │ ├── hash.test.ts (4ccae4e, 226L, 5558ch, 5584B) +│ │ │ ├── overrides.test.ts (1a4a1c5, 100L, 2374ch, 2374B) +│ │ │ ├── resolver.test.ts (f034831, 442L, 12034ch, 12074B) +│ │ │ └── storage.test.ts (dd036b0, 171L, 4452ch, 4452B) +│ │ ├── lifecycle/ (3) +│ │ │ ├── handlers.test.ts (172ef91, 326L, 8624ch, 8624B) +│ │ │ ├── manager.test.ts (2f7ce32, 705L, 17663ch, 17663B) +│ │ │ └── types.test.ts (ddcd4ce, 82L, 1879ch, 1879B) +│ │ ├── lock/ (3) +│ │ │ ├── errors.test.ts (14e1d59, 215L, 5889ch, 5889B) +│ │ │ ├── manager.test.ts (37f122b, 581L, 16221ch, 16221B) +│ │ │ └── types.test.ts (2bdf84e, 48L, 1238ch, 1238B) +│ │ ├── logger/ (8) +│ │ │ ├── classifier.test.ts (3eb2232, 188L, 5356ch, 5356B) +│ │ │ ├── formatter.test.ts (701d119, 316L, 8877ch, 8877B) +│ │ │ ├── logger.test.ts (00e1420, 512L, 13305ch, 13305B) +│ │ │ ├── output.test.ts (a17b46f, 371L, 11119ch, 11119B) +│ │ │ ├── queue.test.ts (2e16bbf, 295L, 6804ch, 6804B) +│ │ │ ├── reader.test.ts (f361ff0, 439L, 14445ch, 14445B) +│ │ │ ├── redact.test.ts (026b645, 554L, 15482ch, 15482B) +│ │ │ └── rotation.test.ts (2aad64b, 360L, 9943ch, 9943B) +│ │ ├── mcp/ (2) +│ │ │ ├── init.test.ts (9cb2913, 70L, 2197ch, 2197B) +│ │ │ └── server.test.ts (98bb227, 400L, 11152ch, 11552B) +│ │ ├── rpc/ (5) +│ │ │ ├── commands.test.ts (e512c37, 456L, 14464ch, 14466B) +│ │ │ ├── protection.test.ts (a9c8015, 188L, 4790ch, 4790B) +│ │ │ ├── registry-integration.test.ts (ce3964c, 230L, 6153ch, 6159B) +│ │ │ ├── registry.test.ts (6cbc997, 105L, 2893ch, 2893B) +│ │ │ └── session.test.ts (83a9eea, 50L, 1007ch, 1007B) +│ │ ├── runner/ (4) +│ │ │ ├── fixtures/ (6 files, 0 dirs) +│ │ │ ├── checksum.test.ts (16c5853, 151L, 4092ch, 4092B) +│ │ │ ├── mssql-batches.test.ts (4d6ccef, 283L, 8017ch, 8023B) +│ │ │ └── runner.test.ts (4ba2300, 151L, 4724ch, 4724B) +│ │ ├── settings/ (5) +│ │ │ ├── env-override.test.ts (a3300c6, 119L, 3276ch, 3276B) +│ │ │ ├── manager.test.ts (4f1c37b, 1144L, 25776ch, 25776B) +│ │ │ ├── rules.test.ts (de167f9, 416L, 13184ch, 13184B) +│ │ │ ├── schema.test.ts (5e7baac, 517L, 13184ch, 13184B) +│ │ │ └── setTeardown.test.ts (c2d7f2b, 84L, 2603ch, 2603B) +│ │ ├── shared/ (2) +│ │ │ ├── errors.test.ts (e78da77, 279L, 7975ch, 9571B) +│ │ │ └── tables.test.ts (b53c90b, 110L, 3272ch, 3272B) +│ │ ├── sql-terminal/ (2) +│ │ │ ├── executor.test.ts (00d1c88, 396L, 13321ch, 13321B) +│ │ │ └── history.test.ts (0db8ae6, 1013L, 34309ch, 34309B) +│ │ ├── state/ (2) +│ │ │ ├── encryption/ (1 file, 0 dirs) +│ │ │ └── manager.test.ts (47119ac, 664L, 20958ch, 22666B) +│ │ ├── teardown/ (2) +│ │ │ ├── dialects/ (4 files, 0 dirs) +│ │ │ └── operations.test.ts (9e8b400, 609L, 20381ch, 21875B) +│ │ ├── template/ (6) +│ │ │ ├── fixtures/ (0 files, 4 dirs) +│ │ │ ├── engine.test.ts (f3f1e18, 388L, 11697ch, 11697B) +│ │ │ ├── helpers.test.ts (4d0a204, 130L, 3944ch, 3944B) +│ │ │ ├── loaders.test.ts (115cc74, 257L, 6369ch, 6369B) +│ │ │ ├── security.test.ts (82855d4, 237L, 7609ch, 7609B) +│ │ │ └── utils.test.ts (d54a7a5, 165L, 3442ch, 3442B) +│ │ ├── transfer/ (5) +│ │ │ ├── dialects/ (4 files, 0 dirs) +│ │ │ ├── events.test.ts (ab6d02e, 364L, 10614ch, 10614B) +│ │ │ ├── executor.test.ts (602b084, 420L, 13400ch, 13400B) +│ │ │ ├── planner.test.ts (a81c2b6, 341L, 10240ch, 10240B) +│ │ │ └── same-server.test.ts (cf2bf8d, 369L, 10550ch, 10550B) +│ │ ├── update/ (3) +│ │ │ ├── checker.test.ts (4db209f, 191L, 5313ch, 5313B) +│ │ │ ├── global-settings.test.ts (1190fee, 231L, 6358ch, 6358B) +│ │ │ └── registry.test.ts (6f2fad2, 214L, 5987ch, 5987B) +│ │ ├── vault/ (1) +│ │ │ └── idempotent-init.test.ts (a903fc3, 236L, 6218ch, 6218B) +│ │ ├── version/ (5) +│ │ │ ├── manager.test.ts (8ff56f8, 319L, 11020ch, 11020B) +│ │ │ ├── schema.test.ts (9fc95bd, 658L, 20341ch, 20341B) +│ │ │ ├── settings.test.ts (4681701, 294L, 7728ch, 7728B) +│ │ │ ├── state.test.ts (17e78f0, 333L, 8663ch, 8663B) +│ │ │ └── types.test.ts (9892c26, 125L, 3021ch, 3021B) +│ │ ├── worker-bridge/ (3) +│ │ │ ├── bridge.test.ts (a6225e4, 63L, 1725ch, 1725B) +│ │ │ ├── order-buffer.test.ts (182cbed, 85L, 1988ch, 1992B) +│ │ │ └── pool.test.ts (f2754a9, 64L, 1798ch, 1798B) +│ │ ├── project-init.test.ts (48fa3fe, 75L, 2387ch, 2387B) +│ │ └── project.test.ts (cd6cc41, 243L, 6575ch, 6575B) +│ ├── fixtures/ (3) +│ │ ├── ci/ (2) +│ │ │ ├── failure/ (0 files, 1 dir) +│ │ │ └── success/ (0 files, 1 dir) +│ │ ├── sql/ (5) +│ │ │ ├── edge-cases/ (5 files, 0 dirs) +│ │ │ ├── mssql/ (9 files, 0 dirs) +│ │ │ ├── mysql/ (4 files, 0 dirs) +│ │ │ ├── postgres/ (5 files, 0 dirs) +│ │ │ └── sqlite/ (3 files, 0 dirs) +│ │ └── workers/ (2) +│ │ ├── adder.ts (3243e61, 15L, 443ch, 443B) +│ │ └── echo.ts (90dfb9f, 19L, 574ch, 574B) +│ ├── integration/ (10) +│ │ ├── cli/ (3) +│ │ │ ├── db.test.ts (3f4283c, 435L, 11442ch, 12906B) +│ │ │ ├── lock.test.ts (52b726a, 302L, 8178ch, 9398B) +│ │ │ └── setup.ts (9e503fb, 450L, 11325ch, 12301B) +│ │ ├── error-diagnostics/ (2) +│ │ │ ├── mssql.test.ts (38889e2, 178L, 5706ch, 5706B) +│ │ │ └── postgres.test.ts (2044f49, 103L, 3005ch, 3005B) +│ │ ├── explore/ (5) +│ │ │ ├── edge-cases.test.ts (a99161b, 524L, 15926ch, 15926B) +│ │ │ ├── mssql.test.ts (7c4a061, 566L, 18515ch, 18515B) +│ │ │ ├── mysql.test.ts (ad2a8d7, 471L, 14059ch, 14059B) +│ │ │ ├── postgres.test.ts (b4c311b, 444L, 14795ch, 14795B) +│ │ │ └── sqlite.test.ts (2b44515, 426L, 13079ch, 13079B) +│ │ ├── impersonate/ (2) +│ │ │ ├── mssql.test.ts (36695d6, 114L, 3625ch, 3629B) +│ │ │ └── postgres.test.ts (1638202, 113L, 3593ch, 3595B) +│ │ ├── runner/ (1) +│ │ │ └── mssql-batches.test.ts (1f41720, 264L, 7895ch, 7897B) +│ │ ├── sdk/ (2) +│ │ │ ├── tvf.test.ts (5d4f3a2, 279L, 7496ch, 8228B) +│ │ │ └── tvp.test.ts (61c50ec, 599L, 17433ch, 19149B) +│ │ ├── sql-terminal/ (4) +│ │ │ ├── mssql.test.ts (9352a97, 776L, 22700ch, 22700B) +│ │ │ ├── mysql.test.ts (b493743, 658L, 18368ch, 18368B) +│ │ │ ├── postgres.test.ts (a7137f8, 942L, 28694ch, 28694B) +│ │ │ └── sqlite.test.ts (f1cd0ad, 768L, 22000ch, 22000B) +│ │ ├── teardown/ (5) +│ │ │ ├── mssql.test.ts (63e885e, 598L, 21330ch, 21582B) +│ │ │ ├── mysql.test.ts (730f424, 400L, 12896ch, 12896B) +│ │ │ ├── postgres.test.ts (629207f, 403L, 14664ch, 14664B) +│ │ │ ├── sdk-preserve.test.ts (8d2711e, 177L, 5673ch, 6601B) +│ │ │ └── sqlite.test.ts (04aa3c8, 396L, 13541ch, 13541B) +│ │ ├── transfer/ (3) +│ │ │ ├── mssql.test.ts (b07cd1e, 364L, 12067ch, 12069B) +│ │ │ ├── mysql.test.ts (3ad6515, 383L, 12859ch, 12861B) +│ │ │ └── postgres.test.ts (449c11f, 371L, 12407ch, 12409B) +│ │ └── version/ (1) +│ │ └── schema.test.ts (e1c16d7, 727L, 22493ch, 23469B) +│ ├── sdk/ (9) +│ │ ├── impersonate/ (3) +│ │ │ ├── dialect-strategy.test.ts (d630381, 146L, 3515ch, 4491B) +│ │ │ ├── impersonate.test.ts (da5bf5f, 297L, 7604ch, 8580B) +│ │ │ └── scope.test.ts (b9d359a, 122L, 3310ch, 3798B) +│ │ ├── bundle-smoke.test.ts (5a63bf9, 319L, 8866ch, 10820B) +│ │ ├── context.test.ts (4804a5c, 597L, 18663ch, 19407B) +│ │ ├── db-namespace.test.ts (4994ef5, 259L, 7784ch, 8704B) +│ │ ├── destructive-ops.test.ts (36308b1, 196L, 5828ch, 7386B) +│ │ ├── guards.test.ts (585e35c, 178L, 4768ch, 4768B) +│ │ ├── lifecycle.test.ts (371af51, 126L, 3617ch, 4353B) +│ │ ├── noorm-ops.test.ts (0564bc8, 321L, 8629ch, 9801B) +│ │ └── sql.test.ts (959c16c, 1035L, 32675ch, 34387B) +│ ├── utils/ (3) +│ │ ├── db-splitter.test.ts (4db513c, 280L, 8506ch, 8506B) +│ │ ├── db.ts (0dfa608, 851L, 23959ch, 23969B) +│ │ └── mssql-batches.test.ts (551221c, 270L, 7309ch, 7313B) +│ ├── workers/ (2) +│ │ ├── compute.test.ts (66855f3, 72L, 2003ch, 2003B) +│ │ └── connection.test.ts (249b2cc, 77L, 2242ch, 2242B) +│ ├── global-setup.ts (15aaeca, 97L, 2596ch, 2603B) +│ ├── global-teardown.ts (1cce0b3, 39L, 936ch, 939B) +│ ├── preload.ts (86e4ebc, 100L, 2645ch, 2649B) +│ └── sample.env (fd7767a, 26L, 664ch, 664B) +├── .gitignore (48047e2, 48L, 493ch, 493B) +├── .npmrc (60376c8, 1L, 36ch, 36B) +├── .prettierignore (e3b0c44, 0L, 0ch, 0B) +├── .signalsignore (b0287a5, 17L, 662ch, 674B) +├── CLAUDE.md (f460842, 201L, 8300ch, 8506B) +├── CNAME (f3bed50, 1L, 9ch, 9B) +├── README.md (6bbc797, 84L, 2482ch, 2502B) +├── TODO.md (6a63836, 375L, 21659ch, 21929B) +├── bun.lockb (feb08f9, 451L, 266552ch, 268632B) +├── bunfig.toml (dab752d, 5L, 89ch, 89B) +├── docker-compose.yml (d2d79cb, 59L, 1534ch, 1534B) +├── eslint.config.js (cd11fe6, 56L, 2111ch, 2111B) +├── install.sh (0cc90a2, 116L, 2925ch, 2925B) +├── package.json (2904b1a, 102L, 3036ch, 3036B) +├── postgres-problems.md (1cbb5b5, 200L, 13674ch, 13799B) +├── tsconfig.json (640d95f, 21L, 616ch, 616B) +├── tsconfig.sdk-types.json (47a2c39, 13L, 303ch, 303B) +├── tsconfig.test.json (a5e75f7, 10L, 221ch, 221B) +├── tsup.cli.config.ts (8de2742, 45L, 1440ch, 1440B) +└── tsup.sdk.config.ts (a6518e1, 40L, 1107ch, 1111B) + +## Manifests + +- docs/package.json: name=@noormdev/docs, scripts=[build, dev, preview] +- examples/llm-memory-db-mssql/package.json: name=@noormdev/example-llm-memory-db-mssql, version=0.0.1-alpha.1, scripts=[test, test:watch, typecheck] +- examples/llm-memory-db-pg/package.json: name=@noormdev/example-llm-memory-db-pg, version=0.0.1-alpha.1, scripts=[test, test:watch, typecheck] +- examples/todo-db/package.json: name=@noormdev/example-todo-db, version=0.0.1-alpha.1, scripts=[test, test:watch, typecheck] +- package.json: name=@noormdev/main, version=0.0.1, scripts=[build, build:binary, build:packages, changeset, clean, dev, lint, lint:fix, prepublishOnly, release, start, start:init, test, test:coverage, test:watch, typecheck, typecheck:tests, version] +- packages/cli/package.json: name=@noormdev/cli, version=1.0.0-alpha.35, scripts=[postinstall] +- packages/sdk/package.json: name=@noormdev/sdk, version=1.0.0-alpha.35 + +## Languages + +- TypeScript: 199535 LOC (80%), 872 files (75%) +- Markdown: 42464 LOC (17%), 186 files (16%) +- YAML: 1114 LOC (0%), 16 files (1%) +- JavaScript: 1005 LOC (0%), 22 files (1%) +- HTML: 955 LOC (0%), 26 files (2%) +- CSS: 913 LOC (0%), 3 files (0%) +- Shell: 726 LOC (0%), 4 files (0%) +- JSON: 549 LOC (0%), 23 files (1%) +- Vue: 181 LOC (0%), 3 files (0%) +- TOML: 10 LOC (0%), 2 files (0%) diff --git a/.claude/project/followups/INDEX.md b/.claude/project/followups/INDEX.md new file mode 100644 index 0000000..e11bfa4 --- /dev/null +++ b/.claude/project/followups/INDEX.md @@ -0,0 +1,21 @@ +# Project follow-ups index + +Auto-generated by `atomic followups render`. Do not edit. + +Open: 2 • Stale: 0 • Last rendered: 2026-06-01 + +## 📋 plans (0) + +(none) + +## 🟡 risks (1) + +- [config-module-scope-env-snapshot](config-module-scope-env-snapshot.md) — Move makeNestedConfig to call-time in config module (0d) + +## 🔵 nits (1) + +- [debug-process-test-no-assertions](debug-process-test-no-assertions.md) — debug-process.test.ts has no assertions (passes unconditionally) (0d) + +## ❓ questions (0) + +(none) diff --git a/.claude/project/followups/config-module-scope-env-snapshot.md b/.claude/project/followups/config-module-scope-env-snapshot.md new file mode 100644 index 0000000..e21e4ca --- /dev/null +++ b/.claude/project/followups/config-module-scope-env-snapshot.md @@ -0,0 +1,13 @@ +--- +id: config-module-scope-env-snapshot +title: Move makeNestedConfig to call-time in config module +created: "2026-06-01" +origin: | + refresh-signals scan +kind: finding +severity: risk +review_by: "2026-07-31" +status: open +file: src/core/config/index.ts:34 +--- + diff --git a/.claude/project/followups/debug-process-test-no-assertions.md b/.claude/project/followups/debug-process-test-no-assertions.md new file mode 100644 index 0000000..c4eaffc --- /dev/null +++ b/.claude/project/followups/debug-process-test-no-assertions.md @@ -0,0 +1,13 @@ +--- +id: debug-process-test-no-assertions +title: debug-process.test.ts has no assertions (passes unconditionally) +created: "2026-06-01" +origin: | + refresh-signals scan +kind: finding +severity: nit +review_by: "2026-07-31" +status: open +file: tests/core/config/debug-process.test.ts +--- + diff --git a/.claude/project/signals-steering.md b/.claude/project/signals-steering.md new file mode 100644 index 0000000..4278ddf --- /dev/null +++ b/.claude/project/signals-steering.md @@ -0,0 +1,21 @@ +# Signals steering +# +# User-provided hints for the signals inferrer. When this file exists, +# the inferrer reads it before writing signals.md and treats its +# content as ground truth — steering wins over detection when they +# conflict. Delete sections you don't need. +# +# ## Framework +# NestJS monorepo (not plain Express) +# +# ## Domains +# - src/billing/ and src/payments/ are one domain ("payments") +# - src/internal-tools/ is scratch code — not a real domain +# +# ## Build +# - Build: pnpm turbo build +# - Test: pnpm test:ci (not pnpm test — that runs watch mode) +# +# ## Ignore for domains +# - vendor/ +# - generated/ diff --git a/.claude/project/signals.md b/.claude/project/signals.md new file mode 100644 index 0000000..23afccf --- /dev/null +++ b/.claude/project/signals.md @@ -0,0 +1,79 @@ +# Project signals + +## Framework & runtime + +- **Language:** TypeScript (80% LOC, 872 files), Bun runtime (>=1.2), Node >=22.13 +- **SQL layer:** Kysely query builder + executor; dialect-aware across PostgreSQL, MySQL, MSSQL, SQLite +- **TUI:** Ink 6 + React 19 (`src/tui/`); Citty for CLI arg parsing (`src/cli/`) +- **Event bus:** `@logosdx/observer` (`ObserverEngine`); module-scope singleton in `src/core/observer.ts` +- **Templating:** Eta 4 for `.sql.tmpl` files; data loaders for JSON5/YAML/CSV/JS side-cars +- **Error handling:** `@logosdx/utils` `attempt`/`attemptSync` tuples — no try-catch in source +- **Encryption:** AES-256-GCM for state (`src/core/state/encryption/`), Ed25519-like keypairs for identity +- **MCP:** `@modelcontextprotocol/sdk` wrapping RPC registry over stdio + +## Build / test / lint + +| Purpose | Command | Source | +|---------|---------|--------| +| Build (tsc) | `bun run build` | `package.json` | +| Build packages | `bun run build:packages` | `scripts/build.mjs` (tsup) | +| Build binary | `bun run build:binary` | `scripts/build-binary.mjs` (bun compile) | +| Dev watch | `bun run dev` | `package.json` | +| Test (all, serial) | `bun run test` | `package.json` | +| Test CI group 1 | `bun test --serial $(find tests/utils tests/core tests/sdk -name '*.test.ts' \| grep -v tests/core/transfer \| sort \| tr '\n' ' ')` | `.github/workflows/ci.yml:127` | +| Test CI group 2 | `bun test --serial tests/core/transfer` | `.github/workflows/ci.yml:132` | +| Test CI group 3 | `bun test --serial tests/cli` | `.github/workflows/ci.yml:137` | +| Test CI group 4 | `bun test --serial tests/integration` | `.github/workflows/ci.yml:142` | +| Lint | `bun run lint` | ESLint, `eslint.config.js` | +| Typecheck | `bun run typecheck` | `tsconfig.json` | + +CI gate: lint → typecheck → build → 4 test groups → 3 example jobs. Integration tests require live DB services (docker-compose or CI service containers). + +## Language breakdown + +| Language | LOC | Files | % | +|----------|-----|-------|---| +| TypeScript | 199535 | 872 | 80% | +| Markdown | 42464 | 186 | 17% | +| YAML | 1114 | 16 | 1% | +| JavaScript | 1005 | 22 | 1% | +| HTML | 955 | 26 | 2% | +| CSS | 913 | 3 | <1% | +| Shell | 726 | 4 | <1% | + +## DevOps & CI + +- **CI:** GitHub Actions (`ubuntu-24.04`), Bun 1.3.11 pinned; 4 test groups + 3 example jobs per push to master/main +- **DB services (CI):** Postgres 17 on 15432, MySQL 8.0 on 13306, MSSQL 2022 on 11433 +- **DB services (local):** `docker-compose.yml` at repo root (same ports) +- **Publish:** Changesets-driven (`changeset publish`) via `.github/workflows/publish.yml`; packages: `@noormdev/cli` and `@noormdev/sdk` +- **Binary release:** `bun build --compile` → GitHub Releases via `.github/workflows/release-binary.yml` +- **Docs:** VitePress, deployed via `.github/workflows/docs.yml` + +## Domains + +| Domain | Repo paths | One-liner | Detail | +|--------|------------|-----------|--------| +| core-change | `src/core/change/`, `src/cli/change/`, `tests/core/change/` | Versioned DB changes: scaffold, parse, execute, history | .claude/project/signals/core-change.md | +| core-runner | `src/core/runner/`, `src/core/template/`, `src/cli/run/`, `tests/core/runner/`, `tests/core/template/` | SQL file execution with checksum dedup and Eta templating | .claude/project/signals/core-runner.md | +| core-db | `src/core/db/`, `src/core/connection/`, `src/core/explore/`, `src/core/teardown/`, `src/core/transfer/`, `src/cli/db/`, `tests/core/connection/`, `tests/core/explore/`, `tests/core/teardown/`, `tests/core/transfer/`, `tests/integration/` | DB lifecycle: create/drop, explore schema, teardown, cross-DB transfer | .claude/project/signals/core-db.md | +| core-state | `src/core/state/`, `src/core/settings/`, `src/core/config/`, `src/core/lifecycle/`, `src/core/version/`, `src/core/project.ts`, `src/core/project-init.ts`, `src/core/environment.ts`, `src/core/observer.ts`, `tests/core/state/`, `tests/core/settings/`, `tests/core/config/`, `tests/core/lifecycle/`, `tests/core/version/` | Encrypted state, settings.yml, config resolution, lifecycle, version migration | .claude/project/signals/core-state.md | +| core-identity | `src/core/identity/`, `src/core/vault/`, `src/core/logger/`, `src/core/sql-terminal/`, `src/cli/identity/`, `src/cli/secret/`, `src/cli/vault/`, `src/cli/sql/`, `tests/core/identity/`, `tests/core/vault/`, `tests/core/logger/`, `tests/core/sql-terminal/` | Identity keypairs, vault secrets, structured logger, SQL terminal history | .claude/project/signals/core-identity.md | +| sdk | `src/sdk/`, `src/core/dt/`, `packages/sdk/`, `tests/sdk/`, `tests/integration/sdk/` | Programmatic API (`createContext`) + DT binary serialization format | .claude/project/signals/sdk.md | +| cli | `src/cli/`, `packages/cli/`, `skills/noorm/`, `tests/cli/` | Citty CLI with 17 command groups, headless mode, binary distribution | .claude/project/signals/cli.md | +| tui | `src/tui/`, `src/hooks/`, `.claude/rules/tui-development.md`, `tests/cli/components/`, `tests/cli/hooks/`, `tests/cli/screens/` | Ink/React TUI with focus manager, keyboard routing, per-domain screens | .claude/project/signals/tui.md | +| mcp-rpc | `src/mcp/`, `src/rpc/`, `src/cli/mcp/`, `tests/core/mcp/`, `tests/core/rpc/` | MCP server over stdio wrapping flat RPC command registry | .claude/project/signals/mcp-rpc.md | +| worker-bridge | `src/core/worker-bridge/`, `src/workers/`, `tests/core/worker-bridge/`, `tests/workers/` | Hub-and-spoke worker threads for DT serialization and DB connection worker | .claude/project/signals/worker-bridge.md | +| infra | `.github/`, `scripts/`, `examples/`, `docs/`, `tsup.*.config.ts`, `docker-compose.yml`, `bunfig.toml` | CI, build pipeline, binary release, example projects, VitePress docs | .claude/project/signals/infra.md | + +## Cross-cutting + +**Test layout:** Tests mirror `src/` under `tests/`. `tests/utils/` holds shared DB helpers. `tests/fixtures/` has SQL fixtures per dialect. `tests/integration/` requires live databases. `tests/global-setup.ts` / `tests/global-teardown.ts` coordinate integration DB bootstrap. + +**Known contamination:** `src/core/config/index.ts:34` calls `makeNestedConfig(process.env, …)` at module scope — snaps env at first import. This causes test cross-contamination when running the full suite in one process. Workaround: run test groups in separate `bun test --serial` invocations (same as CI). + +**Convention pointers:** `.claude/rules/typescript.md` (4-block function structure, `attempt` over try-catch), `.claude/rules/tui-development.md` (focus system, Ink layout), `.claude/rules/testing.md` (test naming, coverage), `.claude/rules/documentation.md` (three-pillar structure). + +**Domain partitioning basis:** Domains are functional vertical slices. `core-state` groups the startup/persistence concerns (state, settings, config, lifecycle, version) because they all initialize together in `project-init.ts`. `core-identity` groups crypto identity, vault, logger, and SQL terminal because they share the "user-facing sensitive data" concern. `core-db` groups connection, explore, transfer, and teardown because they all operate against a live database connection. `core-change` and `core-runner` are separate because changes are versioned operations while runner handles idempotent file execution — they share `runFile` but have distinct lifecycles. + +**Deterministic substrate:** `.claude/project/deterministic-signals.md` (generated 2026-06-01T02:30:22Z, atomic 3.0.0) diff --git a/.claude/project/signals/cli.md b/.claude/project/signals/cli.md new file mode 100644 index 0000000..e925b44 --- /dev/null +++ b/.claude/project/signals/cli.md @@ -0,0 +1,68 @@ +# cli + +## What it does + +Citty-based CLI with 17 top-level command groups. Each command group maps to a subdirectory under `src/cli/`. Commands emit events via the observer and delegate to core modules. Headless mode (`--yes`, `--json`) suppresses interactive prompts and formats output as JSON. + +Published as `@noormdev/cli` from `packages/cli/`. + +## Artifacts + +- `packages/cli/package.json` — published package `@noormdev/cli`, version `1.0.0-alpha.35`; entry `noorm.js` +- `packages/cli/noorm.js` — thin wrapper that runs the compiled binary +- `packages/cli/scripts/postinstall.js` — postinstall script for binary extraction +- `packages/cli/CHANGELOG.md` — CLI release history +- `skills/noorm/SKILL.md` — Claude Code skill for noorm CLI usage +- `skills/noorm/references/cli.md` — comprehensive CLI command reference (1011L) +- `skills/noorm/references/config.md` — config management reference +- `skills/noorm/references/sdk.md` — SDK reference for skill use +- `skills/noorm/references/templates.md` — template reference for skill use + +## CLI code + +- `src/cli/index.ts` — citty entry point; registers all subcommands, help interceptor, `--cwd` global flag +- `src/cli/_utils.ts` — shared CLI utilities: headless detection, output formatting, flag parsing +- `src/cli/change/` — `change add|edit|ff|history|list|next|revert|rewind|rm|run` (13 files) +- `src/cli/ci/` — `ci init|secrets|identity/*` — CI automation commands +- `src/cli/config/` — `config add|cp|edit|export|import|list|rm|use|validate` (10 files) +- `src/cli/db/` — `db create|drop|explore*|reset|teardown|transfer|truncate` (16 files) +- `src/cli/dev/` — `dev test-helpers|test-workers` — internal diagnostics +- `src/cli/identity/` — `identity edit|export|init|list` +- `src/cli/lock/` — `lock acquire|force|release|status` +- `src/cli/mcp/` — `mcp init|serve` +- `src/cli/run/` — `run build|dir|exec|file|files|inspect|preview` (8 files) +- `src/cli/secret/` — `secret list|rm|set` +- `src/cli/settings/` — `settings build|edit|init|secret` (5 files) +- `src/cli/sql/` — `sql clear|history|query|repl` +- `src/cli/vault/` — `vault cp|init|list|propagate|rm|set` +- `src/cli/init.ts` — `noorm init` — project initialization wizard +- `src/cli/info.ts` — `noorm info` — display project + env info +- `src/cli/ui.ts` — `noorm ui` — launch TUI +- `src/cli/update.ts` — `noorm update` — self-update +- `src/cli/version.ts` — `noorm version` — print version info + +## Docs + +- `docs/cli/` — 9 user-facing CLI reference pages +- `docs/dev/headless.md` — headless mode internals +- `docs/guide/automation/non-interactive.md` — non-interactive usage +- `docs/guide/automation/ci.md` — CI usage +- `docs/guide/automation/mcp.md` — MCP usage +- `docs/headless.md` — public headless reference (1592L) + +## Coupling + +- Every CLI command imports from `src/core/` — any core API change may require CLI command updates. +- `src/cli/ui.ts` launches the TUI (`src/tui/app.tsx`) — TUI startup is a CLI concern. +- `src/cli/mcp/serve.ts` starts the MCP server from `src/mcp/server.ts` — MCP domain depends on CLI entry. +- Headless mode output shape is consumed by CI pipelines and SDK integration tests. +- `settings.paths.sql` and `settings.paths.changes` from `settings.yml` are the correct path sources (not per-config `paths` fields) — several run-related screens use `settings?.paths?.changes ?? 'changes'` pattern. + +## Conventions worth knowing + +- Commands attach `examples: string[]` to their `defineCommand` result; the help interceptor in `src/cli/index.ts` appends them after citty's auto-generated usage. +- `--cwd ` global flag (like `git -C`) must precede the subcommand. +- `--yes` / `-y` flag suppresses all confirmation prompts (headless mode). +- `--json` flag formats output as machine-readable JSON. +- Build produces a standalone binary via `bun build --compile` — worker paths must use `resolveWorker()`. +- Workspace package `@noormdev/cli` publishes the pre-built binary; `postinstall.js` extracts it. diff --git a/.claude/project/signals/core-change.md b/.claude/project/signals/core-change.md new file mode 100644 index 0000000..0b59893 --- /dev/null +++ b/.claude/project/signals/core-change.md @@ -0,0 +1,42 @@ +# core-change + +## What it does + +Manages versioned database changes: scaffold (create/add/remove/reorder files), parse (discover + validate), execute (forward/revert with tracking), and history (execution records per change and per file). + +Change directories hold a `manifest.json` and SQL files. Each change has a description-based name, forward files, and optional revert files. Execution state is stored in the `__noorm_change__` and `__noorm_executions__` noorm tables. + +## CLI code + +- `src/core/change/scaffold.ts` — create/add/remove/rename/reorder change files on disk +- `src/core/change/parser.ts` — `parseChange`, `discoverChanges`, `resolveManifest`, `validateChange`, `parseSequence`, `parseDescription` +- `src/core/change/executor.ts` — `executeChange`, `revertChange`; applies SQL via the runner, records results +- `src/core/change/history.ts` — `ChangeHistory`; queries `__noorm_change__` and `__noorm_executions__` for per-change and per-file history +- `src/core/change/tracker.ts` — `ChangeTracker`; `canRevert` logic, orphaned-change detection +- `src/core/change/manager.ts` — `ChangeManager`; high-level facade: `list`, `run`, `revert`, `ff` (fast-forward) +- `src/core/change/validation.ts` — `validateChangeContent`; structural content checks +- `src/core/change/types.ts` — all change types, error classes (`ChangeValidationError`, `ChangeNotFoundError`, etc.) + +## Docs + +- `docs/dev/change.md` — developer reference for change internals +- `docs/guide/changes/overview.md` — user-facing: what changes are +- `docs/guide/changes/forward-revert.md` — forward and revert semantics +- `docs/guide/changes/history.md` — history querying +- `docs/cli/run.md` — run command docs (also covers change run) + +## Coupling + +- Calls `runner` (`runFile`) to execute SQL inside a change — changes in runner's `RunOptions` or file-execution semantics propagate here. +- Reads config via `src/core/config/` to resolve the active database connection — config schema changes affect `ChangeContext` construction. +- Emits events via `src/core/observer.ts` (`change:*` events) — the TUI subscribes via `useChangeProgress` hook. +- Writes to `__noorm_change__` and `__noorm_executions__` tables defined in `src/core/shared/tables.ts` — table renames propagate to executor and history queries. +- CLI commands in `src/cli/change/` call manager + scaffold functions — CLI argument shape changes here require CLI command updates. + +## Conventions worth knowing + +- Change directory names follow the pattern `YYYY-MM-DD-`. +- `manifest.json` lists files in execution order; reorder functions rewrite it. +- `parseSequence` extracts a numeric prefix from filename for ordering. +- `DEFAULT_CHANGE_OPTIONS` and `DEFAULT_BATCH_OPTIONS` define timeout and retry defaults. +- Error classes extend `Error` with a `code` field; callers check `code` to distinguish failure modes. diff --git a/.claude/project/signals/core-db.md b/.claude/project/signals/core-db.md new file mode 100644 index 0000000..a633c7d --- /dev/null +++ b/.claude/project/signals/core-db.md @@ -0,0 +1,52 @@ +# core-db + +## What it does + +Database lifecycle operations: create/drop databases, schema exploration (tables, views, functions, indexes, FKs, stored procedures, types), data transfer between databases, and schema teardown (truncate data, drop all objects). All operations are dialect-aware (PostgreSQL, MySQL, MSSQL, SQLite). + +## CLI code + +- `src/core/db/index.ts` — `checkDbStatus`, `createDb`, `destroyDb`, `getDialectOperations` +- `src/core/db/dual.ts` — `withDualConnection`; opens source + destination connections for transfer +- `src/core/db/dialects/` — dialect-specific create/drop implementations +- `src/core/explore/operations.ts` — `queryTables`, `queryViews`, `queryFunctions`, `queryIndexes`, `queryForeignKeys`, `queryProcedures`, `queryTypes` +- `src/core/explore/dialects/` — per-dialect SQL for introspection queries +- `src/core/explore/types.ts` — `TableInfo`, `ColumnInfo`, `ViewInfo`, `IndexInfo`, `ForeignKeyInfo`, etc. +- `src/core/teardown/operations.ts` — `truncateData`, `teardownSchema`, `previewTeardown` +- `src/core/teardown/dialects/` — dialect-specific truncate/drop implementations +- `src/core/teardown/types.ts` — `TruncateOptions`, `TeardownOptions`, `TeardownResult` +- `src/core/transfer/executor.ts` — `executeTransfer`; batch row copy with FK ordering +- `src/core/transfer/planner.ts` — `planTransfer`; dependency-sorted transfer plan +- `src/core/transfer/same-server.ts` — `sameServerTransfer`; direct SQL shortcut when source + dest are on same server +- `src/core/transfer/dialects/` — per-dialect identity-column and conflict-resolution strategies +- `src/core/transfer/types.ts` — `TransferOptions`, `TransferResult`, `TransferPlan` +- `src/core/connection/factory.ts` — `createConnection`, `testConnection`; Kysely instance factory +- `src/core/connection/manager.ts` — `ConnectionManager`; singleton connection lifecycle +- `src/core/connection/dialects/` — dialect drivers (pg, mysql2, tedious, better-sqlite3) + +## Docs + +- `docs/dev/explore.md` — explore internals +- `docs/dev/teardown.md` — teardown internals +- `docs/dev/transfer.md` — transfer internals +- `docs/guide/database/create.md` — create database guide +- `docs/guide/database/teardown.md` — teardown guide +- `docs/guide/database/transfer.md` — transfer guide +- `docs/guide/database/explore.md` — explore guide +- `docs/guide/database/terminal.md` — SQL terminal guide + +## Coupling + +- Transfer calls `withDualConnection` from `src/core/db/dual.ts` — dual-connection semantics shared with other DB ops. +- Teardown must skip `__noorm_*` tables (defined in `src/core/shared/tables.ts`) — `isNoormTable` guard in `teardown/operations.ts`. +- Connection manager (`src/core/connection/manager.ts`) is used by runner, change executor, SQL terminal, vault ops — reset-manager pattern coordinates with lifecycle domain. +- CLI commands in `src/cli/db/` surface all these ops — explore query shapes flow through to CLI output formatters. +- DT module (`src/core/dt/`) reads rows from transfer context — transfer and DT share the row-fetch pattern. + +## Conventions worth knowing + +- `testConnection(config, { testServerOnly: true })` connects to the dialect's system database without requiring the target DB — used in setup wizards. +- All dialects tested in integration: `tests/integration/explore/`, `tests/integration/teardown/`, `tests/integration/transfer/`. +- Transfer supports PostgreSQL, MySQL, MSSQL only (not SQLite) — `TRANSFER_SUPPORTED_DIALECTS` in `src/core/transfer/dialects/index.ts`. +- Same-server transfer skips batch loop and uses direct `INSERT … SELECT` SQL. +- Teardown skips noorm internal tables by name; `previewTeardown` returns a dry-run list without executing. diff --git a/.claude/project/signals/core-identity.md b/.claude/project/signals/core-identity.md new file mode 100644 index 0000000..ca24ae0 --- /dev/null +++ b/.claude/project/signals/core-identity.md @@ -0,0 +1,59 @@ +# core-identity + +## What it does + +Two-tier identity system: (1) audit identity — name/email for execution provenance tracking; (2) cryptographic identity — Ed25519-like keypair for config sharing and state encryption. Also owns the vault (per-database encrypted secret store), the logger (structured log output with redaction), and the SQL terminal history. + +## CLI code + +- `src/core/identity/crypto.ts` — keypair generation, `encryptForRecipient`, `decryptWithPrivateKey`, `deriveStateKey`, `encryptState`, `decryptState` +- `src/core/identity/factory.ts` — `loadExistingIdentity`; load keypair from disk +- `src/core/identity/resolver.ts` — `resolveIdentity`, `formatIdentity`, `identityToString`; audit identity resolution with caching +- `src/core/identity/storage.ts` — `saveKeyPair`, `loadPrivateKey`, `loadPublicKey`; disk persistence at `~/.noorm/` +- `src/core/identity/sync.ts` — `registerIdentity`; syncs identity record to `__noorm_identities__` table +- `src/core/identity/env.ts` — `loadIdentityFromEnv`; CI override via `NOORM_IDENTITY_*` env vars +- `src/core/identity/hash.ts` — identity hash derivation +- `src/core/identity/types.ts` — `Identity`, `CryptoIdentity`, `KnownUser`, `IdentityOptions` +- `src/core/vault/storage.ts` — vault CRUD (`initVault`, `getSecret`, `setSecret`, `removeSecret`, `listSecrets`) +- `src/core/vault/key.ts` — `generateVaultKey`, `encryptVaultKey`, `decryptVaultKey`, `encryptSecret`, `decryptSecret` +- `src/core/vault/copy.ts` — `copyVaultKey`; share vault access with another identity +- `src/core/vault/propagate.ts` — `propagateVault`; push vault data across configs +- `src/core/vault/resolve.ts` — `resolveVaultSecret`; read a secret at runtime for template context injection +- `src/core/vault/events.ts` — vault observer event types +- `src/core/logger/logger.ts` — `Logger`; structured logging with levels, rotation, redaction +- `src/core/logger/redact.ts` — pattern-based redaction of sensitive values +- `src/core/logger/formatter.ts` — log line formatting +- `src/core/logger/rotation.ts` — log file rotation +- `src/core/logger/queue.ts` — async write queue to prevent I/O blocking +- `src/core/logger/classifier.ts` — log level classification +- `src/core/sql-terminal/executor.ts` — `executeRawSql`; raw SQL execution without runner tracking +- `src/core/sql-terminal/history.ts` — `SqlHistoryManager`; persistent SQL REPL history + +## Docs + +- `docs/dev/identity.md` — cryptographic identity internals +- `docs/dev/vault.md` — vault internals +- `docs/dev/secrets.md` — secret management +- `docs/dev/logger.md` — logger internals +- `docs/dev/sql-terminal.md` — SQL terminal internals +- `docs/guide/environments/vault.md` — user guide: vault +- `docs/guide/environments/secrets.md` — user guide: secrets +- `docs/cli/identity.md` — identity CLI reference +- `docs/dev/headless.md` — headless/CI identity override docs + +## Coupling + +- Identity keypair is used by `src/core/state/manager.ts` for state encryption/decryption — identity must initialize before StateManager loads. +- Vault uses the identity hash for per-user encryption key derivation — identity + vault are tightly coupled. +- Logger uses `src/core/observer.ts` events to capture log lines from all modules. +- SQL terminal history writes to `~/.noorm/sql-history/` — path convention separate from project `.noorm/`. +- CI environment loads identity from env vars (`NOORM_IDENTITY_NAME`, `NOORM_IDENTITY_EMAIL`, `NOORM_IDENTITY_KEY`) via `loadIdentityFromEnv` — CLI init reads from keychain by default. +- `__noorm_identities__` table (defined in `src/core/shared/tables.ts`) stores registered identities — `sync.ts` writes to it. + +## Conventions worth knowing + +- Cryptographic identity stored at `~/.noorm/identity.key` (private) and `~/.noorm/identity.pub` (public). +- Vault secrets encrypted per-database with a vault key; vault key encrypted per-user with their public key. +- Logger redaction patterns are configurable; `redact.ts` uses regex matching against log line text. +- `loadIdentityFromEnv` checks `NOORM_IDENTITY_*` vars — used by `noorm ci identity` command for CI injection. +- Audit identity resolution caches result for duration of command execution. diff --git a/.claude/project/signals/core-runner.md b/.claude/project/signals/core-runner.md new file mode 100644 index 0000000..13bf354 --- /dev/null +++ b/.claude/project/signals/core-runner.md @@ -0,0 +1,45 @@ +# core-runner + +## What it does + +Executes SQL files against a database connection with checksum-based deduplication. Processes `.sql` and `.sql.tmpl` files. Template files are rendered via Eta before execution. Results are tracked in `__noorm_executions__`. Preview mode renders and returns SQL without executing. + +The template engine (`src/core/template/`) is co-owned by the runner: runner calls `processFile`/`isTemplate` to render `.sql.tmpl` files before execution. + +## CLI code + +- `src/core/runner/runner.ts` — `runBuild`, `runFile`, `runDir`, `preview`, `discoverFiles`; core execution loop +- `src/core/runner/tracker.ts` — `Tracker`; records execution results, computes `needsRun`, queries `__noorm_executions__` +- `src/core/runner/checksum.ts` — `computeChecksum`, `computeChecksumFromContent`, `computeCombinedChecksum`; SHA-based change detection +- `src/core/runner/mssql-batches.ts` — `executeSqlBody`; splits MSSQL `GO`-delimited batches before execution +- `src/core/runner/types.ts` — `RunOptions`, `RunContext`, `FileResult`, `BatchResult`, `SkipReason`, etc. +- `src/core/template/engine.ts` — Eta-based `renderTemplate`; called by runner for `.sql.tmpl` files +- `src/core/template/context.ts` — `buildContext`; assembles variables injected into templates +- `src/core/template/helpers.ts` — built-in SQL helpers (`sqlEscape`, `sqlQuote`, `generateUuid`, `isoNow`) +- `src/core/template/loaders/` — data loaders for JSON5, YAML, CSV, JS, SQL side-car files +- `src/core/template/types.ts` — `TemplateContext`, `Loader`, `LoaderRegistry` + +## Docs + +- `docs/dev/runner.md` — runner internals reference +- `docs/dev/template.md` — template engine internals +- `docs/guide/sql-files/execution.md` — user guide: file execution +- `docs/guide/sql-files/templates.md` — user guide: template syntax +- `docs/guide/sql-files/organization.md` — user guide: file layout conventions + +## Coupling + +- Runner calls `src/core/template/` (`processFile`, `isTemplate`) — template API changes affect runner's file loop. +- Runner writes to `__noorm_executions__` table defined in `src/core/shared/tables.ts`. +- Change executor (`src/core/change/executor.ts`) calls `runFile` — runner `RunOptions` changes propagate to change execution. +- MSSQL batch splitting (`mssql-batches.ts`) is only invoked for MSSQL dialect; dialect info flows in via `RunContext`. +- CLI commands in `src/cli/run/` call `runBuild`, `runFile`, `runDir`, `preview` — CLI surface reflects `RunOptions` defaults. + +## Conventions worth knowing + +- `DEFAULT_RUN_OPTIONS` defines timeout, skip-unchanged, and preview-mode defaults. +- `.sql.tmpl` extension triggers template rendering; `.sql` files are executed verbatim. +- `$helpers.ts` file in the SQL directory is loaded as helper functions for templates. +- MSSQL batches split on `GO` token (case-insensitive) per `mssql-batches.ts`. +- Checksum is SHA-256 of file content; combined checksum used for directory-level change detection. +- `SkipReason` enum: `unchanged`, `preview`, `dry-run`. diff --git a/.claude/project/signals/core-state.md b/.claude/project/signals/core-state.md new file mode 100644 index 0000000..a882b89 --- /dev/null +++ b/.claude/project/signals/core-state.md @@ -0,0 +1,62 @@ +# core-state + +## What it does + +Manages encrypted application state (configs, secrets, active config pointer), project settings (`settings.yml`), config resolution, and version migration across three layers (schema, state, settings). Also owns lifecycle (shutdown orchestration) and the project-discovery bootstrap. + +Configs are stored encrypted in `.noorm/state/state.enc` using AES-256-GCM. Settings live in `.noorm/settings.yml` (plaintext YAML). Version migration runs at startup across all three layers. + +## CLI code + +- `src/core/state/manager.ts` — `StateManager`; encrypt/decrypt state, CRUD for configs and secrets +- `src/core/state/encryption/` — AES-256-GCM encrypt/decrypt primitives +- `src/core/state/migrations.ts` — `migrateState`, `needsMigration` +- `src/core/settings/manager.ts` — `SettingsManager`; loads/saves `settings.yml`, validates against schema, stage merging +- `src/core/settings/schema.ts` — Zod schema for settings file +- `src/core/settings/rules.ts` — `ruleMatches`, `evaluateRule`, `evaluateRules`; config-based conditional overrides +- `src/core/settings/defaults.ts` — `DEFAULT_SETTINGS` +- `src/core/settings/events.ts` — settings-related observer event types +- `src/core/config/index.ts` — `makeNestedConfig`; builds config object from env at module scope (known contamination source — see CLAUDE.md) +- `src/core/config/resolver.ts` — `resolveConfig`, `SettingsProvider`; picks active config from state + settings +- `src/core/config/protection.ts` — `ConfigProtection`; hard-block rules for protected configs +- `src/core/config/schema.ts` — config schema validation +- `src/core/lifecycle/manager.ts` — `LifecycleManager`; shutdown phase orchestration, signal handlers +- `src/core/lifecycle/handlers.ts` — signal/exception handler registration +- `src/core/lifecycle/types.ts` — `ShutdownPhase`, `AppMode`, lifecycle state types +- `src/core/version/index.ts` — `VersionManager`, `checkSchemaVersion`, `migrateSchema`, `ensureSchemaVersion`, `bootstrapSchema` +- `src/core/version/schema/`, `src/core/version/state/`, `src/core/version/settings/` — per-layer migrations +- `src/core/project.ts` — `findProjectRoot`, `initProjectContext`, `isNoormProject`, `getGlobalNoormPath` +- `src/core/project-init.ts` — `initProjectContext` bootstrap: loads state, settings, lifecycle, runs version migrations +- `src/core/environment.ts` — env variable detection and normalization +- `src/core/observer.ts` — singleton `observer` (ObserverEngine from `@logosdx/observer`); event bus for all modules + +## Docs + +- `docs/dev/config.md` — config internals +- `docs/dev/config-sharing.md` — multi-user config sharing +- `docs/dev/settings.md` — settings file reference +- `docs/dev/state.md` — state file internals +- `docs/dev/version.md` — version migration internals +- `docs/dev/project-discovery.md` — project root detection +- `docs/dev/logger.md` — logger internals (uses observer) +- `docs/guide/environments/configs.md` — user guide: configs +- `docs/guide/environments/stages.md` — user guide: stages +- `docs/guide/environments/secrets.md` — user guide: secrets + +## Coupling + +- `src/core/config/index.ts:34` calls `makeNestedConfig(process.env, …)` at module scope — snapshots env at first import. Same bug was fixed for `SettingsManager` in commit `ec9ccc2`. Not yet migrated to call-time. +- Observer (`src/core/observer.ts`) is imported by virtually every core module — it is the event bus; all `observer.emit()` calls couple to TUI hooks. +- VersionManager runs migrations at startup via `project-init.ts` — schema + state + settings must all be at CURRENT_VERSIONS before app proceeds. +- StateManager uses identity key from `src/core/identity/storage.ts` for encryption — identity domain must initialize before state loads. +- LifecycleManager coordinates connection teardown — `ConnectionManager.reset()` is called in lifecycle shutdown handlers. +- RPC session layer (`src/rpc/session.ts`) reads state via StateManager for active config lookup. + +## Conventions worth knowing + +- State file path: `.noorm/state/state.enc` (configurable via `StateManagerOptions`). +- Settings file path: `.noorm/settings.yml`; `SETTINGS_FILE_PATH` constant exported from `src/core/settings/index.ts`. +- `CURRENT_VERSIONS` in `src/core/version/index.ts` is the version triple that must match after migration. +- `observer` is a module-scope singleton; `resetConnectionManager`/`resetSettingsManager`/`resetStateManager` are test-only reset points. +- Stages in settings allow per-environment config overrides; `evaluateRules` applies them at runtime. +- `initProjectContext` is the canonical startup sequence called by CLI entry and SDK `createContext`. diff --git a/.claude/project/signals/infra.md b/.claude/project/signals/infra.md new file mode 100644 index 0000000..c53e13e --- /dev/null +++ b/.claude/project/signals/infra.md @@ -0,0 +1,54 @@ +# infra + +## What it does + +Build pipeline, CI, binary release, package publishing, and reference examples. The monorepo root orchestrates two publishable packages (`@noormdev/cli`, `@noormdev/sdk`) and three examples. CI runs four isolated test groups. Binary release produces a standalone `noorm` executable via `bun build --compile`. + +## Artifacts + +- `examples/llm-memory-db-pg/` — PostgreSQL LLM memory DB example with SDK, CLI, and MCP coverage +- `examples/llm-memory-db-mssql/` — MSSQL equivalent with TVP patterns +- `examples/todo-db/` — reference CI target: soft-deletes, JSONB, TVFs, transactional SPs; used as CI stress test + +## CLI code + +- `scripts/build.mjs` — builds both `@noormdev/cli` and `@noormdev/sdk` packages via tsup +- `scripts/build-binary.mjs` — `bun build --compile` to produce standalone binary +- `scripts/Dockerfile` — Docker image for binary builds +- `scripts/install.sh` — shell installer for binary distribution +- `scripts/ralph-wiggum.sh` — release automation helper +- `tsup.cli.config.ts` — tsup config for CLI package build +- `tsup.sdk.config.ts` — tsup config for SDK package build +- `tsconfig.json` — root TypeScript config +- `tsconfig.sdk-types.json` — SDK type extraction config +- `tsconfig.test.json` — test TypeScript config +- `bunfig.toml` — Bun runtime config +- `docker-compose.yml` — local dev databases: PostgreSQL (15432), MySQL (13306), MSSQL (11433) + +## Docs + +- `.github/workflows/ci.yml` — CI: lint → typecheck → build → 4 test groups → 3 example jobs (445L) +- `.github/workflows/publish.yml` — changesets-driven publish to npm +- `.github/workflows/release-binary.yml` — binary release to GitHub Releases +- `.github/workflows/docs.yml` — VitePress docs deployment +- `docs/getting-started/installation.md` — install instructions +- `docs/.vitepress/config.mts` — VitePress site config (192L) + +## Coupling + +- Binary build (`build-binary.mjs`) must list all worker entry points explicitly — worker-bridge domain path conventions must be stable. +- CI test split (4 groups) is a workaround for `mock.module` cross-contamination + runner image regression — see CLAUDE.md for the known contamination source. +- Examples use `@noormdev/sdk` and CLI — they serve as integration smoke tests in CI. +- Changeset config (`.changeset/config.json`) references `@noormdev/cli` and `@noormdev/sdk` — only these two are publishable. +- `packages/cli/package.json` and `packages/sdk/package.json` carry the published versions and peer deps. + +## Conventions worth knowing + +- CI services: Postgres 17 on port 15432, MySQL 8.0 on port 13306, MSSQL 2022 on port 11433. +- CI runs on `ubuntu-24.04`. +- Test split: (1) utils+core(no transfer)+sdk, (2) core/transfer, (3) cli, (4) integration. All use `--serial`. +- Integration tests need live DB services — not runnable locally without `docker-compose up`. +- Examples run as separate CI jobs (`example-todo-db`, `example-llm-memory-db-pg`, `example-llm-memory-db-mssql`). +- `NOORM_TEST_PREBUILT=1` tells example test harness to skip local bootstrap and use CLI-generated DB state. +- Bun pinned to `1.3.11` in CI (see `.github/workflows/ci.yml`); local dev uses `>=1.2`. +- `@noormdev/main` (root `package.json`) is private and not published. diff --git a/.claude/project/signals/mcp-rpc.md b/.claude/project/signals/mcp-rpc.md new file mode 100644 index 0000000..6583d8e --- /dev/null +++ b/.claude/project/signals/mcp-rpc.md @@ -0,0 +1,46 @@ +# mcp-rpc + +## What it does + +MCP (Model Context Protocol) server that exposes noorm operations to AI agents. The MCP server wraps an RPC registry — commands are registered by name, then dispatched by the MCP `run_noorm_cmd` tool. A second tool `noorm_help` lists available commands. Session management tracks per-config connection state across MCP calls. + +## CLI code + +- `src/mcp/server.ts` — `createMcpServer`; builds `McpServer` with `run_noorm_cmd` and `noorm_help` tools +- `src/mcp/init.ts` — `initMcpServer`; initializes RPC registry, registers all commands, wires session +- `src/mcp/index.ts` — barrel export +- `src/rpc/registry.ts` — `RpcRegistry`; flat `Map` with register/get/list +- `src/rpc/session.ts` — `SessionManager`; tracks active Kysely connections per config name +- `src/rpc/protection.ts` — `RpcProtection`; validates commands against protected-config rules +- `src/rpc/commands/changes.ts` — RPC commands: `list_changes`, `run_change`, `revert_change`, `ff_changes` +- `src/rpc/commands/config.ts` — RPC commands: `list_configs`, `get_active_config` +- `src/rpc/commands/explore.ts` — RPC commands: `list_tables`, `describe_table`, `list_views`, `list_functions` +- `src/rpc/commands/query.ts` — RPC commands: `sql`, `run_sql` +- `src/rpc/commands/run.ts` — RPC commands: `run_file`, `run_build` +- `src/rpc/commands/session.ts` — RPC commands: `connect`, `disconnect`, `overview` +- `src/rpc/commands/index.ts` — command group barrel +- `src/rpc/types.ts` — `RpcCommand`, `RpcCommandInfo`, `RpcSession` type definitions +- `src/cli/mcp/init.ts` — `mcp init` CLI command; writes `.mcp.json` config file +- `src/cli/mcp/serve.ts` — `mcp serve` CLI command; starts MCP server over stdio + +## Docs + +- `docs/guide/automation/mcp.md` — MCP setup and usage guide +- `docs/dev/headless.md` — headless/MCP usage patterns + +## Coupling + +- MCP server wraps RPC registry — new RPC commands are automatically discoverable via `noorm_help`. +- RPC commands delegate to core modules (same as CLI) — core API changes need RPC command updates in parallel with CLI changes. +- `RpcProtection` uses `src/core/config/protection.ts` rules — config protection domain changes affect which RPC commands are allowed. +- `SessionManager` holds live Kysely connections — connection lifecycle must coordinate with `src/core/connection/manager.ts`. +- `src/cli/mcp/serve.ts` is the CLI entry; `src/mcp/init.ts` is the wiring; `src/mcp/server.ts` is the MCP layer. + +## Conventions worth knowing + +- MCP transport: stdio (JSON-RPC over stdin/stdout). +- `run_noorm_cmd` dispatches by command name string — command names are stable API surface. +- `noorm_help` lists all registered commands with descriptions and parameter schemas. +- `mcp init` writes `.mcp.json` with the `noorm mcp serve` invocation for Claude Desktop / IDE integration. +- Zod schemas on each RPC command define the `payload` shape validated at dispatch time. +- Tests in `tests/core/mcp/` cover server init and command dispatch; `tests/core/rpc/` covers registry, protection, session. diff --git a/.claude/project/signals/sdk.md b/.claude/project/signals/sdk.md new file mode 100644 index 0000000..83ac4a1 --- /dev/null +++ b/.claude/project/signals/sdk.md @@ -0,0 +1,64 @@ +# sdk + +## What it does + +Programmatic API for noorm-managed databases. `createContext` returns a `Context` object with a Kysely instance plus namespaced noorm operations (run, changes, db, dt, lock, vault, transfer, templates, secrets, utils). Published as `@noormdev/sdk` from `packages/sdk/`. + +Also includes the DT (Data Transfer format) module for typed binary serialization of database rows — separate from the `transfer` domain. DT produces `.dt` files with a universal type system. + +## Artifacts + +- `packages/sdk/package.json` — published package `@noormdev/sdk`, version `1.0.0-alpha.35` +- `packages/sdk/CHANGELOG.md` — SDK release history + +## CLI code + +- `src/sdk/index.ts` — `createContext` factory; resolves config, initializes state, returns `Context` +- `src/sdk/context.ts` — `Context` class; holds Kysely instance, all namespaced ops, connect/disconnect +- `src/sdk/namespaces/run.ts` — `RunNamespace`; wraps `runFile`, `runDir`, `runBuild`, `preview` +- `src/sdk/namespaces/changes.ts` — `ChangesNamespace`; wraps `ChangeManager` for ff/run/revert/list +- `src/sdk/namespaces/db.ts` — `DbNamespace`; explore, create, drop, teardown, truncate, reset +- `src/sdk/namespaces/dt.ts` — `DtNamespace`; export/import `.dt` files +- `src/sdk/namespaces/lock.ts` — `LockNamespace`; acquire/release/force-release/status +- `src/sdk/namespaces/vault.ts` — `VaultNamespace`; init, get/set/remove secrets, propagate, copy key +- `src/sdk/namespaces/transfer.ts` — `TransferNamespace`; wraps `transferData` +- `src/sdk/namespaces/templates.ts` — `TemplatesNamespace`; render, process file/files +- `src/sdk/namespaces/secrets.ts` — `SecretsNamespace`; stage-level secret resolution +- `src/sdk/namespaces/utils.ts` — `UtilsNamespace`; Kysely sql tag, connection ping +- `src/sdk/impersonate/scope.ts` — `ImpersonateScope`; run operations as a different identity +- `src/sdk/impersonate/dialect-strategy.ts` — per-dialect identity-column handling for impersonation +- `src/sdk/sql.ts` — `createSqlHelper`; typed SQL tag builder wrapping Kysely's `sql` +- `src/sdk/tvp.ts` — `createTvp`, `TvpBuilder`; MSSQL table-valued parameter construction +- `src/sdk/noorm-ops.ts` — `NoormOps`; assembled namespace object attached to `ctx.noorm` +- `src/sdk/guards.ts` — `checkRequireTest`; prevents SDK use in production without explicit opt-in +- `src/sdk/types.ts` — `CreateContextOptions`, `ContextConfig`, SDK-level types +- `src/core/dt/index.ts` — DT module: `exportTable`, `importTable`, serialize/deserialize, versioning, crypto +- `src/core/dt/dialects/` — per-dialect type mapping for DT +- `src/core/dt/type-map.ts` — `SimpleType` vs `EncodedType` classification; `text` type uses gz64 compression +- `src/core/dt/schema.ts` — DT file schema validation + +## Docs + +- `docs/dev/sdk.md` — SDK internals reference (1094L) +- `docs/reference/sdk.md` — public SDK API reference (1426L) +- `docs/dev/transfer.md` — DT transfer internals +- `docs/getting-started/building-your-sdk.md` — getting-started guide for SDK users +- `skills/noorm/references/sdk.md` — skill reference for SDK usage patterns + +## Coupling + +- `createContext` calls `initProjectContext` from `src/core/project-init.ts` — same startup sequence as CLI. +- All namespaces delegate to core modules — any core API change propagates to the namespace wrappers. +- DT module (`src/core/dt/`) is co-owned: SDK exposes it via `ctx.noorm.dt`, but it is also used standalone by `src/cli/db/transfer.ts`. +- Impersonation (`src/sdk/impersonate/`) requires the identity domain keypair loaded. +- TVP (`src/sdk/tvp.ts`) is MSSQL-only — dialect guard at construction time. +- Published package build defined in `tsup.sdk.config.ts`; types extracted via `@microsoft/api-extractor` + `dts-bundle-generator`. + +## Conventions worth knowing + +- `createContext` is the only public entry point — do not instantiate `Context` directly. +- `ctx.kysely` is a raw Kysely instance for type-safe queries. +- `ctx.noorm` is the noorm namespace (changes, run, db, dt, lock, vault, transfer, templates, secrets, utils). +- DT `text` type uses gz64 compression for large TEXT columns; `string` is for short VARCHAR/CHAR. +- `checkRequireTest` throws `RequireTestError` if `options.requireTest === true` but `config.isTest === false` — prevents accidental production use of test helpers. +- Integration tests in `tests/sdk/` and `tests/integration/sdk/` cover TVF and TVP patterns. diff --git a/.claude/project/signals/tui.md b/.claude/project/signals/tui.md new file mode 100644 index 0000000..9829964 --- /dev/null +++ b/.claude/project/signals/tui.md @@ -0,0 +1,62 @@ +# tui + +## What it does + +Ink/React-based terminal UI launched by `noorm ui`. Full-screen interactive interface with a home screen, keyboard-driven navigation, and per-domain screens for all noorm operations. Focus management, keyboard routing, and observer-based state updates are core TUI concerns. + +## CLI code + +- `src/tui/app.tsx` — root component; mounts `AppContext`, `ObserverContext`, focus/keyboard providers +- `src/tui/app-context.tsx` — `AppContext` (1196L); global state: active config, settings, lock status, update check, screen routing +- `src/tui/screens.tsx` — `ScreenRegistry`; maps screen IDs to components (664L) +- `src/tui/screens/home.tsx` — home screen; keyboard shortcuts for all domains (635L) +- `src/tui/router.tsx` — `Router`; screen stack push/pop navigation +- `src/tui/focus.tsx` — `FocusManager`; focus stack for nested interactive components +- `src/tui/keyboard.tsx` — `KeyboardManager`; global key event routing with priority stacking (401L) +- `src/tui/observer-context.ts` — `ObserverContext`; provides observer singleton to React tree +- `src/tui/shutdown.tsx` — graceful shutdown sequence with progress display +- `src/tui/types.ts` — TUI type contracts (Screen, ScreenProps, etc.) (470L) +- `src/tui/components/` — shared UI components: dialogs, feedback, forms, layout, lists, overlays, secrets, status, terminal +- `src/tui/hooks/` — 14 hooks: `useObserver`, `useConnection`, `useChangeProgress`, `useRunProgress`, `useTransferProgress`, `useLockStatus`, `useVaultConnection`, `useVaultSecretKeys`, `useSettingsOperation`, `useUpdateChecker`, `useSecretSource`, `useAsyncEffect`, `useLoadGuard` +- `src/tui/providers/ConnectionProvider.tsx` — `ConnectionProvider`; DB connection lifecycle for TUI screens +- `src/tui/utils/` — 12 utilities: path resolution, connection helpers, config validation, clipboard, change-loader +- `src/tui/screens/change/` — 12 change-related screens +- `src/tui/screens/config/` — 11 config screens +- `src/tui/screens/db/` — 11 DB screens + 1 subdir +- `src/tui/screens/debug/` — 4 debug screens +- `src/tui/screens/identity/` — 6 identity screens +- `src/tui/screens/init/` — 4 init wizard screens +- `src/tui/screens/lock/` — 6 lock screens +- `src/tui/screens/run/` — 7 run screens +- `src/tui/screens/secret/` — 4 secret screens +- `src/tui/screens/settings/` — 17 settings screens +- `src/tui/screens/vault/` — 5 vault screens +- `src/hooks/observer.ts` — `useObserver` hook (non-tui hooks barrel) +- `src/tui/screens/UpdateScreen.tsx` — update available prompt +- `src/tui/screens/MoreScreen.tsx` — extended help screen + +## Docs + +- `docs/tui.md` — TUI user guide (407L) +- `docs/dev/ink-cheatsheet.md` — Ink layout reference for developers (1427L) +- `docs/dev/ink-testing-library-cheatsheet.md` — testing cheatsheet (737L) +- `.claude/rules/tui-development.md` — TUI development rules (focus system, UI patterns, layout) +- `.claude/skills/noorm-design/` — design system assets and colors + +## Coupling + +- All TUI screens consume observer events from `src/core/observer.ts` — observer event shape changes break TUI hooks. +- `useConnection` and `ConnectionProvider` use `src/core/connection/manager.ts` — connection manager resets affect TUI session. +- `src/tui/utils/paths.ts` uses `settings.paths.sql`/`settings.paths.changes` from SettingsManager — not per-config paths (see project CLAUDE.md). +- Lifecycle shutdown (`src/core/lifecycle/`) drives `src/tui/shutdown.tsx` — shutdown phase changes affect TUI teardown. +- TUI is launched by `src/cli/ui.ts` — CLI dependency. + +## Conventions worth knowing + +- Tests use `ink-testing-library`: `render()` → `await new Promise(r => setTimeout(r, 50))` → `stdin.write()` → `lastFrame()` → `unmount()`. +- Key codes for tests: Tab=`\t`, Shift+Tab=`\x1b[Z`, Down=`\x1b[B`, Up=`\x1b[A`, Enter=`\r`, Esc=`\x1b`. +- Focus stack initialized in `useEffect` — must wait 50ms after render before sending input in tests. +- `numberNav` prop on `SelectList` enables 1-9 quick selection. +- Home screen hotkeys: `c`=config, `g`=changes, `r`=run, `d`=db, `l`=lock, `s`=settings, `k`=secrets, `i`=identity, `q`=quit. +- Sub-screen hotkeys: `a`=add, `e`=edit, `d`=delete, `x`=export, `i`=import, `u`=use/activate, `v`=validate, `k`=secrets. +- `Shift+L` toggles log viewer overlay globally. diff --git a/.claude/project/signals/worker-bridge.md b/.claude/project/signals/worker-bridge.md new file mode 100644 index 0000000..973edc0 --- /dev/null +++ b/.claude/project/signals/worker-bridge.md @@ -0,0 +1,36 @@ +# worker-bridge + +## What it does + +Hub-and-spoke worker thread infrastructure. `WorkerBridge` (an `ObserverRelay` subclass) owns message routing between main thread and worker threads. `WorkerPool` provides round-robin dispatch to N workers. `OrderBuffer` reassembles index-ordered responses. Used for CPU-bound DT serialization/deserialization and for the persistent DB connection worker. + +## CLI code + +- `src/core/worker-bridge/bridge.ts` — `WorkerBridge`; ObserverRelay subclass, message correlation, error propagation +- `src/core/worker-bridge/pool.ts` — `WorkerPool`; round-robin N-worker dispatch +- `src/core/worker-bridge/order-buffer.ts` — `OrderBuffer`; index-ordered response reassembly +- `src/core/worker-bridge/paths.ts` — `resolveWorker`; path resolution for dev/compiled contexts +- `src/core/worker-bridge/types.ts` — `WireMessage`, `Correlated`, event contract types +- `src/workers/connection.ts` — persistent DB worker entry point; owns Kysely instance, handles all DB ops +- `src/workers/compute.ts` — stateless compute worker entry point; serialize/deserialize for DT pipeline + +## Docs + +- `docs/dev/README.md` — worker bridge architecture overview (section in monorepo dev guide) + +## Coupling + +- `resolveWorker` is called wherever a worker is spawned — never hardcode worker paths. +- `WorkerBridge` extends `ObserverRelay` from `@logosdx/observer` — observer domain is a dependency. +- DT module (`src/core/dt/`) spawns compute workers via `WorkerPool` — DT changes may require worker message-type updates. +- Connection worker (`src/workers/connection.ts`) holds the Kysely instance used by runner and change executor in worker contexts — worker restart resets all in-flight operations. +- Bun `--compile` binary path resolution: `src/workers/compute.ts` → `workers/compute.js` in `$bunfs` — the `IS_COMPILED` guard in `paths.ts` handles this. + +## Conventions worth knowing + +- `IS_COMPILED = import.meta.url.includes('$bunfs')` detects compiled binary context. +- In compiled binary: `new URL('./workers/${name}.js', import.meta.url)` resolves against `$bunfs`. +- In dev/dist: `resolve(WORKER_DIR, '${name}.js')` resolves to absolute path. +- `noorm dev test-workers` runs 5 worker thread tests across all execution contexts. +- `WireMessage` carries a correlation ID for request-response matching across thread boundary. +- `OrderBuffer` is needed when worker results arrive out-of-order (e.g., concurrent compute workers). diff --git a/.gitignore b/.gitignore index fad099f..833808b 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,5 @@ worktrees/ skills/*/workspace/ skills/*/evals/ graphify-out/ +.claude/.scratchpad/ +.claude/project/.deterministic-signals.prev.md diff --git a/.signalsignore b/.signalsignore new file mode 100644 index 0000000..4aef838 --- /dev/null +++ b/.signalsignore @@ -0,0 +1,17 @@ +# .signalsignore +# +# Augments .gitignore for the signals scan. Gitignored paths are +# already excluded automatically. This file is for TRACKED paths +# you want excluded from signals or flagged as generated. +# +# Two modes: +# plain glob → fully excluded from scan (not in tree at all) +# + prefix → appears in tree with [generated] flag (inferrer skips) +# +# One glob per line. Blank lines and # comments ignored. +# +# Examples: +# third_party/** ← committed but excluded from signals +# fixtures/** ← committed but excluded from signals +# +dist/** ← in tree, flagged [generated] +# +*.pb.go ← in tree, flagged [generated] diff --git a/CLAUDE.md b/CLAUDE.md index 69d0880..418f8d5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -190,3 +190,12 @@ Consistent hotkey conventions across all screens: | `Shift+L` | Toggle log viewer overlay | Use `numberNav` prop on `SelectList` for 1-9 quick selection in lists. + + + +## Project signals (auto-loaded) + + +@.claude/project/signals.md + + diff --git a/docs/design/.gitkeep b/docs/design/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/spec/.gitkeep b/docs/spec/.gitkeep new file mode 100644 index 0000000..e69de29 From 8fa90e0a9573da8beaf16a9ffb4a3f1fa2eaec47 Mon Sep 17 00:00:00 2001 From: Danilo Alonso Date: Sat, 6 Jun 2026 10:53:31 -0400 Subject: [PATCH 3/7] fix(teardown,sdk): MSSQL CHECK-constraint teardown + clean db.reset() Two related MSSQL teardown fixes plus cross-dialect verification. 1) teardown drops MSSQL CHECK constraints before functions (#36) A scalar UDF referenced by a CHECK constraint can't be dropped while its table exists (MSSQL error 3729). Functions are dropped before tables to satisfy schema-bound dependents, so the CHECK dependency is severed first via a new MSSQL-only dropCheckConstraints() op that drops all user-schema CHECK constraints (excluding the noorm schema) ahead of the function drops. PostgreSQL is unaffected (dropFunction uses CASCADE); MySQL forbids UDFs in CHECK constraints; SQLite has no stored functions. A PG integration test locks in the CASCADE behavior so a refactor can't reintroduce the break. 2) db.reset() ignores preserveTables for a clean rebuild db.reset() (teardown + build) honored settings.teardown.preserveTables, so preserved reference tables survived teardown and collided with the build's CREATE TABLE, aborting the rebuild. reset() now runs a full teardown that ignores preserveTables; the setting still applies to teardown() and truncate(). Verified end to end: db.reset() now fully and idempotently rebuilds the 38-table llm-memory-db-mssql example schema. Changesets added for both @noormdev/sdk and @noormdev/cli (noorm db teardown and noorm db reset route through the same core). Closes #36 --- .../reset-ignores-preserve-tables-cli.md | 13 ++ .changeset/reset-ignores-preserve-tables.md | 13 ++ ...teardown-mssql-check-constraint-udf-cli.md | 13 ++ .../teardown-mssql-check-constraint-udf.md | 13 ++ .claude/project/followups/CLOSED.md | 1 + .claude/project/followups/INDEX.md | 6 +- .../tests/helpers/test-context.ts | 9 +- src/core/teardown/dialects/mssql.ts | 20 +++ src/core/teardown/operations.ts | 11 ++ src/core/teardown/types.ts | 11 ++ src/sdk/namespaces/db.ts | 12 +- tests/core/teardown/operations.test.ts | 90 ++++++++++++++ tests/integration/sdk/db-reset.test.ts | 117 ++++++++++++++++++ tests/integration/teardown/mssql.test.ts | 43 +++++++ tests/integration/teardown/postgres.test.ts | 52 +++++++- 15 files changed, 415 insertions(+), 9 deletions(-) create mode 100644 .changeset/reset-ignores-preserve-tables-cli.md create mode 100644 .changeset/reset-ignores-preserve-tables.md create mode 100644 .changeset/teardown-mssql-check-constraint-udf-cli.md create mode 100644 .changeset/teardown-mssql-check-constraint-udf.md create mode 100644 .claude/project/followups/CLOSED.md create mode 100644 tests/integration/sdk/db-reset.test.ts diff --git a/.changeset/reset-ignores-preserve-tables-cli.md b/.changeset/reset-ignores-preserve-tables-cli.md new file mode 100644 index 0000000..18d4a85 --- /dev/null +++ b/.changeset/reset-ignores-preserve-tables-cli.md @@ -0,0 +1,13 @@ +--- +"@noormdev/cli": patch +--- + +fix(cli): noorm db reset rebuilds cleanly regardless of preserveTables + +`noorm db reset` (teardown + build) honored `settings.teardown.preserveTables` +during the teardown phase, so preserved tables (e.g. reference vocabulary kept +for the `noorm db truncate` workflow) survived and then collided with the +build's `CREATE TABLE`, aborting the rebuild and leaving a partial schema. +`noorm db reset` now performs a full teardown that ignores `preserveTables` — +a full rebuild starts from nothing. `noorm db teardown` and `noorm db truncate` +still honor the setting. diff --git a/.changeset/reset-ignores-preserve-tables.md b/.changeset/reset-ignores-preserve-tables.md new file mode 100644 index 0000000..89bd51c --- /dev/null +++ b/.changeset/reset-ignores-preserve-tables.md @@ -0,0 +1,13 @@ +--- +"@noormdev/sdk": patch +--- + +fix(sdk): db.reset() no longer preserves tables before rebuilding + +`db.reset()` (teardown + build) honored `settings.teardown.preserveTables`, +so any preserved table (e.g. reference vocabulary kept for the per-test +`truncate()` workflow) survived the teardown and then collided with the +build's `CREATE TABLE`, aborting the rebuild and leaving a partial schema. +reset() now performs a full teardown that ignores `preserveTables` — a full +rebuild starts from nothing. `preserveTables` still applies to standalone +`teardown()` and `truncate()`. diff --git a/.changeset/teardown-mssql-check-constraint-udf-cli.md b/.changeset/teardown-mssql-check-constraint-udf-cli.md new file mode 100644 index 0000000..3277a4c --- /dev/null +++ b/.changeset/teardown-mssql-check-constraint-udf-cli.md @@ -0,0 +1,13 @@ +--- +"@noormdev/cli": patch +--- + +fix(cli): noorm db teardown drops MSSQL CHECK constraints before functions + +`noorm db teardown` (and `noorm db reset`) aborted with MSSQL error 3729 on +any schema where a scalar UDF is referenced by a CHECK constraint (the +canonical base/subtype "IsType" pattern). Functions are dropped before tables +to satisfy schema-bound dependents, which left the CHECK-constraint dependency +intact. Teardown now severs it first by dropping all user-schema CHECK +constraints (excluding the `noorm` schema) ahead of the function drops, so +both schema-bound functions and CHECK-backed functions tear down cleanly. diff --git a/.changeset/teardown-mssql-check-constraint-udf.md b/.changeset/teardown-mssql-check-constraint-udf.md new file mode 100644 index 0000000..bab2334 --- /dev/null +++ b/.changeset/teardown-mssql-check-constraint-udf.md @@ -0,0 +1,13 @@ +--- +"@noormdev/sdk": patch +--- + +fix(teardown): drop MSSQL CHECK constraints before functions + +`teardown()` aborted with MSSQL error 3729 on any schema where a scalar UDF +is referenced by a CHECK constraint (the canonical base/subtype "IsType" +pattern). Functions are dropped before tables to satisfy schema-bound +dependents, but that left the CHECK-constraint dependency intact. Teardown +now severs it first by dropping all user-schema CHECK constraints (excluding +the `noorm` schema) ahead of the function drops, so both schema-bound +functions and CHECK-backed functions tear down cleanly. diff --git a/.claude/project/followups/CLOSED.md b/.claude/project/followups/CLOSED.md new file mode 100644 index 0000000..f86ef9b --- /dev/null +++ b/.claude/project/followups/CLOSED.md @@ -0,0 +1 @@ +- 2026-06-06 mssql-build-non-idempotent-create-type — "MSSQL build non-idempotent: bare CREATE TYPE blocks db.reset() rebuild" — *(closed 2026-06-06)* diff --git a/.claude/project/followups/INDEX.md b/.claude/project/followups/INDEX.md index e11bfa4..854cf5a 100644 --- a/.claude/project/followups/INDEX.md +++ b/.claude/project/followups/INDEX.md @@ -2,7 +2,7 @@ Auto-generated by `atomic followups render`. Do not edit. -Open: 2 • Stale: 0 • Last rendered: 2026-06-01 +Open: 2 • Stale: 0 • Last rendered: 2026-06-06 ## 📋 plans (0) @@ -10,11 +10,11 @@ Open: 2 • Stale: 0 • Last rendered: 2026-06-01 ## 🟡 risks (1) -- [config-module-scope-env-snapshot](config-module-scope-env-snapshot.md) — Move makeNestedConfig to call-time in config module (0d) +- [config-module-scope-env-snapshot](config-module-scope-env-snapshot.md) — Move makeNestedConfig to call-time in config module (5d) ## 🔵 nits (1) -- [debug-process-test-no-assertions](debug-process-test-no-assertions.md) — debug-process.test.ts has no assertions (passes unconditionally) (0d) +- [debug-process-test-no-assertions](debug-process-test-no-assertions.md) — debug-process.test.ts has no assertions (passes unconditionally) (5d) ## ❓ questions (0) diff --git a/examples/llm-memory-db-mssql/tests/helpers/test-context.ts b/examples/llm-memory-db-mssql/tests/helpers/test-context.ts index ddf8fae..131f142 100644 --- a/examples/llm-memory-db-mssql/tests/helpers/test-context.ts +++ b/examples/llm-memory-db-mssql/tests/helpers/test-context.ts @@ -13,10 +13,11 @@ * pools concurrently call `db.truncate()` (sp_MSforeach_worker fights * for schema locks across connections). * - * Why not `db.reset()`: noorm's teardown drops tables before - * functions, but schema-bound `fn_MemoryConfidence` etc. reference - * `Memory` and `Task` and block the table drop. Using `truncate()` - * sidesteps this and is also faster (no full DDL rebuild per file). + * Why not `db.reset()`: `db.reset()` works correctly now (teardown #36 + * fixed the drop ordering, and reset() no longer preserves the reference + * vocabulary so the rebuild doesn't collide). We build once + `truncate()` + * per test purely for speed — a full DDL rebuild on every `beforeEach` + * would be far slower than wiping rows. * * @example * let ctx: Awaited>['ctx']; diff --git a/src/core/teardown/dialects/mssql.ts b/src/core/teardown/dialects/mssql.ts index f804e62..b881676 100644 --- a/src/core/teardown/dialects/mssql.ts +++ b/src/core/teardown/dialects/mssql.ts @@ -116,4 +116,24 @@ export const mssqlTeardownOperations: TeardownDialectOperations = { }, + dropCheckConstraints(): string { + + // A scalar UDF referenced by a CHECK constraint can't be dropped while + // the referencing table exists (error 3729). We drop functions before + // tables (for schema-bound deps), so sever the CHECK dependency first. + // Built dynamically because constraint names aren't known up front; + // the `noorm` schema is excluded so internal tables stay intact. + return [ + "DECLARE @noorm_drop_checks NVARCHAR(MAX) = N'';", + 'SELECT @noorm_drop_checks += N\'ALTER TABLE \' ' + + '+ QUOTENAME(SCHEMA_NAME(t.schema_id)) + N\'.\' + QUOTENAME(t.name) ' + + '+ N\' DROP CONSTRAINT \' + QUOTENAME(cc.name) + N\';\'', + 'FROM sys.check_constraints cc ' + + 'INNER JOIN sys.tables t ON cc.parent_object_id = t.object_id', + "WHERE SCHEMA_NAME(t.schema_id) <> 'noorm';", + 'EXEC sp_executesql @noorm_drop_checks;', + ].join('\n'); + + }, + }; diff --git a/src/core/teardown/operations.ts b/src/core/teardown/operations.ts index f547d84..970017c 100644 --- a/src/core/teardown/operations.ts +++ b/src/core/teardown/operations.ts @@ -307,6 +307,17 @@ export async function teardownSchema( } + // 1b. Drop CHECK constraints before functions (unless keepFunctions). + // A scalar UDF referenced by a CHECK constraint can't be dropped while + // its table exists (MSSQL error 3729). Functions are dropped before + // tables below for schema-bound deps, so sever the CHECK dependency + // first. Only MSSQL provides this op; other dialects don't need it. + if (!options.keepFunctions && ops.dropCheckConstraints) { + + statements.push(ops.dropCheckConstraints()); + + } + // 2. Drop procedures (unless keepProcedures) — drop early so they // don't hold references to functions/views/tables we drop below if (!options.keepProcedures) { diff --git a/src/core/teardown/types.ts b/src/core/teardown/types.ts index 6c5bdfd..14feb86 100644 --- a/src/core/teardown/types.ts +++ b/src/core/teardown/types.ts @@ -268,4 +268,15 @@ export interface TeardownDialectOperations { */ dropForeignKey(constraintName: string, tableName: string, schema?: string): string; + /** + * Generate SQL to drop every CHECK constraint in user schemas. + * + * Optional, MSSQL-only. A scalar UDF referenced by a CHECK constraint + * cannot be dropped while the referencing table still exists (MSSQL + * error 3729). Functions are dropped before tables to satisfy + * schema-bound dependents, so the CHECK dependency must be severed + * first. Other dialects don't need this and omit the method. + */ + dropCheckConstraints?(): string; + } diff --git a/src/sdk/namespaces/db.ts b/src/sdk/namespaces/db.ts index 3fb84af..c704b6f 100644 --- a/src/sdk/namespaces/db.ts +++ b/src/sdk/namespaces/db.ts @@ -319,7 +319,17 @@ export class DbNamespace { checkProtectedConfig(this.#state.config, 'reset'); - await this.teardown(); + // Full teardown — deliberately does NOT honor preserveTables. + // reset() rebuilds the entire schema from sql/, so any table left + // standing (e.g. reference vocabulary kept in preserveTables for the + // truncate workflow) would collide with the build's CREATE TABLE and + // abort the rebuild. preserveTables stays in effect for standalone + // teardown() and truncate(); a full rebuild starts from nothing. + await teardownSchema(this.#kysely, this.#dialect, { + configName: this.#state.config.name, + executedBy: formatIdentity(this.#state.identity), + postScript: this.#state.settings.teardown?.postScript, + }); if (this.#buildFn) { diff --git a/tests/core/teardown/operations.test.ts b/tests/core/teardown/operations.test.ts index b09b3f0..92ba8aa 100644 --- a/tests/core/teardown/operations.test.ts +++ b/tests/core/teardown/operations.test.ts @@ -607,3 +607,93 @@ describe('teardown: teardownSchema drop order', () => { }); }); + +// ───────────────────────────────────────────────────────────── +// teardownSchema — CHECK constraints vs function drops (issue #36) +// A scalar UDF referenced by a CHECK constraint cannot be dropped +// while the referencing table exists (MSSQL error 3729). Functions +// are dropped before tables (for schema-bound deps), so the CHECK +// dependency must be severed first by dropping CHECK constraints +// ahead of the function drops. +// ───────────────────────────────────────────────────────────── + +describe('teardown: teardownSchema mssql CHECK-constraint dependency (issue #36)', () => { + + const schemaWithCheckBackedFunction = { + tables: [ + { table_name: 'Thing', schema_name: 'dbo', column_count: 2, row_count: 0 }, + ], + functions: [ + { func_name: 'IsPositive_fn', schema_name: 'dbo', param_count: 1, return_type: 'scalar' }, + ], + }; + + it('drops CHECK constraints before any function drop', async () => { + + const db = createMockKyselyForTeardown(schemaWithCheckBackedFunction); + + const result = await teardownSchema(db, 'mssql', { dryRun: true }); + const stmts = result.statements.filter((s) => !s.startsWith('--')); + + const checkIdx = stmts.findIndex((s) => s.includes('sys.check_constraints')); + const funcIdx = stmts.findIndex((s) => s.includes('DROP FUNCTION')); + + expect(checkIdx).toBeGreaterThanOrEqual(0); + expect(funcIdx).toBeGreaterThan(checkIdx); + + }); + + it('drops CHECK constraints before tables', async () => { + + const db = createMockKyselyForTeardown(schemaWithCheckBackedFunction); + + const result = await teardownSchema(db, 'mssql', { dryRun: true }); + const stmts = result.statements.filter((s) => !s.startsWith('--')); + + const checkIdx = stmts.findIndex((s) => s.includes('sys.check_constraints')); + const tableIdx = stmts.findIndex((s) => s.includes('DROP TABLE')); + + expect(checkIdx).toBeGreaterThanOrEqual(0); + expect(tableIdx).toBeGreaterThan(checkIdx); + + }); + + it('preserves noorm-schema CHECK constraints (excludes noorm schema)', async () => { + + const db = createMockKyselyForTeardown(schemaWithCheckBackedFunction); + + const result = await teardownSchema(db, 'mssql', { dryRun: true }); + const checkStmt = result.statements.find((s) => s.includes('sys.check_constraints')); + + expect(checkStmt).toBeDefined(); + expect(checkStmt).toContain("<> 'noorm'"); + + }); + + it('does not drop CHECK constraints when functions are kept', async () => { + + const db = createMockKyselyForTeardown(schemaWithCheckBackedFunction); + + const result = await teardownSchema(db, 'mssql', { dryRun: true, keepFunctions: true }); + const checkStmt = result.statements.find((s) => s.includes('sys.check_constraints')); + + expect(checkStmt).toBeUndefined(); + + }); + + it('does not emit CHECK-constraint drops for non-mssql dialects', async () => { + + const db = createMockKyselyForTeardown({ + tables: [ + { table_name: 'thing', schema_name: 'public', column_count: 2, row_count: 0 }, + ], + }); + + const result = await teardownSchema(db, 'postgres', { dryRun: true }); + const checkStmt = result.statements.find((s) => s.includes('check_constraints')); + + expect(checkStmt).toBeUndefined(); + + }); + +}); diff --git a/tests/integration/sdk/db-reset.test.ts b/tests/integration/sdk/db-reset.test.ts new file mode 100644 index 0000000..7909d5c --- /dev/null +++ b/tests/integration/sdk/db-reset.test.ts @@ -0,0 +1,117 @@ +/** + * Integration tests for DbNamespace.reset() vs teardown.preserveTables. + * + * reset() is a full rebuild (teardown + forced build from sql/). It must + * NOT honor `settings.teardown.preserveTables`: any table left standing + * would collide with the build's CREATE TABLE and abort the rebuild + * (regression — the llm-memory-db-mssql example hit this, because its + * reference vocabulary tables are in preserveTables for the per-test + * truncate workflow). Standalone teardown() must still preserve them. + */ +import { describe, it, expect, beforeAll, afterAll } from 'bun:test'; +import { sql } from 'kysely'; + +import { DbNamespace } from '../../../src/sdk/namespaces/db.js'; +import { createTestConnection, skipIfNoContainer } from '../../utils/db.js'; + +import type { ContextState } from '../../../src/sdk/state.js'; +import type { ConnectionResult } from '../../../src/core/connection/index.js'; + +describe('integration: sdk DbNamespace reset vs preserveTables', () => { + + let conn: ConnectionResult; + + beforeAll(async () => { + + await skipIfNoContainer('postgres'); + conn = await createTestConnection('postgres'); + + }); + + afterAll(async () => { + + if (conn) { + + await sql.raw('DROP TABLE IF EXISTS ev_keep CASCADE').execute(conn.db).catch(() => {}); + await sql.raw('DROP TABLE IF EXISTS ev_drop CASCADE').execute(conn.db).catch(() => {}); + await conn.destroy(); + + } + + }); + + function makeState(): ContextState { + + return { + connection: conn, + config: { + name: 'test', + type: 'local', + isTest: true, + protected: false, + connection: { dialect: 'postgres', database: 'noorm_test' }, + }, + settings: { teardown: { preserveTables: ['ev_keep'] } }, + identity: { name: 'tester', source: 'system' }, + options: {}, + projectRoot: '/tmp', + changeManager: null, + }; + + } + + async function createMarkers(): Promise { + + await sql.raw('DROP TABLE IF EXISTS ev_keep CASCADE').execute(conn.db); + await sql.raw('DROP TABLE IF EXISTS ev_drop CASCADE').execute(conn.db); + await sql.raw('CREATE TABLE ev_keep (id int)').execute(conn.db); + await sql.raw('CREATE TABLE ev_drop (id int)').execute(conn.db); + + } + + async function exists(table: string): Promise { + + const r = await sql<{ n: number }>` + SELECT COUNT(*)::int AS n FROM information_schema.tables + WHERE table_schema = 'public' AND table_name = ${table} + `.execute(conn.db); + + return (r.rows[0]?.n ?? 0) > 0; + + } + + it('teardown() honors settings.teardown.preserveTables', async () => { + + await createMarkers(); + + const db = new DbNamespace(makeState()); + await db.teardown(); + + expect(await exists('ev_keep')).toBe(true); + expect(await exists('ev_drop')).toBe(false); + + }); + + it('reset() ignores preserveTables and drops everything before rebuilding', async () => { + + await createMarkers(); + + const db = new DbNamespace(makeState()); + + let buildForced: boolean | undefined; + db._buildFn = async (opts?: { force?: boolean }) => { + + buildForced = opts?.force; + + }; + + await db.reset(); + + // The build runs (forced) on a fully cleared schema — no collision. + expect(buildForced).toBe(true); + expect(await exists('ev_keep')).toBe(false); + expect(await exists('ev_drop')).toBe(false); + + }); + +}); diff --git a/tests/integration/teardown/mssql.test.ts b/tests/integration/teardown/mssql.test.ts index a0297bf..6f089d1 100644 --- a/tests/integration/teardown/mssql.test.ts +++ b/tests/integration/teardown/mssql.test.ts @@ -559,6 +559,49 @@ describe('integration: mssql teardown', () => { }); + describe('teardownSchema with CHECK-constraint UDF (issue #36)', () => { + + beforeEach(async () => { + + await teardownTestSchema(db, 'mssql').catch(() => {}); + + }); + + it('drops a table whose CHECK constraint references a scalar UDF without error 3729', async () => { + + // Scalar UDF referenced by a CHECK constraint — the canonical + // base/subtype "IsType" pattern. Pre-fix, teardown dropped the + // function before the table and failed with: + // Cannot DROP FUNCTION 'dbo.IsPositive_fn' because it is being + // referenced by object 'Thing_IsPositive'. (3729) + await sql.raw(` + CREATE FUNCTION dbo.IsPositive_fn(@n INT) RETURNS BIT + AS BEGIN RETURN IIF(@n > 0, 1, 0) END + `).execute(db); + + await sql.raw(` + CREATE TABLE dbo.Thing ( + Id INT PRIMARY KEY, + Qty INT NOT NULL, + CONSTRAINT Thing_IsPositive CHECK (dbo.IsPositive_fn(Qty) = 1) + ) + `).execute(db); + + const result = await teardownSchema(db, 'mssql'); + + expect(result.dropped.tables).toContain('Thing'); + expect(result.dropped.functions).toContain('IsPositive_fn'); + + // Both objects should be gone. + const tables = await fetchList(db, 'mssql', 'tables'); + expect(tables.map((t) => t.name)).not.toContain('Thing'); + const functions = await fetchList(db, 'mssql', 'functions'); + expect(functions.map((f) => f.name)).not.toContain('IsPositive_fn'); + + }); + + }); + describe('truncateData deadlock canary (M-6)', () => { beforeEach(async () => { diff --git a/tests/integration/teardown/postgres.test.ts b/tests/integration/teardown/postgres.test.ts index fdcf91f..2e7e781 100644 --- a/tests/integration/teardown/postgres.test.ts +++ b/tests/integration/teardown/postgres.test.ts @@ -4,7 +4,7 @@ * Tests truncateData, teardownSchema, and previewTeardown against a real PostgreSQL database. * Requires docker-compose.test.yml containers to be running. */ -import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'bun:test'; +import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from 'bun:test'; import { sql } from 'kysely'; import type { Kysely } from 'kysely'; @@ -322,6 +322,56 @@ describe('integration: postgres teardown', () => { }); + // ───────────────────────────────────────────────────────────── + // CHECK-constraint UDF teardown (issue #36 cross-dialect guard) + // MSSQL needs an explicit CHECK-constraint pre-drop because it has + // no DROP FUNCTION ... CASCADE. Postgres is immune *only because* + // dropFunction emits CASCADE, which removes the dependent CHECK + // constraint. This test locks in that behavior so a future refactor + // that drops CASCADE doesn't silently reintroduce the bug on PG. + // ───────────────────────────────────────────────────────────── + + describe('teardownSchema with CHECK-constraint UDF (issue #36)', () => { + + afterEach(async () => { + + await sql.raw('DROP TABLE IF EXISTS ev_thing CASCADE').execute(db).catch(() => {}); + await sql.raw('DROP FUNCTION IF EXISTS ev_is_positive(int) CASCADE').execute(db).catch(() => {}); + + }); + + it('emits DROP FUNCTION ... CASCADE so a CHECK-referenced UDF tears down', async () => { + + const result = await teardownSchema(db, 'postgres', { dryRun: true }); + + const fnDrop = result.statements.find((s) => s.includes('DROP FUNCTION')); + expect(fnDrop).toBeDefined(); + expect(fnDrop).toContain('CASCADE'); + + }); + + it('drops a table whose CHECK constraint references a scalar UDF without error', async () => { + + await sql.raw( + 'CREATE FUNCTION ev_is_positive(n int) RETURNS boolean ' + + 'AS $$ SELECT n > 0 $$ LANGUAGE sql IMMUTABLE', + ).execute(db); + await sql.raw( + 'CREATE TABLE ev_thing (id int PRIMARY KEY, qty int NOT NULL ' + + 'CHECK (ev_is_positive(qty)))', + ).execute(db); + + const result = await teardownSchema(db, 'postgres'); + + expect(result.dropped.tables).toContain('ev_thing'); + + const functions = await fetchList(db, 'postgres', 'functions'); + expect(functions.map((f) => f.name)).not.toContain('ev_is_positive'); + + }); + + }); + describe('previewTeardown', () => { it('should return preview without executing', async () => { From 82292bd2930ee8501074afcc3236be43a1555675 Mon Sep 17 00:00:00 2001 From: Danilo Alonso Date: Sat, 6 Jun 2026 11:59:11 -0400 Subject: [PATCH 4/7] test: fix CI test contamination and stale identifier-quoting assertions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Master CI has been red since the alpha.36 work began; the failures were cross-file test contamination plus stale assertions, not product bugs. Each failing test passes in isolation. Contamination (state leaking across files in a serial in-process run): - tests/core/identity/env.test.ts reassigned the whole process.env object in afterEach, detaching the reference the config module bound at import — poisoned every later env-config test (config: env / resolver). Restore env keys in place instead. - tests/core/sql-terminal/executor.test.ts spied on kysely's shared sql.raw export without restoring it, so sql.raw stayed mocked process-wide and the sdk impersonate suite executed no SQL. Add vi.restoreAllMocks() in afterEach. Stale assertions (identifier quoting added in 66e6fb8 / df24197; execution tests already pass with the quoted form, only the SQL-string asserts lagged): - tests/sdk/context.test.ts: EXEC checkout_trx -> EXEC [checkout_trx] - tests/integration/sdk/tvf.test.ts: quote pg and mssql TVF call identifiers Verified: all four CI groups green locally (group1 2040, group2 116, group3 337, group4 587), typecheck + lint clean. --- tests/core/identity/env.test.ts | 12 +++++++++++- tests/core/sql-terminal/executor.test.ts | 6 ++++++ tests/integration/sdk/tvf.test.ts | 4 ++-- tests/sdk/context.test.ts | 2 +- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/core/identity/env.test.ts b/tests/core/identity/env.test.ts index 07ef974..de0d1f4 100644 --- a/tests/core/identity/env.test.ts +++ b/tests/core/identity/env.test.ts @@ -10,7 +10,17 @@ describe('core: identity env loader', () => { afterEach(() => { - process.env = { ...originalEnv }; + // Restore env in place — never reassign `process.env` to a new object. + // Modules that captured the original reference at import (e.g. the + // config module's makeNestedConfig binding) would otherwise read a + // detached object, silently poisoning every later env-config test. + for (const key of Object.keys(process.env)) { + + if (!(key in originalEnv)) delete process.env[key]; + + } + + Object.assign(process.env, originalEnv); }); diff --git a/tests/core/sql-terminal/executor.test.ts b/tests/core/sql-terminal/executor.test.ts index 148e08c..227eb09 100644 --- a/tests/core/sql-terminal/executor.test.ts +++ b/tests/core/sql-terminal/executor.test.ts @@ -55,6 +55,12 @@ describe('sql-terminal: executor', () => { beforeCleanup(); afterCleanup(); + // Restore spies — these tests spy on kysely's shared `sql.raw` + // export. Left un-restored, the mock leaks process-wide and + // breaks every later test that runs real SQL through sql.raw + // (e.g. the sdk impersonate suite). + vi.restoreAllMocks(); + }); // Mock Kysely execute function diff --git a/tests/integration/sdk/tvf.test.ts b/tests/integration/sdk/tvf.test.ts index a10abfe..031eacf 100644 --- a/tests/integration/sdk/tvf.test.ts +++ b/tests/integration/sdk/tvf.test.ts @@ -169,7 +169,7 @@ describe('integration: postgres tvf()', () => { const query = buildTvfCall('postgres', 'fn_get_todo_items_by_list', { p_list_id: LIST_ID_1 }); const compiled = query.compile(db); - expect(compiled.sql).toBe('SELECT * FROM fn_get_todo_items_by_list(p_list_id => $1)'); + expect(compiled.sql).toBe('SELECT * FROM "fn_get_todo_items_by_list"("p_list_id" => $1)'); expect(compiled.parameters).toEqual([LIST_ID_1]); }); @@ -271,7 +271,7 @@ describe('integration: mssql tvf()', () => { const query = buildTvfCall('mssql', 'fn_GetTodoItemsByList', [LIST_ID_1]); const compiled = query.compile(db); - expect(compiled.sql).toBe('SELECT * FROM fn_GetTodoItemsByList(@1)'); + expect(compiled.sql).toBe('SELECT * FROM [fn_GetTodoItemsByList](@1)'); expect(compiled.parameters).toEqual([LIST_ID_1]); }); diff --git a/tests/sdk/context.test.ts b/tests/sdk/context.test.ts index f100340..a3958bb 100644 --- a/tests/sdk/context.test.ts +++ b/tests/sdk/context.test.ts @@ -494,7 +494,7 @@ describe('sdk: Context proc/func runtime', () => { expect(query.sql).toContain('DECLARE @__tvp_Items CheckoutItems'); expect(query.sql).toContain('INSERT INTO @__tvp_Items'); - expect(query.sql).toContain('EXEC checkout_trx'); + expect(query.sql).toContain('EXEC [checkout_trx]'); }); From 9342730d59220dda7c820c45926e3c246ccb1109 Mon Sep 17 00:00:00 2001 From: Danilo Alonso Date: Sat, 6 Jun 2026 12:07:29 -0400 Subject: [PATCH 5/7] test(cli): deflake useObserver hook tests under CI load The two useOnEvent tests used fixed 10ms setTimeout waits for observer to React state propagation, which is too tight on a loaded CI runner (the subscribe test failed all three retries). Add a polling waitFor helper: the single-event test re-emits each poll until received (tolerates a not- yet-registered subscription); the counter test waits generously for the subscription, emits exactly three times, then polls for the final count. Verified stable across repeated local runs. --- tests/cli/hooks/useObserver.test.tsx | 44 ++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/tests/cli/hooks/useObserver.test.tsx b/tests/cli/hooks/useObserver.test.tsx index 997f60a..1473c7f 100644 --- a/tests/cli/hooks/useObserver.test.tsx +++ b/tests/cli/hooks/useObserver.test.tsx @@ -26,6 +26,29 @@ function WithProvider({ children }: { children: React.ReactNode }) { } +/** + * Poll until `check` is true or the timeout elapses. Replaces fixed + * `setTimeout` waits, which flake under CI load when observer→React state + * propagation takes longer than the hard-coded delay. + */ +async function waitFor(check: () => boolean, timeout = 2000, interval = 5): Promise { + + const start = Date.now(); + + while (!check()) { + + if (Date.now() - start > timeout) { + + throw new Error('waitFor: condition not met within timeout'); + + } + + await new Promise((r) => setTimeout(r, interval)); + + } + +} + describe('cli: hooks/useObserver', () => { afterEach(() => { @@ -60,9 +83,17 @@ describe('cli: hooks/useObserver', () => { expect(lastFrame()).toContain('received:none'); - await new Promise((r) => setTimeout(r, 10)); - observer.emit('config:created', { name: 'test-config' }); - await new Promise((r) => setTimeout(r, 10)); + // Re-emit each poll until received: this tolerates both a not-yet + // -registered useEffect subscription and slow state propagation, + // without depending on a fixed delay. Re-emitting is harmless here + // because the handler sets the same value each time. + await waitFor(() => { + + observer.emit('config:created', { name: 'test-config' }); + + return lastFrame()?.includes('received:test-config') ?? false; + + }); expect(lastFrame()).toContain('received:test-config'); @@ -92,13 +123,16 @@ describe('cli: hooks/useObserver', () => { const { lastFrame, unmount } = render(); - await new Promise((r) => setTimeout(r, 10)); + // Subscription must be active before emitting exactly three times, + // so the count is deterministic (can't re-emit to recover here). + // Generous wait for the useEffect to register, then poll the count. + await new Promise((r) => setTimeout(r, 100)); observer.emit('config:created', { name: 'first' }); observer.emit('config:created', { name: 'second' }); observer.emit('config:created', { name: 'third' }); - await new Promise((r) => setTimeout(r, 10)); + await waitFor(() => lastFrame()?.includes('count:3') ?? false); expect(lastFrame()).toContain('count:3'); From 2bd96827010368e57bbd134c881a28d6db20bd1f Mon Sep 17 00:00:00 2001 From: Danilo Alonso Date: Sat, 6 Jun 2026 12:20:41 -0400 Subject: [PATCH 6/7] fix(mssql): guard tarn/tedious CJS interop in bundled dialect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit await import('tarn') / import('tedious') expose their exports under .default once bundled by tsup, so spreading the namespace left tarn.Pool undefined and kysely threw 'Pool is not a constructor' on every MSSQL connection — breaking noorm db create/run/change in the published CLI and any bundled SDK consumer. Normalize with 'module.default ?? module', mirroring the postgres dialect's existing pg.default?.Pool guard. Surfaced by the new example-llm-memory-db-mssql CI job. Verified: the bundled CLI now connects to MSSQL and queries sys.databases instead of crashing. --- .changeset/fix-mssql-tarn-bundle-interop-cli.md | 11 +++++++++++ .changeset/fix-mssql-tarn-bundle-interop.md | 11 +++++++++++ src/core/connection/dialects/mssql.ts | 11 ++++++++--- 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 .changeset/fix-mssql-tarn-bundle-interop-cli.md create mode 100644 .changeset/fix-mssql-tarn-bundle-interop.md diff --git a/.changeset/fix-mssql-tarn-bundle-interop-cli.md b/.changeset/fix-mssql-tarn-bundle-interop-cli.md new file mode 100644 index 0000000..9fde98a --- /dev/null +++ b/.changeset/fix-mssql-tarn-bundle-interop-cli.md @@ -0,0 +1,11 @@ +--- +"@noormdev/cli": patch +--- + +fix(mssql): noorm db create / run no longer crash with "Pool is not a constructor" + +In the bundled CLI, `await import('tarn')` / `await import('tedious')` expose +their exports under `.default`, so kysely's MSSQL dialect received an undefined +`Pool` and every MSSQL command (`noorm db create`, `run`, `change`, etc.) failed +with `Cannot connect to server: Pool is not a constructor`. Normalize the CJS +interop, mirroring the postgres dialect. diff --git a/.changeset/fix-mssql-tarn-bundle-interop.md b/.changeset/fix-mssql-tarn-bundle-interop.md new file mode 100644 index 0000000..74128d9 --- /dev/null +++ b/.changeset/fix-mssql-tarn-bundle-interop.md @@ -0,0 +1,11 @@ +--- +"@noormdev/sdk": patch +--- + +fix(mssql): construct tarn/tedious through CJS-interop guard in bundles + +When the SDK is bundled (tsup), `await import('tarn')` / `await import('tedious')` +expose their exports under `.default`, so spreading the namespace left +`tarn.Pool` undefined and kysely threw `Pool is not a constructor` on every +MSSQL connection. Normalize both with `module.default ?? module`, mirroring the +postgres dialect's existing guard. diff --git a/src/core/connection/dialects/mssql.ts b/src/core/connection/dialects/mssql.ts index 6ac6396..367f803 100644 --- a/src/core/connection/dialects/mssql.ts +++ b/src/core/connection/dialects/mssql.ts @@ -118,9 +118,14 @@ async function verifyDatabaseExists( */ export async function createMssqlConnection(config: ConnectionConfig): Promise { - // Dynamic import to avoid compile-time dependency - const Tedious = await import('tedious'); - const Tarn = await import('tarn'); + // Dynamic import to avoid compile-time dependency. Normalize CJS interop: + // when bundled (tsup), the module's exports land under `.default`, so a + // bare `Tarn.Pool` is undefined and kysely throws "Pool is not a + // constructor". Mirror the postgres dialect's `pkg.default ?? pkg` guard. + const TediousImport = await import('tedious'); + const TarnImport = await import('tarn'); + const Tedious = TediousImport.default ?? TediousImport; + const Tarn = TarnImport.default ?? TarnImport; // Preflight: verify database exists via master await verifyDatabaseExists(Tedious, Tarn, config); From 99b9f5a632530452640d821829e6fa784c9e7a53 Mon Sep 17 00:00:00 2001 From: Danilo Alonso Date: Sat, 6 Jun 2026 12:32:13 -0400 Subject: [PATCH 7/7] test(example-mssql): mcp-discovery connects with the test config The MCP discovery test hard-coded connect config 'dev', but the example suite (createContext({config:'test'})) and the CI job (noorm ci init --name test) only provision the 'test' config, so the MCP connect returned an error under CI. Use 'test' to match the rest of the suite. --- .../tests/integration/mcp-discovery.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/llm-memory-db-mssql/tests/integration/mcp-discovery.test.ts b/examples/llm-memory-db-mssql/tests/integration/mcp-discovery.test.ts index 613b951..8710651 100644 --- a/examples/llm-memory-db-mssql/tests/integration/mcp-discovery.test.ts +++ b/examples/llm-memory-db-mssql/tests/integration/mcp-discovery.test.ts @@ -121,9 +121,12 @@ beforeAll(async () => { // The MCP server starts disconnected. `overview` and `list` both call // `session.getContext()`, which throws if no connection exists. Connect // once for the whole suite — the SessionManager memoises by config. + // Connect with the `test` config — the one the suite bootstraps + // (createContext({ config: 'test' }) in the helpers, and `noorm ci init + // --name test` in CI). The `dev` config isn't provisioned under test. const connectRes = await client.callTool({ name: 'run_noorm_cmd', - arguments: { command: 'connect', config: 'dev' }, + arguments: { command: 'connect', config: 'test' }, }); expect(isError(connectRes)).toBeFalsy();