feat: compile GLS protocols into native runtime guards
This commit is contained in:
parent
96769846ac
commit
d6b1290e1c
13 changed files with 1909 additions and 6 deletions
|
|
@ -0,0 +1,139 @@
|
|||
import { createHash } from 'node:crypto'
|
||||
import { readdir, readFile, writeFile } from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
|
||||
const args = new Map()
|
||||
for (let index = 2; index < process.argv.length; index += 2) {
|
||||
args.set(process.argv[index], process.argv[index + 1])
|
||||
}
|
||||
|
||||
const sourceRoot = args.get('--source-root')
|
||||
const sourceCommit = args.get('--source-commit')
|
||||
const output = args.get('--output')
|
||||
const projectionsPath = args.get('--projections')
|
||||
|
||||
if (!sourceRoot || !sourceCommit || !output || !projectionsPath) {
|
||||
throw new Error('usage: compile-gls-runtime-registry.mjs --source-root <REPO-012/gls> --source-commit <sha> --output <registry.json> --projections <projections.json>')
|
||||
}
|
||||
if (!/^[a-f0-9]{40}$/.test(sourceCommit)) throw new Error('source commit must be a full SHA-1')
|
||||
|
||||
async function walk(directory, prefix = '') {
|
||||
const entries = await readdir(directory, { withFileTypes: true })
|
||||
const paths = []
|
||||
for (const entry of entries) {
|
||||
const relative = prefix ? `${prefix}/${entry.name}` : entry.name
|
||||
if (entry.isDirectory()) paths.push(...await walk(path.join(directory, entry.name), relative))
|
||||
else paths.push(relative)
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
function sourceRank(relative) {
|
||||
if (!relative.includes('/')) return 0
|
||||
if (relative.startsWith('standards/')) return 1
|
||||
if (relative.includes('/notion-export/')) return 3
|
||||
return 2
|
||||
}
|
||||
|
||||
function cleanHeading(line, id) {
|
||||
return line
|
||||
.replace(/^#+\s*/, '')
|
||||
.replaceAll('**', '')
|
||||
.replace(new RegExp(`^${id}\\s*[·::-]?\\s*`), '')
|
||||
.trim() || id
|
||||
}
|
||||
|
||||
function sourceStatus(raw) {
|
||||
const line = raw.split(/\r?\n/, 100).find((item) => /^\s*(?:>|[-*]\s*)?(?:状态|Status)\s*[::]/i.test(item))
|
||||
if (!line) return 'UNSPECIFIED_SOURCE_STATUS'
|
||||
return line.replace(/^\s*(?:>|[-*]\s*)?(?:状态|Status)\s*[::]\s*/i, '').replaceAll('`', '').trim()
|
||||
}
|
||||
|
||||
const projectionManifest = JSON.parse(await readFile(projectionsPath, 'utf8'))
|
||||
if (projectionManifest.schema !== 'hololake.gls-executable-projections/v1' || projectionManifest.source_commit !== sourceCommit) {
|
||||
throw new Error('projection manifest does not match the selected REPO-012 source commit')
|
||||
}
|
||||
|
||||
const candidates = (await walk(sourceRoot))
|
||||
.map((relative) => ({ relative, match: path.basename(relative).match(/^(GLS-\d{4}).*\.hdlp$/) }))
|
||||
.filter((item) => item.match)
|
||||
.map((item) => ({ id: item.match[1], relative: item.relative }))
|
||||
|
||||
const grouped = new Map()
|
||||
for (const candidate of candidates) {
|
||||
const group = grouped.get(candidate.id) || []
|
||||
group.push(candidate.relative)
|
||||
grouped.set(candidate.id, group)
|
||||
}
|
||||
|
||||
const protocols = []
|
||||
for (const [id, sources] of [...grouped.entries()].sort(([left], [right]) => left.localeCompare(right))) {
|
||||
sources.sort((left, right) => sourceRank(left) - sourceRank(right) || left.localeCompare(right))
|
||||
const relative = sources[0]
|
||||
const raw = await readFile(path.join(sourceRoot, relative), 'utf8')
|
||||
const heading = raw.split(/\r?\n/).find((line) => line.startsWith('#') && line.includes(id))
|
||||
const projection = projectionManifest.projections[id]
|
||||
protocols.push({
|
||||
id,
|
||||
title: cleanHeading(heading || id, id),
|
||||
source_status: sourceStatus(raw),
|
||||
source_path: `gls/${relative}`,
|
||||
source_sha256: createHash('sha256').update(raw).digest('hex'),
|
||||
alternate_source_count: sources.length - 1,
|
||||
projection_state: projection ? 'EXECUTABLE_PROJECTION' : 'INVENTORIED_NOT_EXECUTABLE',
|
||||
projection_kind: projection?.projection_kind || null,
|
||||
adapter: projection?.adapter || null,
|
||||
event_kinds: projection?.event_kinds || [],
|
||||
dependencies: projection?.dependencies || [],
|
||||
})
|
||||
}
|
||||
|
||||
const known = new Set(protocols.map((protocol) => protocol.id))
|
||||
for (const [id, projection] of Object.entries(projectionManifest.projections)) {
|
||||
if (!known.has(id)) throw new Error(`projection references an unknown protocol: ${id}`)
|
||||
for (const dependency of projection.dependencies) {
|
||||
if (!projectionManifest.projections[dependency]) {
|
||||
throw new Error(`${id} depends on a protocol without an executable projection: ${dependency}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const visiting = new Set()
|
||||
const visited = new Set()
|
||||
function visit(id) {
|
||||
if (visiting.has(id)) throw new Error(`executable protocol dependency cycle at ${id}`)
|
||||
if (visited.has(id)) return
|
||||
visiting.add(id)
|
||||
for (const dependency of projectionManifest.projections[id].dependencies) visit(dependency)
|
||||
visiting.delete(id)
|
||||
visited.add(id)
|
||||
}
|
||||
for (const id of Object.keys(projectionManifest.projections)) visit(id)
|
||||
|
||||
const executableCount = protocols.filter((protocol) => protocol.projection_state === 'EXECUTABLE_PROJECTION').length
|
||||
const registry = {
|
||||
schema: 'hololake.gls-protocol-runtime-registry/v1',
|
||||
record_id: 'HLP-GLS-PROTOCOL-RUNTIME-001',
|
||||
source: {
|
||||
repository: 'REPO-012',
|
||||
commit: sourceCommit,
|
||||
root: 'gls',
|
||||
},
|
||||
compiler: {
|
||||
source_protocol_is_human_and_machine_authority: true,
|
||||
raw_protocol_text_executed: false,
|
||||
arbitrary_protocol_code_allowed: false,
|
||||
executable_projection_requires_explicit_adapter: true,
|
||||
unprojected_protocol_behavior: 'INVENTORIED_NOT_EXECUTABLE',
|
||||
dependency_cycles: 'REJECT',
|
||||
unknown_protocol: 'FAIL_CLOSED',
|
||||
},
|
||||
protocol_count: protocols.length,
|
||||
executable_projection_count: executableCount,
|
||||
inventoried_not_executable_count: protocols.length - executableCount,
|
||||
protocols,
|
||||
}
|
||||
|
||||
await writeFile(output, `${JSON.stringify(registry, null, 2)}\n`)
|
||||
console.log(`GLS_RUNTIME_REGISTRY_COMPILED protocols=${protocols.length} executable=${executableCount} output=${output}`)
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import assert from 'node:assert/strict'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import test from 'node:test'
|
||||
|
||||
const registryUrl = new URL('../contracts/gls-runtime-registry.json', import.meta.url)
|
||||
|
||||
test('compiled GLS registry inventories every current numbered protocol without executing raw text', async () => {
|
||||
const registry = JSON.parse(await readFile(registryUrl, 'utf8'))
|
||||
|
||||
assert.equal(registry.schema, 'hololake.gls-protocol-runtime-registry/v1')
|
||||
assert.equal(registry.source.repository, 'REPO-012')
|
||||
assert.equal(registry.source.commit, '2598fbfba8caf64c7ab9740a3036c5aab977502e')
|
||||
assert.equal(registry.compiler.raw_protocol_text_executed, false)
|
||||
assert.equal(registry.compiler.arbitrary_protocol_code_allowed, false)
|
||||
assert.equal(registry.compiler.unprojected_protocol_behavior, 'INVENTORIED_NOT_EXECUTABLE')
|
||||
assert.equal(registry.protocol_count, 75)
|
||||
assert.equal(new Set(registry.protocols.map((protocol) => protocol.id)).size, 75)
|
||||
assert.ok(registry.protocols.every((protocol) => /^[a-f0-9]{64}$/.test(protocol.source_sha256)))
|
||||
})
|
||||
|
||||
test('only explicit deterministic projections enter the runtime enforcement set', async () => {
|
||||
const registry = JSON.parse(await readFile(registryUrl, 'utf8'))
|
||||
const protocols = Object.fromEntries(registry.protocols.map((protocol) => [protocol.id, protocol]))
|
||||
|
||||
assert.equal(protocols['GLS-0253'].projection_state, 'EXECUTABLE_PROJECTION')
|
||||
assert.equal(protocols['GLS-0253'].adapter, 'zero-core-numbering')
|
||||
assert.ok(protocols['GLS-0253'].event_kinds.includes('IDENTITY_ROUTE'))
|
||||
assert.equal(protocols['GLS-0003'], undefined)
|
||||
assert.equal(registry.executable_projection_count, 4)
|
||||
assert.equal(registry.inventoried_not_executable_count, 71)
|
||||
})
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import assert from 'node:assert/strict'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import test from 'node:test'
|
||||
|
||||
const contractUrl = new URL('../contracts/zero-core-numbering-kernel.json', import.meta.url)
|
||||
|
||||
test('zero-core numbering kernel pins the canonical authority map and fails closed', async () => {
|
||||
const contract = JSON.parse(await readFile(contractUrl, 'utf8'))
|
||||
|
||||
assert.equal(contract.schema, 'hololake.zero-core-numbering-kernel/v1')
|
||||
assert.equal(contract.authority.map_id, 'GH-IDENTITY-AUTHORITY-MAP-001')
|
||||
assert.equal(contract.authority.source_commit, '2598fbfba8caf64c7ab9740a3036c5aab977502e')
|
||||
assert.equal(contract.runtime.number_shape_is_authority, false)
|
||||
assert.equal(contract.runtime.unknown_number, 'FAIL_CLOSED')
|
||||
assert.equal(contract.runtime.automatic_identity_issuance, false)
|
||||
})
|
||||
|
||||
test('only registered human namespaces can enter a human route', async () => {
|
||||
const contract = JSON.parse(await readFile(contractUrl, 'utf8'))
|
||||
const namespaces = Object.fromEntries(contract.namespaces.map((item) => [item.id, item]))
|
||||
|
||||
assert.equal(namespaces.ICE_GL.subject_kind, 'FIFTH_DOMAIN_HUMAN')
|
||||
assert.equal(namespaces.ICE_GL.human_entry, true)
|
||||
assert.equal(namespaces.TCS_GL.subject_kind, 'ZERO_SENSE_HUMAN_CONTROLLER_TEAM_MEMBER')
|
||||
assert.equal(namespaces.TCS_GL.human_entry, true)
|
||||
assert.equal(namespaces.ICE_P.human_entry, false)
|
||||
assert.equal(namespaces.ICE_BB.human_entry, false)
|
||||
})
|
||||
Loading…
Reference in a new issue