Skip to content

Add offline SQL generation for Master schema attribute indexes - #47

Open
brnskn wants to merge 4 commits into
masterfrom
feature/attribute-index-sql
Open

brnskn wants to merge 4 commits into
masterfrom
feature/attribute-index-sql

Conversation

@brnskn

@brnskn brnskn commented Sep 14, 2026

Copy link
Copy Markdown

Summary

Add wf indexes generate to prepare attribute-index SQL from local workflow and Master schema definitions. The command writes a new batch of SQL files and a source manifest without contacting the API or database.

DBA teams review and execute the generated SQL during a planned maintenance window. The scripts reconcile generated columns and indexes with existing database structures, reuse equivalent indexes and update the runtime’s ready-projection catalog.

Key Changes

  • Offline SQL generation: Generate one SQL file per workflow, a manifest containing source versions and SHA-256 checksums, and execution notes. Preserve previous batches.
  • Master schema resolution: Resolve local workflow-to-Master references and combine index requirements across all local versions of each workflow.
  • Index metadata: Support x-indexed on nested scalar fields while retaining x-filterOperators and x-sortable as filtering and sorting permissions.
  • Deterministic naming: Generate versioned projection keys and readable index names containing a hash of their physical definitions.
  • Index reconciliation: Compare actual PostgreSQL index structures, reuse equivalent indexes, rebuild changed managed indexes and reject unmanaged name collisions.
  • Explicit retirement: Retain obsolete projections by default. Provide --retire-obsolete for planned retirement when all active workflow versions are available locally.
  • Regression coverage: Add offline generation tests and a PostgreSQL suite covering replay, index reconciliation, conversion failures, retirement and locking.

Implementation Details

Command usage

Run from a workspace containing vnext.config.json:

wf indexes generate --output ./index-sql

wf indexes generate \
  --flow money-transfer \
  --output ./index-sql

Each invocation creates a separate batch directory containing:

  • One .sql file per selected workflow.
  • manifest.json with source paths, component versions, checksums and physical index definitions.
  • README.txt with execution and rollback guidance.

No database flag or connection is required. Generation is not invoked automatically by sync, update or Master publication.

Master schema example

Add x-indexed: true to eligible fields in the Master component’s attributes.schema:

{
  "type": "object",
  "properties": {
    "amount": {
      "type": "number",
      "x-indexed": true,
      "x-filterOperators": ["gt", "between"],
      "x-sortable": true
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "x-indexed": true,
      "x-filterOperators": ["between"]
    },
    "customer": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "x-indexed": true,
          "x-filterOperators": ["contains", "startsWith"],
          "x-sortable": true
        }
      }
    }
  }
}

Supported indexed types are string, number, integer and boolean. Date-time fields use format: "date-time". Unsupported array, object, reference and conditional index declarations fail validation.

Full package revisions remain pinned. latest and partial version selectors follow runtime-compatible ordering. Missing referenced schemas, duplicate component identities, conflicting field storage types and physical schema-name collisions fail generation.

Generated database structures

Usage Structure
Numeric comparisons numeric generated column and B-tree index
Date-time comparisons timestamptz generated column and B-tree index
Textual comparison and ordering text generated column and B-tree indexes
Text search Trigram GIN index

Projection keys retain the runtime’s v1:latest contract. Text projections remain available alongside numeric/date projections to preserve textual comparison semantics.

Generated expressions populate values only for current rows, and indexes use WHERE "IsLatest" = true. Before adding a projection, SQL checks conversion compatibility across current and historical data. Conversion failures report the field and roll back the transaction. Date-time conversion requires an explicit ISO-8601 offset and is independent of session time-zone settings.

DBA execution and reconciliation

psql -X -v ON_ERROR_STOP=1 \
  --dbname=vNext_MyDomainDb \
  --file=index-sql/<batch>/money_transfer.sql

Each script uses a transaction, an advisory transaction lock and an ACCESS EXCLUSIVE table lock. New stored generated columns are added together to consolidate the table rewrite. The default lock timeout is five seconds.

Index comparison includes keys, included columns, ordering, collation, operator classes, access method and partial predicates. Equivalent indexes can be adopted under existing names. Replaying an unchanged batch preserves index OIDs and skips conversion scans, table rewrites and ANALYZE.

This is maintenance-window DDL, not concurrent index creation. Text-search indexes require pg_trgm in public and the tr-TR-x-icu collation.

Retirement and runtime activation

wf indexes generate --flow money-transfer --retire-obsolete

Retirement requires a complete local inventory of all still-active workflow versions. The offline generator cannot verify deployment state.

Retirement removes obsolete generated expressions and owned indexes, marks catalog entries unavailable and retains columns and stored values. Readers and writers must be drained for this operation.

The companion runtime uses AttributeIndexes:Enabled to activate ready-projection routing and AttributeIndexes:DisabledFlows for per-flow rollback. Catalog refresh defaults to 30 seconds. Physical cleanup remains a separate maintenance operation.

Validation

The PostgreSQL suite is included but was not rerun after the latest master merge. It can be executed against a disposable database:

VNEXT_INDEX_TEST_URL=postgresql://user:password@localhost:5432/disposable_test \
  node --test test/indexes.postgres.test.js

Validation completed:

  • node --test test/indexes.test.js: 7 passed, 0 failed after the master merge and discovery-interface adaptation.
  • The real CLI generation test passed with network connections explicitly blocked.

@brnskn brnskn self-assigned this Sep 14, 2026
@brnskn
brnskn requested review from a team and a lite review from Copilot September 14, 2026 10:11
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-14T10:15:44.871488Z bb2e572 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 102118e0-64b5-4ec6-a918-23d10e049137


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.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bb2e5720bf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}));
}
async function loadPlans(projectRoot, flow) {
const config = loadVnextConfig(projectRoot);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Honor --domain when loading the solution

In a multi-solution workspace, wf --domain partner indexes generate still loads the default vnext.config.json: the root hook forwards domain into the command options, but generate() passes only options.flow to loadPlans(), and this call hard-codes the default solution file. The command therefore silently emits SQL and a manifest for the wrong domain; with --retire-obsolete, executing that batch against the selected domain's database could retire or recreate projections from the default domain. Load the requested solution (or reject --domain) rather than ignoring the option.

Useful? React with 👍 / 👎.

Copilot AI 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.

🟡 Changes recommended

loadPlans(..., flow) currently enforces schema-name collision validation across all workflows before applying the --flow filter, which can incorrectly block scoped generation.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR adds a new offline CLI capability (wf indexes generate) to generate DBA-reviewed SQL scripts for attribute-index maintenance based on local workflow + Master schema definitions, without contacting the API or database.

Changes:

  • Added wf indexes generate command that produces an immutable batch of per-flow SQL files plus a manifest and execution notes.
  • Implemented offline planning (Master resolution, field extraction, projection/index definitions) and deterministic SQL generation with reconciliation/retirement logic.
  • Added Node 18+ offline tests plus an optional PostgreSQL integration suite (gated by VNEXT_INDEX_TEST_URL), and documented the new workflow.
File summaries
File Description
test/indexes.test.js Adds offline unit tests for version selection, schema validation, deterministic naming, and end-to-end offline generation.
test/indexes.postgres.test.js Adds optional integration tests that execute generated SQL against PostgreSQL to validate replay/reconciliation/retirement behaviors.
src/lib/indexes/versions.js Implements runtime-aligned component version parsing and “best match” selection (including -pkg. revisions).
src/lib/indexes/sql.js Generates deterministic per-flow SQL with conversion guards, index signature comparison, adoption/rebuild logic, and optional retirement.
src/lib/indexes/definitions.js Loads local workflow/Master inputs, validates/merges indexed field requirements, and builds per-flow plans for SQL generation.
src/commands/indexes.js Implements the indexes generate command: renders SQL, writes immutable batches, and produces a manifest/README.
README.md Documents the new offline SQL generator command, its outputs, and how DBAs should execute and roll back scripts.
CLAUDE.md Updates repository guidance to include the new index SQL tests and outlines the “strictly offline” contract.
bin/workflow.js Wires the new wf indexes generate Commander subcommand and its options into the CLI entrypoint.
Review details
  • Files reviewed: 8/9 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

}
const [workflows, schemas] = await Promise.all([read(dirs.workflows, 'sys-flows'), read(dirs.schemas, 'sys-schemas')]);
const schemaOwners = new Map();
for (const w of workflows.filter(w => w.domain === config.domain)) {
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.

2 participants