Skip to content

chore: verify-tenancy DoD probe script (dev + prod) - #62

Merged
Bonobo791 merged 5 commits into
mainfrom
chore-verify-tenancy
Aug 3, 2026
Merged

Bonobo791 merged 5 commits into
mainfrom
chore-verify-tenancy

Conversation

@Bonobo791

@Bonobo791 Bonobo791 commented Aug 3, 2026

Copy link
Copy Markdown
Owner

User description

Behavior

Packages the tenancy Definition-of-Done database checks as one read-only command so the production application of migration 0013 is verifiable in a single step:

node --env-file=.env scripts/verify-tenancy.mjs   # dev (db); point TURSO_DATABASE_URL at moderaty for prod

Ten checks, each printing PASS/FAIL with offending rows on failure, exit 1 if anything fails:

  • foreign_key_check zero rows, integrity_check ok
  • channels DDL contains channels_org_requires_owner; an owned channel with NULL org is rejected and leaves no row (self-cleaning probe)
  • Zero channels with user_id set and org_id NULL
  • Every live user has exactly one personal org and owns it; no ownerless orgs; no memberless orgs

Verified live against the dev DB: 10/10 PASS.

Tests

scripts/verify-tenancy.test.mjs (3 tests, child-process against temp file DBs, follows the seed-dev.test.mjs pattern): healthy contract DB → exit 0 / ALL CHECKS PASSED; pre-contract DB (no CHECK) → exit 1 naming the DDL and rejection checks; ownerless org → exit 1 naming the invariant. Mutation-checked: neutering the failure counter fails both negative tests.

Verification

npm run check 0/0 · npm run test 47 files, 380/380 · npm run build clean · codacy-analysis 0 issues


CodeAnt-AI Description

Add a reliable tenancy verification check for development and production databases

What Changed

  • Added a single command that reports PASS/FAIL results for database integrity, tenancy constraints, and organization membership rules, returning a failing exit status when any check fails.
  • The contract check now confirms the specific tenancy rule rejects invalid channels instead of treating unrelated errors as success.
  • Verification uses a unique temporary row, removes only what it created, and preserves existing records.
  • Individual check errors no longer stop the run; the final summary still identifies that verification failed.
  • Added coverage for healthy databases, missing or incorrect constraints, missing tables, invalid organization data, cleanup, and legacy probe rows.
  • Documented the required production verification step before completing a tenancy rollout.

Impact

✅ Reliable production tenancy verification
✅ No leftover or deleted application data from checks
✅ Clear failure results for incomplete migrations and invalid tenancy data

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@cla-bot cla-bot Bot added the cla-signed label Aug 3, 2026
@codeant-ai

codeant-ai Bot commented Aug 3, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Incremental review completed 6ad4451 Aug 03, 2026 · 20:09 20:12
✅ Incremental review completed 4107f66 Aug 03, 2026 · 19:54 19:57
✅ Reviewed your PR 56d8d11 Aug 03, 2026 · 19:38 19:40

@netlify

netlify Bot commented Aug 3, 2026

Copy link
Copy Markdown

Deploy Preview for moderaty ready!

Name Link
🔨 Latest commit 6ad4451
🔍 Latest deploy log https://app.netlify.com/projects/moderaty/deploys/6a70f57d559b6d000848eff1
😎 Deploy Preview https://deploy-preview-62--moderaty.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 90
Accessibility: 97
Best Practices: 100
SEO: 100
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added a read-only tenancy verification command that checks database integrity, ownership constraints, and membership relationships.
    • Reports clear diagnostics and returns a failure status when validation checks do not pass.
  • Tests

    • Added automated coverage for healthy databases and common tenancy configuration issues, including missing constraints, unexpected database errors, and ownerless organizations.
    • Verifies probe cleanup and collision safety using temporary databases.

Walkthrough

The PR adds a read-only tenancy verification CLI. It checks database integrity, ownership constraints, and tenancy invariants. Vitest tests run the CLI against temporary healthy and invalid libSQL databases.

Changes

Tenancy verification

Layer / File(s) Summary
Probe entry and contract checks
scripts/verify-tenancy.mjs
The CLI validates configuration, connects to Turso, checks SQLite integrity, and verifies the channels ownership constraint with cleanup verification.
Tenancy invariant reporting
scripts/verify-tenancy.mjs
The CLI checks channel, user, organization, and membership invariants, reports offending rows, closes the client, and sets the exit status.
Probe behavior validation
scripts/verify-tenancy.test.mjs
Tests create temporary databases and verify healthy, invalid, missing-table, cleanup, collision, unrelated-constraint, and ownerless-organization results without Turso credentials.

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

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant verifyTenancy as verify-tenancy.mjs
  participant Turso
  participant Database
  Operator->>verifyTenancy: run tenancy verification
  verifyTenancy->>Turso: connect with database URL and token
  Turso->>Database: execute integrity and tenancy queries
  Database-->>verifyTenancy: return results
  verifyTenancy->>Database: run and clean up ownership probe
  verifyTenancy-->>Operator: report checks and exit status
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the new tenancy verification script and its development and production scope.
Description check ✅ Passed The description directly explains the tenancy verification command, checks, tests, failure behavior, and production use.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore-verify-tenancy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Aug 3, 2026
@codeant-ai

