WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
const assert = require('node:assert/strict')
|
||||
const crypto = require('node:crypto')
|
||||
const fs = require('node:fs')
|
||||
const path = require('node:path')
|
||||
const { execFileSync } = require('node:child_process')
|
||||
|
||||
const root = path.resolve(__dirname, '../..')
|
||||
const modelID = '404e005b-83ae-5e2e-a3b3-999f9d5cd47e'
|
||||
const receiptPath = path.join(root, 'acceptance/graph-model-thinking-fix-1789827335493.json')
|
||||
const docker = args => execFileSync('docker', ['--context', 'colima-yawan', ...args],
|
||||
{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: 8 * 1024 * 1024, timeout: 30000 })
|
||||
function query(sql) {
|
||||
return JSON.parse(execFileSync('docker', ['--context', 'colima-yawan', 'exec', '-i', 'WeKnora-postgres',
|
||||
'psql', '-X', '-qAt', '-v', 'ON_ERROR_STOP=1', '-U', 'postgres', '-d', 'yawan_native_acceptance_20260915'],
|
||||
{ input: `BEGIN READ ONLY;\n${sql}\nCOMMIT;`, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 15000 }))
|
||||
}
|
||||
const literal = value => "'" + JSON.stringify(value).replaceAll("'", "''") + "'::jsonb"
|
||||
function decodeCopyField(value) {
|
||||
if (value === '\\N') return null
|
||||
const escapes = { b: '\b', f: '\f', n: '\n', r: '\r', t: '\t', v: '\v', '\\': '\\' }
|
||||
return value.replace(/\\([bfnrtv\\])/g, (_, key) => escapes[key])
|
||||
}
|
||||
function decrypt(value, key) {
|
||||
if (!value || !value.startsWith('enc:v1:')) return value || ''
|
||||
const data = Buffer.from(value.slice(7), 'base64url')
|
||||
const decipher = crypto.createDecipheriv('aes-256-gcm', key, data.subarray(0, 12))
|
||||
decipher.setAuthTag(data.subarray(-16))
|
||||
return Buffer.concat([decipher.update(data.subarray(12, -16)), decipher.final()]).toString('utf8')
|
||||
}
|
||||
|
||||
let stage = 'read_backup'
|
||||
try {
|
||||
const receipt = JSON.parse(fs.readFileSync(receiptPath, 'utf8'))
|
||||
const dump = docker(['run', '--rm', '--entrypoint', 'pg_restore',
|
||||
'-v', `${root}/releases/public-20260919-214700:/backup:ro`,
|
||||
'paradedb/paradedb:v0.22.2-pg17', '--data-only', '--table=models', '--file=-', '/backup/yawan_native_acceptance_20260915.dump'])
|
||||
stage = 'parse_backup'
|
||||
const copy = /COPY public\.models \(([^)]+)\) FROM stdin;\n([\s\S]*?)\n\\\./.exec(dump)
|
||||
assert.ok(copy)
|
||||
const columns = copy[1].split(', ')
|
||||
const rows = copy[2].split('\n').map(line => line.split('\t').map(decodeCopyField))
|
||||
const fields = rows.find(row => row[columns.indexOf('id')] === modelID)
|
||||
assert.equal(fields?.length, columns.length)
|
||||
const oldRow = Object.fromEntries(columns.map((name, index) => [name, fields[index]]))
|
||||
oldRow.parameters = JSON.parse(oldRow.parameters)
|
||||
stage = 'compare_database'
|
||||
const audit = query(`WITH previous AS (SELECT (jsonb_populate_record(null::models, ${literal(oldRow)})).*),
|
||||
current AS (SELECT * FROM models WHERE id='${modelID}' AND tenant_id=10059)
|
||||
SELECT json_build_object(
|
||||
'backup_fingerprint',encode(sha256(convert_to((to_jsonb(p) - 'updated_at' - 'parameters' ||
|
||||
jsonb_build_object('parameters',p.parameters #- '{extra_config,thinking_control}'))::text,'UTF8')),'hex'),
|
||||
'metadata_preserved',(to_jsonb(p)-'parameters'-'updated_at')=(to_jsonb(c)-'parameters'-'updated_at'),
|
||||
'parameters_preserved', (p.parameters-'api_key'-'app_secret' #- '{extra_config,thinking_control}')=
|
||||
(c.parameters-'api_key'-'app_secret' #- '{extra_config,thinking_control}'),
|
||||
'parameters',c.parameters) FROM previous p CROSS JOIN current c;`)
|
||||
stage = 'compare_snapshot'
|
||||
assert.equal(audit.backup_fingerprint, receipt.before.unchanged_fields_sha256)
|
||||
stage = 'compare_metadata'
|
||||
assert.equal(audit.metadata_preserved, true)
|
||||
assert.equal(audit.parameters_preserved, true)
|
||||
assert.equal(oldRow.parameters.extra_config.thinking_control, 'none')
|
||||
assert.equal(audit.parameters.extra_config.thinking_control, 'thinking_type')
|
||||
stage = 'compare_credentials'
|
||||
const environment = JSON.parse(docker(['inspect', 'yawan-native-acceptance', '--format', '{{json .Config.Env}}']))
|
||||
const aes = Buffer.from(environment.find(value => value.startsWith('SYSTEM_AES_KEY=')).slice('SYSTEM_AES_KEY='.length))
|
||||
assert.equal(aes.length, 32)
|
||||
for (const name of ['api_key', 'app_secret']) {
|
||||
assert.ok(decrypt(oldRow.parameters[name], aes) === decrypt(audit.parameters[name], aes))
|
||||
}
|
||||
const verification = { checked_at: new Date().toISOString(), passed: true, model_calls: 0, production_write: false,
|
||||
backup_matches_immediate_before_snapshot: true, unrelated_fields_preserved: true, credential_plaintexts_preserved: true,
|
||||
ciphertext_changed_due_to_aes_gcm_nonce: oldRow.parameters.api_key !== audit.parameters.api_key,
|
||||
effective_change: { thinking_control: { before: 'none', after: 'thinking_type' } } }
|
||||
stage = 'write_report'
|
||||
fs.writeFileSync(path.join(root, 'acceptance/graph-model-thinking-fix-verification-20260919.json'),
|
||||
JSON.stringify(verification, null, 2) + '\n', { flag: 'wx', mode: 0o600 })
|
||||
console.log(JSON.stringify(verification))
|
||||
} catch {
|
||||
console.error(`Read-only model configuration comparison failed at ${stage}; no secrets or raw database output are displayed.`)
|
||||
process.exitCode = 1
|
||||
}
|
||||
Reference in New Issue
Block a user