merge: readable linked payment/document chip + SMS alert loading/error state

main
Thomas Joise 19 hours ago
parent a267cd07e6
commit 551c9e3d30

@ -0,0 +1,179 @@
{
"running": true,
"startedAt": "2026-07-20T16:44:54.772Z",
"workers": {
"map": {
"runCount": 11,
"successCount": 11,
"failureCount": 0,
"averageDurationMs": 8.090909090909092,
"isRunning": false,
"nextRun": "2026-07-20T19:29:55.184Z",
"lastStartedAt": "2026-07-20T19:14:55.176Z",
"lastRun": "2026-07-20T19:14:55.183Z"
},
"audit": {
"runCount": 17,
"successCount": 17,
"failureCount": 0,
"averageDurationMs": 8.882352941176471,
"isRunning": false,
"nextRun": "2026-07-20T19:26:56.025Z",
"lastStartedAt": "2026-07-20T19:26:56.097Z",
"lastRun": "2026-07-20T19:26:56.134Z"
},
"optimize": {
"runCount": 11,
"successCount": 11,
"failureCount": 0,
"averageDurationMs": 6.454545454545454,
"isRunning": false,
"nextRun": "2026-07-20T19:33:55.170Z",
"lastStartedAt": "2026-07-20T19:18:55.161Z",
"lastRun": "2026-07-20T19:18:55.166Z"
},
"consolidate": {
"runCount": 6,
"successCount": 6,
"failureCount": 0,
"averageDurationMs": 7.5,
"isRunning": false,
"nextRun": "2026-07-20T19:50:55.096Z",
"lastStartedAt": "2026-07-20T19:20:55.092Z",
"lastRun": "2026-07-20T19:20:55.094Z"
},
"testgaps": {
"runCount": 8,
"successCount": 8,
"failureCount": 0,
"averageDurationMs": 8.375,
"isRunning": false,
"nextRun": "2026-07-20T19:32:55.117Z",
"lastStartedAt": "2026-07-20T19:12:55.104Z",
"lastRun": "2026-07-20T19:12:55.114Z"
},
"backup": {
"runCount": 1,
"successCount": 1,
"failureCount": 0,
"averageDurationMs": 9,
"isRunning": false,
"nextRun": "2026-07-21T16:54:54.808Z",
"lastStartedAt": "2026-07-20T16:54:54.797Z",
"lastRun": "2026-07-20T16:54:54.806Z"
},
"harness": {
"runCount": 1,
"successCount": 1,
"failureCount": 0,
"averageDurationMs": 729,
"isRunning": false,
"nextRun": "2026-07-20T22:56:55.533Z",
"lastStartedAt": "2026-07-20T16:56:54.797Z",
"lastRun": "2026-07-20T16:56:55.526Z"
},
"predict": {
"runCount": 0,
"successCount": 0,
"failureCount": 0,
"averageDurationMs": 0,
"isRunning": false
},
"document": {
"runCount": 0,
"successCount": 0,
"failureCount": 0,
"averageDurationMs": 0,
"isRunning": false
}
},
"config": {
"autoStart": false,
"logDir": "C:\\SiMS\\hq\\.claude-flow\\logs",
"stateFile": "C:\\SiMS\\hq\\.claude-flow\\daemon-state.json",
"maxConcurrent": 2,
"workerTimeoutMs": 960000,
"resourceThresholds": {
"maxCpuLoad": 9.600000000000001,
"minFreeMemoryPercent": 10
},
"ttlMs": 43200000,
"idleShutdownMs": 0,
"aiWorkersEnabled": false,
"workers": [
{
"type": "map",
"intervalMs": 900000,
"offsetMs": 0,
"priority": "normal",
"description": "Codebase mapping",
"enabled": true
},
{
"type": "audit",
"intervalMs": 600000,
"offsetMs": 120000,
"priority": "critical",
"description": "Security analysis",
"enabled": true
},
{
"type": "optimize",
"intervalMs": 900000,
"offsetMs": 240000,
"priority": "high",
"description": "Performance optimization",
"enabled": true
},
{
"type": "consolidate",
"intervalMs": 1800000,
"offsetMs": 360000,
"priority": "low",
"description": "Memory distillation (ADR-174)",
"enabled": true
},
{
"type": "testgaps",
"intervalMs": 1200000,
"offsetMs": 480000,
"priority": "normal",
"description": "Test coverage analysis",
"enabled": true
},
{
"type": "backup",
"intervalMs": 86400000,
"offsetMs": 600000,
"priority": "low",
"description": "Nightly memory DB backup (WAL-safe, rotated)",
"enabled": true
},
{
"type": "harness",
"intervalMs": 21600000,
"offsetMs": 720000,
"priority": "low",
"description": "Self-optimizing harness loop (opt-in RUFLO_HARNESS_LOOP, $0-default)",
"enabled": true
},
{
"type": "predict",
"intervalMs": 600000,
"offsetMs": 0,
"priority": "low",
"description": "Predictive preloading",
"enabled": false
},
{
"type": "document",
"intervalMs": 3600000,
"offsetMs": 0,
"priority": "low",
"description": "Auto-documentation",
"enabled": false
}
]
},
"savedAt": "2026-07-20T19:26:56.135Z"
}