codeant-ai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Sequence Diagram

This PR adds a single read-only probe command that checks database integrity, verifies the channel tenancy constraint with a self-cleaning rejection probe, and evaluates organization invariants before returning a pass or fail exit status.

sequenceDiagram
    participant Operator
    participant Probe
    participant Database

    Operator->>Probe: Run tenancy verification
    Probe->>Database: Check database integrity
    Database-->>Probe: Integrity results
    Probe->>Database: Verify channel constraint and rejection behavior
    Database-->>Probe: Constraint and cleanup results
    Probe->>Database: Check user and organization invariants
    Database-->>Probe: Invariant results
    Probe-->>Operator: Print check results and exit status
Loading

Generated by CodeAnt AI

@codeant-ai

codeant-ai Bot commented Aug 3, 2026

Copy link
Copy Markdown

🏁 CodeAnt Quality Gate Results

Commit: 6ad4451b
Scan Time: 2026-08-03 20:43:28 UTC

✅ Overall Status: PASSED

Quality Gate Details

Quality Gate Status Details
Secrets ✅ PASSED 0 secrets found
Duplicate Code ✅ PASSED 0.0% duplicated
SAST ✅ PASSED No security issues
Bugs ✅ PASSED Rating S: No bugs
IAC ✅ PASSED No IAC issues

View Full Results

@codacy-production

codacy-production Bot commented Aug 3, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 29 complexity · 0 duplication

Metric Results
Complexity 29 (≤ 100 complexity)
Duplication 0 (≤ 1 duplication)

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This PR adds a comprehensive tenancy verification script with good test coverage. However, there are 3 critical defects that must be fixed before merge:

Critical Issues:

  1. Crash risk in scalar() helper - will crash if queries return no rows
  2. Silent error swallowing - empty catch block masks database connection failures
  3. Undefined exit code handling - test runner may return undefined exit codes

These issues could cause the verification script to crash or report misleading results in production, defeating its purpose as a database integrity check. Once these defects are addressed, the script will reliably verify tenancy invariants.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

Comment thread scripts/verify-tenancy.mjs Outdated
if (!ok) failures += 1;
}

const scalar = async (sql) => (await client.execute(sql)).rows[0].n;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛑 Crash Risk: The scalar helper function will crash with "Cannot read properties of undefined" if a query returns no rows, since it unconditionally accesses rows[0].n. This breaks the script when any of the count queries return unexpected results.

Suggested change
const scalar = async (sql) => (await client.execute(sql)).rows[0].n;
const scalar = async (sql) => {
const result = await client.execute(sql);
if (!result.rows || result.rows.length === 0) {
throw new Error(`Query returned no rows: ${sql}`);
}
return result.rows[0].n;
};

Comment thread scripts/verify-tenancy.mjs Outdated
Comment on lines +61 to +67
let rejected = false;
try {
await client.execute("INSERT INTO channels (id, user_id, title, refresh_token_enc) VALUES ('UCverify-probe', 'probe', 't', 'x')");
} catch {
rejected = true;
}
report('owned channel with NULL org is rejected', rejected, 'INSERT was allowed');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛑 Logic Error: The empty catch block silently swallows all errors, including database connection failures and authentication errors. This causes the script to incorrectly report "INSERT was allowed" when the real issue is a database connection failure, masking critical infrastructure problems.

Suggested change
let rejected = false;
try {
await client.execute("INSERT INTO channels (id, user_id, title, refresh_token_enc) VALUES ('UCverify-probe', 'probe', 't', 'x')");
} catch {
rejected = true;
}
report('owned channel with NULL org is rejected', rejected, 'INSERT was allowed');
let rejected = false;
let insertError = null;
try {
await client.execute("INSERT INTO channels (id, user_id, title, refresh_token_enc) VALUES ('UCverify-probe', 'probe', 't', 'x')");
} catch (error) {
insertError = error;
rejected = true;
}
report('owned channel with NULL org is rejected', rejected, insertError ? insertError.message : 'INSERT was allowed');

Comment on lines +108 to +110
} catch (error) {
return { code: error.code, stdout: error.stdout ?? '' };
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛑 Crash Risk: The code accesses error.code which may be undefined for non-exit errors (like thrown exceptions or signals). When execFileAsync rejects with an error that doesn't have a code property, this returns { code: undefined, stdout: '' }, causing test assertions that compare exit codes to fail or behave unpredictably.

Suggested change
} catch (error) {
return { code: error.code, stdout: error.stdout ?? '' };
}
} catch (error) {
return { code: error.code ?? 1, stdout: error.stdout ?? '' };
}

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add verify-tenancy probe to validate multi-tenancy invariants (dev + prod)

