Skip to content

feat(db-tools): provision_database no longer touches Postgres - #1863

Merged
pyramation merged 1 commit into
mainfrom
feat/provision-harness-no-psql
Sep 28, 2026
Merged

pyramation merged 1 commit into
mainfrom
feat/provision-harness-no-psql

Conversation

@pyramation

@pyramation pyramation commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes constructive-io/constructive-planning#2114 B-01. provision_database used to open a direct pg connection to the physical control-plane DB (CONSTRUCTIVE_DB || 'constructive', credentials sourced from pgpm env) to flip local-dev naming settings and auto-approve memberships. On hosted/devnet there is no reachable Postgres, so that step failed — and the tool still returned success: true with a fixupNote.

(a) The tool no longer touches Postgres. provision-database/pg-fixups.ts (dynamic import('pg'), pgpm env sourcing, ALTER DATABASE … SET constructive.simple_schema_names/schema_use_underscores, UPDATE app_membership_defaults/app_memberships) is deleted, along with applySqlFixups, CONSTRUCTIVE_DB, and ProvisionDatabaseDetails.fixupNote. Provisioning is GraphQL-only (requestDatabase + ticket poll).

(b) App scaffolding no longer auto-approves sign-ups. The bootstrapped owner is already active/approved (bootstrap sets is_owner/is_admin). Whether later sign-ups are auto-approved/verified is the tenant owner's product setting: the owner signs in and approves members / changes membership defaults in the dashboard. The harness does not set it.

Schema resolution (context.ts resolveSchemaId):

// before: name === 'app_public' → else name === 'public' → else nodes[0]; query error → undefined
// after:
schema.findMany({ where: { databaseId: { equalTo: id }, name: { equalTo: 'app_public' } } })
// !ok      → { reason: 'Could not query the app_public schema …: <graphql error>' }  (schema-unresolved)
// no row   → { reason: 'Database … has no app_public schema …' }                      (schema-unresolved)

metaschema_public.schema.name is the logical name (always app_public, inserted by the create-database trigger); the physical, possibly hashed hosted name (<db>-<hash>-app-public) is schemaName. So this match works with hashed names and no longer depends on the simple-naming settings. No other field identifies the app schema by role: every module public schema (auth_public, memberships_public, …) is also isPublic/EXPOSABLE, and category is never set (all APP). The public/first-row fallbacks are removed because they silently picked an arbitrary schema.

(c) Harness docs/skills describing the fixups live in constructive-desktop and are updated in the companion PR https://github.com/constructive-io/constructive-desktop/pull/148: BLUEPRINT-PENDING-001 rewritten (owner approves / changes defaults in the dashboard), MEMBERSHIP-DB-001 and the fix-membership-defaults SQL workaround removed, the fixupNote tool-card UI and the parsePgpmEnv test import dropped. Nothing in this repo (agentic/, docs, skills) referenced fixupNote/applySqlFixups besides provision-database.ts.

Review focus

  • Behavior change for hosted and local: new tenant members are no longer auto-approved/verified by the harness.
  • schema-unresolved now carries the underlying GraphQL error instead of a generic message.

Link to Devin session: https://app.devin.ai/sessions/d5f0aade466d4f638cc14673e42309de
Open in Devin Desktop: https://app.devin.ai/desktop/session/d5f0aade466d4f638cc14673e42309de?variant=devin
Requested by: @pyramation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@tenki-reviewer

tenki-reviewer Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Review complete. 🟠 2 high

💬 Inline comments (2)


The PR deletes provision-database/pg-fixups.ts (naming-settings and membership-defaults fixups) and reworks resolveSchemaId to require a literal app_public schema, failing hard with a reason instead of falling back. Provisioning now goes straight from the API request to .env persistence.

Files Change
agentic/db-tools/src/context.ts resolveSchemaId returns a {id} | {reason} union and requires an exact app_public match, no fallback to public
agentic/db-tools/src/tools/provision-database.ts Drops the fixup application step and fixupNote feedback; persists connection config immediately after provisioning
agentic/db-tools/src/provision-database/pg-fixups.ts Deleted — was the sole source of simple_schema_names and membership-default fixups
agentic/db-tools/__tests__/context.test.ts Tests updated for the new strict schema-resolution behavior

Reviewed commit: 2b80563

