godcrm/scripts/adr-0020-create-missing-companions.mjs
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

128 lines
5.1 KiB
JavaScript

#!/usr/bin/env node
// ADR-0020 Phase 1 §гайка-2 — create missing companion tables for ADR-0017/0018/0019.
//
// Background: registry rows 134519/134725/135069 in table 2197 (widget 218 = ADRs)
// were created without a per-doc companion table — `data.table_id` is absent.
// Without a companion, the bdd-list / atom widgets inside those docs cannot
// render. Other recently-created ADRs (0010/0012/0020/0021) all have companions
// in project 138 (Architecture & ADR), space 11.
//
// What this script does (idempotent):
// 1. For each (regRowId, slug) target, check if a companion table named
// `doc_<slug>_<regRowId>` already exists.
// 2. If not: INSERT into universal_tables (project 138, table_type=
// 'document_content'), then provision DOCUMENT_TABLE_COLUMNS via the
// canonical helper.
// 3. Patch the registry row: merge `table_id: <newId>` into data.
//
// Usage:
// node scripts/adr-0020-create-missing-companions.mjs --dry-run (default)
// node scripts/adr-0020-create-missing-companions.mjs --apply
import { dbAll, dbGet, dbRun } from '../backend/database/connection.js';
import {
DOCUMENT_TABLE_COLUMNS,
createTableColumns,
} from '../backend/routes/v3/documents/_helpers.js';
import { generateBaseId } from '../backend/utils/baseId.js';
const APPLY = process.argv.includes('--apply');
const DRY = !APPLY;
const PROJECT_ID = 138; // Architecture & ADR (space 11)
const REGISTRY_TABLE_ID = 2197;
const TARGETS = [
{ regRowId: 134519, slug: 'adr-0017-json-column-viewer-editor', display: 'ADR-0017 — JSON Column: Viewer, Editor & Templates' },
{ regRowId: 134725, slug: 'adr-0018-godcrm-watch-app-garmin-bridge', display: 'ADR-0018 — Godcrm Watch App: Garmin Connect IQ ↔ Flutter Bridge' },
{ regRowId: 135069, slug: 'adr-0019-background-jobs-framework', display: 'ADR-0019 — Background Jobs Framework (admin space 1)' },
];
const log = (...a) => console.log('[adr-0020-companions]', ...a);
async function main() {
log(DRY ? 'DRY-RUN (no writes)' : 'APPLY MODE — will write to DB');
for (const t of TARGETS) {
const tableName = `doc_${t.slug}_${t.regRowId}`;
log('---');
log(`target: row ${t.regRowId}${tableName}`);
const existingTable = await dbGet(
`SELECT id, project_id FROM universal_tables WHERE name = $1 LIMIT 1`,
[tableName]
);
const reg = await dbGet(
`SELECT id, data FROM table_rows WHERE id = $1 AND table_id = $2`,
[t.regRowId, REGISTRY_TABLE_ID]
);
if (!reg) {
log(` ✗ registry row ${t.regRowId} NOT FOUND in table ${REGISTRY_TABLE_ID} — skipping`);
continue;
}
const regData = typeof reg.data === 'string' ? JSON.parse(reg.data) : reg.data;
const currentTableId = regData?.table_id ?? null;
if (existingTable) {
log(` ⊙ companion already exists: id=${existingTable.id}, project=${existingTable.project_id}`);
if (currentTableId !== existingTable.id) {
log(` ⚠ registry table_id (${currentTableId}) != existing table id (${existingTable.id}) — would patch`);
if (APPLY) {
const patched = { ...regData, table_id: existingTable.id };
await dbRun(
`UPDATE table_rows SET data = $1, updated_at = NOW() WHERE id = $2`,
[JSON.stringify(patched), t.regRowId]
);
log(` ✓ patched registry row ${t.regRowId} with table_id=${existingTable.id}`);
}
} else {
log(` ✓ registry already bound to ${currentTableId}`);
}
continue;
}
if (currentTableId) {
log(` ⚠ registry has table_id=${currentTableId} but no table by name ${tableName} exists — possible drift, skipping (manual review needed)`);
continue;
}
log(` → would create table "${tableName}" in project ${PROJECT_ID}`);
log(` → would provision ${DOCUMENT_TABLE_COLUMNS.length} canonical columns`);
log(` → would patch registry row ${t.regRowId} data.table_id`);
if (!APPLY) continue;
const baseId = generateBaseId();
const insertRes = await dbRun(
`INSERT INTO universal_tables (project_id, name, display_name, table_type, base_id, created_by)
VALUES ($1, $2, $3, 'document_content', $4, NULL)
RETURNING id`,
[PROJECT_ID, tableName, t.display, baseId]
);
const newTableId = insertRes.lastInsertRowid ?? insertRes.rows?.[0]?.id ?? insertRes.id;
if (!newTableId) {
log(` ✗ failed to determine new table id from insert result:`, insertRes);
continue;
}
log(` ✓ created universal_tables.id=${newTableId} (base_id=${baseId})`);
await createTableColumns(newTableId, DOCUMENT_TABLE_COLUMNS);
log(` ✓ provisioned ${DOCUMENT_TABLE_COLUMNS.length} columns`);
const patched = { ...regData, table_id: newTableId };
await dbRun(
`UPDATE table_rows SET data = $1, updated_at = NOW() WHERE id = $2`,
[JSON.stringify(patched), t.regRowId]
);
log(` ✓ patched registry row ${t.regRowId} with table_id=${newTableId}`);
}
log('---');
log('done');
}
main().then(() => process.exit(0)).catch((err) => {
console.error('[adr-0020-companions] FATAL', err);
process.exit(1);
});