feat: register deterministic HoloLake stage relay

This commit is contained in:
冰朔 2026-08-11 21:37:49 +08:00
commit 5f86116303
11 changed files with 1065 additions and 4 deletions

View file

@ -0,0 +1,32 @@
{
"schema": "hololake.stage-relay-registration-receipt/v1",
"receipt_id": "HLP-STAGE-RELAY-001-20260811",
"development_id": "DEV-20260811-008",
"record_id": "HLP-STAGE-RELAY-001",
"state": "SOURCE_IMPLEMENTED_TESTED_AND_REGISTERED_NO_SUCCESSOR_THREAD_CREATED_DURING_ACCEPTANCE",
"source": "server-tools/lake-lamp-continuity-guard/stage-relay.mjs",
"runtime": "/Volumes/JZAO/HoloLake/persona-runtime/continuity-guard/scripts/stage-relay.mjs",
"formal_host_capabilities": [
"codex_app.create_thread",
"codex_app.list_threads",
"codex_app.automation_update"
],
"acceptance": {
"finalizer_evidence_gate": 100,
"duplicate_prepare_idempotency": 100,
"unknown_create_result_reconciliation": 100,
"successor_ready_live_readback": 100,
"active_stage_read_only": 100,
"human_boundary_pause": 100,
"final_product_stop": 100,
"continuity_lane_regression": 100,
"continuity_sentinel_regression": 100
},
"boundaries": [
"No successor HoloLake development task was created during mechanism acceptance.",
"The existing hololake heartbeat remains bound to DEV-20260811-008 until final closeout.",
"No cron, private database or chat statement is accepted as a Codex thread receipt.",
"JZAO collaboration remains the only coordination truth store."
],
"recorded_at": "2026-08-11T13:35:00.000Z"
}

View file

@ -0,0 +1,55 @@
{
"schema": "hololake.stage-relay-map/v1",
"map_id": "HLP-STAGE-RELAY-001",
"version": "2026-08-11.1",
"state": "SOURCE_IMPLEMENTED_TESTED_NOT_YET_USED_TO_CREATE_A_SUCCESSOR",
"development_id": "DEV-20260811-008",
"runtime_source": "server-tools/lake-lamp-continuity-guard/stage-relay.mjs",
"local_runtime": "/Volumes/JZAO/HoloLake/persona-runtime/continuity-guard/scripts/stage-relay.mjs",
"coordination_store": "HL-PERSONA-RUNTIME-LOCAL-001/continuity-memory/collaboration",
"second_truth_store_allowed": false,
"source_completion_gates": [
"FINALIZER_PASS_100",
"REMOTE_FULL_SHA",
"FRESH_CLONE_READBACK_100",
"FINAL_RECEIPTS_SHA256_100",
"SOURCE_LEASES_RELEASED_100",
"COMPLETION_STOPPED_100"
],
"next_stage_inputs": [
"HLP-CURRENT-ARCH-001",
"LATEST_HUMAN_CORRECTIONS",
"PREVIOUS_STAGE_FINALIZER_RECEIPT",
"UNFINISHED_BINARY_GATES"
],
"host_adapter": {
"host": "HOST-CODEX-MACOS-001",
"create_tool": "codex_app.create_thread",
"list_tool": "codex_app.list_threads",
"thread_id_required": true,
"script_or_chat_may_claim_thread_created": false
},
"idempotency": {
"key": "SHA256_SOURCE_DEV_PLUS_FINALIZER_SHA256",
"maximum_successor_threads_per_source_completion": 1,
"unknown_create_result": "RECONCILE_COMPLETE_HOST_SNAPSHOT_BEFORE_RETRY"
},
"successor_ready_gates": [
"PERSONA_RESTORE_100",
"CURRENT_ARCHITECTURE_RESTORE_100",
"NEW_DEV_LANE_REGISTRATION_100",
"EXACT_RESOURCE_LEASES_100",
"FORMAL_TEN_MINUTE_HEARTBEAT_BINDING_100"
],
"human_pause_boundaries": [
"PAYMENT_OR_BUDGET",
"ACCOUNT_LOGIN",
"VERIFICATION_OR_2FA",
"LEGAL_OR_PUBLIC_COMMITMENT",
"IRREVERSIBLE_OR_UNAUTHORIZED_EXTERNAL_DECISION",
"PHYSICAL_ACTION",
"PRODUCTION_CUTOVER",
"MISSING_NEW_HUMAN_WILL"
],
"final_stop": "ALL_BINARY_ACCEPTANCE_100_AND_NO_UNFINISHED_GATE_AND_NO_OPEN_BLOCKER_AND_FINAL_PRODUCT_CLOSEOUT_RECEIPT_PASS_100"
}

View file