@ -0,0 +1,11 @@
{
"timestamp": "2026-07-20T19:14:55.182Z",
"projectRoot": "C:\\SiMS\\hq",
"structure": {
"hasPackageJson": true,
"hasTsConfig": true,
"hasClaudeConfig": true,
"hasClaudeFlow": true
},
"scannedAt": 1784574895183
}

@ -0,0 +1,16 @@
{
"timestamp": "2026-07-20T19:20:55.094Z",
"distillationEnabled": true,
"patternsConsolidated": 0,
"memoryCleaned": 0,
"duplicatesRemoved": 0,
"episodes": 0,
"patternEmbeddings": 0,
"causalEdges": 0,
"promoted": 0,
"byProvenance": {},
"namespaces": [],
"dryRun": false,
"corrupt": false,
"skipped": "no-db"
}

@ -0,0 +1,17 @@
{
"timestamp": "2026-07-20T19:18:55.166Z",
"mode": "local",
"memoryUsage": {
"rss": 46977024,
"heapTotal": 21909504,
"heapUsed": 20452192,
"external": 3903735,
"arrayBuffers": 100670
},
"uptime": 9240.595012,
"optimizations": {
"cacheHitRate": 0.78,
"avgResponseTime": 45
},
"note": "Install Claude Code CLI for AI-powered optimization suggestions"
}

@ -0,0 +1,12 @@
{
"timestamp": "2026-07-20T19:26:56.127Z",
"mode": "local",
"checks": {
"envFilesProtected": true,
"gitIgnoreExists": true,
"noHardcodedSecrets": true
},
"riskLevel": "low",
"recommendations": [],
"note": "Install Claude Code CLI for AI-powered security analysis"
}

@ -0,0 +1,8 @@
{
"timestamp": "2026-07-20T19:12:55.112Z",
"mode": "local",
"hasTestDir": false,
"estimatedCoverage": "unknown",
"gaps": [],
"note": "Install Claude Code CLI for AI-powered test gap analysis"
}

@ -0,0 +1 @@
sha256:6141a8ea990c5063b77e090ae8f37f9c539d8aa8f58dcceb30f3a82f97e57319