@tenki-reviewer tenki-reviewer 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.

This PR replaces the post-provisioning SQL "fixup" step with a strict app_public schema resolver and simpler provisioning flow, but removes two provisioning guarantees without in-repo replacements.

Key findings

Comment on lines +348 to +362
where: { databaseId: { equalTo: databaseId }, name: { equalTo: 'app_public' } },
})
.execute();

if (!result.ok) return undefined;
if (!result.ok) {
const detail = result.errors?.[0]?.message ?? 'unknown error';
return { reason: `Could not query the app_public schema for database ${databaseId}: ${detail}` };
}

const nodes = (result.data.schemas?.nodes ?? []).filter((s) => Boolean(s && s.id));
const appPublic = nodes.find((s) => s.name === 'app_public');
if (appPublic) return appPublic.id;
const pub = nodes.find((s) => s.name === 'public');
if (pub) return pub.id;
return nodes[0]?.id;
const appPublic = (result.data.schemas?.nodes ?? []).find((s) => s?.name === 'app_public');
if (!appPublic?.id) {
return {
reason: `Database ${databaseId} has no app_public schema. The database may not be fully provisioned yet.`,
};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 bug · high

Strict app_public lookup loses its naming-settings guarantee

resolveSchemaId now returns schema-unresolved unless a logical schema named app_public exists (agentic/db-tools/src/context.ts:348), but this PR also deleted pg-fixups.ts, the only code that set constructive.simple_schema_names/schema_use_underscores — which, per that file's own comment, is what makes provision_blueprint create app_public instead of a hyphenated <db>-<hex>-app-public schema. No replacement exists in the provision request path (request-database.ts sends only a preset/module list). If the backend still applies the old naming default, provisioning succeeds but every tool gated on resolveProjectContext fails with schema-unresolved; previously the resolver fell back to public/any first schema and kept working. Legacy databases kept by the reprovision path (old database preserved) cannot resolve at all under the strict filter.

📋 Prompt for AI Agents

In agentic/db-tools, reconcile the strict app_public requirement in resolveSchemaId (agentic/db-tools/src/context.ts lines 341-364) with the removal of provision-database/pg-fixups.ts. The deleted fixup set constructive.simple_schema_names and constructive.schema_use_underscores so provision_blueprint created an app_public schema. Either move that guarantee into the provisioning request path (agentic/db-tools/src/provision-database/request-database.ts or a preset flag in presets.ts) or add a documented fallback in resolveSchemaId when app_public is absent (with a legacy-naming reason pointing to reprovision). Update agentic/db-tools/tests/context.test.ts to cover the chosen behavior.

// provisioning already succeeded, so a fixup failure is a warning, not an error.
const fixup = await applySqlFixups({ databaseName, physicalDb });

// Persist the binding to the project .env (upsert, preserving other keys).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 bug · high

Membership-defaults fixup deleted without replacement

Deleting pg-fixups.ts removes the fixup that flipped app_membership_defaults/app_memberships to is_approved=true/is_verified=true, and nothing in the provision flow replaces it — provision-database.ts now goes from requestDatabaseProvision straight to .env persistence (agentic/db-tools/src/tools/provision-database.ts:267). The deleted file's own header documented the consequence: new sign-ups land unapproved/unverified and the AuthzEntityMembership policy silently denies every insert/select, so CRUD rows never persist. The tool also dropped its fixupNote success-message feedback and its 'enable membership defaults' description wording, so users get no signal that the guarantee is gone.

📋 Prompt for AI Agents

Restore the guarantee that agentic/db-tools/src/provision-database/pg-fixups.ts previously provided: after provisioning, app_membership_defaults and app_memberships must have is_approved=TRUE and is_verified=TRUE. Implement it in the provisioning backend or in the request payload built by agentic/db-tools/src/provision-database/request-database.ts (used from agentic/db-tools/src/tools/provision-database.ts around lines 249-296), since the CLI no longer connects to Postgres directly. If the backend cannot be confirmed to apply it, surface a warning note in the tool's success message/details so users know sign-in/CRUD may need a manual fixup.

@pyramation
pyramation merged commit 921179f into main Sep 28, 2026
21 checks passed
@pyramation
pyramation deleted the feat/provision-harness-no-psql branch September 28, 2026 07:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant