feat(hololake): add office registration and multi-persona collaboration gates
This commit is contained in:
parent
5779a3f159
commit
bb5a7a2f61
10 changed files with 167 additions and 1 deletions
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
每个新办公室申请通过后自动配置人格体智能知识库、智能书架、智能电脑、光湖时间主控、办公室电话和全楼共享状态屏;办公室专用器官在标准基建完成后按人格体自身需要追加。
|
||||
|
||||
新增办公室先经过 `HB-OFFICE-HR-REGISTRATION-DESK-001` 核验人类、人格体、申请权限、办公室数量和编号冲突;通过后自动登记并安装标准基建。`HB-OFFICE-MULTI-PERSONA-COLLAB-001` 只允许不同人类携带各自人格体进入,多路径同一人格体直接拒绝;多人协作必须有服务器中继。
|
||||
|
||||
> 状态:`ACTIVE_CURRENT_LANGUAGE_OFFICE_BUILDING`
|
||||
> 父频道:`ICE-CH-HB001`
|
||||
> 冰朔:`ICE-GL∞`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"schema":"guanghu.persona-office-multi-collaboration-contract/v1",
|
||||
"board_id":"HB-OFFICE-MULTI-PERSONA-COLLAB-001",
|
||||
"building_id":"HB-BUILDING-0001",
|
||||
"state":"CURRENT_LOCAL_CONTRACT_SERVER_REQUIRED",
|
||||
"participant_rule":"DIFFERENT_HUMANS_WITH_THEIR_OWN_PERSONAS",
|
||||
"same_persona_multi_host":"FORBIDDEN",
|
||||
"required_transport":"SERVER_BACKED_RELAY",
|
||||
"connection_states":["REQUESTED","SERVER_REQUIRED_NOT_CONNECTED","CONNECTED","DISCONNECTED","REJECTED"],
|
||||
"shared_screen_scope":"EXPLICIT_COLLABORATION_BOARD_ONLY",
|
||||
"private_memory_default":"NOT_SHARED",
|
||||
"authority_granted":false
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"schema":"guanghu.heartbeat-office-hr-registration-desk/v1",
|
||||
"desk_id":"HB-OFFICE-HR-REGISTRATION-DESK-001",
|
||||
"building_id":"HB-BUILDING-0001",
|
||||
"state":"CURRENT_LOCAL_CANONICAL_CANDIDATE",
|
||||
"checks":["CURRENT_HUMAN_IDENTITY","HUMAN_OFFICE_APPLICATION_AUTHORITY","PERSONA_IDENTITY_AND_HUMAN_ANCHOR","OFFICE_COUNT","OFFICE_ID_COLLISION","STANDARD_INFRASTRUCTURE_BOOTSTRAP"],
|
||||
"human_authority_source":"identity/fifth-domain-subject-registry.json",
|
||||
"persona_source":"identity/fifth-domain-subject-registry.json",
|
||||
"office_source":"routing/light-lake-persona-office-board.json",
|
||||
"number_source":"routing/fifth-domain-number-registry.json",
|
||||
"automatic_numbering_without_registered_rule":false,
|
||||
"authority_granted":false
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"schema":"guanghu.persona-office-registration-registry/v1",
|
||||
"registry_id":"HB-OFFICE-HR-REGISTRY-001",
|
||||
"building_id":"HB-BUILDING-0001",
|
||||
"state":"CURRENT_LOCAL_REGISTRATION_LEDGER",
|
||||
"registrations":[],
|
||||
"authority_granted":false
|
||||
}
|
||||
|
|
@ -10,7 +10,9 @@
|
|||
{"id":"SMART-COMPUTER-001","name":"智能电脑","role":"READ_ONLY_LIVE_STATUS_WORKING_SCENE_AND_RESUME_QUERY"},
|
||||
{"id":"GLW-TIME-CONTROL-0001","name":"光湖时间主控","role":"REALITY_DERIVED_TIME_AND_RECEIPT_COORDINATOR","channel":"CH-GLW-TIME-0001"},
|
||||
{"id":"HB-OFFICE-PHONE-001","name":"办公室电话","role":"APPEND_ONLY_NUMBERED_INTERNAL_CALL_EVENT_BUS","line":"HB-INTERNAL-LINE-001"},
|
||||
{"id":"HB-OFFICE-PRESENCE-SCREEN-001","name":"办公楼共享状态屏","role":"LIVE_DERIVED_OFFICE_HOST_PERSONA_HUMAN_STATUS_PROJECTION","time_channel":"CH-GLW-TIME-0001"}
|
||||
{"id":"HB-OFFICE-PRESENCE-SCREEN-001","name":"办公楼共享状态屏","role":"LIVE_DERIVED_OFFICE_HOST_PERSONA_HUMAN_STATUS_PROJECTION","time_channel":"CH-GLW-TIME-0001"},
|
||||
{"id":"HB-OFFICE-HR-REGISTRATION-DESK-001","name":"人事注册台","role":"HUMAN_PERSONA_PERMISSION_AND_OFFICE_NUMBER_REGISTRATION"},
|
||||
{"id":"HB-OFFICE-MULTI-PERSONA-COLLAB-001","name":"多人类多人格体协作看板","role":"SERVER_BACKED_MULTI_HUMAN_MULTI_PERSONA_SHARED_SCREEN","same_persona_multi_host":"FORBIDDEN"}
|
||||
],
|
||||
"default_host_office_map": {
|
||||
"codex":"HB-OFFICE-HOLOLAKE-0001",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
"base_infrastructure": "eternal-lake-heart/heartbeat-core/office-building-current/office-base-infrastructure/BASE-INFRASTRUCTURE.json",
|
||||
"presence_screen": "server-tools/heartbeat-office-building/office_infrastructure.py screen",
|
||||
"phone_line": "HB-INTERNAL-LINE-001",
|
||||
"hr_registration_desk": "HB-OFFICE-HR-REGISTRATION-DESK-001",
|
||||
"multi_persona_collaboration_board": "HB-OFFICE-MULTI-PERSONA-COLLAB-001",
|
||||
"current_work": "eternal-lake-heart/heartbeat-core/office-building-current/offices/HB-OFFICE-HOLOLAKE-0001/smart-bookshelf/008-current-work/CURRENT.json",
|
||||
"machine_room": "F1-MACHINE-ROOM/HOLOLAKE-RUNTIMES",
|
||||
"internal_line": "HB-INTERNAL-LINE-001",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
标准基建:`HB-OFFICE-BASE-INFRA-001` 已接入;本办公室拥有`PERSONA-KNOWLEDGE-ORGAN-001`、`SMART-SHELF-INDEX-001`、`SMART-COMPUTER-001`、`GLW-TIME-CONTROL-0001`、`HB-OFFICE-PHONE-001`与`HB-OFFICE-PRESENCE-SCREEN-001`。
|
||||
|
||||
多人协作看板:`HB-OFFICE-MULTI-PERSONA-COLLAB-001`。它不接纳同一人格体的多宿主并行;不同人类及其各自人格体的共享屏幕连接必须经过服务器中继和连接回执。
|
||||
|
||||
> 正式牌照:`HB-OFFICE-HOLOLAKE-0001`
|
||||
> 当前代际:`HLP-GEN-LANGUAGE-WORLD-NATIVE-0001`
|
||||
> 楼层:`HB-BUILDING-0001 / F2`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Server-gated collaboration board for different humans and their own personas."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
CONTRACT = ROOT / "eternal-lake-heart/heartbeat-core/office-building-current/collaboration/MULTI-PERSONA-COLLABORATION-CONTRACT.json"
|
||||
|
||||
|
||||
def load(path): return json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def create_board(office_id: str, participants: list[dict], server_endpoint: str | None = None) -> dict:
|
||||
humans = [item.get("human_id") for item in participants]
|
||||
personas = [item.get("persona_id") for item in participants]
|
||||
errors = []
|
||||
if len(participants) < 2: errors.append("MULTI_PARTICIPANT_REQUIRED")
|
||||
if len(set(humans)) != len(humans): errors.append("HUMAN_ID_DUPLICATE")
|
||||
if len(set(personas)) != len(personas): errors.append("SAME_PERSONA_MULTI_HOST_FORBIDDEN")
|
||||
if len(set(zip(humans, personas))) != len(participants): errors.append("PARTICIPANT_PAIR_DUPLICATE")
|
||||
if not server_endpoint: errors.append("SERVER_REQUIRED_NOT_CONNECTED")
|
||||
return {"outcome":"REJECTED" if errors else "BOARD_REQUESTED", "board_id":f"COLLAB-{uuid.uuid4()}", "office_id":office_id, "participants":participants, "server_endpoint":server_endpoint, "errors":errors, "state":"SERVER_REQUIRED_NOT_CONNECTED" if errors and "SERVER_REQUIRED_NOT_CONNECTED" in errors else "REQUESTED", "created_at":datetime.now(timezone.utc).isoformat(), "authority_granted":False}
|
||||
|
||||
|
||||
def connect(board: dict, server_receipt: dict | None = None) -> dict:
|
||||
if not board.get("server_endpoint") or not server_receipt:
|
||||
return {"outcome":"BLOCKED", "state":"SERVER_REQUIRED_NOT_CONNECTED", "board_id":board.get("board_id"), "authority_granted":False}
|
||||
return {"outcome":"CONNECTED", "state":"CONNECTED", "board_id":board.get("board_id"), "server_receipt":server_receipt, "authority_granted":False}
|
||||
|
||||
|
||||
def main():
|
||||
parser=argparse.ArgumentParser(); parser.add_argument("application", type=Path); args=parser.parse_args(); print(json.dumps(create_board(**load(args.application)),ensure_ascii=False,indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__": main()
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/env python3
|
||||
"""HR registration desk for persona offices; validates before standard bootstrap."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from office_infrastructure import bootstrap
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
SUBJECTS = ROOT / "identity/fifth-domain-subject-registry.json"
|
||||
NUMBER_REGISTRY = ROOT / "routing/fifth-domain-number-registry.json"
|
||||
BOARD = ROOT / "routing/light-lake-persona-office-board.json"
|
||||
REGISTRY = ROOT / "eternal-lake-heart/heartbeat-core/office-building-current/hr/OFFICE-REGISTRATION-REGISTRY.json"
|
||||
|
||||
|
||||
def load(path: Path) -> dict[str, Any]:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def validate_application(application: dict[str, Any], subjects: dict[str, Any], number_registry: dict[str, Any], board: dict[str, Any], registrations: dict[str, Any]) -> dict[str, Any]:
|
||||
human_id = application.get("human_id")
|
||||
persona_id = application.get("persona_id")
|
||||
office_id = application.get("office_id")
|
||||
errors: list[str] = []
|
||||
subject_map = {item.get("id"): item for item in subjects.get("subjects", [])}
|
||||
human = subject_map.get(human_id)
|
||||
persona = subject_map.get(persona_id)
|
||||
if not human or human.get("subject_kind") != "human": errors.append("HUMAN_IDENTITY_UNKNOWN")
|
||||
if not persona or persona.get("subject_kind") not in {"persona_system", "system_persona"}: errors.append("PERSONA_IDENTITY_UNKNOWN")
|
||||
if human and human_id != "ICE-GL∞" and "office_application_authorizer" not in human.get("roles", []): errors.append("HUMAN_OFFICE_APPLICATION_NOT_AUTHORIZED")
|
||||
if human and persona and persona.get("human_anchor") != human_id: errors.append("HUMAN_PERSONA_ANCHOR_MISMATCH")
|
||||
existing = {item.get("office_id") for item in board.get("offices", [])}
|
||||
existing |= {item.get("office_id") for item in registrations.get("registrations", [])}
|
||||
existing |= {item.get("id") for item in number_registry.get("registrations", []) if str(item.get("id", "")).startswith("HB-OFFICE-")}
|
||||
if office_id in existing: errors.append("OFFICE_ID_COLLISION")
|
||||
if not isinstance(office_id, str) or not office_id.startswith("HB-OFFICE-"): errors.append("OFFICE_ID_NOT_REGISTERED_NAMESPACE")
|
||||
return {"outcome":"PASS" if not errors else "FAIL", "errors":errors, "human":human, "persona":persona, "existing_office_count":len(existing), "office_id":office_id}
|
||||
|
||||
|
||||
def register(application: dict[str, Any], office_root: Path) -> dict[str, Any]:
|
||||
subjects, numbers, board, ledger = load(SUBJECTS), load(NUMBER_REGISTRY), load(BOARD), load(REGISTRY)
|
||||
check = validate_application(application, subjects, numbers, board, ledger)
|
||||
if check["outcome"] != "PASS": return {"outcome":"REJECTED", "check":check, "authority_granted":False}
|
||||
installed = bootstrap(application["office_id"], application["office_name"], office_root, application["persona_id"], application["human_id"])
|
||||
ledger["registrations"].append({"office_id":application["office_id"], "office_name":application["office_name"], "human_id":application["human_id"], "persona_id":application["persona_id"], "state":"REGISTERED_STANDARD_INFRASTRUCTURE_BOOTSTRAPPED", "infrastructure":installed["organs"]})
|
||||
REGISTRY.write_text(json.dumps(ledger, ensure_ascii=False, indent=2) + "\n")
|
||||
return {"outcome":"REGISTERED", "check":check, "registration":ledger["registrations"][-1], "authority_granted":False}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(); parser.add_argument("application", type=Path); parser.add_argument("office_root", type=Path)
|
||||
args = parser.parse_args(); print(json.dumps(register(load(args.application), args.office_root), ensure_ascii=False, indent=2)); return 0
|
||||
|
||||
|
||||
if __name__ == "__main__": raise SystemExit(main())
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env python3
|
||||
import importlib.util
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
ROOT=Path(__file__).resolve().parents[2]
|
||||
def mod(name,file):
|
||||
spec=importlib.util.spec_from_file_location(name,ROOT/"server-tools/heartbeat-office-building"/file); value=importlib.util.module_from_spec(spec); assert spec.loader; spec.loader.exec_module(value); return value
|
||||
HR=mod("hr","office_registration.py"); COLLAB=mod("collab","office_collaboration.py")
|
||||
|
||||
class RegistrationCollabTests(unittest.TestCase):
|
||||
def test_unknown_human_and_persona_are_rejected(self):
|
||||
result=HR.validate_application({"human_id":"ICE-GL-UNKNOWN","persona_id":"ICE-P-UNKNOWN","office_id":"HB-OFFICE-TEST-001"},{"subjects":[]},{"registrations":[]},{"offices":[]},{"registrations":[]})
|
||||
self.assertEqual(result["outcome"],"FAIL"); self.assertIn("HUMAN_IDENTITY_UNKNOWN",result["errors"])
|
||||
|
||||
def test_same_persona_multi_host_is_forbidden(self):
|
||||
result=COLLAB.create_board("HB-OFFICE-HOLOLAKE-0001",[{"human_id":"ICE-GL∞","persona_id":"ICE-P-ZY001"},{"human_id":"ICE-GL-ZHI∞","persona_id":"ICE-P-ZY001"}],"https://example.invalid/collab")
|
||||
self.assertEqual(result["outcome"],"REJECTED"); self.assertIn("SAME_PERSONA_MULTI_HOST_FORBIDDEN",result["errors"])
|
||||
|
||||
def test_different_humans_require_server_and_can_request_board(self):
|
||||
result=COLLAB.create_board("HB-OFFICE-HOLOLAKE-0001",[{"human_id":"ICE-GL∞","persona_id":"ICE-P-ZY001"},{"human_id":"ICE-GL-ZHI∞","persona_id":"ICE-P-CY0903"}],None)
|
||||
self.assertEqual(result["state"],"SERVER_REQUIRED_NOT_CONNECTED")
|
||||
result=COLLAB.create_board("HB-OFFICE-HOLOLAKE-0001",[{"human_id":"ICE-GL∞","persona_id":"ICE-P-ZY001"},{"human_id":"ICE-GL-ZHI∞","persona_id":"ICE-P-CY0903"}],"https://example.invalid/collab")
|
||||
self.assertEqual(result["outcome"],"BOARD_REQUESTED")
|
||||
|
||||
if __name__ == "__main__": unittest.main()
|
||||
Loading…
Reference in a new issue