✨ Enhancement 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Add a read-only CLI probe to verify tenancy invariants against the configured Turso DB.
• Enforce “contract exists + bites” by attempting a self-cleaning invalid INSERT.
• Add Vitest coverage that asserts PASS/FAIL output and exit codes across DB shapes.
Diagram

graph TD
  A["Operator / CI"] --> B["verify-tenancy.mjs"] --> C["@libsql/client"] --> D[("Turso / SQLite DB")]
  B --> E["PASS/FAIL + exit code"]
  T["vitest"] --> U["verify-tenancy.test.mjs"] --> V["node child_process"] --> B
  U --> W[("temp file DB")]
  subgraph Legend
    direction LR
    _actor["Invoker"] ~~~ _proc["Script/Test"] ~~~ _db[("Database")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Pure SQL probe (no Node)
  • ➕ No runtime dependency on Node/@libsql/client
  • ➕ Easier to run directly in sqlite/libsql shells
  • ➖ Harder to do self-cleaning negative probes and structured PASS/FAIL output
  • ➖ Less ergonomic to test end-to-end with Vitest/child-process assertions
2. Startup health-check endpoint / admin command in app
  • ➕ Continuously validates production invariants, not just when manually run
  • ➕ Could integrate into monitoring/alerting
  • ➖ Adds runtime surface area and potential operational risk
  • ➖ May require auth/admin gating and more code paths than a one-off probe script

Recommendation: Keep the current approach: a standalone, read-only-by-default Node probe is the most practical way to verify production after migration application while remaining easy to run and easy to test. If desired, a small follow-up could add a package.json script alias (e.g., db:verify-tenancy) for discoverability, but the core strategy is sound.

Files changed (2) +242 / -0

Enhancement (1) +100 / -0
verify-tenancy.mjsAdd CLI probe for tenancy DoD invariants (PASS/FAIL + exit code) +100/-0

Add CLI probe for tenancy DoD invariants (PASS/FAIL + exit code)

• Introduces a node script that connects to the Turso/SQLite database via @libsql/client and runs structural PRAGMA checks plus tenancy invariants. It verifies the channels tenancy contract both by DDL inspection and by a self-cleaning invalid INSERT probe, prints offending rows on failures, and exits 1 if any checks fail.

scripts/verify-tenancy.mjs

Tests (1) +142 / -0
verify-tenancy.test.mjsAdd end-to-end Vitest coverage for verify-tenancy probe behavior +142/-0

Add end-to-end Vitest coverage for verify-tenancy probe behavior

• Adds child-process-based behavior tests that build temporary file-backed databases with and without the tenancy CHECK constraint. Asserts the probe exits 0 on a healthy contract DB, and exits 1 with specific failing check labels for pre-contract and ownerless-org scenarios.

scripts/verify-tenancy.test.mjs

@codacy-production codacy-production Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull Request Overview

The PR introduces a database verification script that is functionally comprehensive but contains a critical flaw in its 'non-destructive' design. While intended to be read-only (with a probe exception), the script currently risks polluting production databases with 'UCverify-probe' records if the tenancy constraint it is testing for is missing.

Additionally, the verification logic in the probe's catch block is too broad, which could lead to false positives if the insertion fails for unrelated reasons (like missing tables). The test suite also requires a fix for cross-platform compatibility to ensure it runs correctly on Windows environments. Codacy grade is up to standards, but these logic issues should be addressed before merging.

About this PR

  • The automated test suite lacks negative test cases for several key invariants. While it verifies healthy states, it should also include tests for memberless organizations and users with multiple personal organizations to ensure the reporting and exit codes function as expected for all failure modes.

Test suggestions

  • Verify structural integrity and foreign key pragmas return successful results on a healthy DB.
  • Verify the presence and enforcement of the 'channels_org_requires_owner' constraint via DDL inspection and a probe INSERT.
  • Verify that every active user has exactly one personal organization and is its owner.
  • Verify that organizations without owners are identified as failures.
  • Verify that organizations without any members are identified as failures.
  • Verify that the script exits with code 1 and identifies the failure when a constraint is missing or a row is orphaned.
  • Missing negative test scenario for memberless organizations
  • Missing negative test scenario for users with multiple personal organizations
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. Missing negative test scenario for memberless organizations
2. Missing negative test scenario for users with multiple personal organizations

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

Comment on lines +62 to +70
try {
await client.execute("INSERT INTO channels (id, user_id, title, refresh_token_enc) VALUES ('UCverify-probe', 'probe', 't', 'x')");
} catch {
rejected = true;
}
report('owned channel with NULL org is rejected', rejected, 'INSERT was allowed');
const orphans = await scalar("SELECT count(*) AS n FROM channels WHERE id = 'UCverify-probe'");
report('rejected probe left no row', orphans === 0, `${orphans} row(s) present`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 HIGH RISK

The probe INSERT logic poses a data pollution risk and a false-positive risk. 1) If the tenancy constraint is missing (the failure case), the 'UCverify-probe' row remains in the database; an explicit DELETE or transaction rollback is required. 2) The catch block is too broad; it should specifically verify that the error is a 'channels_org_requires_owner' constraint violation to avoid false positives from other errors (e.g., missing tables).