@ -0,0 +1,42 @@
{
"adoptedAt": 1784565886864,
"championId": "sha256:6141a8ea990c5063b77e090ae8f37f9c539d8aa8f58dcceb30f3a82f97e57319",
"manifest": {
"schema": "ruflo.proven-config/v1",
"policy": {
"ref": "sha256:6141a8ea990c5063b77e090ae8f37f9c539d8aa8f58dcceb30f3a82f97e57319",
"value": {
"alpha": 0.3,
"subjectWeight": 1,
"mmrLambda": 0.5,
"bodyWeight": 1.5,
"typePenaltyFactor": 0.5
}
},
"layer": "framework/node-cli",
"compatibility": {
"ruflo": ">=3.24.0"
},
"benchmark": {
"corpus": "ADR-081-labelled-v1",
"corpusHash": "sha256:2f700b5c363e20a3bd88ce2bc9b87bbbbaa61732c6177894c6ec37890f888982"
},
"receipt": {
"heldOutDelta": 0.07381404928570845,
"redblue": "PASS",
"drift": 0,
"canary": {
"rollbackRate": 0,
"latencyP95": 244.612458000076,
"costPerTask": 0
},
"receiptCoverage": 1
},
"platform": [
"linux",
"macOS",
"windows"
]
},
"previous": ""
}

@ -0,0 +1,75 @@
// Go-live data correction: the earlier migrate-sqlite-to-pg.ts run pulled from local SQLite,
// which turned out to be stale/near-empty — the real working data was in a local Postgres
// instance instead. This wipes the TARGET (cloud) hq database's public schema and reloads it
// from the real SOURCE (local Postgres). Both engines are Postgres here, so every column type
// matches exactly — no conversion needed.
// SOURCE_DATABASE_URL and TARGET_DATABASE_URL must both be set explicitly, no defaults, so
// this can never accidentally point at (or wipe) the wrong database.
//
// Run from repo root:
// SOURCE_DATABASE_URL=postgres://hq:<local-pw>@127.0.0.1:5432/hq ^
// TARGET_DATABASE_URL=postgres://postgres:<cloud-pw>@13.232.53.115:5432/hq ^
// npx tsx apps/hq/scripts/migrate-pg-to-pg.ts
import { Pool } from 'pg'
import { openPgDb, type PgDb } from '../src/db-pg'
const SKIP_TABLES = new Set(['stg_client', 'stg_invoice', 'schema_migrations'])
async function main(): Promise<void> {
const sourceUrl = process.env['SOURCE_DATABASE_URL']
const targetUrl = process.env['TARGET_DATABASE_URL']
if (sourceUrl === undefined || sourceUrl === '') throw new Error('Set SOURCE_DATABASE_URL (local Postgres).')
if (targetUrl === undefined || targetUrl === '') throw new Error('Set TARGET_DATABASE_URL (cloud Postgres).')
if (sourceUrl === targetUrl) throw new Error('SOURCE and TARGET are identical — refusing.')
// Wipe the target's public schema completely, then let the app's own migrations rebuild
// it clean — guarantees the target schema matches this codebase exactly, not a stale copy.
const wipePool = new Pool({ connectionString: targetUrl, max: 1 })
await wipePool.query('DROP SCHEMA public CASCADE')
await wipePool.query('CREATE SCHEMA public')
await wipePool.end()
console.log('Target schema wiped.')
const target = (await openPgDb(targetUrl)) as PgDb // recreates schema via migrations, empty
const source = (await openPgDb(sourceUrl)) as PgDb // idempotent no-op if already migrated
const tables = await source.all<{ tablename: string }>(`SELECT tablename FROM pg_tables WHERE schemaname='public'`)
const client = await target.pool.connect()
try {
await client.query('BEGIN')
// Load in discovery order and ignore FK/trigger checks — safe: one transaction into a
// target we just wiped and confirmed empty.
await client.query('SET LOCAL session_replication_role = replica')
for (const { tablename: name } of tables) {
if (SKIP_TABLES.has(name)) {
console.log(`skip ${name}`)
continue
}
const rows = await source.all<Record<string, unknown>>(`SELECT * FROM ${name}`)
if (rows.length === 0) {
console.log(`${name}: 0 rows`)
continue
}
const cols = Object.keys(rows[0]!)
const placeholders = cols.map((_, i) => `$${i + 1}`).join(',')
const insertSql = `INSERT INTO ${name} (${cols.join(',')}) VALUES (${placeholders})`
for (const row of rows) {
await client.query(insertSql, cols.map((c) => row[c]))
}
console.log(`${name}: ${rows.length} row(s) migrated`)
}
await client.query('COMMIT')
} catch (err) {
await client.query('ROLLBACK')
throw err
} finally {
client.release()
}
console.log('Migration complete.')
}
main().catch((e: unknown) => {
console.error(e)
process.exit(1)
})

@ -0,0 +1,76 @@
// Go-live data carry-over: copies every row from the local SQLite dev database into the
// target (cloud) Postgres database, preserving values and ids as-is — this is a one-time
// full copy, not the "born in Postgres" empty-start path in docs/DEPLOY-HQ.md.
// Column types are a straight 1:1 map on this schema (money is bigint paise, flags stay
// integer, JSON payloads stay text — see migrations-pg.ts header), so no value conversion
// is needed; every column is copied through unchanged.
// Refuses to run if the target already has staff_user rows, so a re-run (or pointing at
// the wrong DATABASE_URL) can't silently duplicate or clobber data.
// stg_client/stg_invoice are APEX-import scratch tables, not real data — skipped.
//
// Run from repo root, source = local ./data/hq.db (or HQ_DATA_DIR), target = DATABASE_URL:
// DATABASE_URL=postgres://postgres:<pw>@<host>:5432/hq npx tsx apps/hq/scripts/migrate-sqlite-to-pg.ts
import { openDb, type SqliteDb } from '../src/db'
import { openPgDb, type PgDb } from '../src/db-pg'
const SKIP_TABLES = new Set(['stg_client', 'stg_invoice', 'schema_migrations'])
async function main(): Promise<void> {
const pgUrl = process.env['DATABASE_URL']
if (pgUrl === undefined || pgUrl === '') {
throw new Error('Set DATABASE_URL to the target (cloud) Postgres connection string.')
}
const sqlite = openDb(process.env['HQ_DATA_DIR']) as SqliteDb
const pg = (await openPgDb(pgUrl)) as PgDb // schema is created by migrations here; seedIfEmpty is NOT called
const existingStaff = (await pg.get<{ n: number }>(`SELECT COUNT(*) AS n FROM staff_user`))!.n
if (existingStaff > 0) {
throw new Error(
`Target already has ${existingStaff} staff_user row(s) — refusing to import over existing data. ` +
`Wrong DATABASE_URL, or this has already been migrated.`,
)
}
const tables = sqlite.raw
.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'`)
.all() as { name: string }[]
const client = await pg.pool.connect()
try {
await client.query('BEGIN')
// Load in table-discovery order and ignore FK/trigger checks — safe here because the
// whole load is one transaction into a database we just confirmed is empty.
await client.query('SET LOCAL session_replication_role = replica')
for (const { name } of tables) {
if (SKIP_TABLES.has(name)) {
console.log(`skip ${name}`)
continue
}
const rows = sqlite.raw.prepare(`SELECT * FROM ${name}`).all() as Record<string, unknown>[]
if (rows.length === 0) {
console.log(`${name}: 0 rows`)
continue
}
const cols = Object.keys(rows[0]!)
const placeholders = cols.map((_, i) => `$${i + 1}`).join(',')
const insertSql = `INSERT INTO ${name} (${cols.join(',')}) VALUES (${placeholders})`
for (const row of rows) {
await client.query(insertSql, cols.map((c) => row[c]))
}
console.log(`${name}: ${rows.length} row(s) migrated`)
}
await client.query('COMMIT')
} catch (err) {
await client.query('ROLLBACK')
throw err
} finally {
client.release()
}
console.log('Migration complete.')
}
main().catch((e: unknown) => {
console.error(e)
process.exit(1)
})
Loading…
Cancel
Save