@ -2,7 +2,7 @@
"schema": "guanghu.host-skill-navigation-map/v1",
"map_id": "GLW-HOST-SKILL-NAV-001",
"lighthouse_id": "SYS-GLW-LTH-0001",
"version": "2026-08-11.1",
"version": "2026-08-11.2",
"state": "CURRENT",
"hosts": [
{
@ -37,6 +37,38 @@
}
],
"intents": [
{
"id": "INTENT-HOLOLAKE-STAGE-RELAY-001",
"phrases": [
"HoloLake阶段自动接力守卫",
"阶段接力监督器",
"创建下一阶段Codex任务",
"successor-ready"
],
"skill_id": "GHS-007-MEMORY-CONTINUITY-SELF-BUILD",
"target_id": "HLP-STAGE-RELAY-001",
"transport": "CODEX_FORMAL_CREATE_THREAD_AND_HEARTBEAT",
"executor": "server-tools/lake-lamp-continuity-guard/stage-relay.mjs",
"sequence": [
"observe_active_stage_read_only",
"verify_source_completion_binary_gates",
"resolve_HLP_CURRENT_ARCH_001_and_latest_human_corrections",
"select_one_unfinished_binary_gate",
"reserve_one_immutable_successor_contract",
"create_successor_with_codex_formal_create_thread",
"reconcile_unknown_create_result_before_retry",
"wait_for_successor_ready_live_readback",
"stop_old_relay_supervisor"
],
"stop_conditions": [
"SOURCE_COMPLETION_EVIDENCE_NOT_100",
"CURRENT_ARCHITECTURE_UNREADABLE",
"PAUSE_FOR_HUMAN",
"CREATE_RESULT_UNKNOWN_REQUIRES_RECONCILIATION",
"SUCCESSOR_READY_NOT_100"
],
"authority": "CURRENT_USER_AUTHORIZATION_AND_IMMUTABLE_STAGE_CONTRACT_ONLY"
},
{
"id": "INTENT-REPOSITORY-PUBLISH-001",
"phrases": [

View file

@ -2,7 +2,7 @@
"schema": "guanghu.lighthouse-path-registry/v1",
"registry_id": "GLW-LIGHTHOUSE-PATH-REGISTRY-001",
"lighthouse_id": "SYS-GLW-LTH-0001",
"version": "2026-08-11.1",
"version": "2026-08-11.2",
"state": "CURRENT_CANONICAL",
"source_repository": "REPO-012",
"source_branch": "main",
@ -76,6 +76,15 @@
"offline": "/Volumes/JZAO/HoloLake/source-mirrors/hololake-system-architecture.git",
"truth_policy": "ONLINE_REPO_014_MAIN_FIRST_EXACT_SHA_REQUIRED"
},
{
"id": "HLP-STAGE-RELAY-001",
"kind": "hololake_deterministic_stage_relay_supervisor",
"state": "CURRENT",
"online": "routing/hololake-stage-relay-map.json",
"executor": "server-tools/lake-lamp-continuity-guard/stage-relay.mjs",
"offline": "/Volumes/JZAO/HoloLake/persona-runtime/continuity-guard/scripts/stage-relay.mjs",
"truth_policy": "SAME_CONTINUITY_LEDGER_FORMAL_CODEX_THREAD_TOOLS_AND_BINARY_RECEIPTS_ONLY"
},
{
"id": "GH-AIOS-PARADIGM-MAP-001",
"kind": "paradigm_ai_language_persona_operating_system_architecture",

View file

@ -1,7 +1,7 @@
{
"schema": "guanghu.public-navigation-anchor/v1",
"anchor_id": "GLW-PUBLIC-NAV-ANCHOR-001",
"version": "2026-08-11.1",
"version": "2026-08-11.2",
"state": "CURRENT_CANONICAL",
"repository_id": "REPO-012",
"branch": "main",
@ -48,11 +48,16 @@
"lighthouse_paths": {
"path": "routing/lighthouse-path-registry.json",
"id": "GLW-LIGHTHOUSE-PATH-REGISTRY-001",
"version": "2026-08-11.1"
"version": "2026-08-11.2"
},
"host_skills": {
"path": "routing/host-skill-navigation-map.json",
"id": "GLW-HOST-SKILL-NAV-001",
"version": "2026-08-11.2"
},
"hololake_stage_relay": {
"path": "routing/hololake-stage-relay-map.json",
"id": "HLP-STAGE-RELAY-001",
"version": "2026-08-11.1"
},
"origin_domain": {

View file

@ -36,3 +36,14 @@ node finalize-development.mjs \
默认自动识别 `target``coverage`、Python/Node 工具缓存等安全候选;其他缓存必须用
`--cleanup-path` 明确声明并继续接受同一套路径、Git 忽略和活跃进程门禁。源码、会话、
人格记忆、用户输出、交付物及未被忽略的目录永不进入自动删除范围。
## 阶段自动接力守卫
`stage-relay.mjs` 在同一小湖灯账本中监督已完成阶段,并生成一个不可变的后继任务契约。
它只认 finalizer、远端完整 SHA、全新副本、最终回执、租约释放和
`completion-stopped` 的二值证据;阶段仍运行时只读观察。
脚本不直接创建任务。它产生带稳定请求号和幂等键的宿主动作,由 Codex 正式
`create_thread` 执行并回写真实 `threadId`。创建结果未知时先完整核对任务列表,禁止直接
重发。新任务恢复人格与架构、登记 DEV、领取精确租约、建立十分钟 heartbeat 并形成
`successor-ready` 后,旧监督器才可停止。完整状态机见 `stage-relay-state-machine.md`

View file

@ -0,0 +1,66 @@
# HoloLake 阶段自动接力守卫
`stage-relay.mjs` 是小湖灯连续性账本内的确定性接力投影,不是第二协调真相源,也不直接
调用私有接口创建任务。它只在现有 `collaboration/` 下保存来源回执摘要、不可变任务契约、
正式 Codex 宿主动作请求和宿主回执。
## 状态机
```text
OBSERVING_ACTIVE_STAGE (无写入)
└─ finalizer + exact remote SHA + fresh clone + final receipt
+ lease release + completion-stopped 全部为100
├─ human boundary ─> PAUSE_FOR_HUMAN
├─ complete product ─> FINAL_PRODUCT_COMPLETE ─> stop last heartbeat
└─ one unfinished gate ─> CREATE_THREAD_REQUIRED
└─ dispatch-create ─> CREATE_THREAD_IN_FLIGHT
├─ success ─> WAITING_SUCCESSOR_READY
├─ confirmed absent ─> RETRY_CREATE_THREAD
└─ unknown ─> RECONCILE_CREATE_RESULT
├─ matching thread ─> WAITING_SUCCESSOR_READY
└─ complete host snapshot confirms absent ─> RETRY_CREATE_THREAD
└─ successor-ready gates all100 ─> SUCCESSOR_READY ─> stop old relay supervisor
```
同一来源完成回执的幂等键固定为 `sha256(source DEV + finalizer file sha256)`;状态文件按
来源 DEV 唯一,后继 DEV 预约使用排他文件。创建结果未知时禁止重发,必须先读取 Codex
完整任务列表,以稳定 `SUCCESSOR-...` 请求号核对;只有完整快照确认不存在时才可重试。
## 正式宿主适配
1. `prepare` 返回 `CODEX_CREATE_THREAD` 动作,但不创建任务。
2. 调度人格先执行 `dispatch-create`,再调用 Codex 正式 `create_thread`
3. 成功后立即 `record-created`;超时或未知结果执行 `record-create-failure --outcome unknown`
4. `RECONCILE_CREATE_RESULT` 时只能使用 Codex 正式 `list_threads/read_thread` 核对,不得重发。
5. 新任务恢复人格与当前架构、登记自己的 DEV、取得精确租约、建立正式十分钟 heartbeat
签到时把 `--relay-contract-fingerprint` 写入车道,再写
`hololake.successor-ready-receipt/v1` 并由旧监督器 `record-ready` 回读。
脚本、聊天、cron、数据库行、文件夹或自动化提示都不能冒充 `create_thread` 成功;只有正式
工具返回的 `threadId` 和后续活车道回读才算任务存在。
## PAUSE_FOR_HUMAN
以下任一类型直接暂停,禁止靠创建任务绕过:付款或预算、账号登录、验证码/2FA、法律或
公开承诺、不可逆或未授权外部决定、物理动作、生产切流、缺少新的人类意志。
## 失败谱系
- `SOURCE_COMPLETION_STOPPED_NOT_100`:来源任务或 heartbeat 尚未完成停止。
- `SOURCE_LEASE_RELEASE_NOT_100`:来源仍持有资源。
- `FINALIZER_EVIDENCE_NOT_100`finalizer、远端 SHA、全新副本、fsck 或最终回执不完整。
- `CURRENT_ARCHITECTURE_EVIDENCE_NOT_100`:动态架构真相源不完整或不是当前正本。
- `CREATE_THREAD_IN_FLIGHT` 超时:进入 `RECONCILE_CREATE_RESULT`,不重发。
- `SUCCESSOR_READY_EVIDENCE_NOT_100`新任务签到、租约、heartbeat 或恢复门不是100。
- `SUCCESSOR_LIVE_REGISTRATION_MISMATCH`ready 文本与连续性账本现场不一致。
## 二值验收
运行:
```bash
node scripts/stage-relay.mjs self-test
```
必须同时得到 finalizer 证据门、重复 prepare 幂等、未知创建结果核对、新任务 ready 现场回读、
活跃阶段只读五项 `100`

View file

@ -0,0 +1,46 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://guanghulab.com/schemas/hldp/stage-relay-state.v1.schema.json",
"title": "HoloLake Stage Relay State",
"type": "object",
"required": [
"schema",
"source_development_id",
"source_thread_id",
"source_finalizer_sha256",
"source_remote_head",
"architecture",
"state",
"created_at",
"updated_at"
],
"properties": {
"schema": { "const": "hololake.stage-relay-state/v1" },
"source_development_id": { "type": "string", "pattern": "^DEV-[0-9]{8}-[0-9]{3}$" },
"source_thread_id": { "type": "string", "minLength": 1 },
"source_finalizer_path": { "type": "string", "minLength": 1 },
"source_finalizer_sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
"source_remote_head": { "type": "string", "pattern": "^[a-f0-9]{40}$" },
"architecture": { "type": "object" },
"state": {
"enum": [
"PAUSE_FOR_HUMAN",
"FINAL_PRODUCT_COMPLETE",
"CREATE_THREAD_REQUIRED",
"CREATE_THREAD_IN_FLIGHT",
"RECONCILE_CREATE_RESULT",
"RETRY_CREATE_THREAD",
"WAITING_SUCCESSOR_READY",
"SUCCESSOR_READY"
]
},
"idempotency_key": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
"successor_contract": { "type": "object" },
"create_request": { "type": "object" },
"successor_thread": { "type": ["object", "null"] },
"successor_ready_receipt": { "type": ["object", "null"] },
"created_at": { "type": "string", "format": "date-time" },
"updated_at": { "type": "string", "format": "date-time" }
},
"additionalProperties": true
}

View file

@ -0,0 +1,752 @@
#!/usr/bin/env node
import crypto from "node:crypto";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
const CANONICAL_ROOT =
"/Volumes/JZAO/HoloLake/persona-runtime/continuity-memory/collaboration";
const HUMAN_BOUNDARIES = new Set([
"PAYMENT_OR_BUDGET",
"ACCOUNT_LOGIN",
"VERIFICATION_OR_2FA",
"LEGAL_OR_PUBLIC_COMMITMENT",
"IRREVERSIBLE_OR_UNAUTHORIZED_EXTERNAL_DECISION",
"PHYSICAL_ACTION",
"PRODUCTION_CUTOVER",
"MISSING_NEW_HUMAN_WILL",
]);
const SHA40 = /^[a-f0-9]{40}$/u;
const SHA256 = /^[a-f0-9]{64}$/u;
const DEV_ID = /^DEV-(\d{8})-(\d{3})$/u;
function parseArgs(argv) {
const args = { command: argv[0] };
for (let index = 1; index < argv.length; index += 1) {
const key = argv[index];
if (!key.startsWith("--")) throw new Error(`UNKNOWN_ARGUMENT:${key}`);
const name = key.slice(2).replaceAll(/-([a-z])/gu, (_, letter) =>
letter.toUpperCase(),
);
if (name === "selfTest") args.selfTest = true;
else args[name] = argv[++index];
}
return args;
}
function required(args, key) {
const value = args[key];
if (typeof value !== "string" || !value.trim()) {
throw new Error(`${key.toUpperCase()}_REQUIRED`);
}
return value.trim();
}
function bool(value) {
if (value === true || value === "true") return true;
if (value === false || value === "false" || value == null) return false;
throw new Error(`BOOLEAN_REQUIRED:${value}`);
}
function now() {
return new Date().toISOString();
}
function readJson(file) {
return JSON.parse(fs.readFileSync(file, "utf8"));
}
function canonicalJson(value) {
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
if (value && typeof value === "object") {
return `{${Object.keys(value)
.sort()
.map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`)
.join(",")}}`;
}
return JSON.stringify(value);
}
function digest(value) {
return crypto.createHash("sha256").update(value).digest("hex");
}
function fileDigest(file) {
return digest(fs.readFileSync(file));
}
function writeJsonAtomic(file, value, exclusive = false) {
fs.mkdirSync(path.dirname(file), { recursive: true });
const body = `${JSON.stringify(value, null, 2)}\n`;
if (exclusive) {
fs.writeFileSync(file, body, { encoding: "utf8", flag: "wx" });
return;
}
const temporary = `${file}.${process.pid}.${crypto.randomUUID()}.tmp`;
fs.writeFileSync(temporary, body, { encoding: "utf8", flag: "wx" });
try {
fs.renameSync(temporary, file);
} catch (error) {
try { fs.unlinkSync(temporary); } catch { /* best effort */ }
throw error;
}
}
function storeRoot(args) {
if (!args.storeRoot) return CANONICAL_ROOT;
if (!bool(args.testMode)) throw new Error("NON_CANONICAL_STORE_FORBIDDEN");
const resolved = path.resolve(args.storeRoot);
const temporaryRoot = path.resolve(os.tmpdir());
if (!resolved.startsWith(`${temporaryRoot}${path.sep}`)) {
throw new Error("TEST_STORE_MUST_BE_TEMPORARY");
}
return resolved;
}
function ensureStore(root) {
if (root === CANONICAL_ROOT && !fs.existsSync("/Volumes/JZAO")) {
throw new Error("JZAO_NOT_MOUNTED_NO_SECOND_TRUTH_STORE");
}
for (const name of ["lanes", "leases", "stage-relays", "stage-relays/reservations"]) {
fs.mkdirSync(path.join(root, name), { recursive: true });
}
}
function statePath(root, developmentId) {
return path.join(root, "stage-relays", `${developmentId}.json`);
}
function listJson(directory) {
if (!fs.existsSync(directory)) return [];
return fs.readdirSync(directory)
.filter((name) => name.endsWith(".json") && !name.startsWith("._"))
.sort()
.map((name) => readJson(path.join(directory, name)));
}
function validateSourceCompletion(root, developmentId, finalizerPath) {
const lanePath = path.join(root, "lanes", `${developmentId}.json`);
if (!fs.existsSync(lanePath)) throw new Error("SOURCE_LANE_NOT_FOUND");
const lane = readJson(lanePath);
if (["active", "waiting"].includes(lane.status)) {
return { observing: true, lane };
}
if (
lane.status !== "completed" ||
lane.notification_state !== "stopped" ||
lane.completion_actions?.status !== "completed"
) {
throw new Error("SOURCE_COMPLETION_STOPPED_NOT_100");
}
const held = listJson(path.join(root, "leases"))
.filter((lease) => lease.development_id === developmentId);
if (held.length) throw new Error("SOURCE_LEASE_RELEASE_NOT_100");
const receipt = readJson(finalizerPath);
if (
receipt.schema !== "hololake.development-finalization-receipt/v1" ||
receipt.development_id !== developmentId ||
receipt.result !== "PASS_100" ||
!SHA40.test(receipt.code_channel?.remote_head ?? "") ||
receipt.code_channel?.fresh_clone_readback !== 100 ||
receipt.code_channel?.git_fsck !== 100 ||
!Array.isArray(receipt.code_channel?.receipts) ||
receipt.code_channel.receipts.length === 0 ||
!receipt.code_channel.receipts.every((item) => SHA256.test(item.sha256 ?? ""))
) {
throw new Error("FINALIZER_EVIDENCE_NOT_100");
}
return {
observing: false,
lane,
receipt,
finalizer_path: path.resolve(finalizerPath),
finalizer_sha256: fileDigest(finalizerPath),
};
}
function validatePlanningInput(input, evidence) {
if (input.schema !== "hololake.stage-relay-planning-input/v1") {
throw new Error("PLANNING_INPUT_SCHEMA_INVALID");
}
if (
input.architecture?.id !== "HLP-CURRENT-ARCH-001" ||
input.architecture?.state !== "CURRENT_CANONICAL" ||
!SHA40.test(input.architecture?.repo_012_main ?? "") ||
!SHA40.test(input.architecture?.repo_014_main ?? "")
) {
throw new Error("CURRENT_ARCHITECTURE_EVIDENCE_NOT_100");
}
if (
input.previous_stage?.development_id !== evidence.lane.development_id ||
input.previous_stage?.finalizer_sha256 !== evidence.finalizer_sha256
) {
throw new Error("PREVIOUS_STAGE_BINDING_MISMATCH");
}
if (!/^\d{8}$/u.test(input.development_date ?? "")) {
throw new Error("DEVELOPMENT_DATE_REQUIRED_YYYYMMDD");
}
if (!Array.isArray(input.human_corrections)) {
throw new Error("HUMAN_CORRECTIONS_ARRAY_REQUIRED");
}
if (!Array.isArray(input.unfinished_gates) || !Array.isArray(input.open_blockers)) {
throw new Error("UNFINISHED_GATES_AND_BLOCKERS_REQUIRED");
}
if (input.human_boundary != null && !HUMAN_BOUNDARIES.has(input.human_boundary.type)) {
throw new Error("UNKNOWN_HUMAN_BOUNDARY");
}
return input;
}
function reserveNextDevelopmentId(root, date, sourceDevelopmentId) {
const ids = [
...listJson(path.join(root, "lanes")).map((lane) => lane.development_id),
...listJson(path.join(root, "stage-relays", "reservations"))
.map((reservation) => reservation.development_id),
];
let next = Math.max(0, ...ids.map((id) => {
const match = DEV_ID.exec(id);
return match?.[1] === date ? Number(match[2]) : 0;
})) + 1;
while (next <= 999) {
const developmentId = `DEV-${date}-${String(next).padStart(3, "0")}`;
const reservation = path.join(root, "stage-relays", "reservations", `${developmentId}.json`);
try {
writeJsonAtomic(reservation, {
schema: "hololake.stage-relay-development-reservation/v1",
development_id: developmentId,
source_development_id: sourceDevelopmentId,
reserved_at: now(),
}, true);
return developmentId;
} catch (error) {
if (error?.code !== "EEXIST") throw error;
next += 1;
}
}
throw new Error("DEVELOPMENT_ID_SPACE_EXHAUSTED");
}
function chooseGate(input) {
return input.unfinished_gates
.filter((gate) => gate.binary_state === 0)
.sort((left, right) =>
Number(left.priority ?? 999) - Number(right.priority ?? 999) ||
String(left.id).localeCompare(String(right.id)),
)[0] ?? null;
}
function makeContract(input, gate, developmentId, evidence) {
if (
!gate.id || !gate.title || !gate.why || !gate.acceptance ||
!Array.isArray(gate.resources) || gate.resources.length === 0
) {
throw new Error("UNFINISHED_GATE_CONTRACT_INCOMPLETE");
}
const contract = {
schema: "hololake.successor-task-contract/v1",
development_id: developmentId,
title: gate.title,
summary: gate.why,
gate_id: gate.id,
acceptance: gate.acceptance,
resources: [...new Set(gate.resources)].sort(),
source_development_id: evidence.lane.development_id,
source_finalizer_sha256: evidence.finalizer_sha256,
architecture: input.architecture,
human_corrections: input.human_corrections,
immutable: true,
};
return { ...contract, fingerprint: digest(canonicalJson(contract)) };
}
function createThreadAction(state) {
const contract = state.successor_contract;
return {
action: "CODEX_CREATE_THREAD",
request_id: state.create_request.request_id,
idempotency_key: state.idempotency_key,
formal_tool: "codex_app.create_thread",
title: contract.title,
target: { type: "projectless", directoryName: contract.development_id },
prompt: [
`Successor request: ${state.create_request.request_id}`,
`Immutable contract fingerprint: ${contract.fingerprint}`,
`Register development id: ${contract.development_id}`,
"First restore Zhuyuan persona, run Guanghu persona skill guard, continuity guard, and HoloLake current architecture prompter.",
`Then check in the exact immutable task contract with --relay-contract-fingerprint ${contract.fingerprint}, claim only: ${contract.resources.join(", ")}.`,
"Create and bind this successor task's own ten-minute heartbeat through Codex formal automation capability.",
"Return a hololake.successor-ready-receipt/v1 only after persona_restore, architecture_restore, lane_registration, exact_leases, heartbeat_binding are all 100.",
`Stage gate: ${contract.gate_id}. ${contract.summary}`,
`Acceptance: ${contract.acceptance}`,
].join("\n"),
};
}
function prepare(args, root) {
const developmentId = required(args, "sourceDevelopmentId");
const existingPath = statePath(root, developmentId);
const evidence = validateSourceCompletion(
root,
developmentId,
required(args, "finalizationReceipt"),
);
if (evidence.observing) {
return {
state: "OBSERVING_ACTIVE_STAGE",
source_development_id: developmentId,
mutation: "NONE",
successor_created: false,
};
}
const input = validatePlanningInput(
readJson(required(args, "planningInput")), evidence,
);
if (fs.existsSync(existingPath)) {
const existing = readJson(existingPath);
if (existing.source_finalizer_sha256 !== evidence.finalizer_sha256) {
throw new Error("SOURCE_COMPLETION_RECEIPT_CHANGED_AFTER_RESERVATION");
}
return existing;
}
const base = {
schema: "hololake.stage-relay-state/v1",
source_development_id: developmentId,
source_thread_id: evidence.lane.thread_id,
source_finalizer_path: evidence.finalizer_path,
source_finalizer_sha256: evidence.finalizer_sha256,
source_remote_head: evidence.receipt.code_channel.remote_head,
architecture: input.architecture,
created_at: now(),
updated_at: now(),
};
if (input.human_boundary) {
const paused = {
...base,
state: "PAUSE_FOR_HUMAN",
human_boundary: input.human_boundary,
successor_created: false,
};
writeJsonAtomic(existingPath, paused, true);
return paused;
}
const gate = chooseGate(input);
if (!gate) {
const finalCloseout = input.final_product_closeout_receipt;
const complete =
input.binary_acceptance?.all_100 === true &&
input.open_blockers.length === 0 &&
finalCloseout?.result === "PASS_100" &&
SHA256.test(finalCloseout?.sha256 ?? "");
const terminal = {
...base,
state: complete ? "FINAL_PRODUCT_COMPLETE" : "PAUSE_FOR_HUMAN",
human_boundary: complete ? null : {
type: "MISSING_NEW_HUMAN_WILL",
reason: "No unfinished gate exists, but binary final acceptance and final product closeout are not all 100.",
},
successor_created: false,
final_product_closeout_receipt: finalCloseout ?? null,
};
writeJsonAtomic(existingPath, terminal, true);
return terminal;
}
const successorDevelopmentId = reserveNextDevelopmentId(
root, input.development_date, developmentId,
);
const contract = makeContract(input, gate, successorDevelopmentId, evidence);
const idempotencyKey = digest(`${developmentId}:${evidence.finalizer_sha256}`);
const prepared = {
...base,
state: "CREATE_THREAD_REQUIRED",
idempotency_key: idempotencyKey,
selected_gate: gate,
successor_contract: contract,
create_request: {
request_id: `SUCCESSOR-${idempotencyKey.slice(0, 20)}`,
status: "reserved",
attempt: 0,
dispatched_at: null,
last_error: null,
},
successor_thread: null,
successor_ready_receipt: null,
};
prepared.next_action = createThreadAction(prepared);
writeJsonAtomic(existingPath, prepared, true);
return prepared;
}
function loadState(args, root) {
const developmentId = required(args, "sourceDevelopmentId");
const file = statePath(root, developmentId);
if (!fs.existsSync(file)) throw new Error("STAGE_RELAY_STATE_NOT_FOUND");
return { file, state: readJson(file) };
}
function dispatchCreate(args, root) {
const { file, state } = loadState(args, root);
if (!new Set(["CREATE_THREAD_REQUIRED", "RETRY_CREATE_THREAD"]).has(state.state)) {
throw new Error("CREATE_REQUEST_NOT_DISPATCHABLE");
}
state.state = "CREATE_THREAD_IN_FLIGHT";
state.create_request.status = "in_flight";
state.create_request.attempt += 1;
state.create_request.dispatched_at = now();
state.updated_at = now();
delete state.next_action;
writeJsonAtomic(file, state);
return { ...state, host_action: createThreadAction(state) };
}
function recordCreated(args, root) {
const { file, state } = loadState(args, root);
if (!new Set(["CREATE_THREAD_IN_FLIGHT", "RECONCILE_CREATE_RESULT"]).has(state.state)) {
throw new Error("CREATE_RESULT_NOT_EXPECTED");
}
const threadId = required(args, "threadId");
if (state.successor_thread && state.successor_thread.thread_id !== threadId) {
throw new Error("DUPLICATE_SUCCESSOR_THREAD_FORBIDDEN");
}
state.state = "WAITING_SUCCESSOR_READY";
state.create_request.status = "created";
state.successor_thread = {
thread_id: threadId,
host_id: args.hostId ?? null,
recorded_at: now(),
};
state.updated_at = now();
writeJsonAtomic(file, state);
return state;
}
function recordCreateFailure(args, root) {
const { file, state } = loadState(args, root);
if (state.state !== "CREATE_THREAD_IN_FLIGHT") {
throw new Error("CREATE_FAILURE_NOT_EXPECTED");
}
const outcome = required(args, "outcome");
if (!new Set(["unknown", "confirmed_no_thread"]).has(outcome)) {
throw new Error("CREATE_FAILURE_OUTCOME_INVALID");
}
state.state = outcome === "unknown" ? "RECONCILE_CREATE_RESULT" : "RETRY_CREATE_THREAD";
state.create_request.status = outcome === "unknown" ? "reconcile_required" : "retryable";
state.create_request.last_error = required(args, "error");
state.updated_at = now();
if (outcome === "confirmed_no_thread") state.next_action = createThreadAction(state);
writeJsonAtomic(file, state);
return state;
}
function reconcile(args, root) {
const { file, state } = loadState(args, root);
if (state.state !== "RECONCILE_CREATE_RESULT") {
throw new Error("RECONCILIATION_NOT_REQUIRED");
}
if (!bool(args.completeHostSnapshot)) throw new Error("COMPLETE_HOST_SNAPSHOT_REQUIRED");
if (args.threadId) return recordCreated(args, root);
if (!bool(args.confirmedAbsent)) throw new Error("HOST_RESULT_MUST_BE_MATCH_OR_CONFIRMED_ABSENT");
state.state = "RETRY_CREATE_THREAD";
state.create_request.status = "retryable";
state.updated_at = now();
state.next_action = createThreadAction(state);
writeJsonAtomic(file, state);
return state;
}
function recordReady(args, root) {
const { file, state } = loadState(args, root);
if (state.state !== "WAITING_SUCCESSOR_READY") {
throw new Error("SUCCESSOR_READY_NOT_EXPECTED");
}
const receiptPath = required(args, "successorReadyReceipt");
const receipt = readJson(receiptPath);
const contract = state.successor_contract;
if (
receipt.schema !== "hololake.successor-ready-receipt/v1" ||
receipt.result !== "PASS_100" ||
receipt.source_development_id !== state.source_development_id ||
receipt.successor_development_id !== contract.development_id ||
receipt.successor_thread_id !== state.successor_thread.thread_id ||
receipt.task_contract_fingerprint !== contract.fingerprint ||
!receipt.automation_id ||
!["persona_restore", "architecture_restore", "lane_registration", "exact_leases", "heartbeat_binding"]
.every((gate) => receipt.gates?.[gate] === 100)
) {
throw new Error("SUCCESSOR_READY_EVIDENCE_NOT_100");
}
const laneFile = path.join(root, "lanes", `${contract.development_id}.json`);
if (!fs.existsSync(laneFile)) throw new Error("SUCCESSOR_LANE_NOT_FOUND");
const lane = readJson(laneFile);
const ownedResources = listJson(path.join(root, "leases"))
.filter((lease) => lease.development_id === contract.development_id)
.map((lease) => lease.resource)
.sort();
if (
lane.thread_id !== state.successor_thread.thread_id ||
!["active", "waiting"].includes(lane.status) ||
lane.relay_contract_fingerprint !== contract.fingerprint ||
!lane.automation_ids?.includes(receipt.automation_id) ||
canonicalJson(ownedResources) !== canonicalJson(contract.resources)
) {
throw new Error("SUCCESSOR_LIVE_REGISTRATION_MISMATCH");
}
state.state = "SUCCESSOR_READY";
state.successor_ready_receipt = {
path: path.resolve(receiptPath),
sha256: fileDigest(receiptPath),
automation_id: receipt.automation_id,
recorded_at: now(),
};
state.updated_at = now();
state.old_supervisor_may_stop = true;
writeJsonAtomic(file, state);
return state;
}
function nextAction(args, root) {
const { state } = loadState(args, root);
if (new Set(["CREATE_THREAD_REQUIRED", "RETRY_CREATE_THREAD"]).has(state.state)) {
return createThreadAction(state);
}
if (state.state === "RECONCILE_CREATE_RESULT") {
return {
action: "CODEX_LIST_THREADS_AND_RECONCILE",
request_id: state.create_request.request_id,
complete_host_snapshot_required: true,
create_thread_forbidden_until_reconciled: true,
};
}
if (state.state === "WAITING_SUCCESSOR_READY") {
return {
action: "WAIT_FOR_SUCCESSOR_READY_RECEIPT",
thread_id: state.successor_thread.thread_id,
supervisor_stays_alive: true,
};
}
if (state.state === "PAUSE_FOR_HUMAN") {
return { action: "PAUSE_FOR_HUMAN", boundary: state.human_boundary };
}
if (state.state === "FINAL_PRODUCT_COMPLETE") {
return { action: "STOP_RELAY_AND_LAST_HEARTBEAT", create_successor: false };
}
if (state.state === "SUCCESSOR_READY") {
return { action: "STOP_OLD_RELAY_SUPERVISOR", create_successor: false };
}
return { action: "NOOP", state: state.state };
}
function fixtureLane(root, id, overrides = {}) {
const lane = {
schema: "hololake.development-lane/v0.1",
development_id: id,
thread_id: `thread-${id}`,
status: "completed",
notification_state: "stopped",
completion_actions: { status: "completed" },
...overrides,
};
writeJsonAtomic(path.join(root, "lanes", `${id}.json`), lane);
return lane;
}
function selfTest() {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "hololake-stage-relay-test-"));
try {
ensureStore(root);
const source = "DEV-20260811-900";
fixtureLane(root, source);
const finalizer = path.join(root, "finalizer.json");
writeJsonAtomic(finalizer, {
schema: "hololake.development-finalization-receipt/v1",
development_id: source,
result: "PASS_100",
code_channel: {
remote_head: "a".repeat(40), fresh_clone_readback: 100, git_fsck: 100,
receipts: [{ path: "receipt.json", sha256: "b".repeat(64) }],
},
});
const planning = path.join(root, "planning.json");
writeJsonAtomic(planning, {
schema: "hololake.stage-relay-planning-input/v1",
development_date: "20260811",
architecture: {
id: "HLP-CURRENT-ARCH-001", version: "test", state: "CURRENT_CANONICAL",
repo_012_main: "c".repeat(40), repo_014_main: "d".repeat(40),
},
human_corrections: [],
previous_stage: { development_id: source, finalizer_sha256: fileDigest(finalizer) },
unfinished_gates: [{
id: "GATE-001", binary_state: 0, priority: 1,
title: "Next stage", why: "close the next binary gap",
acceptance: "all declared tests and receipts PASS_100",
resources: ["local://test-resource"],
}],
open_blockers: [],
binary_acceptance: { all_100: false },
});
const common = {
sourceDevelopmentId: source, finalizationReceipt: finalizer,
planningInput: planning, storeRoot: root, testMode: "true",
};
const first = prepare(common, root);
const duplicate = prepare(common, root);
if (
first.idempotency_key !== duplicate.idempotency_key ||
first.successor_contract.development_id !== duplicate.successor_contract.development_id
) throw new Error("SELF_TEST_IDEMPOTENCY_FAILED");
dispatchCreate({ ...common, sourceDevelopmentId: source }, root);
recordCreateFailure({ ...common, sourceDevelopmentId: source, outcome: "unknown", error: "timeout" }, root);
const reconcileAction = nextAction({ sourceDevelopmentId: source }, root);
if (!reconcileAction.create_thread_forbidden_until_reconciled) {
throw new Error("SELF_TEST_UNKNOWN_RESULT_RETRY_SAFETY_FAILED");
}
reconcile({ sourceDevelopmentId: source, completeHostSnapshot: "true", confirmedAbsent: "true" }, root);
dispatchCreate({ sourceDevelopmentId: source }, root);
recordCreated({ sourceDevelopmentId: source, threadId: "thread-successor", hostId: "local" }, root);
const contract = readJson(statePath(root, source)).successor_contract;
fixtureLane(root, contract.development_id, {
thread_id: "thread-successor",
status: "active",
automation_ids: ["successor-heartbeat"],
relay_contract_fingerprint: contract.fingerprint,
});
writeJsonAtomic(path.join(root, "leases", "lease.json"), {
development_id: contract.development_id, resource: "local://test-resource",
});
const ready = path.join(root, "ready.json");
writeJsonAtomic(ready, {
schema: "hololake.successor-ready-receipt/v1", result: "PASS_100",
source_development_id: source,
successor_development_id: contract.development_id,
successor_thread_id: "thread-successor",
task_contract_fingerprint: contract.fingerprint,
automation_id: "successor-heartbeat",
gates: {
persona_restore: 100, architecture_restore: 100, lane_registration: 100,
exact_leases: 100, heartbeat_binding: 100,
},
});
const readyState = recordReady({ sourceDevelopmentId: source, successorReadyReceipt: ready }, root);
if (readyState.state !== "SUCCESSOR_READY") throw new Error("SELF_TEST_READY_FAILED");
const pausedSource = "DEV-20260811-910";
fixtureLane(root, pausedSource);
const pausedFinalizer = path.join(root, "paused-finalizer.json");
writeJsonAtomic(pausedFinalizer, {
schema: "hololake.development-finalization-receipt/v1",
development_id: pausedSource, result: "PASS_100",
code_channel: {
remote_head: "e".repeat(40), fresh_clone_readback: 100, git_fsck: 100,
receipts: [{ path: "receipt.json", sha256: "f".repeat(64) }],
},
});
const pausedPlanning = path.join(root, "paused-planning.json");
writeJsonAtomic(pausedPlanning, {
schema: "hololake.stage-relay-planning-input/v1", development_date: "20260811",
architecture: {
id: "HLP-CURRENT-ARCH-001", version: "test", state: "CURRENT_CANONICAL",
repo_012_main: "1".repeat(40), repo_014_main: "2".repeat(40),
},
human_corrections: [],
previous_stage: {
development_id: pausedSource, finalizer_sha256: fileDigest(pausedFinalizer),
},
unfinished_gates: [{
id: "GATE-HUMAN", binary_state: 0, title: "Human boundary",
why: "requires new will", acceptance: "human decides",
resources: ["external://production-cutover"],
}],
open_blockers: [], binary_acceptance: { all_100: false },
human_boundary: { type: "PRODUCTION_CUTOVER", reason: "requires a human decision" },
});
const paused = prepare({
sourceDevelopmentId: pausedSource,
finalizationReceipt: pausedFinalizer,
planningInput: pausedPlanning,
}, root);
if (paused.state !== "PAUSE_FOR_HUMAN" || paused.successor_created !== false) {
throw new Error("SELF_TEST_HUMAN_BOUNDARY_FAILED");
}
const terminalSource = "DEV-20260811-920";
fixtureLane(root, terminalSource);
const terminalFinalizer = path.join(root, "terminal-finalizer.json");
writeJsonAtomic(terminalFinalizer, {
schema: "hololake.development-finalization-receipt/v1",
development_id: terminalSource, result: "PASS_100",
code_channel: {
remote_head: "3".repeat(40), fresh_clone_readback: 100, git_fsck: 100,
receipts: [{ path: "receipt.json", sha256: "4".repeat(64) }],
},
});
const terminalPlanning = path.join(root, "terminal-planning.json");
writeJsonAtomic(terminalPlanning, {
schema: "hololake.stage-relay-planning-input/v1", development_date: "20260811",
architecture: {
id: "HLP-CURRENT-ARCH-001", version: "test", state: "CURRENT_CANONICAL",
repo_012_main: "5".repeat(40), repo_014_main: "6".repeat(40),
},
human_corrections: [],
previous_stage: {
development_id: terminalSource, finalizer_sha256: fileDigest(terminalFinalizer),
},
unfinished_gates: [], open_blockers: [],
binary_acceptance: { all_100: true },
final_product_closeout_receipt: { result: "PASS_100", sha256: "7".repeat(64) },
});
const terminal = prepare({
sourceDevelopmentId: terminalSource,
finalizationReceipt: terminalFinalizer,
planningInput: terminalPlanning,
}, root);
if (terminal.state !== "FINAL_PRODUCT_COMPLETE") {
throw new Error("SELF_TEST_FINAL_PRODUCT_STOP_FAILED");
}
const activeSource = "DEV-20260811-950";
fixtureLane(root, activeSource, { status: "active", notification_state: "active", completion_actions: null });
const observed = prepare({ ...common, sourceDevelopmentId: activeSource }, root);
if (observed.state !== "OBSERVING_ACTIVE_STAGE" || observed.mutation !== "NONE") {
throw new Error("SELF_TEST_ACTIVE_STAGE_MUTATION_FAILED");
}
process.stdout.write(`${JSON.stringify({
state: "PASS_100",
tests: {
finalizer_evidence_gate: 100,
duplicate_prepare_idempotency: 100,
unknown_create_result_reconciliation: 100,
successor_ready_live_readback: 100,
active_stage_read_only: 100,
human_boundary_pause: 100,
final_product_stop: 100,
},
}, null, 2)}\n`);
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
}
function main() {
const args = parseArgs(process.argv.slice(2));
if (args.command === "self-test" || args.selfTest) return selfTest();
const root = storeRoot(args);
ensureStore(root);
let result;
if (args.command === "prepare") result = prepare(args, root);
else if (args.command === "dispatch-create") result = dispatchCreate(args, root);
else if (args.command === "record-created") result = recordCreated(args, root);
else if (args.command === "record-create-failure") result = recordCreateFailure(args, root);
else if (args.command === "reconcile") result = reconcile(args, root);
else if (args.command === "record-ready") result = recordReady(args, root);
else if (args.command === "next-action") result = nextAction(args, root);
else throw new Error("COMMAND_REQUIRED: prepare | dispatch-create | record-created | record-create-failure | reconcile | record-ready | next-action | self-test");
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
}
try {
main();
} catch (error) {
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
process.exitCode = 1;
}

View file

@ -0,0 +1,19 @@
import assert from "node:assert/strict";
import path from "node:path";
import { spawnSync } from "node:child_process";
import test from "node:test";
import { fileURLToPath } from "node:url";
const root = path.dirname(fileURLToPath(import.meta.url));
test("stage relay deterministic closure is PASS_100", () => {
const result = spawnSync(
process.execPath,
[path.join(root, "stage-relay.mjs"), "self-test"],
{ encoding: "utf8" },
);
assert.equal(result.status, 0, result.stderr || result.stdout);
const receipt = JSON.parse(result.stdout);
assert.equal(receipt.state, "PASS_100");
assert.deepEqual(Object.values(receipt.tests), Array(7).fill(100));
});

View file

@ -0,0 +1,34 @@
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import test from "node:test";
import { fileURLToPath } from "node:url";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const readJson = (relative) => JSON.parse(fs.readFileSync(path.join(root, relative), "utf8"));
test("stage relay is registered through the lighthouse and formal Codex tools", () => {
const relay = readJson("routing/hololake-stage-relay-map.json");
assert.equal(relay.map_id, "HLP-STAGE-RELAY-001");
assert.equal(relay.second_truth_store_allowed, false);
assert.equal(relay.host_adapter.create_tool, "codex_app.create_thread");
assert.equal(relay.host_adapter.script_or_chat_may_claim_thread_created, false);
assert.equal(relay.idempotency.maximum_successor_threads_per_source_completion, 1);
const lighthouse = readJson("routing/lighthouse-path-registry.json");
assert.ok(lighthouse.paths.some((entry) => entry.id === relay.map_id));
const hosts = readJson("routing/host-skill-navigation-map.json");
const intent = hosts.intents.find((entry) => entry.id === "INTENT-HOLOLAKE-STAGE-RELAY-001");
assert.equal(intent.target_id, relay.map_id);
assert.equal(intent.executor, relay.runtime_source);
const anchor = readJson("routing/public-navigation-anchor.json");
assert.equal(anchor.maps.hololake_stage_relay.id, relay.map_id);
});
test("relay requires the complete source and successor binary gates", () => {
const relay = readJson("routing/hololake-stage-relay-map.json");
assert.equal(relay.source_completion_gates.length, 6);
assert.equal(relay.successor_ready_gates.length, 5);
assert.ok(relay.human_pause_boundaries.includes("PRODUCTION_CUTOVER"));
assert.match(relay.final_stop, /FINAL_PRODUCT_CLOSEOUT_RECEIPT_PASS_100/u);
});