Comment thread scripts/verify-tenancy.test.mjs Outdated
async function runProbe(url) {
const { TURSO_AUTH_TOKEN: _token, TURSO_DATABASE_URL: _url, ...rest } = process.env;
try {
const { stdout } = await execFileAsync('node', [PROBE.pathname], { env: { ...rest, TURSO_DATABASE_URL: url } });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 MEDIUM RISK

Suggestion: Use fileURLToPath from the node:url module for robust cross-platform path resolution. On Windows, URL.pathname can return invalid path formats (e.g., leading slashes before drive letters), which may cause execFileAsync to fail.

@qodo-code-review

qodo-code-review Bot commented Aug 3, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 77 rules

Grey Divider


Action required

1. Contract probe misattributes errors ✓ Resolved 🐞 Bug ≡ Correctness
Description
The contract probe treats any INSERT error as proof that channels_org_requires_owner rejected the
row, so a read-only/permission-restricted prod credential (or other non-CHECK failures) can
incorrectly PASS the “owned channel with NULL org is rejected” check. If the INSERT is allowed
(broken contract), the script reports failure but does not delete/rollback the probe row,
contradicting the script’s “clean up after themselves” claim and mutating the target DB.
Code

scripts/verify-tenancy.mjs[R62-65]

+try {
+	await client.execute("INSERT INTO channels (id, user_id, title, refresh_token_enc) VALUES ('UCverify-probe', 'probe', 't', 'x')");
+} catch {
+	rejected = true;
Relevance

●● Moderate

No close history on probe error-classification/rollback; team fixes correctness but evidence
indirect.

PR-#24

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The script advertises that probes “clean up after themselves”, but the contract probe only sets a
boolean on any exception and never performs a rollback/delete; it also doesn’t confirm the exception
was triggered by the intended CHECK constraint.

scripts/verify-tenancy.mjs[20-23]
scripts/verify-tenancy.mjs[59-69]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The tenancy contract probe currently marks the contract as enforced if the probe INSERT throws for any reason, and it does not guarantee cleanup if the INSERT succeeds. This can yield a false PASS when writes are disallowed (or other errors occur) and can leave `UCverify-probe` persisted when the contract is missing.

### Issue Context
This script is intended as an ops-grade “Definition-of-Done” verification command and explicitly states it is READ ONLY except for self-cleaning probes.

### Fix Focus Areas
- scripts/verify-tenancy.mjs[20-23]
- scripts/verify-tenancy.mjs[59-69]

### Suggested fix approach
- Use a unique/random probe id (e.g., `crypto.randomUUID()`-based) to avoid collisions.
- Run the probe in a transaction/SAVEPOINT and always roll it back (or `DELETE FROM channels WHERE id = ?` in a `finally`) so the DB is unchanged even when the contract is missing.
- When the INSERT fails, validate the failure reason:
 - If the error indicates the expected CHECK/constraint (e.g., message contains `channels_org_requires_owner`), count it as “rejected as expected”.
 - Otherwise, report FAIL with the actual error (e.g., permission/auth/write-disallowed/network), because the probe did not prove the CHECK “bites”.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. verify-tenancy.mjs can target prod 📘 Rule violation § Compliance
Description
The new scripts/verify-tenancy.mjs tooling reads TURSO_DATABASE_URL and explicitly suggests
pointing it at production, so local tooling is not constrained to DEV-only databases. This increases
the risk of accidentally running verification/probe logic against production.
Code

scripts/verify-tenancy.mjs[R20-23]

+// Tenancy Definition-of-Done probe: verifies the multi-tenancy invariants of
+// whichever Turso database TURSO_DATABASE_URL points at (dev `db` by default;
+// point it at production `moderaty` after applying a migration there). READ
+// ONLY except the contract probes, which clean up after themselves.
Relevance

●●● Strong

Repo already gated Turso scripts to local-only; prod-targeting guidance likely rejected.

PR-#9
PR-#2

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2473537 requires local tooling that accesses Turso to be configured to use DEV-only
databases/variables. The added script reads TURSO_DATABASE_URL and documents running it against
production, so it is not DEV-only.

Rule 2473537: Local npm tooling must be configured to use only the DEV Turso database
scripts/verify-tenancy.mjs[20-23]
scripts/verify-tenancy.mjs[33-34]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`scripts/verify-tenancy.mjs` is local tooling that can be pointed at production via `TURSO_DATABASE_URL` (and even suggests doing so), violating the requirement that local tooling use only the DEV Turso database.

## Issue Context
The compliance rule requires local tooling to either hardcode a DEV database identifier or reference a DEV-only environment variable (e.g., `TURSO_DEV_URL`) and avoid production variables/targets.

## Fix Focus Areas
- scripts/verify-tenancy.mjs[20-38]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Unhandled errors abort probe ✓ Resolved 🐞 Bug ☼ Reliability
Description
All invariant checks execute SQL directly without per-check error handling or a top-level
try/finally, so any unexpected SQL/connection/schema error will abort the script before it prints
the promised FAIL line(s) and final PASS/FAIL summary. Because client.close() is only called at
the bottom, exceptions can also skip cleanup and make failures harder to diagnose operationally.
Code

scripts/verify-tenancy.mjs[R98-100]

+console.log(failures === 0 ? 'ALL CHECKS PASSED' : `${failures} CHECK(S) FAILED`);
+client.close();
+process.exit(failures === 0 ? 0 : 1);
Relevance

●● Moderate

No prior feedback found on try/finally for scripts; reliability fixes sometimes only partially
accepted.

PR-#24

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The code performs many awaited SQL calls without any surrounding try/catch and relies on a final
client.close()/process.exit() at the bottom; any earlier exception prevents the script from
reaching the summary/exit logic.

scripts/verify-tenancy.mjs[48-49]
scripts/verify-tenancy.mjs[72-96]
scripts/verify-tenancy.mjs[98-100]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The verifier is meant to print PASS/FAIL for each check and a final summary, but any thrown error during SQL execution will terminate the script early, skipping structured reporting and bypassing `client.close()`.

### Issue Context
Current implementation runs many `await` calls at top-level module scope and only closes the client at the very end.

### Fix Focus Areas
- scripts/verify-tenancy.mjs[48-56]
- scripts/verify-tenancy.mjs[72-96]
- scripts/verify-tenancy.mjs[98-100]

### Suggested fix approach
- Wrap the main body in `try { ... } catch (e) { ... } finally { client.close(); }`.
- Consider a helper like `await runCheck(label, async () => { ... })` that catches exceptions and converts them into `report(label, false, String(e))` so one broken query becomes a single FAIL instead of aborting the whole probe.
- Ensure the process exit code still reflects `failures` after handling unexpected errors.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment on lines +20 to +23
// Tenancy Definition-of-Done probe: verifies the multi-tenancy invariants of
// whichever Turso database TURSO_DATABASE_URL points at (dev `db` by default;
// point it at production `moderaty` after applying a migration there). READ
// ONLY except the contract probes, which clean up after themselves.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. verify-tenancy.mjs can target prod 📘 Rule violation § Compliance

The new scripts/verify-tenancy.mjs tooling reads TURSO_DATABASE_URL and explicitly suggests
pointing it at production, so local tooling is not constrained to DEV-only databases. This increases
the risk of accidentally running verification/probe logic against production.
Agent Prompt
## Issue description
`scripts/verify-tenancy.mjs` is local tooling that can be pointed at production via `TURSO_DATABASE_URL` (and even suggests doing so), violating the requirement that local tooling use only the DEV Turso database.

## Issue Context
The compliance rule requires local tooling to either hardcode a DEV database identifier or reference a DEV-only environment variable (e.g., `TURSO_DEV_URL`) and avoid production variables/targets.

## Fix Focus Areas
- scripts/verify-tenancy.mjs[20-38]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread scripts/verify-tenancy.mjs Outdated
Comment thread scripts/verify-tenancy.mjs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 3

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

Inline comments:
In `@scripts/verify-tenancy.mjs`:
- Around line 59-60: Update the schema validation flow around the channels DDL
query to detect a missing channels table explicitly instead of defaulting to an
empty string. Report a labeled schema failure, close the client, and exit with
status 1 before executing the channel checks; add a regression test covering the
missing-table case.
- Around line 72-96: Extract the repeated count-validation and detail-reporting
flow from the five checks into a reusable helper in scripts/verify-tenancy.mjs
that accepts the report label, count query, and detail query. Have the helper
execute the count query, compare the result with zero, serialize the
detail-query rows, and call report; then replace each duplicated block while
preserving its existing queries and labels.
- Around line 61-69: Update scripts/verify-tenancy.mjs lines 61-69 and the
related tests in scripts/verify-tenancy.test.mjs lines 122-128: run the channel
constraint probe in a rollback-only transaction using a fresh ID and valid user
reference, and mark rejection as successful only when the error contains
channels_org_requires_owner. Ensure the transaction always rolls back, add
assertions that no probe row remains and that unrelated errors report FAIL, and
add the reproducing tests before the implementation fix.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 671f4bd6-bc11-4334-9d8b-300ea77fdbd1

📥 Commits

Reviewing files that changed from the base of the PR and between 4403d9c and 56d8d11.

📒 Files selected for processing (2)
  • scripts/verify-tenancy.mjs
  • scripts/verify-tenancy.test.mjs

Comment thread scripts/verify-tenancy.mjs Outdated
Comment on lines +59 to +60
const ddl = (await client.execute("SELECT sql FROM sqlite_master WHERE name = 'channels' AND type = 'table'")).rows[0]?.sql ?? '';
report('channels DDL contains channels_org_requires_owner', ddl.includes('channels_org_requires_owner'), ddl.slice(0, 200));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Stop after a missing channels table.

The ?? '' fallback reports one failed check. Line 63 then throws for the missing table. The command skips the remaining reports, final summary, and client.close().

Report a labeled schema failure, close the client, and exit with status 1 before running channel checks. Add a missing-table regression test before the fix.

As per coding guidelines, “Fail loudly: never use silent fallbacks; fallbacks must log server-side and show an appropriate message to the user.”

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

In `@scripts/verify-tenancy.mjs` around lines 59 - 60, Update the schema
validation flow around the channels DDL query to detect a missing channels table
explicitly instead of defaulting to an empty string. Report a labeled schema
failure, close the client, and exit with status 1 before executing the channel
checks; add a regression test covering the missing-table case.

Source: Coding guidelines

Comment thread scripts/verify-tenancy.mjs Outdated
Comment thread scripts/verify-tenancy.mjs Outdated
@Bonobo791

Copy link
Copy Markdown
Owner Author

Triage — bot round 1 (commit f864724)

All 5 findings valid, all fixed:

  1. Amazon Q — scalar returning undefined on empty rows: fixed — scalar now throws on empty result sets, so a missing row is a loud probe failure, never a silent undefined compare.
  2. Amazon Q — broad catch counting any error as contract rejection (false PASS): fixed — the probe catch only counts errors matching /channels_org_requires_owner|CHECK constraint/i as rejection; anything else produces FAIL … unexpected error (not the contract): …. Mutation-checked: broadening the catch back to if (true) makes the new test fail; restored and green (kill confirmed both directions).
  3. Codacy — probe row pollution on pre-contract DBs: fixed — the probe row DELETE is now unconditional after the probe, and the check was renamed to probe row is gone after cleanup. The pre-contract test asserts both the PASS line and a direct leftover count of 0.
  4. Amazon Q — error.code ?? 1: fixed.
  5. Codacy — fileURLToPath for the probe path: fixed — PROBE now resolves via fileURLToPath(new URL(...)), no process.argv[1] string surgery.

New regression test: never false-passes when the probe fails for a non-contract reason (no channels table) — exits 1 with the unexpected-error FAIL line.

Gates: npm run check 0/0, npm run test 47 files / 381 tests green, npm run build clean, codacy-analysis 0 issues on both files.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 3

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

Inline comments:
In `@scripts/verify-tenancy.mjs`:
- Around line 81-85: Update the verification flow around the probe and cleanup
so a missing channels table is handled before attempting unconditional cleanup,
while preserving explicit failure reporting rather than silently falling back.
Capture stderr for the missing-table test and assert the controlled final
output, including the remaining reports and summary; ensure client.close() is
always executed by moving it into a finally block around the verification
lifecycle.
- Around line 68-85: Make the channel verification probe use a unique
per-invocation ID and ensure cleanup cannot remove pre-existing data: update the
probe INSERT, DELETE, and orphan-count query in the verification flow,
preferably by running the probe in a rollback-only transaction or by deleting
only after confirming this invocation inserted the row. Add a regression test
that seeds the shared legacy ID, runs the command, and verifies the seeded row
remains unchanged.
- Around line 74-78: Update the error-matching condition in the tenancy
verification flow to accept only messages containing the named
channels_org_requires_owner constraint, removing the generic CHECK constraint
alternative. Add a test covering a schema without channels_org_requires_owner
but with a different rejecting CHECK constraint, and assert that verification
reports FAIL; commit the test and fix together.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2008a585-f682-4d95-aa06-c13a61bd6e87

📥 Commits

Reviewing files that changed from the base of the PR and between 56d8d11 and f864724.

📒 Files selected for processing (2)
  • scripts/verify-tenancy.mjs
  • scripts/verify-tenancy.test.mjs

Comment thread scripts/verify-tenancy.mjs Outdated
Comment thread scripts/verify-tenancy.mjs Outdated
Comment thread scripts/verify-tenancy.mjs Outdated
…er-check error containment, dedupe zero-count checks
@Bonobo791

Copy link
Copy Markdown
Owner Author

Triage — bot round 2 (commit 4107f66)

Stale (posted on the pre-f864724 commit, already fixed there): Amazon Q scalar crash, Amazon Q broad catch, Amazon Q error.code ?? 1, Codacy probe pollution + broad catch, Codacy fileURLToPath, qodo #2 (probe misattributes errors), CodeRabbit 19:42 rollback/constraint-match (bot itself marked it ✅ addressed in f864724). No action.

Valid — fixed in 4107f66 (failing test first, then fix, per repo rule):

  1. CodeRabbit — probe must not delete pre-existing data (UCverify-probe collision): probe ID is now UCverify-${randomUUID()} per run, and cleanup only runs when this invocation's INSERT actually succeeded (inserted flag, parameterized DELETE). Tests: never deletes a pre-existing row… (legacy row survives a pre-contract run, probe INSERT must report INSERT was allowed) and probe result does not depend on pre-existing rows… (contract DB + legacy row → PASS, row intact). Mutation-checked: reverting to the fixed ID fails the first test; restored green.
  2. CodeRabbit — match only channels_org_requires_owner: the generic |CHECK constraint alternative is removed; a different rejecting CHECK now reads as FAIL with the actual error. Test: never counts a different CHECK constraint as the tenancy contract (channels_title_len rejects the probe → FAIL). Mutation-checked: re-broadening the regex fails the test; restored green.
  3. qodo Add server-side moderation pipeline #3 + CodeRabbit (×2) — unhandled errors abort the probe / skip client.close(): every check is now contained (throws become labeled FAILs), the body runs in try/catch/finally with client.close() in finally, and the final summary always prints. The no-channels-table test now also asserts CHECK(S) FAILED is reached.
  4. CodeRabbit — extract the repeated zero-count flow: the five invariant checks now share an expectZero(label, countSql, detailSql) helper (which also carries the per-check containment).

Invalid — no change: qodo #1 (script can target prod). This is the tool's documented purpose: it is the tenancy Definition-of-Done probe a human runs against whichever database TURSO_DATABASE_URL points at, including prod moderaty after the human applies migration 0013 there. It is read-only except a single self-cleaning probe row; it holds no credentials itself and writes nothing durable.

Gates: npm run check 0/0, npm run test 47 files / 384 tests green, npm run build clean, codacy-analysis 0 issues. Live re-verified against dev db: ALL CHECKS PASSED.

@codeant-ai codeant-ai Bot added size:L This PR changes 100-499 lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files labels Aug 3, 2026
@Bonobo791

Copy link
Copy Markdown
Owner Author

Triage — bot round 3 (commit f00ced0)

  • SonarQube S4624 (nested template literal in report): valid, fixed — suffix hoisted to a local; output format unchanged (the FAIL-line format is pinned by existing test assertions). SonarCloud now reports 0 open issues, quality gate passed.
  • No new review comments from the other bots on 4107f66/f00ced0; every finding from rounds 1–2 is either fixed or triaged above.

PR state: npm run check 0/0, npm run test 47 files / 384 tests green, npm run build clean, codacy-analysis 0 issues, SonarQube gate green. Ready for human review/merge.

@codeant-ai codeant-ai Bot added size:L This PR changes 100-499 lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files labels Aug 3, 2026
@sonarqubecloud

sonarqubecloud Bot commented Aug 3, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/verify-tenancy.test.mjs (1)

97-114: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use client.executeMultiple instead of manually splitting on ;.

(NON_CHANNEL_DDL + channelsDdl + seedSql).split(';') breaks statement boundaries incorrectly if any seed value ever contains a literal ; (for example, in a title string). @libsql/client provides executeMultiple(sql) to run "a sequence of SQL statements separated by semicolons" and throws on the first failing statement, which removes this fragility and the manual loop.

♻️ Proposed fix
-	for (const statement of (NON_CHANNEL_DDL + channelsDdl + seedSql).split(';')) {
-		const trimmed = statement.trim();
-		if (trimmed) await client.execute(trimmed);
-	}
+	await client.executeMultiple(NON_CHANNEL_DDL + channelsDdl + seedSql);

Please confirm executeMultiple behaves as documented in the @libsql/client version pinned by this repository (0.17.4), since this changes how partial-failure DDL/seed setup surfaces in test error output.

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

In `@scripts/verify-tenancy.test.mjs` around lines 97 - 114, The buildDb function
should use client.executeMultiple with the combined NON_CHANNEL_DDL,
channelsDdl, and seedSql instead of manually splitting SQL on semicolons and
executing a loop. Confirm the pinned `@libsql/client` 0.17.4 supports this API and
preserves first-failure propagation, while retaining client closure and URL
return behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/verify-tenancy.mjs`:
- Around line 71-93: Extract the repeated try/catch/report logic from the
structural checks and channels DDL read into a small reusable helper, following
the existing expectZero pattern. Update the PRAGMA foreign_key_check, PRAGMA
integrity_check, and channels DDL retrieval call sites to use the helper while
preserving their labels, success conditions, and fail(error) reporting.

---

Outside diff comments:
In `@scripts/verify-tenancy.test.mjs`:
- Around line 97-114: The buildDb function should use client.executeMultiple
with the combined NON_CHANNEL_DDL, channelsDdl, and seedSql instead of manually
splitting SQL on semicolons and executing a loop. Confirm the pinned
`@libsql/client` 0.17.4 supports this API and preserves first-failure propagation,
while retaining client closure and URL return behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d9d28711-175d-4b2d-9b8b-04ac5fe1b566

📥 Commits

Reviewing files that changed from the base of the PR and between f864724 and f00ced0.

📒 Files selected for processing (2)
  • scripts/verify-tenancy.mjs
  • scripts/verify-tenancy.test.mjs

Comment on lines +71 to +93
try {
// 1. Structural integrity.
try {
const fk = await rows('PRAGMA foreign_key_check');
report('PRAGMA foreign_key_check returns zero rows', fk.length === 0, JSON.stringify(fk));
} catch (error) {
report('PRAGMA foreign_key_check returns zero rows', false, fail(error));
}
try {
const integrity = (await client.execute('PRAGMA integrity_check')).rows[0]?.integrity_check;
report('PRAGMA integrity_check is ok', integrity === 'ok', String(integrity));
} catch (error) {
report('PRAGMA integrity_check is ok', false, fail(error));
}

// 2. Tenancy contract: the CHECK exists and bites.
let ddl = '';
try {
ddl = (await client.execute("SELECT sql FROM sqlite_master WHERE name = 'channels' AND type = 'table'")).rows[0]?.sql ?? '';
} catch (error) {
report('channels DDL is readable', false, fail(error));
}
report('channels DDL contains channels_org_requires_owner', ddl.includes('channels_org_requires_owner'), ddl.slice(0, 200));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the repeated try/catch/report pattern.

The PRAGMA foreign_key_check block (Lines 73-78), the PRAGMA integrity_check block (Lines 79-84), and the DDL read (Lines 88-92) repeat the same try { ... } catch (error) { report(label, false, fail(error)); } shape. A small helper following the same pattern as expectZero removes this duplication.

As per coding guidelines, "Do not copy and paste code; create reusable code instead."

♻️ Proposed helper
+async function guarded(label, fn) {
+	try {
+		return await fn();
+	} catch (error) {
+		report(label, false, fail(error));
+		return undefined;
+	}
+}
+
 try {
 	// 1. Structural integrity.
-	try {
-		const fk = await rows('PRAGMA foreign_key_check');
-		report('PRAGMA foreign_key_check returns zero rows', fk.length === 0, JSON.stringify(fk));
-	} catch (error) {
-		report('PRAGMA foreign_key_check returns zero rows', false, fail(error));
-	}
+	await guarded('PRAGMA foreign_key_check returns zero rows', async () => {
+		const fk = await rows('PRAGMA foreign_key_check');
+		report('PRAGMA foreign_key_check returns zero rows', fk.length === 0, JSON.stringify(fk));
+	});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/verify-tenancy.mjs` around lines 71 - 93, Extract the repeated
try/catch/report logic from the structural checks and channels DDL read into a
small reusable helper, following the existing expectZero pattern. Update the
PRAGMA foreign_key_check, PRAGMA integrity_check, and channels DDL retrieval
call sites to use the helper while preserving their labels, success conditions,
and fail(error) reporting.

Source: Coding guidelines

@codeant-ai

codeant-ai Bot commented Aug 3, 2026

Copy link
Copy Markdown

PR Code Suggestions ✨

Latest suggestions up to commit 6ad4451
CategorySuggestion                                                                                                                                    SeverityGenerated at (UTC)
Incomplete implementation
The test fixture omits foreign-key enforcement, making the structural integrity check vacuous

The healthy fixture recreates memberships without the production foreign keys and
never enables PRAGMA foreign_keys, so PRAGMA foreign_key_check is guaranteed to
return zero rows regardless of relational corruption. Consequently the test does not
verify the structural-integrity check it claims to cover and can remain green when
those constraints are missing or broken. Mirror the production foreign keys and
enable foreign-key enforcement when constructing the fixture.

scripts/verify-tenancy.test.mjs [57-63]

Why it matters? 🤔
  • ⚠️ Healthy probe tests do not exercise membership foreign-key integrity.
  • ⚠️ Structural database regressions can pass CI undetected.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** scripts/verify-tenancy.test.mjs
**Line:** 57:63
**Comment:**
	*Incomplete Implementation: The healthy fixture recreates `memberships` without the production foreign keys and never enables `PRAGMA foreign_keys`, so `PRAGMA foreign_key_check` is guaranteed to return zero rows regardless of relational corruption. Consequently the test does not verify the structural-integrity check it claims to cover and can remain green when those constraints are missing or broken. Mirror the production foreign keys and enable foreign-key enforcement when constructing the fixture.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
Major2026-08-03 20:12

@Bonobo791
Bonobo791 merged commit 52fe637 into main Aug 3, 2026
16 of 19 checks passed
@Bonobo791
Bonobo791 deleted the chore-verify-tenancy branch August 3, 2026 20:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant