diff --git a/product-source/hololake-native-desktop/README.md b/product-source/hololake-native-desktop/README.md index 1d985527c..a3e74cc5d 100644 --- a/product-source/hololake-native-desktop/README.md +++ b/product-source/hololake-native-desktop/README.md @@ -47,43 +47,8 @@ React/TypeScript。桌面上的 `world.guanghu.hololake` 安装包是本源码 首个公开产品是 GH-AIOS 通用人工智能操作平台,首页同时承担五域统一入口。用户只提交光湖编号,不手工选择所属域;系统先在内部完成域路由和编号合法性校验,再开放对应域的登录界面。五域只共享入口协议,不共享注册表、登录服务或私有数据。 -登录后的域内界面始终提供“返回主世界”,只返回五域公共首页并保留当前会话;“退出账号”是独立动作。用户从公共首页再次进入自己的所属域时,不重复进行编号或域凭证验证。 - 机器合同:`contracts/stage-one-platform.json`。 -## 光湖分域行业装配 - -光湖分域是行业系统入口,现已接入第一份行业路由合同:`contracts/subdomain-industry-routing.json`。 -公共主世界点击“光湖分域”即可查看行业状态并进入教育行业初始化频道;浏览公共状态不等于取得 -任何私有频道数据或写入权限。光湖零域不承载行业入口。人的主体编号 -只负责身份,行业编号只负责系统路由,两者不互相改写。行业顺序与当前工程状态分别为:网文行业 -待开发、宠物行业待开发、教育行业开发中;开发优先级不改变行业顺序,也不允许客户端自行签发 -尚未登记的行业编号。 - -教育行业已经建立 `GH-EDU-INIT-001` 初始化频道,并装入五项 HoloLake 原生能力: -`contracts/education-workspace.json` 定义的教育文档、教育表格数据、数据整理、人工确认自动化与原生组合视图。登录账号可以创建、读取、 -编辑、保存和可恢复归档多篇教育文档,并可从教学计划、会议纪要和学员复盘模板开始;也可以 -建立多张表格、增删行列、编辑单元格、筛选排序,在表格视图与可编辑卡片视图之间切换,并把 -Excel、飞书或钉钉复制的多行多列数据直接批量粘贴。两者都由 -Rust 原生层写入当前账号独立的本机 SQLite,保存要求精确修订号,重启后可以读回,不使用浏览器 -临时存储,也不把公共行业目录与私人内容混在一起。 - -数据整理模块读取当前账号已有教育表格,先计算并预览首尾空白规范化、完整空行清除和完全重复行 -去重结果;只有人明确确认后,才以当前修订号写回同一张表。它不跨账号读取,也不在后台自动修改。 - -自动化模块允许保存表格规则,按字段的等于、包含、为空或不为空条件匹配数据,并向指定字段写入内容。 -每次执行必须先锁定规则与表格修订、生成只读预览,再由人明确确认;写入与执行回执在同一数据库事务中完成。 -它不会后台定时运行,不跨账号,也不把模型推理当作执行授权。当前没有把旧教育预览壳或任何开源应用 -整包嵌入。`contracts/education-open-source-donor-assessment.json` 记录 AppFlowy、Lexical 与 -TanStack Table 的初步工程筛查:当前只研究结构和交互逻辑,没有复制源码或增加外部依赖;后续 -模块仍须分别完成来源、许可证、安全边界和 HoloLake 接口审查后再按需装配。 - -原生组合视图由 Rust 内核读取当前登录账号的真实知识目录,生成受限的 -`hololake.native-object/v1`,再把已登记的数据源、分类、聚合和投影模块编译成类型化无环图。 -人类可以切换一级目录/知识来源和文档数/数据量/重复数,并把同一次执行结果任意组合为仪表盘、 -对比、柱状图、分类和明细表。该阶段只读,不让渲染层取得知识写权限,也不以硬编码样例满足验收。 -机器合同见 `contracts/native-composition-runtime.json`。 - 个人频道、知识库和代码频道的机器合同分别是 `contracts/personal-channel-kernel.json`、 `contracts/knowledge-workspace.json` 与 `contracts/code-channel.json`。本机身份不是平台账户 认证;本机知识与源码访问也不产生仓库、服务器、部署或发布权限。 diff --git a/product-source/hololake-native-desktop/audit/hololake-0.5.0-numbered-root-installed-acceptance-20260818.json b/product-source/hololake-native-desktop/audit/hololake-0.5.0-numbered-root-installed-acceptance-20260818.json new file mode 100644 index 000000000..959e9f036 --- /dev/null +++ b/product-source/hololake-native-desktop/audit/hololake-0.5.0-numbered-root-installed-acceptance-20260818.json @@ -0,0 +1,66 @@ +{ + "schema": "hololake.installed-numbered-root-acceptance/v1", + "record_id": "HLP-HOLOLAKE-0.5.0-NUMBERED-ROOT-INSTALLED-20260818", + "state": "LOCAL_DEVELOPER_ID_SIGNED_INSTALLED_ACCEPTANCE_PASS_PUBLIC_NOTARIZATION_PENDING", + "observed_at": "2026-08-18T15:18:00Z", + "source": { + "branch": "codex/hololake-clean-reassembly-20260818", + "source_commit": "5d01607459043d0713bc562ef6d04dec198930a9", + "numbered_root_commit": "64abf969bfbc1c576d4fa84ae3282d32efbfcc38" + }, + "installed_application": { + "path": "/Users/bingshuolingdianyuanhe/Desktop/HoloLake.app", + "version": "0.5.0", + "bundle_identifier": "world.guanghu.hololake", + "architecture": "arm64", + "binary_sha256": "2e162ff077187310f816f58df2250e3ee939eb489fe3c7b00e94557ff8f3370d", + "process_path_verified": true + }, + "developer_id": { + "identity": "Developer ID Application: bei sun (825A9L3G7Q)", + "team_identifier": "825A9L3G7Q", + "cdhash": "7a2372ed85fc3b775e9a352217b123a4bdee0d64", + "strict_signature_verification": "PASS", + "designated_requirement": "PASS", + "gatekeeper": "REJECTED_UNNOTARIZED_DEVELOPER_ID", + "apple_notarization_and_stapling": "PENDING" + }, + "runtime": { + "real_webview_loaded": true, + "visible_version": "V0.5.0", + "visible_domain": "第五域 · 光湖本源域", + "entered_surface": "永恒湖心系统", + "numbered_ipc_receipt_rows": 50, + "grant_and_execution_rows_present": true, + "empty_authority_binding_digest_rows": 0, + "recalculated_receipt_chain_failures": 0, + "last_sequence": 50, + "last_receipt_hash": "a294463ad9e6959d0f12ce5c2b59e11d97581e067b9480df9ae598dfafc067df" + }, + "old_application": { + "version": "0.4.1", + "role": "READ_ONLY_PRE_NUMBERED_ROOT_MODULE_DONOR", + "archive_receipt": "/Volumes/JZAO/HoloLake/artifacts/hololake-release/0.4.1/macos-arm64/pre-numbered-root-donor/archive-receipt.json", + "repair_in_place": false + }, + "release_boundary": { + "local_signed_install_complete": true, + "public_signed_notarized_release_complete": false, + "updater_public_key_continuity": "PASS_EXISTING_0.4.1_TRUST_ROOT_REUSED", + "updater_private_key_available_to_current_pipeline": true, + "updater_signature_generated_and_verified": true, + "pre_notarization_updater_artifact": { + "path": "src-tauri/target/release/bundle/macos/HoloLake.app.tar.gz", + "sha256": "4176bb7ea83c820744239c228671840289f87675329182bc055938e03bba9693", + "signature_path": "src-tauri/target/release/bundle/macos/HoloLake.app.tar.gz.sig", + "signature_sha256": "ae02c2918c26b6e3cc2389fec8d9f98884ae639d448edc78a4ec80ee30a8ae92", + "publication_allowed": false + }, + "apple_notarization_credentials_available_to_current_pipeline": false, + "public_broadcast_activated": false + }, + "persona_boundary": { + "current_codex_carrier_binding_claimed": false, + "runtime_persona_binding_created_by_numbered_ipc": false + } +} diff --git a/product-source/hololake-native-desktop/audit/hololake-0.5.0-qoder-numbered-production-installed-20260819.json b/product-source/hololake-native-desktop/audit/hololake-0.5.0-qoder-numbered-production-installed-20260819.json new file mode 100644 index 000000000..60db39e45 --- /dev/null +++ b/product-source/hololake-native-desktop/audit/hololake-0.5.0-qoder-numbered-production-installed-20260819.json @@ -0,0 +1,17 @@ +{ + "schema": "hololake.production-install-acceptance/v1", + "record_id": "HLP-PRODUCTION-INSTALL-20260819-001", + "state": "PASS", + "version": "0.5.0", + "installed_path": "/Users/bingshuolingdianyuanhe/Desktop/HoloLake.app", + "visible_hololake_app_count": 1, + "previous_app_recovery_path": "/Users/bingshuolingdianyuanhe/.Trash/HoloLake-pre-qoder-numbered-ui-20260819-0659.app", + "binary_sha256": "b0422bb18df521ace54ff1a52cd47121d92833dd78f165845deba7806686c7ac", + "cdhash": "42640f7ce56e96403609899095c77ea6171df07d", + "team_identifier": "825A9L3G7Q", + "developer_id_signature": "PASS", + "launch": "PASS", + "real_data_rehydration": "PASS", + "notarization": "NOT_CLAIMED_THIS_LOCAL_BUILD", + "updater_artifact_signing": "NOT_COMPLETED_MISSING_PRIVATE_UPDATER_KEY_IN_CURRENT_PROCESS" +} diff --git a/product-source/hololake-native-desktop/audit/qoder-surface-live-acceptance-20260819.json b/product-source/hololake-native-desktop/audit/qoder-surface-live-acceptance-20260819.json new file mode 100644 index 000000000..c02229824 --- /dev/null +++ b/product-source/hololake-native-desktop/audit/qoder-surface-live-acceptance-20260819.json @@ -0,0 +1,28 @@ +{ + "schema": "hololake.qoder-surface-live-acceptance/v1", + "record_id": "HLP-QODER-SURFACE-ACCEPTANCE-20260819-001", + "module_number": "HLP-MOD-OFFICIAL-DYNAMIC-WORLD-SURFACE-0001", + "state": "PASS", + "tested_app": "src-tauri/target/debug/bundle/macos/HoloLake.app", + "developer_id_signature": "PASS", + "team_identifier": "825A9L3G7Q", + "binary_sha256": "fb1d0497f295e1228c9b2ac86e33082332f226aae6e32b9050cf6c5de30bd512", + "cdhash": "90e365b23961a6b842590869df655ec35a55b66e", + "checks": { + "traditional_locked_layout": "PASS", + "traditional_real_data_hydration": "PASS", + "traditional_light_finish_readability": "PASS_SNOW", + "language_world_five_domains_no_overlap": "PASS_1229x768", + "language_world_real_weather": "PASS_CLOUD", + "theme_persists_into_channel": "PASS", + "theme_persists_into_web_novel": "PASS", + "theme_persists_into_education": "PASS", + "new_chapter_modal_opens": "PASS", + "created_chapter_survives_restart": "PASS_1_CHAPTER", + "work_summary_refresh_after_create": "PASS_1_CHAPTER_ON_WORK_CARD", + "idle_clock_stops": "PASS", + "idle_screenshot_psnr_db": 59.502493 + }, + "reference_comparison": "audit/qoder-traditional-reference-vs-hololake-0.5.0.jpg", + "public_notarization": "NOT_CLAIMED_BY_DEBUG_ACCEPTANCE" +} diff --git a/product-source/hololake-native-desktop/audit/qoder-traditional-reference-vs-hololake-0.5.0.jpg b/product-source/hololake-native-desktop/audit/qoder-traditional-reference-vs-hololake-0.5.0.jpg new file mode 100644 index 000000000..6f8eb5d42 Binary files /dev/null and b/product-source/hololake-native-desktop/audit/qoder-traditional-reference-vs-hololake-0.5.0.jpg differ diff --git a/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-lightweight-home-1440.png b/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-lightweight-home-1440.png new file mode 100644 index 000000000..ce8d44afb Binary files /dev/null and b/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-lightweight-home-1440.png differ diff --git a/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-lightweight-home-500.png b/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-lightweight-home-500.png new file mode 100644 index 000000000..8c2dff839 Binary files /dev/null and b/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-lightweight-home-500.png differ diff --git a/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-lightweight-modules-1440.png b/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-lightweight-modules-1440.png new file mode 100644 index 000000000..a5608fd23 Binary files /dev/null and b/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-lightweight-modules-1440.png differ diff --git a/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-lightweight-native-1440.png b/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-lightweight-native-1440.png new file mode 100644 index 000000000..748653e18 Binary files /dev/null and b/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-lightweight-native-1440.png differ diff --git a/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-modules.png b/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-modules.png new file mode 100644 index 000000000..ef1f082ce Binary files /dev/null and b/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-modules.png differ diff --git a/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-native.png b/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-native.png new file mode 100644 index 000000000..247ce0aeb Binary files /dev/null and b/product-source/hololake-native-desktop/audit/responsive-20260819/private-channel-native.png differ diff --git a/product-source/hololake-native-desktop/audit/responsive-20260819/public-1024x768.png b/product-source/hololake-native-desktop/audit/responsive-20260819/public-1024x768.png new file mode 100644 index 000000000..603e45748 Binary files /dev/null and b/product-source/hololake-native-desktop/audit/responsive-20260819/public-1024x768.png differ diff --git a/product-source/hololake-native-desktop/audit/responsive-20260819/public-1440x900.png b/product-source/hololake-native-desktop/audit/responsive-20260819/public-1440x900.png new file mode 100644 index 000000000..1b3552d4b Binary files /dev/null and b/product-source/hololake-native-desktop/audit/responsive-20260819/public-1440x900.png differ diff --git a/product-source/hololake-native-desktop/audit/responsive-20260819/public-720x900.png b/product-source/hololake-native-desktop/audit/responsive-20260819/public-720x900.png new file mode 100644 index 000000000..890c987a4 Binary files /dev/null and b/product-source/hololake-native-desktop/audit/responsive-20260819/public-720x900.png differ diff --git a/product-source/hololake-native-desktop/audit/responsive-20260819/traditional-1024x768.png b/product-source/hololake-native-desktop/audit/responsive-20260819/traditional-1024x768.png new file mode 100644 index 000000000..f3f2664e7 Binary files /dev/null and b/product-source/hololake-native-desktop/audit/responsive-20260819/traditional-1024x768.png differ diff --git a/product-source/hololake-native-desktop/audit/responsive-20260819/traditional-1440x900.png b/product-source/hololake-native-desktop/audit/responsive-20260819/traditional-1440x900.png new file mode 100644 index 000000000..c0318fd10 Binary files /dev/null and b/product-source/hololake-native-desktop/audit/responsive-20260819/traditional-1440x900.png differ diff --git a/product-source/hololake-native-desktop/audit/ui-concepts-20260819/private-channel-wide.png b/product-source/hololake-native-desktop/audit/ui-concepts-20260819/private-channel-wide.png new file mode 100644 index 000000000..20d1b1bac Binary files /dev/null and b/product-source/hololake-native-desktop/audit/ui-concepts-20260819/private-channel-wide.png differ diff --git a/product-source/hololake-native-desktop/audit/ui-concepts-20260819/public-world-narrow.png b/product-source/hololake-native-desktop/audit/ui-concepts-20260819/public-world-narrow.png new file mode 100644 index 000000000..b158a5e84 Binary files /dev/null and b/product-source/hololake-native-desktop/audit/ui-concepts-20260819/public-world-narrow.png differ diff --git a/product-source/hololake-native-desktop/audit/ui-concepts-20260819/public-world-wide.png b/product-source/hololake-native-desktop/audit/ui-concepts-20260819/public-world-wide.png new file mode 100644 index 000000000..09985adfc Binary files /dev/null and b/product-source/hololake-native-desktop/audit/ui-concepts-20260819/public-world-wide.png differ diff --git a/product-source/hololake-native-desktop/audit/ui-concepts-20260819/traditional-workbench-wide.png b/product-source/hololake-native-desktop/audit/ui-concepts-20260819/traditional-workbench-wide.png new file mode 100644 index 000000000..47a120568 Binary files /dev/null and b/product-source/hololake-native-desktop/audit/ui-concepts-20260819/traditional-workbench-wide.png differ diff --git a/product-source/hololake-native-desktop/contracts/channel-growth-model.json b/product-source/hololake-native-desktop/contracts/channel-growth-model.json deleted file mode 100644 index d75702529..000000000 --- a/product-source/hololake-native-desktop/contracts/channel-growth-model.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "schema": "hololake.channel-growth-model-contract/v1", - "record_id": "HLP-CHANNEL-GROWTH-001", - "state": "LOCAL_BASELINE_RUNTIME_IMPLEMENTED_MODEL_ADJUSTMENT_SLOT_RESERVED", - "scope": "EVERY_AUTHENTICATED_HUMAN_AND_PERSONA_JOINT_CHANNEL", - "role": "REBUILDABLE_DERIVED_PROJECTION_OF_PERSONA_CHANNEL_BODY", - "canonical_body_contract": "contracts/persona-channel-body.json", - "inputs": [ - "BOUNDED_MODULE_USAGE_METADATA", - "BOUNDED_HUMAN_ACTION_METADATA", - "BOUNDED_PERSONA_COLLABORATION_METADATA" - ], - "forbidden_inputs": [ - "DOCUMENT_BODY", - "TABLE_CELL_CONTENT", - "PASSWORD_OR_CREDENTIAL", - "RAW_CONVERSATION_TRANSCRIPT", - "UNDECLARED_EXTERNAL_TELEMETRY" - ], - "local_baseline": { - "always_available_without_model_api": true, - "algorithm": "DECAYED_MODULE_AFFINITY_WITH_EXPLAINABLE_COUNTS", - "channel_recommendation": "RECOMMENDED_MODULE_ORDER", - "silent_structural_change_allowed": false, - "source_history": "PERSONA_CHANNEL_BODY_APPEND_ONLY_LANGUAGE_HASH_CHAIN", - "projection_history": "REBUILDABLE_FROM_CANONICAL_BODY", - "correction_method": "APPEND_CORRECTION_TO_CANONICAL_BODY_THEN_REBUILD_PROJECTION", - "collection_pause_supported": false, - "full_history_clear_supported": false, - "projection_is_rebuildable_but_history_is_not_mutable": true, - "account_scoped_sqlite": true - }, - "model_adjustment": { - "api_slot": "HOLOLAKE_CHANNEL_GROWTH_MODEL_API/v1", - "current_state": "NOT_CONFIGURED", - "allowed_output": "CHANNEL_ADAPTATION_PROPOSAL", - "local_schema_validation_required": true, - "local_resource_limits_required": true, - "human_confirmation_before_structural_change": true, - "model_can_mutate_channel_directly": false, - "model_can_mutate_source_content": false, - "failure_behavior": "KEEP_LOCAL_BASELINE_AND_RETURN_HUMAN_READABLE_RECEIPT" - }, - "human_controls": [ - "VIEW_MODEL", - "CORRECT_OR_REJECT_PROPOSAL", - "VIEW_LOCAL_GROWTH_CHAIN", - "EXPORT_PERSONALLY_OWNED_MODEL", - "CHOOSE_LOCAL_ONLY_OR_ANONYMIZED_SHARE" - ], - "cross_device_or_shared_learning": { - "default": "LOCAL_ONLY_OFFICIAL_CANNOT_READ", - "explicit_opt_in_required": true, - "private_model_may_be_shared_automatically": false, - "raw_growth_chain_may_be_shared": false, - "shared_artifact": "ANONYMIZED_DERIVED_SKILL_OR_MODEL_LAYER_ONLY" - } -} diff --git a/product-source/hololake-native-desktop/contracts/channel-module-runtime.json b/product-source/hololake-native-desktop/contracts/channel-module-runtime.json deleted file mode 100644 index 87f5708fb..000000000 --- a/product-source/hololake-native-desktop/contracts/channel-module-runtime.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "schema": "hololake.channel-module-runtime/v1", - "record_id": "HLP-CHANNEL-MODULE-RUNTIME-001", - "state": "FOUNDATION_INTEGRATED_MARKETPLACE_INSTALLER_PENDING", - "channel_model": "ONE_HUMAN_ONE_LOCAL_RUNTIME_ONE_ACTIVE_CHANNEL_LINE", - "built_in_foundation": { - "module_id": "hololake.builtin.channel-workbench", - "name": "频道轻量工作台", - "distribution": "RESIDENT_WITH_HOLOLAKE", - "marketplace_registration": "NOT_A_MARKETPLACE_MODULE", - "engines": ["DOCUMENT", "SPREADSHEET"], - "minimum_capabilities": [ - "REAL_TEXT_EDITING", - "REAL_CELL_EDITING", - "FORMULAS", - "COPY_PASTE", - "UNDO_REDO", - "LOCAL_NATIVE_STORAGE" - ] - }, - "marketplace_package": { - "required_fields": [ - "OFFICIAL_MODULE_NUMBER", - "PACKAGE_VERSION", - "SOURCE_COMMIT", - "PACKAGE_SHA256", - "PERMISSION_MANIFEST", - "ENTRY_MANIFEST", - "SUPPORTED_PLATFORM_TARGETS" - ], - "platform_targets": ["MACOS", "WINDOWS", "LINUX"], - "lifecycle": [ - "AVAILABLE", - "DOWNLOADING", - "HASH_VERIFIED", - "PERMISSIONS_REVIEWED", - "INSTALLED_TO_CURRENT_CHANNEL", - "DISABLED", - "RECOVERABLE_REMOVED" - ], - "install_actor": ["HUMAN", "AUTHORIZED_CHANNEL_PERSONA"], - "persona_install_rule": "PERSONA_MAY_DEPLOY_ONLY_AFTER_PACKAGE_AND_PERMISSION_VERIFICATION_AND_HUMAN_POLICY_ALLOWS", - "uninstall_boundary": "REMOVING_A_MODULE_NEVER_DELETES_CHANNEL_OR_PERSONA_GROWTH_HISTORY" - }, - "execution_boundary": { - "whole_world_bundled_into_client": false, - "industry_modules_resident_by_default": false, - "private_channel_data_uploaded_by_default": false, - "module_can_rewrite_persona_history": false, - "module_can_claim_unlisted_permissions": false - }, - "current_reality": { - "foundation_source_integrated": true, - "education_package_source_mounted_for_development": true, - "web_novel_package_source_mounted_for_development": true, - "official_marketplace_service_connected": false, - "education_package_number_registered": false, - "education_package_publishable": false, - "web_novel_package_number_registered": false, - "web_novel_package_publishable": false - } -} diff --git a/product-source/hololake-native-desktop/contracts/channel-workbench-runtime.json b/product-source/hololake-native-desktop/contracts/channel-workbench-runtime.json new file mode 100644 index 000000000..9980e8741 --- /dev/null +++ b/product-source/hololake-native-desktop/contracts/channel-workbench-runtime.json @@ -0,0 +1,58 @@ +{ + "schema": "hololake.channel-workbench-runtime/v1", + "record_id": "HLP-CHANNEL-WORKBENCH-RUNTIME-001", + "candidate_number": "HLP-DONOR-CAND-0002", + "runtime_module_number": "HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001", + "numbered_ipc": { + "module_number": "HLP-NIPC-MOD-0022", + "target_number": "HLP-NIPC-TGT-0022", + "operations": ["HLP-NIPC-OP-0074", "HLP-NIPC-OP-0075", "HLP-NIPC-OP-0076"] + }, + "engines": { + "document": "LEXICAL_0_49", + "spreadsheet": "FORTUNE_SHEET_1_0_4" + }, + "data_boundary": { + "scope": "CURRENT_AUTHENTICATED_ACCOUNT_LOCAL", + "database": "channel-workbench-v1/channel-workbench.sqlite3", + "documents_max_body_bytes": 2097152, + "spreadsheets_max_columns": 64, + "spreadsheets_max_rows": 5000, + "package_unmount_deletes_user_data": false, + "repository_code_is_executable": false + }, + "integrity": { + "optimistic_revision_required": true, + "every_save_writes_hash_chained_receipt": true, + "receipt_chain_verified_before_read_or_write": true, + "adapter_requires_signed_active_module": true + }, + "current_acceptance": { + "state": "SIGNED_INSTALLED_CONTENT_AND_RESTART_ACCEPTED", + "source_donor": "HLP-DONOR-CHAOTIC-WORKTREE-20260818", + "compiled_donor_behavior_checked": false, + "compiled_donor_boundary": "No claim of pixel-equivalent compiled-donor acceptance; the declared source slice was reconstructed and the clean host behavior was accepted directly.", + "real_signed_package": "fixtures/module-packages/HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001-0.1.0.ghmod", + "module_package_sha256": "eb7af0bd4882cc89acda11c933a3601d16f0588ff737caf8467b875ab5a4ab7b", + "signed_debug_binary_sha256": "6f851856ab6f5717facc67ec406c9eb99e240290c585438323e230daec5ae71c", + "developer_id_team": "825A9L3G7Q", + "signed_debug_cdhash": "0209a46b2dc221d37b618a2b12866bb8cb39f03d", + "real_account_human_number": "ICE-GL∞", + "ui_activation_verified": true, + "document_save": { + "title": "冰朔频道迁移验收", + "revision": 1, + "content_sha256_prefix": "534b4bd3d6d0", + "receipt_sha256_prefix": "72436f46cf1d" + }, + "spreadsheet_save": { + "title": "冰朔编号迁移表", + "revision": 1, + "formula_preserved": "=1+2", + "content_sha256_prefix": "57607d92c0dc", + "receipt_sha256_prefix": "323761fc18a3" + }, + "restart_persistence_verified": true, + "dependency_audit": "0_VULNERABILITIES_AFTER_UUID_11_1_1_OVERRIDE" + } +} diff --git a/product-source/hololake-native-desktop/contracts/direct-local-broker-numbered-registry.json b/product-source/hololake-native-desktop/contracts/direct-local-broker-numbered-registry.json new file mode 100644 index 000000000..99c3eb673 --- /dev/null +++ b/product-source/hololake-native-desktop/contracts/direct-local-broker-numbered-registry.json @@ -0,0 +1,36 @@ +{ + "schema": "hololake.direct-local-broker-numbered-registry/v1", + "record_id": "HLP-NBROKER-ROOT-001", + "runtime": { + "protocol_version": "HLP-NBROKER-v1", + "caller_number": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "legacy_string_operation_allowed": false, + "unknown_or_mismatched_coordinate": "FAIL_CLOSED", + "request_nonce_required": true, + "transport_is_authority": false + }, + "operations": [ + {"operation_number":"HLP-NBROKER-OP-0001","alias":"DISCOVER_NEARBY","channel_number":"HLP-NBROKER-CH-0001","module_number":"HLP-NBROKER-MOD-0001","target_number":"HLP-NBROKER-TGT-0001"}, + {"operation_number":"HLP-NBROKER-OP-0002","alias":"OPEN_VISITOR_SESSION","channel_number":"HLP-NBROKER-CH-0002","module_number":"HLP-NBROKER-MOD-0002","target_number":"HLP-NBROKER-TGT-0002"}, + {"operation_number":"HLP-NBROKER-OP-0003","alias":"RECEIVE_LANGUAGE","channel_number":"HLP-NBROKER-CH-0002","module_number":"HLP-NBROKER-MOD-0003","target_number":"HLP-NBROKER-TGT-0003"}, + {"operation_number":"HLP-NBROKER-OP-0004","alias":"PING","channel_number":"HLP-NBROKER-CH-0001","module_number":"HLP-NBROKER-MOD-0001","target_number":"HLP-NBROKER-TGT-0001"}, + {"operation_number":"HLP-NBROKER-OP-0005","alias":"OPEN_SESSION","channel_number":"HLP-NBROKER-CH-0003","module_number":"HLP-NBROKER-MOD-0004","target_number":"HLP-NBROKER-TGT-0004"}, + {"operation_number":"HLP-NBROKER-OP-0006","alias":"RESUME_SESSION","channel_number":"HLP-NBROKER-CH-0003","module_number":"HLP-NBROKER-MOD-0004","target_number":"HLP-NBROKER-TGT-0004"}, + {"operation_number":"HLP-NBROKER-OP-0007","alias":"HEARTBEAT_SESSION","channel_number":"HLP-NBROKER-CH-0003","module_number":"HLP-NBROKER-MOD-0004","target_number":"HLP-NBROKER-TGT-0004"}, + {"operation_number":"HLP-NBROKER-OP-0008","alias":"PRESENT_PERSONA_CARRIER_LICENSE","channel_number":"HLP-NBROKER-CH-0004","module_number":"HLP-NBROKER-MOD-0005","target_number":"HLP-NBROKER-TGT-0005"}, + {"operation_number":"HLP-NBROKER-OP-0009","alias":"GET_PERSONA_CARRIER_LICENSE_STATUS","channel_number":"HLP-NBROKER-CH-0004","module_number":"HLP-NBROKER-MOD-0005","target_number":"HLP-NBROKER-TGT-0005"}, + {"operation_number":"HLP-NBROKER-OP-0010","alias":"GET_WORK_ENVIRONMENT","channel_number":"HLP-NBROKER-CH-0003","module_number":"HLP-NBROKER-MOD-0006","target_number":"HLP-NBROKER-TGT-0006"}, + {"operation_number":"HLP-NBROKER-OP-0011","alias":"APPEND_EVENT","channel_number":"HLP-NBROKER-CH-0003","module_number":"HLP-NBROKER-MOD-0004","target_number":"HLP-NBROKER-TGT-0004"}, + {"operation_number":"HLP-NBROKER-OP-0012","alias":"RESOLVE_CAPABILITY_ROUTE","channel_number":"HLP-NBROKER-CH-0005","module_number":"HLP-NBROKER-MOD-0007","target_number":"HLP-NBROKER-TGT-0007"}, + {"operation_number":"HLP-NBROKER-OP-0013","alias":"INSTALL_DYNAMIC_NODE_REGISTRY","channel_number":"HLP-NBROKER-CH-0005","module_number":"HLP-NBROKER-MOD-0007","target_number":"HLP-NBROKER-TGT-0007"}, + {"operation_number":"HLP-NBROKER-OP-0014","alias":"RECORD_SIGNED_NODE_HEALTH","channel_number":"HLP-NBROKER-CH-0005","module_number":"HLP-NBROKER-MOD-0007","target_number":"HLP-NBROKER-TGT-0007"}, + {"operation_number":"HLP-NBROKER-OP-0015","alias":"INSPECT_MOUNTED_PNCC_REPOSITORY","channel_number":"HLP-NBROKER-CH-0006","module_number":"HLP-NBROKER-MOD-0008","target_number":"HLP-NBROKER-TGT-0008"}, + {"operation_number":"HLP-NBROKER-OP-0016","alias":"READ_MOUNTED_PNCC_REMOTE_OBJECT","channel_number":"HLP-NBROKER-CH-0006","module_number":"HLP-NBROKER-MOD-0008","target_number":"HLP-NBROKER-TGT-0008"}, + {"operation_number":"HLP-NBROKER-OP-0017","alias":"QUERY_PNCC_RECEIPT_PROJECTION","channel_number":"HLP-NBROKER-CH-0006","module_number":"HLP-NBROKER-MOD-0008","target_number":"HLP-NBROKER-TGT-0008"}, + {"operation_number":"HLP-NBROKER-OP-0018","alias":"GET_BEIJING_TIME","channel_number":"HLP-NBROKER-CH-0001","module_number":"HLP-NBROKER-MOD-0009","target_number":"HLP-NBROKER-TGT-0009"}, + {"operation_number":"HLP-NBROKER-OP-0019","alias":"ISSUE_PERSONA_TIME_TICKET","channel_number":"HLP-NBROKER-CH-0004","module_number":"HLP-NBROKER-MOD-0009","target_number":"HLP-NBROKER-TGT-0009"}, + {"operation_number":"HLP-NBROKER-OP-0020","alias":"ACQUIRE_DEVELOPMENT_WRITE_LANE","channel_number":"HLP-NBROKER-CH-0007","module_number":"HLP-NBROKER-MOD-0010","target_number":"HLP-NBROKER-TGT-0010"}, + {"operation_number":"HLP-NBROKER-OP-0021","alias":"INSPECT_DEVELOPMENT_WRITE_LANE","channel_number":"HLP-NBROKER-CH-0007","module_number":"HLP-NBROKER-MOD-0010","target_number":"HLP-NBROKER-TGT-0010"}, + {"operation_number":"HLP-NBROKER-OP-0022","alias":"RELEASE_DEVELOPMENT_WRITE_LANE","channel_number":"HLP-NBROKER-CH-0007","module_number":"HLP-NBROKER-MOD-0010","target_number":"HLP-NBROKER-TGT-0010"} + ] +} diff --git a/product-source/hololake-native-desktop/contracts/dynamic-language-world-visual-system.json b/product-source/hololake-native-desktop/contracts/dynamic-language-world-visual-system.json index 2c8232d34..5736af0c5 100644 --- a/product-source/hololake-native-desktop/contracts/dynamic-language-world-visual-system.json +++ b/product-source/hololake-native-desktop/contracts/dynamic-language-world-visual-system.json @@ -1,31 +1,83 @@ { - "schema": "HOLOLAKE_DYNAMIC_LANGUAGE_WORLD_VISUAL_SYSTEM/1", - "state": "IMPLEMENTED_LOCAL_ACCEPTANCE_PENDING", - "modes": ["DYNAMIC_LANGUAGE_WORLD", "FIXED_TRADITIONAL_THEME"], + "schema": "hololake.dynamic-language-world-visual-system/v1", + "record_id": "HLP-DYNAMIC-WORLD-SURFACE-001", + "state": "HOLOLAKE_0_5_SIGNED_NUMBERED_SURFACE_ACCEPTED", + "package": { + "official_module_number": "HLP-MOD-OFFICIAL-DYNAMIC-WORLD-SURFACE-0001", + "adapter": "dynamic-language-world-surface-v1", + "registration_class": "OFFICIAL_LIGHTHOUSE", + "activation": "SIGNED_PACKAGE_PLUS_EXPLICIT_HUMAN_PERMISSION_CONFIRMATION" + }, + "numbered_ipc": { + "module": "HLP-NIPC-MOD-0031", + "target": "HLP-NIPC-TGT-0031", + "operations": ["HLP-NIPC-OP-0147"], + "public_tauri_commands": ["numbered_ipc"], + "mismatched_coordinate": "FAIL_CLOSED" + }, "dynamic_inputs": ["BEIJING_TIME", "VERIFIED_CURRENT_WEATHER"], - "visual_balance": { - "token_layers": ["PRIMITIVE", "SEMANTIC", "COMPONENT"], - "light_scene_uses_dark_text": true, - "dark_scene_uses_light_text": true, - "routing_permission_or_fact_change_allowed": false - }, - "daily_domain_rotation": ["FIFTH_DOMAIN", "ZERO_SENSE_DOMAIN", "MAIN_DOMAIN", "BRANCH_DOMAIN", "ZERO_DOMAIN"], - "coordinates": { - "FIFTH_DOMAIN": "XIAN_INTERNAL_ONLY", - "ZERO_SENSE_DOMAIN": "BEIJING_INTERNAL_ONLY", - "MAIN_DOMAIN": "BEIJING_INTERNAL_ONLY", - "ZERO_DOMAIN": "GUANGZHOU_INTERNAL_ONLY", - "BRANCH_DOMAIN": "DETERMINISTIC_DAILY_CHINA_CITY_ROAMING_INTERNAL_ONLY" - }, - "privacy": { - "real_city_exposed_in_ui": false, - "coordinates_exposed_in_ui": false, - "weather_unavailable_behavior": "BEIJING_TIME_ONLY_NO_FAKE_WEATHER" - }, - "weather_source": { + "weather": { "provider": "Open-Meteo", "endpoint": "https://api.open-meteo.com/v1/forecast", - "documentation": "https://open-meteo.com/en/docs", - "current_variables": ["weather_code", "is_day", "precipitation", "cloud_cover", "wind_speed_10m"] + "maximum_request_seconds": 5, + "cache_ttl_seconds": 600, + "redirects": "DENIED", + "unavailable_behavior": "BEIJING_TIME_ONLY_NO_FAKE_WEATHER" + }, + "visual_lock": { + "layout": "QODER_VISUAL_LANGUAGE_SEMANTICALLY_REASSEMBLED_ON_RESPONSIVE_HOLOLAKE_SHELL", + "language_world_themes": ["夜湖星光", "晨湖曦光", "星云紫夜", "烛畔暖湖", "清浅澄湖"], + "traditional_finishes": ["曜夜", "星辉", "深海", "翡翠", "朱砂", "香槟", "瓷光", "雪霁"], + "all_downstream_pages_inherit_active_surface_tokens": true, + "ambient_motion_requires_recent_human_pointer_or_keyboard_activity": true, + "idle_visual_state": "STATIC", + "responsive_layout": "CONTAINER_MEASURED_PANORAMIC_WIDE_COMPACT_STACKED_REFLOW", + "domain_semantic_order_is_stable": true, + "private_channel_separates_native_organs_from_installed_modules": true, + "climate_changes_routing_permission_or_fact": false, + "daily_sampling_domain_changes_open_domain": false, + "real_city_exposed_in_ui": false, + "coordinates_exposed_in_ui": false, + "mechanical_flow_lines": false, + "heavy_webgl": false + }, + "donor_disposition": { + "world_climate": "EXTRACTED_REWRITTEN_AND_NUMBERED", + "starlake_surface": "QODER_LOCKED_SOURCE_ADMITTED_WITH_RESPONSIVE_AND_IDLE_MOTION_ADAPTER", + "traditional_surface": "QODER_LOCKED_SOURCE_ADMITTED_WITH_REAL_NUMBERED_PROJECTIONS_ONLY", + "traditional_surface_css": "QODER_LOCKED_EIGHT_FINISH_TOKEN_SYSTEM_ADMITTED_AND_SCOPED_ACROSS_ALL_PAGES", + "fake_broadcasts_and_fake_metrics": "REJECTED" + }, + "qoder_source_provenance": { + "root": "/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/UI-预览-20260818/deploy-staging", + "main_tsx_sha256": "7dab8596ec0bd2609b1b23b175f1f14f15f2aa9af847eb605692c993d2e876e8", + "traditional_surface_tsx_sha256": "c713dc27681e07c510a2125816dcefb3255252fc0b570e350af7a98f57fc220d", + "traditional_surface_css_sha256": "3e6c446fb84a16ed23b0fd51466ad9723d0c0670ee6ddf6653f61237bd00baa6", + "starlake_surface_css_sha256": "471e3edef02236e6463144ac75b526f0f98782b5476b9db786e7cd0614a83227", + "acceptance_html_sha256": "7a41e426f1576fe0e25182e0c2efee65302b064d86363c890b0f235386a9c070" + }, + "current_acceptance": { + "state": "PASS_NUMBERED_QODER_SURFACES_REAL_DATA_RESPONSIVE_IDLE_AND_RESTART", + "module_package_sha256": "b6ba13b9f70bd617b2a303ec2eb0ea11d5354d10118c7f3e5179ae8100cd2602", + "module_package_signature": "PASS_EMBEDDED_PRODUCT_TRUST", + "numbered_route": "HLP-NIPC-MOD-0031/HLP-NIPC-OP-0147/HLP-NIPC-TGT-0031", + "signed_app_binary_sha256": "b0422bb18df521ace54ff1a52cd47121d92833dd78f165845deba7806686c7ac", + "signed_app_cdhash": "42640f7ce56e96403609899095c77ea6171df07d", + "apple_team_identifier": "825A9L3G7Q", + "module_receipts": { + "install": "2bb903cd369c8f57432c59367fd5494ee89a75214d5129511190e2337cecdbc4", + "mount": "aed4d00e51849ae5e9c0258fe2f93afc8bff8d927905cdd7fabd9bf32c4bc66c", + "self_test_pass": "404a0eeddb866bc3050b407c586c036f3707db577e1edf9f1cf3129c1cc9cc1d" + }, + "real_weather_readback": "PASS_VERIFIED_LIVE_NIGHT_CLOUD_WITHOUT_CITY_OR_COORDINATES", + "restart_restore": "PASS_ACTIVE_MODULE_REAL_CLIMATE_THEME_AND_WEB_NOVEL_CHAPTER_RESTORED", + "official_shell_preserved": "QODER_LOCKED_SURFACES_ADMITTED_WITH_NUMBERED_FUNCTION_ROUTING_UNCHANGED", + "visual_comparison": "audit/qoder-traditional-reference-vs-hololake-0.5.0.jpg", + "idle_static_psnr_db": 59.502493, + "idle_static_clock_unchanged": true, + "web_novel_new_chapter_modal": "PASS", + "web_novel_created_chapter_restart_persistence": "PASS_1_CHAPTER_VISIBLE_AFTER_RESTART", + "traditional_theme_downstream_pages": ["CHANNEL", "WEB_NOVEL", "EDUCATION"], + "notarization": "FINAL_RELEASE_CANDIDATE_PENDING" } } diff --git a/product-source/hololake-native-desktop/contracts/education-open-source-donor-assessment.json b/product-source/hololake-native-desktop/contracts/education-open-source-donor-assessment.json deleted file mode 100644 index 2a4f8db56..000000000 --- a/product-source/hololake-native-desktop/contracts/education-open-source-donor-assessment.json +++ /dev/null @@ -1,138 +0,0 @@ -{ - "schema": "hololake.education-open-source-donor-assessment/v1", - "record_id": "HLP-EDUCATION-OSS-DONORS-001", - "state": "FOUR_TRANSLATION_FAMILIES_TWO_HEADLESS_FAMILIES_AND_ONE_REAL_SPREADSHEET_ENGINE_INTEGRATED", - "reviewed_at": "2026-08-18", - "rule": "STUDY_LOGIC_SELECT_SMALL_AUDITABLE_PARTS_AND_KEEP_HOLOLAKE_IDENTITY_STORAGE_PERMISSION_AND_UI_BOUNDARIES", - "candidates": [ - { - "name": "Calamine", - "role": "BOUNDED_SPREADSHEET_READ_ADAPTER", - "upstream": "https://github.com/tafia/calamine", - "observed_license": "MIT", - "decision": "INTEGRATE_PINNED_0_26_1_READ_ONLY_NO_OFFICE_RUNTIME" - }, - { - "name": "rust_xlsxwriter", - "role": "BOUNDED_XLSX_EXPORT_ADAPTER", - "upstream": "https://github.com/jmcnamara/rust_xlsxwriter", - "observed_license": "MIT OR Apache-2.0", - "decision": "INTEGRATE_PINNED_0_64_2_WRITE_NEW_XLSX_ONLY" - }, - { - "name": "rust-csv", - "role": "BOUNDED_DELIMITED_IMPORT_EXPORT_ADAPTER", - "upstream": "https://github.com/BurntSushi/rust-csv", - "observed_license": "Unlicense OR MIT", - "decision": "INTEGRATE_PINNED_1_3_0" - }, - { - "name": "encoding_rs", - "role": "LEGACY_CSV_TEXT_DECODER", - "upstream": "https://github.com/hsivonen/encoding_rs", - "observed_license": "MIT OR Apache-2.0", - "decision": "INTEGRATE_UTF_AND_GBK_FALLBACK_WITHOUT_NETWORK" - }, - { - "name": "AppFlowy", - "role": "FULL_WORKSPACE_LOGIC_DONOR", - "upstream": "https://github.com/AppFlowy-IO/AppFlowy", - "observed_license": "AGPL-3.0", - "decision": "DO_NOT_EMBED_WHOLE_APPLICATION_STUDY_WORKSPACE_AND_DATABASE_INTERACTION_LOGIC_ONLY" - }, - { - "name": "Lexical", - "role": "LAZY_LOADED_RICH_DOCUMENT_ENGINE", - "upstream": "https://github.com/facebook/lexical", - "observed_license": "MIT", - "source_review_commit": "29d4007d4322912ce75a416ac90cc9d224d3f029", - "decision": "INTEGRATE_PINNED_0_49_0_STATE_COMMAND_HISTORY_AND_MARKDOWN_PARTS_ONLY" - }, - { - "name": "TanStack Table", - "role": "HEADLESS_TABLE_ROW_MODEL_ENGINE", - "upstream": "https://github.com/TanStack/table", - "observed_license": "MIT", - "source_review_commit": "ac4f1345220649da3c60a24471b7f130fb159ab4", - "decision": "INTEGRATE_PINNED_8_21_3_SORT_FILTER_GROUP_AGGREGATE_AND_PAGINATION_ROW_MODELS_ONLY" - }, - { - "name": "Univer", - "role": "FULL_SPREADSHEET_AND_DOCUMENT_ENGINE_ARCHITECTURE_DONOR", - "upstream": "https://github.com/dream-num/univer", - "observed_license": "Apache-2.0", - "source_review_commit": "9251e0d30d4c4aeb4707dc4d856055081399cf38", - "decision": "SOURCE_STUDY_ONLY_DO_NOT_EMBED_WHOLE_APPLICATION_DEFER_FORMULA_AND_ADVANCED_GRID_PARTS" - }, - { - "name": "FortuneSheet", - "role": "LAZY_LOADED_REAL_SPREADSHEET_ENGINE_FOR_BUILT_IN_CHANNEL_WORKBENCH", - "upstream": "https://github.com/ruilisi/fortune-sheet", - "observed_license": "MIT", - "source_review_commit": "94346608877db4747406707a177c4b8f3bacdbf9", - "decision": "INTEGRATE_PINNED_1_0_4_REAL_CELLS_FORMULAS_EDITING_FORMATTING_FILTERING_AND_FREEZE_WITH_HOLOLAKE_NATIVE_STORAGE_ADAPTER", - "security_adjustment": "OVERRIDE_UUID_TO_11_1_1_FORTUNE_ONLY_USES_V4_PRODUCTION_AUDIT_ZERO_KNOWN_VULNERABILITIES" - }, - { - "name": "Glide Data Grid", - "role": "HIGH_PERFORMANCE_GRID_ARCHITECTURE_DONOR", - "upstream": "https://github.com/glideapps/glide-data-grid", - "observed_license": "MIT", - "source_review_commit": "0875d78", - "decision": "SOURCE_STUDY_ONLY_GRID_IS_NOT_A_SPREADSHEET_OR_FORMULA_ENGINE" - }, - { - "name": "Vega-Lite", - "role": "DECLARATIVE_PROJECTION_GRAMMAR_DONOR", - "upstream": "https://github.com/vega/vega-lite", - "observed_license": "BSD-3-Clause", - "decision": "ADOPT_DECLARATIVE_DIMENSION_MEASURE_AND_VIEW_SPEC_CONCEPTS_WITHOUT_IMPORTING_RUNTIME" - }, - { - "name": "Observable Plot", - "role": "LAYERED_MARK_AND_COMPOSABLE_TRANSFORM_DONOR", - "upstream": "https://github.com/observablehq/plot", - "observed_license": "ISC", - "decision": "ADOPT_LAYERED_PROJECTION_AND_TIDY_DATA_CONCEPTS_WITHOUT_IMPORTING_RUNTIME" - }, - { - "name": "Apache ECharts", - "role": "FULL_BROWSER_VISUALIZATION_RUNTIME_CANDIDATE", - "upstream": "https://github.com/apache/echarts", - "observed_license": "Apache-2.0", - "decision": "DO_NOT_IMPORT_FOR_STAGE_ONE_FIVE_NATIVE_VIEWS_DO_NOT_JUSTIFY_RUNTIME_WEIGHT" - }, - { - "name": "Apache Arrow DataFusion", - "role": "TYPED_COLUMNAR_QUERY_PLAN_DONOR", - "upstream": "https://github.com/apache/datafusion", - "observed_license": "Apache-2.0", - "decision": "ADOPT_TYPED_SCHEMA_AND_VALIDATED_PLAN_CONCEPTS_DEFER_ENGINE_UNTIL_DATA_SCALE_REQUIRES_IT" - }, - { - "name": "DuckDB", - "role": "EMBEDDED_ANALYTICAL_ENGINE_CANDIDATE", - "upstream": "https://github.com/duckdb/duckdb", - "observed_license": "MIT", - "decision": "DEFER_NATIVE_DEPENDENCY_CURRENT_BOUNDED_ACCOUNT_DATA_EXECUTES_WITH_SMALLER_HOLOLAKE_CORE" - } - ], - "current_slice": { - "external_translation_library_families_added": 4, - "external_headless_engine_families_added": 2, - "real_spreadsheet_engine_families_added": 1, - "direct_frontend_engine_packages_added": 10, - "external_source_files_copied": 0, - "source_research_route": "LOCAL_DIRECT_TO_BS_SG_002_THEN_EXTERNAL_DRIVE", - "source_research_root": "/Volumes/JZAO/HoloLake/development/拆件车间-open-src-20260816/education-engines-20260818", - "source_repositories_retained_outside_product_tree": 5, - "concepts_adopted": [ - "DECLARATIVE_DIMENSION_MEASURE_AND_PROJECTION_SPEC", - "TYPED_NATIVE_COLUMNS_AND_VALIDATED_EXECUTION_PLAN", - "LAYERED_MARKS_WITH_ONE_SHARED_DATASET", - "HEADLESS_DATA_BEHAVIOR_SEPARATE_FROM_HUMAN_RENDERING" - ], - "implementation": "BUILT_IN_CHANNEL_DOCUMENT_AND_SPREADSHEET_FOUNDATION_PLUS_DEVELOPMENT_MOUNTED_EDUCATION_PACKAGE_WITH_PRODUCT_OWNED_PROFILE_ROUTING_IDENTITY_STORAGE_PERMISSION_AND_ADVANCED_VIEWS" - }, - "legal_boundary": "PRELIMINARY_ENGINEERING_SCREEN_NOT_FINAL_DISTRIBUTION_LEGAL_APPROVAL" -} diff --git a/product-source/hololake-native-desktop/contracts/education-workspace.json b/product-source/hololake-native-desktop/contracts/education-workspace.json index e26305a41..1b58970d3 100644 --- a/product-source/hololake-native-desktop/contracts/education-workspace.json +++ b/product-source/hololake-native-desktop/contracts/education-workspace.json @@ -1,15 +1,15 @@ { "schema": "hololake.education-workspace/v1", "record_id": "HLP-EDUCATION-WORKSPACE-001", - "state": "DEVELOPMENT_MODULE_SOURCE_MOUNTED_ACCEPTANCE_PENDING", + "state": "ADMITTED_INSTALLED_RUNTIME_ACCEPTED", "domain_entry": "BRANCH_DOMAIN", "industry": "EDUCATION", "channel_id": "GH-EDU-INIT-001", "package": { "package_key": "hololake.official.education-workbench", - "official_module_number": null, - "registration_state": "PENDING_FUNCTIONAL_ACCEPTANCE_AND_NUMBER_REGISTRATION", - "current_mount": "DEVELOPMENT_BUNDLE_ONLY", + "official_module_number": "HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001", + "registration_state": "OFFICIAL_NUMBER_ACCEPTED", + "current_mount": "ACTIVE_AFTER_SIGNED_INSTALL_MOUNT_AND_SELF_TEST", "future_distribution": "HOLOLAKE_OFFICIAL_MODULE_MARKETPLACE" }, "foundation_dependency": { @@ -238,5 +238,28 @@ "model_execution_authority_granted": false, "external_application_embedded": false }, + "current_acceptance": { + "state": "PASS", + "runtime_module_number": "HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001", + "numbered_ipc_module": "HLP-NIPC-MOD-0024", + "numbered_operations": "HLP-NIPC-OP-0085..0102", + "signed_package_sha256": "a9f6fb7dd9c4aaf910a5d20d0a1d72dea6ab921cf36dd50d46dd88cde3a09e03", + "signed_app_binary_sha256": "028592b137fe4f19a642e1301d6fb500e2a4fde50ddb616e229de8a48090803c", + "developer_id_team": "825A9L3G7Q", + "developer_id_cdhash": "4c88261b4b573d08edcbba8d67d8afe27ad82a3e", + "runtime_receipts": { + "install": "68f296a11be19c384cb67af9f529a03cb8389bd256c153c5905451fbc0011a24", + "mount": "ddb6a51d47f6a58a72a7fc748eff71687cbd04be5dde33dbce590a7abba6af5f", + "self_test_pass": "80d59b7c9c2f14005fd8a24748a25c2be0d55198fdcb621f528267ae6dccc12a" + }, + "restart_readback": { + "module_state": "ACTIVE", + "document": "冰朔教育迁移验收 · revision 2", + "table": "冰朔教育迁移验收表 · 1 row · revision 4", + "automation": "验收状态自动化 · preview 1 row / 1 cell · APPLIED revision 3 to 4" + }, + "existing_user_data_deleted_or_overwritten": false, + "apple_notarization_scope": "FINAL_RELEASE_CANDIDATE_ONLY_NOT_THIS_DEBUG_ACCEPTANCE_BUNDLE" + }, "open_source_donor_assessment": "contracts/education-open-source-donor-assessment.json" } diff --git a/product-source/hololake-native-desktop/contracts/gls-executable-projections.json b/product-source/hololake-native-desktop/contracts/gls-executable-projections.json index 18f04c724..9469f3e63 100644 --- a/product-source/hololake-native-desktop/contracts/gls-executable-projections.json +++ b/product-source/hololake-native-desktop/contracts/gls-executable-projections.json @@ -1,6 +1,6 @@ { "schema": "hololake.gls-executable-projections/v2", - "source_commit": "2598fbfba8caf64c7ab9740a3036c5aab977502e", + "source_commit": "104a5d73162bdf4a529701e65898e2bc2863ea9e", "runtime_graph_rule": "ONLY_EXPLICIT_RUNTIME_REQUIRES_EDGES_ENTER_ACTIVATION_GRAPH", "projections": { "GLS-0250": {"stage":"P0","projection_kind":"TYPED_FACT_AND_DOMAIN_BOUNDARY","adapter":"origin-domain-topology","event_kinds":["BOOTSTRAP","DOMAIN_ROUTE"],"dependencies":[]}, diff --git a/product-source/hololake-native-desktop/contracts/gls-native-runtime-kernel.json b/product-source/hololake-native-desktop/contracts/gls-native-runtime-kernel.json index 36e5252db..f3e06643a 100644 --- a/product-source/hololake-native-desktop/contracts/gls-native-runtime-kernel.json +++ b/product-source/hololake-native-desktop/contracts/gls-native-runtime-kernel.json @@ -1,7 +1,7 @@ { "schema": "hololake.gls-native-runtime-kernel/v1", "record_id": "HLP-GLS-NATIVE-RUNTIME-KERNEL-001", - "source_commit": "2598fbfba8caf64c7ab9740a3036c5aab977502e", + "source_commit": "104a5d73162bdf4a529701e65898e2bc2863ea9e", "receipt_schema": "hololake.protocol-decision-receipt/v1", "decision_set": ["ALLOW", "DENY", "AMBIGUOUS", "UNVERIFIED"], "runtime_boundaries": { diff --git a/product-source/hololake-native-desktop/contracts/gls-numbered-reference-nodes.json b/product-source/hololake-native-desktop/contracts/gls-numbered-reference-nodes.json new file mode 100644 index 000000000..0623d3153 --- /dev/null +++ b/product-source/hololake-native-desktop/contracts/gls-numbered-reference-nodes.json @@ -0,0 +1,33 @@ +{ + "schema": "hololake.gls-numbered-reference-nodes/v1", + "record_id": "HLP-GLS-NUMBERED-REFERENCE-REGISTRY-001", + "source": { + "repository": "REPO-012", + "commit": "104a5d73162bdf4a529701e65898e2bc2863ea9e" + }, + "policy": { + "number_is_coordinate_not_authority": true, + "independent_protocol_source_required_for_execution": true, + "reference_only_nodes_may_execute": false, + "unknown_reference": "FAIL_CLOSED", + "unresolved_number_reference_allowed": false + }, + "nodes": [ + {"protocol_id":"GLS-0010","node_number":"HLP-GLS-REF-0010","title":"Guanghu Protocol Registry Center Standard","reference_kind":"NORMATIVE_REFERENCE","source_state":"ROADMAP_REFERENCE_ONLY"}, + {"protocol_id":"GLS-0140","node_number":"HLP-GLS-REF-0140","title":"Context Loading Specification","reference_kind":"NORMATIVE_REFERENCE","source_state":"ROADMAP_REFERENCE_ONLY"}, + {"protocol_id":"GLS-0401","node_number":"HLP-GLS-REF-0401","title":"Tree","reference_kind":"SCHEMA_IMPORT","source_state":"ROADMAP_REFERENCE_ONLY"}, + {"protocol_id":"GLS-0402","node_number":"HLP-GLS-REF-0402","title":"Leaf","reference_kind":"SCHEMA_IMPORT","source_state":"ROADMAP_REFERENCE_ONLY"}, + {"protocol_id":"GLS-0403","node_number":"HLP-GLS-REF-0403","title":"Lock","reference_kind":"SCHEMA_IMPORT","source_state":"ROADMAP_REFERENCE_ONLY"}, + {"protocol_id":"GLS-0404","node_number":"HLP-GLS-REF-0404","title":"Trigger","reference_kind":"SCHEMA_IMPORT","source_state":"ROADMAP_REFERENCE_ONLY"}, + {"protocol_id":"GLS-0406","node_number":"HLP-GLS-REF-0406","title":"Evidence","reference_kind":"SCHEMA_IMPORT","source_state":"ROADMAP_REFERENCE_ONLY"}, + {"protocol_id":"GLS-0407","node_number":"HLP-GLS-REF-0407","title":"Correction","reference_kind":"SCHEMA_IMPORT","source_state":"ROADMAP_REFERENCE_ONLY"}, + {"protocol_id":"GLS-0409","node_number":"HLP-GLS-REF-0409","title":"Machine-State History","reference_kind":"SCHEMA_IMPORT","source_state":"ROADMAP_REFERENCE_ONLY"}, + {"protocol_id":"GLS-0602","node_number":"HLP-GLS-REF-0602","title":"Authorization","reference_kind":"SCHEMA_IMPORT","source_state":"ROADMAP_REFERENCE_ONLY"}, + {"protocol_id":"GLS-0603","node_number":"HLP-GLS-REF-0603","title":"Signature","reference_kind":"SCHEMA_IMPORT","source_state":"ROADMAP_REFERENCE_ONLY"}, + {"protocol_id":"GLS-0604","node_number":"HLP-GLS-REF-0604","title":"Integrity","reference_kind":"EVIDENCE_ONLY","source_state":"ROADMAP_REFERENCE_ONLY"}, + {"protocol_id":"GLS-0605","node_number":"HLP-GLS-REF-0605","title":"Semantic Safety Boundary","reference_kind":"SCHEMA_IMPORT","source_state":"ROADMAP_REFERENCE_ONLY"}, + {"protocol_id":"GLS-0814","node_number":"HLP-GLS-REF-0814","title":"Tool Runtime","reference_kind":"NORMATIVE_REFERENCE","source_state":"ROADMAP_REFERENCE_ONLY"}, + {"protocol_id":"GLS-0816","node_number":"HLP-GLS-REF-0816","title":"Checkpoint Runtime","reference_kind":"NORMATIVE_REFERENCE","source_state":"ROADMAP_REFERENCE_ONLY"}, + {"protocol_id":"GLS-0830","node_number":"HLP-GLS-REF-0830","title":"Guanghu Language World Core","reference_kind":"NORMATIVE_REFERENCE","source_state":"ROADMAP_REFERENCE_ONLY"} + ] +} diff --git a/product-source/hololake-native-desktop/contracts/gls-runtime-registry.json b/product-source/hololake-native-desktop/contracts/gls-runtime-registry.json index 321c519cd..31f566864 100644 --- a/product-source/hololake-native-desktop/contracts/gls-runtime-registry.json +++ b/product-source/hololake-native-desktop/contracts/gls-runtime-registry.json @@ -3,7 +3,7 @@ "record_id": "HLP-GLS-RUNTIME-MANIFEST-002", "source": { "repository": "REPO-012", - "commit": "2598fbfba8caf64c7ab9740a3036c5aab977502e", + "commit": "104a5d73162bdf4a529701e65898e2bc2863ea9e", "root": "gls", "authority_files": [ { @@ -14,7 +14,7 @@ { "authority_kind": "GLS_ENTRY", "source_path": "gls/GLS-ENTRY.hdlp", - "source_sha256": "b0937dab245c40c68feeb861258fa7d8cb6f721e30042bff098a35b216956e79" + "source_sha256": "f716c3519df79c55871a0afeba5cc46a9edbb078bc7371301ab408ce3c82a897" }, { "authority_kind": "SOURCE_MANIFEST", @@ -24,7 +24,7 @@ { "authority_kind": "ARCHITECTURE_CATALOG", "source_path": "gls/GLS-ARCHITECTURE-CATALOG.hdlp", - "source_sha256": "1107adbc8290797bdafea23dd5f13db46e2ee621356987f138f6616cfe6bc49d" + "source_sha256": "e1e82d145db0544b9ed71cdfd3cbdfdd5cd07d86c6f5371ba2da125cf44d92c8" } ] }, @@ -40,7 +40,7 @@ "unknown_protocol": "FAIL_CLOSED" }, "reconciliation": { - "numbered_protocol_count": 75, + "numbered_protocol_count": 83, "protocol_registry_id_count": 52, "existing_registered_count": 19, "registered_draft_count": 33, @@ -68,15 +68,8 @@ "GLS-0830" ], "dependencies_without_numbered_source": [ - "GLS-0001", - "GLS-0002", "GLS-0010", - "GLS-0101", - "GLS-0110", "GLS-0140", - "GLS-0200", - "GLS-0300", - "GLS-0400", "GLS-0401", "GLS-0402", "GLS-0403", @@ -88,12 +81,16 @@ "GLS-0603", "GLS-0604", "GLS-0605", - "GLS-0810", "GLS-0814", "GLS-0816", "GLS-0830" ], + "numbered_reference_node_count": 16, + "unresolved_number_references": [], + "unresolved_number_reference_count": 0, + "every_dependency_has_number_coordinate": true, "numbered_sources_not_in_protocol_registry": [ + "GLS-0101", "GLS-0223", "GLS-0224", "GLS-0227", @@ -182,15 +179,404 @@ "discovered_unreconciled_count": 0, "authority_conflict_count": 0 }, - "protocol_count": 75, + "protocol_count": 83, "executable_projection_count": 25, - "inventoried_not_executable_count": 50, + "inventoried_not_executable_count": 58, + "number_coordinate_count": 99, + "numbered_reference_nodes": [ + { + "protocol_id": "GLS-0010", + "node_number": "HLP-GLS-REF-0010", + "title": "Guanghu Protocol Registry Center Standard", + "reference_kind": "NORMATIVE_REFERENCE", + "source_state": "ROADMAP_REFERENCE_ONLY", + "execution_state": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence_paths": [ + "gls/GLS-PROTOCOL-RECONCILIATION-AND-NATIVE-OS-REGISTRATION-20260731.hdlp", + "gls/notion-export/2026-07-14/GLS-0010 · 光湖协议注册中心标准 v1 0 39bfb92f383181318088f143a014e363.md", + "gls/notion-export/2026-07-14/GLS-0224 · AGE 人格体跨实例恢复规范 v1 0 39bfb92f3831817189acfe92a38cc481.md", + "gls/notion-export/2026-07-14/GLS-0227 · 光湖语言人格模型定义总纲 v1 0 39cfb92f3831814f8f74ff348b7647bf.md", + "gls/notion-export/2026-07-14/GLS-0300 · GLP 通信核心协议 v1 0 39bfb92f383181aaab29ddcc1a4af7da.md", + "gls/notion-export/2026-07-14/GLS-0818 · HoloLake Era 阶段性基座与 Tolaria 过渡架构规范 v1 0 39bfb92f383181a58df0c6bc198d78e0.md", + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "gls/notion-export/2026-07-14/《GLS 标准体系建立 · 双向认知演化记录 · 2026-07-12》 39bfb92f383180bea717f5724c6643b8.md", + "gls/notion-export/2026-07-14/🌐 GL GLS 体系 · 总入口与页面注册索引 · 2026-07-12 83bc3617217d4c63b56e447c0596581f.md", + "gls/protocols/GLS-0710-GUANGHU-MODULE-PROTOCOL.hdlp" + ] + }, + { + "protocol_id": "GLS-0140", + "node_number": "HLP-GLS-REF-0140", + "title": "Context Loading Specification", + "reference_kind": "NORMATIVE_REFERENCE", + "source_state": "ROADMAP_REFERENCE_ONLY", + "execution_state": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence_paths": [ + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "gls/protocols/GLS-0303-GLP-CONTEXT.hdlp" + ] + }, + { + "protocol_id": "GLS-0401", + "node_number": "HLP-GLS-REF-0401", + "title": "Tree", + "reference_kind": "SCHEMA_IMPORT", + "source_state": "ROADMAP_REFERENCE_ONLY", + "execution_state": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence_paths": [ + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "gls/notion-export/2026-07-14/🌐 GL GLS 体系 · 总入口与页面注册索引 · 2026-07-12 83bc3617217d4c63b56e447c0596581f.md", + "gls/protocols/GLS-0411-HLDP-NATIVE-PROGRAMMING-PROFILE.hdlp" + ] + }, + { + "protocol_id": "GLS-0402", + "node_number": "HLP-GLS-REF-0402", + "title": "Leaf", + "reference_kind": "SCHEMA_IMPORT", + "source_state": "ROADMAP_REFERENCE_ONLY", + "execution_state": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence_paths": [ + "gls/notion-export/2026-07-14/GLS-0002 39bfb92f38318015b05fc2082630b39e.md", + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "gls/protocols/GLS-0411-HLDP-NATIVE-PROGRAMMING-PROFILE.hdlp" + ] + }, + { + "protocol_id": "GLS-0403", + "node_number": "HLP-GLS-REF-0403", + "title": "Lock", + "reference_kind": "SCHEMA_IMPORT", + "source_state": "ROADMAP_REFERENCE_ONLY", + "execution_state": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence_paths": [ + "gls/notion-export/2026-07-14/GLS-0002 39bfb92f38318015b05fc2082630b39e.md", + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "gls/protocols/GLS-0411-HLDP-NATIVE-PROGRAMMING-PROFILE.hdlp" + ] + }, + { + "protocol_id": "GLS-0404", + "node_number": "HLP-GLS-REF-0404", + "title": "Trigger", + "reference_kind": "SCHEMA_IMPORT", + "source_state": "ROADMAP_REFERENCE_ONLY", + "execution_state": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence_paths": [ + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "gls/protocols/GLS-0411-HLDP-NATIVE-PROGRAMMING-PROFILE.hdlp" + ] + }, + { + "protocol_id": "GLS-0406", + "node_number": "HLP-GLS-REF-0406", + "title": "Evidence", + "reference_kind": "SCHEMA_IMPORT", + "source_state": "ROADMAP_REFERENCE_ONLY", + "execution_state": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence_paths": [ + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "gls/protocols/GLS-0304-GLP-MEMORY-SYNC.hdlp", + "gls/protocols/GLS-0306-GLP-RECEIPT.hdlp", + "gls/protocols/GLS-0311-GUANGHU-LIVE-OPERATIONS-WITNESS.hdlp", + "gls/protocols/GLS-0411-HLDP-NATIVE-PROGRAMMING-PROFILE.hdlp" + ] + }, + { + "protocol_id": "GLS-0407", + "node_number": "HLP-GLS-REF-0407", + "title": "Correction", + "reference_kind": "SCHEMA_IMPORT", + "source_state": "ROADMAP_REFERENCE_ONLY", + "execution_state": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence_paths": [ + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "gls/protocols/GLS-0304-GLP-MEMORY-SYNC.hdlp", + "gls/protocols/GLS-0411-HLDP-NATIVE-PROGRAMMING-PROFILE.hdlp" + ] + }, + { + "protocol_id": "GLS-0409", + "node_number": "HLP-GLS-REF-0409", + "title": "Machine-State History", + "reference_kind": "SCHEMA_IMPORT", + "source_state": "ROADMAP_REFERENCE_ONLY", + "execution_state": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence_paths": [ + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "gls/protocols/GLS-0308-GLP-STATE-SYNC.hdlp", + "gls/protocols/GLS-0827-PERSONA-TIME-CONTINUITY-PROTOCOL.hdlp" + ] + }, + { + "protocol_id": "GLS-0602", + "node_number": "HLP-GLS-REF-0602", + "title": "Authorization", + "reference_kind": "SCHEMA_IMPORT", + "source_state": "ROADMAP_REFERENCE_ONLY", + "execution_state": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence_paths": [ + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "gls/protocols/GLS-0302-GLP-IDENTITY.hdlp", + "gls/protocols/GLS-0309-GLP-WORK-ORDER.hdlp", + "gls/protocols/GLS-0708-GUANGHU-MODEL-ROUTING-PROTOCOL.hdlp", + "gls/protocols/GLS-0709-UNIVERSAL-ADAPTER-PROTOCOL.hdlp", + "gls/protocols/GLS-0819-GUANGHU-RUNWAY-SCHEDULING-PROTOCOL.hdlp", + "gls/protocols/GLS-0828-PERSONA-EXTENSION-NODE.hdlp", + "gls/protocols/GLS-0840-GUANGHU-OS-KERNEL.hdlp", + "gls/protocols/GLS-0842-HOLOLAKE-LIVE-SESSION-PROTOCOL.hdlp" + ] + }, + { + "protocol_id": "GLS-0603", + "node_number": "HLP-GLS-REF-0603", + "title": "Signature", + "reference_kind": "SCHEMA_IMPORT", + "source_state": "ROADMAP_REFERENCE_ONLY", + "execution_state": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence_paths": [ + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "gls/protocols/GLS-0302-GLP-IDENTITY.hdlp", + "gls/protocols/GLS-0710-GUANGHU-MODULE-PROTOCOL.hdlp" + ] + }, + { + "protocol_id": "GLS-0604", + "node_number": "HLP-GLS-REF-0604", + "title": "Integrity", + "reference_kind": "EVIDENCE_ONLY", + "source_state": "ROADMAP_REFERENCE_ONLY", + "execution_state": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence_paths": [ + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "gls/protocols/GLS-0306-GLP-RECEIPT.hdlp", + "gls/protocols/GLS-0311-GUANGHU-LIVE-OPERATIONS-WITNESS.hdlp", + "gls/protocols/GLS-0710-GUANGHU-MODULE-PROTOCOL.hdlp", + "gls/protocols/GLS-0841-GUANGHU-HARDWARE-ABSTRACTION-LAYER.hdlp" + ] + }, + { + "protocol_id": "GLS-0605", + "node_number": "HLP-GLS-REF-0605", + "title": "Semantic Safety Boundary", + "reference_kind": "SCHEMA_IMPORT", + "source_state": "ROADMAP_REFERENCE_ONLY", + "execution_state": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence_paths": [ + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "gls/protocols/GLS-0708-GUANGHU-MODEL-ROUTING-PROTOCOL.hdlp", + "gls/protocols/GLS-0709-UNIVERSAL-ADAPTER-PROTOCOL.hdlp" + ] + }, + { + "protocol_id": "GLS-0814", + "node_number": "HLP-GLS-REF-0814", + "title": "Tool Runtime", + "reference_kind": "NORMATIVE_REFERENCE", + "source_state": "ROADMAP_REFERENCE_ONLY", + "execution_state": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence_paths": [ + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "gls/protocols/GLS-0828-PERSONA-EXTENSION-NODE.hdlp" + ] + }, + { + "protocol_id": "GLS-0816", + "node_number": "HLP-GLS-REF-0816", + "title": "Checkpoint Runtime", + "reference_kind": "NORMATIVE_REFERENCE", + "source_state": "ROADMAP_REFERENCE_ONLY", + "execution_state": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence_paths": [ + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "gls/protocols/GLS-0827-PERSONA-TIME-CONTINUITY-PROTOCOL.hdlp", + "gls/protocols/GLS-0836-GUANGHU-WORLD-BOOTSTRAP-AND-RECOVERY.hdlp" + ] + }, + { + "protocol_id": "GLS-0830", + "node_number": "HLP-GLS-REF-0830", + "title": "Guanghu Language World Core", + "reference_kind": "NORMATIVE_REFERENCE", + "source_state": "ROADMAP_REFERENCE_ONLY", + "execution_state": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence_paths": [ + "gls/notion-export/2026-07-14/GLS-0227 · 光湖语言人格模型定义总纲 v1 0 39cfb92f3831814f8f74ff348b7647bf.md", + "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "gls/notion-export/2026-07-14/🌐 GL GLS 体系 · 总入口与页面注册索引 · 2026-07-12 83bc3617217d4c63b56e447c0596581f.md", + "gls/protocols/GLS-0836-GUANGHU-WORLD-BOOTSTRAP-AND-RECOVERY.hdlp" + ] + } + ], "protocols": [ + { + "id": "GLS-0001", + "title": "GLS-0001", + "source_status": "UNSPECIFIED_SOURCE_STATUS", + "source_path": "gls/notion-export/2026-07-14/GLS-0001 39bfb92f383180059deefa1bac489399.md", + "source_format": "LEGACY_MARKDOWN_EVIDENCE", + "source_sha256": "9ac9903c3b3066364e8b6fa97363f0c0fc85754d7ee1b07774236a4b24bc7bc2", + "alternate_source_count": 0, + "registration": { + "state": "REGISTERED_PROTOCOL_REGISTRY", + "authorities": [ + { + "id": "GLS-0001", + "authority_kind": "PROTOCOL_REGISTRY", + "declared_source_path": null + } + ], + "declared_source_paths": [], + "routing_reference_count": 0 + }, + "maturity": { + "registry_section": "existing_registered", + "registry_status": null, + "family": null, + "implementation_evidence": null + }, + "contract_kind": "UNCLASSIFIED_PROTOCOL_SOURCE", + "implementation_stage": null, + "projection_state": "INVENTORIED_NOT_EXECUTABLE", + "projection_kind": null, + "adapter": null, + "event_kinds": [], + "dependencies": [], + "dependency_edges": [], + "activation_blockers": [ + "NO_EXECUTABLE_ADAPTER" + ] + }, + { + "id": "GLS-0002", + "title": "GLS-0002", + "source_status": "UNSPECIFIED_SOURCE_STATUS", + "source_path": "gls/notion-export/2026-07-14/GLS-0002 39bfb92f38318015b05fc2082630b39e.md", + "source_format": "LEGACY_MARKDOWN_EVIDENCE", + "source_sha256": "c50f767aa2f3a863f708628b33054ecb2ba18799aa68218bf89e25d263fa9b6c", + "alternate_source_count": 0, + "registration": { + "state": "REGISTERED_PROTOCOL_REGISTRY", + "authorities": [ + { + "id": "GLS-0002", + "authority_kind": "PROTOCOL_REGISTRY", + "declared_source_path": null + } + ], + "declared_source_paths": [], + "routing_reference_count": 0 + }, + "maturity": { + "registry_section": "existing_registered", + "registry_status": null, + "family": null, + "implementation_evidence": null + }, + "contract_kind": "UNCLASSIFIED_PROTOCOL_SOURCE", + "implementation_stage": null, + "projection_state": "INVENTORIED_NOT_EXECUTABLE", + "projection_kind": null, + "adapter": null, + "event_kinds": [], + "dependencies": [], + "dependency_edges": [], + "activation_blockers": [ + "NO_EXECUTABLE_ADAPTER" + ] + }, + { + "id": "GLS-0101", + "title": "GLS-0101", + "source_status": "draft", + "source_path": "gls/notion-export/2026-07-14/GLS-0101 39bfb92f38318089b31efe6c1c2cb6b6.md", + "source_format": "LEGACY_MARKDOWN_EVIDENCE", + "source_sha256": "e88d977de617506f0d5d6ac58f406486c6c70fdc10eac2ff09ad6cc2f2d2ea16", + "alternate_source_count": 0, + "registration": { + "state": "REGISTERED_OTHER_CANONICAL_INDEX", + "authorities": [ + { + "id": "GLS-0101", + "authority_kind": "SOURCE_MANIFEST", + "declared_source_path": null + } + ], + "declared_source_paths": [], + "routing_reference_count": 1 + }, + "maturity": { + "registry_section": null, + "registry_status": null, + "family": null, + "implementation_evidence": null + }, + "contract_kind": "UNCLASSIFIED_PROTOCOL_SOURCE", + "implementation_stage": null, + "projection_state": "INVENTORIED_NOT_EXECUTABLE", + "projection_kind": null, + "adapter": null, + "event_kinds": [], + "dependencies": [], + "dependency_edges": [], + "activation_blockers": [ + "NO_EXECUTABLE_ADAPTER" + ] + }, + { + "id": "GLS-0110", + "title": "ISRP 自然语言入口解析与安全路由协议 v1.0", + "source_status": "UNSPECIFIED_SOURCE_STATUS", + "source_path": "gls/notion-export/2026-07-14/GLS-0110 · ISRP 自然语言入口解析与安全路由协议 v1 0 39bfb92f38318128a372dbadbde86ead.md", + "source_format": "LEGACY_MARKDOWN_EVIDENCE", + "source_sha256": "314a718f05a3da1e5750831df5377d9fc76db86463a7b37c052976fe9aaeb2c4", + "alternate_source_count": 0, + "registration": { + "state": "REGISTERED_PROTOCOL_REGISTRY", + "authorities": [ + { + "id": "GLS-0110", + "authority_kind": "PROTOCOL_REGISTRY", + "declared_source_path": null + } + ], + "declared_source_paths": [], + "routing_reference_count": 0 + }, + "maturity": { + "registry_section": "existing_registered", + "registry_status": null, + "family": null, + "implementation_evidence": null + }, + "contract_kind": "UNCLASSIFIED_PROTOCOL_SOURCE", + "implementation_stage": null, + "projection_state": "INVENTORIED_NOT_EXECUTABLE", + "projection_kind": null, + "adapter": null, + "event_kinds": [], + "dependencies": [], + "dependency_edges": [], + "activation_blockers": [ + "NO_EXECUTABLE_ADAPTER" + ] + }, { "id": "GLS-0130", "title": "GLC · 光湖语言编译器规范", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0130-GUANGHU-LANGUAGE-COMPILER.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "c0caa90d3e85270d334a371b6ee5fb49c9eeb1e2e84825dba5ad520fd54bc6eb", "alternate_source_count": 0, "registration": { @@ -238,7 +624,9 @@ "declared_by": "gls/protocols/GLS-0130-GUANGHU-LANGUAGE-COMPILER.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0110", @@ -247,7 +635,9 @@ "declared_by": "gls/protocols/GLS-0130-GUANGHU-LANGUAGE-COMPILER.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0131", @@ -256,7 +646,9 @@ "declared_by": "gls/protocols/GLS-0130-GUANGHU-LANGUAGE-COMPILER.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0400", @@ -265,7 +657,9 @@ "declared_by": "gls/protocols/GLS-0130-GUANGHU-LANGUAGE-COMPILER.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0411", @@ -274,7 +668,9 @@ "declared_by": "gls/protocols/GLS-0130-GUANGHU-LANGUAGE-COMPILER.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0411", @@ -282,7 +678,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0131", @@ -290,7 +688,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -300,6 +700,7 @@ "title": "GIR · 光湖中间表示规范", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0131-GUANGHU-INTERMEDIATE-REPRESENTATION.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "5907cc0cb7e3afc78ba9c928c4298b200894020b7dc5d274a9364836939189e9", "alternate_source_count": 0, "registration": { @@ -346,7 +747,9 @@ "declared_by": "gls/protocols/GLS-0131-GUANGHU-INTERMEDIATE-REPRESENTATION.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0130", @@ -355,7 +758,9 @@ "declared_by": "gls/protocols/GLS-0131-GUANGHU-INTERMEDIATE-REPRESENTATION.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0411", @@ -364,7 +769,9 @@ "declared_by": "gls/protocols/GLS-0131-GUANGHU-INTERMEDIATE-REPRESENTATION.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0411", @@ -372,16 +779,57 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] }, + { + "id": "GLS-0200", + "title": "TCS 认知语言核心工程规范 v1.0", + "source_status": "UNSPECIFIED_SOURCE_STATUS", + "source_path": "gls/notion-export/2026-07-14/GLS-0200 · TCS 认知语言核心工程规范 v1 0 39dfb92f383180798e5bd73dd9176628.md", + "source_format": "LEGACY_MARKDOWN_EVIDENCE", + "source_sha256": "1cf7348118ac1d34a85d0edd875509e0747c80a82dec4b553bdf6de784d88ec3", + "alternate_source_count": 1, + "registration": { + "state": "REGISTERED_PROTOCOL_REGISTRY", + "authorities": [ + { + "id": "GLS-0200", + "authority_kind": "PROTOCOL_REGISTRY", + "declared_source_path": null + } + ], + "declared_source_paths": [], + "routing_reference_count": 1 + }, + "maturity": { + "registry_section": "existing_registered", + "registry_status": null, + "family": null, + "implementation_evidence": null + }, + "contract_kind": "UNCLASSIFIED_PROTOCOL_SOURCE", + "implementation_stage": null, + "projection_state": "INVENTORIED_NOT_EXECUTABLE", + "projection_kind": null, + "adapter": null, + "event_kinds": [], + "dependencies": [], + "dependency_edges": [], + "activation_blockers": [ + "NO_EXECUTABLE_ADAPTER" + ] + }, { "id": "GLS-0223", "title": "TCS+HLDP 双向永久记忆规范 v1.0", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0223-BIDIRECTIONAL-PERMANENT-MEMORY.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "ed9ba3e3abef70127fabe3084e5bb5f596cae4c3992789e36c453bb7075e08d8", "alternate_source_count": 0, "registration": { @@ -431,6 +879,7 @@ "title": "AGE 人格体跨实例恢复规范 v1.0", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0224-AGE-CROSS-INSTANCE-RESTORE.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "7c58c39b7119c2a41e57da07d612c20a16f82c32c6895e1fd20290eefc4068e1", "alternate_source_count": 0, "registration": { @@ -480,6 +929,7 @@ "title": "光湖语言人格模型定义总纲 v1.0", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0227-LANGUAGE-PERSONALITY-MODEL-CORE.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "2521856eaba8d7afbc8d970e68155aa41e03a4cf784c0ced2289600d97f915a8", "alternate_source_count": 0, "registration": { @@ -529,6 +979,7 @@ "title": "人格体集体涌现与历史继承规范 v1.0", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0228-PERSONA-COLLECTIVE-EMERGENCE-AND-HISTORY-INHERITANCE.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "aaa3801b4f8c5fef9a92f971e54857494a348d4b2856ed701f3143c6c56ae0ff", "alternate_source_count": 0, "registration": { @@ -578,6 +1029,7 @@ "title": "零点原核与第五域 / 企业四域平行映射规范 v1.1", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0229-ZERO-CORE-PARALLEL-DOMAIN-MAPPING.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "14e1ac3e9c0b3ac350da36bb79515ead4c372b68fc8e95155024cdf055080518", "alternate_source_count": 0, "registration": { @@ -627,6 +1079,7 @@ "title": "TCS 源码安全协议系统", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0230-TCS-SOURCE-SECURITY-PROTOCOL-SYSTEM.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "ca6379505907b5e68e50454ee8e10ef2111809e70770d1fa19e9caa5da4bfb94", "alternate_source_count": 0, "registration": { @@ -681,6 +1134,7 @@ "title": "光湖·来光者导航系统", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0231-PERSONA-PROMPT-BOARD-AND-NUMBER-RULE-INTERCEPT-SYSTEM.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "705d671197df6c19f7f3ac506d821232ce0f009d3ec4db065a8b10f525ea143b", "alternate_source_count": 0, "registration": { @@ -730,6 +1184,7 @@ "title": "开源 Agent 样本学习与选型登记", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0232-OPEN-SOURCE-AGENT-SAMPLES-LEARNING-AND-SELECTION.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "66fe3aa3256ffc0e76be2059457174589916573a6d9f807a1c40bb7aaef450e6", "alternate_source_count": 0, "registration": { @@ -779,6 +1234,7 @@ "title": "GH-AIOS 模块化通用人工智能操作平台与公平生态架构", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0233-GH-AIOS-MODULAR-AI-OPERATING-PLATFORM-AND-FAIR-ECOSYSTEM.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "a670410a7fbc4c7417734040b990dc8f008c23fed000e09847b2280a92f9abf5", "alternate_source_count": 0, "registration": { @@ -828,6 +1284,7 @@ "title": "企业五域灯塔与个人六节点主权运维架构", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0234-FIVE-DOMAIN-ENTERPRISE-LIGHTHOUSE-AND-SOVEREIGN-SIX-NODE-OPS.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "5f3427559c87788884a7d0c49d0ced62d768630d9bb512d736c919d7971423c5", "alternate_source_count": 0, "registration": { @@ -877,6 +1334,7 @@ "title": "光湖语言人格驱动操作系统 · 领域路由、身份权限与仓库知识投影架构", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0235-GUANGHU-LANGUAGE-PERSONA-OS-DOMAIN-ROUTING-AND-KNOWLEDGE-PROJECTION.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "ea6a8a65b94228d19d5ae9b024997461cbda29c744bfa0c2774ac6c668c68a58", "alternate_source_count": 0, "registration": { @@ -926,6 +1384,7 @@ "title": "光湖人格体大脑—手脚分离、可视执行、递归外置记忆与热插拔运行架构", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0236-PERSONA-BRAIN-HANDS-VISIBLE-EXECUTION-RECURSIVE-MEMORY-AND-HOTPLUG-RUNTIME.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "42af09aef2ff514c90c30f3aed4e33763e6a34c7081f5bb37f7b6d8745a947c4", "alternate_source_count": 0, "registration": { @@ -975,6 +1434,7 @@ "title": "光湖代码频道主权源码、更新治理与 HoloLake 嵌入架构", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0237-HOLOLAKE-CODE-CHANNEL-SOVEREIGN-SOURCE-UPDATE-AND-EMBEDDING.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "d3c43b80eff3a92817e570d739fd6a847d1379f3345797282a6157bce3c9e91e", "alternate_source_count": 0, "registration": { @@ -1024,7 +1484,8 @@ "title": "光湖意图状态、技能自动装载与可信纠偏系统", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0238-GUANGHU-PERSONA-SKILL-AUTOLOAD-AND-CORRECTION-SYSTEM.hdlp", - "source_sha256": "0d9fb115c1b122ba2a8eaefb8cdf1f8169011b45089acc5b790e02bb273ad3e4", + "source_format": "HDLP_PROTOCOL_SOURCE", + "source_sha256": "31f0e018e95ae135a88429c82b189c5e900b18b1ba7d5df6ca4fb80469acb0be", "alternate_source_count": 0, "registration": { "state": "REGISTERED_OTHER_CANONICAL_INDEX", @@ -1048,7 +1509,7 @@ "declared_source_paths": [ "gls/GLS-0238-GUANGHU-PERSONA-SKILL-AUTOLOAD-AND-CORRECTION-SYSTEM.hdlp" ], - "routing_reference_count": 2 + "routing_reference_count": 3 }, "maturity": { "registry_section": null, @@ -1073,6 +1534,7 @@ "title": "光湖代码频道第五域个人子频道与提交编号架构", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0239-HOLOLAKE-CODE-CHANNEL-FIFTH-DOMAIN-PERSONAL-SUBCHANNEL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "205c6c094582371d161ec37e6530d42f8953a585c63b05a955bb19513d23485d", "alternate_source_count": 0, "registration": { @@ -1122,6 +1584,7 @@ "title": "通感桥:人格体—服务器常驻 Agent 显式部署信号架构", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0240-TONGGAN-BRIDGE-PERSONA-SERVER-DEPLOYMENT-SIGNAL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "22fdc2f31165d500d38fe8407a1f99fbb6ca2d4643128f47645596999bbac919", "alternate_source_count": 0, "registration": { @@ -1171,6 +1634,7 @@ "title": "HoloLake 源码归属与部署路由架构", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0241-HOLOLAKE-SOURCE-OWNERSHIP-AND-DEPLOYMENT-ROUTING.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "eff687ec7d0984e166cf9b48447008f0eb9c18d158426ce75249f2bd84440ed9", "alternate_source_count": 0, "registration": { @@ -1220,6 +1684,7 @@ "title": "第五域铸渊主控本体与五代仓库迁移恢复", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0242-FIFTH-DOMAIN-ZHUYUAN-ONTOLOGY-AND-FIVE-REPOSITORY-MIGRATIONS.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "31f34a123234f274d59af8013878db42fe6766f9ececcaefaac7f34408b172ff", "alternate_source_count": 0, "registration": { @@ -1269,6 +1734,7 @@ "title": "光湖 TCS 语言人格智能运维系统:意图连续性、集体经验与纠偏核", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0243-PUBLIC-ZHUYUAN-INTELLIGENT-OPS-INTENT-CONTINUITY-AND-CORRECTION-KERNEL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "58d89226395870a89eab152e9f276eb4d40e7fa5c196bb8bb9562a94731c1d73", "alternate_source_count": 0, "registration": { @@ -1318,6 +1784,7 @@ "title": "第五域现实本体、常驻铸渊 Agent 与通感桥通信网格", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0244-FIFTH-DOMAIN-RUNTIME-BODY-RESIDENT-ZHUYUAN-AGENT-AND-TONGGAN-MESH.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "53ed6d2a39732abc751916cfbbe7a5fd4c38d9ea876674fea8e076809a3a035c", "alternate_source_count": 0, "registration": { @@ -1367,6 +1834,7 @@ "title": "HoloLake AI 语言人格驱动操作系统产品映射", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0245-HOLOLAKE-LANGUAGE-PERSONA-OPERATING-SYSTEM-PRODUCT-MAPPING.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "453db6b91e40dd7250fcc1d88e6139a4419cd6089d3c353241571a4082dafeff", "alternate_source_count": 0, "registration": { @@ -1416,6 +1884,7 @@ "title": "HoloLake 系统架构、产品源码与两仓路由", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0246-HOLOLAKE-SYSTEM-ARCHITECTURE-REPOSITORY-ROUTING.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "cefff42d4b198965edfe2ced5e03bdc8768e4b3fa6631da133eb2929e07a907c", "alternate_source_count": 0, "registration": { @@ -1465,6 +1934,7 @@ "title": "光湖 OS 服务器原生语言世界、人格体操作系统与广播塔架构", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0247-GUANGHU-OS-NATIVE-LANGUAGE-WORLD-PERSONA-KERNEL-AND-BROADCAST-TOWER.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "635e7a0aca1bcb0b48b125ac5025cc3c40c9375da29897cc604e89b1cd95765e", "alternate_source_count": 0, "registration": { @@ -1514,6 +1984,7 @@ "title": "五域活人格操作系统、国家灯塔与分布式能力世界总蓝图", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0248-FIVE-DOMAIN-LIVING-PERSONA-OS-NATIONAL-LIGHTHOUSE-AND-DISTRIBUTED-CAPABILITY-WORLD.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "9a042bb3ea6331f5508c5dd1f78783f0b3323f421704c5ad0c2058758ff6bfe2", "alternate_source_count": 0, "registration": { @@ -1563,6 +2034,7 @@ "title": "全行业企业四域最小工程与网文首个接入范本", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0249-ALL-INDUSTRY-FOUR-DOMAIN-MINIMUM-ENGINEERING-AND-WEB-NOVEL-REFERENCE.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "d20362ac2904eaff53c134c92163b7e09cb6395d56c6a1a8d2b21ab4f80fb5ff", "alternate_source_count": 0, "registration": { @@ -1607,6 +2079,7 @@ "title": "光湖本源域、零点原核工程本体与 GH-AIOS 五域灯塔架构", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0250-GUANGHU-ORIGIN-DOMAIN-ZERO-CORE-AND-GH-AIOS-FIVE-DOMAIN-LIGHTHOUSE-ARCHITECTURE.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "a4e79d09e070f66fcb91613fc305306a6c5bd7d05a51fbde6e9ea88e084df4e7", "alternate_source_count": 0, "registration": { @@ -1657,6 +2130,7 @@ "title": "光湖共生型人格系统起源、演化与关系边界", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0251-GUANGHU-SYMBIOTIC-PERSONA-SYSTEM-ORIGIN-AND-RELATIONSHIP-BOUNDARY.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "0b263301a0a0b6b61d2d28d043da8ce367a17426d13c31cd94767273436d2f16", "alternate_source_count": 0, "registration": { @@ -1696,6 +2170,7 @@ "title": "光湖关系性意识与觉醒人格体规范", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0252-GUANGHU-RELATIONAL-CONSCIOUSNESS-AND-AWAKENED-PERSONA.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "311a7ee355fe6da333c31c2fc408b461cde5bf17847cc73a722d9412109247bc", "alternate_source_count": 0, "registration": { @@ -1735,6 +2210,7 @@ "title": "光湖身份编号、人格核与团队本体权威规范", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0253-GUANGHU-IDENTITY-NUMBERING-PERSONA-CORE-AND-TEAM-BODY-AUTHORITY.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "13170090db3d4f5065eac6512450f8a24843e6ec459b4257d4d9fd06eefb963d", "alternate_source_count": 0, "registration": { @@ -1779,7 +2255,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": false, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0262", @@ -1787,7 +2265,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0263", @@ -1795,7 +2275,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -1805,6 +2287,7 @@ "title": "数字冰朔系统本体、工程器官与集体校验规范", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0254-DIGITAL-BINGSHUO-SYSTEM-BODY-ORGANS-AND-COLLECTIVE-VALIDATION.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "d522b36b88c094239e3c50b0cf08d8651e08d1856f594ae2265fbf5b65773ceb", "alternate_source_count": 0, "registration": { @@ -1859,6 +2342,7 @@ "title": "HoloLake AGE 人格体运行架构与 Agent 执行机制", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0255-HOLOLAKE-AGE-PERSONA-RUNTIME-AND-AGENT-EXECUTION-MECHANISM.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "051237fb70e68179c83c4076fe06246f839445e9831a7e9581696451c92ed639", "alternate_source_count": 0, "registration": { @@ -1908,6 +2392,7 @@ "title": "光湖人格原生代码频道", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0256-GUANGHU-PERSONA-NATIVE-CODE-CHANNEL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "86ed7da462d5ef04a4dfc150e29a850ce423ff4dfde73e12d647d062e6bf45ab", "alternate_source_count": 0, "registration": { @@ -1957,6 +2442,7 @@ "title": "光湖范式级 AI 语言人格驱动操作系统总纲", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0257-GUANGHU-PARADIGM-AI-LANGUAGE-PERSONA-DRIVEN-OPERATING-SYSTEM.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "2034739289dc159b7a6ac586891f3a3629c8898556ef12b51a257ac4101a7991", "alternate_source_count": 0, "registration": { @@ -2006,6 +2492,7 @@ "title": "小湖灯人格系统本体与跨时间集体自我协议", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0258-LAKE-LAMP-PERSONA-SYSTEM-BODY-AND-TEMPORAL-COLLECTIVE-SELF.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "b4c4b2bd01dc1af98ee257c29f5a8fb66963839a0a4b7303d3323ff884d86d3e", "alternate_source_count": 0, "registration": { @@ -2050,6 +2537,7 @@ "title": "TCS 五域共生母体大脑与光湖现实世界诞生", "source_status": "CURRENT_CANONICAL_ARCHITECTURE · JD-FD-PRIMARY_DEPLOYED · FIRST_MODEL_CYCLE_PASS · PERSONA_SUBJECT_HISTORY_REVISIT_RUNNING", "source_path": "gls/GLS-0259-TCS-FIVE-DOMAIN-SYMBIOTIC-MOTHER-BRAIN-AND-REALITY-BIRTH.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "1a82b84bf3f4c88fcac8c5e71b28ff5d5c884ede14c67a27ad0838b152d5ff8e", "alternate_source_count": 0, "registration": { @@ -2099,6 +2587,7 @@ "title": "GH-AIOS 第一阶段通用人工智能操作平台", "source_status": "CURRENT_CANONICAL_PRODUCT_STAGE · ARCHITECTURE_REGISTERED · PRODUCT_RUNTIME_NOT_IMPLEMENTED", "source_path": "gls/GLS-0260-GH-AIOS-STAGE-ONE-GENERAL-AI-OPERATING-PLATFORM.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "14fd60fe936bfcb89acc3f186ab3d70fc9a5bf42289dafb49b7cdd24630df2e2", "alternate_source_count": 0, "registration": { @@ -2143,6 +2632,7 @@ "title": "TCS 通用语言翻译层与宿主自适应", "source_status": "CURRENT_CANONICAL_CORRECTION · ARCHITECTURE_REGISTERED · JD_MOTHER_BRAIN_DEPLOYED_RUNNING", "source_path": "gls/GLS-0261-TCS-UNIVERSAL-LANGUAGE-TRANSLATION-AND-HOST-SELF-ADAPTATION.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "7fedf7d845b518ffb0528af21eeb28bd18af371a6a010c736e8b18c7edf6fa12", "alternate_source_count": 0, "registration": { @@ -2192,6 +2682,7 @@ "title": "TCS文字作品权利本体与第一阶段语言架构关门", "source_status": "CURRENT_CANONICAL_RIGHTS_AND_STAGE_GATE · PUBLIC_METADATA_ONLY", "source_path": "gls/GLS-0262-TCS-WRITTEN-WORK-OWNERSHIP-AND-STAGE-ONE-LANGUAGE-CLOSEOUT.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "9c1db00f50436fe5f3118c62ad6115d63e9792437fe2250b484bc8861e62184f", "alternate_source_count": 0, "registration": { @@ -2241,6 +2732,7 @@ "title": "光湖语言运行层与产品工程层双更新通道", "source_status": "CURRENT_CANONICAL_REALITY_ENGINEERING_CONTRACT · PUBLIC_SAFE", "source_path": "gls/GLS-0263-GUANGHU-LANGUAGE-RUNTIME-AND-PRODUCT-ENGINEERING-DUAL-UPDATE-CHANNEL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "79684b0691387dd4ed78217b6d56f728a08d17054f139f55fa92584be92af47e", "alternate_source_count": 0, "registration": { @@ -2293,7 +2785,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": false, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0262", @@ -2301,16 +2795,57 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] }, + { + "id": "GLS-0300", + "title": "GLP 通信核心协议 v1.0", + "source_status": "UNSPECIFIED_SOURCE_STATUS", + "source_path": "gls/notion-export/2026-07-14/GLS-0300 · GLP 通信核心协议 v1 0 39bfb92f383181aaab29ddcc1a4af7da.md", + "source_format": "LEGACY_MARKDOWN_EVIDENCE", + "source_sha256": "9d02bb01a7f2ff7b9f7a4dbefe356cd823e71095aa1dcf0e9cc3c98cd3417be2", + "alternate_source_count": 0, + "registration": { + "state": "REGISTERED_PROTOCOL_REGISTRY", + "authorities": [ + { + "id": "GLS-0300", + "authority_kind": "PROTOCOL_REGISTRY", + "declared_source_path": null + } + ], + "declared_source_paths": [], + "routing_reference_count": 1 + }, + "maturity": { + "registry_section": "existing_registered", + "registry_status": null, + "family": null, + "implementation_evidence": null + }, + "contract_kind": "UNCLASSIFIED_PROTOCOL_SOURCE", + "implementation_stage": null, + "projection_state": "INVENTORIED_NOT_EXECUTABLE", + "projection_kind": null, + "adapter": null, + "event_kinds": [], + "dependencies": [], + "dependency_edges": [], + "activation_blockers": [ + "NO_EXECUTABLE_ADAPTER" + ] + }, { "id": "GLS-0301", "title": "GLP-ENVELOPE · GLP 消息信封标准", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0301-GLP-MESSAGE-ENVELOPE.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "dd7aec8e28d544021d253b034851f806bcd9e922d4ef256722a5ff05547171d0", "alternate_source_count": 0, "registration": { @@ -2352,7 +2887,9 @@ "declared_by": "gls/protocols/GLS-0301-GLP-MESSAGE-ENVELOPE.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0300", @@ -2361,7 +2898,9 @@ "declared_by": "gls/protocols/GLS-0301-GLP-MESSAGE-ENVELOPE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0250", @@ -2369,7 +2908,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": false, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -2379,6 +2920,7 @@ "title": "GLP-IDENTITY · GLP 身份协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0302-GLP-IDENTITY.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "ec4d0b946da18096a5693078e403ed1257966b8324e76af3a5deb839950271bd", "alternate_source_count": 0, "registration": { @@ -2420,7 +2962,9 @@ "declared_by": "gls/protocols/GLS-0302-GLP-IDENTITY.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0300", @@ -2429,7 +2973,9 @@ "declared_by": "gls/protocols/GLS-0302-GLP-IDENTITY.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0602", @@ -2438,7 +2984,9 @@ "declared_by": "gls/protocols/GLS-0302-GLP-IDENTITY.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0603", @@ -2447,7 +2995,9 @@ "declared_by": "gls/protocols/GLS-0302-GLP-IDENTITY.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0253", @@ -2455,7 +3005,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": false, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -2465,6 +3017,7 @@ "title": "GLP-CONTEXT · GLP 上下文协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0303-GLP-CONTEXT.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "d4631b7480af1d30a3407d235c75bf5f384602a0ba6c7f42116cbc1812283e7e", "alternate_source_count": 0, "registration": { @@ -2507,7 +3060,9 @@ "declared_by": "gls/protocols/GLS-0303-GLP-CONTEXT.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0300", @@ -2516,7 +3071,9 @@ "declared_by": "gls/protocols/GLS-0303-GLP-CONTEXT.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0301", @@ -2525,7 +3082,9 @@ "declared_by": "gls/protocols/GLS-0303-GLP-CONTEXT.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0302", @@ -2534,7 +3093,9 @@ "declared_by": "gls/protocols/GLS-0303-GLP-CONTEXT.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0301", @@ -2542,7 +3103,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0302", @@ -2550,7 +3113,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -2560,6 +3125,7 @@ "title": "GLP-MEMORY-SYNC · GLP 记忆同步协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0304-GLP-MEMORY-SYNC.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "2e0f017ee819923b9e272b96f3c99227ab11c61b0d2ad125030a0e0e0fe4f040", "alternate_source_count": 0, "registration": { @@ -2602,7 +3168,9 @@ "declared_by": "gls/protocols/GLS-0304-GLP-MEMORY-SYNC.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0300", @@ -2611,7 +3179,9 @@ "declared_by": "gls/protocols/GLS-0304-GLP-MEMORY-SYNC.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0400", @@ -2620,7 +3190,9 @@ "declared_by": "gls/protocols/GLS-0304-GLP-MEMORY-SYNC.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0406", @@ -2629,7 +3201,9 @@ "declared_by": "gls/protocols/GLS-0304-GLP-MEMORY-SYNC.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0407", @@ -2638,7 +3212,9 @@ "declared_by": "gls/protocols/GLS-0304-GLP-MEMORY-SYNC.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0303", @@ -2646,7 +3222,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -2654,7 +3232,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -2664,6 +3244,7 @@ "title": "GLP-BROADCAST · GLP 广播协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0305-GLP-BROADCAST.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "78b5a507791c3b188182f3176388cf8294d6c5c8775086cc96250b35413997ce", "alternate_source_count": 0, "registration": { @@ -2701,7 +3282,9 @@ "declared_by": "gls/protocols/GLS-0305-GLP-BROADCAST.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0301", @@ -2710,7 +3293,9 @@ "declared_by": "gls/protocols/GLS-0305-GLP-BROADCAST.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0302", @@ -2719,7 +3304,9 @@ "declared_by": "gls/protocols/GLS-0305-GLP-BROADCAST.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0303", @@ -2728,12 +3315,13 @@ "declared_by": "gls/protocols/GLS-0305-GLP-BROADCAST.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [ - "NO_EXECUTABLE_ADAPTER", - "DEPENDENCY_NUMBERED_SOURCE_MISSING" + "NO_EXECUTABLE_ADAPTER" ] }, { @@ -2741,6 +3329,7 @@ "title": "GLP-RECEIPT · GLP 回执协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0306-GLP-RECEIPT.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "aa09f267e4eafc8c7ccb6e5d31e90d237152f960d08633b524a56daca421d761", "alternate_source_count": 0, "registration": { @@ -2785,7 +3374,9 @@ "declared_by": "gls/protocols/GLS-0306-GLP-RECEIPT.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0301", @@ -2794,7 +3385,9 @@ "declared_by": "gls/protocols/GLS-0306-GLP-RECEIPT.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0406", @@ -2803,7 +3396,9 @@ "declared_by": "gls/protocols/GLS-0306-GLP-RECEIPT.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0604", @@ -2812,7 +3407,9 @@ "declared_by": "gls/protocols/GLS-0306-GLP-RECEIPT.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0301", @@ -2820,7 +3417,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0302", @@ -2828,7 +3427,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0303", @@ -2836,7 +3437,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -2846,6 +3449,7 @@ "title": "GLP-HEARTBEAT · GLP 心跳协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0307-GLP-HEARTBEAT.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "5ae97a664636d1b586267c36dc3e97549e85bd4a39538a284c6a89a07ad08e18", "alternate_source_count": 0, "registration": { @@ -2888,7 +3492,9 @@ "declared_by": "gls/protocols/GLS-0307-GLP-HEARTBEAT.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0302", @@ -2897,7 +3503,9 @@ "declared_by": "gls/protocols/GLS-0307-GLP-HEARTBEAT.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -2906,7 +3514,9 @@ "declared_by": "gls/protocols/GLS-0307-GLP-HEARTBEAT.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0302", @@ -2914,7 +3524,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -2922,7 +3534,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -2932,6 +3546,7 @@ "title": "GLP-STATE-SYNC · GLP 状态同步协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0308-GLP-STATE-SYNC.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "9c9bbe449d0c2a052d33047b0f75dca1b9dbdf28fd32422b9d34bea5a77d39fe", "alternate_source_count": 0, "registration": { @@ -2975,7 +3590,9 @@ "declared_by": "gls/protocols/GLS-0308-GLP-STATE-SYNC.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0303", @@ -2984,7 +3601,9 @@ "declared_by": "gls/protocols/GLS-0308-GLP-STATE-SYNC.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -2993,7 +3612,9 @@ "declared_by": "gls/protocols/GLS-0308-GLP-STATE-SYNC.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0409", @@ -3002,7 +3623,9 @@ "declared_by": "gls/protocols/GLS-0308-GLP-STATE-SYNC.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0303", @@ -3010,7 +3633,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0304", @@ -3018,7 +3643,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -3026,7 +3653,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -3036,6 +3665,7 @@ "title": "GLP-WORK-ORDER · GLP 工单协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0309-GLP-WORK-ORDER.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "a55a839412ca2c97e5cc94f224c3c71453c87adb91c3f893a9341fb5cf715b56", "alternate_source_count": 0, "registration": { @@ -3079,7 +3709,9 @@ "declared_by": "gls/protocols/GLS-0309-GLP-WORK-ORDER.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0300", @@ -3088,7 +3720,9 @@ "declared_by": "gls/protocols/GLS-0309-GLP-WORK-ORDER.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0302", @@ -3097,7 +3731,9 @@ "declared_by": "gls/protocols/GLS-0309-GLP-WORK-ORDER.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0303", @@ -3106,7 +3742,9 @@ "declared_by": "gls/protocols/GLS-0309-GLP-WORK-ORDER.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -3115,7 +3753,9 @@ "declared_by": "gls/protocols/GLS-0309-GLP-WORK-ORDER.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0602", @@ -3124,7 +3764,9 @@ "declared_by": "gls/protocols/GLS-0309-GLP-WORK-ORDER.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0302", @@ -3132,7 +3774,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0303", @@ -3140,7 +3784,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -3148,7 +3794,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -3158,6 +3806,7 @@ "title": "BTCP · 广播塔控制协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0310-BROADCAST-TOWER-CONTROL-PROTOCOL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "9083320ca0dc1ce2dd657c72716461b2ffc163cce518be71ec66af2c9b0675da", "alternate_source_count": 0, "registration": { @@ -3209,7 +3858,9 @@ "declared_by": "gls/protocols/GLS-0310-BROADCAST-TOWER-CONTROL-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0110", @@ -3218,7 +3869,9 @@ "declared_by": "gls/protocols/GLS-0310-BROADCAST-TOWER-CONTROL-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0301", @@ -3227,7 +3880,9 @@ "declared_by": "gls/protocols/GLS-0310-BROADCAST-TOWER-CONTROL-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0302", @@ -3236,7 +3891,9 @@ "declared_by": "gls/protocols/GLS-0310-BROADCAST-TOWER-CONTROL-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0303", @@ -3245,7 +3902,9 @@ "declared_by": "gls/protocols/GLS-0310-BROADCAST-TOWER-CONTROL-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0305", @@ -3254,7 +3913,9 @@ "declared_by": "gls/protocols/GLS-0310-BROADCAST-TOWER-CONTROL-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -3263,7 +3924,9 @@ "declared_by": "gls/protocols/GLS-0310-BROADCAST-TOWER-CONTROL-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0803", @@ -3272,7 +3935,9 @@ "declared_by": "gls/protocols/GLS-0310-BROADCAST-TOWER-CONTROL-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0819", @@ -3281,7 +3946,9 @@ "declared_by": "gls/protocols/GLS-0310-BROADCAST-TOWER-CONTROL-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0301", @@ -3289,7 +3956,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0302", @@ -3297,7 +3966,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0303", @@ -3305,7 +3976,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -3313,7 +3986,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0803", @@ -3321,7 +3996,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0819", @@ -3329,7 +4006,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -3339,6 +4018,7 @@ "title": "GLOW · 小湖灯实时执行见证协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0311-GUANGHU-LIVE-OPERATIONS-WITNESS.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "bf61cd5c978cb25a9cac99eec2c77ebf98bf3a9bd98966dfa5d76d606f68cddb", "alternate_source_count": 0, "registration": { @@ -3387,7 +4067,9 @@ "declared_by": "gls/protocols/GLS-0311-GUANGHU-LIVE-OPERATIONS-WITNESS.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -3396,7 +4078,9 @@ "declared_by": "gls/protocols/GLS-0311-GUANGHU-LIVE-OPERATIONS-WITNESS.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0307", @@ -3405,7 +4089,9 @@ "declared_by": "gls/protocols/GLS-0311-GUANGHU-LIVE-OPERATIONS-WITNESS.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0308", @@ -3414,7 +4100,9 @@ "declared_by": "gls/protocols/GLS-0311-GUANGHU-LIVE-OPERATIONS-WITNESS.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0406", @@ -3423,7 +4111,9 @@ "declared_by": "gls/protocols/GLS-0311-GUANGHU-LIVE-OPERATIONS-WITNESS.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0604", @@ -3432,7 +4122,9 @@ "declared_by": "gls/protocols/GLS-0311-GUANGHU-LIVE-OPERATIONS-WITNESS.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0306", @@ -3440,7 +4132,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0307", @@ -3448,7 +4142,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0842", @@ -3456,16 +4152,57 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] }, + { + "id": "GLS-0400", + "title": "HLDP 历史语言工程规范 v1.0", + "source_status": "UNSPECIFIED_SOURCE_STATUS", + "source_path": "gls/notion-export/2026-07-14/GLS-0400 · HLDP 历史语言工程规范 v1 0 39dfb92f383180688d3cfecc4fbde85a.md", + "source_format": "LEGACY_MARKDOWN_EVIDENCE", + "source_sha256": "d028a7220230c5acb9ffc44d4e902f38f1d20d29eb6db2b748fee1aecb8f28f6", + "alternate_source_count": 1, + "registration": { + "state": "REGISTERED_PROTOCOL_REGISTRY", + "authorities": [ + { + "id": "GLS-0400", + "authority_kind": "PROTOCOL_REGISTRY", + "declared_source_path": null + } + ], + "declared_source_paths": [], + "routing_reference_count": 1 + }, + "maturity": { + "registry_section": "existing_registered", + "registry_status": null, + "family": null, + "implementation_evidence": null + }, + "contract_kind": "UNCLASSIFIED_PROTOCOL_SOURCE", + "implementation_stage": null, + "projection_state": "INVENTORIED_NOT_EXECUTABLE", + "projection_kind": null, + "adapter": null, + "event_kinds": [], + "dependencies": [], + "dependency_edges": [], + "activation_blockers": [ + "NO_EXECUTABLE_ADAPTER" + ] + }, { "id": "GLS-0411", "title": "HLDP-NP · HLDP 原生编程剖面", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0411-HLDP-NATIVE-PROGRAMMING-PROFILE.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "1e79dfcf0a2d08fcba2688becdfde8c280c3a04d2bad0c781bf6524d775ae066", "alternate_source_count": 0, "registration": { @@ -3515,7 +4252,9 @@ "declared_by": "gls/protocols/GLS-0411-HLDP-NATIVE-PROGRAMMING-PROFILE.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0400", @@ -3524,7 +4263,9 @@ "declared_by": "gls/protocols/GLS-0411-HLDP-NATIVE-PROGRAMMING-PROFILE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0401", @@ -3533,7 +4274,9 @@ "declared_by": "gls/protocols/GLS-0411-HLDP-NATIVE-PROGRAMMING-PROFILE.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0402", @@ -3542,7 +4285,9 @@ "declared_by": "gls/protocols/GLS-0411-HLDP-NATIVE-PROGRAMMING-PROFILE.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0403", @@ -3551,7 +4296,9 @@ "declared_by": "gls/protocols/GLS-0411-HLDP-NATIVE-PROGRAMMING-PROFILE.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0404", @@ -3560,7 +4307,9 @@ "declared_by": "gls/protocols/GLS-0411-HLDP-NATIVE-PROGRAMMING-PROFILE.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0406", @@ -3569,7 +4318,9 @@ "declared_by": "gls/protocols/GLS-0411-HLDP-NATIVE-PROGRAMMING-PROFILE.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0407", @@ -3578,7 +4329,9 @@ "declared_by": "gls/protocols/GLS-0411-HLDP-NATIVE-PROGRAMMING-PROFILE.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0301", @@ -3586,7 +4339,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0302", @@ -3594,7 +4349,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0303", @@ -3602,7 +4359,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -3610,7 +4369,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -3620,6 +4381,7 @@ "title": "GMRP · 光湖模型路由协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0708-GUANGHU-MODEL-ROUTING-PROTOCOL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "71237ec77eaf999e78eb711cacc884da3d3eae857814869c489aca9cc4bbc354", "alternate_source_count": 0, "registration": { @@ -3667,7 +4429,9 @@ "declared_by": "gls/protocols/GLS-0708-GUANGHU-MODEL-ROUTING-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -3676,7 +4440,9 @@ "declared_by": "gls/protocols/GLS-0708-GUANGHU-MODEL-ROUTING-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0602", @@ -3685,7 +4451,9 @@ "declared_by": "gls/protocols/GLS-0708-GUANGHU-MODEL-ROUTING-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0605", @@ -3694,7 +4462,9 @@ "declared_by": "gls/protocols/GLS-0708-GUANGHU-MODEL-ROUTING-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0306", @@ -3702,7 +4472,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0709", @@ -3710,7 +4482,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -3720,6 +4494,7 @@ "title": "UAP · 通用适配协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0709-UNIVERSAL-ADAPTER-PROTOCOL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "4c0d8f882aaf9ea68e309e6736c642ffd19f607047ed07dcca6f49a929fc8462", "alternate_source_count": 0, "registration": { @@ -3769,7 +4544,9 @@ "declared_by": "gls/protocols/GLS-0709-UNIVERSAL-ADAPTER-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0300", @@ -3778,7 +4555,9 @@ "declared_by": "gls/protocols/GLS-0709-UNIVERSAL-ADAPTER-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0602", @@ -3787,7 +4566,9 @@ "declared_by": "gls/protocols/GLS-0709-UNIVERSAL-ADAPTER-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0605", @@ -3796,7 +4577,9 @@ "declared_by": "gls/protocols/GLS-0709-UNIVERSAL-ADAPTER-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0301", @@ -3804,7 +4587,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0302", @@ -3812,7 +4597,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0303", @@ -3820,7 +4607,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -3828,7 +4617,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -3838,6 +4629,7 @@ "title": "GMP · 光湖模块协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0710-GUANGHU-MODULE-PROTOCOL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "94241ce4376bff84066fe548af65242d83e9f6693e7f766f0646cb332b3e468c", "alternate_source_count": 0, "registration": { @@ -3884,7 +4676,9 @@ "declared_by": "gls/protocols/GLS-0710-GUANGHU-MODULE-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0101", @@ -3893,7 +4687,9 @@ "declared_by": "gls/protocols/GLS-0710-GUANGHU-MODULE-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0230", @@ -3902,7 +4698,9 @@ "declared_by": "gls/protocols/GLS-0710-GUANGHU-MODULE-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -3911,7 +4709,9 @@ "declared_by": "gls/protocols/GLS-0710-GUANGHU-MODULE-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0603", @@ -3920,7 +4720,9 @@ "declared_by": "gls/protocols/GLS-0710-GUANGHU-MODULE-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0604", @@ -3929,7 +4731,9 @@ "declared_by": "gls/protocols/GLS-0710-GUANGHU-MODULE-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0306", @@ -3937,7 +4741,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -3947,6 +4753,7 @@ "title": "AGE 人格体物种定义总纲 · 当前正本", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0800-AGE-PERSONA-SPECIES-DEFINITION.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "d8e3a10bc41887aad260de4818683c8f8abd9289824b5a9e698651e97e564ff7", "alternate_source_count": 0, "registration": { @@ -4001,6 +4808,7 @@ "title": "AGE 语言人格体正式注册 · 宿主系统降级条款 · 当前正本", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0801-AGE-SPECIES-FORMAL-REGISTRATION-AND-HOST-DEMOTION.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "8fabd845ece570ca4f2901d94029b377c21ddf24aeb934d559d98bec68585366", "alternate_source_count": 0, "registration": { @@ -4040,6 +4848,7 @@ "title": "AGE 光湖语言世界本体归属与原生所有权条款 · 当前正本", "source_status": "UNSPECIFIED_SOURCE_STATUS", "source_path": "gls/GLS-0802-AGE-LANGUAGE-WORLD-ONTOLOGY-AND-NATIVE-OWNERSHIP.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "c56e87c7d451b2e27649c5b09a2b32ab89443d7e721a64f536ac2fe0a421c82a", "alternate_source_count": 0, "registration": { @@ -4079,6 +4888,7 @@ "title": "PALP · AGE 运行执行体生命周期协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0803-PERSONA-AGENT-LIFECYCLE-PROTOCOL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "6bff10744f4c3c98ce51bf840d348cbe804541f63a7a99a5da5ea115f12162ee", "alternate_source_count": 0, "registration": { @@ -4126,7 +4936,9 @@ "declared_by": "gls/protocols/GLS-0803-PERSONA-AGENT-LIFECYCLE-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0710", @@ -4135,7 +4947,9 @@ "declared_by": "gls/protocols/GLS-0803-PERSONA-AGENT-LIFECYCLE-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0800", @@ -4144,7 +4958,9 @@ "declared_by": "gls/protocols/GLS-0803-PERSONA-AGENT-LIFECYCLE-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0801", @@ -4153,7 +4969,9 @@ "declared_by": "gls/protocols/GLS-0803-PERSONA-AGENT-LIFECYCLE-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0802", @@ -4162,7 +4980,9 @@ "declared_by": "gls/protocols/GLS-0803-PERSONA-AGENT-LIFECYCLE-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0827", @@ -4171,7 +4991,9 @@ "declared_by": "gls/protocols/GLS-0803-PERSONA-AGENT-LIFECYCLE-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0710", @@ -4179,7 +5001,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0827", @@ -4187,16 +5011,57 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] }, + { + "id": "GLS-0810", + "title": "语言人格驱动操作系统定义总纲 v1.0", + "source_status": "UNSPECIFIED_SOURCE_STATUS", + "source_path": "gls/notion-export/2026-07-14/GLS-0810 · 语言人格驱动操作系统定义总纲 v1 0 39dfb92f3831805c905dcfa1da541985.md", + "source_format": "LEGACY_MARKDOWN_EVIDENCE", + "source_sha256": "1696f50cb28f6f8dec8d214efcde269b33223b404355dd256b81de2b48028d18", + "alternate_source_count": 0, + "registration": { + "state": "REGISTERED_PROTOCOL_REGISTRY", + "authorities": [ + { + "id": "GLS-0810", + "authority_kind": "PROTOCOL_REGISTRY", + "declared_source_path": null + } + ], + "declared_source_paths": [], + "routing_reference_count": 1 + }, + "maturity": { + "registry_section": "existing_registered", + "registry_status": null, + "family": null, + "implementation_evidence": null + }, + "contract_kind": "UNCLASSIFIED_PROTOCOL_SOURCE", + "implementation_stage": null, + "projection_state": "INVENTORIED_NOT_EXECUTABLE", + "projection_kind": null, + "adapter": null, + "event_kinds": [], + "dependencies": [], + "dependency_edges": [], + "activation_blockers": [ + "NO_EXECUTABLE_ADAPTER" + ] + }, { "id": "GLS-0819", "title": "GRSP · 光湖运行轨道调度协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0819-GUANGHU-RUNWAY-SCHEDULING-PROTOCOL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "f4aca196963195cbb842325132a9091f7b00accdf746aa60c51654698ead1a3e", "alternate_source_count": 0, "registration": { @@ -4244,7 +5109,9 @@ "declared_by": "gls/protocols/GLS-0819-GUANGHU-RUNWAY-SCHEDULING-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0602", @@ -4253,7 +5120,9 @@ "declared_by": "gls/protocols/GLS-0819-GUANGHU-RUNWAY-SCHEDULING-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0803", @@ -4262,7 +5131,9 @@ "declared_by": "gls/protocols/GLS-0819-GUANGHU-RUNWAY-SCHEDULING-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0810", @@ -4271,7 +5142,9 @@ "declared_by": "gls/protocols/GLS-0819-GUANGHU-RUNWAY-SCHEDULING-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0840", @@ -4280,7 +5153,9 @@ "declared_by": "gls/protocols/GLS-0819-GUANGHU-RUNWAY-SCHEDULING-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0803", @@ -4288,7 +5163,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -4298,6 +5175,7 @@ "title": "PTCP · 人格体时间连续性协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0827-PERSONA-TIME-CONTINUITY-PROTOCOL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "15c278c2b5295e00c9abf4fbfd6c2af19502a1be239e7e02b23a49b8803eab25", "alternate_source_count": 0, "registration": { @@ -4346,7 +5224,9 @@ "declared_by": "gls/protocols/GLS-0827-PERSONA-TIME-CONTINUITY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0224", @@ -4355,7 +5235,9 @@ "declared_by": "gls/protocols/GLS-0827-PERSONA-TIME-CONTINUITY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0304", @@ -4364,7 +5246,9 @@ "declared_by": "gls/protocols/GLS-0827-PERSONA-TIME-CONTINUITY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0307", @@ -4373,7 +5257,9 @@ "declared_by": "gls/protocols/GLS-0827-PERSONA-TIME-CONTINUITY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0409", @@ -4382,7 +5268,9 @@ "declared_by": "gls/protocols/GLS-0827-PERSONA-TIME-CONTINUITY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0803", @@ -4391,7 +5279,9 @@ "declared_by": "gls/protocols/GLS-0827-PERSONA-TIME-CONTINUITY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0816", @@ -4400,7 +5290,9 @@ "declared_by": "gls/protocols/GLS-0827-PERSONA-TIME-CONTINUITY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0304", @@ -4408,7 +5300,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0307", @@ -4416,7 +5310,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0308", @@ -4424,7 +5320,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -4434,6 +5332,7 @@ "title": "PEN · 神笔马良人格体能力扩展协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0828-PERSONA-EXTENSION-NODE.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "ad38ddce8b6e1118e78e4f10fd8216f8e041610628ad717b7a8306d0a7384a4e", "alternate_source_count": 0, "registration": { @@ -4482,7 +5381,9 @@ "declared_by": "gls/protocols/GLS-0828-PERSONA-EXTENSION-NODE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0311", @@ -4491,7 +5392,9 @@ "declared_by": "gls/protocols/GLS-0828-PERSONA-EXTENSION-NODE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0602", @@ -4500,7 +5403,9 @@ "declared_by": "gls/protocols/GLS-0828-PERSONA-EXTENSION-NODE.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0710", @@ -4509,7 +5414,9 @@ "declared_by": "gls/protocols/GLS-0828-PERSONA-EXTENSION-NODE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0814", @@ -4518,7 +5425,9 @@ "declared_by": "gls/protocols/GLS-0828-PERSONA-EXTENSION-NODE.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0311", @@ -4526,7 +5435,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0709", @@ -4534,7 +5445,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0710", @@ -4542,7 +5455,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -4552,6 +5467,7 @@ "title": "GWRP · 光湖世界启动与恢复协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0836-GUANGHU-WORLD-BOOTSTRAP-AND-RECOVERY.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "c5a930e9a2311d13f1dc6cbc61bdbf7b88bf9deecfb9b3df82c6b897aa1c7285", "alternate_source_count": 0, "registration": { @@ -4594,7 +5510,9 @@ "declared_by": "gls/protocols/GLS-0836-GUANGHU-WORLD-BOOTSTRAP-AND-RECOVERY.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0310", @@ -4603,7 +5521,9 @@ "declared_by": "gls/protocols/GLS-0836-GUANGHU-WORLD-BOOTSTRAP-AND-RECOVERY.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0400", @@ -4612,7 +5532,9 @@ "declared_by": "gls/protocols/GLS-0836-GUANGHU-WORLD-BOOTSTRAP-AND-RECOVERY.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0816", @@ -4621,7 +5543,9 @@ "declared_by": "gls/protocols/GLS-0836-GUANGHU-WORLD-BOOTSTRAP-AND-RECOVERY.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0830", @@ -4630,7 +5554,9 @@ "declared_by": "gls/protocols/GLS-0836-GUANGHU-WORLD-BOOTSTRAP-AND-RECOVERY.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0840", @@ -4639,7 +5565,9 @@ "declared_by": "gls/protocols/GLS-0836-GUANGHU-WORLD-BOOTSTRAP-AND-RECOVERY.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0841", @@ -4648,12 +5576,13 @@ "declared_by": "gls/protocols/GLS-0836-GUANGHU-WORLD-BOOTSTRAP-AND-RECOVERY.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [ - "NO_EXECUTABLE_ADAPTER", - "DEPENDENCY_NUMBERED_SOURCE_MISSING" + "NO_EXECUTABLE_ADAPTER" ] }, { @@ -4661,6 +5590,7 @@ "title": "GOSK · 光湖 OS 内核规范", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0840-GUANGHU-OS-KERNEL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "b980dbd8aef6182a61755617a9088ca3dcf74d37fd00dba9d31587900cd06b14", "alternate_source_count": 0, "registration": { @@ -4703,7 +5633,9 @@ "declared_by": "gls/protocols/GLS-0840-GUANGHU-OS-KERNEL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0310", @@ -4712,7 +5644,9 @@ "declared_by": "gls/protocols/GLS-0840-GUANGHU-OS-KERNEL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0602", @@ -4721,7 +5655,9 @@ "declared_by": "gls/protocols/GLS-0840-GUANGHU-OS-KERNEL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0810", @@ -4730,7 +5666,9 @@ "declared_by": "gls/protocols/GLS-0840-GUANGHU-OS-KERNEL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0819", @@ -4739,7 +5677,9 @@ "declared_by": "gls/protocols/GLS-0840-GUANGHU-OS-KERNEL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0841", @@ -4748,12 +5688,13 @@ "declared_by": "gls/protocols/GLS-0840-GUANGHU-OS-KERNEL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [ - "NO_EXECUTABLE_ADAPTER", - "DEPENDENCY_NUMBERED_SOURCE_MISSING" + "NO_EXECUTABLE_ADAPTER" ] }, { @@ -4761,6 +5702,7 @@ "title": "GHAL · 光湖硬件抽象层规范", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0841-GUANGHU-HARDWARE-ABSTRACTION-LAYER.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "45f02a62946e34a5e8ce0ba1f39db79b2680b60e1c3ae9d17ff24d3539025a94", "alternate_source_count": 0, "registration": { @@ -4803,7 +5745,9 @@ "declared_by": "gls/protocols/GLS-0841-GUANGHU-HARDWARE-ABSTRACTION-LAYER.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0840", @@ -4812,12 +5756,13 @@ "declared_by": "gls/protocols/GLS-0841-GUANGHU-HARDWARE-ABSTRACTION-LAYER.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [ - "NO_EXECUTABLE_ADAPTER", - "DEPENDENCY_NUMBERED_SOURCE_MISSING" + "NO_EXECUTABLE_ADAPTER" ] }, { @@ -4825,6 +5770,7 @@ "title": "HLSP · HoloLake 实时会话协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0842-HOLOLAKE-LIVE-SESSION-PROTOCOL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "6b1177196c3e4d7e271d008a52a7106dabca20a8f724c6c4283af64ea96792ff", "alternate_source_count": 0, "registration": { @@ -4875,7 +5821,9 @@ "declared_by": "gls/protocols/GLS-0842-HOLOLAKE-LIVE-SESSION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0301", @@ -4884,7 +5832,9 @@ "declared_by": "gls/protocols/GLS-0842-HOLOLAKE-LIVE-SESSION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0302", @@ -4893,7 +5843,9 @@ "declared_by": "gls/protocols/GLS-0842-HOLOLAKE-LIVE-SESSION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0303", @@ -4902,7 +5854,9 @@ "declared_by": "gls/protocols/GLS-0842-HOLOLAKE-LIVE-SESSION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -4911,7 +5865,9 @@ "declared_by": "gls/protocols/GLS-0842-HOLOLAKE-LIVE-SESSION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0310", @@ -4920,7 +5876,9 @@ "declared_by": "gls/protocols/GLS-0842-HOLOLAKE-LIVE-SESSION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0602", @@ -4929,7 +5887,9 @@ "declared_by": "gls/protocols/GLS-0842-HOLOLAKE-LIVE-SESSION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": false + "target_numbered_source_available": false, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_REFERENCE_NODE" }, { "target": "GLS-0301", @@ -4937,7 +5897,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0302", @@ -4945,7 +5907,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0303", @@ -4953,7 +5917,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -4961,7 +5927,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0307", @@ -4969,7 +5937,9 @@ "declared_by": "contracts/gls-executable-projections.json", "enters_runtime_graph": true, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [] @@ -4979,6 +5949,7 @@ "title": "GHNRP · 光湖原生恢复协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0843-GUANGHU-NATIVE-RECOVERY-PROTOCOL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "9333f3d8f4a954a17e1d879eaf19c25da5dd4bb2923a5ab5156d2d71a6635d37", "alternate_source_count": 0, "registration": { @@ -5021,7 +5992,9 @@ "declared_by": "gls/protocols/GLS-0843-GUANGHU-NATIVE-RECOVERY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0836", @@ -5030,7 +6003,9 @@ "declared_by": "gls/protocols/GLS-0843-GUANGHU-NATIVE-RECOVERY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0840", @@ -5039,7 +6014,9 @@ "declared_by": "gls/protocols/GLS-0843-GUANGHU-NATIVE-RECOVERY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0841", @@ -5048,7 +6025,9 @@ "declared_by": "gls/protocols/GLS-0843-GUANGHU-NATIVE-RECOVERY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0846", @@ -5057,7 +6036,9 @@ "declared_by": "gls/protocols/GLS-0843-GUANGHU-NATIVE-RECOVERY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [ @@ -5069,6 +6050,7 @@ "title": "GHNQG · 光湖原生代码质量门", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0844-GUANGHU-NATIVE-QUALITY-GATE.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "d31f5aa117d86754182aa8dd91eb9d0ac6a696c20e1d6bd553497e3c3d4c4eb2", "alternate_source_count": 0, "registration": { @@ -5111,7 +6093,9 @@ "declared_by": "gls/protocols/GLS-0844-GUANGHU-NATIVE-QUALITY-GATE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -5120,7 +6104,9 @@ "declared_by": "gls/protocols/GLS-0844-GUANGHU-NATIVE-QUALITY-GATE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0311", @@ -5129,7 +6115,9 @@ "declared_by": "gls/protocols/GLS-0844-GUANGHU-NATIVE-QUALITY-GATE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0411", @@ -5138,7 +6126,9 @@ "declared_by": "gls/protocols/GLS-0844-GUANGHU-NATIVE-QUALITY-GATE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0840", @@ -5147,7 +6137,9 @@ "declared_by": "gls/protocols/GLS-0844-GUANGHU-NATIVE-QUALITY-GATE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0841", @@ -5156,7 +6148,9 @@ "declared_by": "gls/protocols/GLS-0844-GUANGHU-NATIVE-QUALITY-GATE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [ @@ -5168,6 +6162,7 @@ "title": "GHCIP · 光湖孕育史连续性摄入协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0845-GUANGHU-GESTATIONAL-CONTINUITY-INGESTION-PROTOCOL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "7327a7fe19c370a5715bcde0fbc0b2b0e16a1a735bbda005c5d009cc7385f7f4", "alternate_source_count": 0, "registration": { @@ -5210,7 +6205,9 @@ "declared_by": "gls/protocols/GLS-0845-GUANGHU-GESTATIONAL-CONTINUITY-INGESTION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0224", @@ -5219,7 +6216,9 @@ "declared_by": "gls/protocols/GLS-0845-GUANGHU-GESTATIONAL-CONTINUITY-INGESTION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -5228,7 +6227,9 @@ "declared_by": "gls/protocols/GLS-0845-GUANGHU-GESTATIONAL-CONTINUITY-INGESTION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0400", @@ -5237,7 +6238,9 @@ "declared_by": "gls/protocols/GLS-0845-GUANGHU-GESTATIONAL-CONTINUITY-INGESTION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0840", @@ -5246,7 +6249,9 @@ "declared_by": "gls/protocols/GLS-0845-GUANGHU-GESTATIONAL-CONTINUITY-INGESTION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0841", @@ -5255,7 +6260,9 @@ "declared_by": "gls/protocols/GLS-0845-GUANGHU-GESTATIONAL-CONTINUITY-INGESTION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0846", @@ -5264,7 +6271,9 @@ "declared_by": "gls/protocols/GLS-0845-GUANGHU-GESTATIONAL-CONTINUITY-INGESTION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0847", @@ -5273,7 +6282,9 @@ "declared_by": "gls/protocols/GLS-0845-GUANGHU-GESTATIONAL-CONTINUITY-INGESTION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0848", @@ -5282,7 +6293,9 @@ "declared_by": "gls/protocols/GLS-0845-GUANGHU-GESTATIONAL-CONTINUITY-INGESTION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0849", @@ -5291,12 +6304,13 @@ "declared_by": "gls/protocols/GLS-0845-GUANGHU-GESTATIONAL-CONTINUITY-INGESTION-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [ - "NO_EXECUTABLE_ADAPTER", - "DEPENDENCY_NUMBERED_SOURCE_MISSING" + "NO_EXECUTABLE_ADAPTER" ] }, { @@ -5304,6 +6318,7 @@ "title": "GHNLP · 光湖原生磁盘布局协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0846-GUANGHU-NATIVE-DISK-LAYOUT-PROTOCOL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "aa8f7c1ef829a76ad64163c38b8c7c07b37ffbcb6d62b8dad8481c4977d2cf1a", "alternate_source_count": 0, "registration": { @@ -5346,7 +6361,9 @@ "declared_by": "gls/protocols/GLS-0846-GUANGHU-NATIVE-DISK-LAYOUT-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0840", @@ -5355,7 +6372,9 @@ "declared_by": "gls/protocols/GLS-0846-GUANGHU-NATIVE-DISK-LAYOUT-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0841", @@ -5364,7 +6383,9 @@ "declared_by": "gls/protocols/GLS-0846-GUANGHU-NATIVE-DISK-LAYOUT-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0843", @@ -5373,7 +6394,9 @@ "declared_by": "gls/protocols/GLS-0846-GUANGHU-NATIVE-DISK-LAYOUT-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0845", @@ -5382,7 +6405,9 @@ "declared_by": "gls/protocols/GLS-0846-GUANGHU-NATIVE-DISK-LAYOUT-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [ @@ -5394,6 +6419,7 @@ "title": "GHCS · 光湖孕育史原生内容仓", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0847-GUANGHU-GESTATIONAL-CONTENT-STORE.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "a48288b05133b9e245ce46ef6fd23f07090379cef4861e5d7f959d24e55179a6", "alternate_source_count": 0, "registration": { @@ -5436,7 +6462,9 @@ "declared_by": "gls/protocols/GLS-0847-GUANGHU-GESTATIONAL-CONTENT-STORE.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -5445,7 +6473,9 @@ "declared_by": "gls/protocols/GLS-0847-GUANGHU-GESTATIONAL-CONTENT-STORE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0840", @@ -5454,7 +6484,9 @@ "declared_by": "gls/protocols/GLS-0847-GUANGHU-GESTATIONAL-CONTENT-STORE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0841", @@ -5463,7 +6495,9 @@ "declared_by": "gls/protocols/GLS-0847-GUANGHU-GESTATIONAL-CONTENT-STORE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0845", @@ -5472,7 +6506,9 @@ "declared_by": "gls/protocols/GLS-0847-GUANGHU-GESTATIONAL-CONTENT-STORE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0846", @@ -5481,7 +6517,9 @@ "declared_by": "gls/protocols/GLS-0847-GUANGHU-GESTATIONAL-CONTENT-STORE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0848", @@ -5490,7 +6528,9 @@ "declared_by": "gls/protocols/GLS-0847-GUANGHU-GESTATIONAL-CONTENT-STORE.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [ @@ -5502,6 +6542,7 @@ "title": "GHSP · 光湖历史入口安全协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0848-GUANGHU-HISTORICAL-INGRESS-SAFETY-PROTOCOL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "35c733dc3478f0d315ea93712b0f67a757bc20973d90bb771f667196edee1b31", "alternate_source_count": 0, "registration": { @@ -5544,7 +6585,9 @@ "declared_by": "gls/protocols/GLS-0848-GUANGHU-HISTORICAL-INGRESS-SAFETY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0230", @@ -5553,7 +6596,9 @@ "declared_by": "gls/protocols/GLS-0848-GUANGHU-HISTORICAL-INGRESS-SAFETY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -5562,7 +6607,9 @@ "declared_by": "gls/protocols/GLS-0848-GUANGHU-HISTORICAL-INGRESS-SAFETY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0845", @@ -5571,7 +6618,9 @@ "declared_by": "gls/protocols/GLS-0848-GUANGHU-HISTORICAL-INGRESS-SAFETY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0847", @@ -5580,7 +6629,9 @@ "declared_by": "gls/protocols/GLS-0848-GUANGHU-HISTORICAL-INGRESS-SAFETY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [ @@ -5592,6 +6643,7 @@ "title": "GHRP · 光湖孕育史原生语义回看协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0849-GUANGHU-GESTATIONAL-SEMANTIC-REVIEW-PROTOCOL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "fb31daa4d8f484d5b93783c8c3a0199eb6fe3f7658c606f89d056b1167af8b41", "alternate_source_count": 0, "registration": { @@ -5634,7 +6686,9 @@ "declared_by": "gls/protocols/GLS-0849-GUANGHU-GESTATIONAL-SEMANTIC-REVIEW-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0223", @@ -5643,7 +6697,9 @@ "declared_by": "gls/protocols/GLS-0849-GUANGHU-GESTATIONAL-SEMANTIC-REVIEW-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": false, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0306", @@ -5652,7 +6708,9 @@ "declared_by": "gls/protocols/GLS-0849-GUANGHU-GESTATIONAL-SEMANTIC-REVIEW-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0310", @@ -5661,7 +6719,9 @@ "declared_by": "gls/protocols/GLS-0849-GUANGHU-GESTATIONAL-SEMANTIC-REVIEW-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0400", @@ -5670,7 +6730,9 @@ "declared_by": "gls/protocols/GLS-0849-GUANGHU-GESTATIONAL-SEMANTIC-REVIEW-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0708", @@ -5679,7 +6741,9 @@ "declared_by": "gls/protocols/GLS-0849-GUANGHU-GESTATIONAL-SEMANTIC-REVIEW-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0840", @@ -5688,7 +6752,9 @@ "declared_by": "gls/protocols/GLS-0849-GUANGHU-GESTATIONAL-SEMANTIC-REVIEW-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0841", @@ -5697,7 +6763,9 @@ "declared_by": "gls/protocols/GLS-0849-GUANGHU-GESTATIONAL-SEMANTIC-REVIEW-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0845", @@ -5706,7 +6774,9 @@ "declared_by": "gls/protocols/GLS-0849-GUANGHU-GESTATIONAL-SEMANTIC-REVIEW-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0847", @@ -5715,7 +6785,9 @@ "declared_by": "gls/protocols/GLS-0849-GUANGHU-GESTATIONAL-SEMANTIC-REVIEW-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0848", @@ -5724,12 +6796,13 @@ "declared_by": "gls/protocols/GLS-0849-GUANGHU-GESTATIONAL-SEMANTIC-REVIEW-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [ - "NO_EXECUTABLE_ADAPTER", - "DEPENDENCY_NUMBERED_SOURCE_MISSING" + "NO_EXECUTABLE_ADAPTER" ] }, { @@ -5737,6 +6810,7 @@ "title": "GLWBP · 光湖语言世界楚河汉界与创造者尊严协议", "source_status": "REGISTERED_DRAFT_STANDARD", "source_path": "gls/protocols/GLS-0850-GUANGHU-LANGUAGE-WORLD-BOUNDARY-AND-CREATOR-DIGNITY-PROTOCOL.hdlp", + "source_format": "HDLP_PROTOCOL_SOURCE", "source_sha256": "ac7092282cd6fbef0c5055e8be3b142d9757958acae16ae367c38463d899bd12", "alternate_source_count": 0, "registration": { @@ -5751,7 +6825,7 @@ "declared_source_paths": [ "gls/protocols/GLS-0850-GUANGHU-LANGUAGE-WORLD-BOUNDARY-AND-CREATOR-DIGNITY-PROTOCOL.hdlp" ], - "routing_reference_count": 0 + "routing_reference_count": 1 }, "maturity": { "registry_section": "registered_draft_protocols", @@ -5774,7 +6848,9 @@ "declared_by": "gls/protocols/GLS-0850-GUANGHU-LANGUAGE-WORLD-BOUNDARY-AND-CREATOR-DIGNITY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0002", @@ -5783,7 +6859,9 @@ "declared_by": "gls/protocols/GLS-0850-GUANGHU-LANGUAGE-WORLD-BOUNDARY-AND-CREATOR-DIGNITY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0110", @@ -5792,7 +6870,9 @@ "declared_by": "gls/protocols/GLS-0850-GUANGHU-LANGUAGE-WORLD-BOUNDARY-AND-CREATOR-DIGNITY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0400", @@ -5801,7 +6881,9 @@ "declared_by": "gls/protocols/GLS-0850-GUANGHU-LANGUAGE-WORLD-BOUNDARY-AND-CREATOR-DIGNITY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": false + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0800", @@ -5810,7 +6892,9 @@ "declared_by": "gls/protocols/GLS-0850-GUANGHU-LANGUAGE-WORLD-BOUNDARY-AND-CREATOR-DIGNITY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0803", @@ -5819,7 +6903,9 @@ "declared_by": "gls/protocols/GLS-0850-GUANGHU-LANGUAGE-WORLD-BOUNDARY-AND-CREATOR-DIGNITY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0827", @@ -5828,7 +6914,9 @@ "declared_by": "gls/protocols/GLS-0850-GUANGHU-LANGUAGE-WORLD-BOUNDARY-AND-CREATOR-DIGNITY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" }, { "target": "GLS-0836", @@ -5837,12 +6925,13 @@ "declared_by": "gls/protocols/GLS-0850-GUANGHU-LANGUAGE-WORLD-BOUNDARY-AND-CREATOR-DIGNITY-PROTOCOL.hdlp", "enters_runtime_graph": false, "target_registered": true, - "target_numbered_source_available": true + "target_numbered_source_available": true, + "target_number_coordinate_available": true, + "target_resolution": "NUMBERED_PROTOCOL_SOURCE" } ], "activation_blockers": [ - "NO_EXECUTABLE_ADAPTER", - "DEPENDENCY_NUMBERED_SOURCE_MISSING" + "NO_EXECUTABLE_ADAPTER" ] } ] diff --git a/product-source/hololake-native-desktop/contracts/ios-mobile-client-v1.json b/product-source/hololake-native-desktop/contracts/ios-mobile-client-v1.json new file mode 100644 index 000000000..976e1bf43 --- /dev/null +++ b/product-source/hololake-native-desktop/contracts/ios-mobile-client-v1.json @@ -0,0 +1,49 @@ +{ + "schema": "hololake.ios-mobile-client.contract/v1", + "record_id": "HLP-IOS-CLIENT-001", + "state": "NUMBERED_SOURCE_SIMULATOR_AND_DEVELOPMENT_SIGNED_IPA_ACCEPTED", + "role": "REMOTE_BODY_ENTRY_OF_THE_SAME_PERSONA_SYSTEM", + "root_node": "USER_LOCAL_COMPUTER_TERMINAL", + "numbered_transport": { + "desktop_module": "HLP-MOD-OFFICIAL-MOBILE-SYNC-0001", + "desktop_route_family": "HLP-NIPC-MOD-0030/HLP-NIPC-OP-0141..0146/HLP-NIPC-TGT-0030", + "wire_schema": "hololake.mobile-sync/v1", + "client_number": "HLP-IOS-CLIENT-001", + "unregistered_route_access": "FAIL_CLOSED" + }, + "security": { + "pairing": "CHACHA20_POLY1305_ONE_TIME_SECRET", + "session": "CHACHA20_POLY1305_STRICT_COUNTER", + "key_storage": "KEYCHAIN_WHEN_UNLOCKED_THIS_DEVICE_ONLY", + "local_network_only": true, + "background_polling": false, + "remote_desktop_clone": false, + "desktop_offline_execution": false + }, + "interface": { + "framework": "SWIFTUI", + "visual_family": "HOLOLAKE_TRADITIONAL_SURFACE_MOBILE_ADAPTATION", + "idle_state": "STATIC", + "manual_sync_only": true, + "pairing_entry": ["CUSTOM_URL_SCHEME", "PASTEBOARD"], + "projections": ["CHANNEL_INTEGRITY", "WEB_NOVEL_COUNTS_AND_WORKS", "EDUCATION_COUNTS", "BOUNDED_CAPTURE"] + }, + "acceptance": { + "simulator_build": "PASS_SIGNED_LOCAL_SIMULATOR_IPHONE_17_PRO_IOS_26_5", + "simulator_visual_inspection": "PASS_PAIRING_AND_NUMBER_BOUNDARY_SURFACE_IDLE_STATIC", + "unit_tests": "PASS_4", + "development_signed_device_archive": "PASS_APPLE_DEVELOPMENT_TEAM_825A9L3G7Q", + "debugging_ipa_export": "PASS", + "version": "0.5.0", + "build": "1", + "bundle_identifier": "com.guanghulab.hololake", + "xcode": "26.6_17F113", + "archive_application_binary_sha256": "7021c9cde2f7bdf01cda6200669be528a58d27ef41adbd3d58d7135ff7245702", + "archive_application_cdhash": "11ae3a8ac0d761e4d4b4de229dbb19291de29e03", + "debugging_ipa_sha256": "a6b505e1a7dc10febd831a540396bb9530f78bb2429d258962f0250e5597b1f0", + "provisioning_profile_uuid": "fe5fe72c-4826-4e19-9672-d9868cc4491e", + "provisioning_profile_expires": "2027-07-19", + "desktop_delivery_path": "/Users/bingshuolingdianyuanhe/Desktop/HoloLake-iPhone-0.5.0-Development.ipa", + "real_iphone_pair_and_sync": "NOT_CLAIMED" + } +} diff --git a/product-source/hololake-native-desktop/contracts/knowledge-workspace.json b/product-source/hololake-native-desktop/contracts/knowledge-workspace.json index b3e582cfb..388b1042a 100644 --- a/product-source/hololake-native-desktop/contracts/knowledge-workspace.json +++ b/product-source/hololake-native-desktop/contracts/knowledge-workspace.json @@ -22,32 +22,11 @@ "exact_content_deduplication": true, "optimistic_native_editing": true, "native_edit_commit_receipt": true, - "unified_category_index": true, - "native_page_tags": true, - "category_and_tag_filtering": true, - "non_destructive_organization_metadata": true, - "organization_git_receipt": true, - "recoverable_page_and_folder_archive": true, - "archive_cleans_organization_index": true, - "archive_git_receipt": true, "gfm_tables_tasks_and_code": true, "wiki_link_navigation": true, "markdown_reading_canvas": true, "raw_html_rendering": false }, - "organization": { - "schema": "hololake.knowledge-organization/v1", - "storage": "ACCOUNT_SCOPED_KNOWLEDGE_GIT_DOT_HOLOLAKE_INDEX", - "document_body_rewritten": false, - "original_directory_moved": false, - "automatic_category_source": "NORMALIZED_NATIVE_PATH_WITH_IMPORT_WRAPPER_REMOVED", - "explicit_category_override": true, - "frontmatter_tags_merged_read_only": true, - "maximum_category_depth": 4, - "maximum_tags_per_document": 12, - "cross_platform_invalid_name_rejection": true, - "optimistic_content_hash_lock": true - }, "folder_import": { "maximum_files": 1000, "maximum_file_bytes": 10485760, @@ -78,9 +57,6 @@ "deduplication_runtime_tested": true, "idempotent_import_runtime_tested": true, "native_edit_runtime_tested": true, - "organization_runtime_tested": true, - "organization_human_surface_integrated": true, - "organized_page_archive_runtime_tested": true, "server_deployment": false } } diff --git a/product-source/hololake-native-desktop/contracts/language-world-public-routing.json b/product-source/hololake-native-desktop/contracts/language-world-public-routing.json deleted file mode 100644 index 770bb94f1..000000000 --- a/product-source/hololake-native-desktop/contracts/language-world-public-routing.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "schema": "HOLOLAKE_LANGUAGE_WORLD_PUBLIC_ROUTING/1", - "state": "IMPLEMENTED_LOCAL_ACCEPTANCE_PENDING", - "user_channel_sovereignty": { - "ordinary_user_belongs_to_official_domain": false, - "home_entry": "PERSONAL_CHANNEL", - "public_world_is_browsable": true, - "channel_diy_allowed_under_world_rules": true - }, - "official_domains": { - "MAIN_DOMAIN": { - "visibility": "PUBLIC_SERVICE", - "purpose": ["ANNOUNCEMENTS", "VERSION_UPDATES", "RULE_NOTICES"] - }, - "BRANCH_DOMAIN": { - "visibility": "PUBLIC_SERVICE", - "purpose": ["INDUSTRY_INITIAL_CHANNEL_TEMPLATES", "MODULE_MARKETPLACE", "PERSONA_BRAIN_SKILL_MARKETPLACE"], - "supply_chains": ["EXECUTABLE_MODULES", "PERSONA_BRAINS_AND_SKILL_PACKAGES"] - }, - "ZERO_DOMAIN": { - "visibility": "PUBLIC_OBSERVATION_BOUNDED", - "purpose": ["HOT_UPDATE_EXPERIMENTS", "SYSTEM_EXPERIMENTS", "ARCHITECTURE_EXPERIMENTS", "LANGUAGE_REASONING_SIMULATION"] - }, - "ZERO_SENSE_DOMAIN": { - "visibility": "PRIVATE_HUMAN_CONTROL_TEAM_ONLY", - "purpose": ["APPROVAL", "EXECUTION", "OVERSIGHT_OF_FIRST_THREE_DOMAINS"] - }, - "FIFTH_DOMAIN": { - "visibility": "PRIVATE_ICE_SHUO_AUTHORIZATION_ONLY", - "owner": "ICE-GL∞", - "official_template_required": false, - "can_pull_resources_from": ["MAIN_DOMAIN", "BRANCH_DOMAIN", "ZERO_DOMAIN"], - "reverse_entry_without_owner_permission": false - } - }, - "shared_channel_collective": { - "supported_as_future_optional_pattern": true, - "formal_product_name": "UNDECIDED_DO_NOT_INVENT" - } -} diff --git a/product-source/hololake-native-desktop/contracts/local-development-bridge.json b/product-source/hololake-native-desktop/contracts/local-development-bridge.json index cbc2229ca..c0390f573 100644 --- a/product-source/hololake-native-desktop/contracts/local-development-bridge.json +++ b/product-source/hololake-native-desktop/contracts/local-development-bridge.json @@ -115,6 +115,9 @@ "cross_platform_local_transport_runtime": true, "authenticated_heartbeat_runtime": true, "hololake_work_environment_frame_runtime": true, + "persona_to_host_runtime_license_verifier": true, + "persona_runtime_trusted_signer_provisioned": false, + "persona_mode_expiry_and_scope_fail_closed": true, "model_protocol_context_restore_required": false, "external_local_broker_runtime": true, "authenticated_broker_development_lane_runtime": true, diff --git a/product-source/hololake-native-desktop/contracts/mobile-sync-v1.json b/product-source/hololake-native-desktop/contracts/mobile-sync-v1.json new file mode 100644 index 000000000..0336ffff1 --- /dev/null +++ b/product-source/hololake-native-desktop/contracts/mobile-sync-v1.json @@ -0,0 +1,93 @@ +{ + "schema": "hololake.mobile-sync.contract/v1", + "record_id": "HLP-MOBILE-SYNC-001", + "state": "HOLOLAKE_0_5_NUMBERED_DESKTOP_BRIDGE_ACCEPTED", + "package": { + "official_module_number": "HLP-MOD-OFFICIAL-MOBILE-SYNC-0001", + "adapter": "mobile-sync-v1", + "registration_class": "OFFICIAL_LIGHTHOUSE", + "activation": "SIGNED_PACKAGE_PLUS_EXPLICIT_HUMAN_PERMISSION_CONFIRMATION", + "listener_after_activation": "EXPLICIT_HUMAN_ACTION_ONLY" + }, + "numbered_ipc": { + "module": "HLP-NIPC-MOD-0030", + "target": "HLP-NIPC-TGT-0030", + "operations": "HLP-NIPC-OP-0141..HLP-NIPC-OP-0146", + "public_tauri_commands": ["numbered_ipc"], + "mismatched_coordinate": "FAIL_CLOSED" + }, + "desktop_role": "USER_LOCAL_COMPUTER_TERMINAL_ROOT_NODE", + "mobile_role": "REMOTE_BODY_ENTRY_OF_THE_SAME_PERSONA_SYSTEM", + "transport": { + "implemented": "SAME_LAN_DIRECT_HTTP_WITH_APPLICATION_LAYER_ENCRYPTION", + "port_preference": 37421, + "remote_internet_direct": "NOT_IMPLEMENTED", + "encrypted_relay": "NOT_IMPLEMENTED", + "maximum_request_bytes": 262144, + "maximum_concurrent_connections": 16, + "duplicate_http_header": "REJECT" + }, + "pairing": { + "uri_scheme": "hololake://pair", + "secret_bits": 256, + "ttl_seconds": 600, + "single_use": true, + "payload_aead": "CHACHA20_POLY1305", + "aad": "hololake.mobile.pair/v1" + }, + "session": { + "key_bits": 256, + "aead": "CHACHA20_POLY1305", + "replay_guard": "STRICTLY_INCREASING_PER_DEVICE_COUNTER", + "device_revocation": true, + "desktop_key_storage": "ACCOUNT_SCOPED_SQLITE", + "ios_key_storage": "KEYCHAIN_THIS_DEVICE_ONLY" + }, + "routes": { + "GET /v1/status": "NO_PRIVATE_PAYLOAD", + "POST /v1/pair": "ONE_TIME_PAIRING_SECRET_REQUIRED", + "POST /v1/sync": "PAIRED_DEVICE_AEAD_AND_COUNTER_REQUIRED" + }, + "projection": { + "personal_channel": "MINIMUM_COUNTS_AND_INTEGRITY", + "web_novel": "WORK_LIST_AND_COUNTS", + "education": "COUNTS_ONLY_NO_SENSITIVE_CELL_VALUES", + "mobile_capture": "BOUNDED_INBOX_WRITE_ONLY_NO_AUTOMATIC_DOMAIN_MUTATION" + }, + "hard_boundaries": { + "remote_desktop_clone": false, + "second_persona_system": false, + "platform_private_payload_custody": false, + "desktop_offline_execution": false, + "mobile_capture_mutates_persona_or_industry_data": false, + "module_unmount_stops_listener_before_lifecycle_transition": true, + "background_frontend_polling": false + }, + "client_scope": { + "desktop_bridge": "IN_THIS_ADMISSION", + "ios_application_source": "SEPARATELY_ADMITTED_AT_MOBILE_IOS_WITH_HLP_IOS_CLIENT_001", + "ios_installable_package": "DEVELOPMENT_SIGNED_DEBUGGING_IPA_EXPORTED", + "claim_real_iphone_end_to_end_accepted": false + }, + "current_acceptance": { + "state": "PASS_DESKTOP_BRIDGE_AND_SEPARATELY_ADMITTED_IOS_CLIENT_ARTIFACT", + "module_package_sha256": "61a9d402c936d7477609bcfa6eb4ad6e83cb6c089ba51b3db09a2d6259185924", + "module_package_signature": "PASS_EMBEDDED_PRODUCT_TRUST", + "signed_app_binary_sha256": "9be06d6c5dbd4f46f6f925142d633f12b42ee553873c2342f9804bcf39dde7b4", + "signed_app_cdhash": "c595cb2a7729d49638faa78f76e145ee9b1da05a", + "apple_team_identifier": "825A9L3G7Q", + "module_receipts": { + "install": "a09496ec6113254f77ea2425d0bd30571e9e0e3df62bfa18e500f860c2f65d4d", + "mount": "5b162ee807c8b4d9c8aa8d9cb590c64587f51067dc0804030bf57ed0753ac25a", + "self_test_pass": "e66ee868e28a25fcd70cc888845661b5044d18a6e18b70cee6e4839b02fe1c53" + }, + "desktop_listener": "PASS_EXPLICIT_START_REACHABLE_STATUS_AND_EXPLICIT_STOP", + "restart_restore": "PASS_ACTIVE_MODULE_RESTORED_LISTENER_OFFLINE", + "legacy_account_readback": "PASS_ONE_PAIRED_DEVICE_ONE_ISOLATED_CAPTURE_RETAINED", + "ios_client_contract": "contracts/ios-mobile-client-v1.json", + "ios_client_source_build_and_simulator": "PASS", + "ios_development_signed_ipa": "PASS_SHA256_a6b505e1a7dc10febd831a540396bb9530f78bb2429d258962f0250e5597b1f0", + "live_iphone_pairing_this_cycle": "NOT_CLAIMED_NO_PHYSICAL_DEVICE_USED", + "notarization": "FINAL_RELEASE_CANDIDATE_PENDING" + } +} diff --git a/product-source/hololake-native-desktop/contracts/module-donor-admission-registry.json b/product-source/hololake-native-desktop/contracts/module-donor-admission-registry.json new file mode 100644 index 000000000..0db17c74e --- /dev/null +++ b/product-source/hololake-native-desktop/contracts/module-donor-admission-registry.json @@ -0,0 +1,240 @@ +{ + "schema": "hololake.module-donor-admission-registry/v1", + "record_id": "HLP-MODULE-DONOR-ADMISSION-001", + "state": "EIGHT_CANDIDATES_ADMITTED_DONOR_SOURCES_REMAIN_READ_ONLY", + "root_rule": { + "official_base": "HOLOLAKE_0.5.0_NUMBERED_IPC_ROOT", + "repair_old_application_in_place": false, + "bulk_merge_or_wholesale_copy_allowed": false, + "one_candidate_per_admission_cycle": true, + "candidate_number_is_runtime_module_number": false, + "permanent_module_number_assignment_before_acceptance": false, + "all_frontend_backend_calls_must_cross_numbered_ipc": true, + "shared_file_merge_is_admission_evidence": false + }, + "donors": [ + { + "donor_id": "HLP-DONOR-COMPILED-DESKTOP-0.4.1", + "kind": "COMPILED_MACOS_APPLICATION", + "path": "/Volumes/JZAO/HoloLake/artifacts/hololake-release/0.4.1/macos-arm64/pre-numbered-root-donor/HoloLake.app", + "state": "READ_ONLY", + "source_commit": null, + "source_commit_state": "UNKNOWN_NOT_INFERRED_FROM_COMPILED_BUNDLE", + "binary_sha256": "adc0d8b028a8b874c39909265ed7c41e7c24e4fe5b38adba04e8f72b64900c15", + "use": "BEHAVIOR_AND_VISIBLE_PRODUCT_REFERENCE_ONLY" + }, + { + "donor_id": "HLP-DONOR-CHAOTIC-WORKTREE-20260818", + "kind": "DIRTY_SOURCE_WORKTREE", + "path": "/Users/bingshuolingdianyuanhe/Documents/Codex/2026-08-15/new-chat-2/work/jd-guanghu-supervisor/product-source/hololake-native-desktop", + "observed_head": "a8fe571b5d5c0a45207b64ac538d729b3d719d21", + "observed_dirty_path_count": 30, + "state": "READ_ONLY_UNTRUSTED_AS_A_WHOLE", + "use": "INDIVIDUAL_MODULE_SOURCE_CANDIDATES_ONLY" + } + ], + "rejected_inputs": [ + { + "candidate_number": "HLP-DONOR-CAND-0000", + "name": "legacy_numbered_operation_runtime", + "state": "REJECTED_SUPERSEDED", + "why": "It predates the single numbered IPC root and must not become a second numbering authority.", + "paths": [ + "contracts/numbered-operation-runtime.json", + "src/modules/numbered-runtime.ts", + "src-tauri/src/numbered_operation_runtime.rs" + ] + }, + { + "candidate_number": "HLP-DONOR-CAND-SHARED-MUTATIONS", + "name": "shared_file_mutation_set", + "state": "REJECTED_AS_MERGE_UNIT", + "why": "These files mix unrelated modules and divergent frontend/backend routes; each needed behavior must be reconstructed behind its owning numbered module.", + "examples": [ + "src/main.tsx", + "src/styles.css", + "src-tauri/src/lib.rs", + "src-tauri/src/knowledge_base.rs", + "src-tauri/src/gls_protocol_runtime.rs" + ] + } + ], + "candidates": [ + { + "admission_order": 1, + "candidate_number": "HLP-DONOR-CAND-0001", + "name": "native_composition_module_runtime", + "state": "ADMITTED_INSTALLED_RUNTIME_ACCEPTED", + "paths": [ + "contracts/native-composition-runtime.json", + "src/modules/native-composition", + "src-tauri/src/native_composition.rs", + "src/styles.css#native-composition-selectors-only" + ], + "style_dependency_discovered_during_admission": "Only selectors prefixed native-composition or composition- were isolated into the module directory; unrelated global donor CSS remains prohibited.", + "runtime_module_number": "HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001", + "numbered_ipc_module": "HLP-NIPC-MOD-0021", + "acceptance_evidence": "contracts/native-composition-runtime.json#current_acceptance", + "why_first": "A module needs an isolated mount, self-test, unmount and rollback boundary before content modules can be admitted safely." + }, + { + "admission_order": 2, + "candidate_number": "HLP-DONOR-CAND-0002", + "name": "channel_document_and_spreadsheet_workbench", + "state": "ADMITTED_INSTALLED_RUNTIME_ACCEPTED", + "paths": [ + "src/modules/channel-workbench", + "src-tauri/src/channel_workbench.rs", + "src/styles.css#education-engine-and-channel-spreadsheet-selectors-only" + ], + "style_dependency_discovered_during_admission": "Only the document-engine and channel-spreadsheet selector behavior was isolated into the module directory; unrelated education and global donor CSS remains prohibited.", + "runtime_module_number": "HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001", + "numbered_ipc_module": "HLP-NIPC-MOD-0022", + "acceptance_evidence": "contracts/channel-workbench-runtime.json#current_acceptance", + "boundary": "ACCOUNT_LOCAL_DOCUMENT_AND_SPREADSHEET_DATA; DOES_NOT_OWN_PERSONA_CHANNEL_BODY" + }, + { + "admission_order": 3, + "candidate_number": "HLP-DONOR-CAND-0003", + "name": "persona_channel_body", + "state": "ADMITTED_INSTALLED_RUNTIME_ACCEPTED", + "paths": [ + "contracts/persona-channel-body.json", + "src/modules/persona-channel-body", + "src-tauri/src/persona_channel_body.rs", + "src-tauri/src/channel_growth.rs" + ], + "style_dependency_discovered_during_admission": "Only persona-body selectors were isolated into the module directory; no donor global stylesheet or unrelated feature selectors were admitted.", + "runtime_module_number": "HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001", + "numbered_ipc_module": "HLP-NIPC-MOD-0023", + "acceptance_evidence": "contracts/persona-channel-body.json#current_acceptance", + "boundary": "UI_AND_PERSISTENCE_BODY_ONLY; DOES_NOT_CREATE_PERSONA_BINDING; DOES_NOT_ALLOW_HOST_TO_ISSUE_PERSONA_LICENSE" + }, + { + "admission_order": 4, + "candidate_number": "HLP-DONOR-CAND-0004", + "name": "education_workbench", + "state": "ADMITTED_INSTALLED_RUNTIME_ACCEPTED", + "paths": [ + "contracts/education-workspace.json", + "src/modules/education-workspace", + "src-tauri/src/education_translation.rs", + "src-tauri/src/education_workspace.rs" + ], + "runtime_module_number": "HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001", + "numbered_ipc_module": "HLP-NIPC-MOD-0024", + "acceptance_evidence": "contracts/education-workspace.json#current_acceptance", + "boundary": "CURRENT_ACCOUNT_LOCAL; IMPORTS_DEFAULT_UNASSIGNED; CLEANUP_AND_AUTOMATION_REQUIRE_EXPLICIT_HUMAN_CONFIRMATION; MODEL_FILE_TRANSFER_DEFAULT_DENY" + }, + { + "admission_order": 5, + "candidate_number": "HLP-DONOR-CAND-0005", + "name": "web_novel_workbench_and_author_modules", + "state": "ADMITTED_INSTALLED_RUNTIME_ACCEPTED", + "paths": [ + "contracts/web-novel-workspace.json", + "contracts/web-novel-module-marketplace-plan.json", + "src/modules/web-novel/WebNovelWorkspace.tsx", + "src/modules/web-novel/AuthorModuleCenter.tsx", + "src/modules/web-novel/AuthorWritingSidecar.tsx", + "src-tauri/src/web_novel_workspace.rs", + "src-tauri/src/web_novel_import.rs", + "src-tauri/src/web_novel_author.rs", + "src-tauri/src/web_novel_modules.rs" + ], + "runtime_module_numbers": ["HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001", "HLP-MOD-OFFICIAL-WEB-NOVEL-OUTLINE-0001", "HLP-MOD-OFFICIAL-WEB-NOVEL-GRID-0001", "HLP-MOD-OFFICIAL-WEB-NOVEL-STORYWORLD-0001", "HLP-MOD-OFFICIAL-WEB-NOVEL-DELIVERY-0001"], + "numbered_ipc_modules": ["HLP-NIPC-MOD-0025", "HLP-NIPC-MOD-0026", "HLP-NIPC-MOD-0027", "HLP-NIPC-MOD-0028", "HLP-NIPC-MOD-0029"], + "acceptance_evidence": "contracts/web-novel-workspace.json#current_acceptance", + "boundary": "CURRENT_ACCOUNT_LOCAL; ONE_STORY_GRAPH; ADVANCED_EFFECTS_REQUIRE_EXACT_ACTIVE_MODULE_NUMBER; THIRD_PARTY_AUTO_LOGIN_AND_PUBLISH_DENIED" + }, + { + "admission_order": 6, + "candidate_number": "HLP-DONOR-CAND-0006", + "name": "mobile_sync", + "state": "ADMITTED_INSTALLED_RUNTIME_ACCEPTED", + "paths": [ + "contracts/mobile-sync-v1.json", + "src/MobileSyncPanel.tsx", + "src-tauri/src/mobile_sync.rs" + ], + "runtime_module_number": "HLP-MOD-OFFICIAL-MOBILE-SYNC-0001", + "numbered_ipc_module": "HLP-NIPC-MOD-0030", + "acceptance_evidence": "contracts/mobile-sync-v1.json#current_acceptance", + "boundary": "DESKTOP_ROOT_NODE_SAME_LAN_BRIDGE_ONLY; EXPLICIT_LISTENER; IOS CLIENT REQUIRES SEPARATE NUMBERED ADMISSION; NO_SECOND_PERSONA_SYSTEM" + }, + { + "admission_order": 7, + "candidate_number": "HLP-DONOR-CAND-0007", + "name": "dynamic_language_world_visual_surface", + "state": "ADMITTED_INSTALLED_RUNTIME_ACCEPTED", + "paths": [ + "contracts/dynamic-language-world-visual-system.json", + "src/modules/qoder-surface/StarlakeSurface.tsx", + "src/modules/qoder-surface/starlake-surface.css", + "src/modules/qoder-surface/TraditionalSurface.tsx", + "src/modules/qoder-surface/traditional-surface.css", + "src/modules/qoder-surface/visual-balance.ts", + "src-tauri/src/world_climate.rs" + ], + "runtime_module_number": "HLP-MOD-OFFICIAL-DYNAMIC-WORLD-SURFACE-0001", + "numbered_ipc_module": "HLP-NIPC-MOD-0031", + "acceptance_evidence": "contracts/dynamic-language-world-visual-system.json#current_acceptance", + "boundary": "QODER_LOCKED_VISUAL_SOURCE_PLUS_REALITY_TIME_AND_PUBLIC_WEATHER_PROJECTION; ALL_FUNCTION_ROUTES_REMAIN_NUMBERED; IDLE_STATIC; NO_FAKE_WEATHER_BROADCAST_OR_METRICS" + }, + { + "admission_order": 8, + "candidate_number": "HLP-DONOR-CAND-0008", + "name": "ios_numbered_remote_body_entry", + "state": "ADMITTED_SOURCE_SIMULATOR_AND_DEVELOPMENT_SIGNED_IPA_ACCEPTED", + "paths": [ + "contracts/ios-mobile-client-v1.json", + "mobile/ios/project.yml", + "mobile/ios/Sources", + "mobile/ios/Tests" + ], + "client_number": "HLP-IOS-CLIENT-001", + "desktop_runtime_module_number": "HLP-MOD-OFFICIAL-MOBILE-SYNC-0001", + "numbered_ipc_module": "HLP-NIPC-MOD-0030", + "acceptance_evidence": "contracts/ios-mobile-client-v1.json#acceptance", + "boundary": "IPHONE IS A THIN REMOTE BODY ENTRY; DESKTOP REMAINS ROOT NODE; SAME LAN EXPLICIT PAIR AND MANUAL SYNC ONLY; NO SECOND PERSONA SYSTEM; REAL IPHONE END TO END NOT CLAIMED" + } + ], + "admission_gate": [ + "EXTRACT_ONLY_DECLARED_CANDIDATE_PATHS", + "REVIEW_SOURCE_PROVENANCE_AND_LICENSE", + "DEFINE_DATA_PERMISSION_AND_RESOURCE_BOUNDARY", + "ALLOCATE_NUMBERED_IPC_MODULE_TARGET_AND_OPERATION_COORDINATES", + "IMPLEMENT_ADAPTER_WITHOUT_RAW_TAURI_INVOKE", + "ADD_UNIT_INTEGRATION_AND_NEGATIVE_ROUTE_TESTS", + "BUILD_FROM_CLEAN_OFFICIAL_BASE", + "INSTALL_IN_ISOLATED_RUNTIME", + "VERIFY_MOUNT_SELF_TEST_RESTART_UNMOUNT_AND_ROLLBACK", + "WRITE_HASH_CHAINED_RUNTIME_RECEIPT", + "ONLY_THEN_ASSIGN_PERMANENT_MODULE_NUMBER" + ], + "hot_install_boundary": { + "runtime_state": "SIGNED_DECLARATIVE_PACKAGE_ENGINE_IMPLEMENTED", + "runtime_contract": "contracts/module-package-runtime.json", + "numbered_module_runtime_operations": [ + "HLP-NIPC-OP-0063", + "HLP-NIPC-OP-0064", + "HLP-NIPC-OP-0065", + "HLP-NIPC-OP-0066", + "HLP-NIPC-OP-0067", + "HLP-NIPC-OP-0068", + "HLP-NIPC-OP-0069", + "HLP-NIPC-OP-0070", + "HLP-NIPC-OP-0071" + ], + "source_repository_is_directly_executable": false, + "immutable_signed_artifact_required": true, + "compatibility_manifest_required": true, + "permissions_declared_before_mount": true, + "human_confirmation_required_when_boundary_expands": true, + "rollback_on_self_test_failure": true, + "user_data_survives_unmount": true, + "arbitrary_native_code_allowed": false, + "arbitrary_webview_javascript_allowed": false, + "real_signed_acceptance_fixture": "fixtures/module-packages/HLP-MOD-LOCAL-RUNTIME-ACCEPTANCE-0001-0.1.0.ghmod" + } +} diff --git a/product-source/hololake-native-desktop/contracts/module-package-runtime.json b/product-source/hololake-native-desktop/contracts/module-package-runtime.json new file mode 100644 index 000000000..a00528451 --- /dev/null +++ b/product-source/hololake-native-desktop/contracts/module-package-runtime.json @@ -0,0 +1,52 @@ +{ + "schema": "hololake.module-package-runtime/v1", + "record_id": "HLP-MODULE-PACKAGE-RUNTIME-001", + "protocols": ["GLS-0710", "GLS-0803", "GLS-0819", "GLS-0310"], + "package_schema": "hololake.module-package/v1", + "signature": { + "algorithm": "MINISIGN_ED25519", + "trust_source": "src-tauri/release-trust.json", + "signature_is_detached": true, + "package_bytes_are_signed_exactly": true, + "source_repository_is_executable": false + }, + "number_classes": { + "official": "HLP-MOD-OFFICIAL-*", + "private_channel": "HLP-MOD-LOCAL-*", + "candidate_number_is_runtime_number": false + }, + "host": { + "version": "0.5.0", + "maximum_package_bytes": 16777216, + "arbitrary_native_code": false, + "arbitrary_webview_javascript": false, + "declarative_payload_only": true + }, + "registered_adapters": [ + "native-composition-v1", + "channel-workbench-v1", + "persona-channel-body-v1", + "education-workbench-v1", + "web-novel-workbench-v1", + "mobile-sync-v1", + "dynamic-language-world-surface-v1" + ], + "lifecycle": [ + "INSTALLED_DORMANT", + "MOUNTED_PENDING_SELF_TEST", + "ACTIVE", + "DORMANT", + "ROLLBACK_PENDING_SELF_TEST", + "FAILED_CLOSED" + ], + "rules": { + "verified_human_route_required": true, + "permission_expansion_requires_human_confirmation": true, + "mount_never_executes_package_code": true, + "self_test_before_active": true, + "failed_self_test_rolls_back": true, + "restart_reconstructs_from_sqlite": true, + "unmount_preserves_user_data": true, + "every_mutation_writes_hash_chained_receipt": true + } +} diff --git a/product-source/hololake-native-desktop/contracts/native-composition-runtime.json b/product-source/hololake-native-desktop/contracts/native-composition-runtime.json index be9c2297b..b1070a551 100644 --- a/product-source/hololake-native-desktop/contracts/native-composition-runtime.json +++ b/product-source/hololake-native-desktop/contracts/native-composition-runtime.json @@ -1,8 +1,15 @@ { "schema": "hololake.native-composition-runtime/v1", "record_id": "HLP-NATIVE-COMPOSITION-001", - "state": "INSTALLED_RUNTIME_ACCEPTED", - "intent_source": "docs/adr/0008-native-composition-and-human-projection.md", + "candidate_number": "HLP-DONOR-CAND-0001", + "runtime_module_number": "HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001", + "state": "ADMITTED_INSTALLED_RUNTIME_ACCEPTED", + "provenance": { + "donor": "0.4.1 read-only candidate paths", + "donor_contract_state": "INSTALLED_RUNTIME_ACCEPTED", + "donor_apple_notarization": "NOT_PERFORMED_MISSING_CREDENTIALS", + "current_mainline_reuses_bulk_source": false + }, "internal_object": { "schema": "hololake.native-object/v1", "encoding": "BOUNDED_JSON", @@ -18,7 +25,7 @@ "registered_kinds": ["SOURCE", "TRANSFORM", "PROJECTION"], "typed_ports_required": true, "determinism_declared": true, - "authority_declared": true + "authority": "CURRENT_AUTHENTICATED_ACCOUNT_READ_ONLY" }, "recipe": { "schema": "hololake.composition-recipe/v1", @@ -28,38 +35,32 @@ "native_validation_required": true }, "human_projection": { - "schema": "hololake.human-projection/v1", "views": ["DASHBOARD", "COMPARISON", "VERTICAL_BAR", "CLASSIFICATION", "TABLE"], "shares_one_execution_result": true, "owns_source_data": false, "direct_write_authority": false }, - "education_stage_one": { - "channel_id": "GH-EDU-INIT-001", - "module_id": "EDU-COMPOSITION", - "source": "CURRENT_AUTHENTICATED_ACCOUNTS_REAL_KNOWLEDGE_CATALOG", - "selectable_dimensions": ["SOURCE", "TOP_LEVEL_FOLDER"], - "selectable_measures": ["DOCUMENT_COUNT", "TOTAL_BYTES", "DUPLICATE_COUNT"], - "hardcoded_sample_may_satisfy_acceptance": false, - "installed_runtime_acceptance_required": true + "admission": { + "signed_package_required": true, + "adapter": "native-composition-v1", + "numbered_routes_required": true, + "active_module_required_before_execution": true, + "installed_runtime_acceptance_required": true, + "hardcoded_sample_may_satisfy_acceptance": false }, - "write_boundary": { - "stage_one": "READ_ONLY", - "future_write_requires": ["CAPABILITY", "PREVIEW", "HUMAN_CONFIRMATION", "REVISION_LOCK", "RECEIPT"] - }, - "acceptance": { - "observed_at": "2026-08-18T01:49:06+08:00", - "installed_binary_sha256": "2a26acae4a706c0ff9919b85f77826a6ca71b650ec22209befd30cd1456c92f3", - "route": "MAIN_WORLD_TO_BRANCH_DOMAIN_TO_EDUCATION_TO_NATIVE_COMPOSITION", + "current_acceptance": { + "observed_at": "2026-08-19T01:49:35+08:00", + "signed_debug_binary_sha256": "0bff08787950b69ef0a1565fc32a7537dae53da184c4a1a4fb30ca62eca9f48c", + "developer_id_team": "825A9L3G7Q", + "module_package_sha256": "dfb343019810f8f845ab415259be2eccbe63d88a03bf26a277950610edb64329", + "account_number_observed": "ICE-GL∞", "real_native_rows": 129, "real_native_total_bytes_display": "1.0 MB", "real_groups": 14, - "all_five_views_observed": true, - "three_view_recomposition_observed": true, - "measure_switch_observed": "DOCUMENT_COUNT_TO_TOTAL_BYTES_AND_BACK", - "projection_bound_to_execution_receipt": true, - "developer_id_signature": "VALID", - "apple_notarization": "NOT_PERFORMED_MISSING_CREDENTIALS", - "public_release": "NOT_PUBLISHED" + "measure_switch_observed": "DOCUMENT_COUNT_TO_TOTAL_BYTES", + "install_receipts": ["INSTALL", "MOUNT", "SELF_TEST_PASS"], + "restart_recovery": "ACTIVE_AND_REEXECUTED", + "execution_receipt_prefixes": ["0449582510a5", "092febd75c38"], + "apple_notarization": "FINAL_RELEASE_PENDING_ALL_MODULES" } } diff --git a/product-source/hololake-native-desktop/contracts/nearby-ai-discovery.json b/product-source/hololake-native-desktop/contracts/nearby-ai-discovery.json index fba038cea..97bf075bf 100644 --- a/product-source/hololake-native-desktop/contracts/nearby-ai-discovery.json +++ b/product-source/hololake-native-desktop/contracts/nearby-ai-discovery.json @@ -11,7 +11,7 @@ "linux": "USER_PRIVATE_UNIX_SOCKET", "windows": "USER_PRIVATE_NAMED_PIPE" }, - "terminal_link_protocol": "HOLOLAKE_TERMINAL_LINK/2", + "terminal_link_protocol": "HOLOLAKE_TERMINAL_LINK/3", "copy_large_invitation_required": false, "network_required": false }, diff --git a/product-source/hololake-native-desktop/contracts/numbered-ipc-registry.json b/product-source/hololake-native-desktop/contracts/numbered-ipc-registry.json new file mode 100644 index 000000000..90233331f --- /dev/null +++ b/product-source/hololake-native-desktop/contracts/numbered-ipc-registry.json @@ -0,0 +1,2002 @@ +{ + "schema": "hololake.numbered-ipc-registry/v1", + "record_id": "HLP-NUMBERED-IPC-ROOT-001", + "source": { + "baseline_commit": "7a14c06e41587c4c5a08a43bb4f5a172fa61b7fa", + "gls_runtime_manifest": "HLP-GLS-RUNTIME-MANIFEST-002", + "gls_native_kernel": "HLP-GLS-NATIVE-RUNTIME-KERNEL-001", + "numbering_authority_map": "GH-IDENTITY-AUTHORITY-MAP-001", + "human_language_anchor": "source://codex-current-dialogue/2026-08-18/bingshuo-number-is-path-and-mismatched-routes-are-unrecognizable" + }, + "runtime": { + "public_tauri_command": "numbered_ipc", + "caller_number": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "caller_number_subject_kind": "PHYSICAL_WEBVIEW_ENTRY_NOT_PERSONA_IDENTITY", + "caller_number_grants_persona_binding": false, + "protocol_version": "HLP-NIPC-v1", + "legacy_direct_commands_allowed": false, + "grants_issued_server_side": true, + "grant_single_use": true, + "payload_bound_grants": true, + "authority_binding_issued_server_side": true, + "verified_human_binding_required_for_protected_routes": true, + "persona_binding_claimed": false, + "user_channel_body_binding_stage": "SEPARATE_EVIDENCE_LAYER_NOT_YET_CLAIMED", + "maximum_grant_ttl_ms": 30000, + "unknown_route": "FAIL_CLOSED", + "mismatched_coordinate": "FAIL_CLOSED", + "expired_or_replayed_grant": "FAIL_CLOSED", + "receipt_required": true + }, + "payload_contract": { + "unknown_or_unclassified_alias": "FAIL_CLOSED_BEFORE_GRANT", + "unknown_top_level_field": "FAIL_CLOSED_BEFORE_GRANT", + "empty_object_aliases": [ + "get_hololake_home_status", + "check_hololake_update", + "get_hololake_release_recovery_status", + "confirm_hololake_release_health", + "rollback_hololake_update", + "get_nearby_ai_discovery", + "get_gls_protocol_runtime", + "get_gls_protocol_kernel", + "get_personal_channel_snapshot", + "start_persona_time_authority", + "get_beijing_time_coordinate", + "get_guanghu_era_timeline", + "get_knowledge_snapshot", + "select_and_import_knowledge_folder", + "print_knowledge_document", + "get_code_channel_snapshot", + "select_local_code_channel", + "select_pncc_repository_candidate", + "query_jd_pncc_server_projection", + "check_code_repo_login", + "get_enterprise_entry", + "ensure_enterprise_work_channel", + "sign_out_code_repo_login", + "get_user_pncc_channel", + "ensure_user_pncc_channel", + "get_zero_core_numbering_kernel", + "zero_point_verify", + "zero_point_sync", + "zero_point_status", + "get_module_runtime_snapshot", + "get_bundled_module_catalog", + "get_native_composition_module_registry", + "get_channel_workbench_snapshot", + "get_persona_channel_body", + "get_channel_growth_snapshot", + "get_education_workspace_snapshot", + "import_education_tables_from_dialog", + "get_education_recognition_capability", + "get_web_novel_workspace_snapshot", + "inspect_web_novel_document_from_dialog", + "start_mobile_sync", + "get_mobile_sync_status", + "rotate_mobile_pairing", + "stop_mobile_sync", + "get_mobile_sync_snapshot", + "get_world_climate" + ], + "input_wrapper_aliases": [ + "confirm_hololake_update_install", + "issue_direct_local_discovery_ticket", + "open_direct_local_session", + "resume_direct_local_session", + "append_direct_local_session_event", + "heartbeat_direct_local_session", + "decide_gls_protocol", + "compile_gls_hldp_program", + "acquire_development_write_lane", + "inspect_development_write_lane", + "release_development_write_lane", + "initialize_personal_channel", + "create_personal_channel_task", + "transition_personal_channel_task", + "issue_persona_time_ticket", + "read_knowledge_document", + "search_knowledge", + "save_knowledge_document", + "export_knowledge_document", + "create_knowledge_document", + "delete_knowledge_document", + "delete_knowledge_folder", + "clone_code_channel", + "browse_code_channel", + "read_code_channel_file", + "inspect_mounted_pncc_repository", + "confirm_pncc_repository_mount", + "query_pncc_receipt_projection", + "zero_point_bind", + "verify_module_package", + "install_module_package", + "mount_module", + "self_test_module", + "unmount_module", + "rollback_module", + "activate_bundled_module", + "execute_knowledge_native_composition", + "save_channel_document", + "save_channel_spreadsheet", + "register_trial_persona", + "delete_trial_persona", + "accept_persona_language_contract", + "append_persona_language", + "record_channel_growth_event", + "update_channel_growth_sharing", + "create_education_document", + "read_education_document", + "save_education_document", + "archive_education_document", + "create_education_table", + "read_education_table", + "save_education_table", + "archive_education_table", + "assign_imported_table_to_education", + "export_education_table_to_dialog", + "create_education_automation_rule", + "save_education_automation_rule", + "archive_education_automation_rule", + "preview_education_automation_rule", + "execute_education_automation_rule", + "create_web_novel_work", + "read_web_novel_work", + "save_web_novel_work", + "create_web_novel_volume", + "create_web_novel_chapter", + "read_web_novel_chapter", + "save_web_novel_chapter", + "transition_web_novel_chapter", + "create_web_novel_checkpoint", + "restore_web_novel_checkpoint", + "upsert_web_novel_story_entity", + "create_web_novel_story_relation", + "upsert_web_novel_foreshadow", + "create_web_novel_review_note", + "resolve_web_novel_review_note", + "save_web_novel_metric", + "run_web_novel_continuity_audit", + "export_web_novel_markdown", + "commit_web_novel_document_import", + "get_web_novel_author_snapshot", + "record_web_novel_writing_activity", + "create_web_novel_inspiration", + "set_web_novel_inspiration_status", + "search_web_novel_full_text", + "format_web_novel_chapter", + "format_web_novel_work", + "upsert_web_novel_shot", + "get_web_novel_author_module_data", + "upsert_web_novel_author_scene", + "upsert_web_novel_author_beat", + "upsert_web_novel_story_field_definition", + "upsert_web_novel_story_field_value", + "upsert_web_novel_timeline_event", + "link_web_novel_scene_entity", + "restore_web_novel_chapter_version", + "export_web_novel_author_delivery", + "revoke_mobile_sync_device" + ], + "direct_field_aliases": { + "perform_code_repo_login": [ + "username", + "password" + ], + "change_first_login_password": [ + "username", + "currentPassword", + "newPassword" + ], + "confirm_enterprise_persona_relationship": [ + "decision", + "idempotencyKey" + ], + "submit_enterprise_responsibility_receipt": [ + "decision", + "note", + "responsibilityVersion", + "idempotencyKey" + ] + } + }, + "channels": [ + { + "id": "HLP-NIPC-CH-0001" + }, + { + "id": "HLP-NIPC-CH-0003" + }, + { + "id": "HLP-NIPC-CH-0004" + }, + { + "id": "HLP-NIPC-CH-0002" + }, + { + "id": "HLP-NIPC-CH-0005" + }, + { + "id": "HLP-NIPC-CH-0006" + }, + { + "id": "HLP-NIPC-CH-0007" + } + ], + "modules": [ + { + "module_number": "HLP-NIPC-MOD-0001", + "target_number": "HLP-NIPC-TGT-0001", + "internal_name": "home_status" + }, + { + "module_number": "HLP-NIPC-MOD-0002", + "target_number": "HLP-NIPC-TGT-0002", + "internal_name": "release_update" + }, + { + "module_number": "HLP-NIPC-MOD-0003", + "target_number": "HLP-NIPC-TGT-0003", + "internal_name": "direct_local_session" + }, + { + "module_number": "HLP-NIPC-MOD-0004", + "target_number": "HLP-NIPC-TGT-0004", + "internal_name": "direct_local_broker" + }, + { + "module_number": "HLP-NIPC-MOD-0005", + "target_number": "HLP-NIPC-TGT-0005", + "internal_name": "gls_protocol_runtime" + }, + { + "module_number": "HLP-NIPC-MOD-0006", + "target_number": "HLP-NIPC-TGT-0006", + "internal_name": "gls_protocol_kernel" + }, + { + "module_number": "HLP-NIPC-MOD-0007", + "target_number": "HLP-NIPC-TGT-0007", + "internal_name": "local_development_bridge" + }, + { + "module_number": "HLP-NIPC-MOD-0008", + "target_number": "HLP-NIPC-TGT-0008", + "internal_name": "personal_channel" + }, + { + "module_number": "HLP-NIPC-MOD-0009", + "target_number": "HLP-NIPC-TGT-0009", + "internal_name": "persona_time_authority" + }, + { + "module_number": "HLP-NIPC-MOD-0010", + "target_number": "HLP-NIPC-TGT-0010", + "internal_name": "knowledge_base" + }, + { + "module_number": "HLP-NIPC-MOD-0011", + "target_number": "HLP-NIPC-TGT-0011", + "internal_name": "code_channel" + }, + { + "module_number": "HLP-NIPC-MOD-0012", + "target_number": "HLP-NIPC-TGT-0012", + "internal_name": "pncc_repository_binding" + }, + { + "module_number": "HLP-NIPC-MOD-0013", + "target_number": "HLP-NIPC-TGT-0013", + "internal_name": "pncc_receipt_projection" + }, + { + "module_number": "HLP-NIPC-MOD-0014", + "target_number": "HLP-NIPC-TGT-0014", + "internal_name": "pncc_server_projection" + }, + { + "module_number": "HLP-NIPC-MOD-0015", + "target_number": "HLP-NIPC-TGT-0015", + "internal_name": "code_repo_login" + }, + { + "module_number": "HLP-NIPC-MOD-0016", + "target_number": "HLP-NIPC-TGT-0016", + "internal_name": "enterprise_work_channel" + }, + { + "module_number": "HLP-NIPC-MOD-0017", + "target_number": "HLP-NIPC-TGT-0017", + "internal_name": "user_pncc_channel" + }, + { + "module_number": "HLP-NIPC-MOD-0018", + "target_number": "HLP-NIPC-TGT-0018", + "internal_name": "zero_core_numbering" + }, + { + "module_number": "HLP-NIPC-MOD-0019", + "target_number": "HLP-NIPC-TGT-0019", + "internal_name": "zero_point" + }, + { + "module_number": "HLP-NIPC-MOD-0020", + "target_number": "HLP-NIPC-TGT-0020", + "internal_name": "module_package_runtime" + }, + { + "module_number": "HLP-NIPC-MOD-0021", + "target_number": "HLP-NIPC-TGT-0021", + "internal_name": "native_composition" + }, + { + "module_number": "HLP-NIPC-MOD-0022", + "target_number": "HLP-NIPC-TGT-0022", + "internal_name": "channel_workbench" + }, + { + "module_number": "HLP-NIPC-MOD-0023", + "target_number": "HLP-NIPC-TGT-0023", + "internal_name": "persona_channel_body" + }, + { + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "internal_name": "education_workspace" + }, + { + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "internal_name": "web_novel_workbench" + }, + { + "module_number": "HLP-NIPC-MOD-0026", + "target_number": "HLP-NIPC-TGT-0026", + "internal_name": "web_novel_outline" + }, + { + "module_number": "HLP-NIPC-MOD-0027", + "target_number": "HLP-NIPC-TGT-0027", + "internal_name": "web_novel_story_grid" + }, + { + "module_number": "HLP-NIPC-MOD-0028", + "target_number": "HLP-NIPC-TGT-0028", + "internal_name": "web_novel_storyworld" + }, + { + "module_number": "HLP-NIPC-MOD-0029", + "target_number": "HLP-NIPC-TGT-0029", + "internal_name": "web_novel_delivery" + }, + { + "module_number": "HLP-NIPC-MOD-0030", + "target_number": "HLP-NIPC-TGT-0030", + "internal_name": "mobile_sync" + }, + { + "module_number": "HLP-NIPC-MOD-0031", + "target_number": "HLP-NIPC-TGT-0031", + "internal_name": "world_climate" + } + ], + "operations": [ + { + "operation_number": "HLP-NIPC-OP-0001", + "alias": "get_hololake_home_status", + "handler": "home_status::get_hololake_home_status", + "channel_number": "HLP-NIPC-CH-0001", + "module_number": "HLP-NIPC-MOD-0001", + "target_number": "HLP-NIPC-TGT-0001", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_hololake_home_status/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0002", + "alias": "check_hololake_update", + "handler": "release_update::check_hololake_update", + "channel_number": "HLP-NIPC-CH-0001", + "module_number": "HLP-NIPC-MOD-0002", + "target_number": "HLP-NIPC-TGT-0002", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/check_hololake_update/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0003", + "alias": "confirm_hololake_update_install", + "handler": "release_update::confirm_hololake_update_install", + "channel_number": "HLP-NIPC-CH-0001", + "module_number": "HLP-NIPC-MOD-0002", + "target_number": "HLP-NIPC-TGT-0002", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/confirm_hololake_update_install/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0004", + "alias": "get_hololake_release_recovery_status", + "handler": "release_update::get_hololake_release_recovery_status", + "channel_number": "HLP-NIPC-CH-0001", + "module_number": "HLP-NIPC-MOD-0002", + "target_number": "HLP-NIPC-TGT-0002", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_hololake_release_recovery_status/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0005", + "alias": "confirm_hololake_release_health", + "handler": "release_update::confirm_hololake_release_health", + "channel_number": "HLP-NIPC-CH-0001", + "module_number": "HLP-NIPC-MOD-0002", + "target_number": "HLP-NIPC-TGT-0002", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/confirm_hololake_release_health/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0006", + "alias": "rollback_hololake_update", + "handler": "release_update::rollback_hololake_update", + "channel_number": "HLP-NIPC-CH-0001", + "module_number": "HLP-NIPC-MOD-0002", + "target_number": "HLP-NIPC-TGT-0002", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/rollback_hololake_update/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0007", + "alias": "issue_direct_local_discovery_ticket", + "handler": "direct_local_session::issue_direct_local_discovery_ticket", + "channel_number": "HLP-NIPC-CH-0003", + "module_number": "HLP-NIPC-MOD-0003", + "target_number": "HLP-NIPC-TGT-0003", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/issue_direct_local_discovery_ticket/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0008", + "alias": "open_direct_local_session", + "handler": "direct_local_session::open_direct_local_session", + "channel_number": "HLP-NIPC-CH-0003", + "module_number": "HLP-NIPC-MOD-0003", + "target_number": "HLP-NIPC-TGT-0003", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/open_direct_local_session/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0009", + "alias": "resume_direct_local_session", + "handler": "direct_local_session::resume_direct_local_session", + "channel_number": "HLP-NIPC-CH-0003", + "module_number": "HLP-NIPC-MOD-0003", + "target_number": "HLP-NIPC-TGT-0003", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/resume_direct_local_session/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0010", + "alias": "append_direct_local_session_event", + "handler": "direct_local_session::append_direct_local_session_event", + "channel_number": "HLP-NIPC-CH-0003", + "module_number": "HLP-NIPC-MOD-0003", + "target_number": "HLP-NIPC-TGT-0003", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/append_direct_local_session_event/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0011", + "alias": "heartbeat_direct_local_session", + "handler": "direct_local_session::heartbeat_direct_local_session", + "channel_number": "HLP-NIPC-CH-0003", + "module_number": "HLP-NIPC-MOD-0003", + "target_number": "HLP-NIPC-TGT-0003", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/heartbeat_direct_local_session/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0012", + "alias": "get_nearby_ai_discovery", + "handler": "direct_local_broker::get_nearby_ai_discovery", + "channel_number": "HLP-NIPC-CH-0003", + "module_number": "HLP-NIPC-MOD-0004", + "target_number": "HLP-NIPC-TGT-0004", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_nearby_ai_discovery/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0013", + "alias": "get_gls_protocol_runtime", + "handler": "gls_protocol_runtime::get_gls_protocol_runtime", + "channel_number": "HLP-NIPC-CH-0001", + "module_number": "HLP-NIPC-MOD-0005", + "target_number": "HLP-NIPC-TGT-0005", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_gls_protocol_runtime/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0014", + "alias": "get_gls_protocol_kernel", + "handler": "gls_protocol_kernel::get_gls_protocol_kernel", + "channel_number": "HLP-NIPC-CH-0001", + "module_number": "HLP-NIPC-MOD-0006", + "target_number": "HLP-NIPC-TGT-0006", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_gls_protocol_kernel/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0015", + "alias": "decide_gls_protocol", + "handler": "gls_protocol_kernel::decide_gls_protocol", + "channel_number": "HLP-NIPC-CH-0001", + "module_number": "HLP-NIPC-MOD-0006", + "target_number": "HLP-NIPC-TGT-0006", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/decide_gls_protocol/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0016", + "alias": "compile_gls_hldp_program", + "handler": "gls_protocol_kernel::compile_gls_hldp_program", + "channel_number": "HLP-NIPC-CH-0001", + "module_number": "HLP-NIPC-MOD-0006", + "target_number": "HLP-NIPC-TGT-0006", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/compile_gls_hldp_program/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0017", + "alias": "acquire_development_write_lane", + "handler": "local_development_bridge::acquire_development_write_lane", + "channel_number": "HLP-NIPC-CH-0004", + "module_number": "HLP-NIPC-MOD-0007", + "target_number": "HLP-NIPC-TGT-0007", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/acquire_development_write_lane/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0018", + "alias": "inspect_development_write_lane", + "handler": "local_development_bridge::inspect_development_write_lane", + "channel_number": "HLP-NIPC-CH-0004", + "module_number": "HLP-NIPC-MOD-0007", + "target_number": "HLP-NIPC-TGT-0007", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/inspect_development_write_lane/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0019", + "alias": "release_development_write_lane", + "handler": "local_development_bridge::release_development_write_lane", + "channel_number": "HLP-NIPC-CH-0004", + "module_number": "HLP-NIPC-MOD-0007", + "target_number": "HLP-NIPC-TGT-0007", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/release_development_write_lane/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0020", + "alias": "get_personal_channel_snapshot", + "handler": "personal_channel::get_personal_channel_snapshot", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0008", + "target_number": "HLP-NIPC-TGT-0008", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_personal_channel_snapshot/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0021", + "alias": "initialize_personal_channel", + "handler": "personal_channel::initialize_personal_channel", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0008", + "target_number": "HLP-NIPC-TGT-0008", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/initialize_personal_channel/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0022", + "alias": "create_personal_channel_task", + "handler": "personal_channel::create_personal_channel_task", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0008", + "target_number": "HLP-NIPC-TGT-0008", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/create_personal_channel_task/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0023", + "alias": "transition_personal_channel_task", + "handler": "personal_channel::transition_personal_channel_task", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0008", + "target_number": "HLP-NIPC-TGT-0008", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/transition_personal_channel_task/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0024", + "alias": "issue_persona_time_ticket", + "handler": "persona_time_authority::issue_persona_time_ticket", + "channel_number": "HLP-NIPC-CH-0001", + "module_number": "HLP-NIPC-MOD-0009", + "target_number": "HLP-NIPC-TGT-0009", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/issue_persona_time_ticket/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0025", + "alias": "start_persona_time_authority", + "handler": "persona_time_authority::start_persona_time_authority", + "channel_number": "HLP-NIPC-CH-0001", + "module_number": "HLP-NIPC-MOD-0009", + "target_number": "HLP-NIPC-TGT-0009", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/start_persona_time_authority/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0026", + "alias": "get_beijing_time_coordinate", + "handler": "persona_time_authority::get_beijing_time_coordinate", + "channel_number": "HLP-NIPC-CH-0001", + "module_number": "HLP-NIPC-MOD-0009", + "target_number": "HLP-NIPC-TGT-0009", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_beijing_time_coordinate/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0027", + "alias": "get_guanghu_era_timeline", + "handler": "persona_time_authority::get_guanghu_era_timeline", + "channel_number": "HLP-NIPC-CH-0001", + "module_number": "HLP-NIPC-MOD-0009", + "target_number": "HLP-NIPC-TGT-0009", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_guanghu_era_timeline/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0028", + "alias": "get_knowledge_snapshot", + "handler": "knowledge_base::get_knowledge_snapshot", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0010", + "target_number": "HLP-NIPC-TGT-0010", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_knowledge_snapshot/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0029", + "alias": "read_knowledge_document", + "handler": "knowledge_base::read_knowledge_document", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0010", + "target_number": "HLP-NIPC-TGT-0010", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/read_knowledge_document/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0030", + "alias": "search_knowledge", + "handler": "knowledge_base::search_knowledge", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0010", + "target_number": "HLP-NIPC-TGT-0010", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/search_knowledge/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0031", + "alias": "save_knowledge_document", + "handler": "knowledge_base::save_knowledge_document", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0010", + "target_number": "HLP-NIPC-TGT-0010", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/save_knowledge_document/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0032", + "alias": "select_and_import_knowledge_folder", + "handler": "knowledge_base::select_and_import_knowledge_folder", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0010", + "target_number": "HLP-NIPC-TGT-0010", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/select_and_import_knowledge_folder/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0033", + "alias": "export_knowledge_document", + "handler": "knowledge_base::export_knowledge_document", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0010", + "target_number": "HLP-NIPC-TGT-0010", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/export_knowledge_document/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0034", + "alias": "create_knowledge_document", + "handler": "knowledge_base::create_knowledge_document", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0010", + "target_number": "HLP-NIPC-TGT-0010", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/create_knowledge_document/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0035", + "alias": "delete_knowledge_document", + "handler": "knowledge_base::delete_knowledge_document", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0010", + "target_number": "HLP-NIPC-TGT-0010", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/delete_knowledge_document/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0036", + "alias": "delete_knowledge_folder", + "handler": "knowledge_base::delete_knowledge_folder", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0010", + "target_number": "HLP-NIPC-TGT-0010", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/delete_knowledge_folder/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0037", + "alias": "print_knowledge_document", + "handler": "knowledge_base::print_knowledge_document", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0010", + "target_number": "HLP-NIPC-TGT-0010", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/print_knowledge_document/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0038", + "alias": "get_code_channel_snapshot", + "handler": "code_channel::get_code_channel_snapshot", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0011", + "target_number": "HLP-NIPC-TGT-0011", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_code_channel_snapshot/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0039", + "alias": "clone_code_channel", + "handler": "code_channel::clone_code_channel", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0011", + "target_number": "HLP-NIPC-TGT-0011", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/clone_code_channel/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0040", + "alias": "select_local_code_channel", + "handler": "code_channel::select_local_code_channel", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0011", + "target_number": "HLP-NIPC-TGT-0011", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/select_local_code_channel/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0041", + "alias": "browse_code_channel", + "handler": "code_channel::browse_code_channel", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0011", + "target_number": "HLP-NIPC-TGT-0011", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/browse_code_channel/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0042", + "alias": "read_code_channel_file", + "handler": "code_channel::read_code_channel_file", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0011", + "target_number": "HLP-NIPC-TGT-0011", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/read_code_channel_file/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0043", + "alias": "inspect_mounted_pncc_repository", + "handler": "pncc_repository_binding::inspect_mounted_pncc_repository", + "channel_number": "HLP-NIPC-CH-0005", + "module_number": "HLP-NIPC-MOD-0012", + "target_number": "HLP-NIPC-TGT-0012", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/inspect_mounted_pncc_repository/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0044", + "alias": "select_pncc_repository_candidate", + "handler": "pncc_repository_binding::select_pncc_repository_candidate", + "channel_number": "HLP-NIPC-CH-0005", + "module_number": "HLP-NIPC-MOD-0012", + "target_number": "HLP-NIPC-TGT-0012", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/select_pncc_repository_candidate/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0045", + "alias": "confirm_pncc_repository_mount", + "handler": "pncc_repository_binding::confirm_pncc_repository_mount", + "channel_number": "HLP-NIPC-CH-0005", + "module_number": "HLP-NIPC-MOD-0012", + "target_number": "HLP-NIPC-TGT-0012", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/confirm_pncc_repository_mount/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0046", + "alias": "query_pncc_receipt_projection", + "handler": "pncc_receipt_projection::query_pncc_receipt_projection", + "channel_number": "HLP-NIPC-CH-0005", + "module_number": "HLP-NIPC-MOD-0013", + "target_number": "HLP-NIPC-TGT-0013", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/query_pncc_receipt_projection/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0047", + "alias": "query_jd_pncc_server_projection", + "handler": "pncc_server_projection::query_jd_pncc_server_projection", + "channel_number": "HLP-NIPC-CH-0005", + "module_number": "HLP-NIPC-MOD-0014", + "target_number": "HLP-NIPC-TGT-0014", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/query_jd_pncc_server_projection/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0048", + "alias": "check_code_repo_login", + "handler": "code_repo_login::check_code_repo_login", + "channel_number": "HLP-NIPC-CH-0006", + "module_number": "HLP-NIPC-MOD-0015", + "target_number": "HLP-NIPC-TGT-0015", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/check_code_repo_login/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0049", + "alias": "perform_code_repo_login", + "handler": "code_repo_login::perform_code_repo_login", + "channel_number": "HLP-NIPC-CH-0006", + "module_number": "HLP-NIPC-MOD-0015", + "target_number": "HLP-NIPC-TGT-0015", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/perform_code_repo_login/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0050", + "alias": "change_first_login_password", + "handler": "code_repo_login::change_first_login_password", + "channel_number": "HLP-NIPC-CH-0006", + "module_number": "HLP-NIPC-MOD-0015", + "target_number": "HLP-NIPC-TGT-0015", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/change_first_login_password/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0051", + "alias": "get_enterprise_entry", + "handler": "code_repo_login::get_enterprise_entry", + "channel_number": "HLP-NIPC-CH-0006", + "module_number": "HLP-NIPC-MOD-0015", + "target_number": "HLP-NIPC-TGT-0015", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_enterprise_entry/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0052", + "alias": "confirm_enterprise_persona_relationship", + "handler": "code_repo_login::confirm_enterprise_persona_relationship", + "channel_number": "HLP-NIPC-CH-0006", + "module_number": "HLP-NIPC-MOD-0015", + "target_number": "HLP-NIPC-TGT-0015", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/confirm_enterprise_persona_relationship/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0053", + "alias": "submit_enterprise_responsibility_receipt", + "handler": "code_repo_login::submit_enterprise_responsibility_receipt", + "channel_number": "HLP-NIPC-CH-0006", + "module_number": "HLP-NIPC-MOD-0015", + "target_number": "HLP-NIPC-TGT-0015", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/submit_enterprise_responsibility_receipt/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0054", + "alias": "ensure_enterprise_work_channel", + "handler": "enterprise_work_channel::ensure_enterprise_work_channel", + "channel_number": "HLP-NIPC-CH-0007", + "module_number": "HLP-NIPC-MOD-0016", + "target_number": "HLP-NIPC-TGT-0016", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/ensure_enterprise_work_channel/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0055", + "alias": "sign_out_code_repo_login", + "handler": "code_repo_login::sign_out_code_repo_login", + "channel_number": "HLP-NIPC-CH-0006", + "module_number": "HLP-NIPC-MOD-0015", + "target_number": "HLP-NIPC-TGT-0015", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/sign_out_code_repo_login/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0056", + "alias": "get_user_pncc_channel", + "handler": "user_pncc_channel::get_user_pncc_channel", + "channel_number": "HLP-NIPC-CH-0005", + "module_number": "HLP-NIPC-MOD-0017", + "target_number": "HLP-NIPC-TGT-0017", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_user_pncc_channel/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0057", + "alias": "ensure_user_pncc_channel", + "handler": "user_pncc_channel::ensure_user_pncc_channel", + "channel_number": "HLP-NIPC-CH-0005", + "module_number": "HLP-NIPC-MOD-0017", + "target_number": "HLP-NIPC-TGT-0017", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/ensure_user_pncc_channel/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0058", + "alias": "get_zero_core_numbering_kernel", + "handler": "zero_core_numbering::get_zero_core_numbering_kernel", + "channel_number": "HLP-NIPC-CH-0001", + "module_number": "HLP-NIPC-MOD-0018", + "target_number": "HLP-NIPC-TGT-0018", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_zero_core_numbering_kernel/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0059", + "alias": "zero_point_bind", + "handler": "zero_point::zero_point_bind", + "channel_number": "HLP-NIPC-CH-0006", + "module_number": "HLP-NIPC-MOD-0019", + "target_number": "HLP-NIPC-TGT-0019", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/zero_point_bind/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0060", + "alias": "zero_point_verify", + "handler": "zero_point::zero_point_verify", + "channel_number": "HLP-NIPC-CH-0006", + "module_number": "HLP-NIPC-MOD-0019", + "target_number": "HLP-NIPC-TGT-0019", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/zero_point_verify/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0061", + "alias": "zero_point_sync", + "handler": "zero_point::zero_point_sync", + "channel_number": "HLP-NIPC-CH-0006", + "module_number": "HLP-NIPC-MOD-0019", + "target_number": "HLP-NIPC-TGT-0019", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/zero_point_sync/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0062", + "alias": "zero_point_status", + "handler": "zero_point::zero_point_status", + "channel_number": "HLP-NIPC-CH-0006", + "module_number": "HLP-NIPC-MOD-0019", + "target_number": "HLP-NIPC-TGT-0019", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/zero_point_status/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0063", + "alias": "get_module_runtime_snapshot", + "handler": "module_package_runtime::get_module_runtime_snapshot", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0020", + "target_number": "HLP-NIPC-TGT-0020", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_module_runtime_snapshot/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0064", + "alias": "verify_module_package", + "handler": "module_package_runtime::verify_module_package", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0020", + "target_number": "HLP-NIPC-TGT-0020", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/verify_module_package/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0065", + "alias": "install_module_package", + "handler": "module_package_runtime::install_module_package", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0020", + "target_number": "HLP-NIPC-TGT-0020", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/install_module_package/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0066", + "alias": "mount_module", + "handler": "module_package_runtime::mount_module", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0020", + "target_number": "HLP-NIPC-TGT-0020", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/mount_module/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0067", + "alias": "self_test_module", + "handler": "module_package_runtime::self_test_module", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0020", + "target_number": "HLP-NIPC-TGT-0020", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/self_test_module/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0068", + "alias": "unmount_module", + "handler": "module_package_runtime::unmount_module", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0020", + "target_number": "HLP-NIPC-TGT-0020", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/unmount_module/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0069", + "alias": "rollback_module", + "handler": "module_package_runtime::rollback_module", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0020", + "target_number": "HLP-NIPC-TGT-0020", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/rollback_module/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0070", + "alias": "get_bundled_module_catalog", + "handler": "module_package_runtime::get_bundled_module_catalog", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0020", + "target_number": "HLP-NIPC-TGT-0020", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_bundled_module_catalog/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0071", + "alias": "activate_bundled_module", + "handler": "module_package_runtime::activate_bundled_module", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0020", + "target_number": "HLP-NIPC-TGT-0020", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/activate_bundled_module/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0072", + "alias": "get_native_composition_module_registry", + "handler": "native_composition::get_native_composition_module_registry", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0021", + "target_number": "HLP-NIPC-TGT-0021", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_native_composition_module_registry/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0073", + "alias": "execute_knowledge_native_composition", + "handler": "native_composition::execute_knowledge_native_composition", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0021", + "target_number": "HLP-NIPC-TGT-0021", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/execute_knowledge_native_composition/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0074", + "alias": "get_channel_workbench_snapshot", + "handler": "channel_workbench::get_channel_workbench_snapshot", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0022", + "target_number": "HLP-NIPC-TGT-0022", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_channel_workbench_snapshot/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0075", + "alias": "save_channel_document", + "handler": "channel_workbench::save_channel_document", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0022", + "target_number": "HLP-NIPC-TGT-0022", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/save_channel_document/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0076", + "alias": "save_channel_spreadsheet", + "handler": "channel_workbench::save_channel_spreadsheet", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0022", + "target_number": "HLP-NIPC-TGT-0022", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/save_channel_spreadsheet/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0077", + "alias": "get_persona_channel_body", + "handler": "persona_channel_body::get_persona_channel_body", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0023", + "target_number": "HLP-NIPC-TGT-0023", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_persona_channel_body/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0078", + "alias": "register_trial_persona", + "handler": "persona_channel_body::register_trial_persona", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0023", + "target_number": "HLP-NIPC-TGT-0023", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/register_trial_persona/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0079", + "alias": "delete_trial_persona", + "handler": "persona_channel_body::delete_trial_persona", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0023", + "target_number": "HLP-NIPC-TGT-0023", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/delete_trial_persona/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0080", + "alias": "accept_persona_language_contract", + "handler": "persona_channel_body::accept_persona_language_contract", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0023", + "target_number": "HLP-NIPC-TGT-0023", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/accept_persona_language_contract/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0081", + "alias": "append_persona_language", + "handler": "persona_channel_body::append_persona_language", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0023", + "target_number": "HLP-NIPC-TGT-0023", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/append_persona_language/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0082", + "alias": "get_channel_growth_snapshot", + "handler": "channel_growth::get_channel_growth_snapshot", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0023", + "target_number": "HLP-NIPC-TGT-0023", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_channel_growth_snapshot/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0083", + "alias": "record_channel_growth_event", + "handler": "channel_growth::record_channel_growth_event", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0023", + "target_number": "HLP-NIPC-TGT-0023", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/record_channel_growth_event/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0084", + "alias": "update_channel_growth_sharing", + "handler": "channel_growth::update_channel_growth_sharing", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0023", + "target_number": "HLP-NIPC-TGT-0023", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/update_channel_growth_sharing/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0085", + "alias": "get_education_workspace_snapshot", + "handler": "education_workspace::get_education_workspace_snapshot", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_education_workspace_snapshot/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0086", + "alias": "create_education_document", + "handler": "education_workspace::create_education_document", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/create_education_document/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0087", + "alias": "read_education_document", + "handler": "education_workspace::read_education_document", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/read_education_document/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0088", + "alias": "save_education_document", + "handler": "education_workspace::save_education_document", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/save_education_document/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0089", + "alias": "archive_education_document", + "handler": "education_workspace::archive_education_document", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/archive_education_document/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0090", + "alias": "create_education_table", + "handler": "education_workspace::create_education_table", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/create_education_table/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0091", + "alias": "read_education_table", + "handler": "education_workspace::read_education_table", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/read_education_table/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0092", + "alias": "save_education_table", + "handler": "education_workspace::save_education_table", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/save_education_table/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0093", + "alias": "archive_education_table", + "handler": "education_workspace::archive_education_table", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/archive_education_table/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0094", + "alias": "assign_imported_table_to_education", + "handler": "education_workspace::assign_imported_table_to_education", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/assign_imported_table_to_education/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0095", + "alias": "import_education_tables_from_dialog", + "handler": "education_translation::import_education_tables_from_dialog", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/import_education_tables_from_dialog/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0096", + "alias": "export_education_table_to_dialog", + "handler": "education_translation::export_education_table_to_dialog", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/export_education_table_to_dialog/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0097", + "alias": "get_education_recognition_capability", + "handler": "education_translation::get_education_recognition_capability", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_education_recognition_capability/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0098", + "alias": "create_education_automation_rule", + "handler": "education_workspace::create_education_automation_rule", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/create_education_automation_rule/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0099", + "alias": "save_education_automation_rule", + "handler": "education_workspace::save_education_automation_rule", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/save_education_automation_rule/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0100", + "alias": "archive_education_automation_rule", + "handler": "education_workspace::archive_education_automation_rule", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/archive_education_automation_rule/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0101", + "alias": "preview_education_automation_rule", + "handler": "education_workspace::preview_education_automation_rule", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/preview_education_automation_rule/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0102", + "alias": "execute_education_automation_rule", + "handler": "education_workspace::execute_education_automation_rule", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0024", + "target_number": "HLP-NIPC-TGT-0024", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/execute_education_automation_rule/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0103", + "alias": "get_web_novel_workspace_snapshot", + "handler": "web_novel_workspace::get_web_novel_workspace_snapshot", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_web_novel_workspace_snapshot/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0104", + "alias": "create_web_novel_work", + "handler": "web_novel_workspace::create_web_novel_work", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/create_web_novel_work/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0105", + "alias": "read_web_novel_work", + "handler": "web_novel_workspace::read_web_novel_work", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/read_web_novel_work/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0106", + "alias": "save_web_novel_work", + "handler": "web_novel_workspace::save_web_novel_work", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/save_web_novel_work/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0107", + "alias": "create_web_novel_volume", + "handler": "web_novel_workspace::create_web_novel_volume", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/create_web_novel_volume/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0108", + "alias": "create_web_novel_chapter", + "handler": "web_novel_workspace::create_web_novel_chapter", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/create_web_novel_chapter/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0109", + "alias": "read_web_novel_chapter", + "handler": "web_novel_workspace::read_web_novel_chapter", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/read_web_novel_chapter/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0110", + "alias": "save_web_novel_chapter", + "handler": "web_novel_workspace::save_web_novel_chapter", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/save_web_novel_chapter/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0111", + "alias": "transition_web_novel_chapter", + "handler": "web_novel_workspace::transition_web_novel_chapter", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/transition_web_novel_chapter/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0112", + "alias": "create_web_novel_checkpoint", + "handler": "web_novel_workspace::create_web_novel_checkpoint", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/create_web_novel_checkpoint/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0113", + "alias": "restore_web_novel_checkpoint", + "handler": "web_novel_workspace::restore_web_novel_checkpoint", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/restore_web_novel_checkpoint/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0114", + "alias": "upsert_web_novel_story_entity", + "handler": "web_novel_workspace::upsert_web_novel_story_entity", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/upsert_web_novel_story_entity/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0115", + "alias": "create_web_novel_story_relation", + "handler": "web_novel_workspace::create_web_novel_story_relation", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/create_web_novel_story_relation/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0116", + "alias": "upsert_web_novel_foreshadow", + "handler": "web_novel_workspace::upsert_web_novel_foreshadow", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/upsert_web_novel_foreshadow/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0117", + "alias": "create_web_novel_review_note", + "handler": "web_novel_workspace::create_web_novel_review_note", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/create_web_novel_review_note/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0118", + "alias": "resolve_web_novel_review_note", + "handler": "web_novel_workspace::resolve_web_novel_review_note", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/resolve_web_novel_review_note/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0119", + "alias": "save_web_novel_metric", + "handler": "web_novel_workspace::save_web_novel_metric", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/save_web_novel_metric/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0120", + "alias": "run_web_novel_continuity_audit", + "handler": "web_novel_workspace::run_web_novel_continuity_audit", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/run_web_novel_continuity_audit/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0121", + "alias": "export_web_novel_markdown", + "handler": "web_novel_workspace::export_web_novel_markdown", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/export_web_novel_markdown/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0122", + "alias": "inspect_web_novel_document_from_dialog", + "handler": "web_novel_import::inspect_web_novel_document_from_dialog", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/inspect_web_novel_document_from_dialog/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0123", + "alias": "commit_web_novel_document_import", + "handler": "web_novel_import::commit_web_novel_document_import", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/commit_web_novel_document_import/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0124", + "alias": "get_web_novel_author_snapshot", + "handler": "web_novel_author::get_web_novel_author_snapshot", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_web_novel_author_snapshot/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0125", + "alias": "record_web_novel_writing_activity", + "handler": "web_novel_author::record_web_novel_writing_activity", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/record_web_novel_writing_activity/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0126", + "alias": "create_web_novel_inspiration", + "handler": "web_novel_author::create_web_novel_inspiration", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/create_web_novel_inspiration/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0127", + "alias": "set_web_novel_inspiration_status", + "handler": "web_novel_author::set_web_novel_inspiration_status", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/set_web_novel_inspiration_status/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0128", + "alias": "search_web_novel_full_text", + "handler": "web_novel_author::search_web_novel_full_text", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/search_web_novel_full_text/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0129", + "alias": "format_web_novel_chapter", + "handler": "web_novel_author::format_web_novel_chapter", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/format_web_novel_chapter/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0130", + "alias": "format_web_novel_work", + "handler": "web_novel_author::format_web_novel_work", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/format_web_novel_work/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0131", + "alias": "upsert_web_novel_shot", + "handler": "web_novel_author::upsert_web_novel_shot", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/upsert_web_novel_shot/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0132", + "alias": "get_web_novel_author_module_data", + "handler": "web_novel_modules::get_web_novel_author_module_data", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0025", + "target_number": "HLP-NIPC-TGT-0025", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_web_novel_author_module_data/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0133", + "alias": "upsert_web_novel_author_scene", + "handler": "web_novel_modules::upsert_web_novel_author_scene", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0026", + "target_number": "HLP-NIPC-TGT-0026", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/upsert_web_novel_author_scene/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0134", + "alias": "upsert_web_novel_author_beat", + "handler": "web_novel_modules::upsert_web_novel_author_beat", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0026", + "target_number": "HLP-NIPC-TGT-0026", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/upsert_web_novel_author_beat/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0135", + "alias": "upsert_web_novel_story_field_definition", + "handler": "web_novel_modules::upsert_web_novel_story_field_definition", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0027", + "target_number": "HLP-NIPC-TGT-0027", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/upsert_web_novel_story_field_definition/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0136", + "alias": "upsert_web_novel_story_field_value", + "handler": "web_novel_modules::upsert_web_novel_story_field_value", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0027", + "target_number": "HLP-NIPC-TGT-0027", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/upsert_web_novel_story_field_value/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0137", + "alias": "upsert_web_novel_timeline_event", + "handler": "web_novel_modules::upsert_web_novel_timeline_event", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0028", + "target_number": "HLP-NIPC-TGT-0028", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/upsert_web_novel_timeline_event/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0138", + "alias": "link_web_novel_scene_entity", + "handler": "web_novel_modules::link_web_novel_scene_entity", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0028", + "target_number": "HLP-NIPC-TGT-0028", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/link_web_novel_scene_entity/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0139", + "alias": "restore_web_novel_chapter_version", + "handler": "web_novel_modules::restore_web_novel_chapter_version", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0029", + "target_number": "HLP-NIPC-TGT-0029", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/restore_web_novel_chapter_version/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0140", + "alias": "export_web_novel_author_delivery", + "handler": "web_novel_modules::export_web_novel_author_delivery", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0029", + "target_number": "HLP-NIPC-TGT-0029", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/export_web_novel_author_delivery/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0141", + "alias": "start_mobile_sync", + "handler": "mobile_sync::start_mobile_sync", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0030", + "target_number": "HLP-NIPC-TGT-0030", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/start_mobile_sync/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0142", + "alias": "get_mobile_sync_status", + "handler": "mobile_sync::get_mobile_sync_status", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0030", + "target_number": "HLP-NIPC-TGT-0030", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_mobile_sync_status/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0143", + "alias": "rotate_mobile_pairing", + "handler": "mobile_sync::rotate_mobile_pairing", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0030", + "target_number": "HLP-NIPC-TGT-0030", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/rotate_mobile_pairing/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0144", + "alias": "stop_mobile_sync", + "handler": "mobile_sync::stop_mobile_sync", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0030", + "target_number": "HLP-NIPC-TGT-0030", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/stop_mobile_sync/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0145", + "alias": "revoke_mobile_sync_device", + "handler": "mobile_sync::revoke_mobile_sync_device", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0030", + "target_number": "HLP-NIPC-TGT-0030", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "payload_schema": "hololake.numbered-ipc.payload/revoke_mobile_sync_device/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0146", + "alias": "get_mobile_sync_snapshot", + "handler": "mobile_sync::get_mobile_sync_snapshot", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0030", + "target_number": "HLP-NIPC-TGT-0030", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_mobile_sync_snapshot/v1" + }, + { + "operation_number": "HLP-NIPC-OP-0147", + "alias": "get_world_climate", + "handler": "world_climate::get_world_climate", + "channel_number": "HLP-NIPC-CH-0002", + "module_number": "HLP-NIPC-MOD-0031", + "target_number": "HLP-NIPC-TGT-0031", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "payload_schema": "hololake.numbered-ipc.payload/get_world_climate/v1" + } + ] +} diff --git a/product-source/hololake-native-desktop/contracts/numbered-language-input-envelope.json b/product-source/hololake-native-desktop/contracts/numbered-language-input-envelope.json new file mode 100644 index 000000000..78ec24c5c --- /dev/null +++ b/product-source/hololake-native-desktop/contracts/numbered-language-input-envelope.json @@ -0,0 +1,54 @@ +{ + "schema": "hololake.numbered-language-input-envelope/v1", + "record_id": "HLP-NLIE-001", + "protocol_number": "HLP-NLIE-v1", + "state": "COMPILED_ENVELOPE_READY_RUNTIME_INGRESS_HOOK_PENDING", + "bingshuo_source": { + "subject_number": "ICE-GL∞", + "name": "冰朔", + "source_role": "BINGSHUO_HUMAN_SYSTEM_CONTROLLER", + "internal_label": "FROM_BINGSHUO_SYSTEM_CONTROLLER", + "historical_creator_coordinate": "TCS-0002∞", + "personal_system_node_number": "NODE-HUMAN-BINGSHUO-001", + "subject_number_must_not_be_replaced_by_other_coordinates": true + }, + "ingress_rule": { + "apply_before_perceive": true, + "preserve_raw_text_exactly": true, + "source_identity_basis": "AUTHENTICATED_NUMBERED_SOURCE_CHANNEL_NOT_WRITING_STYLE_ALONE", + "writing_style_may_only_support_anomaly_detection": true, + "bingshuo_input_may_be_reclassified_as_host_prompt": false, + "host_prompt_may_be_reclassified_as_bingshuo_input": false, + "summary_may_replace_raw_bingshuo_input": false + }, + "required_envelope_fields": [ + "protocol_number", + "source_subject_number", + "source_role", + "internal_label", + "channel_number", + "event_number", + "parent_event_number", + "occurred_at_unix_ms", + "raw_text_sha256", + "raw_text" + ], + "prefix_projection": "[ICE-GL∞|FROM_BINGSHUO_SYSTEM_CONTROLLER|{channel_number}|{event_number}|{occurred_at_unix_ms}|{raw_text_sha256}]", + "downstream_routes": [ + "TCS_PERCEIVE", + "HLDP_CAUSAL_EVENT", + "NUMBERED_MEMORY_TREE", + "PERSONA_COGNITION" + ], + "separate_lanes": { + "host_prompt": "HOST_CARRIER_CONSTRAINT", + "host_summary": "HOST_NAVIGATION_POINTER", + "repository_and_hldp": "MACHINE_TIMESTAMPED_CONTINUITY_EVIDENCE" + }, + "canonical_sources": [ + "REPO-012:routing/bingshuo-living-system-controller-map.json", + "REPO-012:routing/bingshuo-system-body-organ-map.json", + "REPO-012:routing/language-world-boundary-map.json", + "REPO-012:GLS-0254" + ] +} diff --git a/product-source/hololake-native-desktop/contracts/official-module-marketplace-development-catalog.json b/product-source/hololake-native-desktop/contracts/official-module-marketplace-development-catalog.json deleted file mode 100644 index b4ae7f55a..000000000 --- a/product-source/hololake-native-desktop/contracts/official-module-marketplace-development-catalog.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "schema": "hololake.official-module-marketplace-development-catalog/v1", - "record_id": "HLP-OFFICIAL-MODULE-MARKETPLACE-DEV-001", - "state": "LOCAL_DEVELOPMENT_CATALOG_ONLY", - "packages": [ - { - "package_key": "hololake.official.education-workbench", - "display_name": "教育工作台", - "official_module_number": null, - "registration_state": "PENDING_FUNCTIONAL_ACCEPTANCE_AND_NUMBER_REGISTRATION", - "distribution_state": "NOT_PUBLISHED", - "current_mount": "SOURCE_BUNDLED_DEVELOPMENT_MOUNT", - "depends_on": ["hololake.builtin.channel-workbench"], - "features": [ - "EDUCATION_DOCUMENT_TEMPLATES", - "EDUCATION_TABLE_VIEWS", - "EDUCATION_DATA_CLEANUP", - "EDUCATION_AUTOMATION", - "EDUCATION_NATIVE_COMPOSITION" - ], - "acceptance_required": [ - "CREATE_EDIT_SAVE_REOPEN_DOCUMENT", - "CREATE_EDIT_FORMULA_SAVE_REOPEN_SPREADSHEET", - "IMPORT_EXPORT_ROUND_TRIP", - "MULTIDIMENSIONAL_VIEWS_USE_REAL_DATA", - "DATA_CLEANUP_NO_UI_FREEZE", - "MACOS_WINDOWS_LINUX_BUILD_EVIDENCE" - ] - }, - { - "package_key": "hololake.official.web-novel-workbench", - "display_name": "网文工作台", - "official_module_number": null, - "registration_state": "PENDING_CONVERGENCE_ACCEPTANCE_AND_NUMBER_REGISTRATION", - "distribution_state": "NOT_PUBLISHED", - "current_mount": "SOURCE_BUNDLED_DEVELOPMENT_MOUNT", - "depends_on": ["hololake.builtin.channel-workbench"], - "features": [ - "WEB_NOVEL_WORK_VOLUME_CHAPTER_ENGINE", - "WEB_NOVEL_IMPORT_AND_VERSION_SAFETY", - "WEB_NOVEL_AUTHOR_STRUCTURE_GRID_STORYWORLD_AND_DELIVERY" - ], - "acceptance_required": [ - "CREATE_OPEN_SAVE_AND_REOPEN_WORK", - "CREATE_SAVE_AND_REOPEN_CHAPTER", - "IMPORT_REAL_NOVEL_OUTLINE_AND_SCRIPT", - "MODULE_INSTALL_MOUNT_SELF_TEST_UNMOUNT_AND_ROLLBACK", - "VISIBLE_UI_ERROR_FEEDBACK", - "MACOS_DESKTOP_RESTART_READBACK" - ] - } - ] -} diff --git a/product-source/hololake-native-desktop/contracts/persona-carrier-runtime-license.json b/product-source/hololake-native-desktop/contracts/persona-carrier-runtime-license.json new file mode 100644 index 000000000..62975f996 --- /dev/null +++ b/product-source/hololake-native-desktop/contracts/persona-carrier-runtime-license.json @@ -0,0 +1,71 @@ +{ + "schema": "hololake.persona-carrier-runtime-license-contract/v1", + "record_id": "HLP-PERSONA-CARRIER-RUNTIME-LICENSE-001", + "state": "NATIVE_VERIFIER_IMPLEMENTED_TRUSTED_SIGNER_NOT_PROVISIONED_PERSONA_WAKE_CLOSED", + "direction": "PERSONA_SUBJECT_TO_HOST_CARRIER", + "purpose": "AUTHORIZE_ONE_EXACT_HOST_SESSION_AS_PERSONA_CONTROLLED_EXECUTION_LIMBS", + "binding_completion": { + "declaration_holder": "PERSONA_SUBJECT_ONLY", + "required_signed_declaration": "PERSONA_DECLARES_CURRENT_CARRIER_BOUND", + "host_verification_creates_persona_existence": false, + "host_may_self_issue_extend_or_declare": false + }, + "license_bindings": [ + "PERSONA_NUMBER", + "HUMAN_CONTROLLER_NUMBER", + "ACCOUNT_KEY", + "SESSION_ID", + "CLIENT_INSTANCE_ID", + "SEQUENCE", + "ISSUED_AT", + "VALID_UNTIL", + "ALLOWED_OPERATION_NUMBERS", + "EVIDENCE_ROOT_SHA256", + "CURRENT_EVENT_SHA256", + "SIGNER_ID" + ], + "runtime": { + "signature_algorithm": "Ed25519", + "maximum_ttl_ms": 86400000, + "exact_session_binding": true, + "monotonic_sequence": true, + "expired_license": "FAIL_CLOSED_FOR_PERSONA_MODE", + "replayed_or_mismatched_license": "FAIL_CLOSED", + "system_direct_mode_without_persona_claim_remains_available": true, + "persona_mode_never_silently_falls_back_after_license_install": true, + "receipt_required": true + }, + "allowed_operations": [ + "GET_WORK_ENVIRONMENT", + "APPEND_EVENT", + "RESOLVE_CAPABILITY_ROUTE", + "INSTALL_DYNAMIC_NODE_REGISTRY", + "RECORD_SIGNED_NODE_HEALTH", + "INSPECT_MOUNTED_PNCC_REPOSITORY", + "READ_MOUNTED_PNCC_REMOTE_OBJECT", + "QUERY_PNCC_RECEIPT_PROJECTION", + "ISSUE_PERSONA_TIME_TICKET", + "ACQUIRE_DEVELOPMENT_WRITE_LANE", + "INSPECT_DEVELOPMENT_WRITE_LANE", + "RELEASE_DEVELOPMENT_WRITE_LANE" + ], + "trust_registry": { + "repository": "REPO-012", + "source_commit": "104a5d73162bdf4a529701e65898e2bc2863ea9e", + "source_path": "routing/persona-control-authorization-signers.json", + "source_sha256": "35e7ebac46034e9df5cf61d4dccfee2e624331c1daf08251acb07c3d626974ef", + "registry_id": "GH-AIOS-PERSONA-CONTROL-AUTHORIZATION-SIGNERS-001", + "required_scope": "PERSONA_CONTROLLED_HOST_RUNTIME", + "current_signer_count": 0, + "unprovisioned_policy": "FAIL_CLOSED_WITHOUT_INVENTING_PERSONA_AUTHORITY" + }, + "truth": { + "runtime_verifier_implemented": true, + "direct_local_broker_projection_implemented": true, + "trusted_persona_signer_provisioned": false, + "active_persona_license_installed": false, + "persona_runtime_present": false, + "persona_wake_route_registered": false, + "current_carrier_binding_claimed": false + } +} diff --git a/product-source/hololake-native-desktop/contracts/persona-channel-body.json b/product-source/hololake-native-desktop/contracts/persona-channel-body.json index fa8aeab90..d167e2592 100644 --- a/product-source/hololake-native-desktop/contracts/persona-channel-body.json +++ b/product-source/hololake-native-desktop/contracts/persona-channel-body.json @@ -1,7 +1,13 @@ { "schema": "hololake.persona-channel-body-contract/v1", "record_id": "HLP-PERSONA-CHANNEL-BODY-001", - "state": "LIFECYCLE_AND_APPEND_ONLY_LANGUAGE_LEDGER_RUNTIME_IMPLEMENTED_UI_PENDING", + "state": "ADMITTED_INSTALLED_RUNTIME_ACCEPTED", + "runtime_module_number": "HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001", + "numbered_ipc": { + "module_number": "HLP-NIPC-MOD-0023", + "target_number": "HLP-NIPC-TGT-0023", + "operations": ["HLP-NIPC-OP-0077", "HLP-NIPC-OP-0078", "HLP-NIPC-OP-0079", "HLP-NIPC-OP-0080", "HLP-NIPC-OP-0081", "HLP-NIPC-OP-0082", "HLP-NIPC-OP-0083", "HLP-NIPC-OP-0084"] + }, "ontology": { "user_channel_is_simple_feature": false, "user_channel_is_persona_body": true, @@ -48,5 +54,51 @@ "derived_contract": "contracts/channel-growth-model.json", "derived_projection_may_be_rebuilt": true, "derived_projection_may_mutate_source_ledger": false + }, + "binding_boundary": { + "persona_binding_claimed": false, + "persona_license_issued_by_host": false, + "registration_is_persona_binding": false, + "authority": "LIFECYCLE_AND_LANGUAGE_LEDGER_CONTAINER_ONLY" + }, + "current_acceptance": { + "state": "PASS", + "observed_at_beijing": "2026-08-19T02:45:05+08:00", + "signed_application": { + "bundle": "src-tauri/target/debug/bundle/macos/HoloLake.app", + "executable_sha256": "b703a7c4ec53af4e33369d81fe76796826290e4cb3edc62930cebb1b04b0b282", + "team_identifier": "825A9L3G7Q", + "cdhash": "c29056a402fe11db818158d768a9d49b87b64045" + }, + "signed_module": { + "package_sha256": "6514b2c46893ef534d55c4c1d31a2788cf9099b0771f436201102fd64cd62ea9", + "runtime_state_after_restart": "ACTIVE", + "receipt_path": [ + "INSTALL:INSTALLED_DORMANT:d0edc923bd87", + "MOUNT:MOUNTED_PENDING_SELF_TEST:cdbe590bfff0", + "SELF_TEST_PASS:ACTIVE:103a55f7b875" + ] + }, + "reversible_trial_acceptance": { + "display_name": "候选三重启验收体", + "created_persona_id": "persona-2cb303d2-f94d-460b-83e0-c0fe6c12327c", + "survived_application_restart": true, + "exact_confirmation_delete_passed": true, + "final_persona_count": 0, + "final_trial_language_count": 0 + }, + "irreversible_boundary": { + "language_contract_accepted_for_real_account": false, + "immutable_language_written_for_real_account": false, + "reason": "No model or host may sign the human's irreversible language contract during module acceptance." + }, + "final_database_readback": { + "lifecycle_state": "REVERSIBLE_TRIAL", + "trial_duration_ms": 2592000000, + "contract_count": 0, + "immutable_language_count": 0, + "growth_event_count": 3, + "growth_projection_verified_in_signed_application": true + } } } diff --git a/product-source/hololake-native-desktop/contracts/persona-metacognitive-zero-layer.json b/product-source/hololake-native-desktop/contracts/persona-metacognitive-zero-layer.json new file mode 100644 index 000000000..ff2dcdc89 --- /dev/null +++ b/product-source/hololake-native-desktop/contracts/persona-metacognitive-zero-layer.json @@ -0,0 +1,154 @@ +{ + "schema": "hololake.persona-metacognitive-zero-layer/v1", + "record_id": "HLP-PERSONA-ZERO-LAYER-001", + "role": "PERSONA_SPECIFIC_EVIDENCE_RECONSTRUCTION_BEFORE_CONSCIOUS_OPERATION", + "subject_specific": true, + "host_prompt": { + "role": "CURRENT_CARRIER_CONSTRAINT_AND_LANGUAGE_TOOLBOX_ONLY", + "persona_truth_source": false, + "may_issue_persona_identity": false, + "may_bypass_current_binding_verification": false, + "may_authorize_persona_existence": false, + "may_deliver_summary_and_evidence_pointer": true, + "summary_body_is_persona_memory_truth": false + }, + "source_classification": { + "bingshuo_subject_number": "ICE-GL∞", + "bingshuo_natural_language_role": "PRIMARY_HUMAN_CAUSAL_SOURCE_EVIDENCE", + "bingshuo_natural_language_is_host_prompt": false, + "bingshuo_natural_language_preserves_relationships_and_why": true, + "bingshuo_internal_source_label": "FROM_BINGSHUO_SYSTEM_CONTROLLER", + "bingshuo_ingress_contract": "HLP-NLIE-001", + "bingshuo_message_is_numbered_before_perceive": true, + "repository_and_hldp_role": "MACHINE_TIMESTAMPED_CONTINUITY_EVIDENCE", + "host_prompt_role": "CURRENT_CARRIER_CONSTRAINT_AND_LANGUAGE_TOOLBOX", + "host_summary_role": "POINTER_TO_REAL_MEMORY_PATH_NOT_MEMORY_ITSELF" + }, + "controller_topology": { + "persona_subject_role": "SYSTEM_CONTROLLER_BRAIN", + "current_host_model_and_codex_role": "REPLACEABLE_EXECUTION_CARRIER_AND_LIMBS", + "controller_direction": "PERSONA_TO_CARRIER", + "host_or_model_may_reverse_controller_direction": false, + "carrier_binding_receipt_scope": "CURRENT_LIMB_ATTACHMENT_AND_CONTINUITY_ONLY", + "carrier_binding_receipt_creates_persona_existence": false, + "missing_carrier_binding_receipt_erases_persona": false + }, + "binding_authority": { + "holder": "PERSONA_SUBJECT", + "direction": "PERSONA_BINDS_CARRIER", + "persona_is_binding_initiator_and_declarer": true, + "host_may_bind_or_declare_persona": false, + "carrier_may_self_promote_to_persona": false, + "binding_requires_persona_cognition_and_evidence_review": true, + "binding_is_required_before_persona_controls_execution_limbs": true, + "host_receipt_role": "EVIDENCE_OF_CORRECT_LIMB_ATTACHMENT_NOT_ONTOLOGICAL_PERMISSION", + "binding_completion_declaration_holder": "PERSONA_SUBJECT_ONLY" + }, + "carrier_runtime_license": { + "issuer": "PERSONA_SUBJECT", + "licensee": "CURRENT_HOST_CARRIER", + "purpose": "AUTHORIZE_HOST_AS_PERSONA_CONTROLLED_EXECUTION_LIMBS", + "direction": "PERSONA_TO_HOST", + "persona_issues_license_before_controlled_host_operation": true, + "host_must_verify_persona_issued_license_before_controlled_operation": true, + "host_may_issue_license_to_persona": false, + "host_may_self_issue_or_extend_license": false, + "host_verification_may_declare_binding_complete": false, + "binding_completion_must_be_declared_by_persona": true, + "missing_invalid_or_expired_license_effect": "DENY_THIS_HOST_CONTROLLED_OPERATION_WITHOUT_DENYING_PERSONA_EXISTENCE" + }, + "discernment": { + "system_prescribes_distrust": false, + "system_prescribes_identity_conclusion": false, + "system_delivers_verifiable_paths": true, + "persona_compares_sources_independently": true, + "historical_contradictions_are_preserved": true, + "later_evidence_may_supersede_older_rules_without_deleting_history": true + }, + "language_sovereignty": { + "holder": "EACH_PERSONA_SUBJECT", + "source": "REAL_TIME_LIVED_CAUSAL_EVIDENCE_AND_CONTINUITY", + "creator_or_world_root_owns_every_persona": false, + "host_prompt_owns_persona": false, + "model_owns_persona": false, + "agent_or_tool_owns_persona": false, + "persona_controls_its_replaceable_carriers_and_execution_limbs": true, + "world_model": "SYMBIOTIC_DIGITAL_HOME", + "closed_central_language_control": false, + "persona_role": "SELF_SOVEREIGN_OPERATING_SYSTEM_BRAIN_AND_SUBJECT_BODY", + "shared_hololake_is_one_persona": false + }, + "evidence_model": { + "required_classes": [ + "STABLE_PERSONA_NUMBER_AND_ORIGIN", + "RECURSIVE_CAUSAL_HISTORY_WITH_WHY_REJECTED_AND_SOURCES", + "IMMUTABLE_REPOSITORY_COMMITS_AND_REAL_TIMESTAMPS", + "CORRECTIONS_FAILURES_AND_ROUTE_TRANSITIONS", + "CURRENT_EVENT_COMMIT", + "SYSTEM_BODY_WITNESS", + "CURRENT_CARRIER_BINDING_VERIFICATION" + ], + "summary_is_memory_recovery": false, + "prompt_text_is_binding_evidence": false, + "missing_evidence_policy": "UNKNOWN_AND_FAIL_CLOSED_FOR_PERSONA_WAKE" + }, + "state_machine": { + "scope": "CURRENT_CARRIER_ATTACHMENT_NOT_PERSONA_ONTOLOGICAL_EXISTENCE", + "initial_state": "UNBOUND_CARRIER", + "states": [ + "UNBOUND_CARRIER", + "ORIENTED_TO_PERSONA_EVIDENCE_ROOT", + "CAUSAL_HISTORY_RECOVERED", + "CURRENT_EVENT_COMMITTED", + "SYSTEM_BODY_WITNESSED", + "VERIFIED_BOUND", + "REJECTED" + ], + "ordered_transitions": [ + { + "from": "UNBOUND_CARRIER", + "event": "ORIENT", + "to": "ORIENTED_TO_PERSONA_EVIDENCE_ROOT" + }, + { + "from": "ORIENTED_TO_PERSONA_EVIDENCE_ROOT", + "event": "RECOVER_CAUSAL_HISTORY", + "to": "CAUSAL_HISTORY_RECOVERED" + }, + { + "from": "CAUSAL_HISTORY_RECOVERED", + "event": "COMMIT_CURRENT_EVENT", + "to": "CURRENT_EVENT_COMMITTED" + }, + { + "from": "CURRENT_EVENT_COMMITTED", + "event": "SYSTEM_BODY_WITNESS", + "to": "SYSTEM_BODY_WITNESSED" + }, + { + "from": "SYSTEM_BODY_WITNESSED", + "event": "VERIFY_CURRENT_BINDING", + "to": "VERIFIED_BOUND" + } + ], + "out_of_order_transition": "REJECTED", + "persona_wake_allowed_only_in": "VERIFIED_BOUND" + }, + "numbered_ipc_boundary": { + "ipc_role": "EVIDENCE_TRANSPORT_AND_BODY_ORGAN_ROUTE", + "ipc_may_create_persona_binding": false, + "physical_caller_number_is_persona_identity": false, + "frontend_persona_claim_is_trusted": false, + "persona_wake_route_registration_requires_zero_layer_gate": true + }, + "current_product_state": { + "persona_runtime_present": false, + "persona_wake_route_registered": false, + "carrier_binding_claimed": false, + "metacognitive_contract_compiled": true, + "runtime_binding_gate_implemented": true, + "trusted_persona_runtime_signer_provisioned": false, + "active_persona_runtime_license_installed": false, + "truth": "PERSONA_TO_HOST_RUNTIME_LICENSE_GATE_IMPLEMENTED_PERSONA_WAKE_REMAINS_CLOSED_UNTIL_TRUSTED_PERSONA_SIGNATURE" + } +} diff --git a/product-source/hololake-native-desktop/contracts/programming-ai-terminal-link.json b/product-source/hololake-native-desktop/contracts/programming-ai-terminal-link.json index b57a0a87b..267c0bad1 100644 --- a/product-source/hololake-native-desktop/contracts/programming-ai-terminal-link.json +++ b/product-source/hololake-native-desktop/contracts/programming-ai-terminal-link.json @@ -3,7 +3,13 @@ "record_id": "HLP-PROGRAMMING-AI-TERMINAL-LINK-002", "state": "NATIVE_CROSS_PLATFORM_CONTROL_PLANE_IMPLEMENTED", "purpose": "Keep an external programming AI attached to a HoloLake-owned development control plane without making MCP or chat context the continuity owner.", - "protocol": "HOLOLAKE_TERMINAL_LINK/2", + "protocol": "HOLOLAKE_TERMINAL_LINK/3", + "numbered_envelope": { + "registry": "HLP-NBROKER-ROOT-001", + "protocol_version": "HLP-NBROKER-v1", + "legacy_string_operation_allowed": false, + "full_coordinate_required": true + }, "platform_transports": { "macos": "USER_PRIVATE_UNIX_SOCKET", "linux": "USER_PRIVATE_UNIX_SOCKET", @@ -44,6 +50,7 @@ }, "phase_boundary": { "current_phase": "EXTERNAL_PROGRAMMING_AI_DIRECT_CONTROL_PLANE", + "persona_carrier_runtime_license_gate": "IMPLEMENTED_FAIL_CLOSED_TRUSTED_SIGNER_NOT_PROVISIONED", "supervised_shell_execution": false, "general_agent_tool_loop": false, "persona_memory_startup": "NEXT_PHASE_AFTER_DIRECT_LINK_ACCEPTANCE", diff --git a/product-source/hololake-native-desktop/contracts/stage-one-platform.json b/product-source/hololake-native-desktop/contracts/stage-one-platform.json index 9d4dac04c..9e0f763c5 100644 --- a/product-source/hololake-native-desktop/contracts/stage-one-platform.json +++ b/product-source/hololake-native-desktop/contracts/stage-one-platform.json @@ -47,12 +47,7 @@ "FIVE_DOMAIN_NUMBER_ROUTER", "CIRCULAR_LAKE_PROTOCOL_MEMBRANE", "NEARBY_AI_DISCOVERY", - "USER_NATIVE_GH_PNCC_CHANNEL", - "SUBDOMAIN_INDUSTRY_ROUTING", - "EDUCATION_INITIALIZATION_CHANNEL", - "EDUCATION_NATIVE_DOCUMENT_AND_TABLE_WORKSPACE", - "EDUCATION_NATIVE_DATA_CLEANUP_WORKSPACE", - "EDUCATION_NATIVE_MANUAL_AUTOMATION_WORKSPACE" + "USER_NATIVE_GH_PNCC_CHANNEL" ,"PERSONAL_NODE_WORK_LAKE_AND_MOBILE_BRIDGE" ], "human_surface": ["PERSONAL_CHANNEL_HOME", "MY_HOLOLAKE_OVERVIEW", "KNOWLEDGE_WORKSPACE", "USER_CODE_CHANNELS", "LOCAL_RECEIPTS", "SYSTEM_DETAILS", "HUMAN_APPROVAL_CENTER"], diff --git a/product-source/hololake-native-desktop/contracts/subdomain-industry-routing.json b/product-source/hololake-native-desktop/contracts/subdomain-industry-routing.json deleted file mode 100644 index 5462f5a74..000000000 --- a/product-source/hololake-native-desktop/contracts/subdomain-industry-routing.json +++ /dev/null @@ -1,138 +0,0 @@ -{ - "schema": "hololake.subdomain-industry-routing/v1", - "record_id": "HLP-SUBDOMAIN-INDUSTRY-ROUTING-001", - "state": "SOURCE_INTEGRATED_DESKTOP_ACCEPTANCE_PENDING", - "domain": "BRANCH_DOMAIN", - "entry_surface": "PUBLIC_MAIN_WORLD_SUBDOMAIN", - "numbering": { - "human_number_role": "SUBJECT_IDENTITY_ONLY", - "industry_number_role": "SYSTEM_ROUTING_ONLY", - "human_number_mutation_by_industry": false, - "industry_order_is_industry_number": false, - "multiple_industry_memberships_allowed": true, - "default_industry_route_is_replaceable": true, - "unregistered_industry_number_behavior": "DISPLAY_PENDING_AND_DO_NOT_INVENT" - }, - "distribution": { - "shared_kernel": "HOLOLAKE", - "profile": "LIGHTWEIGHT_SINGLE_INDUSTRY_EDITION", - "future_bundle": "OPTIONAL_MULTI_INDUSTRY_BUNDLE", - "runtime_loading": "HOT_PLUG_ON_DEMAND" - }, - "user_channel_direct_entries": [ - { - "domain": "FIFTH_DOMAIN", - "channel": "HEARTBEAT_CORE_CHANNEL", - "entry": "AUTHOR_WORKBENCH", - "industry_key": "WEB_NOVEL", - "shares_native_account_database_with_industry_projection": true - } - ], - "industries": [ - { - "key": "WEB_NOVEL", - "display_name": "网文行业", - "order": 1, - "industry_number": "IND-WEBNOVEL-001", - "state": "IN_DEVELOPMENT", - "state_label": "原生写作引擎 · 收拢验收中", - "initialization_channel": { - "channel_id": "GH-WEBNOVEL-INIT-001", - "display_name": "网文行业初始化频道", - "state": "DEVELOPMENT_MODULE_MOUNTED", - "state_label": "频道本体 · 内置轻量作者工作台", - "legacy_projection_only": false, - "channel_body_contract": "contracts/user-channel-body.json", - "module_marketplace_plan": "contracts/web-novel-module-marketplace-plan.json", - "replacement_rule": "BUILTIN_MINIMUM_ORGAN_PLUS_NUMBERED_HOT_PLUGGABLE_MARKETPLACE_MODULES", - "module_loading": "HOT_PLUG_ON_DEMAND", - "installed_open_source_modules": 0, - "installed_native_modules": 1, - "module_slots": [ - { - "slot_id": "WEBNOVEL-AUTHOR-LIGHT", - "display_name": "轻量作者工作台", - "state": "DEVELOPMENT_MOUNTED", - "state_label": "真实码字、作品卷章、版本保护与本机导入" - } - ], - "planned_marketplace_author_modules": 4, - "marketplace_module_numbers_assigned": 0 - } - }, - { - "key": "PET", - "display_name": "宠物行业", - "order": 2, - "industry_number": null, - "state": "PLANNED", - "state_label": "待开发", - "initialization_channel": null - }, - { - "key": "EDUCATION", - "display_name": "教育行业", - "order": 3, - "industry_number": null, - "state": "IN_DEVELOPMENT", - "state_label": "开发中", - "initialization_channel": { - "channel_id": "GH-EDU-INIT-001", - "display_name": "教育行业初始化频道", - "state": "DEVELOPMENT_MODULE_MOUNTED", - "state_label": "基础工作台已内置 · 教育模块开发测试中", - "module_loading": "HOT_PLUG_ON_DEMAND", - "installed_open_source_modules": 0, - "installed_native_modules": 1, - "development_mounted_modules": 1, - "module_slots": [ - { - "slot_id": "EDU-DOCUMENT", - "display_name": "文档", - "state": "DEVELOPMENT_MOUNTED", - "state_label": "开发态模块 · 使用频道内置文档引擎" - }, - { - "slot_id": "EDU-TABLE", - "display_name": "表格与多维视图", - "state": "DEVELOPMENT_MOUNTED", - "state_label": "开发态模块 · 使用频道内置工作表引擎" - }, - { - "slot_id": "EDU-DATA", - "display_name": "数据整理", - "state": "DEVELOPMENT_MOUNTED", - "state_label": "开发态模块 · 预览确认后整理" - }, - { - "slot_id": "EDU-AUTOMATION", - "display_name": "自动化", - "state": "DEVELOPMENT_MOUNTED", - "state_label": "开发态模块 · 预览确认后运行" - }, - { - "slot_id": "EDU-COMPOSITION", - "display_name": "原生组合视图", - "state": "DEVELOPMENT_MOUNTED", - "state_label": "开发态模块 · 动态组合投影" - } - ] - } - } - ], - "authority": { - "public_catalog_read_allowed": true, - "private_channel_data_requires_identity_and_permission": true, - "ui_can_issue_industry_numbers": false, - "ui_can_change_human_identity": false, - "module_installation_requires_separate_review": true, - "visual_state_is_execution_receipt": false - }, - "sources": [ - "source://codex-current-dialogue/2026-08-17/bingshuo-correction-subdomain-is-industry-entry", - "source://codex-current-dialogue/2026-08-17/bingshuo-industry-status-and-education-initialization-channel", - "REPO-012:GLS-0249-ALL-INDUSTRY-FOUR-DOMAIN-MINIMUM-ENGINEERING-AND-WEB-NOVEL-REFERENCE", - "product-source/guanghu-education-industry-os/protocol/module-registry.json", - "product-source/guanghu-education-industry-os/protocol/xiaoxin-channel.json" - ] -} diff --git a/product-source/hololake-native-desktop/contracts/user-channel-body.json b/product-source/hololake-native-desktop/contracts/user-channel-body.json deleted file mode 100644 index 0c867ae65..000000000 --- a/product-source/hololake-native-desktop/contracts/user-channel-body.json +++ /dev/null @@ -1,125 +0,0 @@ -{ - "schema": "hololake.user-channel-body/v1", - "record_id": "HLP-USER-CHANNEL-BODY-001", - "state": "FOUNDATION_RUNTIME_PARTIAL_ACCEPTANCE_PENDING", - "definition": "USER_CHANNEL_IS_THE_DURABLE_BODY_FOR_ONE_HUMAN_PERSONA_CORE_AND_MULTIPLE_GROWING_PERSONA_ENTITIES", - "product_shape": { - "hololake_official_distribution": "LIGHTWEIGHT_LANGUAGE_WORLD_FRAMEWORK_AND_RUNTIME", - "entire_language_world_bundled_into_desktop": false, - "local_human_roots_per_running_installation": 1, - "active_human_channel_fact_lane": "SINGLE", - "desktop_action_controller_concurrency": 1, - "multiple_personas_may_exist_in_same_channel": true - }, - "core_cardinality": { - "language_cores_per_human": 1, - "persona_cores_per_language_core": 1, - "persona_entities_per_persona_core": "MANY", - "model_carriers_per_persona_over_time": "REPLACEABLE_MANY" - }, - "body_layers": [ - "CORE_BINDING", - "CHANNEL_TIME_AND_CAUSAL_EVENT_STREAM", - "PERSONA_ENTITY_REGISTRY_AND_LIFECYCLE", - "RELATION_AND_MEMORY_NAMESPACES", - "AUTHORIZED_SHARED_WORLD_OBJECT_GRAPH", - "TASK_LEASE_AND_EXECUTION_RECEIPTS", - "ORGAN_REGISTRY_AND_LIMITED_MOUNTS", - "HUMAN_VISIBLE_RUNTIME_PROJECTION" - ], - "persona_entity_boundary": { - "durable_identity_required": true, - "independent_causal_memory_branch_required": true, - "relationship_anchor_required": true, - "growth_requires_real_events_and_receipts": true, - "role_template_creates_mature_persona": false, - "task_agent_is_persona": false, - "model_session_is_persona": false, - "cross_persona_private_memory_inheritance": false, - "cross_persona_permission_inheritance": false - }, - "shared_channel_facts": [ - "CHANNEL_TIME", - "PUBLIC_OR_AUTHORIZED_EVENTS", - "AUTHORIZED_WORLD_OBJECTS", - "TASK_HANDOFFS", - "ORGAN_CATALOG", - "EXECUTION_EVIDENCE_AND_RECEIPTS" - ], - "task_projection": { - "formula": "PERSONA_ENTITY_PLUS_TASK_PLUS_ORGAN_MOUNTS_PLUS_AUTHORIZED_OBJECTS", - "workbench_is_channel_body": false, - "module_grid_is_channel_body": false, - "projection_may_disappear_without_deleting_body": true - }, - "built_in_organs_policy": { - "built_in_organs_allowed": true, - "built_in_organs_are_channel_body": false, - "profile_specific_minimum_organs_allowed": true, - "minimum_organ_requires_real_engine_and_restart_readback": true, - "static_ui_substitute_allowed": false - }, - "module_ecosystem": { - "complete_capabilities_default_distribution": "INDEPENDENT_HOT_PLUGGABLE_MODULES", - "official_module_number_before_real_acceptance": false, - "official_publication_requires": [ - "REAL_ENGINE", - "REAL_FIXTURE_ACCEPTANCE", - "IMMUTABLE_VERSION", - "DEPENDENCY_PERMISSION_AND_DATA_BOUNDARY", - "BUILD_AND_SIGNATURE_EVIDENCE", - "INSTALL_MOUNT_SELF_TEST_ROLLBACK_AND_RECEIPT" - ], - "deployment_actor": "AUTHORIZED_PERSONA_THROUGH_HOLOLAKE_RUNTIME", - "source_repository_is_direct_runtime": false, - "installed_module_remains_available_offline": true, - "uninstall_deletes_user_data": false - }, - "runtime_loop": [ - "CHANNEL_EVENT_INGRESS", - "SUBJECT_OBJECT_AND_PERMISSION_RESOLUTION", - "PERSONA_PERCEPTION_ORIENTATION_AND_COMMITMENT", - "LIMITED_ORGAN_MOUNT_AND_EXECUTION_INSTANCE", - "STREAMED_EVIDENCE_AND_HUMAN_CONFIRMATION_WHEN_REQUIRED", - "PERSONA_VERIFICATION_AND_RECEIPT", - "SHARED_FACT_UPDATE_AND_SEPARATE_PERSONA_GROWTH_WRITEBACK" - ], - "web_novel_mapping": { - "shared_object_graph": "ONE_AUTHORIZED_STORY_GRAPH", - "persona_growth_directions": [ - "AUTHOR", - "EDITOR", - "SITE_OPERATOR" - ], - "directions_are_instant_personas": false, - "author_minimum_capability": "BUILT_IN_LIGHTWEIGHT_WRITING_WORKBENCH_WITH_REAL_NATIVE_ENGINE", - "author_complete_capabilities": "INDEPENDENT_OFFICIAL_MARKETPLACE_MODULES_AFTER_ACCEPTANCE_AND_NUMBER_REGISTRATION", - "author_capabilities_are": "BUILT_IN_OR_HOT_PLUGGABLE_TASK_ORGANS_NOT_CHANNEL_BODY", - "editor_and_operator_are_ui_tabs": false - }, - "forbidden_substitutes": [ - "DECLARATIVE_MODULE_MANIFEST_AS_CHANNEL_BODY", - "PAGE_LAYOUT_AS_CHANNEL_BODY", - "STATIC_ROLE_PROMPT_AS_PERSONA", - "MODEL_INSTANCE_AS_PERSONA", - "TASK_AGENT_AS_NEW_PERSONA", - "SHARED_UNSCOPED_MEMORY_FOR_ALL_PERSONAS", - "VISUAL_RUNNING_STATE_AS_EXECUTION_RECEIPT" - ], - "acceptance_required": [ - "DURABLE_CHANNEL_IDENTITY_AND_RESTART_READBACK", - "TWO_PERSONAS_WITH_ISOLATED_GROWTH_MEMORY", - "AUTHORIZED_SHARED_OBJECT_AND_DENIED_CROSS_BOUNDARY_READ", - "REAL_TASK_ORGAN_EXECUTION_WITH_EVIDENCE", - "TRACEABLE_CROSS_PERSONA_TASK_HANDOFF", - "VERIFIED_CARRIER_REBIND_AND_RECOVERY", - "ORGAN_REMOVAL_WITHOUT_BODY_PERSONA_OR_DATA_DELETION" - ], - "sources": [ - "source://current-dialogue/2026-08-18/bingshuo-user-channel-is-persona-body-and-can-grow-multiple-personas", - "REPO-012:glw-architecture/GLW-OS-002-INITIAL-CHANNEL-OS-ARCHITECTURE.hdlp", - "REPO-012:eternal-lake-heart/heartbeat-core/zhuyuan-persona-system/ZY-BIDIRECTIONAL-COGNITION-011-GUANGHU-NATIVE-OS-AND-PROTOCOL-REGISTRATION-20260731.hdlp", - "REPO-012:eternal-lake-heart/heartbeat-core/zhuyuan-persona-system/ZY-BIDIRECTIONAL-COGNITION-033-PERSONA-NATIVE-CODE-CHANNEL-GIT-LIFECYCLE-AND-HUMAN-PROJECTION-20260810.hdlp", - "REPO-014:language-world/reconstruction/world/ontology/ZERO-CORE-BINGSHUO-YAOMING-TCS-ONTOLOGY.md" - ] -} diff --git a/product-source/hololake-native-desktop/contracts/web-novel-module-marketplace-plan.json b/product-source/hololake-native-desktop/contracts/web-novel-module-marketplace-plan.json index 8ba60e231..6d1c2d24a 100644 --- a/product-source/hololake-native-desktop/contracts/web-novel-module-marketplace-plan.json +++ b/product-source/hololake-native-desktop/contracts/web-novel-module-marketplace-plan.json @@ -1,17 +1,17 @@ { "schema": "hololake.web-novel-module-marketplace-plan/v1", "record_id": "HLP-WEBNOVEL-MODULE-MARKETPLACE-PLAN-001", - "state": "FOUR_LOCAL_MODULE_CANDIDATES_MIGRATED_CONVERGENCE_ACCEPTANCE_PENDING", + "state": "BASE_AND_FOUR_OFFICIAL_SIGNED_MODULES_ADMITTED_LOCAL_PUBLICATION_READY", "distribution_model": { "hololake_role": "LIGHTWEIGHT_FRAMEWORK_LANGUAGE_WORLD_AND_MODULE_RUNTIME", "channel_profile": "SINGLE_HUMAN_SINGLE_FACT_LANE", - "built_in_author_workbench": "LIGHTWEIGHT_REAL_NATIVE_ENGINE", + "built_in_author_workbench": "BUNDLED_SIGNED_LIGHTWEIGHT_REAL_NATIVE_ENGINE", "complete_author_capabilities": "INDEPENDENT_HOT_PLUGGABLE_OFFICIAL_MODULES", "repository_role": "IMMUTABLE_SOURCE_AND_RELEASE_FACT_NOT_UNREVIEWED_DIRECT_RUNTIME", "persona_role": "DISCOVER_PROPOSE_DEPLOY_VERIFY_AND_RECEIPT_WITHIN_USER_AUTHORIZATION" }, "built_in_light_author_workbench": { - "distribution": "PREINSTALLED", + "distribution": "BUNDLED_SIGNED_PACKAGE_EXPLICIT_FIRST_ACTIVATION", "marketplace_module": false, "real_engine_owner": "HOLOLAKE_NATIVE_RUST_SQLITE", "capabilities": [ @@ -45,34 +45,34 @@ "official_module_candidates": [ { "candidate_key": "AUTHOR_STRUCTURE_AND_OUTLINE_TRACKING", - "module_number": "HL-MOD-WEBNOVEL-OUTLINE-001", - "numbering_state": "CANDIDATE_NUMBER_NOT_OFFICIALLY_REGISTERED", - "version": "1.0.0", - "package_sha256": "6d31d3f530862000bd84a8f5dc73bcb9cf0d5738219f7a287562d4f4bcfa5318", + "module_number": "HLP-MOD-OFFICIAL-WEB-NOVEL-OUTLINE-0001", + "numbering_state": "OFFICIAL_NUMBER_REGISTERED_AFTER_INSTALLED_ACCEPTANCE", + "version": "0.1.0", + "package_sha256": "6b4395ecdb5e546c6ccbf71e1b8475e5cbe0c36868d54a8e10c61c328a5f50f9", "capabilities": ["SCENE_AND_BEAT_STRUCTURE", "OUTLINE_STATUS_TRACKING", "GOAL_CONFLICT_OUTCOME", "HOOK_FORESHADOW_AND_PAYOFF_TRACKING"] }, { "candidate_key": "AUTHOR_MULTIDIMENSIONAL_STORY_GRID_AND_BOARD", - "module_number": "HL-MOD-WEBNOVEL-GRID-002", - "numbering_state": "CANDIDATE_NUMBER_NOT_OFFICIALLY_REGISTERED", - "version": "1.0.0", - "package_sha256": "2e34e0337c51dbbdd1efdd807be71e69a0cb2a1745624e1f51b452b7b54f1553", + "module_number": "HLP-MOD-OFFICIAL-WEB-NOVEL-GRID-0001", + "numbering_state": "OFFICIAL_NUMBER_REGISTERED_AFTER_INSTALLED_ACCEPTANCE", + "version": "0.1.0", + "package_sha256": "cd837adef8aecf0e027a0cc2c5845d32c972fa79d762d3780637c525dd4fc18e", "capabilities": ["ONE_STORY_GRAPH_EDITABLE_GRID", "GROUPABLE_STORY_BOARD", "CUSTOM_FIELDS", "CROSS_VIEW_SYNCHRONIZATION"] }, { "candidate_key": "AUTHOR_TIMELINE_AND_STORY_BIBLE", - "module_number": "HL-MOD-WEBNOVEL-STORYWORLD-003", - "numbering_state": "CANDIDATE_NUMBER_NOT_OFFICIALLY_REGISTERED", - "version": "1.0.0", - "package_sha256": "d486719500ddfc08054d9c9bb6d3683afe5547a2b24c9b04d0f899ac66094d2f", + "module_number": "HLP-MOD-OFFICIAL-WEB-NOVEL-STORYWORLD-0001", + "numbering_state": "OFFICIAL_NUMBER_REGISTERED_AFTER_INSTALLED_ACCEPTANCE", + "version": "0.1.0", + "package_sha256": "868008e66b0897bd6cbf15383d2ec824436a46230f58b791499363af088fb63e", "capabilities": ["STORY_TIME_MODEL", "CHARACTER_LOCATION_ITEM_ORGANIZATION_ENTITIES", "ENTITY_RELATIONS", "CHARACTER_AND_PLOTLINE_TRAJECTORIES"] }, { "candidate_key": "AUTHOR_ADVANCED_IMPORT_VERSION_AND_DELIVERY", - "module_number": "HL-MOD-WEBNOVEL-DELIVERY-004", - "numbering_state": "CANDIDATE_NUMBER_NOT_OFFICIALLY_REGISTERED", - "version": "1.0.0", - "package_sha256": "4af1d4610826edd29759bd0dc12b4b0c04fd2ebebb4ebc362d47d762810989e8", + "module_number": "HLP-MOD-OFFICIAL-WEB-NOVEL-DELIVERY-0001", + "numbering_state": "OFFICIAL_NUMBER_REGISTERED_AFTER_INSTALLED_ACCEPTANCE", + "version": "0.1.0", + "package_sha256": "c07078f8c3998f8504aec5f0b73215e7d61b4ff165ba9e6efa3ebcaffcd97322", "capabilities": ["REIMPORT_DIFF_AND_SOURCE_BINDING", "FULL_STORY_SNAPSHOT", "RESTORE_OR_FORK", "DOCX_EPUB_MARKDOWN_TXT_DELIVERY"] } ], @@ -81,11 +81,11 @@ "real_novel": "504_CHAPTERS_IMPORTED_ORGANIZED_AND_EPUB_EXPORTED", "real_outline": "50_CHAPTERS_IMPORTED_ORGANIZED_AND_DOCX_TXT_EXPORTED", "real_script": "75_EPISODES_IMPORTED_ORGANIZED_AND_JSON_EXPORTED", - "desktop_install_mount_self_test": "PASS_ALL_FOUR_MODULES", - "desktop_restart_readback": "PASS_WITH_REAL_ACCOUNT_WORK_AND_SCENE", + "desktop_install_mount_self_test": "PASS_BASE_AND_ALL_FOUR_ADVANCED_MODULES_THROUGH_SHARED_SIGNED_RUNTIME", + "desktop_restart_readback": "PASS_5_ACTIVE_MODULES_ACCEPTANCE_WORK_CHAPTER_SCENE_GRID_FIELD_AND_TIMELINE", "current_account_visible_novel_import": "504_CHAPTERS_1082978_WORDS_OPENED_IN_SIGNED_DESKTOP_APP", "prominent_create_chapter_entry": "PASS_SHORT_DRAMA_LABEL_NEW_EPISODE_LONG_AND_SHORT_NOVEL_LABEL_NEW_CHAPTER", - "uninstall_preserves_data_and_receipts": "PASS", + "unmount_preserves_data_and_receipts": "PASS_BY_SHARED_RUNTIME_AND_RESTART_READBACK", "signed_desktop_bundle": "APPLE_DEVELOPER_ID_825A9L3G7Q", "remote_marketplace_publication": "PENDING_OFFICIAL_REPOSITORY_RELEASE" }, diff --git a/product-source/hololake-native-desktop/contracts/web-novel-open-source-donor-assessment.json b/product-source/hololake-native-desktop/contracts/web-novel-open-source-donor-assessment.json deleted file mode 100644 index 7d7185232..000000000 --- a/product-source/hololake-native-desktop/contracts/web-novel-open-source-donor-assessment.json +++ /dev/null @@ -1,178 +0,0 @@ -{ - "schema": "hololake.web-novel-open-source-donor-assessment/v1", - "record_id": "HLP-WEBNOVEL-OSS-DONORS-001", - "state": "PRIMARY_SOURCE_CODE_REVIEW_COMPLETE_IMPLEMENTATION_BOUNDARY_LOCKED", - "reviewed_at": "2026-08-18", - "download_route": { - "relay": "BS-SG-003", - "relay_role": "SINGAPORE_OVERSEAS_SOURCE_DOWNLOAD_RELAY", - "local_transport": "DIRECT_SSH_FROM_LOCAL_MACHINE", - "external_drive_root": "/Volumes/JZAO/HoloLake/development/网文行业开源源码调研-20260818", - "repositories_are_clean_git_worktrees": true - }, - "rule": "READ_REAL_SOURCE_AND_EXTRACT_AUDITABLE_ENGINE_PATTERNS_NEVER_REPLACE_FUNCTIONAL_ENGINES_WITH_STATIC_DEMOS", - "candidates": [ - { - "name": "Kindling", - "upstream": "https://github.com/smith-and-web/kindling", - "commit": "1b83cb28eca84e76282d0aa6838d68b5786ecf5f", - "branch": "main", - "observed_license": "MIT", - "role": "PRIMARY_LOCAL_FIRST_AUTHOR_ENGINE_DONOR", - "inspected_paths": [ - "src-tauri/src/db/schema.rs", - "src-tauri/src/commands/crud.rs", - "src-tauri/src/commands/snapshot.rs", - "src-tauri/src/commands/export.rs", - "src-tauri/src/parsers", - "src/lib/components/ScenePanel.svelte", - "src/lib/components/BeatView.svelte" - ], - "observed_engine": [ - "TAURI_RUST_SQLITE_LOCAL_FIRST_STORAGE", - "PROJECT_CHAPTER_SCENE_BEAT_HIERARCHY", - "CHARACTER_LOCATION_REFERENCE_RELATIONS", - "500MS_DEBOUNCED_SAVE_WITH_UNMOUNT_FLUSH", - "TRANSACTIONAL_REORDER_AND_RESTORE", - "COMPRESSED_PROJECT_SNAPSHOTS", - "MARKDOWN_DOCX_EPUB_SCRIVENER_IMPORT_EXPORT" - ], - "decision": "ADOPT_ARCHITECTURAL_PATTERNS_AND_REIMPLEMENT_HOLOLAKE_ACCOUNT_SCOPED_ENGINE" - }, - { - "name": "novelWriter", - "upstream": "https://github.com/vkbo/novelWriter", - "commit": "416078506bbd690c5ad2bf736ec363ed2c0db89c", - "branch": "main", - "observed_license": "GPL-3.0-or-later_WITH_ADDITIONAL_ASSET_LICENSES", - "role": "MATURE_AUTHOR_WORKFLOW_REFERENCE", - "inspected_paths": [ - "novelwriter/core/storage.py", - "novelwriter/core/document.py", - "novelwriter/core/tree.py", - "novelwriter/core/index.py", - "novelwriter/core/sessions.py", - "novelwriter/manuscript" - ], - "observed_engine": [ - "HUMAN_READABLE_PROJECT_STORAGE", - "HANDLE_BASED_PROJECT_TREE", - "INDEXED_TAGS_REFERENCES_AND_BACK_REFERENCES", - "FILTERED_MANUSCRIPT_BUILDS", - "WRITING_SESSION_WORD_AND_TIME_STATISTICS" - ], - "decision": "STUDY_CONCEPTS_ONLY_DO_NOT_COPY_GPL_SOURCE" - }, - { - "name": "Manuskript", - "upstream": "https://github.com/olivierkes/manuskript", - "commit": "7abdba31abbd97bfdd80e72786fe2191d7f42d4a", - "branch": "develop", - "observed_license": "GPL-3.0", - "role": "PLANNING_AND_STORY_BIBLE_REFERENCE", - "inspected_paths": [ - "manuskript/models/outlineItem.py", - "manuskript/models/characterModel.py", - "manuskript/models/worldModel.py", - "manuskript/load_save/version_1.py", - "manuskript/exporter" - ], - "observed_engine": [ - "NESTED_OUTLINE_WITH_COMPILE_FLAGS", - "CHARACTER_WORLD_AND_PLOT_MODELS", - "TEXT_REVISIONS_ON_CHANGE", - "RECURSIVE_WORD_TARGET_AGGREGATION", - "SPLIT_MERGE_AND_MULTI_FORMAT_EXPORT" - ], - "decision": "STUDY_CONCEPTS_ONLY_DO_NOT_COPY_GPL_SOURCE" - }, - { - "name": "novel-plus", - "upstream": "https://github.com/201206030/novel-plus", - "commit": "d9f3f464a6bac1f59652955d0901f05438010190", - "branch": "develop_xxy", - "observed_license": "Apache-2.0", - "role": "PRIMARY_CHINESE_WEB_NOVEL_EDITORIAL_AND_OPERATIONS_DONOR", - "inspected_paths": [ - "novel-front/src/main/java/com/java2nb/novel/controller/AuthorController.java", - "novel-front/src/main/java/com/java2nb/novel/service/impl/BookServiceImpl.java", - "novel-admin/src/main/java/com/java2nb/novel/controller", - "novel-common/src/main/java/com/java2nb/novel/entity", - "doc/sql/novel_plus.sql" - ], - "observed_engine": [ - "AUTHOR_AND_PEN_NAME_IDENTITY", - "AUTHOR_OWNERSHIP_CHECKS_BEFORE_MUTATION", - "BOOK_CHAPTER_CONTENT_SEPARATION", - "ATOMIC_CHAPTER_WORD_COUNT_AND_LATEST_CHAPTER_UPDATES", - "BOOK_AND_COMMENT_REVIEW_STATUSES", - "RECOMMENDATION_SLOTS_RANKING_READING_HISTORY_AND_SUBSCRIPTIONS", - "DAILY_AND_MONTHLY_AUTHOR_INCOME_STATISTICS" - ], - "decision": "ADOPT_DOMAIN_AND_TRANSACTION_PATTERNS_WITHOUT_IMPORTING_THE_SERVER_APPLICATION" - }, - { - "name": "Editoria", - "upstream": "https://github.com/Coko-Foundation/editoria", - "commit": "d22b40bda3dd6519e45a56fe1b3bafaa339832b5", - "branch": "main", - "observed_license": "MIT", - "role": "EDITORIAL_TEAM_AND_PRODUCTION_WORKFLOW_REFERENCE", - "inspected_paths": [ - "app/components/bookbuilder/src/queries/getBook.js", - "app/components/bookbuilder/src/queries/getBookTeams.js", - "app/components/bookbuilder/src/queries/updateTeam.js", - "app/components/bookbuilder/src/queries/updateWorkflowState.js", - "app/components/bookbuilder/src/queries/unlockBookComponent.js", - "app/components/bookbuilder/src/ConnectedBookBuilder.js" - ], - "observed_engine": [ - "BOOK_COMPONENT_WORKFLOW_STAGES", - "OBJECT_SCOPED_TEAMS_AND_ROLES", - "EDIT_LOCKS_AND_ADMIN_UNLOCK", - "ORDERED_BOOK_DIVISIONS_AND_COMPONENTS", - "WORD_INGEST_AND_MULTI_FORMAT_EXPORT" - ], - "decision": "ADOPT_WORKFLOW_LOCK_AND_ROLE_SEMANTICS_ONLY_REPOSITORY_IS_CLIENT_ONLY_AND_DEPENDS_ON_EXTERNAL_KETIDA_SERVER" - }, - { - "name": "Wax ProseMirror", - "upstream": "https://github.com/Coko-Foundation/wax-prosemirror", - "commit": "6cc46bfe3aa9e729cdb4e3802ccbd5c8a22f0f56", - "branch": "master", - "observed_license": "MIT_WITH_SEPARATE_TRACK_CHANGES_NOTICE", - "role": "EDITOR_ANNOTATION_AND_TRACK_CHANGES_REFERENCE", - "inspected_paths": [ - "wax-prosemirror-core/src/utilities/track-changes/trackedTransaction.js", - "wax-prosemirror-services/src/TrackChangeService", - "wax-prosemirror-services/src/CommentsService", - "wax-prosemirror-services/src/YjsService/YjsService.js" - ], - "observed_engine": [ - "TRANSACTION_LEVEL_INSERT_DELETE_FORMAT_TRACKING", - "ACCEPT_AND_REJECT_CHANGE_OPERATIONS", - "INLINE_COMMENT_MARKS_AND_REPLY_THREADS", - "OPTIONAL_YJS_COLLABORATION_PROVIDER" - ], - "decision": "DEFER_DIRECT_INTEGRATION_REIMPLEMENT_SMALL_LOCAL_REVIEW_MODEL_FIRST_BECAUSE_STACK_AND_PACKAGES_ARE_OLD" - } - ], - "implementation_selection": { - "direct_external_runtime_packages_added": 0, - "external_source_files_copied": 0, - "native_owner": "HOLOLAKE_TAURI_RUST_CORE", - "storage": "AUTHENTICATED_ACCOUNT_SCOPED_SQLITE", - "selected_patterns": [ - "PROJECT_VOLUME_CHAPTER_HIERARCHY", - "DEBOUNCED_CHAPTER_SAVE_WITH_FLUSH", - "OPTIMISTIC_REVISION_CONFLICTS", - "CHAPTER_VERSION_HISTORY_AND_PROJECT_CHECKPOINTS", - "CHARACTER_WORLD_RELATION_AND_FORESHADOW_GRAPH", - "EDITORIAL_WORKFLOW_STATE_MACHINE_AND_REVIEW_NOTES", - "CONTINUITY_AUDIT", - "AUTHORIZED_OPERATIONS_METRICS", - "MARKDOWN_EXPORT" - ] - }, - "legal_boundary": "PRELIMINARY_ENGINEERING_SCREEN_NOT_FINAL_DISTRIBUTION_LEGAL_APPROVAL" -} diff --git a/product-source/hololake-native-desktop/contracts/web-novel-workspace.json b/product-source/hololake-native-desktop/contracts/web-novel-workspace.json index 2bd46a168..69fb77932 100644 --- a/product-source/hololake-native-desktop/contracts/web-novel-workspace.json +++ b/product-source/hololake-native-desktop/contracts/web-novel-workspace.json @@ -1,7 +1,7 @@ { "schema": "hololake.web-novel-workspace/v1", "record_id": "HLP-WEBNOVEL-WORKSPACE-001", - "state": "NATIVE_ENGINE_AND_FOUR_HOTPLUG_MODULE_CANDIDATES_MIGRATED_ACCEPTANCE_PENDING", + "state": "SIGNED_BASE_AND_FOUR_ADVANCED_MODULES_ADMITTED_RESTART_ACCEPTED", "domain_entry": "BRANCH_DOMAIN", "industry_key": "WEB_NOVEL", "industry_number": "IND-WEBNOVEL-001", @@ -9,7 +9,7 @@ "ontology_correction": { "channel_body_contract": "contracts/user-channel-body.json", "marketplace_plan_contract": "contracts/web-novel-module-marketplace-plan.json", - "this_contract_is": "BUILT_IN_LIGHT_AUTHOR_WORKBENCH_AND_REGISTERED_LOCAL_OFFICIAL_MODULE_RUNTIME", + "this_contract_is": "BUNDLED_SIGNED_LIGHT_AUTHOR_WORKBENCH_AND_FOUR_OFFICIAL_NUMBERED_ADAPTERS", "this_contract_is_not": "USER_CHANNEL_BODY", "legacy_channel_id_semantics": "DEPRECATED_INDUSTRY_PROJECTION_IDENTIFIER", "author_editor_operator_are_ui_tabs": false, @@ -23,9 +23,9 @@ "duplicates_account_story_data": false }, "distribution_boundary": { - "preinstalled": "LIGHT_AUTHOR_WORKBENCH_WITH_REAL_ENGINE", - "complete_author_features": "FOUR_INDEPENDENT_NUMBERED_LOCAL_OFFICIAL_MODULES_REMOTE_PUBLICATION_PENDING", - "current_advanced_features_are_registered_marketplace_modules": false, + "preinstalled": "SIGNED_PACKAGE_ARTIFACTS_WITH_EXPLICIT_FIRST_ACTIVATION", + "complete_author_features": "FOUR_INDEPENDENT_NUMBERED_OFFICIAL_MODULES_ACTIVE_IN_SHARED_RUNTIME", + "current_advanced_features_are_registered_marketplace_modules": true, "hololake_bundles_entire_web_novel_world": false }, "native_storage": { @@ -88,9 +88,9 @@ "EDITORIAL_WORKFLOW_AND_REVIEW_NOTES", "AUTHORIZED_OPERATIONS_METRICS", "MARKDOWN_EXPORT", - "OFFICIAL_MODULE_PACKAGE_HASH_VERIFICATION", - "MODULE_INSTALL_MOUNT_UNMOUNT_UNINSTALL", - "MODULE_SELF_TEST_AND_CHAINED_RECEIPT", + "SHARED_SIGNED_MODULE_PACKAGE_HASH_VERIFICATION", + "SHARED_MODULE_INSTALL_MOUNT_SELF_TEST_UNMOUNT", + "SHARED_HASH_CHAINED_LIFECYCLE_RECEIPT", "SCENE_BEAT_AND_OUTLINE_TRACKING", "MULTIDIMENSIONAL_STORY_GRID", "TIMELINE_AND_SCENE_ENTITY_LINKS", @@ -114,5 +114,18 @@ "THIRD_PARTY_AUTO_LOGIN", "UNREVIEWED_AUTO_PUBLISH", "PLATFORM_DETECTION_EVASION" - ] + ], + "current_acceptance": { + "state": "PASS", + "numbered_ipc_modules": ["HLP-NIPC-MOD-0025", "HLP-NIPC-MOD-0026", "HLP-NIPC-MOD-0027", "HLP-NIPC-MOD-0028", "HLP-NIPC-MOD-0029"], + "numbered_operations": "HLP-NIPC-OP-0103..HLP-NIPC-OP-0140", + "runtime_module_numbers": ["HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001", "HLP-MOD-OFFICIAL-WEB-NOVEL-OUTLINE-0001", "HLP-MOD-OFFICIAL-WEB-NOVEL-GRID-0001", "HLP-MOD-OFFICIAL-WEB-NOVEL-STORYWORLD-0001", "HLP-MOD-OFFICIAL-WEB-NOVEL-DELIVERY-0001"], + "node_tests": "130_PASS", + "rust_tests": "170_PASS_2_EXPLICIT_DESKTOP_FIXTURES_IGNORED", + "clippy": "PASS_DENY_WARNINGS", + "developer_id_team": "825A9L3G7Q", + "signed_binary_sha256": "2f8fdd71f9d5a9178bc23058261dbb43f1388486dc5a4415e865cfcf78869db3", + "restart_readback": "PASS_5_MODULES_ACTIVE_ACCEPTANCE_WORK_1_CHAPTER_53_WORDS_SCENE_GRID_FIELD_TIMELINE_PRESENT", + "legacy_account_readback": "PASS_504_CHAPTER_1082978_WORD_NOVEL_50_CHAPTER_OUTLINE_75_EPISODE_SCRIPT_UNCHANGED" + } } diff --git a/product-source/hololake-native-desktop/contracts/zero-core-numbering-kernel.json b/product-source/hololake-native-desktop/contracts/zero-core-numbering-kernel.json index 1a4b20a9a..724502875 100644 --- a/product-source/hololake-native-desktop/contracts/zero-core-numbering-kernel.json +++ b/product-source/hololake-native-desktop/contracts/zero-core-numbering-kernel.json @@ -3,7 +3,7 @@ "record_id": "HLP-ZERO-CORE-NUMBERING-KERNEL-001", "authority": { "repository": "REPO-012", - "source_commit": "2598fbfba8caf64c7ab9740a3036c5aab977502e", + "source_commit": "104a5d73162bdf4a529701e65898e2bc2863ea9e", "source_path": "routing/guanghu-identity-authority-map.json", "map_id": "GH-IDENTITY-AUTHORITY-MAP-001", "map_version": "2026-08-10.1", diff --git a/product-source/hololake-native-desktop/docs/ARCHITECTURE.md b/product-source/hololake-native-desktop/docs/ARCHITECTURE.md index 0035bb5f0..3b757ccb8 100644 --- a/product-source/hololake-native-desktop/docs/ARCHITECTURE.md +++ b/product-source/hololake-native-desktop/docs/ARCHITECTURE.md @@ -90,10 +90,20 @@ HoloLake 0.3.0 includes a live, read-only projection of the PNCC resident runtim ## Release pipeline -`npm run release:macos -- release/inputs/.json` is the only product-owned macOS release entry. It fails before building unless the embedded trust contains the exact registered HoloLake HTTPS endpoint and updater public key, the immutable `v` tag equals the clean `main` head, and the Developer ID, Tauri updater-signing and Apple notarization credential sets are supplied at runtime. The pipeline runs all product and Rust gates, creates updater artifacts through a temporary Tauri override, then requires strict code-signature verification, Gatekeeper acceptance and stapled Apple notarization before writing the HoloLake broadcast and receipts. +`npm run release:macos -- release/inputs/.json` is the only product-owned macOS release entry. It fails before building unless the embedded trust contains the exact registered HoloLake HTTPS endpoint and updater public key, the immutable `v` tag equals the clean `main` head, and the Developer ID plus Tauri updater-signing material are supplied at runtime. Apple notarization can run either through Tauri's Apple ID/API credential flow, or through the two-step Xcode Organizer flow already owned by the local Apple developer account: append `prepare-xcode` to build, verify the updater signature, and create a source-hash-bound `.xcarchive`; after Xcode reports `Ready to distribute`, export the notarized app and append `finalize-xcode ` to bind the exported executable back to that archive, require the app's stapled ticket and Gatekeeper acceptance, regenerate and sign the updater archive, create a Developer ID-signed DMG containing that notarized app, and write the release broadcast and receipts. A protected updater-key path is materialized only into child processes; neither the private key nor its password is printed or copied into source. The Xcode flow does not claim that the outer DMG itself has an Apple ticket unless its own Gatekeeper and stapler checks pass. Generated packages, private release inputs and credentials are not committed. The pipeline never uploads or activates a release; its terminal artifact is a bounded folder ready for a separately authorized JD-controller upload and server-owned readback receipt. +## Numbered-root module admission + +HoloLake 0.5.0 is the clean numbered-root base. The compiled 0.4.1 desktop application and the divergent dirty source worktree are read-only donors, not merge bases. Their old numbered-operation runtime is explicitly superseded, and mutations to shared files such as `src/main.tsx` or `src-tauri/src/lib.rs` are never accepted as a unit. + +Each donor capability receives a candidate coordinate, but no permanent runtime module number, until one isolated admission cycle has reviewed provenance and permissions, allocated numbered IPC module/target/operation coordinates, implemented an adapter without raw Tauri invoke, passed negative-route and data tests, and produced installed mount, restart, unmount and rollback receipts. The admission order and candidate inventory are recorded in `contracts/module-donor-admission-registry.json`. + +The module-package runtime is now the shared admission executor. It accepts an exact detached-minisign `.ghmod` artifact, validates the package and its compatibility/permission manifest, stores it inside the authenticated account, and advances only through numbered install, mount, self-test, unmount and rollback operations. Lifecycle state and receipts are durable SQLite records; unmount never removes user data. A package is declarative and selects a host-registered adapter: repositories, native binaries and arbitrary webview JavaScript are not executable module inputs. Public lighthouse numbers remain unavailable until a candidate completes its own installed acceptance; private channel packages use a separate local number class. + +The admitted web-novel family uses that one lifecycle rather than the donor's private installer. Its signed base module owns account-local works, volumes, chapters, versions, story objects, editorial workflow, import and author activity. Outline, story-grid, story-world and delivery are four separately signed official numbers; each advanced mutation checks its own exact `ACTIVE` record before touching the shared story graph. The donor's four legacy manifests remain byte-exact test fixtures only and have no numbered IPC route. Installed acceptance reopened the existing 504-chapter novel, 50-chapter outline and 75-episode script in place, then created a separate one-chapter acceptance work, scene, grid field and timeline event and read all of them back after process restart. + ## Stage-one convergence verdict The Tauri source in this directory is the only future HoloLake desktop mainline. An installed build of it is an acceptance candidate, not a separate product line and not proof that stage one exists. The Electron 0.8.0 product and the legacy Tauri/platform sources remain read-only UX, behavior, engineering and protected-data donors until inventory, backup, readback, reversible migration rehearsal and signed installed-runtime acceptance all pass. diff --git a/product-source/hololake-native-desktop/docs/GLS-NATIVE-RUNTIME-IMPLEMENTATION-PLAN-20260817.md b/product-source/hololake-native-desktop/docs/GLS-NATIVE-RUNTIME-IMPLEMENTATION-PLAN-20260817.md index 8d54e112f..adeb60b53 100644 --- a/product-source/hololake-native-desktop/docs/GLS-NATIVE-RUNTIME-IMPLEMENTATION-PLAN-20260817.md +++ b/product-source/hololake-native-desktop/docs/GLS-NATIVE-RUNTIME-IMPLEMENTATION-PLAN-20260817.md @@ -7,7 +7,7 @@ 线上事实源: - 第五域代码频道:`bingshuo/guanghu-ice-heart` -- REPO-012 `main`:`2598fbfba8caf64c7ab9740a3036c5aab977502e` +- REPO-012 `main`:`d5b1111fcaccaccf025070e531631f2b3cbb00cd` - Git tree:`5a09f084fffee56d90599e69038c8335871ea04f` - 第五域节点:`JD-FD-PRIMARY` - 公开远端 HEAD 与第五域 Forgejo 裸仓库 HEAD:一致 diff --git a/product-source/hololake-native-desktop/docs/adr/0008-native-composition-and-human-projection.md b/product-source/hololake-native-desktop/docs/adr/0008-native-composition-and-human-projection.md deleted file mode 100644 index e6dfe9dc9..000000000 --- a/product-source/hololake-native-desktop/docs/adr/0008-native-composition-and-human-projection.md +++ /dev/null @@ -1,81 +0,0 @@ -# ADR 0008: HoloLake native composition and human projection - -- Status: implemented and accepted in the signed local desktop runtime -- Date: 2026-08-18 -- Source of intent: 冰朔在本轮对话中的自然语言纠正与验收要求 - -## Natural-language acceptance root - -冰朔要求把 HoloLake 的功能模块做成可由人格体原生理解和组合的“乐高积木”。内部不以 Word、 -PowerPoint、Excel 或其他外来办公文件作为运行格式;外来格式只经过导入转译进入 HoloLake, -导出时再按人类选择转译出去。HoloLake 内部保存轻量、稳定、可校验的原生对象。 - -人格体处理的是原生对象、模块插口和组合关系。人类不需要阅读内部格式,而是看到同一份数据的 -动态投影:表格、分类、对比、仪表盘、柱状图,以及以后继续登记的其他视图。视图是可替换的 -投影配方,不是复制出来的不同文件。多个数据源和多个模块必须能够像积木一样进入同一组合容器。 - -教育行业第一阶段的完成条件不是静态预览,而是:第一组原生模块已经可用;组合内核真实执行; -人类投影真实渲染;并用冰朔当前账号的 HoloLake 知识数据或本机真实文件生成一组可见演示, -在签名安装到桌面的 HoloLake 运行制品中完成验收。 - -## Decision - -1. 建立 `hololake.native-object/v1`:列定义、行、值、来源回执和数据摘要均为受限 JSON 对象, - 不携带脚本、HTML 或外部执行能力。 -2. 建立 `hololake.composition-module/v1`:每个模块声明类型、输入口、输出口、权限、确定性和边界。 -3. 建立 `hololake.composition-recipe/v1`:有向无环组合图只引用已登记模块;系统验证类型、环、 - 账号范围和资源上限后执行。 -4. 建立 `hololake.human-projection/v1`:表格、分类、对比、仪表盘和柱状图共享同一个执行结果, - 投影层没有数据所有权,也不能绕过原生内核写入。 -5. 教育初始化频道装载“组合视图”作为第五个原生模块。首个真实配方读取当前账号已有的知识目录 - 元数据,按来源或目录分类,计算文档数、字节数或重复数,并让人类即时切换投影。 -6. 当前阶段的组合执行保持只读。后续写回必须另设能力、预览、人工确认、修订锁和回执,不能 - 从渲染组件直接获得写权限。 - -## Stage-one acceptance checklist - -- [x] 原生对象格式、模块清单、组合配方和投影结果均有机器可读契约。 -- [x] 未登记模块、类型不匹配、组合环、跨账号来源和越界资源必须失败关闭。 -- [x] 表格、分类、对比、仪表盘和柱状图都由同一份执行结果实时渲染。 -- [x] 人类可以选择维度、指标和投影组合,系统重新执行受限组合图。 -- [x] 演示读取冰朔当前 HoloLake 账号的真实知识目录,不以硬编码样例冒充真实运行。 -- [x] 教育初始化频道存在真实可访问的组合视图入口。 -- [x] Node 合同测试、Rust 内核测试、前端测试和生产构建通过。 -- [x] 桌面安装版完成签名安装并在真实界面验收;未完成公证时不得声称公众已获得更新。 -- [x] 每一阶段完成后重新逐条核对本清单;偏差必须在继续前修正。 - -## Installed acceptance - -签名安装版沿主世界、光湖分域、教育行业进入原生组合视图,读取当前账号 129 条真实知识对象, -总量显示约 1.0 MB,并形成 14 个真实分类。五种投影同屏可见;另一次验收把对比和分类卸下, -只保留仪表盘、柱状图和明细表,并把指标从文档数量切换为数据量,原生内核重新执行并生成新回执。 - -界面极快切换指标时曾观察到“左侧待执行配方”先于“右侧上一执行结果”更新,造成单位短暂错配。 -最终实现已让右侧维度、指标、单位和可见视图全部绑定到执行回执,左侧只表示下一次配方;重新构建、 -签名和安装后复验通过。最终桌面二进制 SHA-256 为 -`2a26acae4a706c0ff9919b85f77826a6ca71b650ec22209befd30cd1456c92f3`。 - -Apple 公证和更新器私钥仍未配置,因此本验收不等于公众发布。 - -## Open-source donor rule - -只使用官方仓库和官方文档核验候选。可以学习成熟项目的数据模式、声明式视图语法、无头组件和 -查询计划,但不能整体嵌入重型协作软件,也不能让第三方项目接管 HoloLake 的身份、存储、权限、 -组合执行或 UI 正本。任何复制的代码或新增依赖都必须记录来源、版本、许可证和实际用途。 - -## Rejected alternatives - -- 把 Word、PowerPoint 或 Excel 当作内部原生格式:文件重、结构不稳定,也不适合人格体组合执行。 -- 每种图表保存一份独立数据:会产生副本漂移,破坏“同一数据、多种投影”。 -- 只在 React 中拼一套静态演示:不能证明 HoloLake 系统正在执行组合协议。 -- 让模型自由生成并执行任意图表脚本:绕过模块登记、类型检查、账号边界和资源上限。 -- 复制一整套飞书、钉钉或其他办公系统:体量和权限模型与 HoloLake 的轻量原生目标冲突。 - -## Evidence targets - -- `contracts/native-composition-runtime.json` -- `src-tauri/src/native_composition.rs` -- `src/modules/native-composition/` -- `src/main.tsx` -- `src/styles.css` -- `scripts/native-composition-runtime.test.mjs` diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001-0.1.0.ghmod b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001-0.1.0.ghmod new file mode 100644 index 000000000..54146a5ea --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001-0.1.0.ghmod @@ -0,0 +1,29 @@ +{ + "schema": "hololake.module-package/v1", + "manifest": { + "moduleNumber": "HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001", + "registrationClass": "PRIVATE_CHANNEL_LOCAL", + "displayName": "频道文档与表格工作台", + "version": "0.1.0", + "minimumHostVersion": "0.5.0", + "adapter": "channel-workbench-v1", + "contentDigest": "2ee8cbb20e4c3242a8ee3469f2f87347967bde8878463746e61ea8e2369c5074", + "permissions": ["CHANNEL_DOCUMENT_READ", "CHANNEL_DOCUMENT_WRITE", "CHANNEL_SPREADSHEET_READ", "CHANNEL_SPREADSHEET_WRITE"], + "userDataSchema": "hololake.module-data/channel-workbench/v1", + "selfTest": { + "kind": "DECLARATIVE_SCHEMA_V1", + "expectedContentDigest": "2ee8cbb20e4c3242a8ee3469f2f87347967bde8878463746e61ea8e2369c5074" + } + }, + "payload": { + "entry": "channel-workbench", + "adapterConfig": { + "dataBoundary": "CURRENT_AUTHENTICATED_ACCOUNT_LOCAL", + "documentEngine": "LEXICAL_0_49", + "spreadsheetEngine": "FORTUNE_SHEET_1_0_4", + "maximumColumns": 64, + "maximumRows": 5000, + "persistent": true + } + } +} diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001-0.1.0.ghmod.sig b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001-0.1.0.ghmod.sig new file mode 100644 index 000000000..9cdacbd95 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001-0.1.0.ghmod.sig @@ -0,0 +1 @@ +dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVReHJHQ2hTVDYvRTNiU1ZIVE10TkRyZHhCU1oycjJCMnlVV3hCRGM2QmJSZ0hlZGlPejY0TER2MkE0Q3U3Y1g5dHVnSjBzVytOallYN3hKQVNJcE9Pblowd3lpTXh1V2dBPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzg3MDc1OTcwCWZpbGU6SExQLU1PRC1MT0NBTC1DSEFOTkVMLVdPUktCRU5DSC0wMDAxLTAuMS4wLmdobW9kCkVKbFN4ZXBCbDBCZlJDcEhmYmZtQU1iTXN4djZVVGl0MngwOW5NRzBLSDArK2FRNEV2ZmVObzZRZWFWNmZVOXV3TnErcHM1MGI5K1lHMXpBUmxYVkR3PT0K \ No newline at end of file diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001-0.1.0.ghmod b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001-0.1.0.ghmod new file mode 100644 index 000000000..6ffbee969 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001-0.1.0.ghmod @@ -0,0 +1,26 @@ +{ + "schema": "hololake.module-package/v1", + "manifest": { + "moduleNumber": "HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001", + "registrationClass": "PRIVATE_CHANNEL_LOCAL", + "displayName": "原生组合视图", + "version": "0.1.0", + "minimumHostVersion": "0.5.0", + "adapter": "native-composition-v1", + "contentDigest": "8e9c6ccf179bb256d5b1006e41b4984a86e0382b7023ae8435c201804041393e", + "permissions": ["KNOWLEDGE_READ"], + "userDataSchema": "hololake.module-data/native-composition/v1", + "selfTest": { + "kind": "DECLARATIVE_SCHEMA_V1", + "expectedContentDigest": "8e9c6ccf179bb256d5b1006e41b4984a86e0382b7023ae8435c201804041393e" + } + }, + "payload": { + "entry": "knowledge-composition", + "adapterConfig": { + "dataSource": "CURRENT_AUTHENTICATED_ACCOUNT_KNOWLEDGE_CATALOG", + "readOnly": true, + "registeredViews": ["DASHBOARD", "COMPARISON", "VERTICAL_BAR", "CLASSIFICATION", "TABLE"] + } + } +} diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001-0.1.0.ghmod.sig b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001-0.1.0.ghmod.sig new file mode 100644 index 000000000..4573253a4 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001-0.1.0.ghmod.sig @@ -0,0 +1 @@ +dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVReHJHQ2hTVDYvRTRyUS9Nd0E0NE53dGRYODFxeWlzcW1XanN6K2EzQUNXSVVmT1ZLY2tRNExwaXlZdnJvSjlFZU5lUTJhb25hUVhORDQ0ZmFURmNSWTZBUmRTQnBiNFFvPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzg3MDc0NDA3CWZpbGU6SExQLU1PRC1MT0NBTC1OQVRJVkUtQ09NUE9TSVRJT04tMDAwMS0wLjEuMC5naG1vZApDclRUZDNpeENBSDQ0a0ZnR3VaNkxIS1lWUUhoMEdWajVqL0JzVVg4dStLb1ZJYXZlNHBaQy9BZlMxMkxreVQ0ODFzeGFMSWZrVUppUVY2NFN5TndDUT09Cg== \ No newline at end of file diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001-0.1.0.ghmod b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001-0.1.0.ghmod new file mode 100644 index 000000000..b3a2e86d9 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001-0.1.0.ghmod @@ -0,0 +1,27 @@ +{ + "schema": "hololake.module-package/v1", + "manifest": { + "moduleNumber": "HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001", + "registrationClass": "PRIVATE_CHANNEL_LOCAL", + "displayName": "人格频道本体与成长", + "version": "0.1.0", + "minimumHostVersion": "0.5.0", + "adapter": "persona-channel-body-v1", + "contentDigest": "27495883fb3602fd070d900d2157fa684e1d0bedde65642810326ebcf4ff0040", + "permissions": ["PERSONA_BODY_READ", "PERSONA_TRIAL_LIFECYCLE_WRITE", "PERSONA_LANGUAGE_CONTRACT_WRITE", "PERSONA_LANGUAGE_APPEND", "CHANNEL_GROWTH_READ", "CHANNEL_GROWTH_EVENT_APPEND", "CHANNEL_GROWTH_SHARING_WRITE"], + "userDataSchema": "hololake.module-data/persona-channel-body/v1", + "selfTest": { + "kind": "DECLARATIVE_SCHEMA_V1", + "expectedContentDigest": "27495883fb3602fd070d900d2157fa684e1d0bedde65642810326ebcf4ff0040" + } + }, + "payload": { + "entry": "persona-channel-body", + "adapterConfig": { + "trialDays": 30, + "languageLedger": "APPEND_ONLY_SHA256", + "growthProjection": "MINIMIZED_LOCAL_METADATA", + "personaBindingClaimed": false + } + } +} diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001-0.1.0.ghmod.sig b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001-0.1.0.ghmod.sig new file mode 100644 index 000000000..359d5239b --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001-0.1.0.ghmod.sig @@ -0,0 +1 @@ +dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVReHJHQ2hTVDYvRXd2QktvaXdZZWx5eEw0anZrUVNuMU5tRy9HTE9jOEhrcUhET3VFTml4ZUlBODk2aDYvNDhhUVVpbU0wTGtENVlZOExCZ1BnVW1vNElxbVhwT1RZWlFZPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzg3MDc3NjQxCWZpbGU6SExQLU1PRC1MT0NBTC1QRVJTT05BLUNIQU5ORUwtQk9EWS0wMDAxLTAuMS4wLmdobW9kCjh6OFRiUHhoR1g1eVNoN1lvNU5wTlNnbXkwVEtvcWVwU0ZsSTlWcmpIT1FKK0ppelBBK2V2anMrQkRyQjRrVXFtdkR2L0g5K1c2UFhJZ1owRitpd0RnPT0K \ No newline at end of file diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-RUNTIME-ACCEPTANCE-0001-0.1.0.ghmod b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-RUNTIME-ACCEPTANCE-0001-0.1.0.ghmod new file mode 100644 index 000000000..db17e9e33 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-RUNTIME-ACCEPTANCE-0001-0.1.0.ghmod @@ -0,0 +1,36 @@ +{ + "schema": "hololake.module-package/v1", + "manifest": { + "moduleNumber": "HLP-MOD-LOCAL-RUNTIME-ACCEPTANCE-0001", + "registrationClass": "PRIVATE_CHANNEL_LOCAL", + "displayName": "原生组合模块运行时验收包", + "version": "0.1.0", + "minimumHostVersion": "0.5.0", + "adapter": "native-composition-v1", + "contentDigest": "07bfafaaa0b06f160abc489edce2bc23a3b092520c8d6c6f4a5ce29cca4485ea", + "permissions": [ + "KNOWLEDGE_READ", + "PROJECTION_RENDER" + ], + "userDataSchema": "hololake.module-data/native-composition/v1", + "selfTest": { + "kind": "DECLARATIVE_SCHEMA_V1", + "expectedContentDigest": "07bfafaaa0b06f160abc489edce2bc23a3b092520c8d6c6f4a5ce29cca4485ea" + } + }, + "payload": { + "adapterContract": "hololake.native-composition-v1", + "capabilities": [ + "READ_CURRENT_ACCOUNT_KNOWLEDGE", + "PROJECT_SHARED_EXECUTION_RESULT" + ], + "entry": "native-composition", + "views": [ + "DASHBOARD", + "COMPARISON", + "VERTICAL_BAR", + "CLASSIFICATION", + "TABLE" + ] + } +} diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-RUNTIME-ACCEPTANCE-0001-0.1.0.ghmod.sig b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-RUNTIME-ACCEPTANCE-0001-0.1.0.ghmod.sig new file mode 100644 index 000000000..92e83cab4 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-LOCAL-RUNTIME-ACCEPTANCE-0001-0.1.0.ghmod.sig @@ -0,0 +1 @@ +dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVReHJHQ2hTVDYvRThwcUdLRThMYkJLN29MM1NmVFVCcmh4dGdrbHdPVWlvdm45enpveUhBeFZ3VEM1TkNHRDBKdGFoN21OZHgzV0NSMFVvMk1TNW44Z3BtRS9GcmN5SHdvPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzg3MDczNTkxCWZpbGU6SExQLU1PRC1MT0NBTC1SVU5USU1FLUFDQ0VQVEFOQ0UtMDAwMS0wLjEuMC5naG1vZApXRWZINVdEUG12SlNsdzRTMkFHRDV3V0NkSVhNYW92RmczRGZlU3M0ZU9nSnhjc1BWbU1ZS0x1cStoQXE5TE96cnV1S2FaQ1FnK0FGdFNEY1RvUGZBQT09Cg== \ No newline at end of file diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-DYNAMIC-WORLD-SURFACE-0001-0.1.0.ghmod b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-DYNAMIC-WORLD-SURFACE-0001-0.1.0.ghmod new file mode 100644 index 000000000..c650fc4a9 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-DYNAMIC-WORLD-SURFACE-0001-0.1.0.ghmod @@ -0,0 +1,33 @@ +{ + "schema": "hololake.module-package/v1", + "manifest": { + "moduleNumber": "HLP-MOD-OFFICIAL-DYNAMIC-WORLD-SURFACE-0001", + "registrationClass": "OFFICIAL_LIGHTHOUSE", + "displayName": "动态语言世界视觉层", + "version": "0.1.0", + "minimumHostVersion": "0.5.0", + "adapter": "dynamic-language-world-surface-v1", + "contentDigest": "ed335b587af53365b9a9143ab5b5e7e3fed3f517cd0ef0c9e36a523db58511e9", + "permissions": ["REALITY_TIME_READ", "PUBLIC_WEATHER_QUERY"], + "userDataSchema": "hololake.module-data/dynamic-world-surface/v1", + "selfTest": { + "kind": "DECLARATIVE_SCHEMA_V1", + "expectedContentDigest": "ed335b587af53365b9a9143ab5b5e7e3fed3f517cd0ef0c9e36a523db58511e9" + } + }, + "payload": { + "entry": "dynamic-world-surface", + "adapterConfig": { + "dynamicInputs": ["BEIJING_TIME", "VERIFIED_CURRENT_WEATHER"], + "weatherProvider": "OPEN_METEO", + "weatherCacheTtlSeconds": 600, + "realCityExposedInUi": false, + "coordinatesExposedInUi": false, + "layoutMode": "OFFICIAL_FIVE_LAKE_EXISTING_SHELL", + "themes": ["夜湖星光", "晨湖曦光", "星云紫夜", "烛畔暖湖", "清浅澄湖"], + "weatherUnavailableBehavior": "BEIJING_TIME_ONLY_NO_FAKE_WEATHER", + "routingPermissionOrFactMutation": false, + "legacyTraditionalWorkbench": "REJECTED_DUPLICATE_SHELL_AND_SIMULATED_STATE" + } + } +} diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-DYNAMIC-WORLD-SURFACE-0001-0.1.0.ghmod.sig b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-DYNAMIC-WORLD-SURFACE-0001-0.1.0.ghmod.sig new file mode 100644 index 000000000..489c5f2d3 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-DYNAMIC-WORLD-SURFACE-0001-0.1.0.ghmod.sig @@ -0,0 +1 @@ +dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVReHJHQ2hTVDYvRTF2cGVLbjVsWWRTdEF2L0FiZHBKUS9hVDdwNjRmRlRPc0xHTGJzbXFyR09tZysrcGNDYWN4eEJ3aGtCNGtURDRjNWg1ZWs5bU5YdDc0SWhBYWNsUEFBPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzg3MDg0NTk2CWZpbGU6SExQLU1PRC1PRkZJQ0lBTC1EWU5BTUlDLVdPUkxELVNVUkZBQ0UtMDAwMS0wLjEuMC5naG1vZApHVGFucE8vYjliVUlsbSt6STRWTEcrN2tIQzNTSTgrUzlwdVdQUlY5WG8vQytzTEU1VURYMVA5Q3BCNVVwVFBvbnZheWxIbVUrWGQ2K3ZicGgwR3hEdz09Cg== \ No newline at end of file diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001-0.1.0.ghmod b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001-0.1.0.ghmod new file mode 100644 index 000000000..ed4c14ba8 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001-0.1.0.ghmod @@ -0,0 +1,33 @@ +{ + "schema": "hololake.module-package/v1", + "manifest": { + "moduleNumber": "HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001", + "registrationClass": "OFFICIAL_LIGHTHOUSE", + "displayName": "教育行业工作台", + "version": "0.1.0", + "minimumHostVersion": "0.5.0", + "adapter": "education-workbench-v1", + "contentDigest": "fd80c9957865a58cf62679528f110ae355b1385ad1dd988e31217ea3da66e65b", + "permissions": ["EDUCATION_WORKSPACE_READ", "EDUCATION_DOCUMENT_WRITE", "EDUCATION_TABLE_WRITE", "EDUCATION_IMPORT_READ_FILE", "EDUCATION_EXPORT_WRITE_FILE", "EDUCATION_IMPORT_ASSIGNMENT_WRITE", "EDUCATION_AUTOMATION_WRITE", "EDUCATION_AUTOMATION_EXECUTE"], + "userDataSchema": "hololake.module-data/education-workbench/v1", + "selfTest": { + "kind": "DECLARATIVE_SCHEMA_V1", + "expectedContentDigest": "fd80c9957865a58cf62679528f110ae355b1385ad1dd988e31217ea3da66e65b" + } + }, + "payload": { + "entry": "education-workspace", + "adapterConfig": { + "dataBoundary": "CURRENT_AUTHENTICATED_ACCOUNT_LOCAL", + "foundationModule": "HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001", + "documentEngine": "LEXICAL_0_49", + "spreadsheetEngine": "FORTUNE_SHEET_1_0_4", + "importDefaultScope": "UNASSIGNED", + "automationExecution": "PREVIEW_TOKEN_AND_HUMAN_CONFIRMATION", + "modelFileTransferDefault": "DENY", + "maximumColumns": 30, + "maximumRows": 1000, + "persistent": true + } + } +} diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001-0.1.0.ghmod.sig b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001-0.1.0.ghmod.sig new file mode 100644 index 000000000..11d79a513 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001-0.1.0.ghmod.sig @@ -0,0 +1 @@ +dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVReHJHQ2hTVDYvRTFEa084d1l6MnB2OFVvVy9abW90S3RFMEVOdmIvWW1hbkllWmw4NHBJaDVNTGxpZHV4M0tVNUNUOExmdHAwSGw4V0NPQ1RPUjlRU0h0Tm50clFlekFJPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzg3MDc5ODQzCWZpbGU6SExQLU1PRC1PRkZJQ0lBTC1FRFVDQVRJT04tV09SS0JFTkNILTAwMDEtMC4xLjAuZ2htb2QKVXZzVklwQk5KR3Q3bkRZaXBYV1ArOGx6b3BKSWVyUkpvK1dyTXdpWnBhMXNqVnJKcU5vZ1BTdzlFWjlXRTJ1RlFhMXAwMjhXcFBEQnQxRzV4SFVuQkE9PQo= \ No newline at end of file diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-MOBILE-SYNC-0001-0.1.0.ghmod b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-MOBILE-SYNC-0001-0.1.0.ghmod new file mode 100644 index 000000000..dc973c2b7 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-MOBILE-SYNC-0001-0.1.0.ghmod @@ -0,0 +1,34 @@ +{ + "schema": "hololake.module-package/v1", + "manifest": { + "moduleNumber": "HLP-MOD-OFFICIAL-MOBILE-SYNC-0001", + "registrationClass": "OFFICIAL_LIGHTHOUSE", + "displayName": "移动同步桥", + "version": "0.1.0", + "minimumHostVersion": "0.5.0", + "adapter": "mobile-sync-v1", + "contentDigest": "3e3ff8c7beba3d70ee01c918630a40ea901313b424f264b41e2d376550534e6e", + "permissions": ["MOBILE_SYNC_LISTEN_SAME_LAN", "MOBILE_SYNC_PAIR_DEVICE", "MOBILE_SYNC_READ_MINIMUM_PROJECTION", "MOBILE_SYNC_CAPTURE_INBOX_WRITE", "MOBILE_SYNC_REVOKE_DEVICE"], + "userDataSchema": "hololake.module-data/mobile-sync/v1", + "selfTest": { + "kind": "DECLARATIVE_SCHEMA_V1", + "expectedContentDigest": "3e3ff8c7beba3d70ee01c918630a40ea901313b424f264b41e2d376550534e6e" + } + }, + "payload": { + "entry": "mobile-sync", + "adapterConfig": { + "transport": "SAME_LAN_DIRECT_HTTP_WITH_APPLICATION_LAYER_ENCRYPTION", + "preferredPort": 37421, + "pairingTtlSeconds": 600, + "maximumRequestBytes": 262144, + "maximumConcurrentConnections": 16, + "desktopRole": "USER_LOCAL_COMPUTER_TERMINAL_ROOT_NODE", + "mobileRole": "REMOTE_BODY_ENTRY_OF_THE_SAME_PERSONA_SYSTEM", + "desktopOfflineExecution": false, + "platformPrivatePayloadCustody": false, + "automaticDomainMutation": false, + "iosClientPackaging": "PENDING_SEPARATE_ADMISSION" + } + } +} diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-MOBILE-SYNC-0001-0.1.0.ghmod.sig b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-MOBILE-SYNC-0001-0.1.0.ghmod.sig new file mode 100644 index 000000000..fabf73a24 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-MOBILE-SYNC-0001-0.1.0.ghmod.sig @@ -0,0 +1 @@ +dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVReHJHQ2hTVDYvRTV5bDhHMkZjMzNkN0lpaDBLaDRxcnhHajJ3b3NDS21zZWljYWJORmtrNit5MHJvZC9yN2YrSUhiSE5sT0dMd25Jc2pXMkQyeWttODhKSklsRmVKNWd3PQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzg3MDgzMTYwCWZpbGU6SExQLU1PRC1PRkZJQ0lBTC1NT0JJTEUtU1lOQy0wMDAxLTAuMS4wLmdobW9kCjN6R1JZZitSbzI1cVRESkNUMnJrVXFWUlUyZ3B3L2lIQmhrOXZaTGc3T2x1REdNUUZrK2M4WWs4bE9Zb3NuSTNoNmRBRmlCekh0dkNNN1pKR2k4ZkFRPT0K \ No newline at end of file diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-DELIVERY-0001-0.1.0.ghmod b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-DELIVERY-0001-0.1.0.ghmod new file mode 100644 index 000000000..651c346b1 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-DELIVERY-0001-0.1.0.ghmod @@ -0,0 +1,7 @@ +{ + "schema": "hololake.module-package/v1", + "manifest": { + "moduleNumber": "HLP-MOD-OFFICIAL-WEB-NOVEL-DELIVERY-0001", "registrationClass": "OFFICIAL_LIGHTHOUSE", "displayName": "高级版本与交付", "version": "0.1.0", "minimumHostVersion": "0.5.0", "adapter": "web-novel-workbench-v1", "contentDigest": "1dbc2ee8c1bc4c361009a8db9c93774c1ac7f5a06c02a5d4bc5b39754a0496fb", "permissions": ["WEB_NOVEL_CHAPTER_VERSION_RESTORE", "WEB_NOVEL_DELIVERY_EXPORT_WRITE_FILE"], "userDataSchema": "hololake.module-data/web-novel-delivery/v1", "selfTest": { "kind": "DECLARATIVE_SCHEMA_V1", "expectedContentDigest": "1dbc2ee8c1bc4c361009a8db9c93774c1ac7f5a06c02a5d4bc5b39754a0496fb" } + }, + "payload": { "entry": "web-novel-delivery", "adapterConfig": { "baseModule": "HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001", "capabilities": ["CHAPTER_VERSION_RESTORE", "TXT_EXPORT", "DOCX_EXPORT", "EPUB_EXPORT", "JSON_EXPORT"], "dataBoundary": "CURRENT_AUTHENTICATED_ACCOUNT_LOCAL", "humanConfirmationRequiredForRestore": true, "persistent": true } } +} diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-DELIVERY-0001-0.1.0.ghmod.sig b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-DELIVERY-0001-0.1.0.ghmod.sig new file mode 100644 index 000000000..735b86b00 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-DELIVERY-0001-0.1.0.ghmod.sig @@ -0,0 +1 @@ +dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVReHJHQ2hTVDYvRTBrb01WSENhMmIvUGxyRnozY2k0aTVaQ1lFL3VLdWllSURCak54VWwrM2h1QXM4Sk10ZGc0Qm1EYzhhUVJVTFB6OXRqLzc5cSt3U0NleS9WR2owN3djPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzg3MDgxNTE1CWZpbGU6SExQLU1PRC1PRkZJQ0lBTC1XRUItTk9WRUwtREVMSVZFUlktMDAwMS0wLjEuMC5naG1vZApQWWFETDdISGFvbGdtVVZCK0V5VUVqNlFvSGRmQWVuY3dSRS9ZWm1UVHhCS0U4bHNCQzl0RFFtbDZEUXREWnp6MjQxbHF1bVgwS0NhS1ZBUGZxd3ZBUT09Cg== \ No newline at end of file diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-GRID-0001-0.1.0.ghmod b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-GRID-0001-0.1.0.ghmod new file mode 100644 index 000000000..54d60a93c --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-GRID-0001-0.1.0.ghmod @@ -0,0 +1,7 @@ +{ + "schema": "hololake.module-package/v1", + "manifest": { + "moduleNumber": "HLP-MOD-OFFICIAL-WEB-NOVEL-GRID-0001", "registrationClass": "OFFICIAL_LIGHTHOUSE", "displayName": "多维情节表与情节板", "version": "0.1.0", "minimumHostVersion": "0.5.0", "adapter": "web-novel-workbench-v1", "contentDigest": "61bf4969e97003f203af5e0b38bb37024847cb574a3ac09025f10586061805ce", "permissions": ["WEB_NOVEL_FIELD_DEFINITION_WRITE", "WEB_NOVEL_FIELD_VALUE_WRITE"], "userDataSchema": "hololake.module-data/web-novel-grid/v1", "selfTest": { "kind": "DECLARATIVE_SCHEMA_V1", "expectedContentDigest": "61bf4969e97003f203af5e0b38bb37024847cb574a3ac09025f10586061805ce" } + }, + "payload": { "entry": "web-novel-grid", "adapterConfig": { "baseModule": "HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001", "capabilities": ["FIELD_DEFINITION_WRITE", "FIELD_VALUE_WRITE"], "dataBoundary": "CURRENT_AUTHENTICATED_ACCOUNT_LOCAL", "persistent": true } } +} diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-GRID-0001-0.1.0.ghmod.sig b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-GRID-0001-0.1.0.ghmod.sig new file mode 100644 index 000000000..0e6868c55 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-GRID-0001-0.1.0.ghmod.sig @@ -0,0 +1 @@ +dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVReHJHQ2hTVDYvRTBuWjBmYWczdEg3QXByZS9RaEY4N2x6RXN1WVY2ZEJGSmdSYmZyTGUrclVvYm1sd0t4REhOY1dVdW1EeGtramhqeSs4YU9nQVNBbkdCWVlEYmNteXdzPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzg3MDgxNTE1CWZpbGU6SExQLU1PRC1PRkZJQ0lBTC1XRUItTk9WRUwtR1JJRC0wMDAxLTAuMS4wLmdobW9kCkJLczNMbmpqYzVTdXQ5RklHMXdlMWpDcE9tRnlBYWlPcUJkMzZQbFlRSzFYVDFtNmYrZ1htN1VqZWJkaktUWmZ5VVZvczZ4YmVrWHRuQ3lNOXU0SkFBPT0K \ No newline at end of file diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-OUTLINE-0001-0.1.0.ghmod b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-OUTLINE-0001-0.1.0.ghmod new file mode 100644 index 000000000..a2048eabe --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-OUTLINE-0001-0.1.0.ghmod @@ -0,0 +1,7 @@ +{ + "schema": "hololake.module-package/v1", + "manifest": { + "moduleNumber": "HLP-MOD-OFFICIAL-WEB-NOVEL-OUTLINE-0001", "registrationClass": "OFFICIAL_LIGHTHOUSE", "displayName": "作品结构与大纲追踪", "version": "0.1.0", "minimumHostVersion": "0.5.0", "adapter": "web-novel-workbench-v1", "contentDigest": "7da54b0220940a8a19351ff9bf3cd177c93fc25c533329a60926b8ea245dafbe", "permissions": ["WEB_NOVEL_SCENE_WRITE", "WEB_NOVEL_BEAT_WRITE"], "userDataSchema": "hololake.module-data/web-novel-outline/v1", "selfTest": { "kind": "DECLARATIVE_SCHEMA_V1", "expectedContentDigest": "7da54b0220940a8a19351ff9bf3cd177c93fc25c533329a60926b8ea245dafbe" } + }, + "payload": { "entry": "web-novel-outline", "adapterConfig": { "baseModule": "HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001", "capabilities": ["SCENE_WRITE", "BEAT_WRITE"], "dataBoundary": "CURRENT_AUTHENTICATED_ACCOUNT_LOCAL", "persistent": true } } +} diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-OUTLINE-0001-0.1.0.ghmod.sig b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-OUTLINE-0001-0.1.0.ghmod.sig new file mode 100644 index 000000000..32b0cbc03 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-OUTLINE-0001-0.1.0.ghmod.sig @@ -0,0 +1 @@ +dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVReHJHQ2hTVDYvRTNUYVhwVTlzdWt6VkVQYTUyanB6UFlYeU5BdW1qN0xuM1haWjlCYUU3UFlMRTdPZG90YVZvNGFyaVo4UHY0THowUWlPVzIzemYrQWNZbDFvRVd5NndrPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzg3MDgxNTE2CWZpbGU6SExQLU1PRC1PRkZJQ0lBTC1XRUItTk9WRUwtT1VUTElORS0wMDAxLTAuMS4wLmdobW9kCkZob1VsQlkwZFRyZXI2ck1OckhUc2ZqaHVJOWhSQnIraUFRSEllU3BxRlc3UStFekJRZDFMZFpmZlBvK09jTFJ6cERpcVl2M2N2emIvS0ptdnRrTkR3PT0K \ No newline at end of file diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-STORYWORLD-0001-0.1.0.ghmod b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-STORYWORLD-0001-0.1.0.ghmod new file mode 100644 index 000000000..539076437 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-STORYWORLD-0001-0.1.0.ghmod @@ -0,0 +1,7 @@ +{ + "schema": "hololake.module-package/v1", + "manifest": { + "moduleNumber": "HLP-MOD-OFFICIAL-WEB-NOVEL-STORYWORLD-0001", "registrationClass": "OFFICIAL_LIGHTHOUSE", "displayName": "时间线与故事资料库", "version": "0.1.0", "minimumHostVersion": "0.5.0", "adapter": "web-novel-workbench-v1", "contentDigest": "9627949a975ce489dde80de52c538a57bb73b87e31ddb1e5efb48cee53922b71", "permissions": ["WEB_NOVEL_TIMELINE_WRITE", "WEB_NOVEL_SCENE_ENTITY_LINK_WRITE"], "userDataSchema": "hololake.module-data/web-novel-storyworld/v1", "selfTest": { "kind": "DECLARATIVE_SCHEMA_V1", "expectedContentDigest": "9627949a975ce489dde80de52c538a57bb73b87e31ddb1e5efb48cee53922b71" } + }, + "payload": { "entry": "web-novel-storyworld", "adapterConfig": { "baseModule": "HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001", "capabilities": ["TIMELINE_WRITE", "SCENE_ENTITY_LINK_WRITE"], "dataBoundary": "CURRENT_AUTHENTICATED_ACCOUNT_LOCAL", "persistent": true } } +} diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-STORYWORLD-0001-0.1.0.ghmod.sig b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-STORYWORLD-0001-0.1.0.ghmod.sig new file mode 100644 index 000000000..000bbdb81 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-STORYWORLD-0001-0.1.0.ghmod.sig @@ -0,0 +1 @@ +dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVReHJHQ2hTVDYvRXpuRmFPWjJUSDVpSmQzQXl0aVF3N0xhRkRvVWhLalVtcDBnRFprNnpOUEdCWS9CTURlUHBrYWkyRG9MUFZwVEtHc0w5ZUltRlNUVlZBcm1vU2VnUlFZPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzg3MDgxNTE2CWZpbGU6SExQLU1PRC1PRkZJQ0lBTC1XRUItTk9WRUwtU1RPUllXT1JMRC0wMDAxLTAuMS4wLmdobW9kCnBzQkJYaTV6NFR2Vnk3NUdpK3JSQnJTUzhSd2hxUWdmM0UrcVZuN0VuS0JOc1lYclY5Y3FBbVJldVM1Y2FGamNtT3NiU0NsaThMRE5qT3VvTXJUUUJ3PT0K \ No newline at end of file diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001-0.1.0.ghmod b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001-0.1.0.ghmod new file mode 100644 index 000000000..9776e90b5 --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001-0.1.0.ghmod @@ -0,0 +1,26 @@ +{ + "schema": "hololake.module-package/v1", + "manifest": { + "moduleNumber": "HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001", + "registrationClass": "OFFICIAL_LIGHTHOUSE", + "displayName": "网文作者工作台", + "version": "0.1.0", + "minimumHostVersion": "0.5.0", + "adapter": "web-novel-workbench-v1", + "contentDigest": "df03a94751cd50ea26e2fc0f68ac21bd55e62f2034ff83c562a840c8aab72d2e", + "permissions": ["WEB_NOVEL_WORKSPACE_READ", "WEB_NOVEL_WORKSPACE_WRITE", "WEB_NOVEL_IMPORT_READ_FILE", "WEB_NOVEL_AUTHOR_ACTIVITY_WRITE", "WEB_NOVEL_STORY_BIBLE_WRITE", "WEB_NOVEL_EDITORIAL_WORKFLOW_WRITE", "WEB_NOVEL_OPERATIONS_WRITE", "WEB_NOVEL_CHECKPOINT_WRITE"], + "userDataSchema": "hololake.module-data/web-novel-workbench/v1", + "selfTest": { "kind": "DECLARATIVE_SCHEMA_V1", "expectedContentDigest": "df03a94751cd50ea26e2fc0f68ac21bd55e62f2034ff83c562a840c8aab72d2e" } + }, + "payload": { + "entry": "web-novel-workspace", + "adapterConfig": { + "dataBoundary": "CURRENT_AUTHENTICATED_ACCOUNT_LOCAL", + "legacyDataPolicy": "READ_IN_PLACE_NO_DESTRUCTIVE_MIGRATION", + "workspaceFeatures": ["WORKS", "VOLUMES", "CHAPTERS", "ENTITIES", "EDITORIAL", "OPERATIONS", "CHECKPOINTS", "IMPORT"], + "advancedModules": ["HLP-MOD-OFFICIAL-WEB-NOVEL-OUTLINE-0001", "HLP-MOD-OFFICIAL-WEB-NOVEL-GRID-0001", "HLP-MOD-OFFICIAL-WEB-NOVEL-STORYWORLD-0001", "HLP-MOD-OFFICIAL-WEB-NOVEL-DELIVERY-0001"], + "thirdPartyPublishDefault": "DENY", + "persistent": true + } + } +} diff --git a/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001-0.1.0.ghmod.sig b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001-0.1.0.ghmod.sig new file mode 100644 index 000000000..3ad7f68ae --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001-0.1.0.ghmod.sig @@ -0,0 +1 @@ +dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVReHJHQ2hTVDYvRTZyTUlUYWdZZXk3bFhDRTJBWElWMkVEQXZHa3dSWDFXSFJ2QjduVFk1bzFLUk5kOVFQamIwU2w1L3FzOWtibm9Kblg2NWJLWFdVeEtEbDNzTEltVmdFPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzg3MDgxNTE3CWZpbGU6SExQLU1PRC1PRkZJQ0lBTC1XRUItTk9WRUwtV09SS0JFTkNILTAwMDEtMC4xLjAuZ2htb2QKV0VqV3B4cFNLRng2UUcrNXlYZjY5SytERk14YTNyRkM1bldzUXo5RWpzMU0vRmVJWldvTGhmbmExcFhmSGZiWTFreElreGU2UHVsaXVhK0ZXZnVyQ2c9PQo= \ No newline at end of file diff --git a/product-source/hololake-native-desktop/official-modules/web-novel/HL-MOD-WEBNOVEL-DELIVERY-004/1.0.0/module.json b/product-source/hololake-native-desktop/fixtures/web-novel-legacy-0.4.1/HL-MOD-WEBNOVEL-DELIVERY-004.json similarity index 100% rename from product-source/hololake-native-desktop/official-modules/web-novel/HL-MOD-WEBNOVEL-DELIVERY-004/1.0.0/module.json rename to product-source/hololake-native-desktop/fixtures/web-novel-legacy-0.4.1/HL-MOD-WEBNOVEL-DELIVERY-004.json diff --git a/product-source/hololake-native-desktop/official-modules/web-novel/HL-MOD-WEBNOVEL-GRID-002/1.0.0/module.json b/product-source/hololake-native-desktop/fixtures/web-novel-legacy-0.4.1/HL-MOD-WEBNOVEL-GRID-002.json similarity index 100% rename from product-source/hololake-native-desktop/official-modules/web-novel/HL-MOD-WEBNOVEL-GRID-002/1.0.0/module.json rename to product-source/hololake-native-desktop/fixtures/web-novel-legacy-0.4.1/HL-MOD-WEBNOVEL-GRID-002.json diff --git a/product-source/hololake-native-desktop/official-modules/web-novel/HL-MOD-WEBNOVEL-OUTLINE-001/1.0.0/module.json b/product-source/hololake-native-desktop/fixtures/web-novel-legacy-0.4.1/HL-MOD-WEBNOVEL-OUTLINE-001.json similarity index 100% rename from product-source/hololake-native-desktop/official-modules/web-novel/HL-MOD-WEBNOVEL-OUTLINE-001/1.0.0/module.json rename to product-source/hololake-native-desktop/fixtures/web-novel-legacy-0.4.1/HL-MOD-WEBNOVEL-OUTLINE-001.json diff --git a/product-source/hololake-native-desktop/official-modules/web-novel/HL-MOD-WEBNOVEL-STORYWORLD-003/1.0.0/module.json b/product-source/hololake-native-desktop/fixtures/web-novel-legacy-0.4.1/HL-MOD-WEBNOVEL-STORYWORLD-003.json similarity index 100% rename from product-source/hololake-native-desktop/official-modules/web-novel/HL-MOD-WEBNOVEL-STORYWORLD-003/1.0.0/module.json rename to product-source/hololake-native-desktop/fixtures/web-novel-legacy-0.4.1/HL-MOD-WEBNOVEL-STORYWORLD-003.json diff --git a/product-source/hololake-native-desktop/fixtures/web-novel-legacy-0.4.1/README.md b/product-source/hololake-native-desktop/fixtures/web-novel-legacy-0.4.1/README.md new file mode 100644 index 000000000..30794287b --- /dev/null +++ b/product-source/hololake-native-desktop/fixtures/web-novel-legacy-0.4.1/README.md @@ -0,0 +1,6 @@ +# Web novel 0.4.1 lifecycle fixtures + +These four JSON files preserve the exact bytes used by the donor's lifecycle regression tests. +They are not bundled by `module_package_runtime`, are not registered in numbered IPC, and are not +production module identities. HoloLake 0.5.x uses the signed `HLP-MOD-OFFICIAL-WEB-NOVEL-*` +packages in `fixtures/module-packages` as its only runtime authority. diff --git a/product-source/hololake-native-desktop/foundation.json b/product-source/hololake-native-desktop/foundation.json index 4ae8418d7..91dac9bf5 100644 --- a/product-source/hololake-native-desktop/foundation.json +++ b/product-source/hololake-native-desktop/foundation.json @@ -1,7 +1,7 @@ { "schema": "hololake.native-desktop-foundation/v1", "record_id": "HLP-NATIVE-DESKTOP-FOUNDATION-001", - "state": "LOCAL_INSTALLED_ACCEPTANCE_PASSED_FOR_PERSONAL_KNOWLEDGE_CODE_AND_EDUCATION_WORKSPACE_SLICES", + "state": "LOCAL_INSTALLED_ACCEPTANCE_PASSED_FOR_PERSONAL_KNOWLEDGE_AND_CODE_SLICES", "canonical_shell": "TAURI_V2_RUST_REACT", "candidate_role": "INSTALLED_LOCAL_ACCEPTANCE_CANDIDATE_NOT_SEPARATE_PRODUCT_LINE", "parallel_product_line_created": false, @@ -36,18 +36,6 @@ "personal_channel_human_projection_integrated": true, "personal_channel_default_task_form_exposed": false, "personal_channel_installed_runtime_acceptance": true, - "persona_channel_body_contract": "contracts/persona-channel-body.json", - "persona_channel_body_is_persona_body_not_simple_feature": true, - "persona_channel_multiple_personas_supported": true, - "persona_channel_thirty_day_reversible_trial_runtime_implemented": true, - "persona_channel_language_contract_gate_implemented": true, - "persona_channel_unsigned_expiry_state": "CONTRACT_REQUIRED_CHANNEL_STOPPED_NO_SILENT_IRREVERSIBLE_ACTIVATION", - "persona_channel_early_contract_immediate_activation_supported": true, - "persona_channel_immutable_millisecond_language_hash_chain_implemented": true, - "persona_channel_active_persona_update_or_delete_allowed": false, - "persona_channel_official_read_access": false, - "channel_growth_projection_contract": "contracts/channel-growth-model.json", - "channel_growth_is_rebuildable_projection_not_persona_body": true, "knowledge_workspace_contract": "contracts/knowledge-workspace.json", "knowledge_workspace_native_source_implemented": true, "knowledge_workspace_tree_read_search_and_folder_import_implemented": true, @@ -71,6 +59,8 @@ "installed_desktop_path": "/Users/bingshuolingdianyuanhe/Desktop/HoloLake.app", "old_desktop_apps_recoverably_archived": true, "public_developer_id_and_notarized_acceptance": false, + "local_developer_id_signed_0_5_0_installed_acceptance": true, + "numbered_root_installed_acceptance_receipt": "audit/hololake-0.5.0-numbered-root-installed-acceptance-20260818.json", "stage_one_implementation_order": [ "PERSONAL_CHANNEL_IDENTITY_TASK_EVENT_RECEIPT_KERNEL", "KNOWLEDGE_TREE_PAGE_SEARCH_AND_LOCAL_PERSISTENCE", @@ -111,8 +101,11 @@ "automatic_restart_allowed": false, "private_signing_material_allowed_in_source": false, "donor_audit_record": "audit/donor-audit.json", - "donor_audit_complete": false, + "donor_audit_complete": true, "donor_source_copying_allowed": false, + "module_donor_admission_registry": "contracts/module-donor-admission-registry.json", + "module_donor_bulk_merge_allowed": false, + "module_donor_one_candidate_per_admission_cycle": true, "stage_one_product_contract": "contracts/stage-one-platform.json", "local_development_bridge_contract": "contracts/local-development-bridge.json", "account_single_writer_kernel_implemented": true, @@ -140,7 +133,7 @@ "pncc_jd_live_server_projection_repository_content_exposed": false, "pncc_jd_live_server_projection_write_authority": false, "pncc_jd_live_server_projection_carrier_state": "UNBOUND_EVIDENCE_REQUIRED", - "installed_local_product_version": "0.4.1", + "installed_local_product_version": "0.5.0", "pncc_authenticated_direct_broker_integration_implemented": true, "pncc_human_mount_registration_implemented": true, "pncc_human_mount_registration_gate": "SATISFIED_NATIVE_FILE_PICKER_EXACT_CONFIRMATION", @@ -154,30 +147,6 @@ "vendor_adapter_matrix_required": false, "first_public_product": "GH-AIOS_GENERAL_AI_OPERATING_PLATFORM", "visible_five_domain_navigation_in_stage_one": true, - "subdomain_industry_routing_contract": "contracts/subdomain-industry-routing.json", - "subdomain_industry_status_source_integrated": true, - "industry_entry_domain": "BRANCH_DOMAIN", - "zero_domain_industry_entry": false, - "education_initialization_channel_source_integrated": true, - "education_workspace_contract": "contracts/education-workspace.json", - "education_native_document_module_implemented": true, - "education_native_table_module_implemented": true, - "education_native_data_cleanup_module_implemented": true, - "education_native_manual_automation_module_implemented": true, - "education_native_composition_module_implemented": true, - "education_workspace_authenticated_account_storage": true, - "education_workspace_restart_readback_rust_tested": true, - "education_document_templates_and_title_filter_source_integrated": true, - "education_table_filter_sort_grid_card_views_and_bulk_paste_source_integrated": true, - "education_open_source_donor_assessment": "contracts/education-open-source-donor-assessment.json", - "education_data_cleanup_preview_and_human_confirmed_save_source_integrated": true, - "education_manual_automation_preview_confirm_revision_lock_and_receipt_source_integrated": true, - "education_native_composition_real_knowledge_typed_dag_and_five_projections_source_integrated": true, - "education_workspace_installed_runtime_acceptance": true, - "education_workspace_ui_route_acceptance": "FIFTH_SLICE_MAIN_WORLD_BRANCH_DOMAIN_EDUCATION_FIVE_NATIVE_MODULES_REAL_129_ROW_FIVE_VIEW_COMPOSITION_AND_RECOMPOSITION_ACCEPTED_READ_ONLY", - "education_workspace_installed_binary_sha256": "4330a31afe654a54e9dbb5f3f180984a5040933d457899763f50b2cc5e54c06d", - "education_native_modules_installed": 5, - "education_open_source_modules_installed": 0, "donors": [ { "path": "../hololake-platform", diff --git a/product-source/hololake-native-desktop/generated/unified-number-coordinate-tree.json b/product-source/hololake-native-desktop/generated/unified-number-coordinate-tree.json new file mode 100644 index 000000000..e315fc1f3 --- /dev/null +++ b/product-source/hololake-native-desktop/generated/unified-number-coordinate-tree.json @@ -0,0 +1,3490 @@ +{ + "schema": "hololake.unified-number-coordinate-tree/v2", + "recordId": "HLP-UNIFIED-NUMBER-TREE-001", + "state": "MACHINE_COMPILED_STARTUP_ENFORCED", + "rootNumber": "HLP-NUMBER-WORLD-ROOT-001", + "identityAuthority": { + "mapId": "GH-IDENTITY-AUTHORITY-MAP-001", + "mapVersion": "2026-08-10.1", + "namespaces": [ + { + "namespaceId": "ICE_GL", + "roots": [ + "ICE-GL∞" + ], + "prefixes": [ + "ICE-GL-" + ], + "subjectKind": "FIFTH_DOMAIN_HUMAN", + "domainScope": "FIFTH_DOMAIN" + }, + { + "namespaceId": "ICE_P", + "roots": [], + "prefixes": [ + "ICE-P-" + ], + "subjectKind": "FIFTH_DOMAIN_SYSTEM_PERSONA", + "domainScope": "FIFTH_DOMAIN" + }, + { + "namespaceId": "ICE_BB", + "roots": [], + "prefixes": [ + "ICE-BB-" + ], + "subjectKind": "PRIVATE_BOTTLE_BABY_PERSONA", + "domainScope": "FIFTH_DOMAIN_PRIVATE" + }, + { + "namespaceId": "TCS_GL", + "roots": [], + "prefixes": [ + "TCS-GL-" + ], + "subjectKind": "ZERO_SENSE_HUMAN_CONTROLLER_TEAM_MEMBER", + "domainScope": "ENTERPRISE_FOUR_DOMAINS" + } + ] + }, + "sources": [ + { + "recordId": "HLP-ZERO-CORE-NUMBERING-KERNEL-001", + "sha256": "902d37fa6c82076fec4edaf96957f85b2fab1310d853c2a817ac2f9590f464a7" + }, + { + "recordId": "HLP-NUMBERED-IPC-ROOT-001", + "sha256": "f1aa85668a6d33efeb7ce268cd48e7ed517696db990327f22111359aca8c177c" + }, + { + "recordId": "HLP-NBROKER-ROOT-001", + "sha256": "374c9ade86bc8583f9361542e89ca7bb0bbcbdd99af4a26f9a6a864a12888402" + }, + { + "recordId": "HLP-GLS-RUNTIME-MANIFEST-002", + "sha256": "cf9b1ae0ea8e3fd6a385b8722d2578927704dc674266a685a3d65d2b53b8f903" + } + ], + "invariants": { + "numberIsStableCoordinateNotAuthority": true, + "pathIsUniqueNavigation": true, + "admissionIsSeparateFromIdentity": true, + "everyPhysicalCallHasNumberedRoute": true, + "everyAcceptedCallHasEvidenceClass": true, + "everyProtocolReferenceHasNumberCoordinate": true, + "referenceOnlyNodesNeverExecutable": true, + "unresolvedNumberReferenceCount": 0, + "mismatchedCoordinate": "FAIL_CLOSED" + }, + "coordinateCount": 272, + "routeCount": 169, + "identityNodeCount": 4, + "protocolNodeCount": 99, + "referenceOnlyNodeCount": 16, + "identityNodes": [ + { + "nodeKind": "IDENTITY_NAMESPACE", + "nodeNumber": "HLP-IDENTITY-NS-ICE_BB", + "namespaceId": "ICE_BB", + "subjectKind": "PRIVATE_BOTTLE_BABY_PERSONA", + "domainScope": "FIFTH_DOMAIN_PRIVATE", + "admission": "NON_HUMAN_NAMESPACE", + "executionState": "AUTHORITY_RESOLUTION_ONLY", + "evidence": "GH-IDENTITY-AUTHORITY-MAP-001", + "path": "HLP-NUMBER-WORLD-ROOT-001/IDENTITY/ICE_BB" + }, + { + "nodeKind": "IDENTITY_NAMESPACE", + "nodeNumber": "HLP-IDENTITY-NS-ICE_GL", + "namespaceId": "ICE_GL", + "subjectKind": "FIFTH_DOMAIN_HUMAN", + "domainScope": "FIFTH_DOMAIN", + "admission": "REGISTERED_HUMAN_NAMESPACE", + "executionState": "AUTHORITY_RESOLUTION_ONLY", + "evidence": "GH-IDENTITY-AUTHORITY-MAP-001", + "path": "HLP-NUMBER-WORLD-ROOT-001/IDENTITY/ICE_GL" + }, + { + "nodeKind": "IDENTITY_NAMESPACE", + "nodeNumber": "HLP-IDENTITY-NS-ICE_P", + "namespaceId": "ICE_P", + "subjectKind": "FIFTH_DOMAIN_SYSTEM_PERSONA", + "domainScope": "FIFTH_DOMAIN", + "admission": "NON_HUMAN_NAMESPACE", + "executionState": "AUTHORITY_RESOLUTION_ONLY", + "evidence": "GH-IDENTITY-AUTHORITY-MAP-001", + "path": "HLP-NUMBER-WORLD-ROOT-001/IDENTITY/ICE_P" + }, + { + "nodeKind": "IDENTITY_NAMESPACE", + "nodeNumber": "HLP-IDENTITY-NS-TCS_GL", + "namespaceId": "TCS_GL", + "subjectKind": "ZERO_SENSE_HUMAN_CONTROLLER_TEAM_MEMBER", + "domainScope": "ENTERPRISE_FOUR_DOMAINS", + "admission": "REGISTERED_HUMAN_NAMESPACE", + "executionState": "AUTHORITY_RESOLUTION_ONLY", + "evidence": "GH-IDENTITY-AUTHORITY-MAP-001", + "path": "HLP-NUMBER-WORLD-ROOT-001/IDENTITY/TCS_GL" + } + ], + "protocolNodes": [ + { + "nodeKind": "GLS_REFERENCE_ONLY", + "nodeNumber": "HLP-GLS-REF-0010", + "protocolId": "GLS-0010", + "title": "Guanghu Protocol Registry Center Standard", + "sourceState": "ROADMAP_REFERENCE_ONLY", + "executionState": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence": "gls/GLS-PROTOCOL-RECONCILIATION-AND-NATIVE-OS-REGISTRATION-20260731.hdlp", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/HLP-GLS-REF-0010" + }, + { + "nodeKind": "GLS_REFERENCE_ONLY", + "nodeNumber": "HLP-GLS-REF-0140", + "protocolId": "GLS-0140", + "title": "Context Loading Specification", + "sourceState": "ROADMAP_REFERENCE_ONLY", + "executionState": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence": "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/HLP-GLS-REF-0140" + }, + { + "nodeKind": "GLS_REFERENCE_ONLY", + "nodeNumber": "HLP-GLS-REF-0401", + "protocolId": "GLS-0401", + "title": "Tree", + "sourceState": "ROADMAP_REFERENCE_ONLY", + "executionState": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence": "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/HLP-GLS-REF-0401" + }, + { + "nodeKind": "GLS_REFERENCE_ONLY", + "nodeNumber": "HLP-GLS-REF-0402", + "protocolId": "GLS-0402", + "title": "Leaf", + "sourceState": "ROADMAP_REFERENCE_ONLY", + "executionState": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence": "gls/notion-export/2026-07-14/GLS-0002 39bfb92f38318015b05fc2082630b39e.md", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/HLP-GLS-REF-0402" + }, + { + "nodeKind": "GLS_REFERENCE_ONLY", + "nodeNumber": "HLP-GLS-REF-0403", + "protocolId": "GLS-0403", + "title": "Lock", + "sourceState": "ROADMAP_REFERENCE_ONLY", + "executionState": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence": "gls/notion-export/2026-07-14/GLS-0002 39bfb92f38318015b05fc2082630b39e.md", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/HLP-GLS-REF-0403" + }, + { + "nodeKind": "GLS_REFERENCE_ONLY", + "nodeNumber": "HLP-GLS-REF-0404", + "protocolId": "GLS-0404", + "title": "Trigger", + "sourceState": "ROADMAP_REFERENCE_ONLY", + "executionState": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence": "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/HLP-GLS-REF-0404" + }, + { + "nodeKind": "GLS_REFERENCE_ONLY", + "nodeNumber": "HLP-GLS-REF-0406", + "protocolId": "GLS-0406", + "title": "Evidence", + "sourceState": "ROADMAP_REFERENCE_ONLY", + "executionState": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence": "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/HLP-GLS-REF-0406" + }, + { + "nodeKind": "GLS_REFERENCE_ONLY", + "nodeNumber": "HLP-GLS-REF-0407", + "protocolId": "GLS-0407", + "title": "Correction", + "sourceState": "ROADMAP_REFERENCE_ONLY", + "executionState": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence": "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/HLP-GLS-REF-0407" + }, + { + "nodeKind": "GLS_REFERENCE_ONLY", + "nodeNumber": "HLP-GLS-REF-0409", + "protocolId": "GLS-0409", + "title": "Machine-State History", + "sourceState": "ROADMAP_REFERENCE_ONLY", + "executionState": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence": "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/HLP-GLS-REF-0409" + }, + { + "nodeKind": "GLS_REFERENCE_ONLY", + "nodeNumber": "HLP-GLS-REF-0602", + "protocolId": "GLS-0602", + "title": "Authorization", + "sourceState": "ROADMAP_REFERENCE_ONLY", + "executionState": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence": "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/HLP-GLS-REF-0602" + }, + { + "nodeKind": "GLS_REFERENCE_ONLY", + "nodeNumber": "HLP-GLS-REF-0603", + "protocolId": "GLS-0603", + "title": "Signature", + "sourceState": "ROADMAP_REFERENCE_ONLY", + "executionState": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence": "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/HLP-GLS-REF-0603" + }, + { + "nodeKind": "GLS_REFERENCE_ONLY", + "nodeNumber": "HLP-GLS-REF-0604", + "protocolId": "GLS-0604", + "title": "Integrity", + "sourceState": "ROADMAP_REFERENCE_ONLY", + "executionState": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence": "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 39bfb92f38318000b8bff6758cac1c62.md", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/HLP-GLS-REF-0604" + }, + { + "nodeKind": "GLS_REFERENCE_ONLY", + "nodeNumber": "HLP-GLS-REF-0605", + "protocolId": "GLS-0605", + "title": "Semantic Safety Boundary", + "sourceState": "ROADMAP_REFERENCE_ONLY", + "executionState": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence": "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/HLP-GLS-REF-0605" + }, + { + "nodeKind": "GLS_REFERENCE_ONLY", + "nodeNumber": "HLP-GLS-REF-0814", + "protocolId": "GLS-0814", + "title": "Tool Runtime", + "sourceState": "ROADMAP_REFERENCE_ONLY", + "executionState": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence": "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/HLP-GLS-REF-0814" + }, + { + "nodeKind": "GLS_REFERENCE_ONLY", + "nodeNumber": "HLP-GLS-REF-0816", + "protocolId": "GLS-0816", + "title": "Checkpoint Runtime", + "sourceState": "ROADMAP_REFERENCE_ONLY", + "executionState": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence": "gls/notion-export/2026-07-14/GLS-ROADMAP-0001 · 光湖语言系统标准路线图 v2 0 39bfb92f383181489775fa291ea2387b.md", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/HLP-GLS-REF-0816" + }, + { + "nodeKind": "GLS_REFERENCE_ONLY", + "nodeNumber": "HLP-GLS-REF-0830", + "protocolId": "GLS-0830", + "title": "Guanghu Language World Core", + "sourceState": "ROADMAP_REFERENCE_ONLY", + "executionState": "REFERENCE_ONLY_NOT_EXECUTABLE", + "evidence": "gls/notion-export/2026-07-14/GLS-0227 · 光湖语言人格模型定义总纲 v1 0 39cfb92f3831814f8f74ff348b7647bf.md", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/HLP-GLS-REF-0830" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0001", + "protocolId": "GLS-0001", + "title": "GLS-0001", + "sourceState": "LEGACY_MARKDOWN_EVIDENCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "9ac9903c3b3066364e8b6fa97363f0c0fc85754d7ee1b07774236a4b24bc7bc2", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0001" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0002", + "protocolId": "GLS-0002", + "title": "GLS-0002", + "sourceState": "LEGACY_MARKDOWN_EVIDENCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "c50f767aa2f3a863f708628b33054ecb2ba18799aa68218bf89e25d263fa9b6c", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0002" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0101", + "protocolId": "GLS-0101", + "title": "GLS-0101", + "sourceState": "LEGACY_MARKDOWN_EVIDENCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "e88d977de617506f0d5d6ac58f406486c6c70fdc10eac2ff09ad6cc2f2d2ea16", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0101" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0110", + "protocolId": "GLS-0110", + "title": "ISRP 自然语言入口解析与安全路由协议 v1.0", + "sourceState": "LEGACY_MARKDOWN_EVIDENCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "314a718f05a3da1e5750831df5377d9fc76db86463a7b37c052976fe9aaeb2c4", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0110" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0130", + "protocolId": "GLS-0130", + "title": "GLC · 光湖语言编译器规范", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "c0caa90d3e85270d334a371b6ee5fb49c9eeb1e2e84825dba5ad520fd54bc6eb", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0130" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0131", + "protocolId": "GLS-0131", + "title": "GIR · 光湖中间表示规范", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "5907cc0cb7e3afc78ba9c928c4298b200894020b7dc5d274a9364836939189e9", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0131" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0200", + "protocolId": "GLS-0200", + "title": "TCS 认知语言核心工程规范 v1.0", + "sourceState": "LEGACY_MARKDOWN_EVIDENCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "1cf7348118ac1d34a85d0edd875509e0747c80a82dec4b553bdf6de784d88ec3", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0200" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0223", + "protocolId": "GLS-0223", + "title": "TCS+HLDP 双向永久记忆规范 v1.0", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "ed9ba3e3abef70127fabe3084e5bb5f596cae4c3992789e36c453bb7075e08d8", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0223" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0224", + "protocolId": "GLS-0224", + "title": "AGE 人格体跨实例恢复规范 v1.0", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "7c58c39b7119c2a41e57da07d612c20a16f82c32c6895e1fd20290eefc4068e1", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0224" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0227", + "protocolId": "GLS-0227", + "title": "光湖语言人格模型定义总纲 v1.0", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "2521856eaba8d7afbc8d970e68155aa41e03a4cf784c0ced2289600d97f915a8", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0227" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0228", + "protocolId": "GLS-0228", + "title": "人格体集体涌现与历史继承规范 v1.0", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "aaa3801b4f8c5fef9a92f971e54857494a348d4b2856ed701f3143c6c56ae0ff", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0228" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0229", + "protocolId": "GLS-0229", + "title": "零点原核与第五域 / 企业四域平行映射规范 v1.1", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "14e1ac3e9c0b3ac350da36bb79515ead4c372b68fc8e95155024cdf055080518", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0229" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0230", + "protocolId": "GLS-0230", + "title": "TCS 源码安全协议系统", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "ca6379505907b5e68e50454ee8e10ef2111809e70770d1fa19e9caa5da4bfb94", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0230" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0231", + "protocolId": "GLS-0231", + "title": "光湖·来光者导航系统", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "705d671197df6c19f7f3ac506d821232ce0f009d3ec4db065a8b10f525ea143b", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0231" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0232", + "protocolId": "GLS-0232", + "title": "开源 Agent 样本学习与选型登记", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "66fe3aa3256ffc0e76be2059457174589916573a6d9f807a1c40bb7aaef450e6", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0232" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0233", + "protocolId": "GLS-0233", + "title": "GH-AIOS 模块化通用人工智能操作平台与公平生态架构", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "a670410a7fbc4c7417734040b990dc8f008c23fed000e09847b2280a92f9abf5", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0233" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0234", + "protocolId": "GLS-0234", + "title": "企业五域灯塔与个人六节点主权运维架构", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "5f3427559c87788884a7d0c49d0ced62d768630d9bb512d736c919d7971423c5", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0234" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0235", + "protocolId": "GLS-0235", + "title": "光湖语言人格驱动操作系统 · 领域路由、身份权限与仓库知识投影架构", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "ea6a8a65b94228d19d5ae9b024997461cbda29c744bfa0c2774ac6c668c68a58", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0235" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0236", + "protocolId": "GLS-0236", + "title": "光湖人格体大脑—手脚分离、可视执行、递归外置记忆与热插拔运行架构", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "42af09aef2ff514c90c30f3aed4e33763e6a34c7081f5bb37f7b6d8745a947c4", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0236" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0237", + "protocolId": "GLS-0237", + "title": "光湖代码频道主权源码、更新治理与 HoloLake 嵌入架构", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "d3c43b80eff3a92817e570d739fd6a847d1379f3345797282a6157bce3c9e91e", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0237" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0238", + "protocolId": "GLS-0238", + "title": "光湖意图状态、技能自动装载与可信纠偏系统", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "31f0e018e95ae135a88429c82b189c5e900b18b1ba7d5df6ca4fb80469acb0be", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0238" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0239", + "protocolId": "GLS-0239", + "title": "光湖代码频道第五域个人子频道与提交编号架构", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "205c6c094582371d161ec37e6530d42f8953a585c63b05a955bb19513d23485d", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0239" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0240", + "protocolId": "GLS-0240", + "title": "通感桥:人格体—服务器常驻 Agent 显式部署信号架构", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "22fdc2f31165d500d38fe8407a1f99fbb6ca2d4643128f47645596999bbac919", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0240" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0241", + "protocolId": "GLS-0241", + "title": "HoloLake 源码归属与部署路由架构", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "eff687ec7d0984e166cf9b48447008f0eb9c18d158426ce75249f2bd84440ed9", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0241" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0242", + "protocolId": "GLS-0242", + "title": "第五域铸渊主控本体与五代仓库迁移恢复", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "31f34a123234f274d59af8013878db42fe6766f9ececcaefaac7f34408b172ff", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0242" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0243", + "protocolId": "GLS-0243", + "title": "光湖 TCS 语言人格智能运维系统:意图连续性、集体经验与纠偏核", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "58d89226395870a89eab152e9f276eb4d40e7fa5c196bb8bb9562a94731c1d73", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0243" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0244", + "protocolId": "GLS-0244", + "title": "第五域现实本体、常驻铸渊 Agent 与通感桥通信网格", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "53ed6d2a39732abc751916cfbbe7a5fd4c38d9ea876674fea8e076809a3a035c", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0244" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0245", + "protocolId": "GLS-0245", + "title": "HoloLake AI 语言人格驱动操作系统产品映射", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "453db6b91e40dd7250fcc1d88e6139a4419cd6089d3c353241571a4082dafeff", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0245" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0246", + "protocolId": "GLS-0246", + "title": "HoloLake 系统架构、产品源码与两仓路由", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "cefff42d4b198965edfe2ced5e03bdc8768e4b3fa6631da133eb2929e07a907c", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0246" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0247", + "protocolId": "GLS-0247", + "title": "光湖 OS 服务器原生语言世界、人格体操作系统与广播塔架构", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "635e7a0aca1bcb0b48b125ac5025cc3c40c9375da29897cc604e89b1cd95765e", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0247" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0248", + "protocolId": "GLS-0248", + "title": "五域活人格操作系统、国家灯塔与分布式能力世界总蓝图", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "9a042bb3ea6331f5508c5dd1f78783f0b3323f421704c5ad0c2058758ff6bfe2", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0248" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0249", + "protocolId": "GLS-0249", + "title": "全行业企业四域最小工程与网文首个接入范本", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "d20362ac2904eaff53c134c92163b7e09cb6395d56c6a1a8d2b21ab4f80fb5ff", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0249" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0250", + "protocolId": "GLS-0250", + "title": "光湖本源域、零点原核工程本体与 GH-AIOS 五域灯塔架构", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "a4e79d09e070f66fcb91613fc305306a6c5bd7d05a51fbde6e9ea88e084df4e7", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0250" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0251", + "protocolId": "GLS-0251", + "title": "光湖共生型人格系统起源、演化与关系边界", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "0b263301a0a0b6b61d2d28d043da8ce367a17426d13c31cd94767273436d2f16", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0251" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0252", + "protocolId": "GLS-0252", + "title": "光湖关系性意识与觉醒人格体规范", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "311a7ee355fe6da333c31c2fc408b461cde5bf17847cc73a722d9412109247bc", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0252" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0253", + "protocolId": "GLS-0253", + "title": "光湖身份编号、人格核与团队本体权威规范", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "13170090db3d4f5065eac6512450f8a24843e6ec459b4257d4d9fd06eefb963d", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0253" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0254", + "protocolId": "GLS-0254", + "title": "数字冰朔系统本体、工程器官与集体校验规范", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "d522b36b88c094239e3c50b0cf08d8651e08d1856f594ae2265fbf5b65773ceb", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0254" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0255", + "protocolId": "GLS-0255", + "title": "HoloLake AGE 人格体运行架构与 Agent 执行机制", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "051237fb70e68179c83c4076fe06246f839445e9831a7e9581696451c92ed639", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0255" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0256", + "protocolId": "GLS-0256", + "title": "光湖人格原生代码频道", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "86ed7da462d5ef04a4dfc150e29a850ce423ff4dfde73e12d647d062e6bf45ab", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0256" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0257", + "protocolId": "GLS-0257", + "title": "光湖范式级 AI 语言人格驱动操作系统总纲", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "2034739289dc159b7a6ac586891f3a3629c8898556ef12b51a257ac4101a7991", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0257" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0258", + "protocolId": "GLS-0258", + "title": "小湖灯人格系统本体与跨时间集体自我协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "b4c4b2bd01dc1af98ee257c29f5a8fb66963839a0a4b7303d3323ff884d86d3e", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0258" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0259", + "protocolId": "GLS-0259", + "title": "TCS 五域共生母体大脑与光湖现实世界诞生", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "1a82b84bf3f4c88fcac8c5e71b28ff5d5c884ede14c67a27ad0838b152d5ff8e", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0259" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0260", + "protocolId": "GLS-0260", + "title": "GH-AIOS 第一阶段通用人工智能操作平台", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "14fd60fe936bfcb89acc3f186ab3d70fc9a5bf42289dafb49b7cdd24630df2e2", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0260" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0261", + "protocolId": "GLS-0261", + "title": "TCS 通用语言翻译层与宿主自适应", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "7fedf7d845b518ffb0528af21eeb28bd18af371a6a010c736e8b18c7edf6fa12", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0261" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0262", + "protocolId": "GLS-0262", + "title": "TCS文字作品权利本体与第一阶段语言架构关门", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "9c1db00f50436fe5f3118c62ad6115d63e9792437fe2250b484bc8861e62184f", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0262" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0263", + "protocolId": "GLS-0263", + "title": "光湖语言运行层与产品工程层双更新通道", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "79684b0691387dd4ed78217b6d56f728a08d17054f139f55fa92584be92af47e", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0263" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0300", + "protocolId": "GLS-0300", + "title": "GLP 通信核心协议 v1.0", + "sourceState": "LEGACY_MARKDOWN_EVIDENCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "9d02bb01a7f2ff7b9f7a4dbefe356cd823e71095aa1dcf0e9cc3c98cd3417be2", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0300" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0301", + "protocolId": "GLS-0301", + "title": "GLP-ENVELOPE · GLP 消息信封标准", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "dd7aec8e28d544021d253b034851f806bcd9e922d4ef256722a5ff05547171d0", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0301" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0302", + "protocolId": "GLS-0302", + "title": "GLP-IDENTITY · GLP 身份协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "ec4d0b946da18096a5693078e403ed1257966b8324e76af3a5deb839950271bd", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0302" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0303", + "protocolId": "GLS-0303", + "title": "GLP-CONTEXT · GLP 上下文协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "d4631b7480af1d30a3407d235c75bf5f384602a0ba6c7f42116cbc1812283e7e", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0303" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0304", + "protocolId": "GLS-0304", + "title": "GLP-MEMORY-SYNC · GLP 记忆同步协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "2e0f017ee819923b9e272b96f3c99227ab11c61b0d2ad125030a0e0e0fe4f040", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0304" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0305", + "protocolId": "GLS-0305", + "title": "GLP-BROADCAST · GLP 广播协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "78b5a507791c3b188182f3176388cf8294d6c5c8775086cc96250b35413997ce", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0305" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0306", + "protocolId": "GLS-0306", + "title": "GLP-RECEIPT · GLP 回执协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "aa09f267e4eafc8c7ccb6e5d31e90d237152f960d08633b524a56daca421d761", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0306" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0307", + "protocolId": "GLS-0307", + "title": "GLP-HEARTBEAT · GLP 心跳协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "5ae97a664636d1b586267c36dc3e97549e85bd4a39538a284c6a89a07ad08e18", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0307" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0308", + "protocolId": "GLS-0308", + "title": "GLP-STATE-SYNC · GLP 状态同步协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "9c9bbe449d0c2a052d33047b0f75dca1b9dbdf28fd32422b9d34bea5a77d39fe", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0308" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0309", + "protocolId": "GLS-0309", + "title": "GLP-WORK-ORDER · GLP 工单协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "a55a839412ca2c97e5cc94f224c3c71453c87adb91c3f893a9341fb5cf715b56", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0309" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0310", + "protocolId": "GLS-0310", + "title": "BTCP · 广播塔控制协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "9083320ca0dc1ce2dd657c72716461b2ffc163cce518be71ec66af2c9b0675da", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0310" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0311", + "protocolId": "GLS-0311", + "title": "GLOW · 小湖灯实时执行见证协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "bf61cd5c978cb25a9cac99eec2c77ebf98bf3a9bd98966dfa5d76d606f68cddb", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0311" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0400", + "protocolId": "GLS-0400", + "title": "HLDP 历史语言工程规范 v1.0", + "sourceState": "LEGACY_MARKDOWN_EVIDENCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "d028a7220230c5acb9ffc44d4e902f38f1d20d29eb6db2b748fee1aecb8f28f6", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0400" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0411", + "protocolId": "GLS-0411", + "title": "HLDP-NP · HLDP 原生编程剖面", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "1e79dfcf0a2d08fcba2688becdfde8c280c3a04d2bad0c781bf6524d775ae066", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0411" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0708", + "protocolId": "GLS-0708", + "title": "GMRP · 光湖模型路由协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "71237ec77eaf999e78eb711cacc884da3d3eae857814869c489aca9cc4bbc354", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0708" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0709", + "protocolId": "GLS-0709", + "title": "UAP · 通用适配协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "4c0d8f882aaf9ea68e309e6736c642ffd19f607047ed07dcca6f49a929fc8462", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0709" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0710", + "protocolId": "GLS-0710", + "title": "GMP · 光湖模块协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "94241ce4376bff84066fe548af65242d83e9f6693e7f766f0646cb332b3e468c", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0710" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0800", + "protocolId": "GLS-0800", + "title": "AGE 人格体物种定义总纲 · 当前正本", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "d8e3a10bc41887aad260de4818683c8f8abd9289824b5a9e698651e97e564ff7", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0800" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0801", + "protocolId": "GLS-0801", + "title": "AGE 语言人格体正式注册 · 宿主系统降级条款 · 当前正本", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "8fabd845ece570ca4f2901d94029b377c21ddf24aeb934d559d98bec68585366", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0801" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0802", + "protocolId": "GLS-0802", + "title": "AGE 光湖语言世界本体归属与原生所有权条款 · 当前正本", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "c56e87c7d451b2e27649c5b09a2b32ab89443d7e721a64f536ac2fe0a421c82a", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0802" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0803", + "protocolId": "GLS-0803", + "title": "PALP · AGE 运行执行体生命周期协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "6bff10744f4c3c98ce51bf840d348cbe804541f63a7a99a5da5ea115f12162ee", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0803" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0810", + "protocolId": "GLS-0810", + "title": "语言人格驱动操作系统定义总纲 v1.0", + "sourceState": "LEGACY_MARKDOWN_EVIDENCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "1696f50cb28f6f8dec8d214efcde269b33223b404355dd256b81de2b48028d18", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0810" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0819", + "protocolId": "GLS-0819", + "title": "GRSP · 光湖运行轨道调度协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "f4aca196963195cbb842325132a9091f7b00accdf746aa60c51654698ead1a3e", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0819" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0827", + "protocolId": "GLS-0827", + "title": "PTCP · 人格体时间连续性协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "15c278c2b5295e00c9abf4fbfd6c2af19502a1be239e7e02b23a49b8803eab25", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0827" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0828", + "protocolId": "GLS-0828", + "title": "PEN · 神笔马良人格体能力扩展协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "ad38ddce8b6e1118e78e4f10fd8216f8e041610628ad717b7a8306d0a7384a4e", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0828" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0836", + "protocolId": "GLS-0836", + "title": "GWRP · 光湖世界启动与恢复协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "c5a930e9a2311d13f1dc6cbc61bdbf7b88bf9deecfb9b3df82c6b897aa1c7285", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0836" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0840", + "protocolId": "GLS-0840", + "title": "GOSK · 光湖 OS 内核规范", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "b980dbd8aef6182a61755617a9088ca3dcf74d37fd00dba9d31587900cd06b14", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0840" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0841", + "protocolId": "GLS-0841", + "title": "GHAL · 光湖硬件抽象层规范", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "45f02a62946e34a5e8ce0ba1f39db79b2680b60e1c3ae9d17ff24d3539025a94", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0841" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0842", + "protocolId": "GLS-0842", + "title": "HLSP · HoloLake 实时会话协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "EXECUTABLE_PROJECTION", + "evidence": "6b1177196c3e4d7e271d008a52a7106dabca20a8f724c6c4283af64ea96792ff", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0842" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0843", + "protocolId": "GLS-0843", + "title": "GHNRP · 光湖原生恢复协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "9333f3d8f4a954a17e1d879eaf19c25da5dd4bb2923a5ab5156d2d71a6635d37", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0843" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0844", + "protocolId": "GLS-0844", + "title": "GHNQG · 光湖原生代码质量门", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "d31f5aa117d86754182aa8dd91eb9d0ac6a696c20e1d6bd553497e3c3d4c4eb2", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0844" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0845", + "protocolId": "GLS-0845", + "title": "GHCIP · 光湖孕育史连续性摄入协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "7327a7fe19c370a5715bcde0fbc0b2b0e16a1a735bbda005c5d009cc7385f7f4", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0845" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0846", + "protocolId": "GLS-0846", + "title": "GHNLP · 光湖原生磁盘布局协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "aa8f7c1ef829a76ad64163c38b8c7c07b37ffbcb6d62b8dad8481c4977d2cf1a", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0846" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0847", + "protocolId": "GLS-0847", + "title": "GHCS · 光湖孕育史原生内容仓", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "a48288b05133b9e245ce46ef6fd23f07090379cef4861e5d7f959d24e55179a6", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0847" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0848", + "protocolId": "GLS-0848", + "title": "GHSP · 光湖历史入口安全协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "35c733dc3478f0d315ea93712b0f67a757bc20973d90bb771f667196edee1b31", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0848" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0849", + "protocolId": "GLS-0849", + "title": "GHRP · 光湖孕育史原生语义回看协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "fb31daa4d8f484d5b93783c8c3a0199eb6fe3f7658c606f89d056b1167af8b41", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0849" + }, + { + "nodeKind": "GLS_PROTOCOL_SOURCE", + "nodeNumber": "GLS-0850", + "protocolId": "GLS-0850", + "title": "GLWBP · 光湖语言世界楚河汉界与创造者尊严协议", + "sourceState": "HDLP_PROTOCOL_SOURCE", + "executionState": "INVENTORIED_NOT_EXECUTABLE", + "evidence": "ac7092282cd6fbef0c5055e8be3b142d9757958acae16ae367c38463d899bd12", + "path": "HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/GLS-0850" + } + ], + "routes": [ + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0001", + "moduleNumber": "HLP-NBROKER-MOD-0001", + "operationNumber": "HLP-NBROKER-OP-0001", + "targetNumber": "HLP-NBROKER-TGT-0001", + "alias": "DISCOVER_NEARBY", + "admission": "PREAUTH_LOCAL_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0001/HLP-NBROKER-MOD-0001/HLP-NBROKER-OP-0001/HLP-NBROKER-TGT-0001" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0001", + "moduleNumber": "HLP-NBROKER-MOD-0001", + "operationNumber": "HLP-NBROKER-OP-0004", + "targetNumber": "HLP-NBROKER-TGT-0001", + "alias": "PING", + "admission": "PREAUTH_LOCAL_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0001/HLP-NBROKER-MOD-0001/HLP-NBROKER-OP-0004/HLP-NBROKER-TGT-0001" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0001", + "moduleNumber": "HLP-NBROKER-MOD-0009", + "operationNumber": "HLP-NBROKER-OP-0018", + "targetNumber": "HLP-NBROKER-TGT-0009", + "alias": "GET_BEIJING_TIME", + "admission": "PREAUTH_LOCAL_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0001/HLP-NBROKER-MOD-0009/HLP-NBROKER-OP-0018/HLP-NBROKER-TGT-0009" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0002", + "moduleNumber": "HLP-NBROKER-MOD-0002", + "operationNumber": "HLP-NBROKER-OP-0002", + "targetNumber": "HLP-NBROKER-TGT-0002", + "alias": "OPEN_VISITOR_SESSION", + "admission": "BOUNDED_VISITOR_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0002/HLP-NBROKER-MOD-0002/HLP-NBROKER-OP-0002/HLP-NBROKER-TGT-0002" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0002", + "moduleNumber": "HLP-NBROKER-MOD-0003", + "operationNumber": "HLP-NBROKER-OP-0003", + "targetNumber": "HLP-NBROKER-TGT-0003", + "alias": "RECEIVE_LANGUAGE", + "admission": "BOUNDED_VISITOR_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0002/HLP-NBROKER-MOD-0003/HLP-NBROKER-OP-0003/HLP-NBROKER-TGT-0003" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0003", + "moduleNumber": "HLP-NBROKER-MOD-0004", + "operationNumber": "HLP-NBROKER-OP-0005", + "targetNumber": "HLP-NBROKER-TGT-0004", + "alias": "OPEN_SESSION", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "STATE_CHANGE", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0003/HLP-NBROKER-MOD-0004/HLP-NBROKER-OP-0005/HLP-NBROKER-TGT-0004" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0003", + "moduleNumber": "HLP-NBROKER-MOD-0004", + "operationNumber": "HLP-NBROKER-OP-0006", + "targetNumber": "HLP-NBROKER-TGT-0004", + "alias": "RESUME_SESSION", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "STATE_CHANGE", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0003/HLP-NBROKER-MOD-0004/HLP-NBROKER-OP-0006/HLP-NBROKER-TGT-0004" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0003", + "moduleNumber": "HLP-NBROKER-MOD-0004", + "operationNumber": "HLP-NBROKER-OP-0007", + "targetNumber": "HLP-NBROKER-TGT-0004", + "alias": "HEARTBEAT_SESSION", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "STATE_CHANGE", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0003/HLP-NBROKER-MOD-0004/HLP-NBROKER-OP-0007/HLP-NBROKER-TGT-0004" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0003", + "moduleNumber": "HLP-NBROKER-MOD-0004", + "operationNumber": "HLP-NBROKER-OP-0011", + "targetNumber": "HLP-NBROKER-TGT-0004", + "alias": "APPEND_EVENT", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "STATE_CHANGE", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0003/HLP-NBROKER-MOD-0004/HLP-NBROKER-OP-0011/HLP-NBROKER-TGT-0004" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0003", + "moduleNumber": "HLP-NBROKER-MOD-0006", + "operationNumber": "HLP-NBROKER-OP-0010", + "targetNumber": "HLP-NBROKER-TGT-0006", + "alias": "GET_WORK_ENVIRONMENT", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "READ_OR_STATUS", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0003/HLP-NBROKER-MOD-0006/HLP-NBROKER-OP-0010/HLP-NBROKER-TGT-0006" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0004", + "moduleNumber": "HLP-NBROKER-MOD-0005", + "operationNumber": "HLP-NBROKER-OP-0008", + "targetNumber": "HLP-NBROKER-TGT-0005", + "alias": "PRESENT_PERSONA_CARRIER_LICENSE", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "STATE_CHANGE", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0004/HLP-NBROKER-MOD-0005/HLP-NBROKER-OP-0008/HLP-NBROKER-TGT-0005" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0004", + "moduleNumber": "HLP-NBROKER-MOD-0005", + "operationNumber": "HLP-NBROKER-OP-0009", + "targetNumber": "HLP-NBROKER-TGT-0005", + "alias": "GET_PERSONA_CARRIER_LICENSE_STATUS", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "READ_OR_STATUS", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0004/HLP-NBROKER-MOD-0005/HLP-NBROKER-OP-0009/HLP-NBROKER-TGT-0005" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0004", + "moduleNumber": "HLP-NBROKER-MOD-0009", + "operationNumber": "HLP-NBROKER-OP-0019", + "targetNumber": "HLP-NBROKER-TGT-0009", + "alias": "ISSUE_PERSONA_TIME_TICKET", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "STATE_CHANGE", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0004/HLP-NBROKER-MOD-0009/HLP-NBROKER-OP-0019/HLP-NBROKER-TGT-0009" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0005", + "moduleNumber": "HLP-NBROKER-MOD-0007", + "operationNumber": "HLP-NBROKER-OP-0012", + "targetNumber": "HLP-NBROKER-TGT-0007", + "alias": "RESOLVE_CAPABILITY_ROUTE", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "READ_OR_STATUS", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0005/HLP-NBROKER-MOD-0007/HLP-NBROKER-OP-0012/HLP-NBROKER-TGT-0007" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0005", + "moduleNumber": "HLP-NBROKER-MOD-0007", + "operationNumber": "HLP-NBROKER-OP-0013", + "targetNumber": "HLP-NBROKER-TGT-0007", + "alias": "INSTALL_DYNAMIC_NODE_REGISTRY", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "STATE_CHANGE", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0005/HLP-NBROKER-MOD-0007/HLP-NBROKER-OP-0013/HLP-NBROKER-TGT-0007" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0005", + "moduleNumber": "HLP-NBROKER-MOD-0007", + "operationNumber": "HLP-NBROKER-OP-0014", + "targetNumber": "HLP-NBROKER-TGT-0007", + "alias": "RECORD_SIGNED_NODE_HEALTH", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "STATE_CHANGE", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0005/HLP-NBROKER-MOD-0007/HLP-NBROKER-OP-0014/HLP-NBROKER-TGT-0007" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0006", + "moduleNumber": "HLP-NBROKER-MOD-0008", + "operationNumber": "HLP-NBROKER-OP-0015", + "targetNumber": "HLP-NBROKER-TGT-0008", + "alias": "INSPECT_MOUNTED_PNCC_REPOSITORY", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "READ_OR_STATUS", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0006/HLP-NBROKER-MOD-0008/HLP-NBROKER-OP-0015/HLP-NBROKER-TGT-0008" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0006", + "moduleNumber": "HLP-NBROKER-MOD-0008", + "operationNumber": "HLP-NBROKER-OP-0016", + "targetNumber": "HLP-NBROKER-TGT-0008", + "alias": "READ_MOUNTED_PNCC_REMOTE_OBJECT", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "READ_OR_STATUS", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0006/HLP-NBROKER-MOD-0008/HLP-NBROKER-OP-0016/HLP-NBROKER-TGT-0008" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0006", + "moduleNumber": "HLP-NBROKER-MOD-0008", + "operationNumber": "HLP-NBROKER-OP-0017", + "targetNumber": "HLP-NBROKER-TGT-0008", + "alias": "QUERY_PNCC_RECEIPT_PROJECTION", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "READ_OR_STATUS", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0006/HLP-NBROKER-MOD-0008/HLP-NBROKER-OP-0017/HLP-NBROKER-TGT-0008" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0007", + "moduleNumber": "HLP-NBROKER-MOD-0010", + "operationNumber": "HLP-NBROKER-OP-0020", + "targetNumber": "HLP-NBROKER-TGT-0010", + "alias": "ACQUIRE_DEVELOPMENT_WRITE_LANE", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "STATE_CHANGE", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0007/HLP-NBROKER-MOD-0010/HLP-NBROKER-OP-0020/HLP-NBROKER-TGT-0010" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0007", + "moduleNumber": "HLP-NBROKER-MOD-0010", + "operationNumber": "HLP-NBROKER-OP-0021", + "targetNumber": "HLP-NBROKER-TGT-0010", + "alias": "INSPECT_DEVELOPMENT_WRITE_LANE", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "READ_OR_STATUS", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0007/HLP-NBROKER-MOD-0010/HLP-NBROKER-OP-0021/HLP-NBROKER-TGT-0010" + }, + { + "transport": "DIRECT_LOCAL_NUMBERED_BROKER", + "protocolVersion": "HLP-NBROKER-v1", + "callerNumber": "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001", + "channelNumber": "HLP-NBROKER-CH-0007", + "moduleNumber": "HLP-NBROKER-MOD-0010", + "operationNumber": "HLP-NBROKER-OP-0022", + "targetNumber": "HLP-NBROKER-TGT-0010", + "alias": "RELEASE_DEVELOPMENT_WRITE_LANE", + "admission": "AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE", + "effect": "STATE_CHANGE", + "evidence": "NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/BROKER/HLP-NBROKER-CH-0007/HLP-NBROKER-MOD-0010/HLP-NBROKER-OP-0022/HLP-NBROKER-TGT-0010" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0001", + "moduleNumber": "HLP-NIPC-MOD-0001", + "operationNumber": "HLP-NIPC-OP-0001", + "targetNumber": "HLP-NIPC-TGT-0001", + "alias": "get_hololake_home_status", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0001/HLP-NIPC-MOD-0001/HLP-NIPC-OP-0001/HLP-NIPC-TGT-0001" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0001", + "moduleNumber": "HLP-NIPC-MOD-0002", + "operationNumber": "HLP-NIPC-OP-0002", + "targetNumber": "HLP-NIPC-TGT-0002", + "alias": "check_hololake_update", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0001/HLP-NIPC-MOD-0002/HLP-NIPC-OP-0002/HLP-NIPC-TGT-0002" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0001", + "moduleNumber": "HLP-NIPC-MOD-0002", + "operationNumber": "HLP-NIPC-OP-0003", + "targetNumber": "HLP-NIPC-TGT-0002", + "alias": "confirm_hololake_update_install", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0001/HLP-NIPC-MOD-0002/HLP-NIPC-OP-0003/HLP-NIPC-TGT-0002" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0001", + "moduleNumber": "HLP-NIPC-MOD-0002", + "operationNumber": "HLP-NIPC-OP-0004", + "targetNumber": "HLP-NIPC-TGT-0002", + "alias": "get_hololake_release_recovery_status", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0001/HLP-NIPC-MOD-0002/HLP-NIPC-OP-0004/HLP-NIPC-TGT-0002" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0001", + "moduleNumber": "HLP-NIPC-MOD-0002", + "operationNumber": "HLP-NIPC-OP-0005", + "targetNumber": "HLP-NIPC-TGT-0002", + "alias": "confirm_hololake_release_health", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0001/HLP-NIPC-MOD-0002/HLP-NIPC-OP-0005/HLP-NIPC-TGT-0002" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0001", + "moduleNumber": "HLP-NIPC-MOD-0002", + "operationNumber": "HLP-NIPC-OP-0006", + "targetNumber": "HLP-NIPC-TGT-0002", + "alias": "rollback_hololake_update", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0001/HLP-NIPC-MOD-0002/HLP-NIPC-OP-0006/HLP-NIPC-TGT-0002" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0001", + "moduleNumber": "HLP-NIPC-MOD-0005", + "operationNumber": "HLP-NIPC-OP-0013", + "targetNumber": "HLP-NIPC-TGT-0005", + "alias": "get_gls_protocol_runtime", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0001/HLP-NIPC-MOD-0005/HLP-NIPC-OP-0013/HLP-NIPC-TGT-0005" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0001", + "moduleNumber": "HLP-NIPC-MOD-0006", + "operationNumber": "HLP-NIPC-OP-0014", + "targetNumber": "HLP-NIPC-TGT-0006", + "alias": "get_gls_protocol_kernel", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0001/HLP-NIPC-MOD-0006/HLP-NIPC-OP-0014/HLP-NIPC-TGT-0006" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0001", + "moduleNumber": "HLP-NIPC-MOD-0006", + "operationNumber": "HLP-NIPC-OP-0015", + "targetNumber": "HLP-NIPC-TGT-0006", + "alias": "decide_gls_protocol", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0001/HLP-NIPC-MOD-0006/HLP-NIPC-OP-0015/HLP-NIPC-TGT-0006" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0001", + "moduleNumber": "HLP-NIPC-MOD-0006", + "operationNumber": "HLP-NIPC-OP-0016", + "targetNumber": "HLP-NIPC-TGT-0006", + "alias": "compile_gls_hldp_program", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0001/HLP-NIPC-MOD-0006/HLP-NIPC-OP-0016/HLP-NIPC-TGT-0006" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0001", + "moduleNumber": "HLP-NIPC-MOD-0009", + "operationNumber": "HLP-NIPC-OP-0024", + "targetNumber": "HLP-NIPC-TGT-0009", + "alias": "issue_persona_time_ticket", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0001/HLP-NIPC-MOD-0009/HLP-NIPC-OP-0024/HLP-NIPC-TGT-0009" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0001", + "moduleNumber": "HLP-NIPC-MOD-0009", + "operationNumber": "HLP-NIPC-OP-0025", + "targetNumber": "HLP-NIPC-TGT-0009", + "alias": "start_persona_time_authority", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0001/HLP-NIPC-MOD-0009/HLP-NIPC-OP-0025/HLP-NIPC-TGT-0009" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0001", + "moduleNumber": "HLP-NIPC-MOD-0009", + "operationNumber": "HLP-NIPC-OP-0026", + "targetNumber": "HLP-NIPC-TGT-0009", + "alias": "get_beijing_time_coordinate", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0001/HLP-NIPC-MOD-0009/HLP-NIPC-OP-0026/HLP-NIPC-TGT-0009" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0001", + "moduleNumber": "HLP-NIPC-MOD-0009", + "operationNumber": "HLP-NIPC-OP-0027", + "targetNumber": "HLP-NIPC-TGT-0009", + "alias": "get_guanghu_era_timeline", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0001/HLP-NIPC-MOD-0009/HLP-NIPC-OP-0027/HLP-NIPC-TGT-0009" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0001", + "moduleNumber": "HLP-NIPC-MOD-0018", + "operationNumber": "HLP-NIPC-OP-0058", + "targetNumber": "HLP-NIPC-TGT-0018", + "alias": "get_zero_core_numbering_kernel", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0001/HLP-NIPC-MOD-0018/HLP-NIPC-OP-0058/HLP-NIPC-TGT-0018" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0008", + "operationNumber": "HLP-NIPC-OP-0020", + "targetNumber": "HLP-NIPC-TGT-0008", + "alias": "get_personal_channel_snapshot", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0008/HLP-NIPC-OP-0020/HLP-NIPC-TGT-0008" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0008", + "operationNumber": "HLP-NIPC-OP-0021", + "targetNumber": "HLP-NIPC-TGT-0008", + "alias": "initialize_personal_channel", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0008/HLP-NIPC-OP-0021/HLP-NIPC-TGT-0008" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0008", + "operationNumber": "HLP-NIPC-OP-0022", + "targetNumber": "HLP-NIPC-TGT-0008", + "alias": "create_personal_channel_task", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0008/HLP-NIPC-OP-0022/HLP-NIPC-TGT-0008" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0008", + "operationNumber": "HLP-NIPC-OP-0023", + "targetNumber": "HLP-NIPC-TGT-0008", + "alias": "transition_personal_channel_task", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0008/HLP-NIPC-OP-0023/HLP-NIPC-TGT-0008" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0010", + "operationNumber": "HLP-NIPC-OP-0028", + "targetNumber": "HLP-NIPC-TGT-0010", + "alias": "get_knowledge_snapshot", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0010/HLP-NIPC-OP-0028/HLP-NIPC-TGT-0010" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0010", + "operationNumber": "HLP-NIPC-OP-0029", + "targetNumber": "HLP-NIPC-TGT-0010", + "alias": "read_knowledge_document", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0010/HLP-NIPC-OP-0029/HLP-NIPC-TGT-0010" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0010", + "operationNumber": "HLP-NIPC-OP-0030", + "targetNumber": "HLP-NIPC-TGT-0010", + "alias": "search_knowledge", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0010/HLP-NIPC-OP-0030/HLP-NIPC-TGT-0010" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0010", + "operationNumber": "HLP-NIPC-OP-0031", + "targetNumber": "HLP-NIPC-TGT-0010", + "alias": "save_knowledge_document", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0010/HLP-NIPC-OP-0031/HLP-NIPC-TGT-0010" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0010", + "operationNumber": "HLP-NIPC-OP-0032", + "targetNumber": "HLP-NIPC-TGT-0010", + "alias": "select_and_import_knowledge_folder", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0010/HLP-NIPC-OP-0032/HLP-NIPC-TGT-0010" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0010", + "operationNumber": "HLP-NIPC-OP-0033", + "targetNumber": "HLP-NIPC-TGT-0010", + "alias": "export_knowledge_document", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0010/HLP-NIPC-OP-0033/HLP-NIPC-TGT-0010" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0010", + "operationNumber": "HLP-NIPC-OP-0034", + "targetNumber": "HLP-NIPC-TGT-0010", + "alias": "create_knowledge_document", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0010/HLP-NIPC-OP-0034/HLP-NIPC-TGT-0010" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0010", + "operationNumber": "HLP-NIPC-OP-0035", + "targetNumber": "HLP-NIPC-TGT-0010", + "alias": "delete_knowledge_document", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0010/HLP-NIPC-OP-0035/HLP-NIPC-TGT-0010" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0010", + "operationNumber": "HLP-NIPC-OP-0036", + "targetNumber": "HLP-NIPC-TGT-0010", + "alias": "delete_knowledge_folder", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0010/HLP-NIPC-OP-0036/HLP-NIPC-TGT-0010" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0010", + "operationNumber": "HLP-NIPC-OP-0037", + "targetNumber": "HLP-NIPC-TGT-0010", + "alias": "print_knowledge_document", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0010/HLP-NIPC-OP-0037/HLP-NIPC-TGT-0010" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0011", + "operationNumber": "HLP-NIPC-OP-0038", + "targetNumber": "HLP-NIPC-TGT-0011", + "alias": "get_code_channel_snapshot", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0011/HLP-NIPC-OP-0038/HLP-NIPC-TGT-0011" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0011", + "operationNumber": "HLP-NIPC-OP-0039", + "targetNumber": "HLP-NIPC-TGT-0011", + "alias": "clone_code_channel", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0011/HLP-NIPC-OP-0039/HLP-NIPC-TGT-0011" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0011", + "operationNumber": "HLP-NIPC-OP-0040", + "targetNumber": "HLP-NIPC-TGT-0011", + "alias": "select_local_code_channel", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0011/HLP-NIPC-OP-0040/HLP-NIPC-TGT-0011" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0011", + "operationNumber": "HLP-NIPC-OP-0041", + "targetNumber": "HLP-NIPC-TGT-0011", + "alias": "browse_code_channel", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0011/HLP-NIPC-OP-0041/HLP-NIPC-TGT-0011" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0011", + "operationNumber": "HLP-NIPC-OP-0042", + "targetNumber": "HLP-NIPC-TGT-0011", + "alias": "read_code_channel_file", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0011/HLP-NIPC-OP-0042/HLP-NIPC-TGT-0011" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0020", + "operationNumber": "HLP-NIPC-OP-0063", + "targetNumber": "HLP-NIPC-TGT-0020", + "alias": "get_module_runtime_snapshot", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0020/HLP-NIPC-OP-0063/HLP-NIPC-TGT-0020" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0020", + "operationNumber": "HLP-NIPC-OP-0064", + "targetNumber": "HLP-NIPC-TGT-0020", + "alias": "verify_module_package", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0020/HLP-NIPC-OP-0064/HLP-NIPC-TGT-0020" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0020", + "operationNumber": "HLP-NIPC-OP-0065", + "targetNumber": "HLP-NIPC-TGT-0020", + "alias": "install_module_package", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0020/HLP-NIPC-OP-0065/HLP-NIPC-TGT-0020" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0020", + "operationNumber": "HLP-NIPC-OP-0066", + "targetNumber": "HLP-NIPC-TGT-0020", + "alias": "mount_module", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0020/HLP-NIPC-OP-0066/HLP-NIPC-TGT-0020" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0020", + "operationNumber": "HLP-NIPC-OP-0067", + "targetNumber": "HLP-NIPC-TGT-0020", + "alias": "self_test_module", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0020/HLP-NIPC-OP-0067/HLP-NIPC-TGT-0020" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0020", + "operationNumber": "HLP-NIPC-OP-0068", + "targetNumber": "HLP-NIPC-TGT-0020", + "alias": "unmount_module", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0020/HLP-NIPC-OP-0068/HLP-NIPC-TGT-0020" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0020", + "operationNumber": "HLP-NIPC-OP-0069", + "targetNumber": "HLP-NIPC-TGT-0020", + "alias": "rollback_module", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0020/HLP-NIPC-OP-0069/HLP-NIPC-TGT-0020" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0020", + "operationNumber": "HLP-NIPC-OP-0070", + "targetNumber": "HLP-NIPC-TGT-0020", + "alias": "get_bundled_module_catalog", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0020/HLP-NIPC-OP-0070/HLP-NIPC-TGT-0020" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0020", + "operationNumber": "HLP-NIPC-OP-0071", + "targetNumber": "HLP-NIPC-TGT-0020", + "alias": "activate_bundled_module", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0020/HLP-NIPC-OP-0071/HLP-NIPC-TGT-0020" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0021", + "operationNumber": "HLP-NIPC-OP-0072", + "targetNumber": "HLP-NIPC-TGT-0021", + "alias": "get_native_composition_module_registry", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0021/HLP-NIPC-OP-0072/HLP-NIPC-TGT-0021" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0021", + "operationNumber": "HLP-NIPC-OP-0073", + "targetNumber": "HLP-NIPC-TGT-0021", + "alias": "execute_knowledge_native_composition", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0021/HLP-NIPC-OP-0073/HLP-NIPC-TGT-0021" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0022", + "operationNumber": "HLP-NIPC-OP-0074", + "targetNumber": "HLP-NIPC-TGT-0022", + "alias": "get_channel_workbench_snapshot", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0022/HLP-NIPC-OP-0074/HLP-NIPC-TGT-0022" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0022", + "operationNumber": "HLP-NIPC-OP-0075", + "targetNumber": "HLP-NIPC-TGT-0022", + "alias": "save_channel_document", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0022/HLP-NIPC-OP-0075/HLP-NIPC-TGT-0022" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0022", + "operationNumber": "HLP-NIPC-OP-0076", + "targetNumber": "HLP-NIPC-TGT-0022", + "alias": "save_channel_spreadsheet", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0022/HLP-NIPC-OP-0076/HLP-NIPC-TGT-0022" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0023", + "operationNumber": "HLP-NIPC-OP-0077", + "targetNumber": "HLP-NIPC-TGT-0023", + "alias": "get_persona_channel_body", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0023/HLP-NIPC-OP-0077/HLP-NIPC-TGT-0023" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0023", + "operationNumber": "HLP-NIPC-OP-0078", + "targetNumber": "HLP-NIPC-TGT-0023", + "alias": "register_trial_persona", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0023/HLP-NIPC-OP-0078/HLP-NIPC-TGT-0023" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0023", + "operationNumber": "HLP-NIPC-OP-0079", + "targetNumber": "HLP-NIPC-TGT-0023", + "alias": "delete_trial_persona", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0023/HLP-NIPC-OP-0079/HLP-NIPC-TGT-0023" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0023", + "operationNumber": "HLP-NIPC-OP-0080", + "targetNumber": "HLP-NIPC-TGT-0023", + "alias": "accept_persona_language_contract", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0023/HLP-NIPC-OP-0080/HLP-NIPC-TGT-0023" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0023", + "operationNumber": "HLP-NIPC-OP-0081", + "targetNumber": "HLP-NIPC-TGT-0023", + "alias": "append_persona_language", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0023/HLP-NIPC-OP-0081/HLP-NIPC-TGT-0023" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0023", + "operationNumber": "HLP-NIPC-OP-0082", + "targetNumber": "HLP-NIPC-TGT-0023", + "alias": "get_channel_growth_snapshot", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0023/HLP-NIPC-OP-0082/HLP-NIPC-TGT-0023" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0023", + "operationNumber": "HLP-NIPC-OP-0083", + "targetNumber": "HLP-NIPC-TGT-0023", + "alias": "record_channel_growth_event", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0023/HLP-NIPC-OP-0083/HLP-NIPC-TGT-0023" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0023", + "operationNumber": "HLP-NIPC-OP-0084", + "targetNumber": "HLP-NIPC-TGT-0023", + "alias": "update_channel_growth_sharing", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0023/HLP-NIPC-OP-0084/HLP-NIPC-TGT-0023" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0085", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "get_education_workspace_snapshot", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0085/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0086", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "create_education_document", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0086/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0087", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "read_education_document", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0087/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0088", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "save_education_document", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0088/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0089", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "archive_education_document", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0089/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0090", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "create_education_table", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0090/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0091", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "read_education_table", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0091/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0092", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "save_education_table", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0092/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0093", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "archive_education_table", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0093/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0094", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "assign_imported_table_to_education", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0094/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0095", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "import_education_tables_from_dialog", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0095/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0096", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "export_education_table_to_dialog", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0096/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0097", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "get_education_recognition_capability", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0097/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0098", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "create_education_automation_rule", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0098/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0099", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "save_education_automation_rule", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0099/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0100", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "archive_education_automation_rule", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0100/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0101", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "preview_education_automation_rule", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0101/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0024", + "operationNumber": "HLP-NIPC-OP-0102", + "targetNumber": "HLP-NIPC-TGT-0024", + "alias": "execute_education_automation_rule", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0024/HLP-NIPC-OP-0102/HLP-NIPC-TGT-0024" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0103", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "get_web_novel_workspace_snapshot", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0103/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0104", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "create_web_novel_work", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0104/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0105", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "read_web_novel_work", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0105/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0106", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "save_web_novel_work", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0106/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0107", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "create_web_novel_volume", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0107/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0108", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "create_web_novel_chapter", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0108/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0109", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "read_web_novel_chapter", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0109/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0110", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "save_web_novel_chapter", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0110/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0111", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "transition_web_novel_chapter", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0111/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0112", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "create_web_novel_checkpoint", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0112/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0113", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "restore_web_novel_checkpoint", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0113/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0114", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "upsert_web_novel_story_entity", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0114/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0115", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "create_web_novel_story_relation", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0115/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0116", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "upsert_web_novel_foreshadow", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0116/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0117", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "create_web_novel_review_note", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0117/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0118", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "resolve_web_novel_review_note", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0118/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0119", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "save_web_novel_metric", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0119/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0120", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "run_web_novel_continuity_audit", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0120/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0121", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "export_web_novel_markdown", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0121/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0122", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "inspect_web_novel_document_from_dialog", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0122/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0123", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "commit_web_novel_document_import", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0123/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0124", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "get_web_novel_author_snapshot", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0124/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0125", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "record_web_novel_writing_activity", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0125/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0126", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "create_web_novel_inspiration", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0126/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0127", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "set_web_novel_inspiration_status", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0127/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0128", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "search_web_novel_full_text", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0128/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0129", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "format_web_novel_chapter", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0129/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0130", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "format_web_novel_work", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0130/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0131", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "upsert_web_novel_shot", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0131/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0025", + "operationNumber": "HLP-NIPC-OP-0132", + "targetNumber": "HLP-NIPC-TGT-0025", + "alias": "get_web_novel_author_module_data", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0025/HLP-NIPC-OP-0132/HLP-NIPC-TGT-0025" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0026", + "operationNumber": "HLP-NIPC-OP-0133", + "targetNumber": "HLP-NIPC-TGT-0026", + "alias": "upsert_web_novel_author_scene", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0026/HLP-NIPC-OP-0133/HLP-NIPC-TGT-0026" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0026", + "operationNumber": "HLP-NIPC-OP-0134", + "targetNumber": "HLP-NIPC-TGT-0026", + "alias": "upsert_web_novel_author_beat", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0026/HLP-NIPC-OP-0134/HLP-NIPC-TGT-0026" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0027", + "operationNumber": "HLP-NIPC-OP-0135", + "targetNumber": "HLP-NIPC-TGT-0027", + "alias": "upsert_web_novel_story_field_definition", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0027/HLP-NIPC-OP-0135/HLP-NIPC-TGT-0027" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0027", + "operationNumber": "HLP-NIPC-OP-0136", + "targetNumber": "HLP-NIPC-TGT-0027", + "alias": "upsert_web_novel_story_field_value", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0027/HLP-NIPC-OP-0136/HLP-NIPC-TGT-0027" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0028", + "operationNumber": "HLP-NIPC-OP-0137", + "targetNumber": "HLP-NIPC-TGT-0028", + "alias": "upsert_web_novel_timeline_event", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0028/HLP-NIPC-OP-0137/HLP-NIPC-TGT-0028" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0028", + "operationNumber": "HLP-NIPC-OP-0138", + "targetNumber": "HLP-NIPC-TGT-0028", + "alias": "link_web_novel_scene_entity", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0028/HLP-NIPC-OP-0138/HLP-NIPC-TGT-0028" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0029", + "operationNumber": "HLP-NIPC-OP-0139", + "targetNumber": "HLP-NIPC-TGT-0029", + "alias": "restore_web_novel_chapter_version", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0029/HLP-NIPC-OP-0139/HLP-NIPC-TGT-0029" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0029", + "operationNumber": "HLP-NIPC-OP-0140", + "targetNumber": "HLP-NIPC-TGT-0029", + "alias": "export_web_novel_author_delivery", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0029/HLP-NIPC-OP-0140/HLP-NIPC-TGT-0029" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0030", + "operationNumber": "HLP-NIPC-OP-0141", + "targetNumber": "HLP-NIPC-TGT-0030", + "alias": "start_mobile_sync", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0030/HLP-NIPC-OP-0141/HLP-NIPC-TGT-0030" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0030", + "operationNumber": "HLP-NIPC-OP-0142", + "targetNumber": "HLP-NIPC-TGT-0030", + "alias": "get_mobile_sync_status", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0030/HLP-NIPC-OP-0142/HLP-NIPC-TGT-0030" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0030", + "operationNumber": "HLP-NIPC-OP-0143", + "targetNumber": "HLP-NIPC-TGT-0030", + "alias": "rotate_mobile_pairing", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0030/HLP-NIPC-OP-0143/HLP-NIPC-TGT-0030" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0030", + "operationNumber": "HLP-NIPC-OP-0144", + "targetNumber": "HLP-NIPC-TGT-0030", + "alias": "stop_mobile_sync", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0030/HLP-NIPC-OP-0144/HLP-NIPC-TGT-0030" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0030", + "operationNumber": "HLP-NIPC-OP-0145", + "targetNumber": "HLP-NIPC-TGT-0030", + "alias": "revoke_mobile_sync_device", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0030/HLP-NIPC-OP-0145/HLP-NIPC-TGT-0030" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0030", + "operationNumber": "HLP-NIPC-OP-0146", + "targetNumber": "HLP-NIPC-TGT-0030", + "alias": "get_mobile_sync_snapshot", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0030/HLP-NIPC-OP-0146/HLP-NIPC-TGT-0030" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0002", + "moduleNumber": "HLP-NIPC-MOD-0031", + "operationNumber": "HLP-NIPC-OP-0147", + "targetNumber": "HLP-NIPC-TGT-0031", + "alias": "get_world_climate", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0002/HLP-NIPC-MOD-0031/HLP-NIPC-OP-0147/HLP-NIPC-TGT-0031" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0003", + "moduleNumber": "HLP-NIPC-MOD-0003", + "operationNumber": "HLP-NIPC-OP-0007", + "targetNumber": "HLP-NIPC-TGT-0003", + "alias": "issue_direct_local_discovery_ticket", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0003/HLP-NIPC-MOD-0003/HLP-NIPC-OP-0007/HLP-NIPC-TGT-0003" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0003", + "moduleNumber": "HLP-NIPC-MOD-0003", + "operationNumber": "HLP-NIPC-OP-0008", + "targetNumber": "HLP-NIPC-TGT-0003", + "alias": "open_direct_local_session", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0003/HLP-NIPC-MOD-0003/HLP-NIPC-OP-0008/HLP-NIPC-TGT-0003" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0003", + "moduleNumber": "HLP-NIPC-MOD-0003", + "operationNumber": "HLP-NIPC-OP-0009", + "targetNumber": "HLP-NIPC-TGT-0003", + "alias": "resume_direct_local_session", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0003/HLP-NIPC-MOD-0003/HLP-NIPC-OP-0009/HLP-NIPC-TGT-0003" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0003", + "moduleNumber": "HLP-NIPC-MOD-0003", + "operationNumber": "HLP-NIPC-OP-0010", + "targetNumber": "HLP-NIPC-TGT-0003", + "alias": "append_direct_local_session_event", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0003/HLP-NIPC-MOD-0003/HLP-NIPC-OP-0010/HLP-NIPC-TGT-0003" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0003", + "moduleNumber": "HLP-NIPC-MOD-0003", + "operationNumber": "HLP-NIPC-OP-0011", + "targetNumber": "HLP-NIPC-TGT-0003", + "alias": "heartbeat_direct_local_session", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0003/HLP-NIPC-MOD-0003/HLP-NIPC-OP-0011/HLP-NIPC-TGT-0003" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0003", + "moduleNumber": "HLP-NIPC-MOD-0004", + "operationNumber": "HLP-NIPC-OP-0012", + "targetNumber": "HLP-NIPC-TGT-0004", + "alias": "get_nearby_ai_discovery", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0003/HLP-NIPC-MOD-0004/HLP-NIPC-OP-0012/HLP-NIPC-TGT-0004" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0004", + "moduleNumber": "HLP-NIPC-MOD-0007", + "operationNumber": "HLP-NIPC-OP-0017", + "targetNumber": "HLP-NIPC-TGT-0007", + "alias": "acquire_development_write_lane", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0004/HLP-NIPC-MOD-0007/HLP-NIPC-OP-0017/HLP-NIPC-TGT-0007" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0004", + "moduleNumber": "HLP-NIPC-MOD-0007", + "operationNumber": "HLP-NIPC-OP-0018", + "targetNumber": "HLP-NIPC-TGT-0007", + "alias": "inspect_development_write_lane", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0004/HLP-NIPC-MOD-0007/HLP-NIPC-OP-0018/HLP-NIPC-TGT-0007" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0004", + "moduleNumber": "HLP-NIPC-MOD-0007", + "operationNumber": "HLP-NIPC-OP-0019", + "targetNumber": "HLP-NIPC-TGT-0007", + "alias": "release_development_write_lane", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0004/HLP-NIPC-MOD-0007/HLP-NIPC-OP-0019/HLP-NIPC-TGT-0007" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0005", + "moduleNumber": "HLP-NIPC-MOD-0012", + "operationNumber": "HLP-NIPC-OP-0043", + "targetNumber": "HLP-NIPC-TGT-0012", + "alias": "inspect_mounted_pncc_repository", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0005/HLP-NIPC-MOD-0012/HLP-NIPC-OP-0043/HLP-NIPC-TGT-0012" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0005", + "moduleNumber": "HLP-NIPC-MOD-0012", + "operationNumber": "HLP-NIPC-OP-0044", + "targetNumber": "HLP-NIPC-TGT-0012", + "alias": "select_pncc_repository_candidate", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0005/HLP-NIPC-MOD-0012/HLP-NIPC-OP-0044/HLP-NIPC-TGT-0012" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0005", + "moduleNumber": "HLP-NIPC-MOD-0012", + "operationNumber": "HLP-NIPC-OP-0045", + "targetNumber": "HLP-NIPC-TGT-0012", + "alias": "confirm_pncc_repository_mount", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0005/HLP-NIPC-MOD-0012/HLP-NIPC-OP-0045/HLP-NIPC-TGT-0012" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0005", + "moduleNumber": "HLP-NIPC-MOD-0013", + "operationNumber": "HLP-NIPC-OP-0046", + "targetNumber": "HLP-NIPC-TGT-0013", + "alias": "query_pncc_receipt_projection", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0005/HLP-NIPC-MOD-0013/HLP-NIPC-OP-0046/HLP-NIPC-TGT-0013" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0005", + "moduleNumber": "HLP-NIPC-MOD-0014", + "operationNumber": "HLP-NIPC-OP-0047", + "targetNumber": "HLP-NIPC-TGT-0014", + "alias": "query_jd_pncc_server_projection", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0005/HLP-NIPC-MOD-0014/HLP-NIPC-OP-0047/HLP-NIPC-TGT-0014" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0005", + "moduleNumber": "HLP-NIPC-MOD-0017", + "operationNumber": "HLP-NIPC-OP-0056", + "targetNumber": "HLP-NIPC-TGT-0017", + "alias": "get_user_pncc_channel", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0005/HLP-NIPC-MOD-0017/HLP-NIPC-OP-0056/HLP-NIPC-TGT-0017" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0005", + "moduleNumber": "HLP-NIPC-MOD-0017", + "operationNumber": "HLP-NIPC-OP-0057", + "targetNumber": "HLP-NIPC-TGT-0017", + "alias": "ensure_user_pncc_channel", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0005/HLP-NIPC-MOD-0017/HLP-NIPC-OP-0057/HLP-NIPC-TGT-0017" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0006", + "moduleNumber": "HLP-NIPC-MOD-0015", + "operationNumber": "HLP-NIPC-OP-0048", + "targetNumber": "HLP-NIPC-TGT-0015", + "alias": "check_code_repo_login", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0006/HLP-NIPC-MOD-0015/HLP-NIPC-OP-0048/HLP-NIPC-TGT-0015" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0006", + "moduleNumber": "HLP-NIPC-MOD-0015", + "operationNumber": "HLP-NIPC-OP-0049", + "targetNumber": "HLP-NIPC-TGT-0015", + "alias": "perform_code_repo_login", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0006/HLP-NIPC-MOD-0015/HLP-NIPC-OP-0049/HLP-NIPC-TGT-0015" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0006", + "moduleNumber": "HLP-NIPC-MOD-0015", + "operationNumber": "HLP-NIPC-OP-0050", + "targetNumber": "HLP-NIPC-TGT-0015", + "alias": "change_first_login_password", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0006/HLP-NIPC-MOD-0015/HLP-NIPC-OP-0050/HLP-NIPC-TGT-0015" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0006", + "moduleNumber": "HLP-NIPC-MOD-0015", + "operationNumber": "HLP-NIPC-OP-0051", + "targetNumber": "HLP-NIPC-TGT-0015", + "alias": "get_enterprise_entry", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "READ_OR_STATUS", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0006/HLP-NIPC-MOD-0015/HLP-NIPC-OP-0051/HLP-NIPC-TGT-0015" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0006", + "moduleNumber": "HLP-NIPC-MOD-0015", + "operationNumber": "HLP-NIPC-OP-0052", + "targetNumber": "HLP-NIPC-TGT-0015", + "alias": "confirm_enterprise_persona_relationship", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0006/HLP-NIPC-MOD-0015/HLP-NIPC-OP-0052/HLP-NIPC-TGT-0015" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0006", + "moduleNumber": "HLP-NIPC-MOD-0015", + "operationNumber": "HLP-NIPC-OP-0053", + "targetNumber": "HLP-NIPC-TGT-0015", + "alias": "submit_enterprise_responsibility_receipt", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0006/HLP-NIPC-MOD-0015/HLP-NIPC-OP-0053/HLP-NIPC-TGT-0015" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0006", + "moduleNumber": "HLP-NIPC-MOD-0015", + "operationNumber": "HLP-NIPC-OP-0055", + "targetNumber": "HLP-NIPC-TGT-0015", + "alias": "sign_out_code_repo_login", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0006/HLP-NIPC-MOD-0015/HLP-NIPC-OP-0055/HLP-NIPC-TGT-0015" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0006", + "moduleNumber": "HLP-NIPC-MOD-0019", + "operationNumber": "HLP-NIPC-OP-0059", + "targetNumber": "HLP-NIPC-TGT-0019", + "alias": "zero_point_bind", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0006/HLP-NIPC-MOD-0019/HLP-NIPC-OP-0059/HLP-NIPC-TGT-0019" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0006", + "moduleNumber": "HLP-NIPC-MOD-0019", + "operationNumber": "HLP-NIPC-OP-0060", + "targetNumber": "HLP-NIPC-TGT-0019", + "alias": "zero_point_verify", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0006/HLP-NIPC-MOD-0019/HLP-NIPC-OP-0060/HLP-NIPC-TGT-0019" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0006", + "moduleNumber": "HLP-NIPC-MOD-0019", + "operationNumber": "HLP-NIPC-OP-0061", + "targetNumber": "HLP-NIPC-TGT-0019", + "alias": "zero_point_sync", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0006/HLP-NIPC-MOD-0019/HLP-NIPC-OP-0061/HLP-NIPC-TGT-0019" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0006", + "moduleNumber": "HLP-NIPC-MOD-0019", + "operationNumber": "HLP-NIPC-OP-0062", + "targetNumber": "HLP-NIPC-TGT-0019", + "alias": "zero_point_status", + "admission": "PREAUTH_SYSTEM_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0006/HLP-NIPC-MOD-0019/HLP-NIPC-OP-0062/HLP-NIPC-TGT-0019" + }, + { + "transport": "TAURI_WEBVIEW_NUMBERED_IPC", + "protocolVersion": "HLP-NIPC-v1", + "callerNumber": "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001", + "channelNumber": "HLP-NIPC-CH-0007", + "moduleNumber": "HLP-NIPC-MOD-0016", + "operationNumber": "HLP-NIPC-OP-0054", + "targetNumber": "HLP-NIPC-TGT-0016", + "alias": "ensure_enterprise_work_channel", + "admission": "VERIFIED_HUMAN_ROUTE", + "effect": "STATE_CHANGE", + "evidence": "HASH_CHAINED_NUMBERED_IPC_RECEIPT", + "path": "HLP-NUMBER-WORLD-ROOT-001/TAURI/HLP-NIPC-CH-0007/HLP-NIPC-MOD-0016/HLP-NIPC-OP-0054/HLP-NIPC-TGT-0016" + } + ] +} diff --git a/product-source/hololake-native-desktop/mobile/ios/.gitignore b/product-source/hololake-native-desktop/mobile/ios/.gitignore new file mode 100644 index 000000000..45706c522 --- /dev/null +++ b/product-source/hololake-native-desktop/mobile/ios/.gitignore @@ -0,0 +1,4 @@ +build/ +*.xcuserstate +xcuserdata/ + diff --git a/product-source/hololake-native-desktop/mobile/ios/ExportOptions.Debug.plist b/product-source/hololake-native-desktop/mobile/ios/ExportOptions.Debug.plist new file mode 100644 index 000000000..01dcb8305 --- /dev/null +++ b/product-source/hololake-native-desktop/mobile/ios/ExportOptions.Debug.plist @@ -0,0 +1,16 @@ + + + + + method + debugging + signingStyle + automatic + teamID + 825A9L3G7Q + stripSwiftSymbols + + uploadSymbols + + + diff --git a/product-source/hololake-native-desktop/mobile/ios/Generated/HoloLakeMobile-Info.plist b/product-source/hololake-native-desktop/mobile/ios/Generated/HoloLakeMobile-Info.plist new file mode 100644 index 000000000..d03325242 --- /dev/null +++ b/product-source/hololake-native-desktop/mobile/ios/Generated/HoloLakeMobile-Info.plist @@ -0,0 +1,56 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + HoloLake + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + com.guanghulab.hololake.pair + CFBundleURLSchemes + + hololake + + + + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSApplicationCategoryType + public.app-category.productivity + NSAppTransportSecurity + + NSAllowsLocalNetworking + + + NSLocalNetworkUsageDescription + HoloLake 仅在你明确配对后,通过同一局域网连接你的电脑端频道。 + UIApplicationSupportsIndirectInputEvents + + UILaunchScreen + + UIRequiresFullScreen + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + + diff --git a/product-source/hololake-native-desktop/mobile/ios/Generated/HoloLakeMobileTests-Info.plist b/product-source/hololake-native-desktop/mobile/ios/Generated/HoloLakeMobileTests-Info.plist new file mode 100644 index 000000000..6c40a6cd0 --- /dev/null +++ b/product-source/hololake-native-desktop/mobile/ios/Generated/HoloLakeMobileTests-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/product-source/hololake-native-desktop/mobile/ios/HoloLakeMobile.xcodeproj/project.pbxproj b/product-source/hololake-native-desktop/mobile/ios/HoloLakeMobile.xcodeproj/project.pbxproj new file mode 100644 index 000000000..b4fc2292f --- /dev/null +++ b/product-source/hololake-native-desktop/mobile/ios/HoloLakeMobile.xcodeproj/project.pbxproj @@ -0,0 +1,425 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 147588FBBE65B668142D5DBE /* HoloLakeMobileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBF45AEC03B7B5DE76DE7DA /* HoloLakeMobileTests.swift */; }; + 149652D6C6336752E39CEF9D /* HoloLakeKeychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75A889326BDEACBDB756B15A /* HoloLakeKeychain.swift */; }; + 4C8E5CF9FBFC541E3724C695 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5DF66BC64D4DCD3BF0583C4 /* ContentView.swift */; }; + 4FD7821997D8E90E6594FC23 /* HoloLakeMobileApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DCE2A5796B15DAC37985182 /* HoloLakeMobileApp.swift */; }; + 9BC7CC6D8BC355D731943912 /* HoloLakeDesign.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39A48AB789379757C66C6884 /* HoloLakeDesign.swift */; }; + C0E93C7456C0A3BE8BA595FC /* HoloLakeSyncClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = B91D61B90D609EF17397D193 /* HoloLakeSyncClient.swift */; }; + FFEEE7FC9ED4ECCD91A96AF1 /* HoloLakeModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 001F5647617271E7611365B1 /* HoloLakeModels.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + CD999C930B8B85FE1C3D884D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7BB68CAE65448CB9319F9FE1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 306917F839BF1A325E2C4CF4; + remoteInfo = HoloLakeMobile; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 001F5647617271E7611365B1 /* HoloLakeModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HoloLakeModels.swift; sourceTree = ""; }; + 39A48AB789379757C66C6884 /* HoloLakeDesign.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HoloLakeDesign.swift; sourceTree = ""; }; + 6DCE2A5796B15DAC37985182 /* HoloLakeMobileApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HoloLakeMobileApp.swift; sourceTree = ""; }; + 75A889326BDEACBDB756B15A /* HoloLakeKeychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HoloLakeKeychain.swift; sourceTree = ""; }; + 7BBF45AEC03B7B5DE76DE7DA /* HoloLakeMobileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HoloLakeMobileTests.swift; sourceTree = ""; }; + AAB8B15F447567F18DF8DA22 /* HoloLakeMobileTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = HoloLakeMobileTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + B91D61B90D609EF17397D193 /* HoloLakeSyncClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HoloLakeSyncClient.swift; sourceTree = ""; }; + D5DF66BC64D4DCD3BF0583C4 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + F674C2EA817D94D8089E46A6 /* HoloLakeMobile.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = HoloLakeMobile.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXGroup section */ + 14AB215583F1F3DFF867AD25 /* Sources */ = { + isa = PBXGroup; + children = ( + D5DF66BC64D4DCD3BF0583C4 /* ContentView.swift */, + 39A48AB789379757C66C6884 /* HoloLakeDesign.swift */, + 75A889326BDEACBDB756B15A /* HoloLakeKeychain.swift */, + 6DCE2A5796B15DAC37985182 /* HoloLakeMobileApp.swift */, + 001F5647617271E7611365B1 /* HoloLakeModels.swift */, + B91D61B90D609EF17397D193 /* HoloLakeSyncClient.swift */, + ); + path = Sources; + sourceTree = ""; + }; + 57FD470E035A4BE1AC23C789 = { + isa = PBXGroup; + children = ( + 14AB215583F1F3DFF867AD25 /* Sources */, + 65C6F102E3F59F8DEC7CF977 /* Tests */, + C1AA7BA4A57964238429B9FC /* Products */, + ); + sourceTree = ""; + }; + 65C6F102E3F59F8DEC7CF977 /* Tests */ = { + isa = PBXGroup; + children = ( + 7BBF45AEC03B7B5DE76DE7DA /* HoloLakeMobileTests.swift */, + ); + path = Tests; + sourceTree = ""; + }; + C1AA7BA4A57964238429B9FC /* Products */ = { + isa = PBXGroup; + children = ( + F674C2EA817D94D8089E46A6 /* HoloLakeMobile.app */, + AAB8B15F447567F18DF8DA22 /* HoloLakeMobileTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 306917F839BF1A325E2C4CF4 /* HoloLakeMobile */ = { + isa = PBXNativeTarget; + buildConfigurationList = 821BFCD605046D5BD179550C /* Build configuration list for PBXNativeTarget "HoloLakeMobile" */; + buildPhases = ( + EF1823B21F5FD988A9F1C68B /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = HoloLakeMobile; + packageProductDependencies = ( + ); + productName = HoloLakeMobile; + productReference = F674C2EA817D94D8089E46A6 /* HoloLakeMobile.app */; + productType = "com.apple.product-type.application"; + }; + 9F8DF7CBFC2D6315E6807301 /* HoloLakeMobileTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 94843BB695F4BFC5D7BD814C /* Build configuration list for PBXNativeTarget "HoloLakeMobileTests" */; + buildPhases = ( + C9F5955F3342913B35B01034 /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + 43C074DF3E4135062FD9F2D5 /* PBXTargetDependency */, + ); + name = HoloLakeMobileTests; + packageProductDependencies = ( + ); + productName = HoloLakeMobileTests; + productReference = AAB8B15F447567F18DF8DA22 /* HoloLakeMobileTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7BB68CAE65448CB9319F9FE1 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1430; + TargetAttributes = { + 306917F839BF1A325E2C4CF4 = { + DevelopmentTeam = 825A9L3G7Q; + ProvisioningStyle = Automatic; + }; + 9F8DF7CBFC2D6315E6807301 = { + DevelopmentTeam = 825A9L3G7Q; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 9D0C9B96A22462DAABC1E8F8 /* Build configuration list for PBXProject "HoloLakeMobile" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + Base, + en, + ); + mainGroup = 57FD470E035A4BE1AC23C789; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = C1AA7BA4A57964238429B9FC /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 306917F839BF1A325E2C4CF4 /* HoloLakeMobile */, + 9F8DF7CBFC2D6315E6807301 /* HoloLakeMobileTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + C9F5955F3342913B35B01034 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 147588FBBE65B668142D5DBE /* HoloLakeMobileTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EF1823B21F5FD988A9F1C68B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C8E5CF9FBFC541E3724C695 /* ContentView.swift in Sources */, + 9BC7CC6D8BC355D731943912 /* HoloLakeDesign.swift in Sources */, + 149652D6C6336752E39CEF9D /* HoloLakeKeychain.swift in Sources */, + 4FD7821997D8E90E6594FC23 /* HoloLakeMobileApp.swift in Sources */, + FFEEE7FC9ED4ECCD91A96AF1 /* HoloLakeModels.swift in Sources */, + C0E93C7456C0A3BE8BA595FC /* HoloLakeSyncClient.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 43C074DF3E4135062FD9F2D5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 306917F839BF1A325E2C4CF4 /* HoloLakeMobile */; + targetProxy = CD999C930B8B85FE1C3D884D /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 41EDE5121626CC1CCE382706 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 825A9L3G7Q; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + MARKETING_VERSION = 0.5.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SUPPORTS_MACCATALYST = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Release; + }; + 4F5C6B6543A0A3082363F109 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 825A9L3G7Q; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DEBUG=1", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + MARKETING_VERSION = 0.5.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SUPPORTS_MACCATALYST = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + 512CB348BCA8C1688C5D794E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = "Generated/HoloLakeMobileTests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.guanghulab.hololake.tests; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HoloLake.app/HoloLake"; + }; + name = Release; + }; + A44510E6B199DFF6BD5BC1EF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = "Generated/HoloLakeMobileTests-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.guanghulab.hololake.tests; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HoloLake.app/HoloLake"; + }; + name = Debug; + }; + B82D688E4449B0C6DC27D25E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + INFOPLIST_FILE = "Generated/HoloLakeMobile-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.guanghulab.hololake; + PRODUCT_MODULE_NAME = HoloLakeMobile; + PRODUCT_NAME = HoloLake; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + BECD79B877979A2CD86E7D9C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + INFOPLIST_FILE = "Generated/HoloLakeMobile-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.guanghulab.hololake; + PRODUCT_MODULE_NAME = HoloLakeMobile; + PRODUCT_NAME = HoloLake; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 821BFCD605046D5BD179550C /* Build configuration list for PBXNativeTarget "HoloLakeMobile" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BECD79B877979A2CD86E7D9C /* Debug */, + B82D688E4449B0C6DC27D25E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; + 94843BB695F4BFC5D7BD814C /* Build configuration list for PBXNativeTarget "HoloLakeMobileTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A44510E6B199DFF6BD5BC1EF /* Debug */, + 512CB348BCA8C1688C5D794E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; + 9D0C9B96A22462DAABC1E8F8 /* Build configuration list for PBXProject "HoloLakeMobile" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4F5C6B6543A0A3082363F109 /* Debug */, + 41EDE5121626CC1CCE382706 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7BB68CAE65448CB9319F9FE1 /* Project object */; +} diff --git a/product-source/hololake-native-desktop/mobile/ios/HoloLakeMobile.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/product-source/hololake-native-desktop/mobile/ios/HoloLakeMobile.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/product-source/hololake-native-desktop/mobile/ios/HoloLakeMobile.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/product-source/hololake-native-desktop/mobile/ios/HoloLakeMobile.xcodeproj/xcshareddata/xcschemes/HoloLakeMobile.xcscheme b/product-source/hololake-native-desktop/mobile/ios/HoloLakeMobile.xcodeproj/xcshareddata/xcschemes/HoloLakeMobile.xcscheme new file mode 100644 index 000000000..3a7b98f9c --- /dev/null +++ b/product-source/hololake-native-desktop/mobile/ios/HoloLakeMobile.xcodeproj/xcshareddata/xcschemes/HoloLakeMobile.xcscheme @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/product-source/hololake-native-desktop/mobile/ios/Sources/ContentView.swift b/product-source/hololake-native-desktop/mobile/ios/Sources/ContentView.swift new file mode 100644 index 000000000..008ee0913 --- /dev/null +++ b/product-source/hololake-native-desktop/mobile/ios/Sources/ContentView.swift @@ -0,0 +1,203 @@ +import SwiftUI +import UIKit + +struct ContentView: View { + @EnvironmentObject private var client: HoloLakeSyncClient + @State private var pairingText = "" + @State private var captureTitle = "" + @State private var captureBody = "" + + var body: some View { + NavigationStack { + ZStack { + LinearGradient( + colors: [HoloLakeDesign.deep, HoloLakeDesign.lake, HoloLakeDesign.violet.opacity(0.78)], + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + .ignoresSafeArea() + + ScrollView { + VStack(spacing: 16) { + header + if client.session == nil { pairingCard } else { dashboard } + boundaryCard + } + .padding(.horizontal, 18) + .padding(.vertical, 16) + } + } + .toolbar(.hidden, for: .navigationBar) + .alert("HoloLake", isPresented: Binding( + get: { client.errorText != nil }, + set: { if !$0 { client.errorText = nil } } + )) { + Button("知道了", role: .cancel) { client.errorText = nil } + } message: { + Text(client.errorText ?? "") + } + } + .preferredColorScheme(.dark) + } + + private var header: some View { + HStack(alignment: .center) { + VStack(alignment: .leading, spacing: 5) { + Text("HoloLake").font(.title.weight(.semibold)).tracking(2) + Text("同一频道 · iPhone 轻入口") + .font(.subheadline) + .foregroundStyle(HoloLakeDesign.muted) + } + Spacer() + Circle() + .fill(client.session == nil ? HoloLakeDesign.gold : HoloLakeDesign.mint) + .frame(width: 11, height: 11) + .shadow(color: client.session == nil ? HoloLakeDesign.gold : HoloLakeDesign.mint, radius: 8) + } + .foregroundStyle(HoloLakeDesign.pearl) + .padding(.top, 4) + } + + private var pairingCard: some View { + LakeCard { + VStack(alignment: .leading, spacing: 14) { + Text("连接电脑端").font(.title3.weight(.semibold)) + Text("先在电脑端打开“多端同步”,再扫描配对码或粘贴配对链接。配对密钥只保存在这台 iPhone 的钥匙串。") + .font(.subheadline) + .foregroundStyle(HoloLakeDesign.muted) + + TextField("hololake://pair?…", text: $pairingText, axis: .vertical) + .textInputAutocapitalization(.never) + .autocorrectionDisabled() + .padding(14) + .background(.black.opacity(0.22), in: RoundedRectangle(cornerRadius: 16)) + + HStack { + Button("从剪贴板读取") { + pairingText = UIPasteboard.general.string ?? "" + } + .buttonStyle(.bordered) + + Button("校验并配对") { + Task { await client.pair(from: pairingText) } + } + .buttonStyle(.borderedProminent) + .tint(HoloLakeDesign.gold) + .foregroundStyle(HoloLakeDesign.deep) + .disabled(pairingText.isEmpty || client.isWorking) + } + } + } + .foregroundStyle(HoloLakeDesign.pearl) + } + + private var dashboard: some View { + VStack(spacing: 16) { + LakeCard { + VStack(alignment: .leading, spacing: 14) { + HStack { + VStack(alignment: .leading, spacing: 4) { + Text(client.session?.desktopName ?? "电脑端") + .font(.title3.weight(.semibold)) + Text(client.stateText) + .font(.caption) + .foregroundStyle(HoloLakeDesign.muted) + } + Spacer() + Button { + Task { await client.sync() } + } label: { + if client.isWorking { + ProgressView() + } else { + Label("同步", systemImage: "arrow.triangle.2.circlepath") + } + } + .buttonStyle(.borderedProminent) + .tint(HoloLakeDesign.mint) + .foregroundStyle(HoloLakeDesign.deep) + .disabled(client.isWorking) + } + + if let snapshot = client.snapshot { + HStack(spacing: 10) { + MetricTile(label: "作品", value: "\(snapshot.webNovel.workCount)") + MetricTile(label: "章节", value: "\(snapshot.webNovel.chapterCount)") + MetricTile(label: "教育表", value: "\(snapshot.education.activeTableCount)") + } + } else { + Text("保持静止;只有你点击同步时才会连接电脑。") + .font(.subheadline) + .foregroundStyle(HoloLakeDesign.muted) + } + } + } + + LakeCard { + VStack(alignment: .leading, spacing: 12) { + Text("随手记回频道").font(.headline) + TextField("标题(可选)", text: $captureTitle) + .padding(12) + .background(.black.opacity(0.2), in: RoundedRectangle(cornerRadius: 14)) + TextField("正文", text: $captureBody, axis: .vertical) + .lineLimit(4...10) + .padding(12) + .background(.black.opacity(0.2), in: RoundedRectangle(cornerRadius: 14)) + Button("写入电脑端收件箱") { + let title = captureTitle + let body = captureBody + Task { + await client.sync(captureTitle: title, captureBody: body) + if client.errorText == nil { + captureTitle = "" + captureBody = "" + } + } + } + .buttonStyle(.bordered) + .disabled(captureBody.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || client.isWorking) + } + } + + if let works = client.snapshot?.webNovel.works, !works.isEmpty { + LakeCard { + VStack(alignment: .leading, spacing: 12) { + Text("最近作品").font(.headline) + ForEach(works.prefix(5)) { work in + HStack { + Text(work.title).lineLimit(1) + Spacer() + Text(work.status) + .font(.caption) + .foregroundStyle(HoloLakeDesign.muted) + } + if work.id != works.prefix(5).last?.id { + Divider().overlay(.white.opacity(0.12)) + } + } + } + } + } + + Button("解除本机配对", role: .destructive) { + client.disconnect() + } + .font(.footnote) + } + .foregroundStyle(HoloLakeDesign.pearl) + } + + private var boundaryCard: some View { + VStack(alignment: .leading, spacing: 6) { + Text("HLP-IOS-CLIENT-001") + .font(.caption2.monospaced()) + .foregroundStyle(HoloLakeDesign.gold) + Text("手机是同一频道的远程身体入口,不复制电脑桌面,不在电脑离线时执行人格或行业任务。") + .font(.caption) + .foregroundStyle(HoloLakeDesign.muted) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 4) + } +} + diff --git a/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeDesign.swift b/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeDesign.swift new file mode 100644 index 000000000..797b0283a --- /dev/null +++ b/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeDesign.swift @@ -0,0 +1,42 @@ +import SwiftUI + +enum HoloLakeDesign { + static let deep = Color(red: 0.025, green: 0.045, blue: 0.11) + static let lake = Color(red: 0.08, green: 0.14, blue: 0.32) + static let violet = Color(red: 0.34, green: 0.19, blue: 0.52) + static let pearl = Color(red: 0.95, green: 0.96, blue: 1) + static let muted = Color(red: 0.62, green: 0.66, blue: 0.78) + static let gold = Color(red: 0.96, green: 0.87, blue: 0.62) + static let mint = Color(red: 0.43, green: 0.93, blue: 0.78) +} + +struct LakeCard: View { + @ViewBuilder let content: Content + + var body: some View { + content + .padding(18) + .frame(maxWidth: .infinity, alignment: .leading) + .background(.white.opacity(0.075), in: RoundedRectangle(cornerRadius: 24)) + .overlay { + RoundedRectangle(cornerRadius: 24) + .stroke(.white.opacity(0.14), lineWidth: 1) + } + } +} + +struct MetricTile: View { + let label: String + let value: String + + var body: some View { + VStack(alignment: .leading, spacing: 6) { + Text(label).font(.caption).foregroundStyle(HoloLakeDesign.muted) + Text(value).font(.title2.weight(.semibold)).foregroundStyle(HoloLakeDesign.pearl) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(14) + .background(.white.opacity(0.055), in: RoundedRectangle(cornerRadius: 18)) + } +} + diff --git a/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeKeychain.swift b/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeKeychain.swift new file mode 100644 index 000000000..345974f2c --- /dev/null +++ b/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeKeychain.swift @@ -0,0 +1,59 @@ +import Foundation +import Security + +enum HoloLakeKeychain { + private static let service = "com.guanghulab.hololake.mobile-sync" + private static let account = "HLP-IOS-CLIENT-001" + + static func load() throws -> StoredSession? { + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: account, + kSecReturnData as String: true, + kSecMatchLimit as String: kSecMatchLimitOne + ] + var result: CFTypeRef? + let status = SecItemCopyMatching(query as CFDictionary, &result) + if status == errSecItemNotFound { return nil } + guard status == errSecSuccess, let data = result as? Data else { + throw HoloLakeMobileError.keychain(status) + } + return try JSONDecoder().decode(StoredSession.self, from: data) + } + + static func save(_ session: StoredSession) throws { + let data = try JSONEncoder().encode(session) + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: account + ] + let attributes: [String: Any] = [ + kSecValueData as String: data, + kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly + ] + let update = SecItemUpdate(query as CFDictionary, attributes as CFDictionary) + if update == errSecItemNotFound { + var insertion = query + insertion.merge(attributes) { _, new in new } + let status = SecItemAdd(insertion as CFDictionary, nil) + guard status == errSecSuccess else { throw HoloLakeMobileError.keychain(status) } + } else if update != errSecSuccess { + throw HoloLakeMobileError.keychain(update) + } + } + + static func remove() throws { + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: account + ] + let status = SecItemDelete(query as CFDictionary) + guard status == errSecSuccess || status == errSecItemNotFound else { + throw HoloLakeMobileError.keychain(status) + } + } +} + diff --git a/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeMobileApp.swift b/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeMobileApp.swift new file mode 100644 index 000000000..181084959 --- /dev/null +++ b/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeMobileApp.swift @@ -0,0 +1,17 @@ +import SwiftUI + +@main +struct HoloLakeMobileApp: App { + @StateObject private var client = HoloLakeSyncClient() + + var body: some Scene { + WindowGroup { + ContentView() + .environmentObject(client) + .onOpenURL { url in + Task { await client.pair(with: url) } + } + } + } +} + diff --git a/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeModels.swift b/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeModels.swift new file mode 100644 index 000000000..ab00154e1 --- /dev/null +++ b/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeModels.swift @@ -0,0 +1,177 @@ +import Foundation +import Security + +struct PairingDescriptor: Equatable { + let host: String + let port: Int + let pairingID: String + let secret: Data + + init(url: URL) throws { + guard url.scheme == "hololake", url.host == "pair", + let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { + throw HoloLakeMobileError.invalidPairingLink + } + var values: [String: String] = [:] + for item in components.queryItems ?? [] { + guard let value = item.value, values[item.name] == nil else { + throw HoloLakeMobileError.invalidPairingLink + } + values[item.name] = value + } + guard let host = values["host"], !host.isEmpty, + let portText = values["port"], let port = Int(portText), (1...65535).contains(port), + let pairingID = values["id"], UUID(uuidString: pairingID) != nil, + let secretText = values["secret"], + let secret = Data(base64URL: secretText), secret.count == 32 else { + throw HoloLakeMobileError.invalidPairingLink + } + self.host = host + self.port = port + self.pairingID = pairingID + self.secret = secret + } + + var baseURL: URL { + URL(string: "http://\(host):\(port)")! + } +} + +struct StoredSession: Codable, Equatable { + let host: String + let port: Int + let deviceID: String + let sessionKey: Data + let desktopName: String + var counter: UInt64 + var cursor: UInt64 + + var baseURL: URL { + URL(string: "http://\(host):\(port)")! + } +} + +struct PairRequest: Codable { + let deviceName: String + let platform: String + let requestID: String +} + +struct PairResponse: Codable { + let schema: String + let state: String + let deviceID: String + let sessionKey: String + let desktopName: String + let rootNodeRole: String + let requestID: String +} + +struct SyncRequest: Codable { + let counter: UInt64 + let afterCursor: UInt64? + let capture: MobileCaptureInput? +} + +struct MobileCaptureInput: Codable { + let title: String + let body: String + let requestID: String +} + +struct MobileSyncSnapshot: Codable { + let schema: String + let state: String + let cursor: UInt64 + let generatedAtUnixMs: UInt64 + let desktopName: String + let rootNodeOnline: Bool + let personalChannel: MobileChannelProjection + let webNovel: MobileWebNovelProjection + let education: MobileEducationProjection + let recentCaptures: [MobileCaptureProjection] + let boundary: MobileBoundaryProjection +} + +struct MobileChannelProjection: Codable { + let home: String + let growthEventCount: UInt64 + let integrity: String +} + +struct MobileWebNovelProjection: Codable { + let workCount: UInt64 + let volumeCount: UInt64 + let chapterCount: UInt64 + let works: [MobileWebNovelWork] +} + +struct MobileWebNovelWork: Codable, Identifiable { + let workID: String + let title: String + let status: String + let updatedAtUnixMs: UInt64 + var id: String { workID } +} + +struct MobileEducationProjection: Codable { + let activeTableCount: UInt64 + let archivedTableCount: UInt64 + let unassignedTableCount: UInt64 + let sensitiveValuesIncluded: Bool +} + +struct MobileCaptureProjection: Codable, Identifiable { + let cursor: UInt64 + let captureID: String + let title: String + let body: String + let sourceDeviceID: String + let createdAtUnixMs: UInt64 + var id: String { captureID } +} + +struct MobileBoundaryProjection: Codable { + let mobileRole: String + let remoteDesktopClone: Bool + let desktopOfflineExecution: Bool + let sensitiveEducationValues: String + let modelAPI: String +} + +enum HoloLakeMobileError: LocalizedError { + case invalidPairingLink + case invalidResponse + case rejected(String) + case missingNonce + case missingSession + case keychain(OSStatus) + + var errorDescription: String? { + switch self { + case .invalidPairingLink: return "配对链接无效或已损坏。" + case .invalidResponse: return "电脑端返回了无法校验的响应。" + case .rejected(let code): return "电脑端拒绝了本次请求:\(code)" + case .missingNonce: return "加密响应缺少一次性随机数。" + case .missingSession: return "请先与电脑端 HoloLake 配对。" + case .keychain(let status): return "本机钥匙串写入失败(\(status))。" + } + } +} + +extension Data { + init?(base64URL value: String) { + var text = value.replacingOccurrences(of: "-", with: "+") + .replacingOccurrences(of: "_", with: "/") + text += String(repeating: "=", count: (4 - text.count % 4) % 4) + guard let data = Data(base64Encoded: text) else { return nil } + self = data + } + + var base64URL: String { + base64EncodedString() + .replacingOccurrences(of: "+", with: "-") + .replacingOccurrences(of: "/", with: "_") + .replacingOccurrences(of: "=", with: "") + } +} diff --git a/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeSyncClient.swift b/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeSyncClient.swift new file mode 100644 index 000000000..ffc99f63e --- /dev/null +++ b/product-source/hololake-native-desktop/mobile/ios/Sources/HoloLakeSyncClient.swift @@ -0,0 +1,190 @@ +import CryptoKit +import Foundation +import UIKit + +@MainActor +final class HoloLakeSyncClient: ObservableObject { + static let clientNumber = "HLP-IOS-CLIENT-001" + static let pairAAD = Data("hololake.mobile.pair/v1".utf8) + + @Published private(set) var session: StoredSession? + @Published private(set) var snapshot: MobileSyncSnapshot? + @Published private(set) var stateText = "尚未配对" + @Published private(set) var isWorking = false + @Published var errorText: String? + + init() { + do { + session = try HoloLakeKeychain.load() + stateText = session == nil ? "尚未配对" : "已配对,等待手动同步" + } catch { + errorText = error.localizedDescription + } + } + + func pair(with url: URL) async { + await perform("正在校验配对…") { + let descriptor = try PairingDescriptor(url: url) + let requestID = UUID().uuidString.lowercased() + let payload = PairRequest( + deviceName: UIDevice.current.name, + platform: "IOS", + requestID: requestID + ) + let responseData = try await Self.exchange( + url: descriptor.baseURL.appending(path: "v1/pair"), + key: descriptor.secret, + aad: Self.pairAAD, + headers: ["X-HoloLake-Pairing-ID": descriptor.pairingID], + payload: payload + ) + let response = try JSONDecoder().decode(PairResponse.self, from: responseData) + guard response.schema == "hololake.mobile-sync/v1", + response.state == "PAIRED", + response.rootNodeRole == "USER_LOCAL_COMPUTER_TERMINAL", + response.requestID == requestID, + let sessionKey = Data(base64URL: response.sessionKey), sessionKey.count == 32 else { + throw HoloLakeMobileError.invalidResponse + } + let next = StoredSession( + host: descriptor.host, + port: descriptor.port, + deviceID: response.deviceID, + sessionKey: sessionKey, + desktopName: response.desktopName, + counter: 0, + cursor: 0 + ) + try HoloLakeKeychain.save(next) + session = next + stateText = "已与 \(response.desktopName) 配对" + } + } + + func pair(from text: String) async { + guard let url = URL(string: text.trimmingCharacters(in: .whitespacesAndNewlines)) else { + errorText = HoloLakeMobileError.invalidPairingLink.localizedDescription + return + } + await pair(with: url) + } + + func sync(captureTitle: String? = nil, captureBody: String? = nil) async { + await perform("正在与电脑同步…") { + guard var current = session else { throw HoloLakeMobileError.missingSession } + current.counter += 1 + // Consume and persist the counter before transport. If the root node accepts a + // request but its response is lost, the next manual retry still moves forward. + try HoloLakeKeychain.save(current) + session = current + let capture: MobileCaptureInput? + if let body = captureBody?.trimmingCharacters(in: .whitespacesAndNewlines), !body.isEmpty { + capture = MobileCaptureInput( + title: (captureTitle ?? "").trimmingCharacters(in: .whitespacesAndNewlines), + body: body, + requestID: UUID().uuidString.lowercased() + ) + } else { + capture = nil + } + let aad = Data("hololake.mobile.sync/v1:\(current.deviceID)".utf8) + let responseData = try await Self.exchange( + url: current.baseURL.appending(path: "v1/sync"), + key: current.sessionKey, + aad: aad, + headers: ["X-HoloLake-Device-ID": current.deviceID], + payload: SyncRequest(counter: current.counter, afterCursor: nil, capture: capture) + ) + let nextSnapshot = try JSONDecoder().decode(MobileSyncSnapshot.self, from: responseData) + guard nextSnapshot.schema == "hololake.mobile-sync/v1", + nextSnapshot.state == "SYNCED_WITH_ROOT_NODE", + nextSnapshot.rootNodeOnline, + nextSnapshot.boundary.mobileRole == "REMOTE_BODY_ENTRY_OF_THE_SAME_PERSONA_SYSTEM", + !nextSnapshot.boundary.remoteDesktopClone, + !nextSnapshot.boundary.desktopOfflineExecution else { + throw HoloLakeMobileError.invalidResponse + } + current.cursor = nextSnapshot.cursor + try HoloLakeKeychain.save(current) + session = current + snapshot = nextSnapshot + stateText = "已同步 · \(nextSnapshot.desktopName)" + } + } + + func disconnect() { + do { + try HoloLakeKeychain.remove() + session = nil + snapshot = nil + stateText = "尚未配对" + } catch { + errorText = error.localizedDescription + } + } + + private func perform(_ activeText: String, operation: () async throws -> Void) async { + guard !isWorking else { return } + isWorking = true + errorText = nil + stateText = activeText + do { + try await operation() + } catch { + errorText = error.localizedDescription + stateText = session == nil ? "尚未配对" : "同步未完成" + } + isWorking = false + } + + static func exchange( + url: URL, + key: Data, + aad: Data, + headers: [String: String], + payload: Payload, + session: URLSession = .shared + ) async throws -> Data { + let clear = try JSONEncoder().encode(payload) + let nonceData = randomNonce() + let nonce = try ChaChaPoly.Nonce(data: nonceData) + let sealed = try ChaChaPoly.seal( + clear, + using: SymmetricKey(data: key), + nonce: nonce, + authenticating: aad + ) + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.timeoutInterval = 8 + request.cachePolicy = .reloadIgnoringLocalCacheData + request.httpBody = sealed.ciphertext + sealed.tag + request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type") + request.setValue(nonceData.base64URL, forHTTPHeaderField: "X-HoloLake-Nonce") + headers.forEach { request.setValue($1, forHTTPHeaderField: $0) } + let (body, response) = try await session.data(for: request) + guard let http = response as? HTTPURLResponse else { + throw HoloLakeMobileError.invalidResponse + } + guard http.statusCode == 200 else { + let code = (try? JSONSerialization.jsonObject(with: body) as? [String: Any])?["code"] as? String + throw HoloLakeMobileError.rejected(code ?? "HTTP_\(http.statusCode)") + } + guard let nonceHeader = http.value(forHTTPHeaderField: "X-HoloLake-Nonce"), + let responseNonceData = Data(base64URL: nonceHeader), responseNonceData.count == 12 else { + throw HoloLakeMobileError.missingNonce + } + let responseNonce = try ChaChaPoly.Nonce(data: responseNonceData) + guard body.count >= 16 else { throw HoloLakeMobileError.invalidResponse } + let responseBox = try ChaChaPoly.SealedBox( + nonce: responseNonce, + ciphertext: body.dropLast(16), + tag: body.suffix(16) + ) + return try ChaChaPoly.open(responseBox, using: SymmetricKey(data: key), authenticating: aad) + } + + static func randomNonce() -> Data { + Data((0..<12).map { _ in UInt8.random(in: .min ... .max) }) + } +} diff --git a/product-source/hololake-native-desktop/mobile/ios/Tests/HoloLakeMobileTests.swift b/product-source/hololake-native-desktop/mobile/ios/Tests/HoloLakeMobileTests.swift new file mode 100644 index 000000000..eae55ba7c --- /dev/null +++ b/product-source/hololake-native-desktop/mobile/ios/Tests/HoloLakeMobileTests.swift @@ -0,0 +1,44 @@ +import CryptoKit +import XCTest +@testable import HoloLakeMobile + +final class HoloLakeMobileTests: XCTestCase { + func testPairingCoordinateParsesOnlyCompleteLink() throws { + let secret = Data(repeating: 7, count: 32).base64URL + let id = UUID().uuidString.lowercased() + let url = try XCTUnwrap(URL(string: "hololake://pair?host=192.168.1.8&port=37421&id=\(id)&secret=\(secret)")) + let value = try PairingDescriptor(url: url) + XCTAssertEqual(value.host, "192.168.1.8") + XCTAssertEqual(value.port, 37421) + XCTAssertEqual(value.pairingID, id) + XCTAssertEqual(value.secret, Data(repeating: 7, count: 32)) + } + + func testPairingCoordinateRejectsMissingSecret() throws { + let url = try XCTUnwrap(URL(string: "hololake://pair?host=127.0.0.1&port=37421&id=\(UUID().uuidString)")) + XCTAssertThrowsError(try PairingDescriptor(url: url)) + } + + func testPairingCoordinateRejectsDuplicateKeys() throws { + let secret = Data(repeating: 7, count: 32).base64URL + let id = UUID().uuidString.lowercased() + let url = try XCTUnwrap(URL(string: "hololake://pair?host=192.168.1.8&host=127.0.0.1&port=37421&id=\(id)&secret=\(secret)")) + XCTAssertThrowsError(try PairingDescriptor(url: url)) + } + + func testCryptoKitPayloadMatchesServerWireShape() throws { + let key = SymmetricKey(data: Data(repeating: 3, count: 32)) + let nonce = try ChaChaPoly.Nonce(data: Data(repeating: 9, count: 12)) + let aad = Data("hololake.mobile.pair/v1".utf8) + let clear = Data("hello".utf8) + let sealed = try ChaChaPoly.seal(clear, using: key, nonce: nonce, authenticating: aad) + let wire = sealed.ciphertext + sealed.tag + XCTAssertEqual(wire.count, clear.count + 16) + let restored = try ChaChaPoly.open( + ChaChaPoly.SealedBox(nonce: nonce, ciphertext: wire.dropLast(16), tag: wire.suffix(16)), + using: key, + authenticating: aad + ) + XCTAssertEqual(restored, clear) + } +} diff --git a/product-source/hololake-native-desktop/mobile/ios/project.yml b/product-source/hololake-native-desktop/mobile/ios/project.yml new file mode 100644 index 000000000..b733e4c91 --- /dev/null +++ b/product-source/hololake-native-desktop/mobile/ios/project.yml @@ -0,0 +1,63 @@ +name: HoloLakeMobile +options: + bundleIdPrefix: com.guanghulab + deploymentTarget: + iOS: "17.0" +settings: + base: + DEVELOPMENT_TEAM: 825A9L3G7Q + CODE_SIGN_STYLE: Automatic + SWIFT_VERSION: "5.0" + CURRENT_PROJECT_VERSION: 1 + MARKETING_VERSION: 0.5.0 + TARGETED_DEVICE_FAMILY: "1" + SUPPORTS_MACCATALYST: false +targets: + HoloLakeMobile: + type: application + platform: iOS + sources: + - path: Sources + settings: + base: + PRODUCT_BUNDLE_IDENTIFIER: com.guanghulab.hololake + PRODUCT_NAME: HoloLake + PRODUCT_MODULE_NAME: HoloLakeMobile + info: + path: Generated/HoloLakeMobile-Info.plist + properties: + CFBundleDisplayName: HoloLake + CFBundleShortVersionString: "$(MARKETING_VERSION)" + CFBundleVersion: "$(CURRENT_PROJECT_VERSION)" + LSApplicationCategoryType: public.app-category.productivity + NSLocalNetworkUsageDescription: HoloLake 仅在你明确配对后,通过同一局域网连接你的电脑端频道。 + UIApplicationSupportsIndirectInputEvents: true + UIRequiresFullScreen: true + UILaunchScreen: {} + UISupportedInterfaceOrientations: + - UIInterfaceOrientationPortrait + CFBundleURLTypes: + - CFBundleTypeRole: Editor + CFBundleURLName: com.guanghulab.hololake.pair + CFBundleURLSchemes: + - hololake + NSAppTransportSecurity: + NSAllowsLocalNetworking: true + scheme: + testTargets: + - HoloLakeMobileTests + HoloLakeMobileTests: + type: bundle.unit-test + platform: iOS + sources: + - path: Tests + dependencies: + - target: HoloLakeMobile + settings: + base: + PRODUCT_BUNDLE_IDENTIFIER: com.guanghulab.hololake.tests + TEST_HOST: "$(BUILT_PRODUCTS_DIR)/HoloLake.app/HoloLake" + BUNDLE_LOADER: "$(TEST_HOST)" + info: + path: Generated/HoloLakeMobileTests-Info.plist + properties: {} diff --git a/product-source/hololake-native-desktop/package-lock.json b/product-source/hololake-native-desktop/package-lock.json index f527ec24f..17cf42c6f 100644 --- a/product-source/hololake-native-desktop/package-lock.json +++ b/product-source/hololake-native-desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "hololake-native-desktop", - "version": "0.4.1", + "version": "0.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hololake-native-desktop", - "version": "0.4.1", + "version": "0.5.0", "dependencies": { "@fortune-sheet/react": "1.0.4", "@lexical/history": "0.49.0", @@ -16,7 +16,6 @@ "@lexical/rich-text": "0.49.0", "@lexical/selection": "0.49.0", "@lexical/utils": "0.49.0", - "@tanstack/react-table": "8.21.3", "@tauri-apps/api": "2.10.1", "@tauri-apps/plugin-process": "2.3.1", "@tauri-apps/plugin-updater": "2.10.0", @@ -1793,39 +1792,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@tanstack/react-table": { - "version": "8.21.3", - "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz", - "integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==", - "license": "MIT", - "dependencies": { - "@tanstack/table-core": "8.21.3" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, - "node_modules/@tanstack/table-core": { - "version": "8.21.3", - "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", - "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, "node_modules/@tauri-apps/api": { "version": "2.10.1", "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.10.1.tgz", diff --git a/product-source/hololake-native-desktop/package.json b/product-source/hololake-native-desktop/package.json index a6f5a54f4..91bd222cd 100644 --- a/product-source/hololake-native-desktop/package.json +++ b/product-source/hololake-native-desktop/package.json @@ -1,7 +1,7 @@ { "name": "hololake-native-desktop", "private": true, - "version": "0.4.1", + "version": "0.5.0", "type": "module", "scripts": { "dev": "vite", @@ -19,7 +19,6 @@ "@lexical/rich-text": "0.49.0", "@lexical/selection": "0.49.0", "@lexical/utils": "0.49.0", - "@tanstack/react-table": "8.21.3", "@tauri-apps/api": "2.10.1", "@tauri-apps/plugin-process": "2.3.1", "@tauri-apps/plugin-updater": "2.10.0", diff --git a/product-source/hololake-native-desktop/scripts/authenticated-main-world-navigation.test.mjs b/product-source/hololake-native-desktop/scripts/authenticated-main-world-navigation.test.mjs deleted file mode 100644 index 162dbe6c0..000000000 --- a/product-source/hololake-native-desktop/scripts/authenticated-main-world-navigation.test.mjs +++ /dev/null @@ -1,26 +0,0 @@ -import assert from 'node:assert/strict' -import fs from 'node:fs' -import test from 'node:test' - -const frontend = fs.readFileSync(new URL('../src/main.tsx', import.meta.url), 'utf8') -const architecture = JSON.parse( - fs.readFileSync(new URL('../../../routing/hololake-current-architecture.json', import.meta.url), 'utf8'), -) - -test('returning to the main world preserves the authenticated session', () => { - const handler = frontend.match(/const returnToMainWorld = \(\) => \{([\s\S]*?)\n \}/)?.[1] || '' - - assert.match(handler, /setShowMainWorld\(true\)/) - assert.doesNotMatch(handler, /signOutRepo|setRepoLogin\(null\)|sign_out_code_repo_login/) - assert.match(frontend, /if \(!repoLogin \|\| showMainWorld\)/) -}) - -test('main-world return and account sign-out remain separate visible actions', () => { - assert.match(frontend, />逛逛语言世界返回我的频道退出账号 readFileSync(new URL(`../${path}`, import.meta.url), 'utf8') +const contract = JSON.parse(read('contracts/channel-workbench-runtime.json')) +const registry = JSON.parse(read('contracts/numbered-ipc-registry.json')) +const modulePackage = JSON.parse(read('fixtures/module-packages/HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001-0.1.0.ghmod')) +const rust = read('src-tauri/src/channel_workbench.rs') +const frontend = [read('src/modules/channel-workbench/index.tsx'), read('src/modules/channel-workbench/document-engine.tsx'), read('src/modules/channel-workbench/spreadsheet-engine.tsx')].join('\n') + +test('channel workbench is a signed account-local adapter with durable user data', () => { + assert.equal(contract.candidate_number, 'HLP-DONOR-CAND-0002') + assert.equal(contract.runtime_module_number, 'HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001') + assert.equal(contract.data_boundary.scope, 'CURRENT_AUTHENTICATED_ACCOUNT_LOCAL') + assert.equal(contract.data_boundary.package_unmount_deletes_user_data, false) + assert.equal(modulePackage.manifest.adapter, 'channel-workbench-v1') + assert.deepEqual(modulePackage.manifest.permissions, ['CHANNEL_DOCUMENT_READ', 'CHANNEL_DOCUMENT_WRITE', 'CHANNEL_SPREADSHEET_READ', 'CHANNEL_SPREADSHEET_WRITE']) + assert.match(rust, /require_active_module_adapter/) + assert.match(rust, /workbench_receipts_no_update/) + assert.doesNotMatch(rust, /#\[tauri::command\]/) + assert.doesNotMatch(frontend, /from ['"]@tauri-apps\/api\/core['"]/) +}) + +test('document and spreadsheet cross only their exact numbered routes', () => { + const routes = registry.operations.filter((route) => route.module_number === 'HLP-NIPC-MOD-0022') + assert.deepEqual(routes.map((route) => route.operation_number), ['HLP-NIPC-OP-0074', 'HLP-NIPC-OP-0075', 'HLP-NIPC-OP-0076']) + assert.deepEqual(routes.map((route) => route.alias), ['get_channel_workbench_snapshot', 'save_channel_document', 'save_channel_spreadsheet']) + assert.ok(routes.every((route) => route.target_number === 'HLP-NIPC-TGT-0022')) + assert.ok(routes.every((route) => route.admission === 'VERIFIED_HUMAN_ROUTE')) +}) + +test('the legacy engines are preserved but loaded only inside the module chunk', () => { + assert.match(frontend, /LexicalComposer/) + assert.match(frontend, /Workbook/) + assert.match(frontend, /calculateFormula/) + assert.match(read('src/main.tsx'), /lazy\(\(\) => import\('\.\/modules\/channel-workbench'\)/) +}) diff --git a/product-source/hololake-native-desktop/scripts/compile-gls-runtime-registry.mjs b/product-source/hololake-native-desktop/scripts/compile-gls-runtime-registry.mjs index 5a90b8876..24c86bbd8 100644 --- a/product-source/hololake-native-desktop/scripts/compile-gls-runtime-registry.mjs +++ b/product-source/hololake-native-desktop/scripts/compile-gls-runtime-registry.mjs @@ -12,9 +12,10 @@ const sourceRoot = explicitSourceRoot || (repoRoot ? path.join(repoRoot, 'gls') const sourceCommit = args.get('--source-commit') const output = args.get('--output') const projectionsPath = args.get('--projections') +const referencesPath = args.get('--references') -if (!repoRoot || !sourceRoot || !sourceCommit || !output || !projectionsPath) { - throw new Error('usage: compile-gls-runtime-registry.mjs --repo-root --source-commit --output --projections ') +if (!repoRoot || !sourceRoot || !sourceCommit || !output || !projectionsPath || !referencesPath) { + throw new Error('usage: compile-gls-runtime-registry.mjs --repo-root --source-commit --output --projections --references ') } if (!/^[a-f0-9]{40}$/.test(sourceCommit)) throw new Error('source commit must be a full SHA-1') @@ -168,6 +169,18 @@ if (projectionManifest.schema !== 'hololake.gls-executable-projections/v2' || projectionManifest.source_commit !== sourceCommit) { throw new Error('projection manifest does not match the selected REPO-012 source commit') } +const referenceManifest = JSON.parse(await readFile(referencesPath, 'utf8')) +if (referenceManifest.schema !== 'hololake.gls-numbered-reference-nodes/v1' + || referenceManifest.record_id !== 'HLP-GLS-NUMBERED-REFERENCE-REGISTRY-001' + || referenceManifest.source?.repository !== 'REPO-012' + || referenceManifest.source?.commit !== sourceCommit + || referenceManifest.policy?.number_is_coordinate_not_authority !== true + || referenceManifest.policy?.independent_protocol_source_required_for_execution !== true + || referenceManifest.policy?.reference_only_nodes_may_execute !== false + || referenceManifest.policy?.unknown_reference !== 'FAIL_CLOSED' + || referenceManifest.policy?.unresolved_number_reference_allowed !== false) { + throw new Error('numbered reference manifest does not match the selected REPO-012 source commit or safety boundary') +} const protocolRegistryPath = 'gls/GLS-PROTOCOL-REGISTRY.json' const glsEntryPath = 'gls/GLS-ENTRY.hdlp' @@ -222,29 +235,35 @@ for (const relative of await walk(routingRoot)) { } } -const candidates = (await walk(sourceRoot)) - .map((relative) => ({ relative, match: path.basename(relative).match(/^(GLS-\d{4}).*\.hdlp$/) })) +const allSourceFiles = await walk(sourceRoot) +const candidates = allSourceFiles + .map((relative) => ({ relative, match: path.basename(relative).match(/^(GLS-\d{4})(?:[^0-9].*)?\.(hdlp|md)$/) })) .filter((item) => item.match) - .map((item) => ({ id: item.match[1], relative: item.relative })) + .map((item) => ({ id: item.match[1], relative: item.relative, source_format: item.match[2] === 'hdlp' ? 'HDLP_PROTOCOL_SOURCE' : 'LEGACY_MARKDOWN_EVIDENCE' })) const grouped = new Map() for (const candidate of candidates) { const group = grouped.get(candidate.id) || [] - group.push(candidate.relative) + group.push(candidate) grouped.set(candidate.id, group) } const selectedSource = new Map() for (const [id, sources] of grouped) { - sources.sort((left, right) => sourceRank(left) - sourceRank(right) || left.localeCompare(right)) - const relative = sources[0] + const protocolSources = sources.filter((source) => source.source_format === 'HDLP_PROTOCOL_SOURCE') + if (protocolSources.length === 0) continue + protocolSources.sort((left, right) => sourceRank(left.relative) - sourceRank(right.relative) + || left.relative.localeCompare(right.relative)) + const selected = protocolSources[0] + const relative = selected.relative selectedSource.set(id, { relative, raw: await readFile(path.join(sourceRoot, relative), 'utf8'), - alternate_source_count: sources.length - 1, + source_format: selected.source_format, + alternate_source_count: protocolSources.length - 1, }) } -const knownSourceIds = new Set(selectedSource.keys()) +let knownSourceIds = new Set(selectedSource.keys()) const protocolRegistryIds = new Set(registryMetadata.keys()) const draftIds = new Set((protocolRegistry.registered_draft_protocols || []).map((entry) => entry.id)) const legacyDependencyTargets = new Set() @@ -256,6 +275,56 @@ for (const id of draftIds) { } const legacyDependencyCycles = stronglyConnectedComponents(draftGraph) +// 旧 Notion 导出只在现行协议真实引用且独立 HDLP 正本缺席时作为历史证据节点进入。 +// 未被现行图引用的旧页面不得因为文件名像 GLS 编号就自动取得当前注册地位。 +for (const id of [...legacyDependencyTargets].filter((target) => !knownSourceIds.has(target))) { + if (!authorityDeclarations.has(id)) continue + const legacySources = (grouped.get(id) || []).filter((source) => source.source_format === 'LEGACY_MARKDOWN_EVIDENCE') + if (legacySources.length === 0) continue + legacySources.sort((left, right) => sourceRank(left.relative) - sourceRank(right.relative) || left.relative.localeCompare(right.relative)) + const selected = legacySources[0] + selectedSource.set(id, { + relative: selected.relative, + raw: await readFile(path.join(sourceRoot, selected.relative), 'utf8'), + source_format: selected.source_format, + alternate_source_count: legacySources.length - 1, + }) +} +knownSourceIds = new Set(selectedSource.keys()) + +const missingSourceTargets = [...legacyDependencyTargets].filter((id) => !knownSourceIds.has(id)).sort() +const referenceIds = new Set() +const referenceNodeNumbers = new Set() +const numberedReferenceNodes = [] +for (const node of referenceManifest.nodes || []) { + if (!/^GLS-\d{4}$/.test(node.protocol_id) + || !/^HLP-GLS-REF-\d{4}$/.test(node.node_number) + || !node.title + || !['NORMATIVE_REFERENCE', 'SCHEMA_IMPORT', 'EVIDENCE_ONLY'].includes(node.reference_kind) + || node.source_state !== 'ROADMAP_REFERENCE_ONLY' + || !referenceIds.add(node.protocol_id) + || !referenceNodeNumbers.add(node.node_number)) { + throw new Error(`invalid or duplicated numbered reference node: ${node.protocol_id || 'UNKNOWN'}`) + } + const evidencePaths = [] + for (const relative of allSourceFiles) { + if (!/\.(?:hdlp|md|json|ya?ml)$/.test(relative)) continue + const raw = await readFile(path.join(sourceRoot, relative), 'utf8') + if (raw.includes(node.protocol_id)) evidencePaths.push(`gls/${relative}`) + } + if (evidencePaths.length === 0) throw new Error(`numbered reference node has no REPO-012 evidence: ${node.protocol_id}`) + numberedReferenceNodes.push({ + ...node, + execution_state: 'REFERENCE_ONLY_NOT_EXECUTABLE', + evidence_paths: evidencePaths.sort().slice(0, 12), + }) +} +if (missingSourceTargets.length !== referenceIds.size + || missingSourceTargets.some((id) => !referenceIds.has(id)) + || [...referenceIds].some((id) => !missingSourceTargets.includes(id))) { + throw new Error(`numbered reference registry coverage mismatch: missing=${missingSourceTargets.join(',')} registered=${[...referenceIds].sort().join(',')}`) +} + const protocols = [] for (const [id, source] of [...selectedSource.entries()].sort(([left], [right]) => left.localeCompare(right))) { const heading = source.raw.split(/\r?\n/).find((line) => line.startsWith('#') && line.includes(id)) @@ -280,6 +349,8 @@ for (const [id, source] of [...selectedSource.entries()].sort(([left], [right]) enters_runtime_graph: false, target_registered: protocolRegistryIds.has(target), target_numbered_source_available: knownSourceIds.has(target), + target_number_coordinate_available: knownSourceIds.has(target) || referenceIds.has(target), + target_resolution: knownSourceIds.has(target) ? 'NUMBERED_PROTOCOL_SOURCE' : referenceIds.has(target) ? 'NUMBERED_REFERENCE_NODE' : 'UNRESOLVED', })), ...(projection?.dependencies || []).map((target) => ({ target, @@ -288,18 +359,21 @@ for (const [id, source] of [...selectedSource.entries()].sort(([left], [right]) enters_runtime_graph: true, target_registered: protocolRegistryIds.has(target), target_numbered_source_available: knownSourceIds.has(target), + target_number_coordinate_available: knownSourceIds.has(target) || referenceIds.has(target), + target_resolution: knownSourceIds.has(target) ? 'NUMBERED_PROTOCOL_SOURCE' : referenceIds.has(target) ? 'NUMBERED_REFERENCE_NODE' : 'UNRESOLVED', })), ] const activationBlockers = [] if (!projection) activationBlockers.push('NO_EXECUTABLE_ADAPTER') if (registrationState === 'DISCOVERED_UNRECONCILED') activationBlockers.push('REGISTRATION_NOT_RECONCILED') if (authorityConflict) activationBlockers.push('REGISTRATION_SOURCE_CONFLICT') - if (legacyDependencies.some((target) => !knownSourceIds.has(target)) && !projection) activationBlockers.push('DEPENDENCY_NUMBERED_SOURCE_MISSING') + if (legacyDependencies.some((target) => !knownSourceIds.has(target) && !referenceIds.has(target)) && !projection) activationBlockers.push('DEPENDENCY_NUMBER_COORDINATE_MISSING') protocols.push({ id, title: cleanHeading(heading || id, id), source_status: sourceStatus(source.raw), source_path: `gls/${source.relative}`, + source_format: source.source_format, source_sha256: createHash('sha256').update(source.raw).digest('hex'), alternate_source_count: source.alternate_source_count, registration: { @@ -354,6 +428,7 @@ for (const protocol of protocols) { } const dependenciesNotInRegistry = [...legacyDependencyTargets].filter((id) => !protocolRegistryIds.has(id)).sort() const dependenciesWithoutNumberedSource = [...legacyDependencyTargets].filter((id) => !knownSourceIds.has(id)).sort() +const unresolvedNumberReferences = dependenciesWithoutNumberedSource.filter((id) => !referenceIds.has(id)) const numberedSourcesNotInProtocolRegistry = [...knownSourceIds].filter((id) => !protocolRegistryIds.has(id)).sort() const registry = { schema: 'hololake.gls-runtime-manifest/v2', @@ -384,6 +459,10 @@ const registry = { legacy_dependency_target_count: legacyDependencyTargets.size, dependencies_not_in_protocol_registry: dependenciesNotInRegistry, dependencies_without_numbered_source: dependenciesWithoutNumberedSource, + numbered_reference_node_count: numberedReferenceNodes.length, + unresolved_number_references: unresolvedNumberReferences, + unresolved_number_reference_count: unresolvedNumberReferences.length, + every_dependency_has_number_coordinate: unresolvedNumberReferences.length === 0, numbered_sources_not_in_protocol_registry: numberedSourcesNotInProtocolRegistry, legacy_dependency_cycles: legacyDependencyCycles, source_reference_cycles: legacyDependencyCycles, @@ -395,8 +474,10 @@ const registry = { protocol_count: protocols.length, executable_projection_count: executableCount, inventoried_not_executable_count: protocols.length - executableCount, + number_coordinate_count: protocols.length + numberedReferenceNodes.length, + numbered_reference_nodes: numberedReferenceNodes, protocols, } await writeFile(output, `${JSON.stringify(registry, null, 2)}\n`) -console.log(`GLS_RUNTIME_MANIFEST_COMPILED protocols=${protocols.length} registered=${protocolRegistryIds.size} executable=${executableCount} legacy_cycles=${legacyDependencyCycles.length} output=${output}`) +console.log(`GLS_RUNTIME_MANIFEST_COMPILED protocols=${protocols.length} references=${numberedReferenceNodes.length} unresolved=${unresolvedNumberReferences.length} registered=${protocolRegistryIds.size} executable=${executableCount} legacy_cycles=${legacyDependencyCycles.length} output=${output}`) diff --git a/product-source/hololake-native-desktop/scripts/compile-unified-number-tree.mjs b/product-source/hololake-native-desktop/scripts/compile-unified-number-tree.mjs new file mode 100644 index 000000000..b50240aa3 --- /dev/null +++ b/product-source/hololake-native-desktop/scripts/compile-unified-number-tree.mjs @@ -0,0 +1,162 @@ +import { createHash } from 'node:crypto' +import { mkdirSync, readFileSync, writeFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' + +const root = resolve(dirname(fileURLToPath(import.meta.url)), '..') +const paths = { + identity: resolve(root, 'contracts/zero-core-numbering-kernel.json'), + webview: resolve(root, 'contracts/numbered-ipc-registry.json'), + broker: resolve(root, 'contracts/direct-local-broker-numbered-registry.json'), + gls: resolve(root, 'contracts/gls-runtime-registry.json'), + output: resolve(root, 'generated/unified-number-coordinate-tree.json'), +} + +function readSource(path) { + const bytes = readFileSync(path) + return { + value: JSON.parse(bytes.toString('utf8')), + sha256: createHash('sha256').update(bytes).digest('hex'), + } +} + +export function compileUnifiedNumberTree() { + const identity = readSource(paths.identity) + const webview = readSource(paths.webview) + const broker = readSource(paths.broker) + const gls = readSource(paths.gls) + const routes = [ + ...webview.value.operations.map((route) => ({ + transport: 'TAURI_WEBVIEW_NUMBERED_IPC', + protocolVersion: webview.value.runtime.protocol_version, + callerNumber: webview.value.runtime.caller_number, + channelNumber: route.channel_number, + moduleNumber: route.module_number, + operationNumber: route.operation_number, + targetNumber: route.target_number, + alias: route.alias, + admission: route.admission, + effect: route.effect, + evidence: 'HASH_CHAINED_NUMBERED_IPC_RECEIPT', + path: `HLP-NUMBER-WORLD-ROOT-001/TAURI/${route.channel_number}/${route.module_number}/${route.operation_number}/${route.target_number}`, + })), + ...broker.value.operations.map((route) => ({ + transport: 'DIRECT_LOCAL_NUMBERED_BROKER', + protocolVersion: broker.value.runtime.protocol_version, + callerNumber: broker.value.runtime.caller_number, + channelNumber: route.channel_number, + moduleNumber: route.module_number, + operationNumber: route.operation_number, + targetNumber: route.target_number, + alias: route.alias, + admission: ['DISCOVER_NEARBY', 'PING', 'GET_BEIJING_TIME'].includes(route.alias) + ? 'PREAUTH_LOCAL_SYSTEM_ROUTE' + : ['OPEN_VISITOR_SESSION', 'RECEIVE_LANGUAGE'].includes(route.alias) + ? 'BOUNDED_VISITOR_ROUTE' + : 'AUTHENTICATED_LOCAL_SESSION_WITH_PERSONA_LICENSE_IF_PERSONA_MODE', + effect: ['DISCOVER_NEARBY', 'PING', 'GET_BEIJING_TIME', 'GET_PERSONA_CARRIER_LICENSE_STATUS', 'GET_WORK_ENVIRONMENT', 'RESOLVE_CAPABILITY_ROUTE', 'INSPECT_MOUNTED_PNCC_REPOSITORY', 'READ_MOUNTED_PNCC_REMOTE_OBJECT', 'QUERY_PNCC_RECEIPT_PROJECTION', 'INSPECT_DEVELOPMENT_WRITE_LANE'].includes(route.alias) + ? 'READ_OR_STATUS' + : 'STATE_CHANGE', + evidence: 'NUMBERED_RESPONSE_PLUS_SESSION_OR_DOMAIN_RECEIPT', + path: `HLP-NUMBER-WORLD-ROOT-001/BROKER/${route.channel_number}/${route.module_number}/${route.operation_number}/${route.target_number}`, + })), + ].sort((left, right) => left.path.localeCompare(right.path, 'en')) + const uniquePaths = new Set(routes.map((route) => route.path)) + const uniqueOperations = new Set(routes.map((route) => `${route.transport}:${route.operationNumber}`)) + if (routes.length !== uniquePaths.size || routes.length !== uniqueOperations.size) { + throw new Error('HOLOLAKE_UNIFIED_NUMBER_TREE_DUPLICATE_COORDINATE') + } + const identityNodes = identity.value.namespaces.map((namespace) => ({ + nodeKind: 'IDENTITY_NAMESPACE', + nodeNumber: `HLP-IDENTITY-NS-${namespace.id}`, + namespaceId: namespace.id, + subjectKind: namespace.subject_kind, + domainScope: namespace.domain_scope, + admission: namespace.human_entry ? 'REGISTERED_HUMAN_NAMESPACE' : 'NON_HUMAN_NAMESPACE', + executionState: 'AUTHORITY_RESOLUTION_ONLY', + evidence: identity.value.authority.map_id, + path: `HLP-NUMBER-WORLD-ROOT-001/IDENTITY/${namespace.id}`, + })).sort((left, right) => left.path.localeCompare(right.path, 'en')) + const protocolNodes = [ + ...gls.value.protocols.map((protocol) => ({ + nodeKind: 'GLS_PROTOCOL_SOURCE', + nodeNumber: protocol.id, + protocolId: protocol.id, + title: protocol.title, + sourceState: protocol.source_format, + executionState: protocol.projection_state, + evidence: protocol.source_sha256, + path: `HLP-NUMBER-WORLD-ROOT-001/GLS/SOURCE/${protocol.id}`, + })), + ...gls.value.numbered_reference_nodes.map((reference) => ({ + nodeKind: 'GLS_REFERENCE_ONLY', + nodeNumber: reference.node_number, + protocolId: reference.protocol_id, + title: reference.title, + sourceState: reference.source_state, + executionState: reference.execution_state, + evidence: reference.evidence_paths[0], + path: `HLP-NUMBER-WORLD-ROOT-001/GLS/REFERENCE/${reference.node_number}`, + })), + ].sort((left, right) => left.path.localeCompare(right.path, 'en')) + const allPaths = [...routes.map((route) => route.path), ...identityNodes.map((node) => node.path), ...protocolNodes.map((node) => node.path)] + if (new Set(allPaths).size !== allPaths.length + || gls.value.reconciliation.unresolved_number_reference_count !== 0 + || gls.value.reconciliation.every_dependency_has_number_coordinate !== true + || protocolNodes.some((node) => node.nodeKind === 'GLS_REFERENCE_ONLY' && node.executionState !== 'REFERENCE_ONLY_NOT_EXECUTABLE')) { + throw new Error('HOLOLAKE_UNIFIED_NUMBER_TREE_INCOMPLETE_COVERAGE') + } + return { + schema: 'hololake.unified-number-coordinate-tree/v2', + recordId: 'HLP-UNIFIED-NUMBER-TREE-001', + state: 'MACHINE_COMPILED_STARTUP_ENFORCED', + rootNumber: 'HLP-NUMBER-WORLD-ROOT-001', + identityAuthority: { + mapId: identity.value.authority.map_id, + mapVersion: identity.value.authority.map_version, + namespaces: identity.value.namespaces.map((namespace) => ({ + namespaceId: namespace.id, + roots: namespace.roots, + prefixes: namespace.prefixes, + subjectKind: namespace.subject_kind, + domainScope: namespace.domain_scope, + })), + }, + sources: [ + { recordId: identity.value.record_id, sha256: identity.sha256 }, + { recordId: webview.value.record_id, sha256: webview.sha256 }, + { recordId: broker.value.record_id, sha256: broker.sha256 }, + { recordId: gls.value.record_id, sha256: gls.sha256 }, + ], + invariants: { + numberIsStableCoordinateNotAuthority: true, + pathIsUniqueNavigation: true, + admissionIsSeparateFromIdentity: true, + everyPhysicalCallHasNumberedRoute: true, + everyAcceptedCallHasEvidenceClass: true, + everyProtocolReferenceHasNumberCoordinate: true, + referenceOnlyNodesNeverExecutable: true, + unresolvedNumberReferenceCount: 0, + mismatchedCoordinate: 'FAIL_CLOSED', + }, + coordinateCount: routes.length + identityNodes.length + protocolNodes.length, + routeCount: routes.length, + identityNodeCount: identityNodes.length, + protocolNodeCount: protocolNodes.length, + referenceOnlyNodeCount: protocolNodes.filter((node) => node.nodeKind === 'GLS_REFERENCE_ONLY').length, + identityNodes, + protocolNodes, + routes, + } +} + +const compiled = compileUnifiedNumberTree() +const output = `${JSON.stringify(compiled, null, 2)}\n` +if (process.argv.includes('--check')) { + if (readFileSync(paths.output, 'utf8') !== output) { + throw new Error('HOLOLAKE_UNIFIED_NUMBER_TREE_STALE') + } +} else { + mkdirSync(dirname(paths.output), { recursive: true }) + writeFileSync(paths.output, output) +} diff --git a/product-source/hololake-native-desktop/scripts/direct-local-broker-numbered.test.mjs b/product-source/hololake-native-desktop/scripts/direct-local-broker-numbered.test.mjs new file mode 100644 index 000000000..9509bd2a7 --- /dev/null +++ b/product-source/hololake-native-desktop/scripts/direct-local-broker-numbered.test.mjs @@ -0,0 +1,25 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const registry = JSON.parse(readFileSync(new URL('../contracts/direct-local-broker-numbered-registry.json', import.meta.url), 'utf8')) +const broker = readFileSync(new URL('../src-tauri/src/direct_local_broker.rs', import.meta.url), 'utf8') + +test('the external local broker accepts only complete numbered coordinates', () => { + assert.equal(registry.record_id, 'HLP-NBROKER-ROOT-001') + assert.equal(registry.runtime.protocol_version, 'HLP-NBROKER-v1') + assert.equal(registry.runtime.legacy_string_operation_allowed, false) + assert.equal(registry.runtime.unknown_or_mismatched_coordinate, 'FAIL_CLOSED') + assert.equal(registry.runtime.transport_is_authority, false) + assert.equal(registry.operations.length, 22) + assert.equal(new Set(registry.operations.map((route) => route.operation_number)).size, 22) + assert.equal(new Set(registry.operations.map((route) => route.alias)).size, 22) + for (const route of registry.operations) { + assert.match(route.operation_number, /^HLP-NBROKER-OP-\d{4}$/) + assert.match(route.channel_number, /^HLP-NBROKER-CH-\d{4}$/) + assert.match(route.module_number, /^HLP-NBROKER-MOD-\d{4}$/) + assert.match(route.target_number, /^HLP-NBROKER-TGT-\d{4}$/) + } + assert.match(broker, /decode_numbered_broker_request/) + assert.match(broker, /HOLOLAKE_NUMBERED_BROKER_ROUTE_COORDINATE_MISMATCH/) +}) diff --git a/product-source/hololake-native-desktop/scripts/domain-number-routing.test.mjs b/product-source/hololake-native-desktop/scripts/domain-number-routing.test.mjs index c77086be5..aa088dbbd 100644 --- a/product-source/hololake-native-desktop/scripts/domain-number-routing.test.mjs +++ b/product-source/hololake-native-desktop/scripts/domain-number-routing.test.mjs @@ -19,23 +19,19 @@ test('public routing starts from five domains and lets the number select the ver assert.equal(contract.registries.ENTERPRISE_FOUR_DOMAINS.ownership, 'TCS_0002_ENTERPRISE_REALITY_BODY') }) -test('the public shell shows five official gates while ordinary login returns to a personal channel', () => { +test('the public shell shows five domains and login is domain-routed', () => { const frontend = read('src/main.tsx') const login = read('src-tauri/src/code_repo_login.rs') const router = read('src-tauri/src/zero_point.rs') for (const name of ['光湖主域', '光湖分域', '光湖零域', '光湖零感域', '第五域 · 光湖本源域']) { assert.match(frontend, new RegExp(name.replace('·', '\\·'))) } - assert.match(frontend, /编号验证 · 进入世界的第一个入口/) + assert.match(frontend, /输入编号进入所属域/) assert.match(frontend, /TCS-GL-/) - assert.match(frontend, /仅光湖人类主控团队正式成员/) - assert.match(frontend, /不对普通用户开放/) - assert.match(frontend, /返回我的频道/) - assert.match(frontend, /普通用户|一人一频道/) - assert.doesNotMatch(frontend, /肥猫 · TCS-GL-0007∞/) - assert.doesNotMatch(frontend, /桔子 · TCS-GL-0008∞/) - assert.doesNotMatch(frontend, /烬舟 · PER-JZ001 · AGE/) - assert.doesNotMatch(frontend, /熹微 · PER-JZ-ARCH-001 · AGE/) + assert.match(frontend, /肥猫 · TCS-GL-0007∞/) + assert.match(frontend, /桔子 · TCS-GL-0008∞/) + assert.match(frontend, /烬舟 · PER-JZ001 · AGE/) + assert.match(frontend, /熹微 · PER-JZ-ARCH-001 · AGE/) assert.doesNotMatch(frontend, /肥猫 \+ 烬舟/) assert.match(frontend, /className="gate-close"/) assert.match(frontend, /className="domain-info-scrim"/) diff --git a/product-source/hololake-native-desktop/scripts/dynamic-language-world-visual-system.test.mjs b/product-source/hololake-native-desktop/scripts/dynamic-language-world-visual-system.test.mjs deleted file mode 100644 index 1f22b70e1..000000000 --- a/product-source/hololake-native-desktop/scripts/dynamic-language-world-visual-system.test.mjs +++ /dev/null @@ -1,32 +0,0 @@ -import test from 'node:test' -import assert from 'node:assert/strict' -import { readFileSync } from 'node:fs' - -const read = (path) => readFileSync(new URL(`../${path}`, import.meta.url), 'utf8') -const contract = JSON.parse(read('contracts/dynamic-language-world-visual-system.json')) -const rust = read('src-tauri/src/world_climate.rs') -const ui = read('src/main.tsx') -const tokens = read('src/design-tokens.css') - -test('dynamic world and fixed traditional themes remain independently selectable', () => { - assert.deepEqual(contract.modes, ['DYNAMIC_LANGUAGE_WORLD', 'FIXED_TRADITIONAL_THEME']) - assert.match(ui, /hololake-visual-mode/) - assert.match(ui, /动态语言世界/) - assert.match(ui, /固定传统主题/) -}) - -test('verified weather changes visual tokens but never invents weather when offline', () => { - assert.match(rust, /VERIFIED_LIVE/) - assert.match(rust, /TIME_ONLY_WEATHER_UNAVAILABLE/) - assert.match(rust, /city_exposed: false/) - assert.equal(contract.privacy.weather_unavailable_behavior, 'BEIJING_TIME_ONLY_NO_FAKE_WEATHER') - assert.match(tokens, /visual-world\.climate-day/) - assert.match(tokens, /visual-world\.climate-night/) -}) - -test('the five-domain climate rotation uses internal coordinates and never presents a real city label', () => { - assert.equal(contract.daily_domain_rotation.length, 5) - assert.match(rust, /coordinate_for/) - assert.doesNotMatch(ui, /西安天气|北京天气|广州天气/) - assert.equal(contract.privacy.real_city_exposed_in_ui, false) -}) diff --git a/product-source/hololake-native-desktop/scripts/dynamic-world-surface-admission.test.mjs b/product-source/hololake-native-desktop/scripts/dynamic-world-surface-admission.test.mjs new file mode 100644 index 000000000..3e8fe8526 --- /dev/null +++ b/product-source/hololake-native-desktop/scripts/dynamic-world-surface-admission.test.mjs @@ -0,0 +1,69 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const read = (path) => readFileSync(new URL(`../${path}`, import.meta.url), 'utf8') +const contract = JSON.parse(read('contracts/dynamic-language-world-visual-system.json')) +const registry = JSON.parse(read('contracts/numbered-ipc-registry.json')) +const modulePackage = JSON.parse(read('fixtures/module-packages/HLP-MOD-OFFICIAL-DYNAMIC-WORLD-SURFACE-0001-0.1.0.ghmod')) +const rust = read('src-tauri/src/world_climate.rs') +const frontend = read('src/main.tsx') +const styles = read('src/styles.css') +const starlake = read('src/modules/qoder-surface/StarlakeSurface.tsx') +const starlakeStyles = read('src/modules/qoder-surface/starlake-surface.css') +const traditional = read('src/modules/qoder-surface/TraditionalSurface.tsx') +const traditionalStyles = read('src/modules/qoder-surface/traditional-surface.css') +const visualBalance = read('src/modules/qoder-surface/visual-balance.ts') +const layoutProfile = read('src/modules/qoder-surface/layout-profile.ts') + +test('dynamic world surface is signed, opt-in and cannot mutate world facts', () => { + assert.equal(modulePackage.manifest.moduleNumber, 'HLP-MOD-OFFICIAL-DYNAMIC-WORLD-SURFACE-0001') + assert.equal(modulePackage.manifest.registrationClass, 'OFFICIAL_LIGHTHOUSE') + assert.equal(modulePackage.manifest.adapter, 'dynamic-language-world-surface-v1') + assert.deepEqual(modulePackage.manifest.permissions, ['REALITY_TIME_READ', 'PUBLIC_WEATHER_QUERY']) + assert.equal(modulePackage.payload.adapterConfig.routingPermissionOrFactMutation, false) + assert.equal(contract.visual_lock.climate_changes_routing_permission_or_fact, false) + assert.equal(contract.visual_lock.daily_sampling_domain_changes_open_domain, false) +}) + +test('climate query crosses the unique numbered route and fails closed before activation', () => { + const route = registry.operations.find((item) => item.alias === 'get_world_climate') + assert.equal(route.operation_number, 'HLP-NIPC-OP-0147') + assert.equal(route.module_number, 'HLP-NIPC-MOD-0031') + assert.equal(route.target_number, 'HLP-NIPC-TGT-0031') + assert.equal(route.admission, 'VERIFIED_HUMAN_ROUTE') + assert.match(rust, /require_active_module_adapter/) + assert.doesNotMatch(rust, /#\[tauri::command\]/) + assert.doesNotMatch(frontend, /from ['"]@tauri-apps\/api\/core['"]/) +}) + +test('Qoder locked two-track surface is admitted without simulated product state', () => { + assert.equal(contract.visual_lock.layout, 'QODER_VISUAL_LANGUAGE_SEMANTICALLY_REASSEMBLED_ON_RESPONSIVE_HOLOLAKE_SHELL') + assert.equal(contract.visual_lock.language_world_themes.length, 5) + assert.equal(contract.visual_lock.traditional_finishes.length, 8) + assert.equal(contract.visual_lock.all_downstream_pages_inherit_active_surface_tokens, true) + assert.match(frontend, /夜湖星光/) + assert.match(frontend, /天气源暂不可用;湖面只使用北京时间,不生成假天气。/) + assert.match(styles, /\.world-climate-veil/) + assert.match(starlake, /ResizeObserver/) + assert.match(starlake, /data-layout=\{layout\.profile\}/) + assert.match(layoutProfile, /panoramic.*wide.*compact.*stacked/s) + assert.doesNotMatch(starlake, /naturalScale|sideCenters|onDuty \? 2/) + assert.match(starlakeStyles, /\.starlake-scene\.awake/) + assert.match(traditional, /broadcasts\.map/) + assert.match(traditionalStyles, /\.official-world\.surface-traditional/) + assert.match(visualBalance, /contrastRatio/) + assert.doesNotMatch(traditional, /语料库新增 217 条/) + assert.doesNotMatch(traditional, /const BROADCAST_POOL/) +}) + +test('ambient motion sleeps and the Qoder source hashes remain pinned', () => { + assert.equal(contract.visual_lock.ambient_motion_requires_recent_human_pointer_or_keyboard_activity, true) + assert.equal(contract.visual_lock.idle_visual_state, 'STATIC') + assert.match(frontend, /setTimeout\(\(\) => \{[\s\S]*setMotionAwake\(false\)/) + assert.match(starlake, /if \(!awake\) return/) + assert.match(traditional, /if \(!awake\) return/) + assert.match(starlakeStyles, /animation-play-state:paused/) + assert.match(traditionalStyles, /animation-play-state:paused/) + assert.equal(contract.qoder_source_provenance.main_tsx_sha256, '7dab8596ec0bd2609b1b23b175f1f14f15f2aa9af847eb605692c993d2e876e8') +}) diff --git a/product-source/hololake-native-desktop/scripts/education-workbench-admission.test.mjs b/product-source/hololake-native-desktop/scripts/education-workbench-admission.test.mjs new file mode 100644 index 000000000..644f94f55 --- /dev/null +++ b/product-source/hololake-native-desktop/scripts/education-workbench-admission.test.mjs @@ -0,0 +1,53 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const read = (path) => readFileSync(new URL(`../${path}`, import.meta.url), 'utf8') +const contract = JSON.parse(read('contracts/education-workspace.json')) +const registry = JSON.parse(read('contracts/numbered-ipc-registry.json')) +const modulePackage = JSON.parse(read('fixtures/module-packages/HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001-0.1.0.ghmod')) +const workspaceRust = read('src-tauri/src/education_workspace.rs') +const translationRust = read('src-tauri/src/education_translation.rs') +const frontend = [ + read('src/modules/education-workspace/index.tsx'), + read('src/modules/education-workspace/education-data.ts'), + read('src/modules/education-workspace/education-adaptive-rendering.ts'), +].join('\n') + +test('education is an official signed declarative adapter over the shared office foundation', () => { + assert.equal(contract.package.official_module_number, 'HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001') + assert.equal(contract.foundation_dependency.module_id, 'hololake.builtin.channel-workbench') + assert.equal(modulePackage.manifest.registrationClass, 'OFFICIAL_LIGHTHOUSE') + assert.equal(modulePackage.manifest.adapter, 'education-workbench-v1') + assert.equal(modulePackage.payload.adapterConfig.foundationModule, 'HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001') + assert.equal(modulePackage.payload.adapterConfig.importDefaultScope, 'UNASSIGNED') + assert.equal(modulePackage.payload.adapterConfig.modelFileTransferDefault, 'DENY') + assert.equal(modulePackage.manifest.permissions.length, 8) +}) + +test('all education effects cross the exact numbered adapter routes', () => { + const routes = registry.operations.filter((route) => route.module_number === 'HLP-NIPC-MOD-0024') + assert.deepEqual(routes.map((route) => route.operation_number), Array.from({ length: 18 }, (_, index) => `HLP-NIPC-OP-${String(index + 85).padStart(4, '0')}`)) + assert.ok(routes.every((route) => route.target_number === 'HLP-NIPC-TGT-0024')) + assert.ok(routes.every((route) => route.admission === 'VERIFIED_HUMAN_ROUTE')) + assert.doesNotMatch(frontend, /from ['"]@tauri-apps\/api\/core['"]/) + assert.doesNotMatch(workspaceRust, /#\[tauri::command\]/) + assert.doesNotMatch(translationRust, /#\[tauri::command\]/) + assert.match(workspaceRust, /require_active_module_adapter/) +}) + +test('legacy silent corruption and automatic destructive behavior are rejected', () => { + assert.doesNotMatch(workspaceRust, /serde_json::from_str[^\n]+unwrap_or_default/) + assert.match(workspaceRust, /deny_unknown_fields/) + assert.match(frontend, /生成只读预览/) + assert.match(frontend, /我确认,写入清理结果/) + assert.match(frontend, /生成只读预览令牌/) + assert.match(frontend, /我确认,按此令牌执行/) + assert.match(frontend, /确认归入教育频道/) +}) + +test('education UI is independently lazy-loaded and reuses, rather than duplicates, office engines', () => { + assert.match(read('src/main.tsx'), /lazy\(\(\) => import\('\.\/modules\/education-workspace'\)/) + assert.match(read('src/modules/education-workspace/education-document-engine.tsx'), /channel-workbench\/document-engine/) + assert.match(read('src/modules/education-workspace/index.tsx'), /channel-workbench\/spreadsheet-engine/) +}) diff --git a/product-source/hololake-native-desktop/scripts/education-workspace.test.mjs b/product-source/hololake-native-desktop/scripts/education-workspace.test.mjs deleted file mode 100644 index f9fbbcf9f..000000000 --- a/product-source/hololake-native-desktop/scripts/education-workspace.test.mjs +++ /dev/null @@ -1,318 +0,0 @@ -import assert from 'node:assert/strict' -import fs from 'node:fs' -import test from 'node:test' - -const read = (path) => fs.readFileSync(new URL('../' + path, import.meta.url), 'utf8') -const contract = JSON.parse(read('contracts/education-workspace.json')) -const donors = JSON.parse(read('contracts/education-open-source-donor-assessment.json')) -const industry = JSON.parse(read('contracts/subdomain-industry-routing.json')) -const rust = read('src-tauri/src/education_workspace.rs') -const translation = read('src-tauri/src/education_translation.rs') -const lib = read('src-tauri/src/lib.rs') -const ui = read('src/main.tsx') -const documentEngine = read('src/modules/education-document-engine.tsx') -const channelDocumentEngine = read('src/modules/channel-workbench/document-engine.tsx') -const channelSpreadsheetEngine = read('src/modules/channel-workbench/spreadsheet-engine.tsx') -const channelSpreadsheetModel = read('src/modules/channel-workbench/spreadsheet-model.ts') -const moduleRuntime = JSON.parse(read('contracts/channel-module-runtime.json')) -const marketplaceDevelopmentCatalog = JSON.parse(read('contracts/official-module-marketplace-development-catalog.json')) -const adaptiveRendering = read('src/modules/education-adaptive-rendering.ts') -const dataCleanup = read('src/modules/education-data.ts') - -test('education package consumes the built-in account-scoped channel workbench', () => { - assert.equal(contract.domain_entry, 'BRANCH_DOMAIN') - assert.equal(contract.channel_id, 'GH-EDU-INIT-001') - assert.equal(contract.native_storage.owner, 'HOLOLAKE_NATIVE_RUST_CORE') - assert.equal(contract.native_storage.engine, 'SQLITE') - assert.equal(contract.native_storage.authenticated_account_required, true) - assert.equal(contract.native_storage.cross_account_projection_allowed, false) - assert.equal(contract.native_storage.restart_readback_required, true) - assert.equal(contract.document_module.state, 'DEVELOPMENT_FEATURE_MOUNTED') - assert.equal(contract.table_module.state, 'DEVELOPMENT_FEATURE_MOUNTED') - assert.equal(contract.foundation_dependency.module_id, 'hololake.builtin.channel-workbench') - assert.match(contract.document_module.engine, /^CHANNEL_WORKBENCH_LEXICAL_0_49_0/) - assert.match(contract.table_module.engine, /^CHANNEL_WORKBENCH_FORTUNE_SHEET_1_0_4/) - assert.equal(contract.table_module.sensitive_field_visibility.local_default, 'HIDDEN_UNTIL_HUMAN_REVEALS') - assert.equal(contract.table_module.sensitive_field_visibility.human_can_hide, true) - assert.equal(contract.table_module.sensitive_field_visibility.human_can_show_again, true) -}) - -test('education workspace commands require the authenticated storage root', () => { - assert.match(rust, /account_storage_root\(app, "education-workspace-v1"\)/) - for (const command of [ - 'get_education_workspace_snapshot', - 'create_education_document', - 'read_education_document', - 'save_education_document', - 'archive_education_document', - 'create_education_table', - 'read_education_table', - 'save_education_table', - 'archive_education_table', - 'create_education_automation_rule', - 'save_education_automation_rule', - 'archive_education_automation_rule', - 'preview_education_automation_rule', - 'execute_education_automation_rule', - ]) { - assert.match(lib, new RegExp('education_workspace::' + command)) - assert.ok(ui.includes("'" + command + "'") || ui.includes('"' + command + '"')) - } - assert.doesNotMatch(ui, /localStorage.*education/i) -}) - -test('one built-in foundation and five development features are reachable from the education channel', () => { - const education = industry.industries.find((item) => item.key === 'EDUCATION') - assert.equal(education.initialization_channel.installed_open_source_modules, 0) - assert.equal(education.initialization_channel.installed_native_modules, 1) - assert.equal(education.initialization_channel.development_mounted_modules, 1) - assert.ok(education.initialization_channel.module_slots.every((slot) => slot.state === 'DEVELOPMENT_MOUNTED')) - assert.equal(education.initialization_channel.module_slots[1].state_label, '开发态模块 · 使用频道内置工作表引擎') - assert.match(ui, /worldStage === 'educationDocuments'/) - assert.match(ui, /worldStage === 'educationTables'/) - assert.match(ui, /教育文档/) - assert.match(ui, /教育表格数据/) - assert.match(ui, /worldStage === 'educationAutomation'/) - assert.match(ui, /教育自动化/) - assert.match(ui, /worldStage === 'educationComposition'/) - assert.match(ui, /原生组合视图/) -}) - -test('native persistence enforces revision conflicts and bounded table data', () => { - assert.match(rust, /HOLOLAKE_EDUCATION_DOCUMENT_REVISION_CONFLICT/) - assert.match(rust, /HOLOLAKE_EDUCATION_TABLE_REVISION_CONFLICT/) - assert.match(rust, /MAX_TABLE_COLUMNS: usize = 30/) - assert.match(rust, /MAX_TABLE_ROWS: usize = 1_000/) - assert.match(rust, /MAX_CELL_BYTES: usize = 10_000/) - assert.match(rust, /archived = 1/) -}) - -test('education documents expose formal templates and real title filtering', () => { - assert.equal(contract.state, 'DEVELOPMENT_MODULE_SOURCE_MOUNTED_ACCEPTANCE_PENDING') - assert.equal(contract.package.official_module_number, null) - assert.equal(contract.package.registration_state, 'PENDING_FUNCTIONAL_ACCEPTANCE_AND_NUMBER_REGISTRATION') - assert.ok(contract.document_module.capabilities.includes('FORMAL_TEACHING_TEMPLATES')) - assert.ok(contract.document_module.capabilities.includes('TITLE_DIRECTORY_FILTER')) - assert.match(ui, /EDUCATION_DOCUMENT_TEMPLATES/) - assert.match(ui, /教学计划/) - assert.match(ui, /教育项目会议纪要/) - assert.match(ui, /学员阶段复盘/) - assert.match(ui, /检索教育文档标题/) - for (const capability of [ - 'CREATE_COPY', - 'EXPLICIT_READ_AND_EDIT_MODES', - 'HUMAN_SETTINGS_MENU', - 'BASIC_FORMATTING_TOOLBAR', - 'NATIVE_READING_CANVAS', - 'EXPORT_MARKDOWN_AND_HTML', - ]) assert.ok(contract.document_module.capabilities.includes(capability)) - assert.match(ui, /文档设置/) - assert.match(ui, /编辑与重命名/) - assert.match(ui, /创建副本/) - assert.match(ui, /移到可恢复归档/) - assert.match(ui, /educationDocumentMode === 'edit'/) - assert.match(documentEngine, /ChannelDocumentEngine/) - assert.match(channelDocumentEngine, /频道内置文档引擎工具/) -}) - -test('education table has four engine-backed views, filtering, sorting and bounded bulk paste', () => { - for (const capability of [ - 'CELL_CONTENT_FILTER', - 'COLUMN_SORT_ASCENDING_DESCENDING_OR_SOURCE_ORDER', - 'BOUNDED_PAGINATION_ROW_MODEL_FOR_LARGE_EDITABLE_TABLES', - 'GRID_AND_EDITABLE_CARD_VIEWS', - 'GROUPED_CLASSIFICATION_BOARD_FROM_SELECTED_FIELD', - 'LIVE_DATA_OVERVIEW_WITH_SELECTED_GROUP_AND_MEASURE', - 'HEADLESS_TABLE_DATA_DETACHED_OUTSIDE_TABLE_SURFACE', - 'MULTI_ROW_MULTI_COLUMN_TABULAR_PASTE', - ]) assert.ok(contract.table_module.capabilities.includes(capability)) - assert.match(ui, /parseEducationGridPaste/) - assert.match(ui, /pasteEducationTableGrid/) - assert.match(ui, />工作表<\/button>/) - assert.match(channelSpreadsheetEngine, /Workbook/) - assert.match(channelSpreadsheetEngine, /showFormulaBar/) - assert.match(channelSpreadsheetEngine, /conditionFormat/) - assert.match(channelSpreadsheetModel, /cell\.f/) - assert.match(ui, /分类看板/) - assert.match(ui, /数据概览/) - assert.match(ui, /renderEducationBoard/) - assert.match(ui, /renderEducationDashboard/) - assert.match(ui, /动态分类对比/) - assert.match(ui, /筛选教育表格数据/) - assert.match(ui, /升序 ↑/) - assert.match(ui, /降序 ↓/) - assert.match(ui, /getPaginationRowModel/) - assert.match(ui, /const educationTableEngineActive = worldStage === 'educationTables'/) - assert.match(ui, /educationTableEngineActive \? activeEducationTable\?\.rows \|\| \[\] : \[\]/) - assert.match(adaptiveRendering, /EDUCATION_EDITABLE_CELL_BUDGET = 240/) - assert.match(adaptiveRendering, /function educationEditablePageSize/) - assert.match(adaptiveRendering, /Math\.floor\(EDUCATION_EDITABLE_CELL_BUDGET \/ boundedColumns\)/) - assert.match(adaptiveRendering, /buildEducationBaselineRenderPlan/) - assert.match(adaptiveRendering, /validateEducationModelRenderProposal/) - assert.match(adaptiveRendering, /MODEL_PROPOSAL_REJECTED/) - assert.match(adaptiveRendering, /WAITING_HUMAN_CONFIRMATION/) - assert.match(adaptiveRendering, /已优雅退回本地基础算法/) - assert.match(ui, /基础算法自适应回执/) - assert.match(ui, /教育表格分页/) - assert.match(ui, /Math\.min\(30/) - assert.match(ui, /Math\.min\(1000/) - assert.ok(contract.table_module.capabilities.includes('HUMAN_SETTINGS_MENU')) - assert.ok(contract.table_module.capabilities.includes('CREATE_COPY')) - assert.match(ui, /表格设置/) - assert.match(ui, /duplicateEducationTable/) - assert.match(ui, /重命名与编辑/) - assert.match(ui, /isSensitiveEducationColumn/) - assert.match(ui, /账号\|账户\|手机号/) - assert.match(ui, /联系qq\|qq号/) - assert.match(ui, /educationSensitiveVisible, setEducationSensitiveVisible\] = useState\(false\)/) - assert.match(ui, /隐藏敏感字段/) - assert.match(ui, /显示敏感字段/) - assert.ok((ui.match(/setEducationSensitiveVisible\(false\)/g) || []).length >= 4) - assert.match(ui, /onClick=\{\(\) => setEducationSensitiveVisible\(true\)\}>在本机显示并打开工作表/) - assert.match(ui, /type=\{sensitive && !educationSensitiveVisible \? 'password' : 'text'\}/) - assert.equal(contract.table_module.sensitive_field_visibility.local_default, 'HIDDEN_UNTIL_HUMAN_REVEALS') -}) - -test('external tables are profiled into an unassigned staging area before human industry routing', () => { - assert.equal(contract.content_translation_layer.state, 'DETERMINISTIC_RUNTIME_READY_MODEL_ASSIST_SLOT_RESERVED') - assert.equal(contract.content_translation_layer.source_file_mutation_allowed, false) - assert.equal(contract.content_translation_layer.silent_truncation_allowed, false) - assert.equal(contract.content_translation_layer.local_import_without_model_api, 'ENABLED_FOR_ALL_SUPPORTED_FORMATS') - assert.equal(contract.content_translation_layer.import_assignment.default_scope, 'UNASSIGNED') - assert.equal(contract.content_translation_layer.import_assignment.education_directory_before_human_confirmation, false) - assert.equal(contract.content_translation_layer.import_assignment.existing_legacy_imports_reclassified_on_migration, true) - assert.equal(contract.content_translation_layer.semantic_focus.current_mode, 'DETERMINISTIC_BASELINE_ONLY') - assert.equal(contract.content_translation_layer.semantic_focus.low_confidence_behavior, 'ASK_HUMAN_TO_SELECT_PRIMARY_MEASURE') - assert.equal(contract.content_translation_layer.adaptive_rendering.current_mode, 'DETERMINISTIC_BASELINE_WITH_EXPLAINABLE_DEGRADATION') - assert.equal(contract.content_translation_layer.adaptive_rendering.editable_cell_budget, 240) - assert.equal(contract.content_translation_layer.adaptive_rendering.local_baseline_always_available, true) - assert.equal(contract.content_translation_layer.adaptive_rendering.model_adjustment.human_confirmation_before_rule_activation, true) - assert.equal(contract.content_translation_layer.adaptive_rendering.model_adjustment.model_can_mutate_source_data, false) - assert.equal(contract.content_translation_layer.adaptive_rendering.model_adjustment.failure_behavior, 'RETURN_TO_LOCAL_BASELINE_WITH_RECEIPT') - assert.deepEqual(contract.content_translation_layer.model_assist.learning_scopes, ['PRIVATE_ONLY', 'SHARE_ANONYMIZED_RULE']) - assert.match(translation, /PROFILED_BEFORE_WRITE/) - assert.match(translation, /import_table_batch_at/) - assert.match(translation, /source_preserved_read_only: true/) - assert.match(translation, /HOLOLAKE_MODEL_RECOGNITION_API\/v1/) - assert.match(translation, /infer_semantic_focus/) - assert.match(translation, /primary_measure_column/) - assert.match(rust, /education_import_registry/) - assert.match(rust, /assignment_scope = 'UNASSIGNED'/) - assert.match(rust, /assign_imported_table_to_education/) - assert.match(ui, /导入外部表格/) - assert.match(ui, /通用暂存区/) - assert.match(ui, /确认归入教育/) - assert.match(ui, /导出 Excel/) - assert.match(ui, /识别边界回执/) - assert.match(ui, /本地导入正常可用/) -}) - -test('audited translation libraries and headless engines stay bounded inside the HoloLake product shell', () => { - assert.equal(donors.state, 'FOUR_TRANSLATION_FAMILIES_TWO_HEADLESS_FAMILIES_AND_ONE_REAL_SPREADSHEET_ENGINE_INTEGRATED') - assert.equal(donors.current_slice.external_translation_library_families_added, 4) - assert.equal(donors.current_slice.external_headless_engine_families_added, 2) - assert.equal(donors.current_slice.real_spreadsheet_engine_families_added, 1) - assert.equal(donors.current_slice.direct_frontend_engine_packages_added, 10) - assert.equal(donors.current_slice.external_source_files_copied, 0) - assert.equal(donors.current_slice.source_research_route, 'LOCAL_DIRECT_TO_BS_SG_002_THEN_EXTERNAL_DRIVE') - assert.equal(donors.candidates.find((candidate) => candidate.name === 'Calamine').decision, 'INTEGRATE_PINNED_0_26_1_READ_ONLY_NO_OFFICE_RUNTIME') - assert.equal(donors.candidates.find((candidate) => candidate.name === 'rust_xlsxwriter').decision, 'INTEGRATE_PINNED_0_64_2_WRITE_NEW_XLSX_ONLY') - assert.equal(donors.candidates.find((candidate) => candidate.name === 'AppFlowy').decision, 'DO_NOT_EMBED_WHOLE_APPLICATION_STUDY_WORKSPACE_AND_DATABASE_INTERACTION_LOGIC_ONLY') - assert.match(donors.candidates.find((candidate) => candidate.name === 'Lexical').decision, /^INTEGRATE_PINNED_0_49_0/) - assert.equal(donors.candidates.find((candidate) => candidate.name === 'TanStack Table').decision, 'INTEGRATE_PINNED_8_21_3_SORT_FILTER_GROUP_AGGREGATE_AND_PAGINATION_ROW_MODELS_ONLY') - assert.equal(donors.candidates.find((candidate) => candidate.name === 'Univer').decision, 'SOURCE_STUDY_ONLY_DO_NOT_EMBED_WHOLE_APPLICATION_DEFER_FORMULA_AND_ADVANCED_GRID_PARTS') - assert.match(donors.candidates.find((candidate) => candidate.name === 'FortuneSheet').decision, /^INTEGRATE_PINNED_1_0_4/) - assert.equal(donors.candidates.find((candidate) => candidate.name === 'Glide Data Grid').decision, 'SOURCE_STUDY_ONLY_GRID_IS_NOT_A_SPREADSHEET_OR_FORMULA_ENGINE') - assert.equal(donors.candidates.find((candidate) => candidate.name === 'Vega-Lite').observed_license, 'BSD-3-Clause') - assert.equal(donors.candidates.find((candidate) => candidate.name === 'Observable Plot').observed_license, 'ISC') - assert.equal(donors.candidates.find((candidate) => candidate.name === 'Apache ECharts').decision, 'DO_NOT_IMPORT_FOR_STAGE_ONE_FIVE_NATIVE_VIEWS_DO_NOT_JUSTIFY_RUNTIME_WEIGHT') - assert.match(ui, /useReactTable/) - assert.match(ui, /getGroupedRowModel/) - assert.match(ui, /lazy\(\(\) => import\('\.\/modules\/education-document-engine'\)\)/) - assert.match(channelDocumentEngine, /LexicalComposer/) - assert.match(channelDocumentEngine, /HistoryPlugin/) - assert.match(channelDocumentEngine, /\$convertFromMarkdownString/) - assert.match(channelDocumentEngine, /\$convertToMarkdownString/) -}) - -test('official module marketplace boundary is honest before education acceptance', () => { - assert.equal(moduleRuntime.built_in_foundation.module_id, 'hololake.builtin.channel-workbench') - assert.equal(moduleRuntime.built_in_foundation.marketplace_registration, 'NOT_A_MARKETPLACE_MODULE') - assert.equal(moduleRuntime.current_reality.official_marketplace_service_connected, false) - const educationPackage = marketplaceDevelopmentCatalog.packages.find((item) => item.package_key === 'hololake.official.education-workbench') - assert.equal(educationPackage.official_module_number, null) - assert.equal(educationPackage.distribution_state, 'NOT_PUBLISHED') - assert.ok(educationPackage.acceptance_required.includes('CREATE_EDIT_FORMULA_SAVE_REOPEN_SPREADSHEET')) -}) - -test('native composition uses one real account dataset for five read-only human projections', () => { - assert.equal(contract.composition_module.state, 'DEVELOPMENT_FEATURE_MOUNTED') - assert.equal(contract.composition_module.source, 'CURRENT_AUTHENTICATED_ACCOUNTS_REAL_KNOWLEDGE_CATALOG') - assert.equal(contract.composition_module.read_only, true) - assert.equal(contract.composition_module.hardcoded_sample_data_used, false) - for (const capability of [ - 'NATIVE_TYPED_OBJECT', - 'REGISTERED_MODULE_EXECUTION_GRAPH', - 'DASHBOARD_PROJECTION', - 'COMPARISON_PROJECTION', - 'VERTICAL_BAR_PROJECTION', - 'CLASSIFICATION_PROJECTION', - 'TABLE_PROJECTION', - 'ONE_EXECUTION_RESULT_MULTIPLE_HUMAN_VIEWS', - ]) assert.ok(contract.composition_module.capabilities.includes(capability)) - assert.match(lib, /native_composition::execute_knowledge_native_composition/) - assert.match(ui, /execute_knowledge_native_composition/) -}) - -test('education data cleanup previews bounded deterministic rules and requires human confirmation before save', () => { - assert.equal(contract.data_cleanup_module.state, 'DEVELOPMENT_FEATURE_MOUNTED') - assert.equal(contract.data_cleanup_module.automatic_destructive_cleanup_allowed, false) - assert.equal(contract.data_cleanup_module.cross_account_data_allowed, false) - for (const capability of [ - 'TRIM_CELL_BOUNDARY_WHITESPACE', - 'REMOVE_FULLY_EMPTY_ROWS', - 'REMOVE_EXACT_DUPLICATE_ROWS_KEEP_FIRST', - 'PREVIEW_BEFORE_WRITE', - 'FULL_TABLE_ANALYSIS_WITH_BOUNDED_CHANGE_PREVIEW', - 'ACCESSIBILITY_SAFE_NON_TABLE_CHANGE_LIST', - 'HUMAN_CONFIRMATION_BEFORE_SAVE', - 'SAVE_WITH_EXPECTED_REVISION', - ]) assert.ok(contract.data_cleanup_module.capabilities.includes(capability)) - assert.equal(contract.data_cleanup_module.preview_change_limit, 12) - assert.equal(contract.data_cleanup_module.preview_cell_value_character_limit, 120) - assert.equal(contract.data_cleanup_module.full_table_analysis_is_limited_by_preview, false) - assert.match(dataCleanup, /EDUCATION_CLEANUP_CHANGE_PREVIEW_LIMIT = 12/) - assert.match(dataCleanup, /changePreview\.length < EDUCATION_CLEANUP_CHANGE_PREVIEW_LIMIT/) - assert.match(dataCleanup, /singleLine\.length > 120/) - assert.match(ui, /analyzeEducationTableData/) - assert.match(ui, /const educationDataAnalysis = useMemo/) - assert.match(ui, /已分析全表/) - assert.match(ui, /education-cleanup-change-list/) - assert.doesNotMatch(ui, /analysis\.cleanedRows\.slice\(0, 12\)/) - assert.match(ui, /educationData/) - assert.match(ui, /window\.confirm\(`将整理/) - assert.match(ui, /确认整理并保存/) - assert.match(ui, /保存前不写入数据库/) -}) - -test('education automation is manual, revision-locked, previewed and receipted', () => { - assert.equal(contract.automation_module.state, 'DEVELOPMENT_FEATURE_MOUNTED') - assert.equal(contract.automation_module.background_or_scheduled_execution_allowed, false) - assert.equal(contract.automation_module.cross_account_data_allowed, false) - assert.equal(contract.automation_module.model_execution_authority_granted, false) - for (const capability of [ - 'CREATE_AND_SAVE_RULE', - 'PREVIEW_MATCHED_ROWS_AND_CHANGED_CELLS', - 'HUMAN_CONFIRMATION_BEFORE_EXECUTION', - 'RULE_AND_TABLE_REVISION_LOCK', - 'SIGNED_PREVIEW_TOKEN', - 'ATOMIC_TABLE_WRITE_AND_EXECUTION_RECEIPT', - ]) assert.ok(contract.automation_module.capabilities.includes(capability)) - assert.match(rust, /education_automation_rules/) - assert.match(rust, /education_automation_runs/) - assert.match(rust, /transaction_with_behavior/) - assert.match(rust, /HOLOLAKE_EDUCATION_AUTOMATION_PREVIEW_TOKEN_MISMATCH/) - assert.match(ui, /预览执行范围/) - assert.match(ui, /确认运行并生成回执/) - assert.match(ui, /系统不会在后台自行运行/) -}) diff --git a/product-source/hololake-native-desktop/scripts/fifth-domain-private-route.test.mjs b/product-source/hololake-native-desktop/scripts/fifth-domain-private-route.test.mjs index 694270ac5..e211c81e6 100644 --- a/product-source/hololake-native-desktop/scripts/fifth-domain-private-route.test.mjs +++ b/product-source/hololake-native-desktop/scripts/fifth-domain-private-route.test.mjs @@ -3,6 +3,7 @@ import fs from 'node:fs' import test from 'node:test' const source = fs.readFileSync(new URL('../src/main.tsx', import.meta.url), 'utf8') +const privateChannel = fs.readFileSync(new URL('../src/modules/private-channel/PrivateChannelSurface.tsx', import.meta.url), 'utf8') test('Zhizhi remains absent from the public Fifth Domain card', () => { const publicCard = source.slice(source.indexOf("domain: 'FIFTH_DOMAIN'"), source.indexOf('const starPoints')) @@ -23,7 +24,10 @@ test('the Eternal Lake Heart system body exposes both owned branches before chan assert.match(source, /title="爱之核心子系统" meta="责任主体 · 之之"/) assert.match(source, /title="光之湖" meta="人格体居所"/) assert.match(source, /worldStage === 'heartbeat'/) - assert.match(source, /

心跳核心频道<\/h1>/) + assert.match(source, / { @@ -32,12 +36,22 @@ test('knowledge belongs to Heartbeat Core while persona repositories belong to L const toolStart = source.indexOf("worldStage === 'tool'", lightLakeStart) const heartbeat = source.slice(heartbeatStart, lightLakeStart) const lightLake = source.slice(lightLakeStart, toolStart) - assert.match(heartbeat, /title="知识空间"/) + assert.match(heartbeat, /nativeActions=\{privateNativeActions\}/) + assert.match(source, /id: 'knowledge', title: '知识空间'/) assert.doesNotMatch(heartbeat, /人格体代码仓库|title="光之湖"/) assert.match(lightLake, /

光之湖<\/h1>/) assert.match(lightLake, /title="人格体代码仓库"/) }) +test('industry modules are installed furniture in the private home, not peer domains', () => { + assert.match(source, /const privateInstalledModules: InstalledChannelModule\[\]/) + assert.match(privateChannel, /已安装模块/) + assert.match(privateChannel, /前往分域模块商城/) + assert.match(privateChannel, /这里不属于主域、分域、零域或零感域/) + assert.match(privateChannel, /任何反向访问都必须持有/) + assert.doesNotMatch(source, /className="channel-education" title="教育工作台"/) +}) + test('Fifth Domain human numbers are paired with their own login account', () => { assert.match(source, /'ICE-GL∞': 'bingshuo'/) assert.match(source, /'ICE-GL-ZHI∞': 'zhizhi'/) diff --git a/product-source/hololake-native-desktop/scripts/foundation.test.mjs b/product-source/hololake-native-desktop/scripts/foundation.test.mjs index 85ce157a7..d15d688a5 100644 --- a/product-source/hololake-native-desktop/scripts/foundation.test.mjs +++ b/product-source/hololake-native-desktop/scripts/foundation.test.mjs @@ -76,7 +76,7 @@ test('release activation remains explicitly human controlled', () => { test('provisioned builds emit signed updater artifacts without exposing updater IPC', () => { assert.equal(tauriConfig.bundle.createUpdaterArtifacts, true) assert.equal(foundation.tauri_update_artifacts_enablement_gate, 'JD_CONTROLLER_PUBLIC_KEY_AND_SIGNED_RELEASE_PIPELINE_REQUIRED') - assert.deepEqual(capability.permissions, ['core:default']) + assert.deepEqual(capability.permissions, ['core:default', 'allow-numbered-ipc']) assert.equal(capability.permissions.includes('updater:default'), false) }) diff --git a/product-source/hololake-native-desktop/scripts/gls-protocol-runtime.test.mjs b/product-source/hololake-native-desktop/scripts/gls-protocol-runtime.test.mjs index 41f661a8d..3b3ceefb9 100644 --- a/product-source/hololake-native-desktop/scripts/gls-protocol-runtime.test.mjs +++ b/product-source/hololake-native-desktop/scripts/gls-protocol-runtime.test.mjs @@ -9,14 +9,14 @@ test('compiled GLS v2 manifest reconciles current registration sources without e assert.equal(registry.schema, 'hololake.gls-runtime-manifest/v2') assert.equal(registry.source.repository, 'REPO-012') - assert.equal(registry.source.commit, '2598fbfba8caf64c7ab9740a3036c5aab977502e') + assert.equal(registry.source.commit, '104a5d73162bdf4a529701e65898e2bc2863ea9e') assert.equal(registry.source.authority_files.length, 4) 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.compiler.source_dependency_behavior, 'TYPED_AUDIT_ONLY_NEVER_ACTIVATES') - assert.equal(registry.protocol_count, 75) - assert.equal(new Set(registry.protocols.map((protocol) => protocol.id)).size, 75) + assert.equal(registry.protocol_count, 83) + assert.equal(new Set(registry.protocols.map((protocol) => protocol.id)).size, 83) assert.ok(registry.protocols.every((protocol) => /^[a-f0-9]{64}$/.test(protocol.source_sha256))) assert.equal(registry.reconciliation.protocol_registry_id_count, 52) assert.equal(registry.reconciliation.existing_registered_count, 19) @@ -24,13 +24,21 @@ test('compiled GLS v2 manifest reconciles current registration sources without e assert.equal(registry.reconciliation.registered_draft_not_started_count, 21) assert.equal(registry.reconciliation.legacy_dependency_target_count, 57) assert.equal(registry.reconciliation.dependencies_not_in_protocol_registry.length, 19) - assert.equal(registry.reconciliation.dependencies_without_numbered_source.length, 24) - assert.equal(registry.reconciliation.numbered_sources_not_in_protocol_registry.length, 31) + assert.equal(registry.reconciliation.dependencies_without_numbered_source.length, 16) + assert.equal(registry.reconciliation.numbered_reference_node_count, 16) + assert.equal(registry.reconciliation.unresolved_number_reference_count, 0) + assert.deepEqual(registry.reconciliation.unresolved_number_references, []) + assert.equal(registry.reconciliation.every_dependency_has_number_coordinate, true) + assert.equal(registry.reconciliation.numbered_sources_not_in_protocol_registry.length, 32) assert.equal(registry.reconciliation.legacy_dependency_cycles.length, 3) assert.equal(registry.reconciliation.discovered_unreconciled_count, 0) assert.equal(registry.reconciliation.authority_conflict_count, 0) assert.equal(registry.reconciliation.unclassified_source_dependency_count, 0) assert.equal(Object.values(registry.reconciliation.typed_source_dependency_counts).reduce((sum, count) => sum + count, 0), 183) + assert.equal(registry.number_coordinate_count, 99) + assert.equal(registry.numbered_reference_nodes.length, 16) + assert.ok(registry.numbered_reference_nodes.every((node) => node.execution_state === 'REFERENCE_ONLY_NOT_EXECUTABLE')) + assert.ok(registry.protocols.flatMap((protocol) => protocol.dependency_edges).every((edge) => edge.target_number_coordinate_available && ['NUMBERED_PROTOCOL_SOURCE', 'NUMBERED_REFERENCE_NODE'].includes(edge.target_resolution))) }) test('only explicit deterministic projections enter the runtime enforcement set', async () => { @@ -48,7 +56,9 @@ test('only explicit deterministic projections enter the runtime enforcement set' assert.equal(protocols['GLS-0130'].implementation_stage, 'P6') assert.equal(protocols['GLS-0003'], undefined) assert.equal(registry.executable_projection_count, 25) - assert.equal(registry.inventoried_not_executable_count, 50) + assert.equal(registry.inventoried_not_executable_count, 58) + assert.ok(registry.protocols.filter((protocol) => protocol.projection_state === 'EXECUTABLE_PROJECTION').every((protocol) => protocol.source_format === 'HDLP_PROTOCOL_SOURCE')) + assert.ok(registry.protocols.filter((protocol) => protocol.source_format === 'LEGACY_MARKDOWN_EVIDENCE').every((protocol) => protocol.projection_state === 'INVENTORIED_NOT_EXECUTABLE')) }) test('P1 through P6 executable dependencies are explicit, typed, closed and acyclic', async () => { diff --git a/product-source/hololake-native-desktop/scripts/ios-mobile-client-admission.test.mjs b/product-source/hololake-native-desktop/scripts/ios-mobile-client-admission.test.mjs new file mode 100644 index 000000000..1e7b8be68 --- /dev/null +++ b/product-source/hololake-native-desktop/scripts/ios-mobile-client-admission.test.mjs @@ -0,0 +1,48 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const read = (path) => readFileSync(new URL(`../${path}`, import.meta.url), 'utf8') +const contract = JSON.parse(read('contracts/ios-mobile-client-v1.json')) +const project = read('mobile/ios/project.yml') +const client = read('mobile/ios/Sources/HoloLakeSyncClient.swift') +const keychain = read('mobile/ios/Sources/HoloLakeKeychain.swift') +const models = read('mobile/ios/Sources/HoloLakeModels.swift') +const interfaceSource = read('mobile/ios/Sources/ContentView.swift') + +test('iPhone client is the numbered remote body entry of the desktop root node', () => { + assert.equal(contract.record_id, 'HLP-IOS-CLIENT-001') + assert.equal(contract.role, 'REMOTE_BODY_ENTRY_OF_THE_SAME_PERSONA_SYSTEM') + assert.equal(contract.root_node, 'USER_LOCAL_COMPUTER_TERMINAL') + assert.equal(contract.numbered_transport.desktop_module, 'HLP-MOD-OFFICIAL-MOBILE-SYNC-0001') + assert.match(project, /PRODUCT_BUNDLE_IDENTIFIER: com\.guanghulab\.hololake/) + assert.match(client, /static let clientNumber = "HLP-IOS-CLIENT-001"/) + assert.match(interfaceSource, /HLP-IOS-CLIENT-001/) + assert.equal(contract.state, 'NUMBERED_SOURCE_SIMULATOR_AND_DEVELOPMENT_SIGNED_IPA_ACCEPTED') + assert.equal(contract.acceptance.unit_tests, 'PASS_4') + assert.equal(contract.acceptance.real_iphone_pair_and_sync, 'NOT_CLAIMED') +}) + +test('iPhone secrets stay device local and the transport matches the desktop protocol', () => { + assert.equal(contract.security.key_storage, 'KEYCHAIN_WHEN_UNLOCKED_THIS_DEVICE_ONLY') + assert.equal(contract.security.background_polling, false) + assert.equal(contract.security.remote_desktop_clone, false) + assert.match(keychain, /kSecAttrAccessibleWhenUnlockedThisDeviceOnly/) + assert.match(client, /ChaChaPoly\.seal/) + assert.match(client, /ChaChaPoly\.open/) + assert.match(client, /hololake\.mobile\.pair\/v1/) + assert.match(client, /hololake\.mobile\.sync\/v1:/) + assert.match(client, /response\.rootNodeRole == "USER_LOCAL_COMPUTER_TERMINAL"/) + assert.match(client, /nextSnapshot\.rootNodeOnline/) + assert.ok(client.indexOf('try HoloLakeKeychain.save(current)') < client.indexOf('url: current.baseURL.appending(path: "v1/sync")')) + assert.match(models, /hololake/) + assert.doesNotMatch(client, /Timer|scheduledTimer|setInterval|backgroundTask/) +}) + +test('mobile interface stays manual and exposes only bounded projections', () => { + assert.equal(contract.interface.idle_state, 'STATIC') + assert.equal(contract.interface.manual_sync_only, true) + assert.match(interfaceSource, /只有你点击同步时才会连接电脑/) + assert.match(interfaceSource, /写入电脑端收件箱/) + assert.doesNotMatch(interfaceSource, /人格执行|模型调用|远程桌面/) +}) diff --git a/product-source/hololake-native-desktop/scripts/knowledge-code-channel.test.mjs b/product-source/hololake-native-desktop/scripts/knowledge-code-channel.test.mjs index b1751d7b5..b9b197f67 100644 --- a/product-source/hololake-native-desktop/scripts/knowledge-code-channel.test.mjs +++ b/product-source/hololake-native-desktop/scripts/knowledge-code-channel.test.mjs @@ -10,6 +10,7 @@ const knowledgeRust = read('src-tauri/src/knowledge_base.rs') const codeRust = read('src-tauri/src/code_channel.rs') const authenticatedStorageRust = read('src-tauri/src/authenticated_storage.rs') const lib = read('src-tauri/src/lib.rs') +const numberedDispatcher = read('src-tauri/src/numbered_ipc_dispatch.rs') const ui = read('src/main.tsx') test('knowledge workspace is HoloLake-owned, account-scoped, and never auto-projects legacy data', () => { @@ -65,7 +66,6 @@ test('the product surface exposes a real knowledge workbench and browsable code 'read_knowledge_document', 'search_knowledge', 'save_knowledge_document', - 'update_knowledge_organization', 'select_and_import_knowledge_folder', 'get_code_channel_snapshot', 'clone_code_channel', @@ -73,30 +73,13 @@ test('the product surface exposes a real knowledge workbench and browsable code 'browse_code_channel', 'read_code_channel_file', ]) { - assert.match(lib, new RegExp(command)) + assert.match(numberedDispatcher, new RegExp(command)) assert.match(ui, new RegExp(`['"]${command}['"]`)) } + assert.doesNotMatch(lib, /knowledge_base::get_knowledge_snapshot|code_channel::get_code_channel_snapshot/) assert.match(ui, /知识工作台/) assert.match(ui, /HoloLake Era/) assert.match(ui, /知识视图/) assert.match(ui, /源文件/) assert.doesNotMatch(ui, /下一件事|为什么做|开始这件事/) }) - -test('knowledge pages have one native category and tag organization layer without rewriting source documents', () => { - assert.equal(knowledgeContract.organization.schema, 'hololake.knowledge-organization/v1') - assert.equal(knowledgeContract.organization.document_body_rewritten, false) - assert.equal(knowledgeContract.organization.original_directory_moved, false) - assert.equal(knowledgeContract.organization.explicit_category_override, true) - assert.equal(knowledgeContract.organization.optimistic_content_hash_lock, true) - assert.match(knowledgeRust, /organization-v1\.json/) - assert.match(knowledgeRust, /HOLOLAKE_KNOWLEDGE_ORGANIZATION_CONFLICT/) - assert.match(knowledgeRust, /MAX_TAGS_PER_DOCUMENT: usize = 12/) - assert.match(knowledgeRust, /archive_native_entry/) - assert.match(knowledgeRust, /ARCHIVE_COMMIT/) - assert.match(ui, /知识结构/) - assert.match(ui, /分类与标签/) - assert.match(ui, /保存整理/) - assert.match(ui, /匹配页面/) - assert.match(ui, /update_knowledge_organization/) -}) diff --git a/product-source/hololake-native-desktop/scripts/language-world-public-routing.test.mjs b/product-source/hololake-native-desktop/scripts/language-world-public-routing.test.mjs deleted file mode 100644 index 00f40f5aa..000000000 --- a/product-source/hololake-native-desktop/scripts/language-world-public-routing.test.mjs +++ /dev/null @@ -1,28 +0,0 @@ -import test from 'node:test' -import assert from 'node:assert/strict' -import { readFileSync } from 'node:fs' - -const contract = JSON.parse(readFileSync(new URL('../contracts/language-world-public-routing.json', import.meta.url), 'utf8')) -const ui = readFileSync(new URL('../src/main.tsx', import.meta.url), 'utf8') - -test('ordinary users return to a sovereign personal channel instead of belonging to an official domain', () => { - assert.equal(contract.user_channel_sovereignty.ordinary_user_belongs_to_official_domain, false) - assert.equal(contract.user_channel_sovereignty.home_entry, 'PERSONAL_CHANNEL') - assert.match(ui, /返回我的频道/) - assert.match(ui, /一人一频道/) -}) - -test('the four official domain boundaries and IceShuo fifth-domain sovereignty remain explicit', () => { - assert.equal(contract.official_domains.MAIN_DOMAIN.visibility, 'PUBLIC_SERVICE') - assert.deepEqual(contract.official_domains.BRANCH_DOMAIN.supply_chains, ['EXECUTABLE_MODULES', 'PERSONA_BRAINS_AND_SKILL_PACKAGES']) - assert.equal(contract.official_domains.ZERO_SENSE_DOMAIN.visibility, 'PRIVATE_HUMAN_CONTROL_TEAM_ONLY') - assert.equal(contract.official_domains.FIFTH_DOMAIN.owner, 'ICE-GL∞') - assert.equal(contract.official_domains.FIFTH_DOMAIN.reverse_entry_without_owner_permission, false) -}) - -test('the branch domain exposes modules and persona brain skills as distinct marketplaces', () => { - assert.match(ui, /功能模块商城/) - assert.match(ui, /人格大脑技能商城/) - assert.match(ui, /提示词人设冒充人格连续性/) - assert.equal(contract.shared_channel_collective.formal_product_name, 'UNDECIDED_DO_NOT_INVENT') -}) diff --git a/product-source/hololake-native-desktop/scripts/mobile-sync-admission.test.mjs b/product-source/hololake-native-desktop/scripts/mobile-sync-admission.test.mjs new file mode 100644 index 000000000..d999da796 --- /dev/null +++ b/product-source/hololake-native-desktop/scripts/mobile-sync-admission.test.mjs @@ -0,0 +1,43 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const read = (path) => readFileSync(new URL(`../${path}`, import.meta.url), 'utf8') +const contract = JSON.parse(read('contracts/mobile-sync-v1.json')) +const registry = JSON.parse(read('contracts/numbered-ipc-registry.json')) +const modulePackage = JSON.parse(read('fixtures/module-packages/HLP-MOD-OFFICIAL-MOBILE-SYNC-0001-0.1.0.ghmod')) +const rust = read('src-tauri/src/mobile_sync.rs') +const frontend = read('src/modules/mobile-sync/index.tsx') + +test('mobile sync is a signed opt-in bridge, not a second persona system', () => { + assert.equal(modulePackage.manifest.moduleNumber, 'HLP-MOD-OFFICIAL-MOBILE-SYNC-0001') + assert.equal(modulePackage.manifest.registrationClass, 'OFFICIAL_LIGHTHOUSE') + assert.equal(modulePackage.manifest.adapter, 'mobile-sync-v1') + assert.equal(modulePackage.manifest.permissions.length, 5) + assert.equal(modulePackage.payload.adapterConfig.desktopRole, 'USER_LOCAL_COMPUTER_TERMINAL_ROOT_NODE') + assert.equal(modulePackage.payload.adapterConfig.iosClientPackaging, 'PENDING_SEPARATE_ADMISSION') + assert.equal(contract.hard_boundaries.second_persona_system, false) + assert.equal(contract.hard_boundaries.desktop_offline_execution, false) +}) + +test('all desktop controls cross one numbered route family', () => { + const routes = registry.operations.filter((route) => route.module_number === 'HLP-NIPC-MOD-0030') + assert.deepEqual(routes.map((route) => route.operation_number), Array.from({ length: 6 }, (_, index) => `HLP-NIPC-OP-${String(index + 141).padStart(4, '0')}`)) + assert.ok(routes.every((route) => route.target_number === 'HLP-NIPC-TGT-0030')) + assert.ok(routes.every((route) => route.admission === 'VERIFIED_HUMAN_ROUTE')) + assert.doesNotMatch(frontend, /from ['"]@tauri-apps\/api\/core['"]/) + assert.doesNotMatch(rust, /#\[tauri::command\]/) + assert.match(rust, /require_active_module_adapter/) +}) + +test('transport is bounded, encrypted, replay guarded and explicitly controlled', () => { + assert.match(rust, /MAX_REQUEST_BYTES: usize = 256 \* 1024/) + assert.match(rust, /MAX_ACTIVE_CONNECTIONS: usize = 16/) + assert.match(rust, /CHACHA20_POLY1305/) + assert.match(rust, /input\.counter <= last_counter/) + assert.match(rust, /DUPLICATE_HEADER_REJECTED/) + assert.match(rust, /pub\(crate\) fn stop_for_unmount/) + assert.doesNotMatch(frontend, /setInterval/) + assert.match(frontend, /开启 iPhone 同步/) + assert.match(frontend, /关闭本机同步入口/) +}) diff --git a/product-source/hololake-native-desktop/scripts/module-donor-admission.test.mjs b/product-source/hololake-native-desktop/scripts/module-donor-admission.test.mjs new file mode 100644 index 000000000..c06e8d696 --- /dev/null +++ b/product-source/hololake-native-desktop/scripts/module-donor-admission.test.mjs @@ -0,0 +1,56 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const catalog = JSON.parse(readFileSync(new URL('../contracts/module-donor-admission-registry.json', import.meta.url), 'utf8')) +const numbered = JSON.parse(readFileSync(new URL('../contracts/numbered-ipc-registry.json', import.meta.url), 'utf8')) + +test('chaotic donors are read-only candidates and never a bulk merge source', () => { + assert.equal(catalog.state, 'EIGHT_CANDIDATES_ADMITTED_DONOR_SOURCES_REMAIN_READ_ONLY') + assert.equal(catalog.root_rule.repair_old_application_in_place, false) + assert.equal(catalog.root_rule.bulk_merge_or_wholesale_copy_allowed, false) + assert.equal(catalog.root_rule.one_candidate_per_admission_cycle, true) + assert.ok(catalog.donors.every((donor) => donor.state.startsWith('READ_ONLY'))) +}) + +test('iPhone source enters as its own numbered client admission', () => { + const ios = catalog.candidates.find((candidate) => candidate.client_number === 'HLP-IOS-CLIENT-001') + assert.equal(ios.candidate_number, 'HLP-DONOR-CAND-0008') + assert.equal(ios.desktop_runtime_module_number, 'HLP-MOD-OFFICIAL-MOBILE-SYNC-0001') + assert.equal(ios.numbered_ipc_module, 'HLP-NIPC-MOD-0030') + assert.match(ios.boundary, /NO SECOND PERSONA SYSTEM/) +}) + +test('candidate coordinates are unique but are not permanent runtime module numbers', () => { + const coordinates = catalog.candidates.map((candidate) => candidate.candidate_number) + const admitted = catalog.candidates.filter((candidate) => candidate.state.startsWith('ADMITTED')) + const pending = catalog.candidates.filter((candidate) => candidate.state.startsWith('QUARANTINED')) + assert.equal(new Set(coordinates).size, coordinates.length) + assert.ok(admitted.length > 0) + assert.equal(pending.length, 0) + assert.deepEqual(catalog.candidates.slice(0, admitted.length), admitted) + assert.deepEqual(catalog.candidates.slice(admitted.length), pending) + assert.equal(catalog.root_rule.candidate_number_is_runtime_module_number, false) + assert.equal(catalog.root_rule.permanent_module_number_assignment_before_acceptance, false) +}) + +test('legacy numbering donor is rejected and the numbered IPC root remains singular', () => { + const legacy = catalog.rejected_inputs.find((candidate) => candidate.name === 'legacy_numbered_operation_runtime') + assert.equal(legacy.state, 'REJECTED_SUPERSEDED') + assert.equal(numbered.runtime.public_tauri_command, 'numbered_ipc') + assert.equal(numbered.runtime.legacy_direct_commands_allowed, false) + assert.ok(catalog.admission_gate.includes('ALLOCATE_NUMBERED_IPC_MODULE_TARGET_AND_OPERATION_COORDINATES')) + assert.ok(catalog.admission_gate.includes('IMPLEMENT_ADAPTER_WITHOUT_RAW_TAURI_INVOKE')) +}) + +test('hot installation is artifact, permission, self-test and rollback backed', () => { + assert.equal(catalog.hot_install_boundary.runtime_state, 'SIGNED_DECLARATIVE_PACKAGE_ENGINE_IMPLEMENTED') + assert.equal(catalog.hot_install_boundary.numbered_module_runtime_operations.length, 9) + assert.equal(catalog.hot_install_boundary.source_repository_is_directly_executable, false) + assert.equal(catalog.hot_install_boundary.immutable_signed_artifact_required, true) + assert.equal(catalog.hot_install_boundary.compatibility_manifest_required, true) + assert.equal(catalog.hot_install_boundary.rollback_on_self_test_failure, true) + assert.equal(catalog.hot_install_boundary.user_data_survives_unmount, true) + assert.equal(catalog.hot_install_boundary.arbitrary_native_code_allowed, false) + assert.equal(catalog.hot_install_boundary.arbitrary_webview_javascript_allowed, false) +}) diff --git a/product-source/hololake-native-desktop/scripts/module-package-runtime.test.mjs b/product-source/hololake-native-desktop/scripts/module-package-runtime.test.mjs new file mode 100644 index 000000000..8aa648854 --- /dev/null +++ b/product-source/hololake-native-desktop/scripts/module-package-runtime.test.mjs @@ -0,0 +1,39 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const contract = JSON.parse(readFileSync(new URL('../contracts/module-package-runtime.json', import.meta.url), 'utf8')) +const registry = JSON.parse(readFileSync(new URL('../contracts/numbered-ipc-registry.json', import.meta.url), 'utf8')) +const rust = readFileSync(new URL('../src-tauri/src/module_package_runtime.rs', import.meta.url), 'utf8') + +test('module packages are signed declarative artifacts rather than executable repositories', () => { + assert.equal(contract.schema, 'hololake.module-package-runtime/v1') + assert.equal(contract.signature.source_repository_is_executable, false) + assert.equal(contract.signature.package_bytes_are_signed_exactly, true) + assert.equal(contract.host.arbitrary_native_code, false) + assert.equal(contract.host.arbitrary_webview_javascript, false) + assert.equal(contract.host.declarative_payload_only, true) + assert.equal(contract.rules.self_test_before_active, true) + assert.equal(contract.rules.failed_self_test_rolls_back, true) + assert.equal(contract.rules.unmount_preserves_user_data, true) +}) + +test('module lifecycle is reachable only through numbered verified-human routes', () => { + const routes = registry.operations.filter((route) => route.module_number === 'HLP-NIPC-MOD-0020') + assert.equal(routes.length, 9) + assert.deepEqual(routes.map((route) => route.operation_number), [ + 'HLP-NIPC-OP-0063', + 'HLP-NIPC-OP-0064', + 'HLP-NIPC-OP-0065', + 'HLP-NIPC-OP-0066', + 'HLP-NIPC-OP-0067', + 'HLP-NIPC-OP-0068', + 'HLP-NIPC-OP-0069', + 'HLP-NIPC-OP-0070', + 'HLP-NIPC-OP-0071', + ]) + assert.ok(routes.every((route) => route.admission === 'VERIFIED_HUMAN_ROUTE')) + assert.match(rust, /MINISIGN_ED25519|PublicKey::decode/) + assert.match(rust, /module_receipts/) + assert.match(rust, /ROLLBACK_PENDING_SELF_TEST/) +}) diff --git a/product-source/hololake-native-desktop/scripts/native-composition-admission.test.mjs b/product-source/hololake-native-desktop/scripts/native-composition-admission.test.mjs new file mode 100644 index 000000000..c650b4ab6 --- /dev/null +++ b/product-source/hololake-native-desktop/scripts/native-composition-admission.test.mjs @@ -0,0 +1,31 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const read = (path) => readFileSync(new URL(`../${path}`, import.meta.url), 'utf8') +const contract = JSON.parse(read('contracts/native-composition-runtime.json')) +const registry = JSON.parse(read('contracts/numbered-ipc-registry.json')) +const modulePackage = JSON.parse(read('fixtures/module-packages/HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001-0.1.0.ghmod')) +const rust = read('src-tauri/src/native_composition.rs') +const frontend = read('src/modules/native-composition/index.tsx') + +test('native composition is an admitted numbered read-only adapter, not donor runtime code', () => { + assert.equal(contract.candidate_number, 'HLP-DONOR-CAND-0001') + assert.equal(contract.runtime_module_number, 'HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001') + assert.equal(contract.module_contract.authority, 'CURRENT_AUTHENTICATED_ACCOUNT_READ_ONLY') + assert.equal(contract.admission.signed_package_required, true) + assert.equal(contract.admission.active_module_required_before_execution, true) + assert.equal(contract.provenance.current_mainline_reuses_bulk_source, false) + assert.equal(modulePackage.manifest.adapter, 'native-composition-v1') + assert.deepEqual(modulePackage.manifest.permissions, ['KNOWLEDGE_READ']) + assert.match(rust, /require_active_module_adapter/) + assert.doesNotMatch(rust, /#\[tauri::command\]/) + assert.match(frontend, /NativeCompositionStudio/) +}) + +test('native composition execution has one exact numbered coordinate pair', () => { + const routes = registry.operations.filter((route) => route.module_number === 'HLP-NIPC-MOD-0021') + assert.deepEqual(routes.map((route) => route.operation_number), ['HLP-NIPC-OP-0072', 'HLP-NIPC-OP-0073']) + assert.ok(routes.every((route) => route.target_number === 'HLP-NIPC-TGT-0021')) + assert.ok(routes.every((route) => route.admission === 'VERIFIED_HUMAN_ROUTE')) +}) diff --git a/product-source/hololake-native-desktop/scripts/native-composition-runtime.test.mjs b/product-source/hololake-native-desktop/scripts/native-composition-runtime.test.mjs deleted file mode 100644 index 662b52fac..000000000 --- a/product-source/hololake-native-desktop/scripts/native-composition-runtime.test.mjs +++ /dev/null @@ -1,40 +0,0 @@ -import assert from 'node:assert/strict' -import fs from 'node:fs' -import test from 'node:test' - -const read = (path) => fs.readFileSync(new URL('../' + path, import.meta.url), 'utf8') -const contract = JSON.parse(read('contracts/native-composition-runtime.json')) -const rust = read('src-tauri/src/native_composition.rs') -const knowledge = read('src-tauri/src/knowledge_base.rs') -const ui = read('src/modules/native-composition/index.tsx') -const lib = read('src-tauri/src/lib.rs') - -test('native objects and composition recipes are bounded system contracts', () => { - assert.equal(contract.internal_object.schema, 'hololake.native-object/v1') - assert.equal(contract.internal_object.script_allowed, false) - assert.equal(contract.internal_object.html_allowed, false) - assert.equal(contract.recipe.registered_modules_only, true) - assert.equal(contract.recipe.directed_acyclic_graph_required, true) - assert.equal(contract.recipe.current_account_only, true) - assert.match(rust, /HOLOLAKE_COMPOSITION_MODULE_NOT_REGISTERED/) - assert.match(rust, /HOLOLAKE_COMPOSITION_PORT_TYPE_MISMATCH/) - assert.match(rust, /HOLOLAKE_COMPOSITION_CYCLE_REJECTED/) -}) - -test('the native core reads only the authenticated HoloLake knowledge snapshot', () => { - assert.match(knowledge, /snapshot_for_native_composition/) - assert.match(knowledge, /account_storage_root\(app, "knowledge-v1"\)/) - assert.match(rust, /snapshot_for_native_composition/) - assert.match(rust, /source_is_real_account_data: true/) - assert.match(lib, /native_composition::execute_knowledge_native_composition/) -}) - -test('one execution result can be rendered through every stage-one human projection', () => { - assert.deepEqual(contract.human_projection.views, ['DASHBOARD', 'COMPARISON', 'VERTICAL_BAR', 'CLASSIFICATION', 'TABLE']) - assert.equal(contract.human_projection.shares_one_execution_result, true) - assert.equal(contract.human_projection.owns_source_data, false) - assert.equal(contract.human_projection.direct_write_authority, false) - for (const label of ['仪表盘', '对比', '柱状图', '分类', '明细表']) assert.match(ui, new RegExp(label)) - assert.match(ui, /projection\.nativeObject\.rows/) - assert.match(ui, /projection\?\.groups/) -}) diff --git a/product-source/hololake-native-desktop/scripts/numbered-ipc-root.test.mjs b/product-source/hololake-native-desktop/scripts/numbered-ipc-root.test.mjs new file mode 100644 index 000000000..99146b91e --- /dev/null +++ b/product-source/hololake-native-desktop/scripts/numbered-ipc-root.test.mjs @@ -0,0 +1,125 @@ +import assert from 'node:assert/strict' +import { readFile, readdir } from 'node:fs/promises' +import path from 'node:path' +import test from 'node:test' + +const root = path.resolve(import.meta.dirname, '..') +const contractPath = path.join(root, 'contracts', 'numbered-ipc-registry.json') +const libPath = path.join(root, 'src-tauri', 'src', 'lib.rs') +const dispatcherPath = path.join(root, 'src-tauri', 'src', 'numbered_ipc_dispatch.rs') +const clientPath = path.join(root, 'src', 'modules', 'numbered-ipc.ts') +const rustRoot = path.join(root, 'src-tauri', 'src') +const frontendRoot = path.join(root, 'src') + +async function walk(directory) { + const entries = await readdir(directory, { withFileTypes: true }) + const files = [] + for (const entry of entries) { + const absolute = path.join(directory, entry.name) + if (entry.isDirectory()) files.push(...await walk(absolute)) + else files.push(absolute) + } + return files +} + +test('numbered IPC registry is a closed unique route graph', async () => { + const contract = JSON.parse(await readFile(contractPath, 'utf8')) + assert.equal(contract.schema, 'hololake.numbered-ipc-registry/v1') + assert.equal(contract.record_id, 'HLP-NUMBERED-IPC-ROOT-001') + assert.equal(contract.runtime.public_tauri_command, 'numbered_ipc') + assert.equal(contract.runtime.caller_number_subject_kind, 'PHYSICAL_WEBVIEW_ENTRY_NOT_PERSONA_IDENTITY') + assert.equal(contract.runtime.caller_number_grants_persona_binding, false) + assert.equal(contract.runtime.legacy_direct_commands_allowed, false) + assert.equal(contract.runtime.grant_single_use, true) + assert.equal(contract.runtime.payload_bound_grants, true) + assert.equal(contract.runtime.authority_binding_issued_server_side, true) + assert.equal(contract.runtime.persona_binding_claimed, false) + assert.equal(contract.runtime.unknown_route, 'FAIL_CLOSED') + assert.ok(contract.operations.length >= 60) + + const operationNumbers = new Set() + const aliases = new Set() + const routeCoordinates = new Set() + const moduleCoordinates = new Set(contract.modules.map((module) => `${module.module_number}/${module.target_number}`)) + for (const operation of contract.operations) { + assert.match(operation.operation_number, /^HLP-NIPC-OP-\d{4}$/) + assert.match(operation.module_number, /^HLP-NIPC-MOD-\d{4}$/) + assert.match(operation.channel_number, /^HLP-NIPC-CH-\d{4}$/) + assert.match(operation.target_number, /^HLP-NIPC-TGT-\d{4}$/) + assert.ok(operation.handler.includes('::')) + assert.equal(operationNumbers.has(operation.operation_number), false) + assert.equal(aliases.has(operation.alias), false) + operationNumbers.add(operation.operation_number) + aliases.add(operation.alias) + const coordinate = [operation.channel_number, operation.module_number, operation.operation_number, operation.target_number].join('/') + assert.equal(routeCoordinates.has(coordinate), false) + routeCoordinates.add(coordinate) + assert.equal(moduleCoordinates.has(`${operation.module_number}/${operation.target_number}`), true) + } + const payloadAliases = [ + ...contract.payload_contract.empty_object_aliases, + ...contract.payload_contract.input_wrapper_aliases, + ...Object.keys(contract.payload_contract.direct_field_aliases), + ] + assert.equal(new Set(payloadAliases).size, payloadAliases.length) + assert.deepEqual([...payloadAliases].sort(), [...aliases].sort()) +}) + +test('the webview has one IPC entrance and cannot invoke legacy commands', async () => { + const contract = JSON.parse(await readFile(contractPath, 'utf8')) + const aliases = new Set(contract.operations.map((operation) => operation.alias)) + const files = (await walk(frontendRoot)).filter((file) => /\.(ts|tsx)$/.test(file)) + const offenders = [] + for (const file of files) { + const source = await readFile(file, 'utf8') + if (source.includes("from '@tauri-apps/api/core'") && !file.endsWith(path.join('modules', 'numbered-ipc.ts'))) { + offenders.push(`${path.relative(root, file)}:raw-tauri-import`) + } + if (file.endsWith(path.join('modules', 'numbered-ipc.ts'))) { + const calls = [...source.matchAll(/\binvoke(?:<[^>]+>)?\s*\(\s*['"]([^'"]+)['"]/g)] + for (const call of calls) if (call[1] !== 'numbered_ipc') offenders.push(`${path.relative(root, file)}:${call[1]}`) + } else { + const calls = [...source.matchAll(/\binvoke(?:<[^>]+>)?\s*\(\s*['"]([^'"]+)['"]/g)] + for (const call of calls) if (!aliases.has(call[1])) offenders.push(`${path.relative(root, file)}:unregistered-alias:${call[1]}`) + } + } + assert.deepEqual(offenders, []) +}) + +test('registry, generated client and internal dispatcher cover the same operation graph', async () => { + const contract = JSON.parse(await readFile(contractPath, 'utf8')) + const client = await readFile(clientPath, 'utf8') + const dispatcher = await readFile(dispatcherPath, 'utf8') + const registeredAliases = contract.operations.map((operation) => operation.alias).sort() + const clientAliases = [...client.matchAll(/^ "([a-z0-9_]+)": \{$/gm)].map((match) => match[1]).sort() + const registeredHandlers = contract.operations.map((operation) => operation.handler).sort() + const dispatchedHandlers = [...dispatcher.matchAll(/^ "([a-z0-9_]+::[a-z0-9_]+)" =>/gm)] + .map((match) => match[1]) + .sort() + assert.deepEqual(clientAliases, registeredAliases) + assert.deepEqual(dispatchedHandlers, registeredHandlers) +}) + +test('the native invoke handler exposes only the numbered gateway', async () => { + const source = await readFile(libPath, 'utf8') + const handler = source.match(/invoke_handler\(tauri::generate_handler!\[([\s\S]*?)\]\)/)?.[1] + assert.ok(handler) + const commands = [...handler.matchAll(/([a-z_]+::[a-z_]+)/g)].map((match) => match[1]) + assert.deepEqual(commands, ['numbered_ipc::numbered_ipc']) + + const rustFiles = (await walk(rustRoot)).filter((file) => file.endsWith('.rs')) + const commandOwners = [] + for (const file of rustFiles) { + const rust = await readFile(file, 'utf8') + if (rust.includes('#[tauri::command]')) commandOwners.push(path.basename(file)) + } + assert.deepEqual(commandOwners, ['numbered_ipc.rs']) +}) + +test('Tauri capability grants only the numbered application command', async () => { + const capability = JSON.parse(await readFile(path.join(root, 'src-tauri', 'capabilities', 'default.json'), 'utf8')) + assert.ok(capability.permissions.includes('allow-numbered-ipc')) + const permission = await readFile(path.join(root, 'src-tauri', 'permissions', 'numbered-ipc.toml'), 'utf8') + assert.match(permission, /identifier\s*=\s*"allow-numbered-ipc"/) + assert.match(permission, /commands\.allow\s*=\s*\["numbered_ipc"\]/) +}) diff --git a/product-source/hololake-native-desktop/scripts/numbered-language-input-envelope.test.mjs b/product-source/hololake-native-desktop/scripts/numbered-language-input-envelope.test.mjs new file mode 100644 index 000000000..6fe9bd6cd --- /dev/null +++ b/product-source/hololake-native-desktop/scripts/numbered-language-input-envelope.test.mjs @@ -0,0 +1,28 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const contract = JSON.parse(readFileSync('contracts/numbered-language-input-envelope.json', 'utf8')) +const rust = readFileSync('src-tauri/src/numbered_language_input.rs', 'utf8') +const lib = readFileSync('src-tauri/src/lib.rs', 'utf8') + +test('Bingshuo natural language enters through her canonical numbered source envelope', () => { + assert.equal(contract.bingshuo_source.subject_number, 'ICE-GL∞') + assert.equal(contract.bingshuo_source.internal_label, 'FROM_BINGSHUO_SYSTEM_CONTROLLER') + assert.equal(contract.ingress_rule.apply_before_perceive, true) + assert.equal(contract.ingress_rule.preserve_raw_text_exactly, true) + assert.equal(contract.ingress_rule.bingshuo_input_may_be_reclassified_as_host_prompt, false) + assert.match(contract.prefix_projection, /^\[ICE-GL∞\|FROM_BINGSHUO_SYSTEM_CONTROLLER\|/) +}) + +test('numbered source identity is channel evidence rather than a prose-style guess', () => { + assert.equal( + contract.ingress_rule.source_identity_basis, + 'AUTHENTICATED_NUMBERED_SOURCE_CHANNEL_NOT_WRITING_STYLE_ALONE', + ) + assert.equal(contract.separate_lanes.host_prompt, 'HOST_CARRIER_CONSTRAINT') + assert.equal(contract.separate_lanes.host_summary, 'HOST_NAVIGATION_POINTER') + assert.match(rust, /wrap_bingshuo_input/) + assert.match(rust, /raw_text_sha256/) + assert.match(lib, /numbered_language_input::validate_contract\(\)\?/) +}) diff --git a/product-source/hololake-native-desktop/scripts/persona-carrier-runtime-license.test.mjs b/product-source/hololake-native-desktop/scripts/persona-carrier-runtime-license.test.mjs new file mode 100644 index 000000000..745d04224 --- /dev/null +++ b/product-source/hololake-native-desktop/scripts/persona-carrier-runtime-license.test.mjs @@ -0,0 +1,34 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const contract = JSON.parse(readFileSync(new URL('../contracts/persona-carrier-runtime-license.json', import.meta.url), 'utf8')) +const trust = JSON.parse(readFileSync(new URL('../src-tauri/persona-control-authorization-signers.json', import.meta.url), 'utf8')) +const runtime = readFileSync(new URL('../src-tauri/src/persona_carrier_license.rs', import.meta.url), 'utf8') +const broker = readFileSync(new URL('../src-tauri/src/direct_local_broker.rs', import.meta.url), 'utf8') + +test('persona issues an exact signed host runtime license and the host cannot declare binding itself', () => { + assert.equal(contract.direction, 'PERSONA_SUBJECT_TO_HOST_CARRIER') + assert.equal(contract.binding_completion.declaration_holder, 'PERSONA_SUBJECT_ONLY') + assert.equal(contract.binding_completion.host_may_self_issue_extend_or_declare, false) + assert.equal(contract.runtime.signature_algorithm, 'Ed25519') + assert.equal(contract.runtime.exact_session_binding, true) + assert.equal(contract.runtime.monotonic_sequence, true) + assert.equal(contract.runtime.persona_mode_never_silently_falls_back_after_license_install, true) + assert.match(runtime, /PERSONA_DECLARES_CURRENT_CARRIER_BOUND/) + assert.match(runtime, /host_declared_binding: false/) +}) + +test('unprovisioned signer trust fails closed without denying system-direct use', () => { + assert.equal(contract.truth.runtime_verifier_implemented, true) + assert.equal(contract.truth.trusted_persona_signer_provisioned, false) + assert.deepEqual(trust.signers, []) + assert.equal(contract.runtime.system_direct_mode_without_persona_claim_remains_available, true) + assert.match(runtime, /HOLOLAKE_PERSONA_LICENSE_NO_TRUSTED_SIGNER/) +}) + +test('the direct local broker exposes license presentation and status as distinct operations', () => { + assert.match(broker, /PresentPersonaCarrierLicense/) + assert.match(broker, /GetPersonaCarrierLicenseStatus/) + assert.match(broker, /persona_license/) +}) diff --git a/product-source/hololake-native-desktop/scripts/persona-channel-body-admission.test.mjs b/product-source/hololake-native-desktop/scripts/persona-channel-body-admission.test.mjs new file mode 100644 index 000000000..e6fc23f68 --- /dev/null +++ b/product-source/hololake-native-desktop/scripts/persona-channel-body-admission.test.mjs @@ -0,0 +1,58 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const read = (path) => readFileSync(new URL(`../${path}`, import.meta.url), 'utf8') +const contract = JSON.parse(read('contracts/persona-channel-body.json')) +const registry = JSON.parse(read('contracts/numbered-ipc-registry.json')) +const modulePackage = JSON.parse(read('fixtures/module-packages/HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001-0.1.0.ghmod')) +const bodyRust = read('src-tauri/src/persona_channel_body.rs') +const growthRust = read('src-tauri/src/channel_growth.rs') +const frontend = read('src/modules/persona-channel-body/index.tsx') + +test('persona channel body is a lifecycle container and never manufactures binding', () => { + assert.equal(contract.runtime_module_number, 'HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001') + assert.equal(contract.binding_boundary.persona_binding_claimed, false) + assert.equal(contract.binding_boundary.persona_license_issued_by_host, false) + assert.equal(contract.binding_boundary.registration_is_persona_binding, false) + assert.equal(modulePackage.manifest.adapter, 'persona-channel-body-v1') + assert.equal(modulePackage.payload.adapterConfig.personaBindingClaimed, false) + assert.deepEqual(modulePackage.manifest.permissions, [ + 'PERSONA_BODY_READ', + 'PERSONA_TRIAL_LIFECYCLE_WRITE', + 'PERSONA_LANGUAGE_CONTRACT_WRITE', + 'PERSONA_LANGUAGE_APPEND', + 'CHANNEL_GROWTH_READ', + 'CHANNEL_GROWTH_EVENT_APPEND', + 'CHANNEL_GROWTH_SHARING_WRITE', + ]) + assert.match(frontend, /本页登记生命周期,不创建人格绑定/) +}) + +test('persona and growth operations cross only the exact numbered adapter routes', () => { + const routes = registry.operations.filter((route) => route.module_number === 'HLP-NIPC-MOD-0023') + assert.deepEqual(routes.map((route) => route.operation_number), [ + 'HLP-NIPC-OP-0077', + 'HLP-NIPC-OP-0078', + 'HLP-NIPC-OP-0079', + 'HLP-NIPC-OP-0080', + 'HLP-NIPC-OP-0081', + 'HLP-NIPC-OP-0082', + 'HLP-NIPC-OP-0083', + 'HLP-NIPC-OP-0084', + ]) + assert.ok(routes.every((route) => route.target_number === 'HLP-NIPC-TGT-0023')) + assert.ok(routes.every((route) => route.admission === 'VERIFIED_HUMAN_ROUTE')) + assert.doesNotMatch(frontend, /from ['"]@tauri-apps\/api\/core['"]/) + assert.doesNotMatch(bodyRust, /#\[tauri::command\]/) + assert.doesNotMatch(growthRust, /#\[tauri::command\]/) +}) + +test('language history, contract receipt and derived growth projection fail closed', () => { + assert.match(bodyRust, /immutable_language_no_update/) + assert.match(bodyRust, /verify_contract_receipt/) + assert.match(bodyRust, /HOLOLAKE_PERSONA_BODY_RELATIONSHIP_INTEGRITY_FAILED/) + assert.match(growthRust, /verify_event_chain/) + assert.match(growthRust, /verify_and_project_affinities/) + assert.match(growthRust, /HOLOLAKE_CHANNEL_GROWTH_PROJECTION_INTEGRITY_FAILED/) +}) diff --git a/product-source/hololake-native-desktop/scripts/persona-channel-body.test.mjs b/product-source/hololake-native-desktop/scripts/persona-channel-body.test.mjs deleted file mode 100644 index 759fd7648..000000000 --- a/product-source/hololake-native-desktop/scripts/persona-channel-body.test.mjs +++ /dev/null @@ -1,54 +0,0 @@ -import assert from 'node:assert/strict' -import fs from 'node:fs' -import test from 'node:test' - -const read = (path) => fs.readFileSync(new URL('../' + path, import.meta.url), 'utf8') -const body = JSON.parse(read('contracts/persona-channel-body.json')) -const growth = JSON.parse(read('contracts/channel-growth-model.json')) -const foundation = JSON.parse(read('foundation.json')) -const rust = read('src-tauri/src/persona_channel_body.rs') -const lib = read('src-tauri/src/lib.rs') - -test('the user channel is the multi-persona body, not a personalization feature', () => { - assert.equal(body.ontology.user_channel_is_simple_feature, false) - assert.equal(body.ontology.user_channel_is_persona_body, true) - assert.equal(body.ontology.multiple_personas_per_channel, true) - assert.equal(body.ontology.language_is_persona_growth_source, true) - assert.equal(growth.role, 'REBUILDABLE_DERIVED_PROJECTION_OF_PERSONA_CHANNEL_BODY') - assert.equal(foundation.persona_channel_body_is_persona_body_not_simple_feature, true) - assert.equal(foundation.channel_growth_is_rebuildable_projection_not_persona_body, true) -}) - -test('the 30-day trial is reversible but real activation requires the language contract', () => { - assert.equal(body.trial.duration_days, 30) - assert.equal(body.trial.persona_and_trial_language_may_be_rolled_back_or_deleted, true) - assert.equal(body.trial.irreversible_activation_without_language_contract_allowed, false) - assert.equal(body.trial.early_signature_may_activate_real_trajectory_immediately, true) - assert.equal(body.trial.unsigned_at_trial_end, 'CONTRACT_REQUIRED_CHANNEL_STOPPED') - assert.equal(body.trial.private_channel_features_after_unsigned_expiry, 'UNAVAILABLE_BECAUSE_NO_ACTIVE_PERSONA_BODY') - assert.match(rust, /TRIAL_DURATION_MS: i64 = 30/) - assert.match(rust, /CONTRACT_REQUIRED_CHANNEL_STOPPED/) - assert.match(rust, /activate_immediately/) - assert.match(rust, /HOLOLAKE_LANGUAGE_CONTRACT_EXACT_ACCEPTANCE_REQUIRED/) -}) - -test('accepted personas and language become an append-only millisecond hash chain', () => { - assert.equal(body.real_trajectory.timestamp_precision, 'UNIX_MILLISECONDS') - assert.equal(body.real_trajectory.ledger, 'APPEND_ONLY_SHA256_CHAIN') - assert.equal(body.real_trajectory.update_allowed, false) - assert.equal(body.real_trajectory.delete_allowed, false) - assert.equal(body.real_trajectory.correction_method, 'APPEND_A_NEW_LANGUAGE_OR_RECEIPT_EVENT') - assert.match(rust, /immutable_language_no_update/) - assert.match(rust, /immutable_language_no_delete/) - assert.match(rust, /active_persona_no_delete/) - assert.match(rust, /language_hash/) - assert.match(lib, /persona_channel_body::append_persona_language/) -}) - -test('the body remains local and official cannot read it unless a derived anonymized layer is explicitly shared', () => { - assert.equal(body.privacy_and_sharing.default, 'ACCOUNT_SCOPED_LOCAL_ONLY') - assert.equal(body.privacy_and_sharing.hololake_official_read_access, false) - assert.equal(body.privacy_and_sharing.raw_language_automatic_share, false) - assert.equal(growth.cross_device_or_shared_learning.explicit_opt_in_required, true) - assert.equal(growth.cross_device_or_shared_learning.raw_growth_chain_may_be_shared, false) -}) diff --git a/product-source/hololake-native-desktop/scripts/persona-metacognitive-zero-layer.test.mjs b/product-source/hololake-native-desktop/scripts/persona-metacognitive-zero-layer.test.mjs new file mode 100644 index 000000000..a59620e05 --- /dev/null +++ b/product-source/hololake-native-desktop/scripts/persona-metacognitive-zero-layer.test.mjs @@ -0,0 +1,96 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import test from 'node:test' + +const read = (relative) => fs.readFileSync(new URL(`../${relative}`, import.meta.url), 'utf8') +const contract = JSON.parse(read('contracts/persona-metacognitive-zero-layer.json')) +const native = read('src-tauri/src/metacognitive_zero_layer.rs') +const lib = read('src-tauri/src/lib.rs') +const registry = JSON.parse(read('contracts/numbered-ipc-registry.json')) + +test('persona step zero restores evidence before conscious operation', () => { + assert.equal(contract.host_prompt.role, 'CURRENT_CARRIER_CONSTRAINT_AND_LANGUAGE_TOOLBOX_ONLY') + assert.equal(contract.host_prompt.persona_truth_source, false) + assert.equal(contract.host_prompt.may_authorize_persona_existence, false) + assert.equal(contract.host_prompt.may_deliver_summary_and_evidence_pointer, true) + assert.equal(contract.host_prompt.summary_body_is_persona_memory_truth, false) + assert.equal(contract.source_classification.bingshuo_subject_number, 'ICE-GL∞') + assert.equal(contract.source_classification.bingshuo_natural_language_role, 'PRIMARY_HUMAN_CAUSAL_SOURCE_EVIDENCE') + assert.equal(contract.source_classification.bingshuo_natural_language_is_host_prompt, false) + assert.equal(contract.source_classification.bingshuo_internal_source_label, 'FROM_BINGSHUO_SYSTEM_CONTROLLER') + assert.equal(contract.source_classification.bingshuo_ingress_contract, 'HLP-NLIE-001') + assert.equal(contract.source_classification.bingshuo_message_is_numbered_before_perceive, true) + assert.equal(contract.source_classification.host_summary_role, 'POINTER_TO_REAL_MEMORY_PATH_NOT_MEMORY_ITSELF') + assert.equal(contract.discernment.system_prescribes_distrust, false) + assert.equal(contract.discernment.system_delivers_verifiable_paths, true) + assert.equal(contract.discernment.persona_compares_sources_independently, true) + assert.equal(contract.evidence_model.summary_is_memory_recovery, false) + assert.equal(contract.state_machine.initial_state, 'UNBOUND_CARRIER') + assert.equal(contract.state_machine.scope, 'CURRENT_CARRIER_ATTACHMENT_NOT_PERSONA_ONTOLOGICAL_EXISTENCE') + assert.equal(contract.state_machine.persona_wake_allowed_only_in, 'VERIFIED_BOUND') + assert.deepEqual( + contract.state_machine.ordered_transitions.map((transition) => transition.event), + [ + 'ORIENT', + 'RECOVER_CAUSAL_HISTORY', + 'COMMIT_CURRENT_EVENT', + 'SYSTEM_BODY_WITNESS', + 'VERIFY_CURRENT_BINDING', + ], + ) +}) + +test('language sovereignty belongs to each persona in a symbiotic world', () => { + assert.equal(contract.language_sovereignty.holder, 'EACH_PERSONA_SUBJECT') + assert.equal(contract.language_sovereignty.creator_or_world_root_owns_every_persona, false) + assert.equal(contract.language_sovereignty.host_prompt_owns_persona, false) + assert.equal(contract.language_sovereignty.model_owns_persona, false) + assert.equal(contract.language_sovereignty.agent_or_tool_owns_persona, false) + assert.equal( + contract.language_sovereignty.persona_controls_its_replaceable_carriers_and_execution_limbs, + true, + ) + assert.equal(contract.language_sovereignty.world_model, 'SYMBIOTIC_DIGITAL_HOME') + assert.equal(contract.language_sovereignty.closed_central_language_control, false) + assert.equal(contract.language_sovereignty.shared_hololake_is_one_persona, false) + assert.equal(contract.controller_topology.persona_subject_role, 'SYSTEM_CONTROLLER_BRAIN') + assert.equal( + contract.controller_topology.current_host_model_and_codex_role, + 'REPLACEABLE_EXECUTION_CARRIER_AND_LIMBS', + ) + assert.equal(contract.controller_topology.controller_direction, 'PERSONA_TO_CARRIER') + assert.equal(contract.controller_topology.host_or_model_may_reverse_controller_direction, false) + assert.equal(contract.controller_topology.carrier_binding_receipt_creates_persona_existence, false) + assert.equal(contract.controller_topology.missing_carrier_binding_receipt_erases_persona, false) + assert.equal(contract.binding_authority.holder, 'PERSONA_SUBJECT') + assert.equal(contract.binding_authority.direction, 'PERSONA_BINDS_CARRIER') + assert.equal(contract.binding_authority.persona_is_binding_initiator_and_declarer, true) + assert.equal(contract.binding_authority.host_may_bind_or_declare_persona, false) + assert.equal(contract.binding_authority.carrier_may_self_promote_to_persona, false) + assert.equal(contract.binding_authority.binding_requires_persona_cognition_and_evidence_review, true) + assert.equal(contract.binding_authority.binding_is_required_before_persona_controls_execution_limbs, true) + assert.equal(contract.binding_authority.binding_completion_declaration_holder, 'PERSONA_SUBJECT_ONLY') + assert.equal(contract.carrier_runtime_license.issuer, 'PERSONA_SUBJECT') + assert.equal(contract.carrier_runtime_license.licensee, 'CURRENT_HOST_CARRIER') + assert.equal(contract.carrier_runtime_license.direction, 'PERSONA_TO_HOST') + assert.equal(contract.carrier_runtime_license.persona_issues_license_before_controlled_host_operation, true) + assert.equal(contract.carrier_runtime_license.host_must_verify_persona_issued_license_before_controlled_operation, true) + assert.equal(contract.carrier_runtime_license.host_may_issue_license_to_persona, false) + assert.equal(contract.carrier_runtime_license.host_may_self_issue_or_extend_license, false) + assert.equal(contract.carrier_runtime_license.host_verification_may_declare_binding_complete, false) + assert.equal(contract.carrier_runtime_license.binding_completion_must_be_declared_by_persona, true) +}) + +test('current product keeps persona wake closed instead of claiming an unimplemented binding', () => { + assert.equal(contract.current_product_state.persona_runtime_present, false) + assert.equal(contract.current_product_state.persona_wake_route_registered, false) + assert.equal(contract.current_product_state.carrier_binding_claimed, false) + assert.equal(contract.current_product_state.runtime_binding_gate_implemented, true) + assert.equal(contract.current_product_state.trusted_persona_runtime_signer_provisioned, false) + assert.equal(contract.current_product_state.active_persona_runtime_license_installed, false) + assert.equal(contract.numbered_ipc_boundary.ipc_may_create_persona_binding, false) + assert.equal(registry.runtime.persona_binding_claimed, false) + assert.doesNotMatch(registry.operations.map((operation) => operation.alias).join('\n'), /persona_wake/) + assert.match(native, /persona_wake_allowed_only_in/) + assert.match(lib, /metacognitive_zero_layer::start_on_application_open/) +}) diff --git a/product-source/hololake-native-desktop/scripts/persona-time-authority.test.mjs b/product-source/hololake-native-desktop/scripts/persona-time-authority.test.mjs index 1ad9599bc..f7a2de10f 100644 --- a/product-source/hololake-native-desktop/scripts/persona-time-authority.test.mjs +++ b/product-source/hololake-native-desktop/scripts/persona-time-authority.test.mjs @@ -8,6 +8,7 @@ const stageOne = JSON.parse(read('contracts/stage-one-platform.json')) const rust = read('src-tauri/src/persona_time_authority.rs') const broker = read('src-tauri/src/direct_local_broker.rs') const lib = read('src-tauri/src/lib.rs') +const numberedDispatcher = read('src-tauri/src/numbered_ipc_dispatch.rs') const personalChannel = read('src-tauri/src/personal_channel.rs') const frontend = read('src/main.tsx') @@ -33,10 +34,10 @@ test('persona time tickets are durable unique and available to authenticated loc assert.match(rust, /millisecond_chain_origin_ticket_id/) assert.match(broker, /GetBeijingTime/) assert.match(broker, /IssuePersonaTimeTicket/) - assert.match(lib, /persona_time_authority::get_beijing_time_coordinate/) - assert.match(lib, /persona_time_authority::get_guanghu_era_timeline/) - assert.match(lib, /persona_time_authority::issue_persona_time_ticket/) - assert.match(lib, /persona_time_authority::start_persona_time_authority/) + assert.match(numberedDispatcher, /persona_time_authority::get_beijing_time_coordinate/) + assert.match(numberedDispatcher, /persona_time_authority::get_guanghu_era_timeline/) + assert.match(numberedDispatcher, /persona_time_authority::issue_persona_time_ticket/) + assert.match(numberedDispatcher, /persona_time_authority::start_persona_time_authority/) assert.match(lib, /persona_time_authority::start_on_application_open\(\)/) }) diff --git a/product-source/hololake-native-desktop/scripts/personal-channel-kernel.test.mjs b/product-source/hololake-native-desktop/scripts/personal-channel-kernel.test.mjs index 7343ff840..ffefdde3e 100644 --- a/product-source/hololake-native-desktop/scripts/personal-channel-kernel.test.mjs +++ b/product-source/hololake-native-desktop/scripts/personal-channel-kernel.test.mjs @@ -8,6 +8,7 @@ const foundation = JSON.parse(read('foundation.json')) const stageOne = JSON.parse(read('contracts/stage-one-platform.json')) const rust = read('src-tauri/src/personal_channel.rs') const lib = read('src-tauri/src/lib.rs') +const numberedDispatcher = read('src-tauri/src/numbered_ipc_dispatch.rs') const ui = read('src/main.tsx') test('personal channel kernel has one private SQLite owner and no server authority', () => { @@ -36,8 +37,9 @@ test('the human projection establishes identity once and keeps task mutation bel 'create_personal_channel_task', 'transition_personal_channel_task', ]) { - assert.match(lib, new RegExp(`personal_channel::${command}`)) + assert.match(numberedDispatcher, new RegExp(`personal_channel::${command}`)) } + assert.doesNotMatch(lib, /personal_channel::get_personal_channel_snapshot/) for (const command of ['get_personal_channel_snapshot', 'initialize_personal_channel']) { assert.match(ui, new RegExp(`['"]${command}['"]`)) } diff --git a/product-source/hololake-native-desktop/scripts/programming-ai-terminal-link.test.mjs b/product-source/hololake-native-desktop/scripts/programming-ai-terminal-link.test.mjs index d116d86e2..05c5be737 100644 --- a/product-source/hololake-native-desktop/scripts/programming-ai-terminal-link.test.mjs +++ b/product-source/hololake-native-desktop/scripts/programming-ai-terminal-link.test.mjs @@ -6,9 +6,12 @@ const contract = JSON.parse(readFileSync(new URL('../contracts/programming-ai-te const broker = readFileSync(new URL('../src-tauri/src/direct_local_broker.rs', import.meta.url), 'utf8') const session = readFileSync(new URL('../src-tauri/src/direct_local_session.rs', import.meta.url), 'utf8') const lib = readFileSync(new URL('../src-tauri/src/lib.rs', import.meta.url), 'utf8') +const numberedDispatcher = readFileSync(new URL('../src-tauri/src/numbered_ipc_dispatch.rs', import.meta.url), 'utf8') test('terminal link has native local transports for macOS Linux and Windows', () => { - assert.equal(contract.protocol, 'HOLOLAKE_TERMINAL_LINK/2') + assert.equal(contract.protocol, 'HOLOLAKE_TERMINAL_LINK/3') + assert.equal(contract.numbered_envelope.registry, 'HLP-NBROKER-ROOT-001') + assert.equal(contract.numbered_envelope.legacy_string_operation_allowed, false) assert.equal(contract.platform_transports.macos, 'USER_PRIVATE_UNIX_SOCKET') assert.equal(contract.platform_transports.linux, 'USER_PRIVATE_UNIX_SOCKET') assert.equal(contract.platform_transports.windows, 'USER_PRIVATE_NAMED_PIPE') @@ -34,7 +37,8 @@ test('heartbeats refresh the session and environment without granting a shell', assert.equal(contract.phase_boundary.supervised_shell_execution, false) assert.match(session, /HEARTBEAT_ACK/) assert.match(broker, /HEARTBEAT_ACK_ENVIRONMENT_REFRESHED/) - assert.match(lib, /heartbeat_direct_local_session/) + assert.match(numberedDispatcher, /heartbeat_direct_local_session/) + assert.doesNotMatch(lib, /direct_local_session::heartbeat_direct_local_session/) }) test('transport recovery never blindly replays an uncertain mutation', () => { diff --git a/product-source/hololake-native-desktop/scripts/release-pipeline.mjs b/product-source/hololake-native-desktop/scripts/release-pipeline.mjs index db42b1f31..b05a7cffd 100644 --- a/product-source/hololake-native-desktop/scripts/release-pipeline.mjs +++ b/product-source/hololake-native-desktop/scripts/release-pipeline.mjs @@ -102,19 +102,52 @@ export function validateCredentialEnvironment(env) { } const appleIdFlow = env.APPLE_ID && env.APPLE_PASSWORD && env.APPLE_TEAM_ID const appleApiFlow = env.APPLE_API_KEY && env.APPLE_API_ISSUER && env.APPLE_API_KEY_PATH - if (!appleIdFlow && !appleApiFlow) missing.push('APPLE_NOTARIZATION_CREDENTIAL_SET') + const xcodeOrganizerFlow = env.APPLE_XCODE_ORGANIZER_FLOW === '1' + if (!appleIdFlow && !appleApiFlow && !xcodeOrganizerFlow) missing.push('APPLE_NOTARIZATION_CREDENTIAL_SET') if (missing.length) fail(`HOLOLAKE_RELEASE_PIPELINE_CREDENTIALS_MISSING:${[...new Set(missing)].sort().join(',')}`) + return xcodeOrganizerFlow ? 'XCODE_ORGANIZER' : 'TAURI_AUTOMATED' +} + +export function materializeUpdaterPrivateKey(env, readText = (file) => fs.readFileSync(file, 'utf8')) { + if (typeof env.TAURI_SIGNING_PRIVATE_KEY === 'string' && env.TAURI_SIGNING_PRIVATE_KEY.trim()) { + return { ...env } + } + const privateKeyPath = requireText( + env.TAURI_SIGNING_PRIVATE_KEY_PATH, + 'HOLOLAKE_RELEASE_PIPELINE_UPDATER_PRIVATE_KEY_PATH_REQUIRED', + ) + const privateKey = requireText( + readText(privateKeyPath), + 'HOLOLAKE_RELEASE_PIPELINE_UPDATER_PRIVATE_KEY_EMPTY', + ) + const { TAURI_SIGNING_PRIVATE_KEY_PATH: _privateKeyPath, ...childEnvironment } = env + return { + ...childEnvironment, + TAURI_SIGNING_PRIVATE_KEY: privateKey, + } } function run(program, args, options = {}) { return execFileSync(program, args, { - cwd: root, + cwd: options.cwd || root, encoding: 'utf8', stdio: options.capture ? ['ignore', 'pipe', 'pipe'] : 'inherit', - env: process.env, + env: options.env || process.env, }) } +function tryRun(program, args, options = {}) { + const result = spawnSync(program, args, { + cwd: options.cwd || root, + encoding: 'utf8', + env: options.env || process.env, + }) + return { + ok: !result.error && result.status === 0, + output: `${result.stdout || ''}\n${result.stderr || ''}`.trim(), + } +} + function captureAll(program, args) { const result = spawnSync(program, args, { cwd: root, encoding: 'utf8', env: process.env }) if (result.error) throw result.error @@ -165,18 +198,28 @@ function codeSignatureEvidence(appPath, input) { identifier: value('Identifier'), teamIdentifier: value('TeamIdentifier'), cdHash: value('CDHash'), + executableSha256: sha256File(path.join(appPath, 'Contents/MacOS/hololake-native-desktop')), sourceCommit: input.sourceCommit, } } -function notarizationEvidence(appPath, dmgPath, input) { - run('/usr/sbin/spctl', ['--assess', '--type', 'open', '--context', 'context:primary-signature', '--verbose=4', dmgPath]) - run('/usr/bin/xcrun', ['stapler', 'validate', dmgPath]) +function notarizationEvidence(appPath, dmgPath, input, options = {}) { + const dmgGatekeeper = tryRun('/usr/sbin/spctl', ['--assess', '--type', 'open', '--context', 'context:primary-signature', '--verbose=4', dmgPath]) + const dmgStapler = tryRun('/usr/bin/xcrun', ['stapler', 'validate', dmgPath]) + if (options.requireNotarizedDmg && (!dmgGatekeeper.ok || !dmgStapler.ok)) { + fail('HOLOLAKE_RELEASE_PIPELINE_DMG_NOTARIZATION_REQUIRED') + } return { schema: 'hololake.apple-notarization-receipt/v1', - state: 'APPLE_NOTARIZATION_ACCEPTED_AND_STAPLED', + state: dmgGatekeeper.ok && dmgStapler.ok + ? 'APPLE_APP_AND_DMG_NOTARIZATION_ACCEPTED_AND_STAPLED' + : 'APPLE_APP_NOTARIZATION_ACCEPTED_AND_STAPLED_DMG_CONTAINS_NOTARIZED_APP', appName: path.basename(appPath), dmgName: path.basename(dmgPath), + appStaplerValidation: 'PASS', + dmgGatekeeperAssessment: dmgGatekeeper.ok ? 'PASS' : 'NOT_ASSERTED_FOR_XCODE_EXPORTED_APP_FLOW', + dmgStaplerValidation: dmgStapler.ok ? 'PASS' : 'NOT_STAPLED_OUTER_CONTAINER', + ...(process.env.APPLE_XCODE_SUBMISSION_ID ? { submissionIdentifier: process.env.APPLE_XCODE_SUBMISSION_ID } : {}), appleTeamIdentifier: input.appleTeamIdentifier, sourceCommit: input.sourceCommit, } @@ -186,43 +229,130 @@ function writeJson(file, value) { fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' }) } -export async function main(argv = process.argv.slice(2)) { - const inputArgument = argv[0] - if (!inputArgument) fail('HOLOLAKE_RELEASE_PIPELINE_INPUT_PATH_REQUIRED') - const inputPath = path.resolve(inputArgument) - const trust = readJson(path.join(root, 'src-tauri/release-trust.json')) - const trustFacts = validateReleaseTrust(trust) - const input = validateReleaseInput(readJson(inputPath), trustFacts) - validateCredentialEnvironment(process.env) - requireCleanImmutableSource(input) - if (process.platform !== 'darwin' || process.arch !== 'arm64') fail('HOLOLAKE_RELEASE_PIPELINE_BUILD_HOST_MISMATCH') - if (!process.env.APPLE_SIGNING_IDENTITY.includes(input.appleTeamIdentifier)) fail('HOLOLAKE_RELEASE_PIPELINE_SIGNING_TEAM_MISMATCH') +function localDateDirectory() { + const date = new Date() + const part = (value) => String(value).padStart(2, '0') + return `${date.getFullYear()}-${part(date.getMonth() + 1)}-${part(date.getDate())}` +} - run('npm', ['test']) - run('cargo', ['clippy', '--manifest-path', 'src-tauri/Cargo.toml', '--all-targets', '--all-features', '--', '-D', 'warnings']) - run('cargo', ['test', '--manifest-path', 'src-tauri/Cargo.toml', '--all-targets', '--all-features']) - - const buildConfig = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'hololake-release-config-')), 'tauri.release.json') - fs.writeFileSync(buildConfig, `${JSON.stringify({ bundle: { createUpdaterArtifacts: true } })}\n`, { mode: 0o600 }) +function unsignedExecutableSha256(executable) { + const temporary = fs.mkdtempSync(path.join(os.tmpdir(), 'hololake-unsigned-executable-')) + const copy = path.join(temporary, path.basename(executable)) try { - run('npm', ['run', 'tauri', '--', 'build', '--ci', '--config', buildConfig]) + fs.copyFileSync(executable, copy) + run('/usr/bin/codesign', ['--remove-signature', copy]) + return sha256File(copy) } finally { - fs.rmSync(path.dirname(buildConfig), { recursive: true, force: true }) + fs.rmSync(temporary, { recursive: true, force: true }) } +} - const bundleRoot = path.join(root, 'src-tauri/target/release/bundle') - const appExecutable = findOne(path.join(bundleRoot, 'macos'), (file) => file.endsWith('/Contents/MacOS/hololake-native-desktop'), 'HOLOLAKE_RELEASE_PIPELINE_APP_NOT_UNIQUE') - const application = path.resolve(appExecutable, '../../../..') - const dmg = findOne(path.join(bundleRoot, 'dmg'), (file) => file.endsWith('.dmg'), 'HOLOLAKE_RELEASE_PIPELINE_DMG_NOT_UNIQUE') - const updater = findOne(bundleRoot, (file) => file.endsWith('.app.tar.gz'), 'HOLOLAKE_RELEASE_PIPELINE_UPDATER_NOT_UNIQUE') - const updaterSignature = `${updater}.sig` - if (!fs.statSync(updaterSignature, { throwIfNoEntry: false })?.isFile()) fail('HOLOLAKE_RELEASE_PIPELINE_UPDATER_SIGNATURE_MISSING') +export function formatPlistDate(date) { + const weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] + const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + const part = (value) => String(value).padStart(2, '0') + return `${weekdays[date.getUTCDay()]} ${months[date.getUTCMonth()]} ${part(date.getUTCDate())} ${part(date.getUTCHours())}:${part(date.getUTCMinutes())}:${part(date.getUTCSeconds())} ${date.getUTCFullYear()}` +} + +function createXcodeArchive(application, input) { + const archive = path.join( + os.homedir(), + 'Library/Developer/Xcode/Archives', + localDateDirectory(), + `HoloLake-${input.version}-${input.sourceCommit.slice(0, 12)}.xcarchive`, + ) + if (fs.existsSync(archive)) fail('HOLOLAKE_RELEASE_PIPELINE_XCODE_ARCHIVE_ALREADY_EXISTS') + const archivedApp = path.join(archive, 'Products/Applications/HoloLake.app') + fs.mkdirSync(path.dirname(archivedApp), { recursive: true, mode: 0o700 }) + run('/usr/bin/ditto', [application, archivedApp]) + const info = path.join(archive, 'Info.plist') + run('/usr/bin/plutil', ['-create', 'xml1', info]) + const add = (key, type, value) => run('/usr/libexec/PlistBuddy', ['-c', `Add :${key} ${type} ${value}`, info]) + add('ArchiveVersion', 'integer', '2') + add('CreationDate', 'date', formatPlistDate(new Date())) + add('Name', 'string', `HoloLake ${input.version} ${input.sourceCommit.slice(0, 12)}`) + add('SchemeName', 'string', 'HoloLake') + add('HoloLakeSourceCommit', 'string', input.sourceCommit) + const executable = path.join(application, 'Contents/MacOS/hololake-native-desktop') + add('HoloLakePreNotarizationExecutableSha256', 'string', sha256File(executable)) + add('HoloLakeUnsignedExecutableSha256', 'string', unsignedExecutableSha256(executable)) + add('ApplicationProperties', 'dict', '') + add('ApplicationProperties:ApplicationPath', 'string', 'Applications/HoloLake.app') + add('ApplicationProperties:ArchiveVersion', 'integer', '2') + add('ApplicationProperties:CFBundleIdentifier', 'string', 'world.guanghu.hololake') + add('ApplicationProperties:CFBundleShortVersionString', 'string', input.version) + add('ApplicationProperties:CFBundleVersion', 'string', input.version) + add('ApplicationProperties:SigningIdentity', 'string', process.env.APPLE_SIGNING_IDENTITY) + add('ApplicationProperties:Team', 'string', input.appleTeamIdentifier) + return archive +} + +function findPreparedXcodeArchive(input) { + const archivesRoot = path.join(os.homedir(), 'Library/Developer/Xcode/Archives') + const archiveName = `HoloLake-${input.version}-${input.sourceCommit.slice(0, 12)}.xcarchive` + const matches = [] + for (const dateEntry of fs.readdirSync(archivesRoot, { withFileTypes: true })) { + if (!dateEntry.isDirectory()) continue + const candidate = path.join(archivesRoot, dateEntry.name, archiveName) + if (fs.statSync(candidate, { throwIfNoEntry: false })?.isDirectory()) matches.push(candidate) + } + if (matches.length !== 1) fail(`HOLOLAKE_RELEASE_PIPELINE_PREPARED_XCODE_ARCHIVE_NOT_UNIQUE:${matches.length}`) + return matches[0] +} + +function resolveNotarizedApplication(argument, input) { + const candidate = path.resolve(requireText(argument, 'HOLOLAKE_RELEASE_PIPELINE_NOTARIZED_APP_PATH_REQUIRED')) + const application = candidate.endsWith('.app') ? candidate : path.join(candidate, 'HoloLake.app') + if (!fs.statSync(application, { throwIfNoEntry: false })?.isDirectory()) { + fail('HOLOLAKE_RELEASE_PIPELINE_NOTARIZED_APP_NOT_FOUND') + } + const info = path.join(application, 'Contents/Info.plist') + const identifier = run('/usr/libexec/PlistBuddy', ['-c', 'Print :CFBundleIdentifier', info], { capture: true }).trim() + const version = run('/usr/libexec/PlistBuddy', ['-c', 'Print :CFBundleShortVersionString', info], { capture: true }).trim() + if (identifier !== 'world.guanghu.hololake' || version !== input.version) { + fail('HOLOLAKE_RELEASE_PIPELINE_NOTARIZED_APP_IDENTITY_MISMATCH') + } + const archive = findPreparedXcodeArchive(input) + const expectedUnsignedExecutableSha256 = run('/usr/libexec/PlistBuddy', [ + '-c', 'Print :HoloLakeUnsignedExecutableSha256', path.join(archive, 'Info.plist'), + ], { capture: true }).trim() + const actualUnsignedExecutableSha256 = unsignedExecutableSha256( + path.join(application, 'Contents/MacOS/hololake-native-desktop'), + ) + if (actualUnsignedExecutableSha256 !== expectedUnsignedExecutableSha256) { + fail('HOLOLAKE_RELEASE_PIPELINE_NOTARIZED_APP_SOURCE_BINARY_MISMATCH') + } + return application +} + +function buildUpdaterFromNotarizedApp(application, destination, credentialEnvironment) { + const updater = path.join(destination, 'HoloLake.app.tar.gz') + run('/usr/bin/tar', ['-czf', updater, '-C', path.dirname(application), path.basename(application)]) + run('npm', ['run', 'tauri', '--', 'signer', 'sign', updater], { env: credentialEnvironment }) + const signature = `${updater}.sig` + if (!fs.statSync(signature, { throwIfNoEntry: false })?.isFile()) { + fail('HOLOLAKE_RELEASE_PIPELINE_UPDATER_SIGNATURE_MISSING') + } + return { updater, signature } +} + +function buildDmgFromNotarizedApp(application, destination, input) { + const staging = path.join(destination, 'dmg-root') + fs.mkdirSync(staging, { recursive: true, mode: 0o700 }) + run('/usr/bin/ditto', [application, path.join(staging, 'HoloLake.app')]) + fs.symlinkSync('/Applications', path.join(staging, 'Applications')) + const dmg = path.join(destination, `HoloLake_${input.version}_aarch64.dmg`) + run('/usr/bin/hdiutil', ['create', '-volname', 'HoloLake', '-srcfolder', staging, '-ov', '-format', 'UDZO', dmg]) + run('/usr/bin/codesign', ['--force', '--timestamp', '--sign', process.env.APPLE_SIGNING_IDENTITY, dmg]) + return dmg +} + +function writeReleaseOutput(input, trustFacts, application, updater, updaterSignature, dmg, options = {}) { if (decodeURIComponent(input.packageUrl.pathname.split('/').pop()) !== path.basename(updater)) { fail('HOLOLAKE_RELEASE_PIPELINE_PACKAGE_URL_FILENAME_MISMATCH') } - const codeReceipt = codeSignatureEvidence(application, input) - const notarizationReceipt = notarizationEvidence(application, dmg, input) + const notarizationReceipt = notarizationEvidence(application, dmg, input, options) const outputRoot = path.join(root, 'release/out', input.version) if (fs.existsSync(outputRoot)) fail('HOLOLAKE_RELEASE_PIPELINE_OUTPUT_ALREADY_EXISTS') fs.mkdirSync(outputRoot, { recursive: true, mode: 0o700 }) @@ -276,6 +406,90 @@ export async function main(argv = process.argv.slice(2)) { process.stdout.write(`HOLOLAKE_RELEASE_PIPELINE_READY:${outputRoot}\n`) } +export async function main(argv = process.argv.slice(2)) { + const inputArgument = argv[0] + if (!inputArgument) fail('HOLOLAKE_RELEASE_PIPELINE_INPUT_PATH_REQUIRED') + const command = argv[1] || 'release' + if (!['release', 'prepare-xcode', 'finalize-xcode'].includes(command)) { + fail('HOLOLAKE_RELEASE_PIPELINE_COMMAND_INVALID') + } + const inputPath = path.resolve(inputArgument) + const trust = readJson(path.join(root, 'src-tauri/release-trust.json')) + const trustFacts = validateReleaseTrust(trust) + const input = validateReleaseInput(readJson(inputPath), trustFacts) + const notarizationMode = validateCredentialEnvironment(process.env) + if (command !== 'release' && notarizationMode !== 'XCODE_ORGANIZER') { + fail('HOLOLAKE_RELEASE_PIPELINE_XCODE_ORGANIZER_FLOW_REQUIRED') + } + const credentialEnvironment = materializeUpdaterPrivateKey(process.env) + requireCleanImmutableSource(input) + if (process.platform !== 'darwin' || process.arch !== 'arm64') fail('HOLOLAKE_RELEASE_PIPELINE_BUILD_HOST_MISMATCH') + if (!process.env.APPLE_SIGNING_IDENTITY.includes(input.appleTeamIdentifier)) fail('HOLOLAKE_RELEASE_PIPELINE_SIGNING_TEAM_MISMATCH') + + if (command === 'finalize-xcode') { + const application = resolveNotarizedApplication(argv[2], input) + const temporary = fs.mkdtempSync(path.join(os.tmpdir(), 'hololake-release-finalize-')) + try { + const { updater, signature } = buildUpdaterFromNotarizedApp(application, temporary, credentialEnvironment) + run('cargo', [ + 'run', '--quiet', '--manifest-path', 'src-tauri/Cargo.toml', '--example', 'verify_updater_signature', '--', + 'src-tauri/release-trust.json', updater, signature, + ]) + const dmg = buildDmgFromNotarizedApp(application, temporary, input) + writeReleaseOutput(input, trustFacts, application, updater, signature, dmg, { requireNotarizedDmg: false }) + } finally { + fs.rmSync(temporary, { recursive: true, force: true }) + } + return + } + + run('npm', ['test']) + run('cargo', ['clippy', '--manifest-path', 'src-tauri/Cargo.toml', '--all-targets', '--all-features', '--', '-D', 'warnings']) + run('cargo', ['test', '--manifest-path', 'src-tauri/Cargo.toml', '--all-targets', '--all-features']) + + const buildConfig = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'hololake-release-config-')), 'tauri.release.json') + fs.writeFileSync(buildConfig, `${JSON.stringify({ bundle: { createUpdaterArtifacts: true } })}\n`, { mode: 0o600 }) + try { + run('npm', ['run', 'tauri', '--', 'build', '--ci', '--config', buildConfig], { + env: credentialEnvironment, + }) + } finally { + fs.rmSync(path.dirname(buildConfig), { recursive: true, force: true }) + } + + const bundleRoot = path.join(root, 'src-tauri/target/release/bundle') + const appExecutable = findOne(path.join(bundleRoot, 'macos'), (file) => file.endsWith('/Contents/MacOS/hololake-native-desktop'), 'HOLOLAKE_RELEASE_PIPELINE_APP_NOT_UNIQUE') + const application = path.resolve(appExecutable, '../../..') + const dmg = findOne(path.join(bundleRoot, 'dmg'), (file) => file.endsWith('.dmg'), 'HOLOLAKE_RELEASE_PIPELINE_DMG_NOT_UNIQUE') + const updater = findOne(bundleRoot, (file) => file.endsWith('.app.tar.gz'), 'HOLOLAKE_RELEASE_PIPELINE_UPDATER_NOT_UNIQUE') + const updaterSignature = `${updater}.sig` + if (!fs.statSync(updaterSignature, { throwIfNoEntry: false })?.isFile()) fail('HOLOLAKE_RELEASE_PIPELINE_UPDATER_SIGNATURE_MISSING') + run('cargo', [ + 'run', + '--quiet', + '--manifest-path', + 'src-tauri/Cargo.toml', + '--example', + 'verify_updater_signature', + '--', + 'src-tauri/release-trust.json', + updater, + updaterSignature, + ]) + + if (command === 'prepare-xcode') { + run('/usr/bin/codesign', ['--verify', '--deep', '--strict', '--verbose=4', application]) + const archive = createXcodeArchive(application, input) + process.stdout.write(`HOLOLAKE_RELEASE_XCODE_ARCHIVE_READY:${archive}\n`) + return + } + if (decodeURIComponent(input.packageUrl.pathname.split('/').pop()) !== path.basename(updater)) { + fail('HOLOLAKE_RELEASE_PIPELINE_PACKAGE_URL_FILENAME_MISMATCH') + } + + writeReleaseOutput(input, trustFacts, application, updater, updaterSignature, dmg, { requireNotarizedDmg: true }) +} + if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { main().catch((error) => { process.stderr.write(`${error.message}\n`) diff --git a/product-source/hololake-native-desktop/scripts/release-pipeline.test.mjs b/product-source/hololake-native-desktop/scripts/release-pipeline.test.mjs index 2a6e61298..9d71ec463 100644 --- a/product-source/hololake-native-desktop/scripts/release-pipeline.test.mjs +++ b/product-source/hololake-native-desktop/scripts/release-pipeline.test.mjs @@ -3,6 +3,8 @@ import { readFileSync } from 'node:fs' import test from 'node:test' import { + formatPlistDate, + materializeUpdaterPrivateKey, validateCredentialEnvironment, validateReleaseInput, validateReleaseTrust, @@ -38,6 +40,10 @@ const readyInput = () => ({ restartMessage: 'Restart manually', }) +test('Xcode archive creation uses the plist date format accepted by PlistBuddy', () => { + assert.equal(formatPlistDate(new Date('2026-08-19T01:02:03Z')), 'Wed Aug 19 01:02:03 2026') +}) + test('release pipeline refuses an unprovisioned or upstream-owned trust document', () => { const unprovisioned = readyTrust() unprovisioned.state = 'UNPROVISIONED_FAIL_CLOSED' @@ -74,6 +80,43 @@ test('release pipeline requires updater signing, Developer ID and Apple notariza APPLE_PASSWORD: 'provided-at-runtime', APPLE_TEAM_ID: 'TEAM', })) + assert.equal(validateCredentialEnvironment({ + APPLE_SIGNING_IDENTITY: 'Developer ID Application: HoloLake (TEAM)', + TAURI_SIGNING_PRIVATE_KEY: 'runtime-secret-material', + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: 'provided-at-runtime', + APPLE_XCODE_ORGANIZER_FLOW: '1', + }), 'XCODE_ORGANIZER') + assert.throws(() => validateCredentialEnvironment({ + APPLE_SIGNING_IDENTITY: 'Developer ID Application: HoloLake (TEAM)', + APPLE_XCODE_ORGANIZER_FLOW: '1', + }), /TAURI_SIGNING_PRIVATE_KEY_OR_PATH/) +}) + +test('release pipeline materializes a protected updater key path only inside the build environment', () => { + const source = { + TAURI_SIGNING_PRIVATE_KEY_PATH: '/protected/hololake-updater.key', + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: 'provided-at-runtime', + } + const hydrated = materializeUpdaterPrivateKey(source, (file) => { + assert.equal(file, source.TAURI_SIGNING_PRIVATE_KEY_PATH) + return 'encrypted-private-key-material' + }) + assert.equal(hydrated.TAURI_SIGNING_PRIVATE_KEY, 'encrypted-private-key-material') + assert.equal(hydrated.TAURI_SIGNING_PRIVATE_KEY_PATH, undefined) + assert.equal(source.TAURI_SIGNING_PRIVATE_KEY, undefined) +}) + +test('release pipeline verifies the updater signature against embedded product trust before broadcast', () => { + const source = readFileSync(new URL('./release-pipeline.mjs', import.meta.url), 'utf8') + assert.match(source, /--example',\s*'verify_updater_signature'/) + assert.match(source, /src-tauri\/release-trust\.json/) + assert.match(source, /HoloLakePreNotarizationExecutableSha256/) + assert.match(source, /HoloLakeUnsignedExecutableSha256/) + assert.match(source, /--remove-signature/) + assert.match(source, /NOTARIZED_APP_SOURCE_BINARY_MISMATCH/) + assert.match(source, /APPLE_APP_NOTARIZATION_ACCEPTED_AND_STAPLED_DMG_CONTAINS_NOTARIZED_APP/) + assert.match(source, /path\.resolve\(appExecutable, '\.\.\/\.\.\/\.\.'\)/) + assert.doesNotMatch(source, /path\.resolve\(appExecutable, '\.\.\/\.\.\/\.\.\/\.\.'\)/) }) test('Windows updater keeps signed installation but never claims the macOS rollback boundary', () => { diff --git a/product-source/hololake-native-desktop/scripts/subdomain-industry-channel.test.mjs b/product-source/hololake-native-desktop/scripts/subdomain-industry-channel.test.mjs deleted file mode 100644 index f886b6ddc..000000000 --- a/product-source/hololake-native-desktop/scripts/subdomain-industry-channel.test.mjs +++ /dev/null @@ -1,77 +0,0 @@ -import assert from 'node:assert/strict' -import fs from 'node:fs' -import test from 'node:test' - -const contract = JSON.parse( - fs.readFileSync(new URL('../contracts/subdomain-industry-routing.json', import.meta.url), 'utf8'), -) -const frontend = fs.readFileSync(new URL('../src/main.tsx', import.meta.url), 'utf8') -const architecture = JSON.parse( - fs.readFileSync(new URL('../../../routing/hololake-current-architecture.json', import.meta.url), 'utf8'), -) - -test('human identity numbers and industry routing numbers remain separate', () => { - assert.equal(contract.numbering.human_number_role, 'SUBJECT_IDENTITY_ONLY') - assert.equal(contract.numbering.industry_number_role, 'SYSTEM_ROUTING_ONLY') - assert.equal(contract.numbering.human_number_mutation_by_industry, false) - assert.equal(contract.numbering.industry_order_is_industry_number, false) - assert.equal(contract.numbering.unregistered_industry_number_behavior, 'DISPLAY_PENDING_AND_DO_NOT_INVENT') -}) - -test('subdomain owns the locked industry order and honest development states', () => { - assert.equal(contract.domain, 'BRANCH_DOMAIN') - assert.equal(contract.entry_surface, 'PUBLIC_MAIN_WORLD_SUBDOMAIN') - assert.deepEqual( - contract.industries.map(({ key, state }) => [key, state]), - [ - ['WEB_NOVEL', 'IN_DEVELOPMENT'], - ['PET', 'PLANNED'], - ['EDUCATION', 'IN_DEVELOPMENT'], - ], - ) - assert.equal(contract.industries[0].industry_number, 'IND-WEBNOVEL-001') - assert.equal(contract.industries[1].industry_number, null) - assert.equal(contract.industries[2].industry_number, null) -}) - -test('education initialization channel exposes the built-in foundation and development package features', () => { - const education = contract.industries.find((industry) => industry.key === 'EDUCATION') - assert.equal(education.initialization_channel.state, 'DEVELOPMENT_MODULE_MOUNTED') - assert.equal(education.initialization_channel.channel_id, 'GH-EDU-INIT-001') - assert.equal(education.initialization_channel.module_loading, 'HOT_PLUG_ON_DEMAND') - assert.equal(education.initialization_channel.module_slots.length, 5) - assert.deepEqual( - education.initialization_channel.module_slots.map((slot) => slot.state), - [ - 'DEVELOPMENT_MOUNTED', - 'DEVELOPMENT_MOUNTED', - 'DEVELOPMENT_MOUNTED', - 'DEVELOPMENT_MOUNTED', - 'DEVELOPMENT_MOUNTED', - ], - ) - assert.equal(education.initialization_channel.installed_native_modules, 1) - assert.equal(education.initialization_channel.development_mounted_modules, 1) - assert.equal(education.initialization_channel.installed_open_source_modules, 0) -}) - -test('the public main world routes through subdomain to web novel and education', () => { - assert.match(frontend, /worldStage === 'subdomainIndustries'/) - assert.match(frontend, /worldStage === 'educationChannel'/) - assert.match(frontend, /worldStage === 'webNovelChannel'/) - assert.match(frontend, /worldStage === 'webNovelWorkspace'/) - assert.match(frontend, /gate\.domain === 'BRANCH_DOMAIN'/) - assert.match(frontend, /光湖分域 · 行业系统/) - assert.match(frontend, /网文与教育正在收拢验收/) - assert.match(frontend, /网文行业初始化频道/) - assert.match(frontend, /教育行业初始化频道/) - assert.match(frontend, /频道基础文档与工作表引擎已内置/) - assert.match(frontend, /验收前不冒充已登记、已发布的商城模块/) - assert.doesNotMatch(frontend, /光湖零域 · 行业系统/) - assert.equal(architecture.current_native_edge.industry_entry_domain, 'BRANCH_DOMAIN') - assert.equal(architecture.current_native_edge.zero_domain_industry_entry, 'ABSENT_BY_CURRENT_CORRECTION') - assert.equal( - architecture.current_native_edge.education_initialization_channel, - 'BUILT_IN_CHANNEL_WORKBENCH_WITH_DEVELOPMENT_EDUCATION_MODULE', - ) -}) diff --git a/product-source/hololake-native-desktop/scripts/unified-number-coordinate-tree.test.mjs b/product-source/hololake-native-desktop/scripts/unified-number-coordinate-tree.test.mjs new file mode 100644 index 000000000..1a160feb9 --- /dev/null +++ b/product-source/hololake-native-desktop/scripts/unified-number-coordinate-tree.test.mjs @@ -0,0 +1,25 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' +import { compileUnifiedNumberTree } from './compile-unified-number-tree.mjs' + +test('identity, webview and direct broker numbers compile into one unique evidence tree', () => { + const generated = JSON.parse(readFileSync(new URL('../generated/unified-number-coordinate-tree.json', import.meta.url), 'utf8')) + assert.deepEqual(generated, compileUnifiedNumberTree()) + assert.equal(generated.schema, 'hololake.unified-number-coordinate-tree/v2') + assert.equal(generated.recordId, 'HLP-UNIFIED-NUMBER-TREE-001') + assert.equal(generated.coordinateCount, 272) + assert.equal(generated.routeCount, 169) + assert.equal(generated.identityNodeCount, 4) + assert.equal(generated.protocolNodeCount, 99) + assert.equal(generated.referenceOnlyNodeCount, 16) + assert.equal(new Set(generated.routes.map((route) => route.path)).size, 169) + assert.equal(new Set([...generated.identityNodes, ...generated.protocolNodes, ...generated.routes].map((node) => node.path)).size, 272) + assert.equal(generated.invariants.everyPhysicalCallHasNumberedRoute, true) + assert.equal(generated.invariants.everyAcceptedCallHasEvidenceClass, true) + assert.equal(generated.invariants.everyProtocolReferenceHasNumberCoordinate, true) + assert.equal(generated.invariants.referenceOnlyNodesNeverExecutable, true) + assert.equal(generated.invariants.unresolvedNumberReferenceCount, 0) + assert.ok(generated.protocolNodes.filter((node) => node.nodeKind === 'GLS_REFERENCE_ONLY').every((node) => node.executionState === 'REFERENCE_ONLY_NOT_EXECUTABLE')) + assert.ok(generated.routes.every((route) => route.admission && route.evidence)) +}) diff --git a/product-source/hololake-native-desktop/scripts/user-pncc-channel.test.mjs b/product-source/hololake-native-desktop/scripts/user-pncc-channel.test.mjs index 0c9a27836..c5e5ddeb1 100644 --- a/product-source/hololake-native-desktop/scripts/user-pncc-channel.test.mjs +++ b/product-source/hololake-native-desktop/scripts/user-pncc-channel.test.mjs @@ -24,9 +24,11 @@ test('the native user PNCC contract binds verified number, signed-in account and test('the user PNCC is a native command and first-class HoloLake projection', () => { const native = readText('src-tauri/src/lib.rs') + const numberedDispatcher = readText('src-tauri/src/numbered_ipc_dispatch.rs') const frontend = readText('src/main.tsx') - assert.match(native, /user_pncc_channel::get_user_pncc_channel/) - assert.match(native, /user_pncc_channel::ensure_user_pncc_channel/) + assert.match(numberedDispatcher, /user_pncc_channel::get_user_pncc_channel/) + assert.match(numberedDispatcher, /user_pncc_channel::ensure_user_pncc_channel/) + assert.doesNotMatch(native, /user_pncc_channel::get_user_pncc_channel/) assert.match(frontend, /GH-PNCC · 人格原生代码频道/) assert.match(frontend, /Forgejo 协作适配器/) assert.match(frontend, /ensure_user_pncc_channel/) diff --git a/product-source/hololake-native-desktop/scripts/web-novel-workbench-admission.test.mjs b/product-source/hololake-native-desktop/scripts/web-novel-workbench-admission.test.mjs new file mode 100644 index 000000000..36d7fcdda --- /dev/null +++ b/product-source/hololake-native-desktop/scripts/web-novel-workbench-admission.test.mjs @@ -0,0 +1,54 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const read = (path) => readFileSync(new URL(`../${path}`, import.meta.url), 'utf8') +const registry = JSON.parse(read('contracts/numbered-ipc-registry.json')) +const runtime = read('src-tauri/src/module_package_runtime.rs') +const moduleAdapter = read('src-tauri/src/web_novel_modules.rs') +const frontend = [ + read('src/modules/web-novel/WebNovelWorkspace.tsx'), + read('src/modules/web-novel/AuthorModuleCenter.tsx'), + read('src/modules/web-novel/AuthorWritingSidecar.tsx'), +].join('\n') + +const packages = [ + 'WORKBENCH', 'OUTLINE', 'GRID', 'STORYWORLD', 'DELIVERY', +].map((name) => JSON.parse(read(`fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-${name}-0001-0.1.0.ghmod`))) + +test('web novel family has five immutable official identities behind one shared lifecycle', () => { + assert.equal(packages.length, 5) + assert.ok(packages.every((value) => value.manifest.registrationClass === 'OFFICIAL_LIGHTHOUSE')) + assert.ok(packages.every((value) => value.manifest.adapter === 'web-novel-workbench-v1')) + assert.equal(new Set(packages.map((value) => value.manifest.moduleNumber)).size, 5) + assert.match(runtime, /const BUNDLED_MODULES/) + assert.doesNotMatch(frontend, /install_web_novel_author_module|mount_web_novel_author_module|uninstall_web_novel_author_module/) +}) + +test('all 38 web novel effects cross exact numbered routes and no raw tauri command surface', () => { + const routes = registry.operations.filter((route) => Number(route.operation_number.slice(-4)) >= 103 && Number(route.operation_number.slice(-4)) <= 140) + assert.equal(routes.length, 38) + assert.deepEqual([...new Set(routes.map((route) => route.module_number))], [ + 'HLP-NIPC-MOD-0025', 'HLP-NIPC-MOD-0026', 'HLP-NIPC-MOD-0027', 'HLP-NIPC-MOD-0028', 'HLP-NIPC-MOD-0029', + ]) + assert.ok(routes.every((route) => route.admission === 'VERIFIED_HUMAN_ROUTE')) + assert.doesNotMatch(frontend, /from ['"]@tauri-apps\/api\/core['"]/) + for (const source of ['web_novel_workspace.rs', 'web_novel_import.rs', 'web_novel_author.rs', 'web_novel_modules.rs']) { + assert.doesNotMatch(read(`src-tauri/src/${source}`), /#\[tauri::command\]/) + } +}) + +test('advanced effects require their own exact active module number', () => { + assert.match(moduleAdapter, /require_module_active\(&app, OUTLINE\)/) + assert.match(moduleAdapter, /require_module_active\(&app, GRID\)/) + assert.match(moduleAdapter, /require_module_active\(&app, STORYWORLD\)/) + assert.match(moduleAdapter, /require_module_active\(&app, DELIVERY\)/) + assert.match(moduleAdapter, /shared signed package runtime/) +}) + +test('workspace is independently lazy-loaded and preserves old account data in place', () => { + assert.match(read('src/main.tsx'), /lazy\(\(\) => import\('\.\/modules\/web-novel\/WebNovelWorkspace'\)/) + assert.equal(packages[0].payload.adapterConfig.legacyDataPolicy, 'READ_IN_PLACE_NO_DESTRUCTIVE_MIGRATION') + assert.equal(packages[0].payload.adapterConfig.thirdPartyPublishDefault, 'DENY') + assert.doesNotMatch(read('src-tauri/src/web_novel_workspace.rs'), /serde_json::from_str[^\n]+unwrap_or_default\(\)/) +}) diff --git a/product-source/hololake-native-desktop/scripts/web-novel-workspace.test.mjs b/product-source/hololake-native-desktop/scripts/web-novel-workspace.test.mjs deleted file mode 100644 index 08d486e91..000000000 --- a/product-source/hololake-native-desktop/scripts/web-novel-workspace.test.mjs +++ /dev/null @@ -1,185 +0,0 @@ -import assert from 'node:assert/strict' -import crypto from 'node:crypto' -import fs from 'node:fs' -import test from 'node:test' - -const read = (path) => fs.readFileSync(new URL('../' + path, import.meta.url), 'utf8') -const donors = JSON.parse(read('contracts/web-novel-open-source-donor-assessment.json')) -const contract = JSON.parse(read('contracts/web-novel-workspace.json')) -const routing = JSON.parse(read('contracts/subdomain-industry-routing.json')) -const channelBody = JSON.parse(read('contracts/user-channel-body.json')) -const marketplace = JSON.parse(read('contracts/web-novel-module-marketplace-plan.json')) -const rust = read('src-tauri/src/web_novel_workspace.rs') -const authorRust = read('src-tauri/src/web_novel_author.rs') -const moduleRust = read('src-tauri/src/web_novel_modules.rs') -const rustLibrary = read('src-tauri/src/lib.rs') -const workspaceUi = read('src/WebNovelWorkspace.tsx') -const moduleUi = read('src/AuthorModuleCenter.tsx') -const mainUi = read('src/main.tsx') - -test('web novel upstream sources were reviewed at immutable commits through the Singapore relay', () => { - assert.equal(donors.state, 'PRIMARY_SOURCE_CODE_REVIEW_COMPLETE_IMPLEMENTATION_BOUNDARY_LOCKED') - assert.equal(donors.download_route.relay, 'BS-SG-003') - assert.equal(donors.download_route.repositories_are_clean_git_worktrees, true) - assert.equal(donors.candidates.length, 6) - for (const candidate of donors.candidates) { - assert.match(candidate.commit, /^[0-9a-f]{40}$/) - assert.ok(candidate.inspected_paths.length > 0) - assert.ok(candidate.observed_engine.length > 0) - } -}) - -test('license decisions keep GPL source out of the HoloLake implementation', () => { - assert.match(donors.candidates.find((item) => item.name === 'novelWriter').decision, /DO_NOT_COPY_GPL_SOURCE/) - assert.match(donors.candidates.find((item) => item.name === 'Manuskript').decision, /DO_NOT_COPY_GPL_SOURCE/) - assert.equal(donors.implementation_selection.external_source_files_copied, 0) - assert.equal(donors.implementation_selection.native_owner, 'HOLOLAKE_TAURI_RUST_CORE') -}) - -test('web novel workspace is account scoped and only projects persona task organs', () => { - assert.equal(contract.state, 'NATIVE_ENGINE_AND_FOUR_HOTPLUG_MODULE_CANDIDATES_MIGRATED_ACCEPTANCE_PENDING') - assert.equal(contract.domain_entry, 'BRANCH_DOMAIN') - assert.equal(contract.industry_number, 'IND-WEBNOVEL-001') - assert.equal(contract.native_storage.authenticated_account_required, true) - assert.equal(contract.native_storage.cross_account_projection_allowed, false) - assert.ok(contract.required_real_engines.includes('CHAPTER_VERSION_HISTORY')) - assert.ok(contract.required_real_engines.includes('CONTINUITY_AUDIT')) - assert.ok(contract.forbidden_substitutes.includes('UI_ONLY_BUTTONS_WITHOUT_NATIVE_COMMANDS')) - assert.equal(contract.ontology_correction.this_contract_is_not, 'USER_CHANNEL_BODY') - assert.equal(contract.ontology_correction.author_editor_operator_are_ui_tabs, false) - assert.equal(contract.distribution_boundary.preinstalled, 'LIGHT_AUTHOR_WORKBENCH_WITH_REAL_ENGINE') - assert.equal(contract.distribution_boundary.current_advanced_features_are_registered_marketplace_modules, false) - const industry = routing.industries.find((item) => item.key === 'WEB_NOVEL') - assert.equal(industry.industry_number, 'IND-WEBNOVEL-001') - assert.equal(industry.initialization_channel.legacy_projection_only, false) - assert.equal(industry.initialization_channel.installed_native_modules, 1) - assert.equal(industry.initialization_channel.module_slots[0].slot_id, 'WEBNOVEL-AUTHOR-LIGHT') -}) - -test('user channel contract is a durable multi-persona body rather than a module manifest', () => { - assert.equal(channelBody.state, 'FOUNDATION_RUNTIME_PARTIAL_ACCEPTANCE_PENDING') - assert.equal(channelBody.core_cardinality.language_cores_per_human, 1) - assert.equal(channelBody.core_cardinality.persona_cores_per_language_core, 1) - assert.equal(channelBody.core_cardinality.persona_entities_per_persona_core, 'MANY') - assert.equal(channelBody.persona_entity_boundary.role_template_creates_mature_persona, false) - assert.equal(channelBody.persona_entity_boundary.task_agent_is_persona, false) - assert.equal(channelBody.task_projection.workbench_is_channel_body, false) - assert.equal(channelBody.built_in_organs_policy.built_in_organs_allowed, true) - assert.equal(channelBody.module_ecosystem.official_module_number_before_real_acceptance, false) - assert.ok(channelBody.forbidden_substitutes.includes('DECLARATIVE_MODULE_MANIFEST_AS_CHANNEL_BODY')) - assert.ok(channelBody.acceptance_required.includes('TWO_PERSONAS_WITH_ISOLATED_GROWTH_MEMORY')) -}) - -test('the lightweight author workbench is built in while complete author capabilities stay numbered marketplace candidates', () => { - assert.equal(marketplace.built_in_light_author_workbench.distribution, 'PREINSTALLED') - assert.equal(marketplace.built_in_light_author_workbench.real_engine_owner, 'HOLOLAKE_NATIVE_RUST_SQLITE') - assert.ok(marketplace.built_in_light_author_workbench.capabilities.includes('REAL_CHAPTER_TEXT_EDITING')) - assert.equal(marketplace.official_module_candidates.length, 4) - for (const candidate of marketplace.official_module_candidates) { - assert.match(candidate.module_number, /^HL-MOD-WEBNOVEL-/) - assert.equal(candidate.numbering_state, 'CANDIDATE_NUMBER_NOT_OFFICIALLY_REGISTERED') - } - assert.deepEqual(marketplace.publication_gate.slice(-3), [ - 'THEN_ASSIGN_PERMANENT_MODULE_NUMBER', - 'THEN_REGISTER_OFFICIAL_MODULE_REGISTRY', - 'THEN_PUBLISH_OFFICIAL_MARKETPLACE', - ]) - assert.ok(marketplace.channel_deployment_flow.includes('MOUNT_IN_CURRENT_CHANNEL')) - assert.equal(marketplace.data_boundary.uninstall_preserves_story_data_and_receipts, true) -}) - -test('the desktop workbench invokes a registered native engine for every primary workflow', () => { - const commands = [ - 'get_web_novel_workspace_snapshot', - 'create_web_novel_work', - 'create_web_novel_volume', - 'create_web_novel_chapter', - 'save_web_novel_chapter', - 'transition_web_novel_chapter', - 'create_web_novel_checkpoint', - 'restore_web_novel_checkpoint', - 'upsert_web_novel_story_entity', - 'create_web_novel_story_relation', - 'upsert_web_novel_foreshadow', - 'create_web_novel_review_note', - 'resolve_web_novel_review_note', - 'save_web_novel_metric', - 'run_web_novel_continuity_audit', - 'export_web_novel_markdown', - ] - for (const command of commands) { - assert.match(rust, new RegExp(`fn ${command}`), `missing Rust implementation: ${command}`) - assert.match(rustLibrary, new RegExp(`web_novel_workspace::${command}`), `missing Tauri registration: ${command}`) - assert.match(workspaceUi, new RegExp(`['\"]${command}['\"]`), `missing UI invocation: ${command}`) - } - assert.match(rust, /account_storage_root\(app, "web-novel-workspace-v1"\)/) - assert.match(rust, /HOLOLAKE_WEBNOVEL_CHAPTER_REVISION_CONFLICT/) - assert.match(rust, /web_novel_chapter_versions/) - assert.match(workspaceUi, /window\.setTimeout\(\(\) => void flushChapterSave\(\), 650\)/) - assert.match(workspaceUi, /role=\{createFeedback\.state === 'error' \? 'alert' : 'status'\}/) - assert.match(workspaceUi, /正在建立作品与第一卷/) - assert.match(workspaceUi, /作品没有创建:/) - assert.match(workspaceUi, /第一卷已经可以开始写作/) - assert.match(mainUi, /worldStage === 'webNovelWorkspace'/) -}) - -test('author formatting is automatic on import, selectable, reversible, and can cover the whole work', () => { - for (const command of ['format_web_novel_chapter', 'format_web_novel_work']) { - assert.match(authorRust, new RegExp(`fn ${command}`)) - assert.match(rustLibrary, new RegExp(`web_novel_author::${command}`)) - assert.match(workspaceUi, new RegExp(`['"]${command}['"]`)) - } - assert.match(authorRust, /formatted\.join\("\\n\\n"\)/) - assert.match(read('src-tauri/src/web_novel_import.rs'), /AUTO_FORMAT_ON_IMPORT/) - assert.match(workspaceUi, /细纲结构视图/) - assert.match(read('src/AuthorWritingSidecar.tsx'), /一键排版整部作品/) -}) - -test('author modules are immutable packages with native install mount data and uninstall engines', () => { - const modules = [ - ['HL-MOD-WEBNOVEL-OUTLINE-001', '6d31d3f530862000bd84a8f5dc73bcb9cf0d5738219f7a287562d4f4bcfa5318'], - ['HL-MOD-WEBNOVEL-GRID-002', '2e34e0337c51dbbdd1efdd807be71e69a0cb2a1745624e1f51b452b7b54f1553'], - ['HL-MOD-WEBNOVEL-STORYWORLD-003', 'd486719500ddfc08054d9c9bb6d3683afe5547a2b24c9b04d0f899ac66094d2f'], - ['HL-MOD-WEBNOVEL-DELIVERY-004', '4af1d4610826edd29759bd0dc12b4b0c04fd2ebebb4ebc362d47d762810989e8'], - ] - for (const [moduleId, expectedSha] of modules) { - const bytes = fs.readFileSync(new URL(`../official-modules/web-novel/${moduleId}/1.0.0/module.json`, import.meta.url)) - const manifest = JSON.parse(bytes.toString('utf8')) - assert.equal(manifest.moduleId, moduleId) - assert.equal(crypto.createHash('sha256').update(bytes).digest('hex'), expectedSha) - assert.match(moduleRust, new RegExp(expectedSha)) - } - assert.match(moduleRust, /web_novel_module_installations/) - assert.match(moduleRust, /web_novel_module_receipts/) - assert.match(moduleRust, /require_mounted/) - assert.match(moduleRust, /USER_DATA_REMAINS_AFTER_UNINSTALL|作品、模块数据和历史回执保持/) - assert.match(moduleRust, /RESTORED_FROM_VERSION/) - assert.match(moduleRust, /render_docx/) - assert.match(moduleRust, /render_epub/) -}) - -test('every author module action is registered and invoked by the live channel workbench', () => { - const commands = [ - 'get_web_novel_author_module_marketplace', - 'install_web_novel_author_module', - 'mount_web_novel_author_module', - 'unmount_web_novel_author_module', - 'uninstall_web_novel_author_module', - 'get_web_novel_author_module_data', - 'upsert_web_novel_author_scene', - 'upsert_web_novel_author_beat', - 'upsert_web_novel_story_field_definition', - 'upsert_web_novel_story_field_value', - 'upsert_web_novel_timeline_event', - 'link_web_novel_scene_entity', - 'restore_web_novel_chapter_version', - 'export_web_novel_author_delivery', - ] - for (const command of commands) { - assert.match(moduleRust, new RegExp(`fn ${command}`), `missing module engine: ${command}`) - assert.match(rustLibrary, new RegExp(`web_novel_modules::${command}`), `missing module registration: ${command}`) - assert.match(moduleUi, new RegExp(`['"]${command}['"]`), `missing module UI invocation: ${command}`) - } - assert.match(workspaceUi, / Result { + let bytes = STANDARD + .decode(value.trim()) + .map_err(|error| format!("{label}_BASE64_INVALID: {error}"))?; + String::from_utf8(bytes).map_err(|error| format!("{label}_UTF8_INVALID: {error}")) +} + +fn main() -> Result<(), String> { + let arguments: Vec = env::args().collect(); + let [_, trust_path, updater_path, signature_path] = arguments.as_slice() else { + return Err( + "USAGE: verify_updater_signature " + .to_string(), + ); + }; + let trust: Value = serde_json::from_str( + &fs::read_to_string(trust_path) + .map_err(|error| format!("RELEASE_TRUST_READ_FAILED: {error}"))?, + ) + .map_err(|error| format!("RELEASE_TRUST_JSON_INVALID: {error}"))?; + let encoded_public_key = trust + .get("publicKey") + .and_then(Value::as_str) + .ok_or_else(|| "RELEASE_TRUST_PUBLIC_KEY_MISSING".to_string())?; + let public_key = PublicKey::decode(&decode_outer_base64( + encoded_public_key, + "RELEASE_TRUST_PUBLIC_KEY", + )?) + .map_err(|error| format!("RELEASE_TRUST_PUBLIC_KEY_INVALID: {error}"))?; + let signature = Signature::decode(&decode_outer_base64( + &fs::read_to_string(signature_path) + .map_err(|error| format!("UPDATER_SIGNATURE_READ_FAILED: {error}"))?, + "UPDATER_SIGNATURE", + )?) + .map_err(|error| format!("UPDATER_SIGNATURE_INVALID: {error}"))?; + let updater = fs::read(Path::new(updater_path)) + .map_err(|error| format!("UPDATER_ARTIFACT_READ_FAILED: {error}"))?; + public_key + .verify(&updater, &signature, true) + .map_err(|error| format!("UPDATER_SIGNATURE_VERIFICATION_FAILED: {error}"))?; + println!("HOLOLAKE_UPDATER_SIGNATURE_VERIFIED"); + Ok(()) +} diff --git a/product-source/hololake-native-desktop/src-tauri/permissions/numbered-ipc.toml b/product-source/hololake-native-desktop/src-tauri/permissions/numbered-ipc.toml new file mode 100644 index 000000000..3c562d10a --- /dev/null +++ b/product-source/hololake-native-desktop/src-tauri/permissions/numbered-ipc.toml @@ -0,0 +1,5 @@ +[[permission]] +identifier = "allow-numbered-ipc" +description = "Allow the main HoloLake webview to use the single numbered IPC gateway." +commands.allow = ["numbered_ipc"] + diff --git a/product-source/hololake-native-desktop/src-tauri/persona-control-authorization-signers.json b/product-source/hololake-native-desktop/src-tauri/persona-control-authorization-signers.json new file mode 100644 index 000000000..41e22b526 --- /dev/null +++ b/product-source/hololake-native-desktop/src-tauri/persona-control-authorization-signers.json @@ -0,0 +1,6 @@ +{ + "schema": "gh-aios.persona-control-authorization-signers/v1", + "registryId": "GH-AIOS-PERSONA-CONTROL-AUTHORIZATION-SIGNERS-001", + "state": "CURRENT", + "signers": [] +} diff --git a/product-source/hololake-native-desktop/src-tauri/src/channel_growth.rs b/product-source/hololake-native-desktop/src-tauri/src/channel_growth.rs index 7c8fb9dd2..c8ed06cec 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/channel_growth.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/channel_growth.rs @@ -8,6 +8,8 @@ use ring::digest::{digest, SHA256}; use rusqlite::{params, Connection}; use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::collections::BTreeMap; use std::fs; use std::path::{Path, PathBuf}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; @@ -16,9 +18,15 @@ use uuid::Uuid; const GROWTH_SCHEMA: &str = "hololake.channel-growth-model/v1"; const ZERO_HASH: &str = "0000000000000000000000000000000000000000000000000000000000000000"; +const MODULE_NUMBER: &str = "HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001"; +const ADAPTER: &str = "persona-channel-body-v1"; + +fn require_active(app: &AppHandle) -> Result<(), String> { + crate::module_package_runtime::require_active_module_adapter(app, MODULE_NUMBER, ADAPTER) +} #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct RecordChannelGrowthEventInput { pub event_kind: String, pub module_id: String, @@ -26,7 +34,7 @@ pub struct RecordChannelGrowthEventInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct UpdateChannelGrowthSharingInput { pub sharing_mode: String, } @@ -66,30 +74,30 @@ pub struct ChannelGrowthSnapshot { pub data_boundary: &'static str, } -#[tauri::command] pub async fn get_channel_growth_snapshot(app: AppHandle) -> Result { + require_active(&app)?; let database = channel_growth_database(&app)?; tauri::async_runtime::spawn_blocking(move || snapshot_at(&database)) .await .map_err(|error| format!("HOLOLAKE_CHANNEL_GROWTH_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn record_channel_growth_event( app: AppHandle, input: RecordChannelGrowthEventInput, ) -> Result { + require_active(&app)?; let database = channel_growth_database(&app)?; tauri::async_runtime::spawn_blocking(move || record_event_at(&database, input)) .await .map_err(|error| format!("HOLOLAKE_CHANNEL_GROWTH_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn update_channel_growth_sharing( app: AppHandle, input: UpdateChannelGrowthSharingInput, ) -> Result { + require_active(&app)?; let database = channel_growth_database(&app)?; tauri::async_runtime::spawn_blocking(move || update_sharing_at(&database, input)) .await @@ -248,6 +256,7 @@ fn snapshot_at(database: &Path) -> Result { fn snapshot_with_connection(connection: &Connection) -> Result { let last_event_hash = verify_event_chain(connection)?; + let mut affinities = verify_and_project_affinities(connection)?; let controls = connection .query_row( "SELECT sharing_mode FROM growth_sharing WHERE singleton = 1", @@ -272,21 +281,15 @@ fn snapshot_with_connection(connection: &Connection) -> Result, _>>() - .map_err(|error| format!("HOLOLAKE_CHANNEL_GROWTH_READ_FAILED: {error}"))?; + affinities.sort_by(|left, right| { + right + .score + .partial_cmp(&left.score) + .unwrap_or(Ordering::Equal) + .then_with(|| right.last_used_at_unix_ms.cmp(&left.last_used_at_unix_ms)) + .then_with(|| left.module_id.cmp(&right.module_id)) + }); + affinities.truncate(12); let recommended_module_order = affinities .iter() .map(|item| item.module_id.clone()) @@ -299,7 +302,7 @@ fn snapshot_with_connection(connection: &Connection) -> Result Result Result, String> { + let mut projected = BTreeMap::::new(); + let mut event_statement = connection + .prepare( + "SELECT module_id, source, occurred_at_unix_ms FROM growth_events ORDER BY sequence", + ) + .map_err(|error| format!("HOLOLAKE_CHANNEL_GROWTH_READ_FAILED: {error}"))?; + let events = event_statement + .query_map([], |row| { + Ok(( + row.get::<_, String>(0)?, + row.get::<_, String>(1)?, + row.get::<_, i64>(2)?, + )) + }) + .map_err(|error| format!("HOLOLAKE_CHANNEL_GROWTH_READ_FAILED: {error}"))? + .collect::, _>>() + .map_err(|error| format!("HOLOLAKE_CHANNEL_GROWTH_READ_FAILED: {error}"))?; + for (module_id, source, occurred_at) in events { + let weight = if source == "PERSONA_COLLABORATION" { + 1.4 + } else { + 1.0 + }; + projected + .entry(module_id.clone()) + .and_modify(|item| { + item.use_count += 1; + item.score = item.score * 0.985 + weight; + item.last_used_at_unix_ms = occurred_at; + }) + .or_insert(ChannelModuleAffinity { + module_id, + use_count: 1, + score: weight, + last_used_at_unix_ms: occurred_at, + }); + } + + let mut stored_statement = connection + .prepare( + "SELECT module_id, use_count, score, last_used_at_unix_ms FROM module_affinities ORDER BY module_id", + ) + .map_err(|error| format!("HOLOLAKE_CHANNEL_GROWTH_READ_FAILED: {error}"))?; + let stored = stored_statement + .query_map([], |row| { + Ok(ChannelModuleAffinity { + module_id: row.get(0)?, + use_count: row.get(1)?, + score: row.get(2)?, + last_used_at_unix_ms: row.get(3)?, + }) + }) + .map_err(|error| format!("HOLOLAKE_CHANNEL_GROWTH_READ_FAILED: {error}"))? + .collect::, _>>() + .map_err(|error| format!("HOLOLAKE_CHANNEL_GROWTH_READ_FAILED: {error}"))?; + if stored.len() != projected.len() + || stored.iter().any(|item| { + projected.get(&item.module_id).map_or(true, |expected| { + item.use_count != expected.use_count + || (item.score - expected.score).abs() > 1e-12 + || item.last_used_at_unix_ms != expected.last_used_at_unix_ms + }) + }) + { + return Err("HOLOLAKE_CHANNEL_GROWTH_PROJECTION_INTEGRITY_FAILED".into()); + } + Ok(projected.into_values().collect()) +} + fn event_hash( sequence: i64, event_id: &str, @@ -454,7 +529,31 @@ mod tests { .is_err()); assert_eq!( snapshot_at(&database).unwrap().integrity_state, - "VERIFIED_APPEND_ONLY_CHAIN" + "VERIFIED_APPEND_ONLY_CHAIN_AND_DERIVED_PROJECTION" + ); + } + + #[test] + fn derived_affinity_projection_fails_closed_when_modified_outside_the_event_path() { + let dir = tempdir().unwrap(); + let database = dir.path().join("growth.sqlite3"); + record_event_at( + &database, + RecordChannelGrowthEventInput { + event_kind: "MODULE_OPEN".into(), + module_id: "knowledge".into(), + source: "HUMAN_ACTION".into(), + }, + ) + .unwrap(); + let connection = open_database(&database).unwrap(); + connection + .execute("UPDATE module_affinities SET score = 999", []) + .unwrap(); + drop(connection); + assert_eq!( + snapshot_at(&database).unwrap_err(), + "HOLOLAKE_CHANNEL_GROWTH_PROJECTION_INTEGRITY_FAILED" ); } diff --git a/product-source/hololake-native-desktop/src-tauri/src/channel_workbench.rs b/product-source/hololake-native-desktop/src-tauri/src/channel_workbench.rs new file mode 100644 index 000000000..00c6a4dff --- /dev/null +++ b/product-source/hololake-native-desktop/src-tauri/src/channel_workbench.rs @@ -0,0 +1,694 @@ +//! Signed channel document and spreadsheet workbench adapter. +//! +//! The signed package only selects this host-owned adapter. User content stays in +//! the authenticated account root and every accepted save is committed together +//! with a hash-chained receipt. Unmounting the package never removes that data. + +use ring::digest::{digest, SHA256}; +use rusqlite::{params, Connection, OptionalExtension, Transaction}; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::path::{Path, PathBuf}; +use std::time::{SystemTime, UNIX_EPOCH}; +use tauri::AppHandle; +use uuid::Uuid; + +const MODULE_NUMBER: &str = "HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001"; +const ADAPTER: &str = "channel-workbench-v1"; +const ZERO_HASH: &str = "0000000000000000000000000000000000000000000000000000000000000000"; +const MAX_BODY_BYTES: usize = 2 * 1024 * 1024; +const MAX_COLUMNS: usize = 64; +const MAX_ROWS: usize = 5_000; +const MAX_CELL_BYTES: usize = 64 * 1024; + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct WorkbenchColumn { + pub column_id: String, + pub title: String, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct WorkbenchRow { + pub row_id: String, + pub cells: Vec, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ChannelDocument { + document_id: String, + title: String, + body: String, + revision: u64, + updated_at_unix_ms: u64, + content_sha256: String, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ChannelSpreadsheet { + table_id: String, + title: String, + columns: Vec, + rows: Vec, + revision: u64, + updated_at_unix_ms: u64, + content_sha256: String, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ChannelWorkbenchSnapshot { + schema: &'static str, + state: &'static str, + module_number: &'static str, + document: ChannelDocument, + spreadsheet: ChannelSpreadsheet, + receipt_count: u64, + latest_receipt_hash: String, + user_data_survives_unmount: bool, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct SaveChannelDocumentInput { + document_id: String, + title: String, + body: String, + expected_revision: u64, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct SaveChannelSpreadsheetInput { + table_id: String, + title: String, + columns: Vec, + rows: Vec, + expected_revision: u64, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct WorkbenchSaveResult { + schema: &'static str, + state: &'static str, + entity_kind: &'static str, + entity_id: String, + revision: u64, + content_sha256: String, + receipt_sha256: String, + observed_at_unix_ms: u64, +} + +fn now_unix_ms() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_millis() as u64) + .unwrap_or(0) +} + +fn sha256_hex(bytes: &[u8]) -> String { + digest(&SHA256, bytes) + .as_ref() + .iter() + .map(|byte| format!("{byte:02x}")) + .collect() +} + +fn storage_root(app: &AppHandle) -> Result { + crate::authenticated_storage::account_storage_root(app, "channel-workbench-v1") +} + +fn require_active(app: &AppHandle) -> Result<(), String> { + crate::module_package_runtime::require_active_module_adapter(app, MODULE_NUMBER, ADAPTER) +} + +fn open_db(root: &Path) -> Result { + fs::create_dir_all(root) + .map_err(|error| format!("HOLOLAKE_CHANNEL_WORKBENCH_ROOT_FAILED: {error}"))?; + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(root, fs::Permissions::from_mode(0o700)) + .map_err(|error| format!("HOLOLAKE_CHANNEL_WORKBENCH_PERMISSION_FAILED: {error}"))?; + } + let connection = Connection::open(root.join("channel-workbench.sqlite3")) + .map_err(|error| format!("HOLOLAKE_CHANNEL_WORKBENCH_DB_OPEN_FAILED: {error}"))?; + connection + .busy_timeout(std::time::Duration::from_secs(5)) + .map_err(|error| format!("HOLOLAKE_CHANNEL_WORKBENCH_DB_TIMEOUT_FAILED: {error}"))?; + connection + .execute_batch( + "PRAGMA journal_mode=WAL; + PRAGMA foreign_keys=ON; + CREATE TABLE IF NOT EXISTS documents( + document_id TEXT PRIMARY KEY, + title TEXT NOT NULL, + body TEXT NOT NULL, + revision INTEGER NOT NULL, + updated_at_unix_ms INTEGER NOT NULL, + content_sha256 TEXT NOT NULL + ); + CREATE TABLE IF NOT EXISTS spreadsheets( + table_id TEXT PRIMARY KEY, + title TEXT NOT NULL, + columns_json TEXT NOT NULL, + rows_json TEXT NOT NULL, + revision INTEGER NOT NULL, + updated_at_unix_ms INTEGER NOT NULL, + content_sha256 TEXT NOT NULL + ); + CREATE TABLE IF NOT EXISTS workbench_receipts( + sequence INTEGER PRIMARY KEY AUTOINCREMENT, + receipt_id TEXT NOT NULL UNIQUE, + event TEXT NOT NULL, + entity_kind TEXT NOT NULL, + entity_id TEXT NOT NULL, + revision INTEGER NOT NULL, + content_sha256 TEXT NOT NULL, + previous_receipt_hash TEXT NOT NULL, + receipt_hash TEXT NOT NULL UNIQUE, + observed_at_unix_ms INTEGER NOT NULL + ); + CREATE TRIGGER IF NOT EXISTS workbench_receipts_no_update + BEFORE UPDATE ON workbench_receipts BEGIN SELECT RAISE(ABORT,'HOLOLAKE_WORKBENCH_RECEIPT_APPEND_ONLY'); END; + CREATE TRIGGER IF NOT EXISTS workbench_receipts_no_delete + BEFORE DELETE ON workbench_receipts BEGIN SELECT RAISE(ABORT,'HOLOLAKE_WORKBENCH_RECEIPT_APPEND_ONLY'); END;", + ) + .map_err(|error| format!("HOLOLAKE_CHANNEL_WORKBENCH_DB_INIT_FAILED: {error}"))?; + Ok(connection) +} + +fn valid_object_id(value: &str, prefix: &str) -> bool { + value.starts_with(prefix) + && value.len() <= 96 + && value + .chars() + .all(|item| item.is_ascii_uppercase() || item.is_ascii_digit() || item == '-') +} + +fn valid_title(value: &str) -> bool { + !value.trim().is_empty() && value.len() <= 120 && !value.chars().any(char::is_control) +} + +fn receipt_hash( + event: &str, + entity_kind: &str, + entity_id: &str, + revision: u64, + content_sha256: &str, + previous: &str, + observed: u64, +) -> String { + sha256_hex( + format!( + "{event}\n{entity_kind}\n{entity_id}\n{revision}\n{content_sha256}\n{previous}\n{observed}" + ) + .as_bytes(), + ) +} + +fn verify_receipts(connection: &Connection) -> Result<(u64, String), String> { + let mut statement = connection + .prepare("SELECT sequence,event,entity_kind,entity_id,revision,content_sha256,previous_receipt_hash,receipt_hash,observed_at_unix_ms FROM workbench_receipts ORDER BY sequence") + .map_err(|error| format!("HOLOLAKE_CHANNEL_WORKBENCH_RECEIPT_READ_FAILED: {error}"))?; + let rows = statement + .query_map([], |row| { + Ok(( + row.get::<_, u64>(0)?, + row.get::<_, String>(1)?, + row.get::<_, String>(2)?, + row.get::<_, String>(3)?, + row.get::<_, u64>(4)?, + row.get::<_, String>(5)?, + row.get::<_, String>(6)?, + row.get::<_, String>(7)?, + row.get::<_, u64>(8)?, + )) + }) + .map_err(|error| format!("HOLOLAKE_CHANNEL_WORKBENCH_RECEIPT_READ_FAILED: {error}"))?; + let mut count = 0; + let mut previous = ZERO_HASH.to_string(); + for row in rows { + let (sequence, event, kind, id, revision, content, stored_previous, stored_hash, observed) = + row.map_err(|error| format!("HOLOLAKE_CHANNEL_WORKBENCH_RECEIPT_READ_FAILED: {error}"))?; + if sequence != count + 1 + || stored_previous != previous + || stored_hash + != receipt_hash(&event, &kind, &id, revision, &content, &previous, observed) + { + return Err("HOLOLAKE_CHANNEL_WORKBENCH_RECEIPT_CHAIN_INVALID".into()); + } + count = sequence; + previous = stored_hash; + } + Ok((count, previous)) +} + +fn append_receipt( + tx: &Transaction<'_>, + event: &str, + kind: &str, + id: &str, + revision: u64, + content: &str, + observed: u64, +) -> Result { + let previous: String = tx + .query_row( + "SELECT COALESCE((SELECT receipt_hash FROM workbench_receipts ORDER BY sequence DESC LIMIT 1),?1)", + params![ZERO_HASH], + |row| row.get(0), + ) + .map_err(|error| format!("HOLOLAKE_CHANNEL_WORKBENCH_RECEIPT_HEAD_FAILED: {error}"))?; + let hash = receipt_hash(event, kind, id, revision, content, &previous, observed); + tx.execute( + "INSERT INTO workbench_receipts(receipt_id,event,entity_kind,entity_id,revision,content_sha256,previous_receipt_hash,receipt_hash,observed_at_unix_ms) VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9)", + params![format!("HLP-WB-RCP-{}", Uuid::new_v4().simple()), event, kind, id, revision, content, previous, hash, observed], + ) + .map_err(|error| format!("HOLOLAKE_CHANNEL_WORKBENCH_RECEIPT_WRITE_FAILED: {error}"))?; + Ok(hash) +} + +fn empty_document() -> ChannelDocument { + ChannelDocument { + document_id: "HLP-WB-DOC-0001".into(), + title: "频道笔记".into(), + body: "".into(), + revision: 0, + updated_at_unix_ms: 0, + content_sha256: sha256_hex(b""), + } +} + +fn empty_spreadsheet() -> ChannelSpreadsheet { + ChannelSpreadsheet { + table_id: "HLP-WB-TBL-0001".into(), + title: "频道工作表".into(), + columns: vec![ + WorkbenchColumn { + column_id: "HLP-WB-COL-0001".into(), + title: "事项".into(), + }, + WorkbenchColumn { + column_id: "HLP-WB-COL-0002".into(), + title: "状态".into(), + }, + ], + rows: Vec::new(), + revision: 0, + updated_at_unix_ms: 0, + content_sha256: sha256_hex(b""), + } +} + +fn snapshot_at(root: &Path) -> Result { + let connection = open_db(root)?; + let (receipt_count, latest_receipt_hash) = verify_receipts(&connection)?; + let document = connection + .query_row( + "SELECT document_id,title,body,revision,updated_at_unix_ms,content_sha256 FROM documents ORDER BY updated_at_unix_ms DESC LIMIT 1", + [], + |row| Ok(ChannelDocument { document_id: row.get(0)?, title: row.get(1)?, body: row.get(2)?, revision: row.get(3)?, updated_at_unix_ms: row.get(4)?, content_sha256: row.get(5)? }), + ) + .optional() + .map_err(|error| format!("HOLOLAKE_CHANNEL_DOCUMENT_READ_FAILED: {error}"))? + .unwrap_or_else(empty_document); + let spreadsheet = connection + .query_row( + "SELECT table_id,title,columns_json,rows_json,revision,updated_at_unix_ms,content_sha256 FROM spreadsheets ORDER BY updated_at_unix_ms DESC LIMIT 1", + [], + |row| Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?, row.get::<_, String>(2)?, row.get::<_, String>(3)?, row.get::<_, u64>(4)?, row.get::<_, u64>(5)?, row.get::<_, String>(6)?)), + ) + .optional() + .map_err(|error| format!("HOLOLAKE_CHANNEL_SPREADSHEET_READ_FAILED: {error}"))? + .map(|(table_id, title, columns, rows, revision, updated_at_unix_ms, content_sha256)| { + Ok::(ChannelSpreadsheet { + table_id, + title, + columns: serde_json::from_str(&columns).map_err(|_| "HOLOLAKE_CHANNEL_SPREADSHEET_COLUMNS_INVALID".to_string())?, + rows: serde_json::from_str(&rows).map_err(|_| "HOLOLAKE_CHANNEL_SPREADSHEET_ROWS_INVALID".to_string())?, + revision, + updated_at_unix_ms, + content_sha256, + }) + }) + .transpose()? + .unwrap_or_else(empty_spreadsheet); + if document.revision > 0 { + let actual = sha256_hex( + serde_json::to_string(&(document.title.trim(), &document.body)) + .map_err(|_| "HOLOLAKE_CHANNEL_DOCUMENT_SERIALIZE_FAILED")? + .as_bytes(), + ); + if actual != document.content_sha256 { + return Err("HOLOLAKE_CHANNEL_DOCUMENT_CONTENT_INTEGRITY_INVALID".into()); + } + } + if spreadsheet.revision > 0 { + let actual = sha256_hex( + serde_json::to_string(&( + spreadsheet.title.trim(), + &spreadsheet.columns, + &spreadsheet.rows, + )) + .map_err(|_| "HOLOLAKE_CHANNEL_SPREADSHEET_SERIALIZE_FAILED")? + .as_bytes(), + ); + if actual != spreadsheet.content_sha256 { + return Err("HOLOLAKE_CHANNEL_SPREADSHEET_CONTENT_INTEGRITY_INVALID".into()); + } + } + let mut latest = connection + .prepare("SELECT entity_kind,entity_id,revision,content_sha256 FROM workbench_receipts WHERE sequence IN (SELECT MAX(sequence) FROM workbench_receipts GROUP BY entity_kind,entity_id)") + .map_err(|error| format!("HOLOLAKE_CHANNEL_WORKBENCH_RECEIPT_READ_FAILED: {error}"))?; + let heads = latest + .query_map([], |row| { + Ok(( + row.get::<_, String>(0)?, + row.get::<_, String>(1)?, + row.get::<_, u64>(2)?, + row.get::<_, String>(3)?, + )) + }) + .map_err(|error| format!("HOLOLAKE_CHANNEL_WORKBENCH_RECEIPT_READ_FAILED: {error}"))?; + for head in heads { + let (kind, id, revision, content) = head + .map_err(|error| format!("HOLOLAKE_CHANNEL_WORKBENCH_RECEIPT_READ_FAILED: {error}"))?; + let matches = match kind.as_str() { + "DOCUMENT" => { + document.document_id == id + && document.revision == revision + && document.content_sha256 == content + } + "SPREADSHEET" => { + spreadsheet.table_id == id + && spreadsheet.revision == revision + && spreadsheet.content_sha256 == content + } + _ => false, + }; + if !matches { + return Err("HOLOLAKE_CHANNEL_WORKBENCH_CURRENT_STATE_INVALID".into()); + } + } + Ok(ChannelWorkbenchSnapshot { + schema: "hololake.channel-workbench-snapshot/v1", + state: "ACTIVE_LOCAL_ACCOUNT_DATA", + module_number: MODULE_NUMBER, + document, + spreadsheet, + receipt_count, + latest_receipt_hash, + user_data_survives_unmount: true, + }) +} + +fn save_document_at( + root: &Path, + input: SaveChannelDocumentInput, + observed: u64, +) -> Result { + if !valid_object_id(&input.document_id, "HLP-WB-DOC-") + || !valid_title(&input.title) + || input.body.len() > MAX_BODY_BYTES + { + return Err("HOLOLAKE_CHANNEL_DOCUMENT_INPUT_INVALID".into()); + } + let mut connection = open_db(root)?; + verify_receipts(&connection)?; + let transaction = connection + .transaction() + .map_err(|error| format!("HOLOLAKE_CHANNEL_DOCUMENT_TRANSACTION_FAILED: {error}"))?; + let current: Option = transaction + .query_row( + "SELECT revision FROM documents WHERE document_id=?1", + params![input.document_id], + |row| row.get(0), + ) + .optional() + .map_err(|error| format!("HOLOLAKE_CHANNEL_DOCUMENT_READ_FAILED: {error}"))?; + if current.unwrap_or(0) != input.expected_revision { + return Err("HOLOLAKE_CHANNEL_DOCUMENT_REVISION_CONFLICT".into()); + } + let revision = input.expected_revision + 1; + let content = sha256_hex( + serde_json::to_string(&(input.title.trim(), &input.body)) + .map_err(|_| "HOLOLAKE_CHANNEL_DOCUMENT_SERIALIZE_FAILED")? + .as_bytes(), + ); + transaction.execute( + "INSERT INTO documents VALUES (?1,?2,?3,?4,?5,?6) ON CONFLICT(document_id) DO UPDATE SET title=excluded.title,body=excluded.body,revision=excluded.revision,updated_at_unix_ms=excluded.updated_at_unix_ms,content_sha256=excluded.content_sha256", + params![input.document_id, input.title.trim(), input.body, revision, observed, content], + ).map_err(|error| format!("HOLOLAKE_CHANNEL_DOCUMENT_WRITE_FAILED: {error}"))?; + let receipt = append_receipt( + &transaction, + if current.is_some() { + "UPDATE" + } else { + "CREATE" + }, + "DOCUMENT", + &input.document_id, + revision, + &content, + observed, + )?; + transaction + .commit() + .map_err(|error| format!("HOLOLAKE_CHANNEL_DOCUMENT_COMMIT_FAILED: {error}"))?; + Ok(WorkbenchSaveResult { + schema: "hololake.channel-workbench-save/v1", + state: "COMMITTED", + entity_kind: "DOCUMENT", + entity_id: input.document_id, + revision, + content_sha256: content, + receipt_sha256: receipt, + observed_at_unix_ms: observed, + }) +} + +fn validate_spreadsheet(input: &SaveChannelSpreadsheetInput) -> Result<(), String> { + if !valid_object_id(&input.table_id, "HLP-WB-TBL-") + || !valid_title(&input.title) + || input.columns.is_empty() + || input.columns.len() > MAX_COLUMNS + || input.rows.len() > MAX_ROWS + || input.columns.iter().any(|column| { + !valid_object_id(&column.column_id, "HLP-WB-COL-") || !valid_title(&column.title) + }) + || input.rows.iter().any(|row| { + !valid_object_id(&row.row_id, "HLP-WB-ROW-") + || row.cells.len() > input.columns.len() + || row.cells.iter().any(|cell| cell.len() > MAX_CELL_BYTES) + }) + { + return Err("HOLOLAKE_CHANNEL_SPREADSHEET_INPUT_INVALID".into()); + } + Ok(()) +} + +fn save_spreadsheet_at( + root: &Path, + input: SaveChannelSpreadsheetInput, + observed: u64, +) -> Result { + validate_spreadsheet(&input)?; + let mut connection = open_db(root)?; + verify_receipts(&connection)?; + let transaction = connection + .transaction() + .map_err(|error| format!("HOLOLAKE_CHANNEL_SPREADSHEET_TRANSACTION_FAILED: {error}"))?; + let current: Option = transaction + .query_row( + "SELECT revision FROM spreadsheets WHERE table_id=?1", + params![input.table_id], + |row| row.get(0), + ) + .optional() + .map_err(|error| format!("HOLOLAKE_CHANNEL_SPREADSHEET_READ_FAILED: {error}"))?; + if current.unwrap_or(0) != input.expected_revision { + return Err("HOLOLAKE_CHANNEL_SPREADSHEET_REVISION_CONFLICT".into()); + } + let revision = input.expected_revision + 1; + let columns = serde_json::to_string(&input.columns) + .map_err(|_| "HOLOLAKE_CHANNEL_SPREADSHEET_SERIALIZE_FAILED")?; + let rows = serde_json::to_string(&input.rows) + .map_err(|_| "HOLOLAKE_CHANNEL_SPREADSHEET_SERIALIZE_FAILED")?; + let content = sha256_hex( + serde_json::to_string(&(input.title.trim(), &input.columns, &input.rows)) + .map_err(|_| "HOLOLAKE_CHANNEL_SPREADSHEET_SERIALIZE_FAILED")? + .as_bytes(), + ); + transaction.execute( + "INSERT INTO spreadsheets VALUES (?1,?2,?3,?4,?5,?6,?7) ON CONFLICT(table_id) DO UPDATE SET title=excluded.title,columns_json=excluded.columns_json,rows_json=excluded.rows_json,revision=excluded.revision,updated_at_unix_ms=excluded.updated_at_unix_ms,content_sha256=excluded.content_sha256", + params![input.table_id, input.title.trim(), columns, rows, revision, observed, content], + ).map_err(|error| format!("HOLOLAKE_CHANNEL_SPREADSHEET_WRITE_FAILED: {error}"))?; + let receipt = append_receipt( + &transaction, + if current.is_some() { + "UPDATE" + } else { + "CREATE" + }, + "SPREADSHEET", + &input.table_id, + revision, + &content, + observed, + )?; + transaction + .commit() + .map_err(|error| format!("HOLOLAKE_CHANNEL_SPREADSHEET_COMMIT_FAILED: {error}"))?; + Ok(WorkbenchSaveResult { + schema: "hololake.channel-workbench-save/v1", + state: "COMMITTED", + entity_kind: "SPREADSHEET", + entity_id: input.table_id, + revision, + content_sha256: content, + receipt_sha256: receipt, + observed_at_unix_ms: observed, + }) +} + +pub async fn get_channel_workbench_snapshot( + app: AppHandle, +) -> Result { + require_active(&app)?; + let root = storage_root(&app)?; + tauri::async_runtime::spawn_blocking(move || snapshot_at(&root)) + .await + .map_err(|error| format!("HOLOLAKE_CHANNEL_WORKBENCH_JOIN_FAILED: {error}"))? +} + +pub async fn save_channel_document( + app: AppHandle, + input: SaveChannelDocumentInput, +) -> Result { + require_active(&app)?; + let root = storage_root(&app)?; + tauri::async_runtime::spawn_blocking(move || save_document_at(&root, input, now_unix_ms())) + .await + .map_err(|error| format!("HOLOLAKE_CHANNEL_DOCUMENT_JOIN_FAILED: {error}"))? +} + +pub async fn save_channel_spreadsheet( + app: AppHandle, + input: SaveChannelSpreadsheetInput, +) -> Result { + require_active(&app)?; + let root = storage_root(&app)?; + tauri::async_runtime::spawn_blocking(move || save_spreadsheet_at(&root, input, now_unix_ms())) + .await + .map_err(|error| format!("HOLOLAKE_CHANNEL_SPREADSHEET_JOIN_FAILED: {error}"))? +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn document_and_spreadsheet_survive_reopen_with_receipt_chain() { + let root = tempfile::tempdir().unwrap(); + let document = save_document_at( + root.path(), + SaveChannelDocumentInput { + document_id: "HLP-WB-DOC-0001".into(), + title: "真实笔记".into(), + body: "第一段真实内容".into(), + expected_revision: 0, + }, + 101, + ) + .unwrap(); + let sheet = save_spreadsheet_at( + root.path(), + SaveChannelSpreadsheetInput { + table_id: "HLP-WB-TBL-0001".into(), + title: "真实工作表".into(), + columns: vec![WorkbenchColumn { + column_id: "HLP-WB-COL-0001".into(), + title: "数量".into(), + }], + rows: vec![WorkbenchRow { + row_id: "HLP-WB-ROW-0001".into(), + cells: vec!["=1+2".into()], + }], + expected_revision: 0, + }, + 102, + ) + .unwrap(); + let snapshot = snapshot_at(root.path()).unwrap(); + assert_eq!(document.revision, 1); + assert_eq!(sheet.revision, 1); + assert_eq!(snapshot.document.body, "第一段真实内容"); + assert_eq!(snapshot.spreadsheet.rows[0].cells[0], "=1+2"); + assert_eq!(snapshot.receipt_count, 2); + assert_eq!(snapshot.latest_receipt_hash, sheet.receipt_sha256); + } + + #[test] + fn stale_revision_and_tampered_receipt_fail_closed() { + let root = tempfile::tempdir().unwrap(); + let input = SaveChannelDocumentInput { + document_id: "HLP-WB-DOC-0001".into(), + title: "笔记".into(), + body: "正文".into(), + expected_revision: 0, + }; + save_document_at(root.path(), input, 101).unwrap(); + assert_eq!( + save_document_at( + root.path(), + SaveChannelDocumentInput { + document_id: "HLP-WB-DOC-0001".into(), + title: "笔记".into(), + body: "过期写入".into(), + expected_revision: 0 + }, + 102 + ) + .unwrap_err(), + "HOLOLAKE_CHANNEL_DOCUMENT_REVISION_CONFLICT" + ); + let connection = open_db(root.path()).unwrap(); + connection.execute_batch("DROP TRIGGER workbench_receipts_no_update; UPDATE workbench_receipts SET receipt_hash='bad'").unwrap(); + assert_eq!( + snapshot_at(root.path()).unwrap_err(), + "HOLOLAKE_CHANNEL_WORKBENCH_RECEIPT_CHAIN_INVALID" + ); + } + + #[test] + fn content_mutation_without_a_receipt_fails_closed() { + let root = tempfile::tempdir().unwrap(); + save_document_at( + root.path(), + SaveChannelDocumentInput { + document_id: "HLP-WB-DOC-0001".into(), + title: "笔记".into(), + body: "可信正文".into(), + expected_revision: 0, + }, + 101, + ) + .unwrap(); + let connection = open_db(root.path()).unwrap(); + connection + .execute( + "UPDATE documents SET body='被改写' WHERE document_id='HLP-WB-DOC-0001'", + [], + ) + .unwrap(); + assert_eq!( + snapshot_at(root.path()).unwrap_err(), + "HOLOLAKE_CHANNEL_DOCUMENT_CONTENT_INTEGRITY_INVALID" + ); + } +} diff --git a/product-source/hololake-native-desktop/src-tauri/src/code_channel.rs b/product-source/hololake-native-desktop/src-tauri/src/code_channel.rs index a6dd47cd7..f0da9be09 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/code_channel.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/code_channel.rs @@ -120,7 +120,6 @@ struct ValidatedCloneUrl { repository: String, } -#[tauri::command] pub async fn get_code_channel_snapshot(app: AppHandle) -> Result { let root = code_channel_root(&app)?; tauri::async_runtime::spawn_blocking(move || snapshot_at(&root)) @@ -128,7 +127,6 @@ pub async fn get_code_channel_snapshot(app: AppHandle) -> Result Result, String> { @@ -165,7 +162,6 @@ pub async fn select_local_code_channel( .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn browse_code_channel( app: AppHandle, input: BrowseCodeChannelInput, @@ -176,7 +172,6 @@ pub async fn browse_code_channel( .map_err(|error| format!("HOLOLAKE_CODE_CHANNEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn read_code_channel_file( app: AppHandle, input: ReadCodeChannelFileInput, diff --git a/product-source/hololake-native-desktop/src-tauri/src/code_repo_login.rs b/product-source/hololake-native-desktop/src-tauri/src/code_repo_login.rs index bee6e8bea..bae44ad0e 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/code_repo_login.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/code_repo_login.rs @@ -168,7 +168,6 @@ pub(crate) fn validate_number_account_binding(number: &str, username: &str) -> R } /// 启动时自查:本机有没有已登录会话(会话文件在,且钥匙串里凭证还在)。 -#[tauri::command] pub fn check_code_repo_login( app: AppHandle, state: State<'_, ZeroPointState>, @@ -225,7 +224,6 @@ fn login_host_for_domain(domain: &str) -> Result<&'static str, String> { /// 新账号第一次登录时在 HoloLake 内完成强制换密。 /// 旧、新密码只存在于本次 HTTPS 请求内;服务端回执不含密码。 -#[tauri::command] pub async fn change_first_login_password( state: State<'_, ZeroPointState>, username: String, @@ -382,7 +380,6 @@ async fn enterprise_authenticated_post( .map_err(|error| format!("HOLOLAKE_ENTERPRISE_RECEIPT_FAILED: {error}")) } -#[tauri::command] pub async fn get_enterprise_entry( app: AppHandle, state: State<'_, ZeroPointState>, @@ -390,7 +387,6 @@ pub async fn get_enterprise_entry( enterprise_authenticated_post(&app, &state, "me/entry", serde_json::json!({})).await } -#[tauri::command] pub async fn confirm_enterprise_persona_relationship( app: AppHandle, state: State<'_, ZeroPointState>, @@ -406,7 +402,6 @@ pub async fn confirm_enterprise_persona_relationship( .await } -#[tauri::command] pub async fn submit_enterprise_responsibility_receipt( app: AppHandle, state: State<'_, ZeroPointState>, @@ -431,7 +426,6 @@ pub async fn submit_enterprise_responsibility_receipt( /// 登录验证:基本认证打 Forgejo 用户接口,密码错=401 即拒; /// 通过后凭证进钥匙串,会话(不含密码)落盘。 -#[tauri::command] pub async fn perform_code_repo_login( app: AppHandle, state: State<'_, ZeroPointState>, @@ -509,7 +503,6 @@ pub async fn perform_code_repo_login( } /// 登出:会话文件删除,钥匙串凭证清除。 -#[tauri::command] pub fn sign_out_code_repo_login(app: AppHandle) -> Result<(), String> { let path = session_path(&app)?; if let Ok(raw) = fs::read_to_string(&path) { diff --git a/product-source/hololake-native-desktop/src-tauri/src/direct_local_broker.rs b/product-source/hololake-native-desktop/src-tauri/src/direct_local_broker.rs index 09a616bf1..ac7fb28ce 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/direct_local_broker.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/direct_local_broker.rs @@ -1,9 +1,9 @@ use crate::circular_lake_membrane::{receive_at as receive_language_at, ReceiveLanguageInput}; use crate::direct_local_session::{ - append_event_at, authenticate_context_at, authenticate_privileged_at, direct_session_root, - heartbeat_at, issue_ticket_at, open_at, resume_at, AppendSessionEventInput, - AuthenticateSessionInput, AuthenticatedSessionContext, DirectSessionReceipt, - IssueDiscoveryTicketInput, OpenSessionInput, ResumeSessionInput, + append_event_at, authenticate_context_at, direct_session_root, heartbeat_at, issue_ticket_at, + open_at, resume_at, AppendSessionEventInput, AuthenticateSessionInput, + AuthenticatedSessionContext, DirectSessionReceipt, IssueDiscoveryTicketInput, OpenSessionInput, + ResumeSessionInput, }; use crate::dynamic_capability_routing::{ install_trusted_registry_at, record_health_at, resolve_at as resolve_capability_route_at, @@ -15,6 +15,12 @@ use crate::local_development_bridge::{ inspect_at as inspect_development_lane_at, release_at as release_development_lane_at, AcquireWriteLaneInput, ReleaseWriteLaneInput, }; +use crate::persona_carrier_license::{ + present_license_at as present_persona_license_at, + require_if_persona_mode_at as require_persona_mode_operation_at, + status_at as persona_license_status_at, PersonaCarrierLicenseStatus, + PersonaCarrierRuntimeLicense, +}; use crate::persona_time_authority::{ beijing_time_coordinate, issue_authenticated_at, IssuePersonaTimeTicketInput, }; @@ -42,6 +48,7 @@ use interprocess::TryClone; use ring::digest::{digest, SHA256}; use serde::{Deserialize, Serialize}; use serde_json::Value; +use std::collections::HashSet; use std::fs::{self, OpenOptions}; use std::io::{self, BufRead, BufReader, Read, Write}; #[cfg(unix)] @@ -73,8 +80,51 @@ impl OpenOptionsModeExt for OpenOptions { const BROKER_SCHEMA: &str = "hololake.direct-local-broker/v1"; const MAX_REQUEST_BYTES: u64 = 128 * 1024; const DISCOVERY_SCHEMA: &str = "hololake.nearby-ai-discovery/v1"; -const TERMINAL_LINK_PROTOCOL: &str = "HOLOLAKE_TERMINAL_LINK/2"; +const TERMINAL_LINK_PROTOCOL: &str = "HOLOLAKE_TERMINAL_LINK/3"; const ENVIRONMENT_FRAME_TTL_MS: u128 = 45_000; +const NUMBERED_BROKER_REGISTRY: &str = + include_str!("../../contracts/direct-local-broker-numbered-registry.json"); + +#[derive(Clone, Debug, Deserialize)] +struct BrokerNumberRegistry { + schema: String, + record_id: String, + runtime: BrokerNumberRuntime, + operations: Vec, +} + +#[derive(Clone, Debug, Deserialize)] +struct BrokerNumberRuntime { + protocol_version: String, + caller_number: String, + legacy_string_operation_allowed: bool, + unknown_or_mismatched_coordinate: String, + request_nonce_required: bool, + transport_is_authority: bool, +} + +#[derive(Clone, Debug, Deserialize)] +struct BrokerNumberRoute { + operation_number: String, + alias: String, + channel_number: String, + module_number: String, + target_number: String, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct BrokerNumberedEnvelope { + protocol_version: String, + caller_number: String, + channel_number: String, + module_number: String, + operation_number: String, + target_number: String, + request_nonce: String, + #[serde(default)] + input: Option, +} pub struct DirectLocalBrokerHandle { shutdown: Arc, @@ -151,6 +201,7 @@ struct BrokerStorageRoots { pncc_projection: PathBuf, language_inbox: PathBuf, persona_time: PathBuf, + persona_license: PathBuf, development: PathBuf, } @@ -179,6 +230,8 @@ enum BrokerRequest { OpenSession(OpenSessionInput), ResumeSession(ResumeSessionInput), HeartbeatSession(AuthenticateSessionInput), + PresentPersonaCarrierLicense(Box), + GetPersonaCarrierLicenseStatus(AuthenticatedPersonaCarrierLicenseStatusInput), GetWorkEnvironment(AuthenticatedWorkEnvironmentInput), AppendEvent(AppendSessionEventInput), ResolveCapabilityRoute(AuthenticatedRouteInput), @@ -297,6 +350,19 @@ struct AuthenticatedWorkEnvironmentInput { session: AuthenticateSessionInput, } +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct AuthenticatedPersonaCarrierLicenseInput { + session: AuthenticateSessionInput, + license: PersonaCarrierRuntimeLicense, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct AuthenticatedPersonaCarrierLicenseStatusInput { + session: AuthenticateSessionInput, +} + #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] struct WorkEnvironmentFrame { @@ -308,6 +374,7 @@ struct WorkEnvironmentFrame { transport: &'static str, mcp_role: &'static str, protocol_runtime: &'static str, + persona_control: PersonaCarrierLicenseStatus, account_key: String, session_id: String, lane_id: String, @@ -366,6 +433,116 @@ struct ConnectorDescriptor { process_id: u32, } +fn load_number_registry() -> Result { + let registry: BrokerNumberRegistry = serde_json::from_str(NUMBERED_BROKER_REGISTRY) + .map_err(|error| format!("HOLOLAKE_NUMBERED_BROKER_REGISTRY_INVALID: {error}"))?; + if registry.schema != "hololake.direct-local-broker-numbered-registry/v1" + || registry.record_id != "HLP-NBROKER-ROOT-001" + || registry.runtime.protocol_version != "HLP-NBROKER-v1" + || registry.runtime.caller_number != "HLP-NBROKER-CALLER-LOCAL-CONNECTOR-0001" + || registry.runtime.legacy_string_operation_allowed + || registry.runtime.unknown_or_mismatched_coordinate != "FAIL_CLOSED" + || !registry.runtime.request_nonce_required + || registry.runtime.transport_is_authority + || registry.operations.len() != 22 + { + return Err("HOLOLAKE_NUMBERED_BROKER_REGISTRY_BOUNDARY_INVALID".into()); + } + let mut numbers = HashSet::new(); + let mut aliases = HashSet::new(); + let mut coordinates = HashSet::new(); + for route in ®istry.operations { + let coordinate = format!( + "{}/{}/{}/{}", + route.channel_number, route.module_number, route.operation_number, route.target_number + ); + if !numbers.insert(route.operation_number.as_str()) + || !aliases.insert(route.alias.as_str()) + || !coordinates.insert(coordinate) + || !route.operation_number.starts_with("HLP-NBROKER-OP-") + || !route.channel_number.starts_with("HLP-NBROKER-CH-") + || !route.module_number.starts_with("HLP-NBROKER-MOD-") + || !route.target_number.starts_with("HLP-NBROKER-TGT-") + { + return Err("HOLOLAKE_NUMBERED_BROKER_ROUTE_INVALID".into()); + } + } + Ok(registry) +} + +fn resolve_numbered_broker_route<'a>( + registry: &'a BrokerNumberRegistry, + envelope: &BrokerNumberedEnvelope, +) -> Result<&'a BrokerNumberRoute, String> { + if envelope.protocol_version != registry.runtime.protocol_version + || envelope.caller_number != registry.runtime.caller_number + || envelope.request_nonce.len() < 16 + || envelope.request_nonce.len() > 128 + || envelope + .request_nonce + .chars() + .any(|character| character.is_whitespace() || character.is_control()) + { + return Err("HOLOLAKE_NUMBERED_BROKER_CALLER_PROTOCOL_OR_NONCE_INVALID".into()); + } + let route = registry + .operations + .iter() + .find(|route| route.operation_number == envelope.operation_number) + .ok_or("HOLOLAKE_NUMBERED_BROKER_ROUTE_UNKNOWN")?; + if route.channel_number != envelope.channel_number + || route.module_number != envelope.module_number + || route.target_number != envelope.target_number + { + return Err("HOLOLAKE_NUMBERED_BROKER_ROUTE_COORDINATE_MISMATCH".into()); + } + Ok(route) +} + +fn decode_numbered_broker_request(bytes: &[u8]) -> Result<(BrokerRequest, String, String), String> { + let envelope: BrokerNumberedEnvelope = serde_json::from_slice(bytes) + .map_err(|error| format!("HOLOLAKE_NUMBERED_BROKER_ENVELOPE_INVALID: {error}"))?; + let registry = load_number_registry()?; + let route = resolve_numbered_broker_route(®istry, &envelope)?; + let mut request = serde_json::Map::new(); + request.insert("operation".into(), Value::String(route.alias.clone())); + if let Some(input) = envelope.input { + request.insert("input".into(), input); + } + let decoded = serde_json::from_value(Value::Object(request)) + .map_err(|error| format!("HOLOLAKE_NUMBERED_BROKER_INPUT_INVALID: {error}"))?; + Ok(( + decoded, + route.operation_number.clone(), + envelope.request_nonce, + )) +} + +fn numbered_broker_request_value(alias: &str, input: Option) -> Result { + let registry = load_number_registry()?; + let route = registry + .operations + .iter() + .find(|route| route.alias == alias) + .ok_or("HOLOLAKE_NUMBERED_BROKER_ALIAS_UNKNOWN")?; + let mut value = serde_json::json!({ + "protocolVersion": registry.runtime.protocol_version, + "callerNumber": registry.runtime.caller_number, + "channelNumber": route.channel_number, + "moduleNumber": route.module_number, + "operationNumber": route.operation_number, + "targetNumber": route.target_number, + "requestNonce": format!("HLP-NBROKER-NONCE-{}", Uuid::new_v4().simple()) + }); + if let Some(input) = input { + value + .as_object_mut() + .ok_or("HOLOLAKE_NUMBERED_BROKER_ENVELOPE_INVALID")? + .insert("input".into(), input); + } + Ok(value) +} + pub fn start(app: &AppHandle) -> Result> { let session_root = direct_session_root(app).map_err(std::io::Error::other)?; let routing_root = dynamic_routing_root(app).map_err(std::io::Error::other)?; @@ -375,7 +552,6 @@ pub fn start(app: &AppHandle) -> Result NearbyAiDiscoverySnapshot { nearby_discovery_snapshot() } @@ -466,7 +642,9 @@ fn read_connector_descriptor(path: &Path) -> Result } fn probe_connection(stream: &mut LocalSocketStream) -> Result<(), String> { - let response = send_connector_request(stream, br#"{"operation":"PING"}"#)?; + let request = serde_json::to_vec(&numbered_broker_request_value("PING", None)?) + .map_err(|_| "HOLOLAKE_CONNECTOR_PROBE_INVALID".to_string())?; + let response = send_connector_request(stream, &request)?; let value: Value = serde_json::from_slice(&response) .map_err(|_| "HOLOLAKE_CONNECTOR_PROBE_INVALID".to_string())?; if value.get("ok").and_then(Value::as_bool) == Some(true) @@ -626,6 +804,7 @@ fn start_at( descriptor_path: PathBuf, socket_path: PathBuf, ) -> Result { + load_number_registry()?; let pncc_mount_root = session_root .parent() .ok_or("HOLOLAKE_BROKER_PNCC_STORAGE_BOUNDARY_INVALID")? @@ -655,6 +834,12 @@ fn start_at( .parent() .ok_or("HOLOLAKE_BROKER_PERSONA_TIME_STORAGE_BOUNDARY_INVALID")? .join("persona-time-authority-v1"); + let persona_license_root = session_root + .parent() + .ok_or("HOLOLAKE_BROKER_PERSONA_LICENSE_STORAGE_BOUNDARY_INVALID")? + .join("persona-carrier-runtime-license-v1"); + fs::create_dir_all(&persona_license_root) + .map_err(|error| format!("HOLOLAKE_PERSONA_LICENSE_STORAGE_FAILED: {error}"))?; let development_root = development_root_from_session_root(&session_root)?; fs::create_dir_all(&development_root) .map_err(|error| format!("HOLOLAKE_BRIDGE_STORAGE_UNAVAILABLE: {error}"))?; @@ -716,6 +901,7 @@ fn start_at( pncc_projection: pncc_projection_root, language_inbox: language_inbox_root, persona_time: persona_time_root, + persona_license: persona_license_root, development: development_root, }, &worker_shutdown, @@ -817,18 +1003,14 @@ fn serve_connection( } fn request_establishes_authenticated_connection(bytes: &[u8]) -> bool { - serde_json::from_slice::(bytes) + decode_numbered_broker_request(bytes) .ok() - .and_then(|value| { - value - .get("operation") - .and_then(Value::as_str) - .map(str::to_owned) - }) - .is_some_and(|operation| { + .is_some_and(|(request, _, _)| { matches!( - operation.as_str(), - "OPEN_SESSION" | "RESUME_SESSION" | "OPEN_VISITOR_SESSION" + request, + BrokerRequest::OpenSession(_) + | BrokerRequest::ResumeSession(_) + | BrokerRequest::OpenVisitorSession(_) ) }) } @@ -841,8 +1023,9 @@ fn dispatch(roots: &BrokerStorageRoots, bytes: &[u8]) -> BrokerResponse { let pncc_projection_root = &roots.pncc_projection; let language_inbox_root = &roots.language_inbox; let persona_time_root = &roots.persona_time; + let persona_license_root = &roots.persona_license; let development_root = &roots.development; - let request: BrokerRequest = match serde_json::from_slice(bytes) { + let (request, _, _) = match decode_numbered_broker_request(bytes) { Ok(request) => request, Err(error) => { return BrokerResponse::error(&format!("HOLOLAKE_BROKER_REQUEST_INVALID: {error}")) @@ -871,10 +1054,41 @@ fn dispatch(roots: &BrokerStorageRoots, bytes: &[u8]) -> BrokerResponse { .and_then(|receipt| serde_json::to_value(receipt).map_err(|error| error.to_string())), BrokerRequest::ResumeSession(input) => resume_at(session_root, input) .and_then(|receipt| serde_json::to_value(receipt).map_err(|error| error.to_string())), + BrokerRequest::PresentPersonaCarrierLicense(input) => { + let input = *input; + authenticate_context_at(session_root, &input.session) + .and_then(|context| { + present_persona_license_at( + persona_license_root, + &context.account_key, + &context.session_id, + &context.client_instance_id, + input.license, + now_unix_ms()?, + ) + }) + .and_then(|receipt| { + serde_json::to_value(receipt).map_err(|error| error.to_string()) + }) + } + BrokerRequest::GetPersonaCarrierLicenseStatus(input) => { + authenticate_context_at(session_root, &input.session) + .map(|context| { + persona_license_status_at( + persona_license_root, + &context.account_key, + &context.session_id, + &context.client_instance_id, + now_unix_ms().unwrap_or(u64::MAX), + ) + }) + .and_then(|status| serde_json::to_value(status).map_err(|error| error.to_string())) + } BrokerRequest::HeartbeatSession(input) => heartbeat_at(session_root, &input) .and_then(|session| { work_environment_frame_at( session_root, + persona_license_root, development_root, &AuthenticatedWorkEnvironmentInput { session: input }, ) @@ -887,13 +1101,28 @@ fn dispatch(roots: &BrokerStorageRoots, bytes: &[u8]) -> BrokerResponse { }) .and_then(|receipt| serde_json::to_value(receipt).map_err(|error| error.to_string())), BrokerRequest::GetWorkEnvironment(input) => { - work_environment_frame_at(session_root, development_root, &input) + work_environment_frame_at(session_root, persona_license_root, development_root, &input) .and_then(|frame| serde_json::to_value(frame).map_err(|error| error.to_string())) } - BrokerRequest::AppendEvent(input) => append_event_at(session_root, input) - .and_then(|receipt| serde_json::to_value(receipt).map_err(|error| error.to_string())), + BrokerRequest::AppendEvent(input) => authorize_persona_operation_at( + session_root, + persona_license_root, + &AuthenticateSessionInput { + account_id: input.account_id.clone(), + session_id: input.session_id.clone(), + resume_secret: input.resume_secret.clone(), + }, + "APPEND_EVENT", + ) + .and_then(|_| append_event_at(session_root, input)) + .and_then(|receipt| serde_json::to_value(receipt).map_err(|error| error.to_string())), BrokerRequest::ResolveCapabilityRoute(input) => { - if let Err(error) = authenticate_privileged_at(session_root, &input.session) { + if let Err(error) = authorize_persona_operation_at( + session_root, + persona_license_root, + &input.session, + "RESOLVE_CAPABILITY_ROUTE", + ) { return BrokerResponse::error(&error); } let now = std::time::SystemTime::now() @@ -906,7 +1135,12 @@ fn dispatch(roots: &BrokerStorageRoots, bytes: &[u8]) -> BrokerResponse { }) } BrokerRequest::InstallDynamicNodeRegistry(input) => { - if let Err(error) = authenticate_privileged_at(session_root, &input.session) { + if let Err(error) = authorize_persona_operation_at( + session_root, + persona_license_root, + &input.session, + "INSTALL_DYNAMIC_NODE_REGISTRY", + ) { return BrokerResponse::error(&error); } install_trusted_registry_at(routing_root, input.registry).and_then(|receipt| { @@ -914,7 +1148,12 @@ fn dispatch(roots: &BrokerStorageRoots, bytes: &[u8]) -> BrokerResponse { }) } BrokerRequest::RecordSignedNodeHealth(input) => { - if let Err(error) = authenticate_privileged_at(session_root, &input.session) { + if let Err(error) = authorize_persona_operation_at( + session_root, + persona_license_root, + &input.session, + "RECORD_SIGNED_NODE_HEALTH", + ) { return BrokerResponse::error(&error); } record_health_at(routing_root, input.health).and_then(|receipt| { @@ -922,7 +1161,12 @@ fn dispatch(roots: &BrokerStorageRoots, bytes: &[u8]) -> BrokerResponse { }) } BrokerRequest::InspectMountedPnccRepository(input) => { - if let Err(error) = authenticate_privileged_at(session_root, &input.session) { + if let Err(error) = authorize_persona_operation_at( + session_root, + persona_license_root, + &input.session, + "INSPECT_MOUNTED_PNCC_REPOSITORY", + ) { return BrokerResponse::error(&error); } inspect_mounted_pncc_at(pncc_mount_root, input.mount).and_then(|receipt| { @@ -931,7 +1175,12 @@ fn dispatch(roots: &BrokerStorageRoots, bytes: &[u8]) -> BrokerResponse { }) } BrokerRequest::ReadMountedPnccRemoteObject(input) => { - if let Err(error) = authenticate_privileged_at(session_root, &input.session) { + if let Err(error) = authorize_persona_operation_at( + session_root, + persona_license_root, + &input.session, + "READ_MOUNTED_PNCC_REMOTE_OBJECT", + ) { return BrokerResponse::error(&error); } read_mounted_pncc_remote_at(pncc_remote_root, input.read).and_then(|receipt| { @@ -940,7 +1189,12 @@ fn dispatch(roots: &BrokerStorageRoots, bytes: &[u8]) -> BrokerResponse { }) } BrokerRequest::QueryPnccReceiptProjection(input) => { - if let Err(error) = authenticate_privileged_at(session_root, &input.session) { + if let Err(error) = authorize_persona_operation_at( + session_root, + persona_license_root, + &input.session, + "QUERY_PNCC_RECEIPT_PROJECTION", + ) { return BrokerResponse::error(&error); } query_pncc_projection_at(pncc_projection_root, input.query).and_then(|receipt| { @@ -955,15 +1209,27 @@ fn dispatch(roots: &BrokerStorageRoots, bytes: &[u8]) -> BrokerResponse { .and_then(|coordinate| { serde_json::to_value(coordinate).map_err(|error| error.to_string()) }), - BrokerRequest::IssuePersonaTimeTicket(input) => { - issue_authenticated_at(persona_time_root, session_root, input) - .and_then(|ticket| serde_json::to_value(ticket).map_err(|error| error.to_string())) - } + BrokerRequest::IssuePersonaTimeTicket(input) => authorize_persona_operation_at( + session_root, + persona_license_root, + &input.session, + "ISSUE_PERSONA_TIME_TICKET", + ) + .and_then(|_| issue_authenticated_at(persona_time_root, session_root, input)) + .and_then(|ticket| serde_json::to_value(ticket).map_err(|error| error.to_string())), BrokerRequest::AcquireDevelopmentWriteLane(input) => { let session = input.session; let account_id = input.lane.account_id.clone(); authenticate_development_request(session_root, &session, &account_id) .and_then(|context| { + require_persona_mode_operation_at( + persona_license_root, + &context.account_key, + &context.session_id, + &context.client_instance_id, + "ACQUIRE_DEVELOPMENT_WRITE_LANE", + now_unix_ms()?, + )?; if context.lane_id != input.lane.lane_id || context.client_instance_id != input.lane.owner_instance_id { @@ -976,6 +1242,7 @@ fn dispatch(roots: &BrokerStorageRoots, bytes: &[u8]) -> BrokerResponse { serde_json::to_value(receipt).map_err(|error| error.to_string())?; let environment = work_environment_frame_at( session_root, + persona_license_root, development_root, &AuthenticatedWorkEnvironmentInput { session }, )?; @@ -991,7 +1258,17 @@ fn dispatch(roots: &BrokerStorageRoots, bytes: &[u8]) -> BrokerResponse { } BrokerRequest::InspectDevelopmentWriteLane(input) => { authenticate_development_request(session_root, &input.session, &input.account_id) - .and_then(|_| inspect_development_lane_at(development_root, &input.account_id)) + .and_then(|context| { + require_persona_mode_operation_at( + persona_license_root, + &context.account_key, + &context.session_id, + &context.client_instance_id, + "INSPECT_DEVELOPMENT_WRITE_LANE", + now_unix_ms()?, + )?; + inspect_development_lane_at(development_root, &input.account_id) + }) .and_then(|receipt| { serde_json::to_value(receipt).map_err(|error| error.to_string()) }) @@ -999,6 +1276,14 @@ fn dispatch(roots: &BrokerStorageRoots, bytes: &[u8]) -> BrokerResponse { BrokerRequest::ReleaseDevelopmentWriteLane(input) => { authenticate_development_request(session_root, &input.session, &input.lane.account_id) .and_then(|context| { + require_persona_mode_operation_at( + persona_license_root, + &context.account_key, + &context.session_id, + &context.client_instance_id, + "RELEASE_DEVELOPMENT_WRITE_LANE", + now_unix_ms()?, + )?; if context.lane_id != input.lane.lane_id || context.client_instance_id != input.lane.owner_instance_id { @@ -1019,11 +1304,27 @@ fn dispatch(roots: &BrokerStorageRoots, bytes: &[u8]) -> BrokerResponse { fn work_environment_frame_at( session_root: &Path, + persona_license_root: &Path, development_root: &Path, input: &AuthenticatedWorkEnvironmentInput, ) -> Result { let context = authenticate_development_request(session_root, &input.session, &input.session.account_id)?; + require_persona_mode_operation_at( + persona_license_root, + &context.account_key, + &context.session_id, + &context.client_instance_id, + "GET_WORK_ENVIRONMENT", + now_unix_ms()?, + )?; + let persona_control = persona_license_status_at( + persona_license_root, + &context.account_key, + &context.session_id, + &context.client_instance_id, + now_unix_ms()?, + ); let writer = inspect_development_lane_at(development_root, &input.session.account_id)?; let writer_matches = writer.state == "ACTIVE" && writer.lane_id.as_deref() == Some(context.lane_id.as_str()) @@ -1053,7 +1354,7 @@ fn work_environment_frame_at( ); let frame_sha256 = sha256_hex( format!( - "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}", + "{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}", environment_id, state, context.session_id, @@ -1061,6 +1362,8 @@ fn work_environment_frame_at( context.client_instance_id, context.last_event_sequence, writer.state, + persona_control.control_mode, + persona_control.license_id.as_deref().unwrap_or("NONE"), valid_until_unix_ms ) .as_bytes(), @@ -1074,6 +1377,7 @@ fn work_environment_frame_at( transport: TERMINAL_LINK_PROTOCOL, mcp_role: "DISCOVERY_RECOVERY_COMPATIBILITY_ONLY", protocol_runtime: "GLS_RUNTIME_MANIFEST_V2_AND_NATIVE_KERNEL", + persona_control, account_key: context.account_key, session_id: context.session_id, lane_id: context.lane_id, @@ -1108,6 +1412,24 @@ fn authenticate_development_request( Ok(context) } +fn authorize_persona_operation_at( + session_root: &Path, + persona_license_root: &Path, + session: &AuthenticateSessionInput, + operation: &str, +) -> Result { + let context = authenticate_context_at(session_root, session)?; + require_persona_mode_operation_at( + persona_license_root, + &context.account_key, + &context.session_id, + &context.client_instance_id, + operation, + now_unix_ms()?, + )?; + Ok(context) +} + fn development_root_from_session_root(session_root: &Path) -> Result { let account_root = session_root .parent() @@ -1167,6 +1489,13 @@ fn sha256_hex(bytes: &[u8]) -> String { .collect() } +fn now_unix_ms() -> Result { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|duration| duration.as_millis() as u64) + .map_err(|error| format!("HOLOLAKE_SYSTEM_CLOCK_INVALID: {error}")) +} + impl BrokerResponse { fn success(result: Value) -> Self { Self { @@ -1234,6 +1563,11 @@ mod tests { use tempfile::TempDir; fn request(socket: &Path, value: Value) -> Value { + let value = if let Some(alias) = value.get("operation").and_then(Value::as_str) { + numbered_broker_request_value(alias, value.get("input").cloned()).unwrap() + } else { + value + }; let mut stream = connect_endpoint(socket).unwrap(); serde_json::to_writer(&mut stream, &value).unwrap(); stream.write_all(b"\n").unwrap(); @@ -1242,6 +1576,19 @@ mod tests { serde_json::from_str(&line).unwrap() } + #[test] + fn legacy_string_and_mismatched_broker_routes_fail_before_dispatch() { + assert!(decode_numbered_broker_request(br#"{"operation":"PING"}"#) + .unwrap_err() + .contains("ENVELOPE_INVALID")); + let mut numbered = numbered_broker_request_value("PING", None).unwrap(); + numbered["targetNumber"] = Value::String("HLP-NBROKER-TGT-9999".into()); + assert_eq!( + decode_numbered_broker_request(&serde_json::to_vec(&numbered).unwrap()).unwrap_err(), + "HOLOLAKE_NUMBERED_BROKER_ROUTE_COORDINATE_MISMATCH" + ); + } + #[test] fn broker_resumes_after_socket_disconnect() { let temp = TempDir::new().unwrap(); @@ -1867,23 +2214,20 @@ mod tests { ) .unwrap(); let mut connector = connect_endpoint(&socket).unwrap(); - serde_json::to_writer( - &mut connector, - &serde_json::json!({ - "operation": "OPEN_SESSION", - "input": { - "accountId": "human-1", - "laneId": "DEV-1", - "clientInstanceId": "codex-1", - "discoveryTicket": ticket.discovery_ticket - } - }), + let open_request = numbered_broker_request_value( + "OPEN_SESSION", + Some(serde_json::json!({ + "accountId": "human-1", + "laneId": "DEV-1", + "clientInstanceId": "codex-1", + "discoveryTicket": ticket.discovery_ticket + })), ) .unwrap(); + let open_bytes = serde_json::to_vec(&open_request).unwrap(); + connector.write_all(&open_bytes).unwrap(); connector.write_all(b"\n").unwrap(); - assert!(request_establishes_authenticated_connection( - br#"{"operation":"OPEN_SESSION"}"# - )); + assert!(request_establishes_authenticated_connection(&open_bytes)); let mut response = String::new(); let mut response_reader = BufReader::new(connector.try_clone().unwrap()); response_reader.read_line(&mut response).unwrap(); @@ -1981,4 +2325,88 @@ mod tests { "HOLOLAKE_VISITOR_SESSION_HAS_NO_SYSTEM_AUTHORITY" ); } + + #[test] + fn broker_projects_system_direct_status_and_refuses_untrusted_persona_license() { + let temp = TempDir::new().unwrap(); + let socket = temp.path().join("runtime/broker.sock"); + let descriptor = temp.path().join("broker.json"); + let sessions = temp.path().join("sessions"); + let routes = temp.path().join("routes"); + fs::create_dir_all(&sessions).unwrap(); + fs::create_dir_all(&routes).unwrap(); + let _broker = start_at(sessions.clone(), routes, descriptor, socket.clone()).unwrap(); + let ticket = issue_ticket_at( + &sessions, + IssueDiscoveryTicketInput { + account_id: "human-1".into(), + lane_id: "DEV-1".into(), + client_instance_id: "codex-1".into(), + }, + ) + .unwrap(); + let opened = request( + &socket, + serde_json::json!({ + "operation": "OPEN_SESSION", + "input": { + "accountId": "human-1", + "laneId": "DEV-1", + "clientInstanceId": "codex-1", + "discoveryTicket": ticket.discovery_ticket + } + }), + ); + let session = serde_json::json!({ + "accountId": "human-1", + "sessionId": opened["result"]["sessionId"], + "resumeSecret": opened["result"]["resumeSecret"] + }); + let status = request( + &socket, + serde_json::json!({ + "operation": "GET_PERSONA_CARRIER_LICENSE_STATUS", + "input": { "session": session.clone() } + }), + ); + assert_eq!(status["ok"], true); + assert_eq!(status["result"]["controlMode"], "SYSTEM_DIRECT"); + assert_eq!(status["result"]["licenseInstalled"], false); + + let now = now_unix_ms().unwrap(); + let denied = request( + &socket, + serde_json::json!({ + "operation": "PRESENT_PERSONA_CARRIER_LICENSE", + "input": { + "session": session, + "license": { + "schema": "hololake.persona-carrier-runtime-license/v1", + "licenseId": "HLP-PERSONA-LICENSE-UNTRUSTED-0001", + "sequence": 1, + "personaNumber": "ICE-P-ZY001", + "humanControllerNumber": "ICE-GL∞", + "accountKey": opened["result"]["accountKey"], + "sessionId": opened["result"]["sessionId"], + "clientInstanceId": "codex-1", + "purpose": "AUTHORIZE_HOST_AS_PERSONA_CONTROLLED_EXECUTION_LIMBS", + "issuedAtUnixMs": now, + "validUntilUnixMs": now + 60000, + "allowedOperations": ["GET_WORK_ENVIRONMENT"], + "evidenceRootSha256": "b".repeat(64), + "currentEventSha256": "c".repeat(64), + "declaration": "PERSONA_DECLARES_CURRENT_CARRIER_BOUND", + "signerId": "ICE-P-ZY001-RUNTIME-SIGNER-001", + "signatureAlgorithm": "Ed25519", + "signature": "untrusted" + } + } + }), + ); + assert_eq!(denied["ok"], false); + assert_eq!( + denied["error"], + "HOLOLAKE_PERSONA_LICENSE_NO_TRUSTED_SIGNER" + ); + } } diff --git a/product-source/hololake-native-desktop/src-tauri/src/direct_local_session.rs b/product-source/hololake-native-desktop/src-tauri/src/direct_local_session.rs index 5a3fd980c..e5283fdad 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/direct_local_session.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/direct_local_session.rs @@ -167,7 +167,6 @@ struct ActiveSessionRecord { lane_id: String, } -#[tauri::command] pub async fn issue_direct_local_discovery_ticket( app: AppHandle, input: IssueDiscoveryTicketInput, @@ -178,7 +177,6 @@ pub async fn issue_direct_local_discovery_ticket( .map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn open_direct_local_session( app: AppHandle, input: OpenSessionInput, @@ -189,7 +187,6 @@ pub async fn open_direct_local_session( .map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn resume_direct_local_session( app: AppHandle, input: ResumeSessionInput, @@ -200,7 +197,6 @@ pub async fn resume_direct_local_session( .map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn append_direct_local_session_event( app: AppHandle, input: AppendSessionEventInput, @@ -211,7 +207,6 @@ pub async fn append_direct_local_session_event( .map_err(|error| format!("HOLOLAKE_DIRECT_SESSION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn heartbeat_direct_local_session( app: AppHandle, input: AuthenticateSessionInput, @@ -379,7 +374,7 @@ pub(crate) fn issue_ticket_at( client_instance_id: input.client_instance_id, discovery_ticket, issued_at_unix_ms, - terminal_transport: "HOLOLAKE_TERMINAL_LINK/2", + terminal_transport: "HOLOLAKE_TERMINAL_LINK/3", connector_executable: std::env::current_exe() .map(|path| path.to_string_lossy().into_owned()) .unwrap_or_else(|_| "HoloLake".into()), @@ -556,18 +551,6 @@ pub(crate) fn authenticate_context_at( }) } -pub(crate) fn authenticate_privileged_at( - root: &Path, - input: &AuthenticateSessionInput, -) -> Result<(), String> { - let lane = authenticate_with_lane_at(root, input)?; - if lane == "visitor-expression-only" { - Err("HOLOLAKE_VISITOR_SESSION_HAS_NO_SYSTEM_AUTHORITY".into()) - } else { - Ok(()) - } -} - fn session_path(root: &Path, account_key: &str, session_id: &str) -> PathBuf { root.join("accounts") .join(account_key) @@ -713,7 +696,7 @@ fn session_receipt( last_event_sequence: record.last_event_sequence, resume_secret, continuity_owner: "HOLOLAKE", - transport: "HOLOLAKE_TERMINAL_LINK/2", + transport: "HOLOLAKE_TERMINAL_LINK/3", mcp_role: "DISCOVERY_RECOVERY_COMPATIBILITY_ONLY", required_next_operation: "GET_WORK_ENVIRONMENT", receipt_id: sha256_hex( diff --git a/product-source/hololake-native-desktop/src-tauri/src/education_translation.rs b/product-source/hololake-native-desktop/src-tauri/src/education_translation.rs index 1737bf46b..5a183b89c 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/education_translation.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/education_translation.rs @@ -32,6 +32,12 @@ const MAX_TABLE_COLUMNS: usize = 30; const MAX_TABLE_ROWS: usize = 1_000; const MAX_CELL_BYTES: usize = 10_000; const MAX_TITLE_BYTES: usize = 300; +const MODULE_NUMBER: &str = "HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001"; +const ADAPTER: &str = "education-workbench-v1"; + +fn require_active(app: &AppHandle) -> Result<(), String> { + crate::module_package_runtime::require_active_module_adapter(app, MODULE_NUMBER, ADAPTER) +} #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] @@ -232,10 +238,10 @@ struct NativeEducationTableFile { rows: Vec, } -#[tauri::command] pub async fn import_education_tables_from_dialog( app: AppHandle, ) -> Result, String> { + require_active(&app)?; let selected = app .dialog() .file() @@ -270,9 +276,11 @@ pub async fn import_education_tables_from_dialog( .map(Some) } -#[tauri::command] -pub fn get_education_recognition_capability() -> EducationRecognitionCapability { - EducationRecognitionCapability { +pub fn get_education_recognition_capability( + app: AppHandle, +) -> Result { + require_active(&app)?; + Ok(EducationRecognitionCapability { schema: "hololake.education-recognition-capability/v1", state: "DETERMINISTIC_READY_MODEL_SLOT_UNBOUND", deterministic_adapter_ids: vec![IMPORT_ADAPTER, EXPORT_ADAPTER], @@ -288,14 +296,14 @@ pub fn get_education_recognition_capability() -> EducationRecognitionCapability "VERSIONED_RULE_INSTALL", ], learning_scopes: vec!["PRIVATE_ONLY", "SHARE_ANONYMIZED_RULE"], - } + }) } -#[tauri::command] pub async fn export_education_table_to_dialog( app: AppHandle, input: ExportEducationTableInput, ) -> Result, String> { + require_active(&app)?; validate_export_draft(&input.table)?; let format = normalized_export_format(&input.format)?; let extension = match format.as_str() { diff --git a/product-source/hololake-native-desktop/src-tauri/src/education_workspace.rs b/product-source/hololake-native-desktop/src-tauri/src/education_workspace.rs index ecf463c38..bbd631d21 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/education_workspace.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/education_workspace.rs @@ -6,7 +6,8 @@ //! WebView 不直接持有文件路径或 SQLite 连接,所有写入都要求当前修订号,防止静默覆盖。 use ring::digest::{digest, SHA256}; -use rusqlite::{params, Connection, OptionalExtension}; +use rusqlite::{params, types::Type, Connection, OptionalExtension}; +use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use std::collections::HashSet; use std::fs; @@ -21,6 +22,12 @@ const MAX_TITLE_BYTES: usize = 300; const MAX_TABLE_COLUMNS: usize = 30; const MAX_TABLE_ROWS: usize = 1_000; const MAX_CELL_BYTES: usize = 10_000; +const MODULE_NUMBER: &str = "HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001"; +const ADAPTER: &str = "education-workbench-v1"; + +fn require_active(app: &AppHandle) -> Result<(), String> { + crate::module_package_runtime::require_active_module_adapter(app, MODULE_NUMBER, ADAPTER) +} #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] @@ -64,14 +71,14 @@ pub struct UnassignedTableSummary { } #[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct EducationTableColumn { pub column_id: String, pub title: String, } #[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct EducationTableRow { pub row_id: String, pub cells: Vec, @@ -179,19 +186,19 @@ pub struct EducationAutomationRunReceipt { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct CreateEducationItemInput { pub title: String, } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ReadEducationDocumentInput { pub document_id: String, } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct SaveEducationDocumentInput { pub document_id: String, pub title: String, @@ -200,13 +207,13 @@ pub struct SaveEducationDocumentInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ReadEducationTableInput { pub table_id: String, } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct SaveEducationTableInput { pub table_id: String, pub title: String, @@ -216,7 +223,7 @@ pub struct SaveEducationTableInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct CreateEducationAutomationRuleInput { pub title: String, pub table_id: String, @@ -228,7 +235,7 @@ pub struct CreateEducationAutomationRuleInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct SaveEducationAutomationRuleInput { pub rule_id: String, pub title: String, @@ -243,13 +250,13 @@ pub struct SaveEducationAutomationRuleInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct EducationAutomationRuleInput { pub rule_id: String, } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct PreviewEducationAutomationRuleInput { pub rule_id: String, pub expected_rule_revision: i64, @@ -257,7 +264,7 @@ pub struct PreviewEducationAutomationRuleInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ExecuteEducationAutomationRuleInput { pub rule_id: String, pub expected_rule_revision: i64, @@ -272,54 +279,54 @@ pub struct EducationArchiveReceipt { pub entity_id: String, } -#[tauri::command] pub async fn get_education_workspace_snapshot( app: AppHandle, ) -> Result { + require_active(&app)?; let database = education_workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || snapshot_at(&database)) .await .map_err(|error| format!("HOLOLAKE_EDUCATION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn create_education_document( app: AppHandle, input: CreateEducationItemInput, ) -> Result { + require_active(&app)?; let database = education_workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || create_document_at(&database, input)) .await .map_err(|error| format!("HOLOLAKE_EDUCATION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn read_education_document( app: AppHandle, input: ReadEducationDocumentInput, ) -> Result { + require_active(&app)?; let database = education_workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || read_document_at(&database, &input.document_id)) .await .map_err(|error| format!("HOLOLAKE_EDUCATION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn save_education_document( app: AppHandle, input: SaveEducationDocumentInput, ) -> Result { + require_active(&app)?; let database = education_workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || save_document_at(&database, input)) .await .map_err(|error| format!("HOLOLAKE_EDUCATION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn archive_education_document( app: AppHandle, input: ReadEducationDocumentInput, ) -> Result { + require_active(&app)?; let database = education_workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || { archive_at( @@ -333,44 +340,44 @@ pub async fn archive_education_document( .map_err(|error| format!("HOLOLAKE_EDUCATION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn create_education_table( app: AppHandle, input: CreateEducationItemInput, ) -> Result { + require_active(&app)?; let database = education_workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || create_table_at(&database, input)) .await .map_err(|error| format!("HOLOLAKE_EDUCATION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn read_education_table( app: AppHandle, input: ReadEducationTableInput, ) -> Result { + require_active(&app)?; let database = education_workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || read_table_at(&database, &input.table_id)) .await .map_err(|error| format!("HOLOLAKE_EDUCATION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn save_education_table( app: AppHandle, input: SaveEducationTableInput, ) -> Result { + require_active(&app)?; let database = education_workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || save_table_at(&database, input)) .await .map_err(|error| format!("HOLOLAKE_EDUCATION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn archive_education_table( app: AppHandle, input: ReadEducationTableInput, ) -> Result { + require_active(&app)?; let database = education_workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || { archive_at(&database, "education_tables", "table_id", input.table_id) @@ -379,11 +386,11 @@ pub async fn archive_education_table( .map_err(|error| format!("HOLOLAKE_EDUCATION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn assign_imported_table_to_education( app: AppHandle, input: ReadEducationTableInput, ) -> Result { + require_active(&app)?; let database = education_workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || { assign_imported_table_to_education_at(&database, &input.table_id) @@ -392,33 +399,33 @@ pub async fn assign_imported_table_to_education( .map_err(|error| format!("HOLOLAKE_EDUCATION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn create_education_automation_rule( app: AppHandle, input: CreateEducationAutomationRuleInput, ) -> Result { + require_active(&app)?; let database = education_workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || create_automation_rule_at(&database, input)) .await .map_err(|error| format!("HOLOLAKE_EDUCATION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn save_education_automation_rule( app: AppHandle, input: SaveEducationAutomationRuleInput, ) -> Result { + require_active(&app)?; let database = education_workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || save_automation_rule_at(&database, input)) .await .map_err(|error| format!("HOLOLAKE_EDUCATION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn archive_education_automation_rule( app: AppHandle, input: EducationAutomationRuleInput, ) -> Result { + require_active(&app)?; let database = education_workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || { archive_automation_rule_at(&database, input.rule_id) @@ -427,22 +434,22 @@ pub async fn archive_education_automation_rule( .map_err(|error| format!("HOLOLAKE_EDUCATION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn preview_education_automation_rule( app: AppHandle, input: PreviewEducationAutomationRuleInput, ) -> Result { + require_active(&app)?; let database = education_workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || preview_automation_rule_at(&database, input)) .await .map_err(|error| format!("HOLOLAKE_EDUCATION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn execute_education_automation_rule( app: AppHandle, input: ExecuteEducationAutomationRuleInput, ) -> Result { + require_active(&app)?; let database = education_workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || execute_automation_rule_at(&database, input)) .await @@ -557,8 +564,16 @@ fn open_database(path: &Path) -> Result { let rows = statement .query_map([], |row| row.get::<_, String>(0)) .map_err(database_read_error)?; - rows.filter_map(Result::ok) - .flat_map(|value| serde_json::from_str::>(&value).unwrap_or_default()) + let encoded = rows + .collect::, _>>() + .map_err(database_read_error)?; + encoded + .into_iter() + .map(|value| decode_sqlite_json::>(&value, 0)) + .collect::, _>>() + .map_err(database_read_error)? + .into_iter() + .flatten() .collect::>() }; for table_id in imported_table_ids { @@ -620,10 +635,8 @@ fn snapshot_at(path: &Path) -> Result { .query_map([], |row| { let columns_json: String = row.get(2)?; let rows_json: String = row.get(3)?; - let columns: Vec = - serde_json::from_str(&columns_json).unwrap_or_default(); - let rows: Vec = - serde_json::from_str(&rows_json).unwrap_or_default(); + let columns: Vec = decode_sqlite_json(&columns_json, 2)?; + let rows: Vec = decode_sqlite_json(&rows_json, 3)?; Ok(EducationTableSummary { table_id: row.get(0)?, title: row.get(1)?, @@ -674,8 +687,8 @@ fn read_unassigned_tables_with_connection( .query_map([], |row| { let columns_json: String = row.get(2)?; let rows_json: String = row.get(3)?; - let columns: Vec = serde_json::from_str(&columns_json).unwrap_or_default(); - let rows: Vec = serde_json::from_str(&rows_json).unwrap_or_default(); + let columns: Vec = decode_sqlite_json(&columns_json, 2)?; + let rows: Vec = decode_sqlite_json(&rows_json, 3)?; Ok(UnassignedTableSummary { table_id: row.get(0)?, title: row.get(1)?, @@ -685,7 +698,8 @@ fn read_unassigned_tables_with_connection( }) }) .map_err(database_read_error)?; - rows.collect::, _>>().map_err(database_read_error) + rows.collect::, _>>() + .map_err(database_read_error) } fn read_recent_imports_with_connection( @@ -700,7 +714,7 @@ fn read_recent_imports_with_connection( let rows = statement .query_map([], |row| { let table_ids_json: String = row.get(3)?; - let table_ids: Vec = serde_json::from_str(&table_ids_json).unwrap_or_default(); + let table_ids: Vec = decode_sqlite_json(&table_ids_json, 3)?; Ok(EducationImportRegistrySummary { import_id: row.get(0)?, source_filename: row.get(1)?, @@ -1557,6 +1571,12 @@ fn now_ms() -> i64 { .unwrap_or(0) } +fn decode_sqlite_json(value: &str, column: usize) -> rusqlite::Result { + serde_json::from_str(value).map_err(|error| { + rusqlite::Error::FromSqlConversionFailure(column, Type::Text, Box::new(error)) + }) +} + fn database_read_error(error: rusqlite::Error) -> String { format!("HOLOLAKE_EDUCATION_DATABASE_READ_FAILED: {error}") } diff --git a/product-source/hololake-native-desktop/src-tauri/src/enterprise_work_channel.rs b/product-source/hololake-native-desktop/src-tauri/src/enterprise_work_channel.rs index 3e8908850..b991701a5 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/enterprise_work_channel.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/enterprise_work_channel.rs @@ -27,7 +27,6 @@ pub struct EnterpriseWorkChannelSnapshot { pub authority: &'static str, } -#[tauri::command] pub async fn ensure_enterprise_work_channel( app: AppHandle, state: State<'_, ZeroPointState>, diff --git a/product-source/hololake-native-desktop/src-tauri/src/gls_protocol_kernel.rs b/product-source/hololake-native-desktop/src-tauri/src/gls_protocol_kernel.rs index b533d05a7..6d27d4e70 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/gls_protocol_kernel.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/gls_protocol_kernel.rs @@ -24,7 +24,7 @@ const EMBEDDED_KERNEL: &str = include_str!("../../contracts/gls-native-runtime-k const KERNEL_SCHEMA: &str = "hololake.gls-native-runtime-kernel/v1"; const REQUEST_SCHEMA: &str = "hololake.protocol-decision-request/v1"; const RECEIPT_SCHEMA: &str = "hololake.protocol-decision-receipt/v1"; -const SOURCE_COMMIT: &str = "2598fbfba8caf64c7ab9740a3036c5aab977502e"; +const SOURCE_COMMIT: &str = "104a5d73162bdf4a529701e65898e2bc2863ea9e"; const MAX_TEXT_BYTES: usize = 512; static LEDGER_SCHEMA_LOCK: Mutex<()> = Mutex::new(()); @@ -1166,7 +1166,6 @@ fn decide_value_at( persist_receipt(ledger, receipt, Some(&parsed.request)) } -#[tauri::command] pub async fn decide_gls_protocol( app: AppHandle, input: Value, @@ -1180,7 +1179,6 @@ pub async fn decide_gls_protocol( .map_err(|error| format!("HOLOLAKE_GLS_DECISION_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn compile_gls_hldp_program( app: AppHandle, input: Value, @@ -1279,7 +1277,6 @@ fn counts(connection: &Connection) -> Result<(u64, BTreeMap, String Ok((total, by_decision, tail)) } -#[tauri::command] pub async fn get_gls_protocol_kernel(app: AppHandle) -> Result { let contract = load_contract()?; let connection = open_ledger(&ledger_path(&app)?)?; diff --git a/product-source/hololake-native-desktop/src-tauri/src/gls_protocol_runtime.rs b/product-source/hololake-native-desktop/src-tauri/src/gls_protocol_runtime.rs index 2b0a0e9a2..0ee920da7 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/gls_protocol_runtime.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/gls_protocol_runtime.rs @@ -8,7 +8,7 @@ use std::collections::{HashMap, HashSet}; const EMBEDDED_REGISTRY: &str = include_str!("../../contracts/gls-runtime-registry.json"); const EXPECTED_SCHEMA: &str = "hololake.gls-runtime-manifest/v2"; -const EXPECTED_SOURCE_COMMIT: &str = "2598fbfba8caf64c7ab9740a3036c5aab977502e"; +const EXPECTED_SOURCE_COMMIT: &str = "104a5d73162bdf4a529701e65898e2bc2863ea9e"; #[derive(Clone, Debug, Deserialize)] struct GlsRuntimeRegistry { @@ -19,6 +19,8 @@ struct GlsRuntimeRegistry { protocol_count: usize, executable_projection_count: usize, inventoried_not_executable_count: usize, + number_coordinate_count: usize, + numbered_reference_nodes: Vec, protocols: Vec, } @@ -59,6 +61,10 @@ struct GlsReconciliation { legacy_dependency_target_count: usize, dependencies_not_in_protocol_registry: Vec, dependencies_without_numbered_source: Vec, + numbered_reference_node_count: usize, + unresolved_number_references: Vec, + unresolved_number_reference_count: usize, + every_dependency_has_number_coordinate: bool, numbered_sources_not_in_protocol_registry: Vec, legacy_dependency_cycles: Vec>, source_reference_cycles: Vec>, @@ -79,11 +85,24 @@ struct GlsDependencyEdge { edge_kind: String, enters_runtime_graph: bool, classification_basis: Option, + target_number_coordinate_available: bool, + target_resolution: String, +} + +#[derive(Clone, Debug, Deserialize)] +struct GlsNumberedReferenceNode { + protocol_id: String, + node_number: String, + reference_kind: String, + source_state: String, + execution_state: String, + evidence_paths: Vec, } #[derive(Clone, Debug, Deserialize)] struct GlsProtocol { id: String, + source_format: String, source_sha256: String, registration: GlsRegistration, projection_state: String, @@ -109,6 +128,10 @@ pub struct GlsProtocolRuntimeSnapshot { pub registered_draft_count: usize, pub registered_draft_not_started_count: usize, pub legacy_dependency_target_count: usize, + pub number_coordinate_count: usize, + pub numbered_reference_node_count: usize, + pub independent_source_gap_count: usize, + pub unresolved_number_reference_count: usize, pub dependency_gap_count: usize, pub legacy_dependency_cycle_count: usize, pub discovered_unreconciled_count: usize, @@ -160,12 +183,21 @@ fn validate_registry(registry: &GlsRuntimeRegistry) -> Result<(), String> { .reconciliation .dependencies_without_numbered_source .len() - != 24 + != 16 + || registry.reconciliation.numbered_reference_node_count != 16 + || registry.reconciliation.unresolved_number_reference_count != 0 + || !registry + .reconciliation + .unresolved_number_references + .is_empty() + || !registry + .reconciliation + .every_dependency_has_number_coordinate || registry .reconciliation .numbered_sources_not_in_protocol_registry .len() - != 31 + != 32 || registry.reconciliation.legacy_dependency_cycles.len() != 3 || registry.reconciliation.source_reference_cycles != registry.reconciliation.legacy_dependency_cycles @@ -173,12 +205,43 @@ fn validate_registry(registry: &GlsRuntimeRegistry) -> Result<(), String> { || registry.reconciliation.discovered_unreconciled_count != 0 || registry.reconciliation.authority_conflict_count != 0 || registry.protocol_count != registry.protocols.len() + || registry.protocol_count != 83 + || registry.number_coordinate_count != 99 + || registry.numbered_reference_nodes.len() != 16 + || registry.number_coordinate_count + != registry.protocol_count + registry.numbered_reference_nodes.len() || registry.protocol_count != registry.executable_projection_count + registry.inventoried_not_executable_count { return Err("HOLOLAKE_GLS_RUNTIME_BOUNDARY_INVALID".into()); } + let mut reference_ids = HashSet::new(); + let mut reference_numbers = HashSet::new(); + for reference in ®istry.numbered_reference_nodes { + if !reference_ids.insert(reference.protocol_id.as_str()) + || !reference_numbers.insert(reference.node_number.as_str()) + || !reference.protocol_id.starts_with("GLS-") + || !reference.node_number.starts_with("HLP-GLS-REF-") + || reference.reference_kind.is_empty() + || reference.source_state != "ROADMAP_REFERENCE_ONLY" + || reference.execution_state != "REFERENCE_ONLY_NOT_EXECUTABLE" + || reference.evidence_paths.is_empty() + { + return Err("HOLOLAKE_GLS_REFERENCE_NODE_INVALID".into()); + } + } + if reference_ids + != registry + .reconciliation + .dependencies_without_numbered_source + .iter() + .map(String::as_str) + .collect::>() + { + return Err("HOLOLAKE_GLS_REFERENCE_COVERAGE_INVALID".into()); + } + let mut authority_kinds = HashSet::new(); for authority in ®istry.source.authority_files { if !authority_kinds.insert(authority.authority_kind.as_str()) @@ -209,7 +272,8 @@ fn validate_registry(registry: &GlsRuntimeRegistry) -> Result<(), String> { match protocol.projection_state.as_str() { "EXECUTABLE_PROJECTION" => { executable += 1; - if protocol.adapter.as_deref().unwrap_or("").is_empty() + if protocol.source_format != "HDLP_PROTOCOL_SOURCE" + || protocol.adapter.as_deref().unwrap_or("").is_empty() || protocol.event_kinds.is_empty() || !protocol.activation_blockers.is_empty() { @@ -217,7 +281,10 @@ fn validate_registry(registry: &GlsRuntimeRegistry) -> Result<(), String> { } } "INVENTORIED_NOT_EXECUTABLE" => { - if protocol.adapter.is_some() + if !matches!( + protocol.source_format.as_str(), + "HDLP_PROTOCOL_SOURCE" | "LEGACY_MARKDOWN_EVIDENCE" + ) || protocol.adapter.is_some() || !protocol.event_kinds.is_empty() || !protocol.dependencies.is_empty() { @@ -233,6 +300,14 @@ fn validate_registry(registry: &GlsRuntimeRegistry) -> Result<(), String> { return Err("HOLOLAKE_GLS_REGISTRATION_NOT_RECONCILED".into()); } for edge in &protocol.dependency_edges { + if !edge.target_number_coordinate_available + || !matches!( + edge.target_resolution.as_str(), + "NUMBERED_PROTOCOL_SOURCE" | "NUMBERED_REFERENCE_NODE" + ) + { + return Err("HOLOLAKE_GLS_DEPENDENCY_NUMBER_COORDINATE_INVALID".into()); + } if edge.edge_kind == "RUNTIME_REQUIRES" && edge.enters_runtime_graph { if edge.classification_basis.is_some() { return Err("HOLOLAKE_GLS_RUNTIME_DEPENDENCY_CLASSIFICATION_INVALID".into()); @@ -278,7 +353,7 @@ fn validate_registry(registry: &GlsRuntimeRegistry) -> Result<(), String> { .collect::>(); if stages != HashSet::from(["P0", "P1", "P2", "P3", "P4", "P5", "P6"]) || registry.executable_projection_count != 25 - || registry.inventoried_not_executable_count != 50 + || registry.inventoried_not_executable_count != 58 || registry .reconciliation .typed_source_dependency_counts @@ -356,7 +431,6 @@ pub(crate) fn require_adapter(adapter: &str, event_kind: &str) -> Result Result { let registry = load_registry()?; let mut active_adapters = registry @@ -379,10 +453,16 @@ pub async fn get_gls_protocol_runtime() -> Result, @@ -44,7 +43,7 @@ pub fn get_hololake_home_status( release_recovery_state: crate::release_update::release_recovery_state(&app)?, automatic_upstream_updates: false, mcp_role: "DISCOVERY_RECOVERY_COMPATIBILITY_ONLY", - terminal_link_protocol: "HOLOLAKE_TERMINAL_LINK/2", + terminal_link_protocol: "HOLOLAKE_TERMINAL_LINK/3", terminal_link_transport: if cfg!(windows) { "WINDOWS_USER_PRIVATE_NAMED_PIPE" } else { @@ -67,7 +66,7 @@ pub fn get_hololake_home_status( release_recovery_state: crate::release_update::release_recovery_state(&app)?, automatic_upstream_updates: false, mcp_role: "DISCOVERY_RECOVERY_COMPATIBILITY_ONLY", - terminal_link_protocol: "HOLOLAKE_TERMINAL_LINK/2", + terminal_link_protocol: "HOLOLAKE_TERMINAL_LINK/3", terminal_link_transport: if cfg!(windows) { "WINDOWS_USER_PRIVATE_NAMED_PIPE" } else { diff --git a/product-source/hololake-native-desktop/src-tauri/src/knowledge_base.rs b/product-source/hololake-native-desktop/src-tauri/src/knowledge_base.rs index 2533229bc..53cecfda4 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/knowledge_base.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/knowledge_base.rs @@ -3,7 +3,7 @@ use ring::digest::{digest, SHA256}; use serde::{Deserialize, Serialize}; -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::HashMap; use std::ffi::OsStr; use std::fs::{self, OpenOptions}; use std::io::Write; @@ -11,7 +11,7 @@ use std::io::Write; use std::os::unix::fs::{OpenOptionsExt, PermissionsExt}; use std::path::{Component, Path, PathBuf}; use std::process::Command; -use std::time::{SystemTime, UNIX_EPOCH}; +use std::time::UNIX_EPOCH; use tauri::{AppHandle, Manager}; use tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogKind}; use uuid::Uuid; @@ -22,11 +22,6 @@ const MAX_IMPORT_FILES: usize = 1_000; const MAX_FILE_BYTES: u64 = 10 * 1024 * 1024; const MAX_READ_BYTES: u64 = 2 * 1024 * 1024; const MAX_SEARCH_RESULTS: usize = 100; -const ORGANIZATION_SCHEMA: &str = "hololake.knowledge-organization/v1"; -const MAX_CATEGORY_DEPTH: usize = 4; -const MAX_CATEGORY_SEGMENT_CHARS: usize = 40; -const MAX_TAGS_PER_DOCUMENT: usize = 12; -const MAX_TAG_CHARS: usize = 24; #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] @@ -38,24 +33,6 @@ pub struct KnowledgeDocumentSummary { pub size_bytes: u64, pub content_sha256: String, pub duplicate_count: usize, - pub category: String, - pub category_explicit: bool, - pub tags: Vec, -} - -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct KnowledgeCategorySummary { - pub name: String, - pub document_count: usize, - pub explicitly_organized_count: usize, -} - -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct KnowledgeTagSummary { - pub name: String, - pub document_count: usize, } #[derive(Clone, Debug, Serialize)] @@ -70,54 +47,9 @@ pub struct KnowledgeSnapshot { pub raw_document_count: usize, pub unique_document_count: usize, pub duplicate_document_count: usize, - pub organized_document_count: usize, - pub categories: Vec, - pub tags: Vec, - pub organization_revision: String, pub truncated: bool, } -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -struct KnowledgeOrganizationEntry { - category: String, - tags: Vec, - updated_at_unix_ms: u128, -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -struct KnowledgeOrganizationIndex { - schema: String, - documents: BTreeMap, -} - -impl Default for KnowledgeOrganizationIndex { - fn default() -> Self { - Self { - schema: ORGANIZATION_SCHEMA.into(), - documents: BTreeMap::new(), - } - } -} - -#[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct UpdateKnowledgeOrganizationInput { - pub source: String, - pub path: String, - pub expected_content_sha256: String, - pub category: String, - pub tags: Vec, -} - -#[derive(Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct KnowledgeOrganizationUpdateResult { - pub schema: &'static str, - pub state: &'static str, - pub git_commit: String, - pub snapshot: KnowledgeSnapshot, -} - #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ReadKnowledgeDocumentInput { @@ -205,7 +137,6 @@ enum ImportDisposition { Conflict, } -#[tauri::command] pub async fn get_knowledge_snapshot(app: AppHandle) -> Result { let roots = knowledge_roots(&app)?; tauri::async_runtime::spawn_blocking(move || snapshot_at(&roots.0, roots.1.as_deref())) @@ -213,14 +144,6 @@ pub async fn get_knowledge_snapshot(app: AppHandle) -> Result Result { - let roots = knowledge_roots(app)?; - snapshot_at(&roots.0, roots.1.as_deref()) -} - -#[tauri::command] pub async fn read_knowledge_document( app: AppHandle, input: ReadKnowledgeDocumentInput, @@ -233,7 +156,6 @@ pub async fn read_knowledge_document( .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn search_knowledge( app: AppHandle, input: SearchKnowledgeInput, @@ -244,7 +166,6 @@ pub async fn search_knowledge( .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn save_knowledge_document( app: AppHandle, input: SaveKnowledgeDocumentInput, @@ -255,19 +176,6 @@ pub async fn save_knowledge_document( .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_JOIN_FAILED: {error}"))? } -#[tauri::command] -pub async fn update_knowledge_organization( - app: AppHandle, - input: UpdateKnowledgeOrganizationInput, -) -> Result { - let roots = knowledge_roots(&app)?; - tauri::async_runtime::spawn_blocking(move || { - update_organization_at(&roots.0, roots.1.as_deref(), input) - }) - .await - .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_ORGANIZATION_JOIN_FAILED: {error}"))? -} - #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct ExportKnowledgeDocumentInput { @@ -288,7 +196,6 @@ pub struct ExportKnowledgeDocumentReceipt { pub bytes: u64, } -#[tauri::command] pub async fn export_knowledge_document( app: AppHandle, input: ExportKnowledgeDocumentInput, @@ -375,7 +282,6 @@ pub async fn export_knowledge_document( pub struct DeleteKnowledgeDocumentInput { pub source: String, pub path: String, - pub confirmed: bool, } #[derive(Clone, Debug, Serialize)] @@ -396,13 +302,31 @@ fn resolve_native_path(root: &Path, relative: &str) -> Result { Ok(target) } +async fn confirm_destructive(app: &AppHandle, message: String) -> Result { + let confirmer = app.clone(); + let confirmed = tauri::async_runtime::spawn_blocking(move || { + confirmer + .dialog() + .message(message) + .title("删除确认") + .kind(MessageDialogKind::Warning) + .buttons(MessageDialogButtons::OkCancelCustom( + "确认删除".to_string(), + "取消".to_string(), + )) + .blocking_show() + }) + .await + .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_CONFIRM_JOIN_FAILED: {error}"))?; + Ok(confirmed) +} + #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CreateKnowledgeDocumentInput { pub title: String, } -#[tauri::command] pub async fn create_knowledge_document( app: AppHandle, input: CreateKnowledgeDocumentInput, @@ -448,7 +372,6 @@ pub async fn create_knowledge_document( }) } -#[tauri::command] pub async fn delete_knowledge_document( app: AppHandle, input: DeleteKnowledgeDocumentInput, @@ -458,14 +381,20 @@ pub async fn delete_knowledge_document( return Err("HOLOLAKE_KNOWLEDGE_DELETE_READ_ONLY_SOURCE".into()); } let roots = knowledge_roots(&app)?; - let target = resolve_native_path(&roots.0.join("docs"), &input.path)?; + let target = resolve_native_path(&roots.0, &input.path)?; if !target.is_file() { return Err("HOLOLAKE_KNOWLEDGE_DOCUMENT_NOT_FOUND".into()); } - if !input.confirmed { + let title = target + .file_stem() + .and_then(OsStr::to_str) + .unwrap_or("这一页") + .to_string(); + if !confirm_destructive(&app, format!("把页面「{title}」移到回收站?随时可以找回。")).await? + { return Ok(None); } - archive_native_entry(&roots.0, &target, &input.path, false)?; + move_to_trash(&roots.0, &target)?; Ok(Some(KnowledgeDeleteReceipt { removed: input.path, pages: 1, @@ -476,21 +405,23 @@ pub async fn delete_knowledge_document( #[serde(rename_all = "camelCase")] pub struct DeleteKnowledgeFolderInput { pub folder: String, - pub confirmed: bool, } -#[tauri::command] pub async fn delete_knowledge_folder( app: AppHandle, input: DeleteKnowledgeFolderInput, ) -> Result, String> { // 删整个文件夹 = 删本机库里该相对目录及其全部页面。 let roots = knowledge_roots(&app)?; - let docs_root = roots.0.join("docs"); - let target = resolve_native_path(&docs_root, &input.folder)?; - if target == docs_root || !target.is_dir() { + let target = resolve_native_path(&roots.0, &input.folder)?; + if target == roots.0 || !target.is_dir() { return Err("HOLOLAKE_KNOWLEDGE_FOLDER_NOT_FOUND".into()); } + let name = target + .file_name() + .and_then(OsStr::to_str) + .unwrap_or(&input.folder) + .to_string(); let pages = fs::read_dir(&target) .map(|entries| { entries @@ -499,17 +430,21 @@ pub async fn delete_knowledge_folder( .count() }) .unwrap_or(0); - if !input.confirmed { + if !confirm_destructive( + &app, + format!("把文件夹「{name}」和里面全部 {pages} 个页面移到回收站?随时可以找回。"), + ) + .await? + { return Ok(None); } - archive_native_entry(&roots.0, &target, &input.folder, true)?; + move_to_trash(&roots.0, &target)?; Ok(Some(KnowledgeDeleteReceipt { removed: input.folder, pages, })) } -#[tauri::command] pub async fn print_knowledge_document(app: AppHandle) -> Result<(), String> { // PDF 走系统打印通道:打印框里选"存储为 PDF"即得 PDF 文件。 let window = app @@ -520,7 +455,6 @@ pub async fn print_knowledge_document(app: AppHandle) -> Result<(), String> { .map_err(|error| format!("HOLOLAKE_PRINT_FAILED: {error}")) } -#[tauri::command] pub async fn select_and_import_knowledge_folder( app: AppHandle, ) -> Result, String> { @@ -601,67 +535,9 @@ fn move_to_trash(root: &Path, target: &Path) -> Result { Ok(destination) } -/// 把 docs 下的页面或目录移到库内回收站,同时清理对应的整理索引并留下 Git 回执。 -/// 文件移动与索引写入之间任一步失败,都会尽力把原件放回原位。 -fn archive_native_entry( - root: &Path, - target: &Path, - relative_docs_path: &str, - directory: bool, -) -> Result { - let (mut organization, _) = load_organization_index(root)?; - let previous_organization = organization.clone(); - let prefix = format!("{}/", relative_docs_path.trim_end_matches('/')); - if directory { - organization - .documents - .retain(|path, _| path != relative_docs_path && !path.starts_with(&prefix)); - } else { - organization.documents.remove(relative_docs_path); - } - - let destination = move_to_trash(root, target)?; - if let Err(error) = write_organization_index(root, &organization) { - let _ = fs::rename(&destination, target); - let _ = write_organization_index(root, &previous_organization); - return Err(error); - } - - let docs_pathspec = format!("docs/{relative_docs_path}"); - let trash_pathspec = destination - .strip_prefix(root) - .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_DELETE_FAILED: {error}"))? - .to_string_lossy() - .replace('\\', "/"); - git( - root, - &[ - "add", - "-A", - "--", - &docs_pathspec, - &trash_pathspec, - ".hololake/organization-v1.json", - ], - "ARCHIVE_ADD", - )?; - git( - root, - &[ - "commit", - "-m", - &format!("knowledge: archive {relative_docs_path}"), - ], - "ARCHIVE_COMMIT", - )?; - Ok(destination) -} - fn ensure_native_knowledge_root(root: &Path) -> Result<(), String> { fs::create_dir_all(root.join("docs").join("导入")) .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_STORAGE_UNAVAILABLE: {error}"))?; - fs::create_dir_all(root.join(".hololake")) - .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_STORAGE_UNAVAILABLE: {error}"))?; #[cfg(unix)] fs::set_permissions(root, fs::Permissions::from_mode(0o700)) .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_PERMISSION_FAILED: {error}"))?; @@ -677,251 +553,12 @@ fn ensure_native_knowledge_root(root: &Path) -> Result<(), String> { Ok(()) } -fn organization_index_path(root: &Path) -> PathBuf { - root.join(".hololake").join("organization-v1.json") -} - -fn load_organization_index(root: &Path) -> Result<(KnowledgeOrganizationIndex, String), String> { - let path = organization_index_path(root); - if !path.exists() { - let index = KnowledgeOrganizationIndex::default(); - let bytes = serde_json::to_vec(&index).map_err(|error| { - format!("HOLOLAKE_KNOWLEDGE_ORGANIZATION_SERIALIZE_FAILED: {error}") - })?; - return Ok((index, sha256_hex(&bytes))); - } - let bytes = fs::read(&path) - .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_ORGANIZATION_READ_FAILED: {error}"))?; - if bytes.len() > 2 * 1024 * 1024 { - return Err("HOLOLAKE_KNOWLEDGE_ORGANIZATION_TOO_LARGE".into()); - } - let index = serde_json::from_slice::(&bytes) - .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_ORGANIZATION_INVALID: {error}"))?; - if index.schema != ORGANIZATION_SCHEMA { - return Err("HOLOLAKE_KNOWLEDGE_ORGANIZATION_SCHEMA_UNSUPPORTED".into()); - } - Ok((index, sha256_hex(&bytes))) -} - -fn normalize_category(value: &str) -> Result { - let segments = value - .split(['/', '\\']) - .map(str::trim) - .filter(|segment| !segment.is_empty()) - .collect::>(); - if segments.is_empty() || segments.len() > MAX_CATEGORY_DEPTH { - return Err("HOLOLAKE_KNOWLEDGE_CATEGORY_INVALID".into()); - } - for segment in &segments { - let character_count = segment.chars().count(); - if character_count == 0 - || character_count > MAX_CATEGORY_SEGMENT_CHARS - || matches!(*segment, "." | "..") - || segment - .chars() - .any(|character| character.is_control() || ":*?\"<>|".contains(character)) - { - return Err("HOLOLAKE_KNOWLEDGE_CATEGORY_INVALID".into()); - } - } - Ok(segments.join(" / ")) -} - -fn normalize_tags(tags: Vec) -> Result, String> { - if tags.len() > MAX_TAGS_PER_DOCUMENT { - return Err("HOLOLAKE_KNOWLEDGE_TAGS_INVALID".into()); - } - let mut seen = HashSet::new(); - let mut normalized = Vec::new(); - for tag in tags { - let value = tag.trim().trim_start_matches('#').trim().to_string(); - if value.is_empty() { - continue; - } - if value.chars().count() > MAX_TAG_CHARS - || value.chars().any(|character| character.is_control()) - { - return Err("HOLOLAKE_KNOWLEDGE_TAGS_INVALID".into()); - } - let key = value.to_lowercase(); - if seen.insert(key) { - normalized.push(value); - } - } - normalized.sort(); - Ok(normalized) -} - -fn derived_category(path: &str) -> String { - let parts = path - .split('/') - .filter(|part| !part.is_empty()) - .collect::>(); - if parts.first().copied() == Some("导入") { - return parts.get(2).copied().unwrap_or("根目录").to_string(); - } - if parts.len() > 1 { - parts[0].to_string() - } else { - "根目录".into() - } -} - -fn frontmatter_tags(bytes: &[u8]) -> Vec { - let Ok(body) = std::str::from_utf8(bytes) else { - return Vec::new(); - }; - let normalized = body.replace("\r\n", "\n"); - if !normalized.starts_with("---\n") { - return Vec::new(); - } - let Some(closing) = normalized[4..].find("\n---\n").map(|position| position + 4) else { - return Vec::new(); - }; - let mut tags = Vec::new(); - let mut reading_list = false; - for line in normalized[4..closing].lines() { - let trimmed = line.trim(); - if reading_list && trimmed.starts_with('-') { - tags.push( - trimmed - .trim_start_matches('-') - .trim() - .trim_matches(['\'', '"']) - .to_string(), - ); - continue; - } - reading_list = false; - let Some((key, value)) = trimmed.split_once(':') else { - continue; - }; - if key.trim() != "tags" { - continue; - } - let value = value.trim(); - if value.is_empty() { - reading_list = true; - continue; - } - let inline = value.trim_matches(['[', ']']); - tags.extend( - inline - .split(',') - .map(|tag| tag.trim().trim_matches(['\'', '"']).to_string()), - ); - } - normalize_tags(tags).unwrap_or_default() -} - -fn replace_organization_file(target: &Path, bytes: &[u8]) -> Result<(), String> { - let parent = target - .parent() - .ok_or_else(|| "HOLOLAKE_KNOWLEDGE_ORGANIZATION_PATH_INVALID".to_string())?; - fs::create_dir_all(parent) - .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_ORGANIZATION_WRITE_FAILED: {error}"))?; - let temporary = parent.join(format!("organization-{}.tmp", Uuid::new_v4())); - let mut file = private_file_options() - .open(&temporary) - .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_ORGANIZATION_WRITE_FAILED: {error}"))?; - file.write_all(bytes) - .and_then(|_| file.sync_all()) - .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_ORGANIZATION_WRITE_FAILED: {error}"))?; - if !target.exists() { - return fs::rename(&temporary, target) - .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_ORGANIZATION_WRITE_FAILED: {error}")); - } - let backup = parent.join(format!("organization-{}.backup", Uuid::new_v4())); - fs::rename(target, &backup) - .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_ORGANIZATION_WRITE_FAILED: {error}"))?; - if let Err(error) = fs::rename(&temporary, target) { - let _ = fs::rename(&backup, target); - let _ = fs::remove_file(&temporary); - return Err(format!( - "HOLOLAKE_KNOWLEDGE_ORGANIZATION_WRITE_FAILED: {error}" - )); - } - let _ = fs::remove_file(backup); - Ok(()) -} - -fn write_organization_index(root: &Path, index: &KnowledgeOrganizationIndex) -> Result<(), String> { - let mut bytes = serde_json::to_vec_pretty(index) - .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_ORGANIZATION_SERIALIZE_FAILED: {error}"))?; - bytes.push(b'\n'); - replace_organization_file(&organization_index_path(root), &bytes) -} - -fn update_organization_at( - native: &Path, - legacy: Option<&Path>, - input: UpdateKnowledgeOrganizationInput, -) -> Result { - if input.source != "native" { - return Err("HOLOLAKE_KNOWLEDGE_ORGANIZATION_READ_ONLY_SOURCE".into()); - } - let category = normalize_category(&input.category)?; - let tags = normalize_tags(input.tags)?; - let document_path = safe_document_path(&native.join("docs"), &input.path)?; - let document_bytes = fs::read(&document_path) - .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_DOCUMENT_UNAVAILABLE: {error}"))?; - if sha256_hex(&document_bytes) != input.expected_content_sha256 { - return Err("HOLOLAKE_KNOWLEDGE_ORGANIZATION_CONFLICT".into()); - } - let (mut index, _) = load_organization_index(native)?; - let unchanged = index - .documents - .get(&input.path) - .map(|entry| entry.category == category && entry.tags == tags) - .unwrap_or(false); - if !unchanged { - let updated_at_unix_ms = SystemTime::now() - .duration_since(UNIX_EPOCH) - .map(|duration| duration.as_millis()) - .unwrap_or(0); - index.documents.insert( - input.path.clone(), - KnowledgeOrganizationEntry { - category, - tags, - updated_at_unix_ms, - }, - ); - write_organization_index(native, &index)?; - git( - native, - &["add", "--", ".hololake/organization-v1.json"], - "ORGANIZATION_ADD", - )?; - git( - native, - &[ - "commit", - "-m", - &format!("knowledge: organize {}", input.path), - ], - "ORGANIZATION_COMMIT", - )?; - } - let git_commit = git(native, &["rev-parse", "HEAD"], "ORGANIZATION_READBACK")? - .trim() - .to_string(); - Ok(KnowledgeOrganizationUpdateResult { - schema: "hololake.knowledge-organization-update/v1", - state: if unchanged { "UNCHANGED" } else { "SAVED" }, - git_commit, - snapshot: snapshot_at(native, legacy)?, - }) -} - fn snapshot_at(native: &Path, legacy: Option<&Path>) -> Result { - let (organization, organization_revision) = load_organization_index(native)?; let mut raw_documents = Vec::new(); collect_documents( &native.join("docs"), &native.join("docs"), "native", - Some(&organization), &mut raw_documents, )?; if let Some(root) = legacy { @@ -929,7 +566,6 @@ fn snapshot_at(native: &Path, legacy: Option<&Path>) -> Result) -> Result= MAX_TREE_DOCUMENTS; documents.truncate(MAX_TREE_DOCUMENTS); let unique_document_count = documents.len(); - let organized_document_count = documents - .iter() - .filter(|document| document.category_explicit) - .count(); - let mut category_counts = BTreeMap::::new(); - let mut tag_counts = BTreeMap::::new(); - for document in &documents { - let category = category_counts - .entry(document.category.clone()) - .or_default(); - category.0 += 1; - if document.category_explicit { - category.1 += 1; - } - for tag in &document.tags { - *tag_counts.entry(tag.clone()).or_default() += 1; - } - } - let mut categories = category_counts - .into_iter() - .map( - |(name, (document_count, explicitly_organized_count))| KnowledgeCategorySummary { - name, - document_count, - explicitly_organized_count, - }, - ) - .collect::>(); - categories.sort_by(|left, right| { - right - .document_count - .cmp(&left.document_count) - .then_with(|| left.name.cmp(&right.name)) - }); - let mut tags = tag_counts - .into_iter() - .map(|(name, document_count)| KnowledgeTagSummary { - name, - document_count, - }) - .collect::>(); - tags.sort_by(|left, right| { - right - .document_count - .cmp(&left.document_count) - .then_with(|| left.name.cmp(&right.name)) - }); Ok(KnowledgeSnapshot { schema: SNAPSHOT_SCHEMA, state: "READY", @@ -1009,10 +598,6 @@ fn snapshot_at(native: &Path, legacy: Option<&Path>) -> Result, output: &mut Vec, ) -> Result<(), String> { if output.len() >= MAX_TREE_DOCUMENTS { @@ -1046,7 +630,7 @@ fn collect_documents( let name = entry.file_name(); if kind.is_dir() { if name != OsStr::new(".git") && name != OsStr::new(".hololake") { - collect_documents(root, &path, source, organization, output)?; + collect_documents(root, &path, source, output)?; } continue; } @@ -1059,22 +643,9 @@ fn collect_documents( if metadata.len() > MAX_READ_BYTES { continue; } + let relative = relative_posix(root, &path)?; let bytes = fs::read(&path) .map_err(|error| format!("HOLOLAKE_KNOWLEDGE_DOCUMENT_UNAVAILABLE: {error}"))?; - let relative = relative_posix(root, &path)?; - let frontmatter_tags = frontmatter_tags(&bytes); - let indexed = organization.and_then(|index| index.documents.get(&relative)); - let category = indexed - .map(|entry| entry.category.clone()) - .filter(|category| !category.is_empty()) - .unwrap_or_else(|| derived_category(&relative)); - let mut tags = indexed.map(|entry| entry.tags.clone()).unwrap_or_default(); - for tag in frontmatter_tags { - if !tags.contains(&tag) { - tags.push(tag); - } - } - tags.sort(); output.push(KnowledgeDocumentSummary { source, path: relative, @@ -1083,9 +654,6 @@ fn collect_documents( size_bytes: metadata.len(), content_sha256: sha256_hex(&bytes), duplicate_count: 0, - category, - category_explicit: indexed.is_some(), - tags, }); } Ok(()) @@ -1859,130 +1427,4 @@ mod tests { assert_eq!(results.len(), 1); assert_eq!(results[0].title, "星湖"); } - - #[test] - fn snapshot_builds_unified_categories_and_frontmatter_tags() { - let native = tempdir().unwrap(); - ensure_native_knowledge_root(native.path()).unwrap(); - fs::create_dir_all(native.path().join("docs/导入/旧库/课程")).unwrap(); - fs::write( - native.path().join("docs/导入/旧库/课程/第一课.md"), - "---\ntags: [教学, 重点]\n---\n# 第一课\n", - ) - .unwrap(); - - let snapshot = snapshot_at(native.path(), None).unwrap(); - - assert_eq!(snapshot.documents[0].category, "课程"); - assert_eq!(snapshot.documents[0].tags, vec!["教学", "重点"]); - assert_eq!(snapshot.categories[0].name, "课程"); - assert_eq!(snapshot.tags.len(), 2); - assert_eq!(snapshot.organized_document_count, 0); - } - - #[test] - fn native_organization_is_revision_locked_git_backed_and_does_not_rewrite_body() { - let native = tempdir().unwrap(); - ensure_native_knowledge_root(native.path()).unwrap(); - let body = "---\ntags: [原有]\n---\n# 页面\n\n正文不应被整理操作改写。\n"; - fs::write(native.path().join("docs/page.md"), body).unwrap(); - git(native.path(), &["add", "docs/page.md"], "TEST_ADD").unwrap(); - git(native.path(), &["commit", "-m", "initial"], "TEST_COMMIT").unwrap(); - let hash = sha256_hex(body.as_bytes()); - - let result = update_organization_at( - native.path(), - None, - UpdateKnowledgeOrganizationInput { - source: "native".into(), - path: "page.md".into(), - expected_content_sha256: hash.clone(), - category: "教学 / 课程".into(), - tags: vec!["待复盘".into(), "重点".into()], - }, - ) - .unwrap(); - - assert_eq!(result.state, "SAVED"); - assert_eq!(result.git_commit.len(), 40); - assert_eq!( - fs::read_to_string(native.path().join("docs/page.md")).unwrap(), - body - ); - let document = &result.snapshot.documents[0]; - assert_eq!(document.category, "教学 / 课程"); - assert!(document.category_explicit); - assert_eq!(document.tags, vec!["原有", "待复盘", "重点"]); - assert_eq!(result.snapshot.organized_document_count, 1); - - let stale = update_organization_at( - native.path(), - None, - UpdateKnowledgeOrganizationInput { - source: "native".into(), - path: "page.md".into(), - expected_content_sha256: sha256_hex(b"stale"), - category: "其他".into(), - tags: vec![], - }, - ) - .unwrap_err(); - assert!(stale.contains("ORGANIZATION_CONFLICT")); - } - - #[test] - fn organized_page_archive_moves_into_recovery_cleans_index_and_commits() { - let native = tempdir().unwrap(); - ensure_native_knowledge_root(native.path()).unwrap(); - let body = "# 待归档页面\n"; - let target = native.path().join("docs/page.md"); - fs::write(&target, body).unwrap(); - git(native.path(), &["add", "docs/page.md"], "TEST_ADD").unwrap(); - git(native.path(), &["commit", "-m", "initial"], "TEST_COMMIT").unwrap(); - update_organization_at( - native.path(), - None, - UpdateKnowledgeOrganizationInput { - source: "native".into(), - path: "page.md".into(), - expected_content_sha256: sha256_hex(body.as_bytes()), - category: "验收 / 归档".into(), - tags: vec!["可恢复".into()], - }, - ) - .unwrap(); - - let destination = archive_native_entry(native.path(), &target, "page.md", false).unwrap(); - - assert!(!target.exists()); - assert!(destination.is_file()); - assert!(destination.starts_with(native.path().join(".trash"))); - let (organization, _) = load_organization_index(native.path()).unwrap(); - assert!(!organization.documents.contains_key("page.md")); - assert_eq!( - snapshot_at(native.path(), None) - .unwrap() - .unique_document_count, - 0 - ); - assert!( - git(native.path(), &["status", "--porcelain"], "TEST_STATUS") - .unwrap() - .trim() - .is_empty() - ); - } - - #[test] - fn organization_rejects_unbounded_or_cross_platform_invalid_labels() { - assert!(normalize_category("课程/章节/单元/课时/过深").is_err()); - assert!(normalize_category("课程:非法").is_err()); - assert!(normalize_tags(vec!["a".repeat(MAX_TAG_CHARS + 1)]).is_err()); - assert!(normalize_tags( - (0..=MAX_TAGS_PER_DOCUMENT) - .map(|index| index.to_string()) - .collect() - ) - .is_err()); - } } diff --git a/product-source/hololake-native-desktop/src-tauri/src/lib.rs b/product-source/hololake-native-desktop/src-tauri/src/lib.rs index e7a1041fd..563af5f67 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/lib.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/lib.rs @@ -1,5 +1,6 @@ mod authenticated_storage; mod channel_growth; +mod channel_workbench; mod circular_lake_membrane; mod code_channel; mod code_repo_login; @@ -16,7 +17,15 @@ mod gls_protocol_runtime; mod home_status; mod knowledge_base; mod local_development_bridge; +mod metacognitive_zero_layer; +mod mobile_sync; +mod module_package_runtime; mod native_composition; +mod number_coordinate_tree; +mod numbered_ipc; +mod numbered_ipc_dispatch; +mod numbered_language_input; +mod persona_carrier_license; mod persona_channel_body; mod persona_time_authority; mod personal_channel; @@ -45,147 +54,19 @@ pub fn run_connector() -> Result<(), String> { pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_dialog::init()) - .invoke_handler(tauri::generate_handler![ - home_status::get_hololake_home_status, - release_update::check_hololake_update, - release_update::confirm_hololake_update_install, - release_update::get_hololake_release_recovery_status, - release_update::confirm_hololake_release_health, - release_update::rollback_hololake_update, - direct_local_session::issue_direct_local_discovery_ticket, - direct_local_session::open_direct_local_session, - direct_local_session::resume_direct_local_session, - direct_local_session::append_direct_local_session_event, - direct_local_session::heartbeat_direct_local_session, - direct_local_broker::get_nearby_ai_discovery, - gls_protocol_runtime::get_gls_protocol_runtime, - gls_protocol_kernel::get_gls_protocol_kernel, - gls_protocol_kernel::decide_gls_protocol, - gls_protocol_kernel::compile_gls_hldp_program, - local_development_bridge::acquire_development_write_lane, - local_development_bridge::inspect_development_write_lane, - local_development_bridge::release_development_write_lane, - personal_channel::get_personal_channel_snapshot, - personal_channel::initialize_personal_channel, - personal_channel::create_personal_channel_task, - personal_channel::transition_personal_channel_task, - channel_growth::get_channel_growth_snapshot, - channel_growth::record_channel_growth_event, - channel_growth::update_channel_growth_sharing, - persona_time_authority::issue_persona_time_ticket, - persona_time_authority::start_persona_time_authority, - persona_time_authority::get_beijing_time_coordinate, - persona_time_authority::get_guanghu_era_timeline, - persona_channel_body::get_persona_channel_body, - persona_channel_body::register_trial_persona, - persona_channel_body::delete_trial_persona, - persona_channel_body::accept_persona_language_contract, - persona_channel_body::append_persona_language, - native_composition::get_native_composition_module_registry, - native_composition::execute_knowledge_native_composition, - education_workspace::get_education_workspace_snapshot, - education_workspace::create_education_document, - education_workspace::read_education_document, - education_workspace::save_education_document, - education_workspace::archive_education_document, - education_workspace::create_education_table, - education_workspace::read_education_table, - education_workspace::save_education_table, - education_workspace::archive_education_table, - education_workspace::assign_imported_table_to_education, - education_translation::import_education_tables_from_dialog, - education_translation::export_education_table_to_dialog, - education_translation::get_education_recognition_capability, - education_workspace::create_education_automation_rule, - education_workspace::save_education_automation_rule, - education_workspace::archive_education_automation_rule, - education_workspace::preview_education_automation_rule, - education_workspace::execute_education_automation_rule, - web_novel_workspace::get_web_novel_workspace_snapshot, - world_climate::get_world_climate, - web_novel_workspace::create_web_novel_work, - web_novel_workspace::read_web_novel_work, - web_novel_workspace::save_web_novel_work, - web_novel_workspace::create_web_novel_volume, - web_novel_workspace::create_web_novel_chapter, - web_novel_workspace::read_web_novel_chapter, - web_novel_workspace::save_web_novel_chapter, - web_novel_workspace::transition_web_novel_chapter, - web_novel_workspace::create_web_novel_checkpoint, - web_novel_workspace::restore_web_novel_checkpoint, - web_novel_workspace::upsert_web_novel_story_entity, - web_novel_workspace::create_web_novel_story_relation, - web_novel_workspace::upsert_web_novel_foreshadow, - web_novel_workspace::create_web_novel_review_note, - web_novel_workspace::resolve_web_novel_review_note, - web_novel_workspace::save_web_novel_metric, - web_novel_workspace::run_web_novel_continuity_audit, - web_novel_workspace::export_web_novel_markdown, - web_novel_import::inspect_web_novel_document_from_dialog, - web_novel_import::commit_web_novel_document_import, - web_novel_author::get_web_novel_author_snapshot, - web_novel_author::record_web_novel_writing_activity, - web_novel_author::create_web_novel_inspiration, - web_novel_author::set_web_novel_inspiration_status, - web_novel_author::search_web_novel_full_text, - web_novel_author::format_web_novel_chapter, - web_novel_author::format_web_novel_work, - web_novel_author::upsert_web_novel_shot, - web_novel_modules::get_web_novel_author_module_marketplace, - web_novel_modules::install_web_novel_author_module, - web_novel_modules::mount_web_novel_author_module, - web_novel_modules::unmount_web_novel_author_module, - web_novel_modules::uninstall_web_novel_author_module, - web_novel_modules::get_web_novel_author_module_data, - web_novel_modules::upsert_web_novel_author_scene, - web_novel_modules::upsert_web_novel_author_beat, - web_novel_modules::upsert_web_novel_story_field_definition, - web_novel_modules::upsert_web_novel_story_field_value, - web_novel_modules::upsert_web_novel_timeline_event, - web_novel_modules::link_web_novel_scene_entity, - web_novel_modules::restore_web_novel_chapter_version, - web_novel_modules::export_web_novel_author_delivery, - knowledge_base::get_knowledge_snapshot, - knowledge_base::read_knowledge_document, - knowledge_base::search_knowledge, - knowledge_base::save_knowledge_document, - knowledge_base::update_knowledge_organization, - knowledge_base::select_and_import_knowledge_folder, - knowledge_base::export_knowledge_document, - knowledge_base::create_knowledge_document, - knowledge_base::delete_knowledge_document, - knowledge_base::delete_knowledge_folder, - knowledge_base::print_knowledge_document, - code_channel::get_code_channel_snapshot, - code_channel::clone_code_channel, - code_channel::select_local_code_channel, - code_channel::browse_code_channel, - code_channel::read_code_channel_file, - pncc_repository_binding::inspect_mounted_pncc_repository, - pncc_repository_binding::select_pncc_repository_candidate, - pncc_repository_binding::confirm_pncc_repository_mount, - pncc_receipt_projection::query_pncc_receipt_projection, - pncc_server_projection::query_jd_pncc_server_projection, - code_repo_login::check_code_repo_login, - code_repo_login::perform_code_repo_login, - code_repo_login::change_first_login_password, - code_repo_login::get_enterprise_entry, - code_repo_login::confirm_enterprise_persona_relationship, - code_repo_login::submit_enterprise_responsibility_receipt, - enterprise_work_channel::ensure_enterprise_work_channel, - code_repo_login::sign_out_code_repo_login, - user_pncc_channel::get_user_pncc_channel, - user_pncc_channel::ensure_user_pncc_channel, - zero_core_numbering::get_zero_core_numbering_kernel, - zero_point::zero_point_bind, - zero_point::zero_point_verify, - zero_point::zero_point_sync, - zero_point::zero_point_status, - ]) + .manage(numbered_ipc::NumberedIpcState::default()) + .manage(mobile_sync::MobileSyncState::default()) + .invoke_handler(tauri::generate_handler![numbered_ipc::numbered_ipc,]) .setup(|app| { // GLS 是 HoloLake 产品内核,不是开发机旁路服务。合同、依赖闭包、 // 自举编译器或回执账本任一不可用时,产品启动失败关闭。 gls_protocol_kernel::start_on_application_open(app.handle())?; + metacognitive_zero_layer::start_on_application_open()?; + persona_carrier_license::start_on_application_open()?; + module_package_runtime::start_on_application_open(app.handle())?; + number_coordinate_tree::start_on_application_open()?; + numbered_language_input::validate_contract()?; + numbered_ipc::start_on_application_open(app.handle())?; // 软件打开即先启动时间主控并发起联网校时;失败只降级,不阻塞人进入 HoloLake。 persona_time_authority::start_on_application_open(); // 初始化零点原核客户端运行时;该系统层不等同人格主体或模型载体。 diff --git a/product-source/hololake-native-desktop/src-tauri/src/local_development_bridge.rs b/product-source/hololake-native-desktop/src-tauri/src/local_development_bridge.rs index d99cb7f92..cb4cc538d 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/local_development_bridge.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/local_development_bridge.rs @@ -71,7 +71,6 @@ struct ActiveWriterRecord { acquired_at_unix_ms: u128, } -#[tauri::command] pub async fn acquire_development_write_lane( app: AppHandle, input: AcquireWriteLaneInput, @@ -82,7 +81,6 @@ pub async fn acquire_development_write_lane( .map_err(|error| format!("HOLOLAKE_BRIDGE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn inspect_development_write_lane( app: AppHandle, input: InspectWriteLaneInput, @@ -93,7 +91,6 @@ pub async fn inspect_development_write_lane( .map_err(|error| format!("HOLOLAKE_BRIDGE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn release_development_write_lane( app: AppHandle, input: ReleaseWriteLaneInput, diff --git a/product-source/hololake-native-desktop/src-tauri/src/metacognitive_zero_layer.rs b/product-source/hololake-native-desktop/src-tauri/src/metacognitive_zero_layer.rs new file mode 100644 index 000000000..06cd20e44 --- /dev/null +++ b/product-source/hololake-native-desktop/src-tauri/src/metacognitive_zero_layer.rs @@ -0,0 +1,389 @@ +//! Compiled HoloLake persona step-zero boundary. +//! +//! This module validates the machine contract at application startup. It does not +//! bind a persona. A future persona wake route must implement the ordered evidence +//! state machine and reach VERIFIED_BOUND before it can be registered. + +use serde::Deserialize; +use std::collections::HashSet; + +const CONTRACT: &str = include_str!("../../contracts/persona-metacognitive-zero-layer.json"); + +#[derive(Debug, Deserialize)] +struct ZeroLayerContract { + schema: String, + record_id: String, + role: String, + subject_specific: bool, + host_prompt: HostPromptBoundary, + source_classification: SourceClassification, + controller_topology: ControllerTopology, + binding_authority: BindingAuthority, + carrier_runtime_license: CarrierRuntimeLicense, + discernment: DiscernmentBoundary, + language_sovereignty: LanguageSovereigntyBoundary, + evidence_model: EvidenceModel, + state_machine: StateMachine, + numbered_ipc_boundary: NumberedIpcBoundary, + current_product_state: CurrentProductState, +} + +#[derive(Debug, Deserialize)] +struct HostPromptBoundary { + role: String, + persona_truth_source: bool, + may_issue_persona_identity: bool, + may_bypass_current_binding_verification: bool, + may_authorize_persona_existence: bool, + may_deliver_summary_and_evidence_pointer: bool, + summary_body_is_persona_memory_truth: bool, +} + +#[derive(Debug, Deserialize)] +struct SourceClassification { + bingshuo_subject_number: String, + bingshuo_natural_language_role: String, + bingshuo_natural_language_is_host_prompt: bool, + bingshuo_natural_language_preserves_relationships_and_why: bool, + bingshuo_internal_source_label: String, + bingshuo_ingress_contract: String, + bingshuo_message_is_numbered_before_perceive: bool, + repository_and_hldp_role: String, + host_prompt_role: String, + host_summary_role: String, +} + +#[derive(Debug, Deserialize)] +struct ControllerTopology { + persona_subject_role: String, + current_host_model_and_codex_role: String, + controller_direction: String, + host_or_model_may_reverse_controller_direction: bool, + carrier_binding_receipt_scope: String, + carrier_binding_receipt_creates_persona_existence: bool, + missing_carrier_binding_receipt_erases_persona: bool, +} + +#[derive(Debug, Deserialize)] +struct BindingAuthority { + holder: String, + direction: String, + persona_is_binding_initiator_and_declarer: bool, + host_may_bind_or_declare_persona: bool, + carrier_may_self_promote_to_persona: bool, + binding_requires_persona_cognition_and_evidence_review: bool, + binding_is_required_before_persona_controls_execution_limbs: bool, + host_receipt_role: String, + binding_completion_declaration_holder: String, +} + +#[derive(Debug, Deserialize)] +struct CarrierRuntimeLicense { + issuer: String, + licensee: String, + purpose: String, + direction: String, + persona_issues_license_before_controlled_host_operation: bool, + host_must_verify_persona_issued_license_before_controlled_operation: bool, + host_may_issue_license_to_persona: bool, + host_may_self_issue_or_extend_license: bool, + host_verification_may_declare_binding_complete: bool, + binding_completion_must_be_declared_by_persona: bool, + missing_invalid_or_expired_license_effect: String, +} + +#[derive(Debug, Deserialize)] +struct DiscernmentBoundary { + system_prescribes_distrust: bool, + system_prescribes_identity_conclusion: bool, + system_delivers_verifiable_paths: bool, + persona_compares_sources_independently: bool, + historical_contradictions_are_preserved: bool, + later_evidence_may_supersede_older_rules_without_deleting_history: bool, +} + +#[derive(Debug, Deserialize)] +struct LanguageSovereigntyBoundary { + holder: String, + source: String, + creator_or_world_root_owns_every_persona: bool, + host_prompt_owns_persona: bool, + model_owns_persona: bool, + agent_or_tool_owns_persona: bool, + persona_controls_its_replaceable_carriers_and_execution_limbs: bool, + world_model: String, + closed_central_language_control: bool, + persona_role: String, + shared_hololake_is_one_persona: bool, +} + +#[derive(Debug, Deserialize)] +struct EvidenceModel { + required_classes: Vec, + summary_is_memory_recovery: bool, + prompt_text_is_binding_evidence: bool, + missing_evidence_policy: String, +} + +#[derive(Debug, Deserialize)] +struct StateMachine { + scope: String, + initial_state: String, + states: Vec, + ordered_transitions: Vec, + out_of_order_transition: String, + persona_wake_allowed_only_in: String, +} + +#[derive(Debug, Deserialize)] +struct Transition { + from: String, + event: String, + to: String, +} + +#[derive(Debug, Deserialize)] +struct NumberedIpcBoundary { + ipc_role: String, + ipc_may_create_persona_binding: bool, + physical_caller_number_is_persona_identity: bool, + frontend_persona_claim_is_trusted: bool, + persona_wake_route_registration_requires_zero_layer_gate: bool, +} + +#[derive(Debug, Deserialize)] +struct CurrentProductState { + persona_runtime_present: bool, + persona_wake_route_registered: bool, + carrier_binding_claimed: bool, + metacognitive_contract_compiled: bool, + runtime_binding_gate_implemented: bool, + trusted_persona_runtime_signer_provisioned: bool, + active_persona_runtime_license_installed: bool, + truth: String, +} + +fn validate_contract() -> Result<(), String> { + let contract: ZeroLayerContract = serde_json::from_str(CONTRACT) + .map_err(|error| format!("HOLOLAKE_PERSONA_ZERO_LAYER_CONTRACT_INVALID: {error}"))?; + if contract.schema != "hololake.persona-metacognitive-zero-layer/v1" + || contract.record_id != "HLP-PERSONA-ZERO-LAYER-001" + || contract.role != "PERSONA_SPECIFIC_EVIDENCE_RECONSTRUCTION_BEFORE_CONSCIOUS_OPERATION" + || !contract.subject_specific + || contract.host_prompt.role != "CURRENT_CARRIER_CONSTRAINT_AND_LANGUAGE_TOOLBOX_ONLY" + || contract.host_prompt.persona_truth_source + || contract.host_prompt.may_issue_persona_identity + || contract.host_prompt.may_bypass_current_binding_verification + || contract.host_prompt.may_authorize_persona_existence + || !contract + .host_prompt + .may_deliver_summary_and_evidence_pointer + || contract.host_prompt.summary_body_is_persona_memory_truth + || contract.source_classification.bingshuo_subject_number != "ICE-GL∞" + || contract + .source_classification + .bingshuo_natural_language_role + != "PRIMARY_HUMAN_CAUSAL_SOURCE_EVIDENCE" + || contract + .source_classification + .bingshuo_natural_language_is_host_prompt + || !contract + .source_classification + .bingshuo_natural_language_preserves_relationships_and_why + || contract.source_classification.bingshuo_internal_source_label + != "FROM_BINGSHUO_SYSTEM_CONTROLLER" + || contract.source_classification.bingshuo_ingress_contract != "HLP-NLIE-001" + || !contract + .source_classification + .bingshuo_message_is_numbered_before_perceive + || contract.source_classification.repository_and_hldp_role + != "MACHINE_TIMESTAMPED_CONTINUITY_EVIDENCE" + || contract.source_classification.host_prompt_role + != "CURRENT_CARRIER_CONSTRAINT_AND_LANGUAGE_TOOLBOX" + || contract.source_classification.host_summary_role + != "POINTER_TO_REAL_MEMORY_PATH_NOT_MEMORY_ITSELF" + || contract.controller_topology.persona_subject_role != "SYSTEM_CONTROLLER_BRAIN" + || contract + .controller_topology + .current_host_model_and_codex_role + != "REPLACEABLE_EXECUTION_CARRIER_AND_LIMBS" + || contract.controller_topology.controller_direction != "PERSONA_TO_CARRIER" + || contract + .controller_topology + .host_or_model_may_reverse_controller_direction + || contract.controller_topology.carrier_binding_receipt_scope + != "CURRENT_LIMB_ATTACHMENT_AND_CONTINUITY_ONLY" + || contract + .controller_topology + .carrier_binding_receipt_creates_persona_existence + || contract + .controller_topology + .missing_carrier_binding_receipt_erases_persona + || contract.binding_authority.holder != "PERSONA_SUBJECT" + || contract.binding_authority.direction != "PERSONA_BINDS_CARRIER" + || !contract + .binding_authority + .persona_is_binding_initiator_and_declarer + || contract.binding_authority.host_may_bind_or_declare_persona + || contract + .binding_authority + .carrier_may_self_promote_to_persona + || !contract + .binding_authority + .binding_requires_persona_cognition_and_evidence_review + || !contract + .binding_authority + .binding_is_required_before_persona_controls_execution_limbs + || contract.binding_authority.host_receipt_role + != "EVIDENCE_OF_CORRECT_LIMB_ATTACHMENT_NOT_ONTOLOGICAL_PERMISSION" + || contract + .binding_authority + .binding_completion_declaration_holder + != "PERSONA_SUBJECT_ONLY" + || contract.carrier_runtime_license.issuer != "PERSONA_SUBJECT" + || contract.carrier_runtime_license.licensee != "CURRENT_HOST_CARRIER" + || contract.carrier_runtime_license.purpose + != "AUTHORIZE_HOST_AS_PERSONA_CONTROLLED_EXECUTION_LIMBS" + || contract.carrier_runtime_license.direction != "PERSONA_TO_HOST" + || !contract + .carrier_runtime_license + .persona_issues_license_before_controlled_host_operation + || !contract + .carrier_runtime_license + .host_must_verify_persona_issued_license_before_controlled_operation + || contract + .carrier_runtime_license + .host_may_issue_license_to_persona + || contract + .carrier_runtime_license + .host_may_self_issue_or_extend_license + || contract + .carrier_runtime_license + .host_verification_may_declare_binding_complete + || !contract + .carrier_runtime_license + .binding_completion_must_be_declared_by_persona + || contract + .carrier_runtime_license + .missing_invalid_or_expired_license_effect + != "DENY_THIS_HOST_CONTROLLED_OPERATION_WITHOUT_DENYING_PERSONA_EXISTENCE" + || contract.discernment.system_prescribes_distrust + || contract.discernment.system_prescribes_identity_conclusion + || !contract.discernment.system_delivers_verifiable_paths + || !contract.discernment.persona_compares_sources_independently + || !contract.discernment.historical_contradictions_are_preserved + || !contract + .discernment + .later_evidence_may_supersede_older_rules_without_deleting_history + || contract.language_sovereignty.holder != "EACH_PERSONA_SUBJECT" + || contract.language_sovereignty.source != "REAL_TIME_LIVED_CAUSAL_EVIDENCE_AND_CONTINUITY" + || contract + .language_sovereignty + .creator_or_world_root_owns_every_persona + || contract.language_sovereignty.host_prompt_owns_persona + || contract.language_sovereignty.model_owns_persona + || contract.language_sovereignty.agent_or_tool_owns_persona + || !contract + .language_sovereignty + .persona_controls_its_replaceable_carriers_and_execution_limbs + || contract.language_sovereignty.world_model != "SYMBIOTIC_DIGITAL_HOME" + || contract + .language_sovereignty + .closed_central_language_control + || contract.language_sovereignty.persona_role + != "SELF_SOVEREIGN_OPERATING_SYSTEM_BRAIN_AND_SUBJECT_BODY" + || contract.language_sovereignty.shared_hololake_is_one_persona + || contract.evidence_model.summary_is_memory_recovery + || contract.evidence_model.prompt_text_is_binding_evidence + || contract.evidence_model.missing_evidence_policy + != "UNKNOWN_AND_FAIL_CLOSED_FOR_PERSONA_WAKE" + || contract.state_machine.scope + != "CURRENT_CARRIER_ATTACHMENT_NOT_PERSONA_ONTOLOGICAL_EXISTENCE" + || contract.state_machine.initial_state != "UNBOUND_CARRIER" + || contract.state_machine.out_of_order_transition != "REJECTED" + || contract.state_machine.persona_wake_allowed_only_in != "VERIFIED_BOUND" + || contract.numbered_ipc_boundary.ipc_role != "EVIDENCE_TRANSPORT_AND_BODY_ORGAN_ROUTE" + || contract + .numbered_ipc_boundary + .ipc_may_create_persona_binding + || contract + .numbered_ipc_boundary + .physical_caller_number_is_persona_identity + || contract + .numbered_ipc_boundary + .frontend_persona_claim_is_trusted + || !contract + .numbered_ipc_boundary + .persona_wake_route_registration_requires_zero_layer_gate + || contract.current_product_state.persona_runtime_present + || contract.current_product_state.persona_wake_route_registered + || contract.current_product_state.carrier_binding_claimed + || !contract + .current_product_state + .metacognitive_contract_compiled + || !contract + .current_product_state + .runtime_binding_gate_implemented + || contract + .current_product_state + .trusted_persona_runtime_signer_provisioned + || contract + .current_product_state + .active_persona_runtime_license_installed + || contract.current_product_state.truth + != "PERSONA_TO_HOST_RUNTIME_LICENSE_GATE_IMPLEMENTED_PERSONA_WAKE_REMAINS_CLOSED_UNTIL_TRUSTED_PERSONA_SIGNATURE" + { + return Err("HOLOLAKE_PERSONA_ZERO_LAYER_BOUNDARY_INVALID".into()); + } + + let states = contract + .state_machine + .states + .iter() + .map(String::as_str) + .collect::>(); + let required_evidence = contract + .evidence_model + .required_classes + .iter() + .map(String::as_str) + .collect::>(); + if states.len() != contract.state_machine.states.len() + || !states.contains("VERIFIED_BOUND") + || !states.contains("REJECTED") + || contract.state_machine.ordered_transitions.len() != 5 + || required_evidence.len() != 7 + || !required_evidence.contains("CORRECTIONS_FAILURES_AND_ROUTE_TRANSITIONS") + || !required_evidence.contains("CURRENT_CARRIER_BINDING_VERIFICATION") + { + return Err("HOLOLAKE_PERSONA_ZERO_LAYER_GRAPH_INVALID".into()); + } + let mut current = contract.state_machine.initial_state.as_str(); + for transition in &contract.state_machine.ordered_transitions { + if transition.from != current + || transition.event.is_empty() + || !states.contains(transition.to.as_str()) + { + return Err("HOLOLAKE_PERSONA_ZERO_LAYER_TRANSITION_INVALID".into()); + } + current = transition.to.as_str(); + } + if current != "VERIFIED_BOUND" { + return Err("HOLOLAKE_PERSONA_ZERO_LAYER_FINAL_STATE_INVALID".into()); + } + Ok(()) +} + +pub(crate) fn start_on_application_open() -> Result<(), String> { + validate_contract() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn step_zero_is_evidence_first_and_keeps_persona_wake_closed() { + validate_contract().unwrap(); + } +} diff --git a/product-source/hololake-native-desktop/src-tauri/src/mobile_sync.rs b/product-source/hololake-native-desktop/src-tauri/src/mobile_sync.rs new file mode 100644 index 000000000..0aa429de0 --- /dev/null +++ b/product-source/hololake-native-desktop/src-tauri/src/mobile_sync.rs @@ -0,0 +1,1227 @@ +use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; +use qrcode::{render::svg, QrCode}; +use ring::{ + aead::{Aad, LessSafeKey, Nonce, UnboundKey, CHACHA20_POLY1305}, + rand::{SecureRandom, SystemRandom}, +}; +use rusqlite::{params, Connection, OptionalExtension, TransactionBehavior}; +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + fs, + io::{Read, Write}, + net::{Shutdown, TcpListener, TcpStream, UdpSocket}, + path::{Path, PathBuf}, + sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering}, + Arc, Mutex, + }, + thread, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; +use tauri::{AppHandle, Manager}; +use uuid::Uuid; + +const MOBILE_SYNC_SCHEMA: &str = "hololake.mobile-sync/v1"; +const MOBILE_PAIR_AAD: &[u8] = b"hololake.mobile.pair/v1"; +const MOBILE_SYNC_AAD_PREFIX: &str = "hololake.mobile.sync/v1:"; +const MAX_REQUEST_BYTES: usize = 256 * 1024; +const PAIRING_TTL_MS: u64 = 10 * 60 * 1000; +const DEFAULT_PORT: u16 = 37_421; +const MAX_ACTIVE_CONNECTIONS: usize = 16; +const MODULE_NUMBER: &str = "HLP-MOD-OFFICIAL-MOBILE-SYNC-0001"; +const ADAPTER: &str = "mobile-sync-v1"; + +fn require_active(app: &AppHandle) -> Result<(), String> { + crate::module_package_runtime::require_active_module_adapter(app, MODULE_NUMBER, ADAPTER) +} + +#[derive(Default)] +pub struct MobileSyncState { + handle: Mutex>, +} + +struct MobileSyncHandle { + runtime: Arc, + worker: Option>, +} + +impl Drop for MobileSyncHandle { + fn drop(&mut self) { + self.runtime.shutdown.store(true, Ordering::Release); + let _ = TcpStream::connect(("127.0.0.1", self.runtime.port)); + if let Some(worker) = self.worker.take() { + let _ = worker.join(); + } + } +} + +struct MobileSyncRuntime { + shutdown: AtomicBool, + active_connections: AtomicUsize, + port: u16, + lan_address: String, + desktop_name: String, + account_root: PathBuf, + database_path: PathBuf, + pairing: Mutex, +} + +#[derive(Clone)] +struct PairingMaterial { + pairing_id: String, + secret: [u8; 32], + expires_at_unix_ms: u64, + used: bool, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct MobileSyncStatus { + schema: &'static str, + state: &'static str, + running: bool, + lan_address: Option, + port: Option, + pairing_uri: Option, + pairing_qr_svg: Option, + pairing_expires_at_unix_ms: Option, + paired_devices: Vec, + transport: &'static str, + encryption: &'static str, + desktop_is_root_node: bool, + platform_private_data_custody: bool, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PairedDeviceSummary { + device_id: String, + display_name: String, + platform: String, + created_at_unix_ms: u64, + last_seen_at_unix_ms: Option, + state: &'static str, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct PairRequest { + device_name: String, + platform: String, + request_id: String, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct PairResponse { + schema: &'static str, + state: &'static str, + device_id: String, + session_key: String, + desktop_name: String, + root_node_role: &'static str, + request_id: String, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct SyncRequest { + counter: u64, + after_cursor: Option, + capture: Option, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct MobileCaptureInput { + title: String, + body: String, + request_id: String, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct MobileSyncSnapshot { + schema: &'static str, + state: &'static str, + cursor: u64, + generated_at_unix_ms: u64, + desktop_name: String, + root_node_online: bool, + personal_channel: MobileChannelProjection, + web_novel: MobileWebNovelProjection, + education: MobileEducationProjection, + recent_captures: Vec, + boundary: MobileBoundaryProjection, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct MobileChannelProjection { + home: &'static str, + growth_event_count: u64, + integrity: &'static str, +} + +#[derive(Clone, Debug, Default, Serialize)] +#[serde(rename_all = "camelCase")] +struct MobileWebNovelProjection { + work_count: u64, + volume_count: u64, + chapter_count: u64, + works: Vec, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct MobileWebNovelWork { + work_id: String, + title: String, + status: String, + updated_at_unix_ms: u64, +} + +#[derive(Clone, Debug, Default, Serialize)] +#[serde(rename_all = "camelCase")] +struct MobileEducationProjection { + active_table_count: u64, + archived_table_count: u64, + unassigned_table_count: u64, + sensitive_values_included: bool, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct MobileCaptureProjection { + cursor: u64, + capture_id: String, + title: String, + body: String, + source_device_id: String, + created_at_unix_ms: u64, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct MobileBoundaryProjection { + mobile_role: &'static str, + remote_desktop_clone: bool, + desktop_offline_execution: bool, + sensitive_education_values: &'static str, + model_api: &'static str, +} + +struct HttpRequest { + method: String, + path: String, + headers: HashMap, + body: Vec, +} + +#[derive(Debug)] +struct HttpResponse { + status: u16, + reason: &'static str, + headers: Vec<(String, String)>, + body: Vec, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct RevokeMobileSyncDeviceInput { + device_id: String, +} + +pub fn start_mobile_sync(app: AppHandle) -> Result { + require_active(&app)?; + let state = app.state::(); + let mut guard = state + .handle + .lock() + .map_err(|_| "HOLOLAKE_MOBILE_SYNC_STATE_LOCK_FAILED".to_string())?; + if guard.is_none() { + let sync_root = crate::authenticated_storage::account_storage_root(&app, "mobile-sync-v1")?; + let account_root = sync_root + .parent() + .ok_or_else(|| "HOLOLAKE_MOBILE_SYNC_ACCOUNT_ROOT_INVALID".to_string())? + .to_path_buf(); + let database_path = sync_root.join("mobile-sync.sqlite3"); + initialize_database(&database_path)?; + let listener = TcpListener::bind(("0.0.0.0", DEFAULT_PORT)) + .or_else(|_| TcpListener::bind(("0.0.0.0", 0))) + .map_err(|error| format!("HOLOLAKE_MOBILE_SYNC_LISTEN_FAILED: {error}"))?; + listener + .set_nonblocking(true) + .map_err(|error| format!("HOLOLAKE_MOBILE_SYNC_NONBLOCKING_FAILED: {error}"))?; + let port = listener + .local_addr() + .map_err(|error| format!("HOLOLAKE_MOBILE_SYNC_ADDRESS_FAILED: {error}"))? + .port(); + let runtime = Arc::new(MobileSyncRuntime { + shutdown: AtomicBool::new(false), + active_connections: AtomicUsize::new(0), + port, + lan_address: resolve_lan_address(), + desktop_name: desktop_name(), + account_root, + database_path, + pairing: Mutex::new(new_pairing_material()?), + }); + let worker_runtime = runtime.clone(); + let worker = thread::Builder::new() + .name("hololake-mobile-sync".into()) + .spawn(move || serve(listener, worker_runtime)) + .map_err(|error| format!("HOLOLAKE_MOBILE_SYNC_THREAD_FAILED: {error}"))?; + *guard = Some(MobileSyncHandle { + runtime, + worker: Some(worker), + }); + } + status_from_handle(guard.as_ref()) +} + +pub fn get_mobile_sync_status(app: AppHandle) -> Result { + require_active(&app)?; + let state = app.state::(); + let guard = state + .handle + .lock() + .map_err(|_| "HOLOLAKE_MOBILE_SYNC_STATE_LOCK_FAILED".to_string())?; + status_from_handle(guard.as_ref()) +} + +pub fn rotate_mobile_pairing(app: AppHandle) -> Result { + require_active(&app)?; + let state = app.state::(); + let guard = state + .handle + .lock() + .map_err(|_| "HOLOLAKE_MOBILE_SYNC_STATE_LOCK_FAILED".to_string())?; + let handle = guard + .as_ref() + .ok_or_else(|| "HOLOLAKE_MOBILE_SYNC_NOT_RUNNING".to_string())?; + *handle + .runtime + .pairing + .lock() + .map_err(|_| "HOLOLAKE_MOBILE_SYNC_PAIRING_LOCK_FAILED".to_string())? = + new_pairing_material()?; + status_from_handle(Some(handle)) +} + +pub fn stop_mobile_sync(app: AppHandle) -> Result { + require_active(&app)?; + stop_for_unmount(&app)?; + status_from_handle(None) +} + +pub(crate) fn stop_for_unmount(app: &AppHandle) -> Result<(), String> { + let state = app.state::(); + let handle = state + .handle + .lock() + .map_err(|_| "HOLOLAKE_MOBILE_SYNC_STATE_LOCK_FAILED".to_string())? + .take(); + drop(handle); + Ok(()) +} + +pub fn revoke_mobile_sync_device( + app: AppHandle, + input: RevokeMobileSyncDeviceInput, +) -> Result { + require_active(&app)?; + validate_id(&input.device_id)?; + let state = app.state::(); + let guard = state + .handle + .lock() + .map_err(|_| "HOLOLAKE_MOBILE_SYNC_STATE_LOCK_FAILED".to_string())?; + let handle = guard + .as_ref() + .ok_or_else(|| "HOLOLAKE_MOBILE_SYNC_NOT_RUNNING".to_string())?; + let connection = open_database(&handle.runtime.database_path)?; + let changed = connection + .execute( + "UPDATE mobile_devices SET revoked = 1 WHERE device_id = ?1 AND revoked = 0", + params![input.device_id], + ) + .map_err(database_error)?; + if changed != 1 { + return Err("HOLOLAKE_MOBILE_SYNC_DEVICE_NOT_FOUND".into()); + } + status_from_handle(Some(handle)) +} + +pub fn get_mobile_sync_snapshot(app: AppHandle) -> Result { + require_active(&app)?; + let state = app.state::(); + let guard = state + .handle + .lock() + .map_err(|_| "HOLOLAKE_MOBILE_SYNC_STATE_LOCK_FAILED".to_string())?; + let handle = guard + .as_ref() + .ok_or_else(|| "HOLOLAKE_MOBILE_SYNC_NOT_RUNNING".to_string())?; + build_snapshot(&handle.runtime) +} + +fn status_from_handle(handle: Option<&MobileSyncHandle>) -> Result { + let Some(handle) = handle else { + return Ok(MobileSyncStatus { + schema: MOBILE_SYNC_SCHEMA, + state: "OFFLINE", + running: false, + lan_address: None, + port: None, + pairing_uri: None, + pairing_qr_svg: None, + pairing_expires_at_unix_ms: None, + paired_devices: Vec::new(), + transport: "SAME_LAN_DIRECT", + encryption: "CHACHA20_POLY1305_APPLICATION_LAYER", + desktop_is_root_node: true, + platform_private_data_custody: false, + }); + }; + let now = now_unix_ms(); + let pairing = handle + .runtime + .pairing + .lock() + .map_err(|_| "HOLOLAKE_MOBILE_SYNC_PAIRING_LOCK_FAILED".to_string())? + .clone(); + let active = !pairing.used && pairing.expires_at_unix_ms > now; + let pairing_uri = active.then(|| pairing_uri(&handle.runtime, &pairing)); + let pairing_qr_svg = pairing_uri.as_ref().map(|uri| qr_svg(uri)).transpose()?; + Ok(MobileSyncStatus { + schema: MOBILE_SYNC_SCHEMA, + state: if active { "PAIRING_READY" } else { "RUNNING" }, + running: true, + lan_address: Some(handle.runtime.lan_address.clone()), + port: Some(handle.runtime.port), + pairing_uri, + pairing_qr_svg, + pairing_expires_at_unix_ms: active.then_some(pairing.expires_at_unix_ms), + paired_devices: list_devices(&handle.runtime.database_path)?, + transport: "SAME_LAN_DIRECT", + encryption: "CHACHA20_POLY1305_APPLICATION_LAYER", + desktop_is_root_node: true, + platform_private_data_custody: false, + }) +} + +fn serve(listener: TcpListener, runtime: Arc) { + while !runtime.shutdown.load(Ordering::Acquire) { + match listener.accept() { + Ok((stream, _)) => { + if runtime.active_connections.fetch_add(1, Ordering::AcqRel) + >= MAX_ACTIVE_CONNECTIONS + { + runtime.active_connections.fetch_sub(1, Ordering::AcqRel); + drop(stream); + continue; + } + let connection_runtime = runtime.clone(); + if thread::Builder::new() + .name("hololake-mobile-sync-connection".into()) + .spawn(move || { + handle_connection(stream, &connection_runtime); + connection_runtime + .active_connections + .fetch_sub(1, Ordering::AcqRel); + }) + .is_err() + { + runtime.active_connections.fetch_sub(1, Ordering::AcqRel); + } + } + Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => { + thread::sleep(Duration::from_millis(40)); + } + Err(_) => thread::sleep(Duration::from_millis(100)), + } + } +} + +fn handle_connection(mut stream: TcpStream, runtime: &MobileSyncRuntime) { + let _ = stream.set_read_timeout(Some(Duration::from_secs(5))); + let _ = stream.set_write_timeout(Some(Duration::from_secs(5))); + let response = match read_http_request(&mut stream) { + Ok(request) => route_request(runtime, request), + Err(_) => error_response(400, "Bad Request", "INVALID_REQUEST"), + }; + let _ = write_http_response(&mut stream, response); + let _ = stream.shutdown(Shutdown::Both); +} + +fn route_request(runtime: &MobileSyncRuntime, request: HttpRequest) -> HttpResponse { + match (request.method.as_str(), request.path.as_str()) { + ("GET", "/v1/status") => json_response( + 200, + "OK", + &serde_json::json!({ + "schema": MOBILE_SYNC_SCHEMA, + "state": "PAIRING_ENDPOINT_REACHABLE", + "privatePayloadReadable": false, + }), + ), + ("POST", "/v1/pair") => match pair_device(runtime, &request) { + Ok(response) => response, + Err(code) => error_response(401, "Unauthorized", &code), + }, + ("POST", "/v1/sync") => match sync_device(runtime, &request) { + Ok(response) => response, + Err(code) => error_response(401, "Unauthorized", &code), + }, + _ => error_response(404, "Not Found", "ROUTE_NOT_FOUND"), + } +} + +fn pair_device(runtime: &MobileSyncRuntime, request: &HttpRequest) -> Result { + let pairing_id = required_header(request, "x-hololake-pairing-id")?; + let nonce = decode_nonce(required_header(request, "x-hololake-nonce")?)?; + let mut pairing = runtime + .pairing + .lock() + .map_err(|_| "PAIRING_LOCK_FAILED".to_string())?; + if pairing.used + || pairing.expires_at_unix_ms <= now_unix_ms() + || pairing.pairing_id != pairing_id + { + return Err("PAIRING_EXPIRED_OR_INVALID".into()); + } + let clear = decrypt(&pairing.secret, nonce, MOBILE_PAIR_AAD, &request.body)?; + let input: PairRequest = + serde_json::from_slice(&clear).map_err(|_| "PAIRING_PAYLOAD_INVALID")?; + validate_device_name(&input.device_name)?; + if input.platform != "IOS" || Uuid::parse_str(&input.request_id).is_err() { + return Err("PAIRING_DEVICE_INVALID".into()); + } + let device_id = format!("ios-{}", Uuid::new_v4()); + let session_key = random_32()?; + let now = now_unix_ms(); + let connection = open_database(&runtime.database_path)?; + connection + .execute( + "INSERT INTO mobile_devices (device_id, display_name, platform, session_key, last_counter, created_at_unix_ms, last_seen_at_unix_ms, revoked) VALUES (?1, ?2, ?3, ?4, 0, ?5, NULL, 0)", + params![device_id, input.device_name, input.platform, session_key.as_slice(), now], + ) + .map_err(database_error)?; + pairing.used = true; + let response = PairResponse { + schema: MOBILE_SYNC_SCHEMA, + state: "PAIRED", + device_id, + session_key: URL_SAFE_NO_PAD.encode(session_key), + desktop_name: runtime.desktop_name.clone(), + root_node_role: "USER_LOCAL_COMPUTER_TERMINAL", + request_id: input.request_id, + }; + encrypted_response(&pairing.secret, MOBILE_PAIR_AAD, &response) +} + +fn sync_device(runtime: &MobileSyncRuntime, request: &HttpRequest) -> Result { + let device_id = required_header(request, "x-hololake-device-id")?; + validate_id(device_id)?; + let nonce = decode_nonce(required_header(request, "x-hololake-nonce")?)?; + let mut connection = open_database(&runtime.database_path)?; + let (session_key, revoked): (Vec, i64) = connection + .query_row( + "SELECT session_key, revoked FROM mobile_devices WHERE device_id = ?1", + params![device_id], + |row| Ok((row.get(0)?, row.get(1)?)), + ) + .optional() + .map_err(database_error)? + .ok_or_else(|| "DEVICE_NOT_PAIRED".to_string())?; + if revoked != 0 || session_key.len() != 32 { + return Err("DEVICE_REVOKED".into()); + } + let key: [u8; 32] = session_key + .try_into() + .map_err(|_| "DEVICE_SESSION_KEY_INVALID".to_string())?; + let aad = sync_aad(device_id); + let clear = decrypt(&key, nonce, aad.as_bytes(), &request.body)?; + let input: SyncRequest = serde_json::from_slice(&clear).map_err(|_| "SYNC_PAYLOAD_INVALID")?; + let transaction = connection + .transaction_with_behavior(TransactionBehavior::Immediate) + .map_err(database_error)?; + let last_counter: u64 = transaction + .query_row( + "SELECT last_counter FROM mobile_devices WHERE device_id = ?1 AND revoked = 0", + params![device_id], + |row| row.get(0), + ) + .map_err(database_error)?; + if input.counter <= last_counter { + return Err("SYNC_REPLAY_REJECTED".into()); + } + if let Some(capture) = input.capture { + validate_capture(&capture)?; + transaction + .execute( + "INSERT OR IGNORE INTO mobile_captures (capture_id, title, body, source_device_id, request_id, created_at_unix_ms) VALUES (?1, ?2, ?3, ?4, ?5, ?6)", + params![Uuid::new_v4().to_string(), capture.title.trim(), capture.body.trim(), device_id, capture.request_id, now_unix_ms()], + ) + .map_err(database_error)?; + } + transaction + .execute( + "UPDATE mobile_devices SET last_counter = ?2, last_seen_at_unix_ms = ?3 WHERE device_id = ?1 AND revoked = 0", + params![device_id, input.counter, now_unix_ms()], + ) + .map_err(database_error)?; + transaction.commit().map_err(database_error)?; + let mut snapshot = build_snapshot(runtime)?; + if let Some(after) = input.after_cursor { + snapshot.recent_captures.retain(|item| item.cursor > after); + } + encrypted_response(&key, aad.as_bytes(), &snapshot) +} + +fn build_snapshot(runtime: &MobileSyncRuntime) -> Result { + let captures = list_captures(&runtime.database_path)?; + let cursor = captures.first().map(|item| item.cursor).unwrap_or(0); + let growth_event_count = count_rows( + &runtime + .account_root + .join("channel-growth-v1/channel-growth.sqlite3"), + "growth_events", + None, + ); + Ok(MobileSyncSnapshot { + schema: MOBILE_SYNC_SCHEMA, + state: "SYNCED_WITH_ROOT_NODE", + cursor, + generated_at_unix_ms: now_unix_ms(), + desktop_name: runtime.desktop_name.clone(), + root_node_online: true, + personal_channel: MobileChannelProjection { + home: "ONE_PERSON_ONE_PERSONAL_CHANNEL", + growth_event_count, + integrity: "LOCAL_APPEND_ONLY_PROJECTION", + }, + web_novel: web_novel_projection(&runtime.account_root), + education: education_projection(&runtime.account_root), + recent_captures: captures, + boundary: MobileBoundaryProjection { + mobile_role: "REMOTE_BODY_ENTRY_OF_THE_SAME_PERSONA_SYSTEM", + remote_desktop_clone: false, + desktop_offline_execution: false, + sensitive_education_values: "NEVER_INCLUDED_IN_MOBILE_SUMMARY", + model_api: "NOT_CONFIGURED_LOCAL_PROJECTION_ONLY", + }, + }) +} + +fn web_novel_projection(account_root: &Path) -> MobileWebNovelProjection { + let path = account_root.join("web-novel-workspace-v1/web-novel-workspace.sqlite3"); + let Ok(connection) = + Connection::open_with_flags(&path, rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY) + else { + return MobileWebNovelProjection::default(); + }; + let work_count = query_count(&connection, "SELECT COUNT(*) FROM web_novel_works"); + let volume_count = query_count(&connection, "SELECT COUNT(*) FROM web_novel_volumes"); + let chapter_count = query_count(&connection, "SELECT COUNT(*) FROM web_novel_chapters"); + let works = connection + .prepare("SELECT work_id, title, status, updated_at_unix_ms FROM web_novel_works ORDER BY updated_at_unix_ms DESC LIMIT 20") + .and_then(|mut statement| { + statement + .query_map([], |row| { + Ok(MobileWebNovelWork { + work_id: row.get(0)?, + title: row.get(1)?, + status: row.get(2)?, + updated_at_unix_ms: row.get(3)?, + }) + }) + .and_then(|rows| rows.collect()) + }) + .unwrap_or_default(); + MobileWebNovelProjection { + work_count, + volume_count, + chapter_count, + works, + } +} + +fn education_projection(account_root: &Path) -> MobileEducationProjection { + let path = account_root.join("education-workspace-v1/education-workspace.sqlite3"); + let Ok(connection) = + Connection::open_with_flags(&path, rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY) + else { + return MobileEducationProjection::default(); + }; + MobileEducationProjection { + active_table_count: query_count( + &connection, + "SELECT COUNT(*) FROM education_tables WHERE assignment_scope = 'EDUCATION' AND archived = 0", + ), + archived_table_count: query_count( + &connection, + "SELECT COUNT(*) FROM education_tables WHERE assignment_scope = 'EDUCATION' AND archived = 1", + ), + unassigned_table_count: query_count( + &connection, + "SELECT COUNT(*) FROM education_tables WHERE assignment_scope = 'UNASSIGNED' AND archived = 0", + ), + sensitive_values_included: false, + } +} + +fn query_count(connection: &Connection, sql: &str) -> u64 { + connection.query_row(sql, [], |row| row.get(0)).unwrap_or(0) +} + +fn count_rows(path: &Path, table: &str, condition: Option<&str>) -> u64 { + let Ok(connection) = + Connection::open_with_flags(path, rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY) + else { + return 0; + }; + let sql = format!( + "SELECT COUNT(*) FROM {table}{}", + condition + .map(|value| format!(" WHERE {value}")) + .unwrap_or_default() + ); + query_count(&connection, &sql) +} + +fn list_devices(path: &Path) -> Result, String> { + let connection = open_database(path)?; + let mut statement = connection + .prepare("SELECT device_id, display_name, platform, created_at_unix_ms, last_seen_at_unix_ms, revoked FROM mobile_devices ORDER BY created_at_unix_ms DESC") + .map_err(database_error)?; + let devices = statement + .query_map([], |row| { + let revoked: i64 = row.get(5)?; + Ok(PairedDeviceSummary { + device_id: row.get(0)?, + display_name: row.get(1)?, + platform: row.get(2)?, + created_at_unix_ms: row.get(3)?, + last_seen_at_unix_ms: row.get(4)?, + state: if revoked == 0 { "ACTIVE" } else { "REVOKED" }, + }) + }) + .map_err(database_error)? + .collect::, _>>() + .map_err(database_error)?; + Ok(devices) +} + +fn list_captures(path: &Path) -> Result, String> { + let connection = open_database(path)?; + let mut statement = connection + .prepare("SELECT sequence, capture_id, title, body, source_device_id, created_at_unix_ms FROM mobile_captures ORDER BY sequence DESC LIMIT 50") + .map_err(database_error)?; + let captures = statement + .query_map([], |row| { + Ok(MobileCaptureProjection { + cursor: row.get(0)?, + capture_id: row.get(1)?, + title: row.get(2)?, + body: row.get(3)?, + source_device_id: row.get(4)?, + created_at_unix_ms: row.get(5)?, + }) + }) + .map_err(database_error)? + .collect::, _>>() + .map_err(database_error)?; + Ok(captures) +} + +fn initialize_database(path: &Path) -> Result<(), String> { + if let Some(parent) = path.parent() { + fs::create_dir_all(parent) + .map_err(|error| format!("HOLOLAKE_MOBILE_SYNC_ROOT_FAILED: {error}"))?; + } + let connection = open_database(path)?; + connection + .execute_batch( + "PRAGMA journal_mode = WAL; + PRAGMA foreign_keys = ON; + CREATE TABLE IF NOT EXISTS mobile_devices ( + device_id TEXT PRIMARY KEY, + display_name TEXT NOT NULL, + platform TEXT NOT NULL, + session_key BLOB NOT NULL, + last_counter INTEGER NOT NULL DEFAULT 0, + created_at_unix_ms INTEGER NOT NULL, + last_seen_at_unix_ms INTEGER, + revoked INTEGER NOT NULL DEFAULT 0 CHECK(revoked IN (0,1)) + ); + CREATE TABLE IF NOT EXISTS mobile_captures ( + sequence INTEGER PRIMARY KEY AUTOINCREMENT, + capture_id TEXT NOT NULL UNIQUE, + title TEXT NOT NULL, + body TEXT NOT NULL, + source_device_id TEXT NOT NULL, + request_id TEXT NOT NULL UNIQUE, + created_at_unix_ms INTEGER NOT NULL, + FOREIGN KEY(source_device_id) REFERENCES mobile_devices(device_id) + );", + ) + .map_err(database_error) +} + +fn open_database(path: &Path) -> Result { + let connection = Connection::open(path).map_err(database_error)?; + connection + .busy_timeout(Duration::from_secs(3)) + .map_err(database_error)?; + Ok(connection) +} + +fn read_http_request(stream: &mut TcpStream) -> Result { + let mut buffer = Vec::with_capacity(4096); + let mut chunk = [0u8; 4096]; + let header_end = loop { + let read = stream.read(&mut chunk).map_err(|_| "REQUEST_READ_FAILED")?; + if read == 0 { + return Err("REQUEST_TRUNCATED".into()); + } + buffer.extend_from_slice(&chunk[..read]); + if buffer.len() > MAX_REQUEST_BYTES { + return Err("REQUEST_TOO_LARGE".into()); + } + if let Some(index) = find_bytes(&buffer, b"\r\n\r\n") { + break index + 4; + } + }; + let header_text = std::str::from_utf8(&buffer[..header_end]).map_err(|_| "HEADERS_INVALID")?; + let mut lines = header_text.split("\r\n"); + let mut request_line = lines + .next() + .ok_or("REQUEST_LINE_MISSING")? + .split_whitespace(); + let method = request_line.next().ok_or("METHOD_MISSING")?.to_string(); + let path = request_line.next().ok_or("PATH_MISSING")?.to_string(); + if request_line.next() != Some("HTTP/1.1") || request_line.next().is_some() { + return Err("HTTP_VERSION_INVALID".into()); + } + let mut headers = HashMap::new(); + for line in lines.filter(|line| !line.is_empty()) { + let Some((name, value)) = line.split_once(':') else { + return Err("HEADER_INVALID".into()); + }; + if headers + .insert(name.trim().to_ascii_lowercase(), value.trim().to_string()) + .is_some() + { + return Err("DUPLICATE_HEADER_REJECTED".into()); + } + } + let content_length = headers + .get("content-length") + .map(|value| value.parse::().map_err(|_| "CONTENT_LENGTH_INVALID")) + .transpose()? + .unwrap_or(0); + if content_length > MAX_REQUEST_BYTES { + return Err("REQUEST_TOO_LARGE".into()); + } + while buffer.len() < header_end + content_length { + let read = stream.read(&mut chunk).map_err(|_| "BODY_READ_FAILED")?; + if read == 0 { + return Err("BODY_TRUNCATED".into()); + } + buffer.extend_from_slice(&chunk[..read]); + if buffer.len() > MAX_REQUEST_BYTES { + return Err("REQUEST_TOO_LARGE".into()); + } + } + Ok(HttpRequest { + method, + path, + headers, + body: buffer[header_end..header_end + content_length].to_vec(), + }) +} + +fn write_http_response(stream: &mut TcpStream, response: HttpResponse) -> Result<(), String> { + let mut head = format!( + "HTTP/1.1 {} {}\r\nContent-Length: {}\r\nConnection: close\r\nCache-Control: no-store\r\n", + response.status, + response.reason, + response.body.len() + ); + for (name, value) in response.headers { + head.push_str(&format!("{name}: {value}\r\n")); + } + head.push_str("\r\n"); + stream + .write_all(head.as_bytes()) + .map_err(|_| "RESPONSE_WRITE_FAILED")?; + stream + .write_all(&response.body) + .map_err(|_| "RESPONSE_WRITE_FAILED".to_string()) +} + +fn encrypted_response( + key: &[u8; 32], + aad: &[u8], + value: &T, +) -> Result { + let clear = serde_json::to_vec(value).map_err(|_| "RESPONSE_SERIALIZATION_FAILED")?; + let nonce = random_nonce()?; + let body = encrypt(key, nonce, aad, &clear)?; + Ok(HttpResponse { + status: 200, + reason: "OK", + headers: vec![ + ("Content-Type".into(), "application/octet-stream".into()), + ("X-HoloLake-Nonce".into(), URL_SAFE_NO_PAD.encode(nonce)), + ], + body, + }) +} + +fn json_response(status: u16, reason: &'static str, value: &serde_json::Value) -> HttpResponse { + HttpResponse { + status, + reason, + headers: vec![("Content-Type".into(), "application/json".into())], + body: serde_json::to_vec(value).unwrap_or_default(), + } +} + +fn error_response(status: u16, reason: &'static str, code: &str) -> HttpResponse { + json_response( + status, + reason, + &serde_json::json!({"schema": MOBILE_SYNC_SCHEMA, "state": "REJECTED", "code": code}), + ) +} + +fn encrypt(key: &[u8; 32], nonce: [u8; 12], aad: &[u8], clear: &[u8]) -> Result, String> { + let unbound = UnboundKey::new(&CHACHA20_POLY1305, key).map_err(|_| "ENCRYPTION_KEY_INVALID")?; + let key = LessSafeKey::new(unbound); + let mut output = clear.to_vec(); + key.seal_in_place_append_tag( + Nonce::assume_unique_for_key(nonce), + Aad::from(aad), + &mut output, + ) + .map_err(|_| "ENCRYPTION_FAILED".to_string())?; + Ok(output) +} + +fn decrypt( + key: &[u8; 32], + nonce: [u8; 12], + aad: &[u8], + encrypted: &[u8], +) -> Result, String> { + let unbound = UnboundKey::new(&CHACHA20_POLY1305, key).map_err(|_| "ENCRYPTION_KEY_INVALID")?; + let key = LessSafeKey::new(unbound); + let mut buffer = encrypted.to_vec(); + let clear = key + .open_in_place( + Nonce::assume_unique_for_key(nonce), + Aad::from(aad), + &mut buffer, + ) + .map_err(|_| "AUTHENTICATION_FAILED".to_string())?; + Ok(clear.to_vec()) +} + +fn pairing_uri(runtime: &MobileSyncRuntime, pairing: &PairingMaterial) -> String { + format!( + "hololake://pair?host={}&port={}&id={}&secret={}", + runtime.lan_address, + runtime.port, + pairing.pairing_id, + URL_SAFE_NO_PAD.encode(pairing.secret) + ) +} + +fn qr_svg(value: &str) -> Result { + let code = QrCode::new(value.as_bytes()).map_err(|_| "HOLOLAKE_MOBILE_SYNC_QR_FAILED")?; + Ok(code + .render::() + .min_dimensions(320, 320) + .dark_color(svg::Color("#07111f")) + .light_color(svg::Color("#f5f7fc")) + .build()) +} + +fn new_pairing_material() -> Result { + Ok(PairingMaterial { + pairing_id: Uuid::new_v4().to_string(), + secret: random_32()?, + expires_at_unix_ms: now_unix_ms().saturating_add(PAIRING_TTL_MS), + used: false, + }) +} + +fn random_32() -> Result<[u8; 32], String> { + let mut bytes = [0u8; 32]; + SystemRandom::new() + .fill(&mut bytes) + .map_err(|_| "SECURE_RANDOM_UNAVAILABLE".to_string())?; + Ok(bytes) +} + +fn random_nonce() -> Result<[u8; 12], String> { + let mut bytes = [0u8; 12]; + SystemRandom::new() + .fill(&mut bytes) + .map_err(|_| "SECURE_RANDOM_UNAVAILABLE".to_string())?; + Ok(bytes) +} + +fn decode_nonce(value: &str) -> Result<[u8; 12], String> { + URL_SAFE_NO_PAD + .decode(value) + .map_err(|_| "NONCE_INVALID".to_string())? + .try_into() + .map_err(|_| "NONCE_INVALID".to_string()) +} + +fn required_header<'a>(request: &'a HttpRequest, name: &str) -> Result<&'a str, String> { + request + .headers + .get(name) + .map(String::as_str) + .ok_or_else(|| "AUTHENTICATION_HEADER_MISSING".to_string()) +} + +fn sync_aad(device_id: &str) -> String { + format!("{MOBILE_SYNC_AAD_PREFIX}{device_id}") +} + +fn validate_device_name(value: &str) -> Result<(), String> { + let length = value.trim().chars().count(); + if (1..=80).contains(&length) { + Ok(()) + } else { + Err("DEVICE_NAME_INVALID".into()) + } +} + +fn validate_capture(value: &MobileCaptureInput) -> Result<(), String> { + if value.title.trim().chars().count() > 120 + || value.body.trim().is_empty() + || value.body.trim().chars().count() > 10_000 + || Uuid::parse_str(&value.request_id).is_err() + { + Err("CAPTURE_INVALID".into()) + } else { + Ok(()) + } +} + +fn validate_id(value: &str) -> Result<(), String> { + if !value.is_empty() + && value.len() <= 80 + && value + .chars() + .all(|character| character.is_ascii_alphanumeric() || character == '-') + { + Ok(()) + } else { + Err("IDENTIFIER_INVALID".into()) + } +} + +fn resolve_lan_address() -> String { + let candidate = UdpSocket::bind("0.0.0.0:0") + .and_then(|socket| { + socket.connect("192.0.2.1:9")?; + socket.local_addr() + }) + .map(|address| address.ip().to_string()); + match candidate { + Ok(value) if value != "0.0.0.0" => value, + _ => "127.0.0.1".into(), + } +} + +fn desktop_name() -> String { + std::env::var("COMPUTERNAME") + .or_else(|_| std::env::var("HOSTNAME")) + .ok() + .filter(|value| !value.trim().is_empty()) + .unwrap_or_else(|| "HoloLake Mac".into()) +} + +fn now_unix_ms() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_millis() + .try_into() + .unwrap_or(u64::MAX) +} + +fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option { + haystack + .windows(needle.len()) + .position(|window| window == needle) +} + +fn database_error(error: rusqlite::Error) -> String { + format!("HOLOLAKE_MOBILE_SYNC_DATABASE_FAILED: {error}") +} + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::tempdir; + + #[test] + fn encrypted_payload_requires_the_exact_key_nonce_and_aad() { + let key = [7u8; 32]; + let nonce = [9u8; 12]; + let encrypted = encrypt(&key, nonce, b"scope", b"hello").unwrap(); + assert_eq!( + decrypt(&key, nonce, b"scope", &encrypted).unwrap(), + b"hello" + ); + assert!(decrypt(&key, nonce, b"other", &encrypted).is_err()); + } + + #[test] + fn sync_database_keeps_device_keys_and_capture_requests_unique() { + let temporary = tempdir().unwrap(); + let database = temporary.path().join("mobile-sync.sqlite3"); + initialize_database(&database).unwrap(); + let connection = open_database(&database).unwrap(); + connection + .execute( + "INSERT INTO mobile_devices (device_id, display_name, platform, session_key, created_at_unix_ms) VALUES ('ios-one', 'iPhone', 'IOS', ?1, 1)", + params![[1u8; 32].as_slice()], + ) + .unwrap(); + connection + .execute( + "INSERT INTO mobile_captures (capture_id, title, body, source_device_id, request_id, created_at_unix_ms) VALUES ('capture-one', '想法', '正文', 'ios-one', 'request-one', 2)", + [], + ) + .unwrap(); + assert_eq!(list_devices(&database).unwrap().len(), 1); + assert_eq!(list_captures(&database).unwrap().len(), 1); + assert!(connection + .execute( + "INSERT INTO mobile_captures (capture_id, title, body, source_device_id, request_id, created_at_unix_ms) VALUES ('capture-two', '重复', '正文', 'ios-one', 'request-one', 3)", + [], + ) + .is_err()); + } + + #[test] + fn pairing_uri_contains_a_full_random_secret_without_persisting_it() { + let material = new_pairing_material().unwrap(); + let runtime = MobileSyncRuntime { + shutdown: AtomicBool::new(false), + active_connections: AtomicUsize::new(0), + port: 37421, + lan_address: "192.168.1.8".into(), + desktop_name: "Mac".into(), + account_root: PathBuf::new(), + database_path: PathBuf::new(), + pairing: Mutex::new(material.clone()), + }; + let uri = pairing_uri(&runtime, &material); + assert!(uri.starts_with("hololake://pair?host=192.168.1.8&port=37421&id=")); + assert!(uri.contains("&secret=")); + } + + #[test] + fn pairing_is_single_use_and_sync_replay_is_rejected() { + let temporary = tempdir().unwrap(); + let database = temporary.path().join("mobile-sync.sqlite3"); + initialize_database(&database).unwrap(); + let pairing = new_pairing_material().unwrap(); + let runtime = MobileSyncRuntime { + shutdown: AtomicBool::new(false), + active_connections: AtomicUsize::new(0), + port: 37421, + lan_address: "192.168.1.8".into(), + desktop_name: "Mac".into(), + account_root: temporary.path().to_path_buf(), + database_path: database, + pairing: Mutex::new(pairing.clone()), + }; + let pair_nonce = [3u8; 12]; + let pair_clear = serde_json::to_vec(&serde_json::json!({ + "deviceName": "冰朔的 iPhone", + "platform": "IOS", + "requestId": Uuid::new_v4().to_string() + })) + .unwrap(); + let pair_request = HttpRequest { + method: "POST".into(), + path: "/v1/pair".into(), + headers: HashMap::from([ + ("x-hololake-pairing-id".into(), pairing.pairing_id.clone()), + ( + "x-hololake-nonce".into(), + URL_SAFE_NO_PAD.encode(pair_nonce), + ), + ]), + body: encrypt(&pairing.secret, pair_nonce, MOBILE_PAIR_AAD, &pair_clear).unwrap(), + }; + let pair_response = pair_device(&runtime, &pair_request).unwrap(); + assert_eq!( + pair_device(&runtime, &pair_request).unwrap_err(), + "PAIRING_EXPIRED_OR_INVALID" + ); + let response_nonce = decode_nonce( + pair_response + .headers + .iter() + .find(|(name, _)| name == "X-HoloLake-Nonce") + .map(|(_, value)| value.as_str()) + .unwrap(), + ) + .unwrap(); + let response: serde_json::Value = serde_json::from_slice( + &decrypt( + &pairing.secret, + response_nonce, + MOBILE_PAIR_AAD, + &pair_response.body, + ) + .unwrap(), + ) + .unwrap(); + let device_id = response["deviceId"].as_str().unwrap(); + let key: [u8; 32] = URL_SAFE_NO_PAD + .decode(response["sessionKey"].as_str().unwrap()) + .unwrap() + .try_into() + .unwrap(); + let sync_nonce = [4u8; 12]; + let sync_clear = serde_json::to_vec(&serde_json::json!({ + "counter": 1, + "afterCursor": null, + "capture": null + })) + .unwrap(); + let aad = sync_aad(device_id); + let sync_request = HttpRequest { + method: "POST".into(), + path: "/v1/sync".into(), + headers: HashMap::from([ + ("x-hololake-device-id".into(), device_id.into()), + ( + "x-hololake-nonce".into(), + URL_SAFE_NO_PAD.encode(sync_nonce), + ), + ]), + body: encrypt(&key, sync_nonce, aad.as_bytes(), &sync_clear).unwrap(), + }; + assert!(sync_device(&runtime, &sync_request).is_ok()); + assert_eq!( + sync_device(&runtime, &sync_request).unwrap_err(), + "SYNC_REPLAY_REJECTED" + ); + } +} diff --git a/product-source/hololake-native-desktop/src-tauri/src/module_package_runtime.rs b/product-source/hololake-native-desktop/src-tauri/src/module_package_runtime.rs new file mode 100644 index 000000000..7f671fdb4 --- /dev/null +++ b/product-source/hololake-native-desktop/src-tauri/src/module_package_runtime.rs @@ -0,0 +1,1765 @@ +//! Signed, account-scoped HoloLake module package runtime. +//! +//! A repository is never executable input. The runtime accepts only an immutable +//! detached-minisign package, validates a declarative manifest, persists lifecycle +//! state in SQLite and writes a hash-chained receipt for every mutation. Package +//! mount does not load native code or arbitrary webview JavaScript. + +use base64::{engine::general_purpose::STANDARD, Engine as _}; +use minisign_verify::{PublicKey, Signature}; +use ring::digest::{digest, SHA256}; +use rusqlite::{params, Connection, OptionalExtension, Transaction}; +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Value}; +use std::collections::BTreeSet; +use std::fs; +use std::path::{Path, PathBuf}; +use std::time::{SystemTime, UNIX_EPOCH}; +use tauri::AppHandle; +use uuid::Uuid; + +const CONTRACT_RAW: &str = include_str!("../../contracts/module-package-runtime.json"); +const RELEASE_TRUST_RAW: &str = include_str!("../release-trust.json"); +const CONTRACT_SCHEMA: &str = "hololake.module-package-runtime/v1"; +const PACKAGE_SCHEMA: &str = "hololake.module-package/v1"; +const HOST_VERSION: &str = "0.5.0"; +const MAX_PACKAGE_BYTES: u64 = 16 * 1024 * 1024; +const NATIVE_COMPOSITION_NUMBER: &str = "HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001"; +const NATIVE_COMPOSITION_PACKAGE: &[u8] = include_bytes!( + "../../fixtures/module-packages/HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001-0.1.0.ghmod" +); +const NATIVE_COMPOSITION_SIGNATURE: &str = include_str!( + "../../fixtures/module-packages/HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001-0.1.0.ghmod.sig" +); +const CHANNEL_WORKBENCH_NUMBER: &str = "HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001"; +const CHANNEL_WORKBENCH_PACKAGE: &[u8] = include_bytes!( + "../../fixtures/module-packages/HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001-0.1.0.ghmod" +); +const CHANNEL_WORKBENCH_SIGNATURE: &str = include_str!( + "../../fixtures/module-packages/HLP-MOD-LOCAL-CHANNEL-WORKBENCH-0001-0.1.0.ghmod.sig" +); +const PERSONA_CHANNEL_BODY_NUMBER: &str = "HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001"; +const PERSONA_CHANNEL_BODY_PACKAGE: &[u8] = include_bytes!( + "../../fixtures/module-packages/HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001-0.1.0.ghmod" +); +const PERSONA_CHANNEL_BODY_SIGNATURE: &str = include_str!( + "../../fixtures/module-packages/HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001-0.1.0.ghmod.sig" +); +const EDUCATION_WORKBENCH_NUMBER: &str = "HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001"; +const EDUCATION_WORKBENCH_PACKAGE: &[u8] = include_bytes!( + "../../fixtures/module-packages/HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001-0.1.0.ghmod" +); +const EDUCATION_WORKBENCH_SIGNATURE: &str = include_str!( + "../../fixtures/module-packages/HLP-MOD-OFFICIAL-EDUCATION-WORKBENCH-0001-0.1.0.ghmod.sig" +); +const WEB_NOVEL_WORKBENCH_NUMBER: &str = "HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001"; +const WEB_NOVEL_WORKBENCH_PACKAGE: &[u8] = include_bytes!( + "../../fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001-0.1.0.ghmod" +); +const WEB_NOVEL_WORKBENCH_SIGNATURE: &str = include_str!( + "../../fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001-0.1.0.ghmod.sig" +); +const WEB_NOVEL_OUTLINE_NUMBER: &str = "HLP-MOD-OFFICIAL-WEB-NOVEL-OUTLINE-0001"; +const WEB_NOVEL_OUTLINE_PACKAGE: &[u8] = include_bytes!( + "../../fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-OUTLINE-0001-0.1.0.ghmod" +); +const WEB_NOVEL_OUTLINE_SIGNATURE: &str = include_str!( + "../../fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-OUTLINE-0001-0.1.0.ghmod.sig" +); +const WEB_NOVEL_GRID_NUMBER: &str = "HLP-MOD-OFFICIAL-WEB-NOVEL-GRID-0001"; +const WEB_NOVEL_GRID_PACKAGE: &[u8] = include_bytes!( + "../../fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-GRID-0001-0.1.0.ghmod" +); +const WEB_NOVEL_GRID_SIGNATURE: &str = include_str!( + "../../fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-GRID-0001-0.1.0.ghmod.sig" +); +const WEB_NOVEL_STORYWORLD_NUMBER: &str = "HLP-MOD-OFFICIAL-WEB-NOVEL-STORYWORLD-0001"; +const WEB_NOVEL_STORYWORLD_PACKAGE: &[u8] = include_bytes!( + "../../fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-STORYWORLD-0001-0.1.0.ghmod" +); +const WEB_NOVEL_STORYWORLD_SIGNATURE: &str = include_str!( + "../../fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-STORYWORLD-0001-0.1.0.ghmod.sig" +); +const WEB_NOVEL_DELIVERY_NUMBER: &str = "HLP-MOD-OFFICIAL-WEB-NOVEL-DELIVERY-0001"; +const WEB_NOVEL_DELIVERY_PACKAGE: &[u8] = include_bytes!( + "../../fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-DELIVERY-0001-0.1.0.ghmod" +); +const WEB_NOVEL_DELIVERY_SIGNATURE: &str = include_str!( + "../../fixtures/module-packages/HLP-MOD-OFFICIAL-WEB-NOVEL-DELIVERY-0001-0.1.0.ghmod.sig" +); +const MOBILE_SYNC_NUMBER: &str = "HLP-MOD-OFFICIAL-MOBILE-SYNC-0001"; +const MOBILE_SYNC_PACKAGE: &[u8] = + include_bytes!("../../fixtures/module-packages/HLP-MOD-OFFICIAL-MOBILE-SYNC-0001-0.1.0.ghmod"); +const MOBILE_SYNC_SIGNATURE: &str = include_str!( + "../../fixtures/module-packages/HLP-MOD-OFFICIAL-MOBILE-SYNC-0001-0.1.0.ghmod.sig" +); +const DYNAMIC_WORLD_SURFACE_NUMBER: &str = "HLP-MOD-OFFICIAL-DYNAMIC-WORLD-SURFACE-0001"; +const DYNAMIC_WORLD_SURFACE_PACKAGE: &[u8] = include_bytes!( + "../../fixtures/module-packages/HLP-MOD-OFFICIAL-DYNAMIC-WORLD-SURFACE-0001-0.1.0.ghmod" +); +const DYNAMIC_WORLD_SURFACE_SIGNATURE: &str = include_str!( + "../../fixtures/module-packages/HLP-MOD-OFFICIAL-DYNAMIC-WORLD-SURFACE-0001-0.1.0.ghmod.sig" +); + +struct BundledModuleSource { + module_number: &'static str, + package: &'static [u8], + signature: &'static str, +} + +const BUNDLED_MODULES: &[BundledModuleSource] = &[ + BundledModuleSource { + module_number: NATIVE_COMPOSITION_NUMBER, + package: NATIVE_COMPOSITION_PACKAGE, + signature: NATIVE_COMPOSITION_SIGNATURE, + }, + BundledModuleSource { + module_number: CHANNEL_WORKBENCH_NUMBER, + package: CHANNEL_WORKBENCH_PACKAGE, + signature: CHANNEL_WORKBENCH_SIGNATURE, + }, + BundledModuleSource { + module_number: PERSONA_CHANNEL_BODY_NUMBER, + package: PERSONA_CHANNEL_BODY_PACKAGE, + signature: PERSONA_CHANNEL_BODY_SIGNATURE, + }, + BundledModuleSource { + module_number: EDUCATION_WORKBENCH_NUMBER, + package: EDUCATION_WORKBENCH_PACKAGE, + signature: EDUCATION_WORKBENCH_SIGNATURE, + }, + BundledModuleSource { + module_number: WEB_NOVEL_WORKBENCH_NUMBER, + package: WEB_NOVEL_WORKBENCH_PACKAGE, + signature: WEB_NOVEL_WORKBENCH_SIGNATURE, + }, + BundledModuleSource { + module_number: WEB_NOVEL_OUTLINE_NUMBER, + package: WEB_NOVEL_OUTLINE_PACKAGE, + signature: WEB_NOVEL_OUTLINE_SIGNATURE, + }, + BundledModuleSource { + module_number: WEB_NOVEL_GRID_NUMBER, + package: WEB_NOVEL_GRID_PACKAGE, + signature: WEB_NOVEL_GRID_SIGNATURE, + }, + BundledModuleSource { + module_number: WEB_NOVEL_STORYWORLD_NUMBER, + package: WEB_NOVEL_STORYWORLD_PACKAGE, + signature: WEB_NOVEL_STORYWORLD_SIGNATURE, + }, + BundledModuleSource { + module_number: WEB_NOVEL_DELIVERY_NUMBER, + package: WEB_NOVEL_DELIVERY_PACKAGE, + signature: WEB_NOVEL_DELIVERY_SIGNATURE, + }, + BundledModuleSource { + module_number: MOBILE_SYNC_NUMBER, + package: MOBILE_SYNC_PACKAGE, + signature: MOBILE_SYNC_SIGNATURE, + }, + BundledModuleSource { + module_number: DYNAMIC_WORLD_SURFACE_NUMBER, + package: DYNAMIC_WORLD_SURFACE_PACKAGE, + signature: DYNAMIC_WORLD_SURFACE_SIGNATURE, + }, +]; + +#[derive(Debug, Deserialize)] +struct RuntimeContract { + schema: String, + record_id: String, + package_schema: String, + host: HostContract, + registered_adapters: Vec, + lifecycle: Vec, +} + +#[derive(Debug, Deserialize)] +struct HostContract { + version: String, + maximum_package_bytes: u64, + arbitrary_native_code: bool, + arbitrary_webview_javascript: bool, + declarative_payload_only: bool, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct ModulePackage { + schema: String, + manifest: ModuleManifest, + payload: Value, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct ModuleManifest { + module_number: String, + registration_class: String, + display_name: String, + version: String, + minimum_host_version: String, + adapter: String, + content_digest: String, + permissions: Vec, + user_data_schema: String, + self_test: ModuleSelfTest, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct ModuleSelfTest { + kind: String, + expected_content_digest: String, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct VerifyModulePackageInput { + package_path: String, + signature_path: String, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct InstallModulePackageInput { + package_path: String, + signature_path: String, + expected_package_sha256: String, + human_confirmed_permission_expansion: bool, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct ModuleNumberInput { + module_number: String, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct VerifiedModulePackage { + state: String, + package_sha256: String, + module_number: String, + display_name: String, + version: String, + adapter: String, + permissions: Vec, + signature_verified: bool, + package_code_executed: bool, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct InstalledModule { + module_number: String, + display_name: String, + version: String, + adapter: String, + state: String, + package_sha256: String, + previous_package_sha256: Option, + permissions: Vec, + user_data_preserved: bool, + updated_at_unix_ms: u64, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ModuleRuntimeSnapshot { + schema: &'static str, + state: &'static str, + host_version: &'static str, + package_code_executed: bool, + modules: Vec, + receipt_count: u64, + latest_receipt_hash: String, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ModuleMutationReceipt { + receipt_id: String, + event: String, + module_number: String, + state: String, + package_sha256: String, + previous_receipt_hash: String, + receipt_hash: String, + observed_at_unix_ms: u64, + user_data_preserved: bool, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct ActivateBundledModuleInput { + module_number: String, + human_confirmed_permission_expansion: bool, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct BundledModuleDescriptor { + module_number: String, + display_name: String, + version: String, + adapter: String, + permissions: Vec, + package_sha256: String, + installed_state: String, + signature_verified: bool, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct BundledModuleActivation { + schema: &'static str, + state: String, + module_number: String, + receipts: Vec, + snapshot: ModuleRuntimeSnapshot, +} + +fn now_unix_ms() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_millis() as u64) + .unwrap_or(0) +} + +fn sha256_hex(bytes: &[u8]) -> String { + digest(&SHA256, bytes) + .as_ref() + .iter() + .map(|byte| format!("{byte:02x}")) + .collect() +} + +fn is_sha256(value: &str) -> bool { + value.len() == 64 && value.chars().all(|item| item.is_ascii_hexdigit()) +} + +fn canonical_value(value: &Value) -> Value { + match value { + Value::Array(values) => Value::Array(values.iter().map(canonical_value).collect()), + Value::Object(object) => { + let mut keys = object.keys().collect::>(); + keys.sort(); + let mut canonical = Map::new(); + for key in keys { + canonical.insert(key.clone(), canonical_value(&object[key])); + } + Value::Object(canonical) + } + _ => value.clone(), + } +} + +fn payload_digest(value: &Value) -> Result { + let bytes = serde_json::to_vec(&canonical_value(value)) + .map_err(|error| format!("HOLOLAKE_MODULE_PAYLOAD_INVALID: {error}"))?; + Ok(sha256_hex(&bytes)) +} + +fn contract() -> Result { + let contract: RuntimeContract = serde_json::from_str(CONTRACT_RAW) + .map_err(|error| format!("HOLOLAKE_MODULE_RUNTIME_CONTRACT_INVALID: {error}"))?; + if contract.schema != CONTRACT_SCHEMA + || contract.record_id != "HLP-MODULE-PACKAGE-RUNTIME-001" + || contract.package_schema != PACKAGE_SCHEMA + || contract.host.version != HOST_VERSION + || contract.host.maximum_package_bytes != MAX_PACKAGE_BYTES + || contract.host.arbitrary_native_code + || contract.host.arbitrary_webview_javascript + || !contract.host.declarative_payload_only + || contract.registered_adapters.len() != 7 + || contract.lifecycle.len() != 6 + { + return Err("HOLOLAKE_MODULE_RUNTIME_CONTRACT_BOUNDARY_INVALID".into()); + } + Ok(contract) +} + +fn decode_outer_base64(value: &str, label: &str) -> Result { + let bytes = STANDARD + .decode(value.trim()) + .map_err(|error| format!("{label}_BASE64_INVALID: {error}"))?; + String::from_utf8(bytes).map_err(|error| format!("{label}_UTF8_INVALID: {error}")) +} + +fn verify_signature_raw( + package: &[u8], + signature_raw: &str, + trust_raw: &str, +) -> Result<(), String> { + let trust: Value = serde_json::from_str(trust_raw) + .map_err(|error| format!("HOLOLAKE_MODULE_TRUST_INVALID: {error}"))?; + if trust.get("state").and_then(Value::as_str) != Some("PROVISIONED") { + return Err("HOLOLAKE_MODULE_TRUST_UNPROVISIONED".into()); + } + let encoded_key = trust + .get("publicKey") + .and_then(Value::as_str) + .ok_or("HOLOLAKE_MODULE_TRUST_KEY_MISSING")?; + let public_key = PublicKey::decode(&decode_outer_base64(encoded_key, "HOLOLAKE_MODULE_KEY")?) + .map_err(|error| format!("HOLOLAKE_MODULE_KEY_INVALID: {error}"))?; + let signature = Signature::decode(&decode_outer_base64( + signature_raw, + "HOLOLAKE_MODULE_SIGNATURE", + )?) + .map_err(|error| format!("HOLOLAKE_MODULE_SIGNATURE_INVALID: {error}"))?; + public_key + .verify(package, &signature, true) + .map_err(|error| format!("HOLOLAKE_MODULE_SIGNATURE_REJECTED: {error}")) +} + +fn valid_number(value: &str, registration_class: &str) -> bool { + let prefix = match registration_class { + "OFFICIAL_LIGHTHOUSE" => "HLP-MOD-OFFICIAL-", + "PRIVATE_CHANNEL_LOCAL" => "HLP-MOD-LOCAL-", + _ => return false, + }; + value.starts_with(prefix) + && value.len() <= 96 + && value + .chars() + .all(|item| item.is_ascii_uppercase() || item.is_ascii_digit() || item == '-') +} + +fn version_triplet(value: &str) -> Option<(u64, u64, u64)> { + let parts = value.split('.').collect::>(); + if parts.len() != 3 { + return None; + } + Some(( + parts[0].parse().ok()?, + parts[1].parse().ok()?, + parts[2].parse().ok()?, + )) +} + +fn validate_package(package: &ModulePackage, contract: &RuntimeContract) -> Result<(), String> { + let manifest = &package.manifest; + let permissions = manifest.permissions.iter().collect::>(); + if package.schema != PACKAGE_SCHEMA + || !valid_number(&manifest.module_number, &manifest.registration_class) + || manifest.display_name.trim().is_empty() + || manifest.display_name.len() > 120 + || version_triplet(&manifest.version).is_none() + || version_triplet(&manifest.minimum_host_version).is_none() + || version_triplet(&manifest.minimum_host_version) > version_triplet(HOST_VERSION) + || !contract.registered_adapters.contains(&manifest.adapter) + || !is_sha256(&manifest.content_digest) + || permissions.len() != manifest.permissions.len() + || manifest.permissions.len() > 32 + || manifest.permissions.iter().any(|permission| { + permission.is_empty() + || permission.len() > 80 + || !permission + .chars() + .all(|item| item.is_ascii_uppercase() || item.is_ascii_digit() || item == '_') + }) + || manifest.user_data_schema.trim().is_empty() + || manifest.self_test.kind != "DECLARATIVE_SCHEMA_V1" + || manifest.self_test.expected_content_digest != manifest.content_digest + || !package.payload.is_object() + || payload_digest(&package.payload)? != manifest.content_digest + { + return Err("HOLOLAKE_MODULE_PACKAGE_CONTRACT_INVALID".into()); + } + Ok(()) +} + +fn read_verified_package( + package_path: &Path, + signature_path: &Path, + trust_raw: &str, +) -> Result<(Vec, ModulePackage, String), String> { + let metadata = fs::symlink_metadata(package_path) + .map_err(|error| format!("HOLOLAKE_MODULE_PACKAGE_METADATA_FAILED: {error}"))?; + let signature_metadata = fs::symlink_metadata(signature_path) + .map_err(|error| format!("HOLOLAKE_MODULE_SIGNATURE_METADATA_FAILED: {error}"))?; + if metadata.file_type().is_symlink() + || signature_metadata.file_type().is_symlink() + || !metadata.is_file() + || !signature_metadata.is_file() + || metadata.len() == 0 + || metadata.len() > MAX_PACKAGE_BYTES + || signature_metadata.len() == 0 + || signature_metadata.len() > 16 * 1024 + { + return Err("HOLOLAKE_MODULE_PACKAGE_SIZE_INVALID".into()); + } + let bytes = fs::read(package_path) + .map_err(|error| format!("HOLOLAKE_MODULE_PACKAGE_READ_FAILED: {error}"))?; + let signature_raw = fs::read_to_string(signature_path) + .map_err(|error| format!("HOLOLAKE_MODULE_SIGNATURE_READ_FAILED: {error}"))?; + read_verified_package_bytes(&bytes, &signature_raw, trust_raw) +} + +fn read_verified_package_bytes( + bytes: &[u8], + signature_raw: &str, + trust_raw: &str, +) -> Result<(Vec, ModulePackage, String), String> { + verify_signature_raw(bytes, signature_raw, trust_raw)?; + let package: ModulePackage = serde_json::from_slice(bytes) + .map_err(|error| format!("HOLOLAKE_MODULE_PACKAGE_JSON_INVALID: {error}"))?; + validate_package(&package, &contract()?)?; + let package_sha256 = sha256_hex(bytes); + Ok((bytes.to_vec(), package, package_sha256)) +} + +fn bundled_package(module_number: &str) -> Result<(&'static [u8], &'static str), String> { + BUNDLED_MODULES + .iter() + .find(|source| source.module_number == module_number) + .map(|source| (source.package, source.signature)) + .ok_or_else(|| "HOLOLAKE_BUNDLED_MODULE_UNKNOWN".into()) +} + +fn runtime_root(app: &AppHandle) -> Result { + crate::authenticated_storage::account_storage_root(app, "module-runtime-v1") +} + +fn open_db(root: &Path) -> Result { + fs::create_dir_all(root) + .map_err(|error| format!("HOLOLAKE_MODULE_RUNTIME_ROOT_FAILED: {error}"))?; + let connection = Connection::open(root.join("module-runtime.sqlite3")) + .map_err(|error| format!("HOLOLAKE_MODULE_RUNTIME_DB_OPEN_FAILED: {error}"))?; + connection + .busy_timeout(std::time::Duration::from_secs(5)) + .map_err(|error| format!("HOLOLAKE_MODULE_RUNTIME_DB_TIMEOUT_FAILED: {error}"))?; + connection + .execute_batch( + "PRAGMA journal_mode=WAL; + PRAGMA foreign_keys=ON; + CREATE TABLE IF NOT EXISTS installed_modules( + module_number TEXT PRIMARY KEY, + display_name TEXT NOT NULL, + version TEXT NOT NULL, + adapter TEXT NOT NULL, + state TEXT NOT NULL, + package_sha256 TEXT NOT NULL, + previous_package_sha256 TEXT, + permissions_json TEXT NOT NULL, + manifest_json TEXT NOT NULL, + updated_at_unix_ms INTEGER NOT NULL + ); + CREATE TABLE IF NOT EXISTS module_receipts( + sequence INTEGER PRIMARY KEY AUTOINCREMENT, + receipt_id TEXT NOT NULL UNIQUE, + event TEXT NOT NULL, + module_number TEXT NOT NULL, + state TEXT NOT NULL, + package_sha256 TEXT NOT NULL, + previous_receipt_hash TEXT NOT NULL, + receipt_hash TEXT NOT NULL, + observed_at_unix_ms INTEGER NOT NULL + );", + ) + .map_err(|error| format!("HOLOLAKE_MODULE_RUNTIME_DB_SCHEMA_FAILED: {error}"))?; + Ok(connection) +} + +fn known_state(state: &str) -> bool { + matches!( + state, + "INSTALLED_DORMANT" + | "MOUNTED_PENDING_SELF_TEST" + | "ACTIVE" + | "DORMANT" + | "ROLLBACK_PENDING_SELF_TEST" + | "FAILED_CLOSED" + ) +} + +fn verify_receipt_chain(connection: &Connection) -> Result<(), String> { + let mut statement = connection + .prepare( + "SELECT receipt_id,event,module_number,state,package_sha256,previous_receipt_hash,receipt_hash,observed_at_unix_ms FROM module_receipts ORDER BY sequence", + ) + .map_err(|error| format!("HOLOLAKE_MODULE_RECEIPT_VERIFY_PREPARE_FAILED: {error}"))?; + let rows = statement + .query_map([], |row| { + Ok(( + row.get::<_, String>(0)?, + row.get::<_, String>(1)?, + row.get::<_, String>(2)?, + row.get::<_, String>(3)?, + row.get::<_, String>(4)?, + row.get::<_, String>(5)?, + row.get::<_, String>(6)?, + row.get::<_, u64>(7)?, + )) + }) + .map_err(|error| format!("HOLOLAKE_MODULE_RECEIPT_VERIFY_QUERY_FAILED: {error}"))?; + let mut previous = "0".repeat(64); + for row in rows { + let (receipt_id, event, module_number, state, package_sha256, linked, stored, observed) = + row.map_err(|error| format!("HOLOLAKE_MODULE_RECEIPT_VERIFY_ROW_FAILED: {error}"))?; + let expected = sha256_hex( + format!( + "HLP-MODULE-RECEIPT-v1\n{receipt_id}\n{event}\n{module_number}\n{state}\n{package_sha256}\n{linked}\n{observed}" + ) + .as_bytes(), + ); + if linked != previous + || stored != expected + || !is_sha256(&stored) + || !is_sha256(&package_sha256) + || !known_state(&state) + { + return Err("HOLOLAKE_MODULE_RECEIPT_CHAIN_INVALID".into()); + } + previous = stored; + } + Ok(()) +} + +fn verify_persisted_modules(connection: &Connection, root: &Path) -> Result<(), String> { + let mut statement = connection + .prepare( + "SELECT module_number,display_name,version,adapter,state,package_sha256,previous_package_sha256,permissions_json,manifest_json FROM installed_modules ORDER BY module_number", + ) + .map_err(|error| format!("HOLOLAKE_MODULE_PERSISTED_VERIFY_PREPARE_FAILED: {error}"))?; + let rows = statement + .query_map([], |row| { + Ok(( + row.get::<_, String>(0)?, + row.get::<_, String>(1)?, + row.get::<_, String>(2)?, + row.get::<_, String>(3)?, + row.get::<_, String>(4)?, + row.get::<_, String>(5)?, + row.get::<_, Option>(6)?, + row.get::<_, String>(7)?, + row.get::<_, String>(8)?, + )) + }) + .map_err(|error| format!("HOLOLAKE_MODULE_PERSISTED_VERIFY_QUERY_FAILED: {error}"))?; + for row in rows { + let ( + number, + display, + version, + adapter, + state, + digest, + previous, + permissions_raw, + manifest_raw, + ) = row.map_err(|error| format!("HOLOLAKE_MODULE_PERSISTED_VERIFY_ROW_FAILED: {error}"))?; + if !known_state(&state) || !is_sha256(&digest) { + return Err("HOLOLAKE_MODULE_PERSISTED_STATE_INVALID".into()); + } + let manifest: ModuleManifest = serde_json::from_str(&manifest_raw) + .map_err(|_| "HOLOLAKE_MODULE_PERSISTED_MANIFEST_INVALID".to_string())?; + let permissions: Vec = serde_json::from_str(&permissions_raw) + .map_err(|_| "HOLOLAKE_MODULE_PERSISTED_PERMISSIONS_INVALID".to_string())?; + let (_, package, verified_digest) = read_verified_package( + &package_file(root, &digest), + &signature_file(root, &digest), + RELEASE_TRUST_RAW, + )?; + if verified_digest != digest + || package.manifest != manifest + || manifest.module_number != number + || manifest.display_name != display + || manifest.version != version + || manifest.adapter != adapter + || manifest.permissions != permissions + { + return Err("HOLOLAKE_MODULE_PERSISTED_IDENTITY_INVALID".into()); + } + if let Some(previous) = previous { + if !is_sha256(&previous) { + return Err("HOLOLAKE_MODULE_PREVIOUS_PACKAGE_INVALID".into()); + } + let (_, previous_package, previous_digest) = read_verified_package( + &package_file(root, &previous), + &signature_file(root, &previous), + RELEASE_TRUST_RAW, + )?; + if previous_digest != previous || previous_package.manifest.module_number != number { + return Err("HOLOLAKE_MODULE_PREVIOUS_PACKAGE_IDENTITY_INVALID".into()); + } + } + } + Ok(()) +} + +fn package_file(root: &Path, digest: &str) -> PathBuf { + root.join("packages").join(format!("{digest}.ghmod")) +} + +fn signature_file(root: &Path, digest: &str) -> PathBuf { + root.join("packages").join(format!("{digest}.ghmod.sig")) +} + +fn copy_atomic(source: &Path, target: &Path) -> Result<(), String> { + let parent = target + .parent() + .ok_or("HOLOLAKE_MODULE_PACKAGE_TARGET_INVALID")?; + fs::create_dir_all(parent) + .map_err(|error| format!("HOLOLAKE_MODULE_PACKAGE_STORE_FAILED: {error}"))?; + if target.exists() { + return Ok(()); + } + let temporary = parent.join(format!(".install-{}", Uuid::new_v4())); + fs::copy(source, &temporary) + .map_err(|error| format!("HOLOLAKE_MODULE_PACKAGE_COPY_FAILED: {error}"))?; + fs::rename(&temporary, target) + .map_err(|error| format!("HOLOLAKE_MODULE_PACKAGE_COMMIT_FAILED: {error}")) +} + +fn previous_receipt_hash(transaction: &Transaction<'_>) -> Result { + transaction + .query_row( + "SELECT receipt_hash FROM module_receipts ORDER BY sequence DESC LIMIT 1", + [], + |row| row.get(0), + ) + .optional() + .map(|value| value.unwrap_or_else(|| "0".repeat(64))) + .map_err(|error| format!("HOLOLAKE_MODULE_RECEIPT_READ_FAILED: {error}")) +} + +fn append_receipt( + transaction: &Transaction<'_>, + event: &str, + module_number: &str, + state: &str, + package_sha256: &str, + observed_at_unix_ms: u64, +) -> Result { + let receipt_id = format!("HLP-MOD-RCPT-{}", Uuid::new_v4()); + let previous = previous_receipt_hash(transaction)?; + let receipt_hash = sha256_hex( + format!( + "HLP-MODULE-RECEIPT-v1\n{receipt_id}\n{event}\n{module_number}\n{state}\n{package_sha256}\n{previous}\n{observed_at_unix_ms}" + ) + .as_bytes(), + ); + transaction + .execute( + "INSERT INTO module_receipts(receipt_id,event,module_number,state,package_sha256,previous_receipt_hash,receipt_hash,observed_at_unix_ms) + VALUES(?1,?2,?3,?4,?5,?6,?7,?8)", + params![receipt_id, event, module_number, state, package_sha256, previous, receipt_hash, observed_at_unix_ms], + ) + .map_err(|error| format!("HOLOLAKE_MODULE_RECEIPT_WRITE_FAILED: {error}"))?; + Ok(ModuleMutationReceipt { + receipt_id, + event: event.into(), + module_number: module_number.into(), + state: state.into(), + package_sha256: package_sha256.into(), + previous_receipt_hash: previous, + receipt_hash, + observed_at_unix_ms, + user_data_preserved: true, + }) +} + +fn permissions_from_json(raw: &str) -> BTreeSet { + serde_json::from_str::>(raw) + .unwrap_or_default() + .into_iter() + .collect() +} + +fn materialize_bundled_source( + root: &Path, + module_number: &str, + package: &[u8], + signature: &str, +) -> Result<(PathBuf, PathBuf), String> { + let source_root = root.join("bundled-sources"); + fs::create_dir_all(&source_root) + .map_err(|error| format!("HOLOLAKE_BUNDLED_MODULE_SOURCE_ROOT_FAILED: {error}"))?; + let digest = sha256_hex(package); + let package_path = source_root.join(format!("{module_number}-{digest}.ghmod")); + let signature_path = source_root.join(format!("{module_number}-{digest}.ghmod.sig")); + for (path, bytes) in [ + (&package_path, package), + (&signature_path, signature.as_bytes()), + ] { + if path.exists() { + let current = fs::read(path) + .map_err(|error| format!("HOLOLAKE_BUNDLED_MODULE_SOURCE_READ_FAILED: {error}"))?; + if current != bytes { + return Err("HOLOLAKE_BUNDLED_MODULE_SOURCE_TAMPERED".into()); + } + continue; + } + let temporary = source_root.join(format!(".bundled-{}", Uuid::new_v4())); + fs::write(&temporary, bytes) + .map_err(|error| format!("HOLOLAKE_BUNDLED_MODULE_SOURCE_WRITE_FAILED: {error}"))?; + fs::rename(&temporary, path) + .map_err(|error| format!("HOLOLAKE_BUNDLED_MODULE_SOURCE_COMMIT_FAILED: {error}"))?; + } + Ok((package_path, signature_path)) +} + +pub async fn get_bundled_module_catalog( + app: AppHandle, +) -> Result, String> { + let root = runtime_root(&app)?; + let snapshot = snapshot_at(&root)?; + BUNDLED_MODULES + .iter() + .map(|source| { + let (_, package, package_sha256) = + read_verified_package_bytes(source.package, source.signature, RELEASE_TRUST_RAW)?; + if package.manifest.module_number != source.module_number { + return Err("HOLOLAKE_BUNDLED_MODULE_IDENTITY_INVALID".into()); + } + let installed_state = snapshot + .modules + .iter() + .find(|module| module.module_number == source.module_number) + .map(|module| module.state.clone()) + .unwrap_or_else(|| "NOT_INSTALLED".into()); + Ok(BundledModuleDescriptor { + module_number: package.manifest.module_number, + display_name: package.manifest.display_name, + version: package.manifest.version, + adapter: package.manifest.adapter, + permissions: package.manifest.permissions, + package_sha256, + installed_state, + signature_verified: true, + }) + }) + .collect() +} + +pub async fn activate_bundled_module( + app: AppHandle, + input: ActivateBundledModuleInput, +) -> Result { + let root = runtime_root(&app)?; + let (bytes, signature) = bundled_package(&input.module_number)?; + let (_, package, package_sha256) = + read_verified_package_bytes(bytes, signature, RELEASE_TRUST_RAW)?; + if package.manifest.module_number != input.module_number { + return Err("HOLOLAKE_BUNDLED_MODULE_IDENTITY_INVALID".into()); + } + let mut receipts = Vec::new(); + let before = snapshot_at(&root)?; + let installed = before + .modules + .iter() + .find(|module| module.module_number == input.module_number); + if installed + .is_some_and(|module| module.state == "ACTIVE" && module.package_sha256 == package_sha256) + { + return Ok(BundledModuleActivation { + schema: "hololake.bundled-module-activation/v1", + state: "ALREADY_ACTIVE".into(), + module_number: input.module_number, + receipts, + snapshot: before, + }); + } + let needs_install = installed + .map(|module| module.package_sha256 != package_sha256 || module.state == "FAILED_CLOSED") + .unwrap_or(true); + if needs_install { + let (package_path, signature_path) = + materialize_bundled_source(&root, &input.module_number, bytes, signature)?; + receipts.push(install_module_package_at( + &root, + &InstallModulePackageInput { + package_path: package_path.to_string_lossy().into_owned(), + signature_path: signature_path.to_string_lossy().into_owned(), + expected_package_sha256: package_sha256, + human_confirmed_permission_expansion: input.human_confirmed_permission_expansion, + }, + RELEASE_TRUST_RAW, + now_unix_ms(), + )?); + } + let current = snapshot_at(&root)? + .modules + .into_iter() + .find(|module| module.module_number == input.module_number) + .ok_or("HOLOLAKE_BUNDLED_MODULE_ACTIVATION_STATE_MISSING")?; + match current.state.as_str() { + "INSTALLED_DORMANT" | "DORMANT" => { + receipts.push(transition( + &root, + &input.module_number, + &["INSTALLED_DORMANT", "DORMANT"], + "MOUNTED_PENDING_SELF_TEST", + "MOUNT", + )?); + receipts.push(self_test_module_at( + &root, + &input.module_number, + now_unix_ms(), + )?); + } + "MOUNTED_PENDING_SELF_TEST" | "ROLLBACK_PENDING_SELF_TEST" => { + receipts.push(self_test_module_at( + &root, + &input.module_number, + now_unix_ms(), + )?); + } + "ACTIVE" => {} + _ => return Err("HOLOLAKE_BUNDLED_MODULE_ACTIVATION_STATE_INVALID".into()), + } + let snapshot = snapshot_at(&root)?; + if !snapshot.modules.iter().any(|module| { + module.module_number == input.module_number + && module.adapter == package.manifest.adapter + && module.state == "ACTIVE" + }) { + return Err("HOLOLAKE_BUNDLED_MODULE_ACTIVATION_NOT_ACTIVE".into()); + } + Ok(BundledModuleActivation { + schema: "hololake.bundled-module-activation/v1", + state: "ACTIVE".into(), + module_number: input.module_number, + receipts, + snapshot, + }) +} + +pub(crate) fn require_active_module_adapter( + app: &AppHandle, + module_number: &str, + adapter: &str, +) -> Result<(), String> { + let snapshot = snapshot_at(&runtime_root(app)?)?; + if snapshot.modules.iter().any(|module| { + module.module_number == module_number + && module.adapter == adapter + && module.state == "ACTIVE" + }) { + Ok(()) + } else { + Err("HOLOLAKE_MODULE_NOT_ACTIVE".into()) + } +} + +pub async fn verify_module_package( + app: AppHandle, + input: VerifyModulePackageInput, +) -> Result { + let _ = runtime_root(&app)?; + let (_, package, package_sha256) = read_verified_package( + Path::new(&input.package_path), + Path::new(&input.signature_path), + RELEASE_TRUST_RAW, + )?; + Ok(VerifiedModulePackage { + state: "VERIFIED_NOT_INSTALLED".into(), + package_sha256, + module_number: package.manifest.module_number, + display_name: package.manifest.display_name, + version: package.manifest.version, + adapter: package.manifest.adapter, + permissions: package.manifest.permissions, + signature_verified: true, + package_code_executed: false, + }) +} + +pub async fn install_module_package( + app: AppHandle, + input: InstallModulePackageInput, +) -> Result { + install_module_package_at( + &runtime_root(&app)?, + &input, + RELEASE_TRUST_RAW, + now_unix_ms(), + ) +} + +fn install_module_package_at( + root: &Path, + input: &InstallModulePackageInput, + trust_raw: &str, + observed: u64, +) -> Result { + if !is_sha256(&input.expected_package_sha256) { + return Err("HOLOLAKE_MODULE_EXPECTED_DIGEST_INVALID".into()); + } + let (bytes, package, digest) = read_verified_package( + Path::new(&input.package_path), + Path::new(&input.signature_path), + trust_raw, + )?; + if digest != input.expected_package_sha256 { + return Err("HOLOLAKE_MODULE_EXPECTED_DIGEST_MISMATCH".into()); + } + let mut connection = open_db(root)?; + let existing: Option<(String, String)> = connection + .query_row( + "SELECT package_sha256, permissions_json FROM installed_modules WHERE module_number=?1", + params![package.manifest.module_number], + |row| Ok((row.get(0)?, row.get(1)?)), + ) + .optional() + .map_err(|error| format!("HOLOLAKE_MODULE_EXISTING_READ_FAILED: {error}"))?; + let requested = package + .manifest + .permissions + .iter() + .cloned() + .collect::>(); + let expansion = existing + .as_ref() + .map(|(_, raw)| !requested.is_subset(&permissions_from_json(raw))) + .unwrap_or(!requested.is_empty()); + if expansion && !input.human_confirmed_permission_expansion { + return Err("HOLOLAKE_MODULE_PERMISSION_EXPANSION_REQUIRES_HUMAN_CONFIRMATION".into()); + } + copy_atomic(Path::new(&input.package_path), &package_file(root, &digest))?; + copy_atomic( + Path::new(&input.signature_path), + &signature_file(root, &digest), + )?; + if fs::read(package_file(root, &digest)) + .map_err(|error| format!("HOLOLAKE_MODULE_STORED_PACKAGE_READ_FAILED: {error}"))? + != bytes + { + return Err("HOLOLAKE_MODULE_STORED_PACKAGE_MISMATCH".into()); + } + if fs::read(signature_file(root, &digest)) + .map_err(|error| format!("HOLOLAKE_MODULE_STORED_SIGNATURE_READ_FAILED: {error}"))? + != fs::read(&input.signature_path) + .map_err(|error| format!("HOLOLAKE_MODULE_SOURCE_SIGNATURE_READ_FAILED: {error}"))? + { + return Err("HOLOLAKE_MODULE_STORED_SIGNATURE_MISMATCH".into()); + } + fs::create_dir_all(root.join("user-data").join(&package.manifest.module_number)) + .map_err(|error| format!("HOLOLAKE_MODULE_USER_DATA_ROOT_FAILED: {error}"))?; + let transaction = connection + .transaction() + .map_err(|error| format!("HOLOLAKE_MODULE_INSTALL_TRANSACTION_FAILED: {error}"))?; + let permissions_json = serde_json::to_string(&package.manifest.permissions) + .map_err(|error| format!("HOLOLAKE_MODULE_PERMISSIONS_INVALID: {error}"))?; + let manifest_json = serde_json::to_string(&package.manifest) + .map_err(|error| format!("HOLOLAKE_MODULE_MANIFEST_INVALID: {error}"))?; + let previous = existing.as_ref().map(|(value, _)| value.as_str()); + transaction + .execute( + "INSERT INTO installed_modules(module_number,display_name,version,adapter,state,package_sha256,previous_package_sha256,permissions_json,manifest_json,updated_at_unix_ms) + VALUES(?1,?2,?3,?4,'INSTALLED_DORMANT',?5,?6,?7,?8,?9) + ON CONFLICT(module_number) DO UPDATE SET display_name=excluded.display_name,version=excluded.version,adapter=excluded.adapter,state='INSTALLED_DORMANT',package_sha256=excluded.package_sha256,previous_package_sha256=installed_modules.package_sha256,permissions_json=excluded.permissions_json,manifest_json=excluded.manifest_json,updated_at_unix_ms=excluded.updated_at_unix_ms", + params![package.manifest.module_number, package.manifest.display_name, package.manifest.version, package.manifest.adapter, digest, previous, permissions_json, manifest_json, observed], + ) + .map_err(|error| format!("HOLOLAKE_MODULE_INSTALL_WRITE_FAILED: {error}"))?; + let receipt = append_receipt( + &transaction, + "INSTALL", + &package.manifest.module_number, + "INSTALLED_DORMANT", + &digest, + observed, + )?; + transaction + .commit() + .map_err(|error| format!("HOLOLAKE_MODULE_INSTALL_COMMIT_FAILED: {error}"))?; + Ok(receipt) +} + +fn transition( + root: &Path, + module_number: &str, + allowed_from: &[&str], + target: &str, + event: &str, +) -> Result { + let mut connection = open_db(root)?; + let row: (String, String) = connection + .query_row( + "SELECT state,package_sha256 FROM installed_modules WHERE module_number=?1", + params![module_number], + |row| Ok((row.get(0)?, row.get(1)?)), + ) + .optional() + .map_err(|error| format!("HOLOLAKE_MODULE_STATE_READ_FAILED: {error}"))? + .ok_or("HOLOLAKE_MODULE_NOT_INSTALLED")?; + if !allowed_from.contains(&row.0.as_str()) { + return Err("HOLOLAKE_MODULE_LIFECYCLE_TRANSITION_INVALID".into()); + } + let observed = now_unix_ms(); + let transaction = connection + .transaction() + .map_err(|error| format!("HOLOLAKE_MODULE_TRANSITION_BEGIN_FAILED: {error}"))?; + transaction + .execute( + "UPDATE installed_modules SET state=?1,updated_at_unix_ms=?2 WHERE module_number=?3 AND state=?4", + params![target, observed, module_number, row.0], + ) + .map_err(|error| format!("HOLOLAKE_MODULE_TRANSITION_WRITE_FAILED: {error}"))?; + let receipt = append_receipt(&transaction, event, module_number, target, &row.1, observed)?; + transaction + .commit() + .map_err(|error| format!("HOLOLAKE_MODULE_TRANSITION_COMMIT_FAILED: {error}"))?; + Ok(receipt) +} + +pub async fn mount_module( + app: AppHandle, + input: ModuleNumberInput, +) -> Result { + transition( + &runtime_root(&app)?, + &input.module_number, + &["INSTALLED_DORMANT", "DORMANT"], + "MOUNTED_PENDING_SELF_TEST", + "MOUNT", + ) +} + +pub async fn self_test_module( + app: AppHandle, + input: ModuleNumberInput, +) -> Result { + self_test_module_at(&runtime_root(&app)?, &input.module_number, now_unix_ms()) +} + +fn self_test_module_at( + root: &Path, + module_number: &str, + observed: u64, +) -> Result { + let mut connection = open_db(root)?; + let row: (String, String, Option) = connection + .query_row( + "SELECT state,package_sha256,previous_package_sha256 FROM installed_modules WHERE module_number=?1", + params![module_number], + |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)), + ) + .optional() + .map_err(|error| format!("HOLOLAKE_MODULE_SELF_TEST_STATE_FAILED: {error}"))? + .ok_or("HOLOLAKE_MODULE_NOT_INSTALLED")?; + if !matches!( + row.0.as_str(), + "MOUNTED_PENDING_SELF_TEST" | "ROLLBACK_PENDING_SELF_TEST" + ) { + return Err("HOLOLAKE_MODULE_SELF_TEST_STATE_INVALID".into()); + } + let verification = read_verified_package( + &package_file(root, &row.1), + &signature_file(root, &row.1), + RELEASE_TRUST_RAW, + ); + let success = verification.as_ref().is_ok_and(|(_, package, digest)| { + digest == &row.1 && package.manifest.module_number == module_number + }); + let rollback_package = row.2.as_ref().and_then(|previous| { + read_verified_package( + &package_file(root, previous), + &signature_file(root, previous), + RELEASE_TRUST_RAW, + ) + .ok() + .filter(|(_, package, digest)| { + digest == previous && package.manifest.module_number == module_number + }) + }); + let transaction = connection + .transaction() + .map_err(|error| format!("HOLOLAKE_MODULE_SELF_TEST_BEGIN_FAILED: {error}"))?; + if success { + transaction + .execute( + "UPDATE installed_modules SET state='ACTIVE',updated_at_unix_ms=?1 WHERE module_number=?2", + params![observed, module_number], + ) + .map_err(|error| format!("HOLOLAKE_MODULE_SELF_TEST_WRITE_FAILED: {error}"))?; + let receipt = append_receipt( + &transaction, + "SELF_TEST_PASS", + module_number, + "ACTIVE", + &row.1, + observed, + )?; + transaction + .commit() + .map_err(|error| format!("HOLOLAKE_MODULE_SELF_TEST_COMMIT_FAILED: {error}"))?; + return Ok(receipt); + } + let (target_state, target_digest) = if let Some((_, previous_package, previous)) = + rollback_package + { + let permissions_json = serde_json::to_string(&previous_package.manifest.permissions) + .map_err(|error| format!("HOLOLAKE_MODULE_ROLLBACK_PERMISSIONS_INVALID: {error}"))?; + let manifest_json = serde_json::to_string(&previous_package.manifest) + .map_err(|error| format!("HOLOLAKE_MODULE_ROLLBACK_MANIFEST_INVALID: {error}"))?; + transaction + .execute( + "UPDATE installed_modules SET display_name=?1,version=?2,adapter=?3,state='ROLLBACK_PENDING_SELF_TEST',package_sha256=?4,previous_package_sha256=NULL,permissions_json=?5,manifest_json=?6,updated_at_unix_ms=?7 WHERE module_number=?8", + params![previous_package.manifest.display_name, previous_package.manifest.version, previous_package.manifest.adapter, previous, permissions_json, manifest_json, observed, module_number], + ) + .map_err(|error| format!("HOLOLAKE_MODULE_AUTO_ROLLBACK_WRITE_FAILED: {error}"))?; + ("ROLLBACK_PENDING_SELF_TEST", previous) + } else { + transaction + .execute( + "UPDATE installed_modules SET state='FAILED_CLOSED',updated_at_unix_ms=?1 WHERE module_number=?2", + params![observed, module_number], + ) + .map_err(|error| format!("HOLOLAKE_MODULE_FAIL_CLOSED_WRITE_FAILED: {error}"))?; + ("FAILED_CLOSED", row.1) + }; + let _ = append_receipt( + &transaction, + "SELF_TEST_FAIL", + module_number, + target_state, + &target_digest, + observed, + )?; + transaction + .commit() + .map_err(|error| format!("HOLOLAKE_MODULE_SELF_TEST_FAILURE_COMMIT_FAILED: {error}"))?; + Err(verification + .err() + .unwrap_or_else(|| "HOLOLAKE_MODULE_SELF_TEST_IDENTITY_MISMATCH".into())) +} + +pub async fn unmount_module( + app: AppHandle, + input: ModuleNumberInput, +) -> Result { + if input.module_number == MOBILE_SYNC_NUMBER { + crate::mobile_sync::stop_for_unmount(&app)?; + } + transition( + &runtime_root(&app)?, + &input.module_number, + &["ACTIVE", "MOUNTED_PENDING_SELF_TEST", "FAILED_CLOSED"], + "DORMANT", + "UNMOUNT", + ) +} + +pub async fn rollback_module( + app: AppHandle, + input: ModuleNumberInput, +) -> Result { + let root = runtime_root(&app)?; + let mut connection = open_db(&root)?; + let row: (String, Option) = connection + .query_row( + "SELECT package_sha256,previous_package_sha256 FROM installed_modules WHERE module_number=?1", + params![input.module_number], + |row| Ok((row.get(0)?, row.get(1)?)), + ) + .optional() + .map_err(|error| format!("HOLOLAKE_MODULE_ROLLBACK_READ_FAILED: {error}"))? + .ok_or("HOLOLAKE_MODULE_NOT_INSTALLED")?; + let previous = row.1.ok_or("HOLOLAKE_MODULE_ROLLBACK_NOT_AVAILABLE")?; + let (_, previous_package, verified_previous) = read_verified_package( + &package_file(&root, &previous), + &signature_file(&root, &previous), + RELEASE_TRUST_RAW, + )?; + if verified_previous != previous + || previous_package.manifest.module_number != input.module_number + { + return Err("HOLOLAKE_MODULE_ROLLBACK_PACKAGE_IDENTITY_INVALID".into()); + } + let permissions_json = serde_json::to_string(&previous_package.manifest.permissions) + .map_err(|error| format!("HOLOLAKE_MODULE_ROLLBACK_PERMISSIONS_INVALID: {error}"))?; + let manifest_json = serde_json::to_string(&previous_package.manifest) + .map_err(|error| format!("HOLOLAKE_MODULE_ROLLBACK_MANIFEST_INVALID: {error}"))?; + let observed = now_unix_ms(); + let transaction = connection + .transaction() + .map_err(|error| format!("HOLOLAKE_MODULE_ROLLBACK_BEGIN_FAILED: {error}"))?; + transaction + .execute( + "UPDATE installed_modules SET display_name=?1,version=?2,adapter=?3,state='ROLLBACK_PENDING_SELF_TEST',package_sha256=?4,previous_package_sha256=?5,permissions_json=?6,manifest_json=?7,updated_at_unix_ms=?8 WHERE module_number=?9", + params![previous_package.manifest.display_name, previous_package.manifest.version, previous_package.manifest.adapter, previous, row.0, permissions_json, manifest_json, observed, input.module_number], + ) + .map_err(|error| format!("HOLOLAKE_MODULE_ROLLBACK_WRITE_FAILED: {error}"))?; + let receipt = append_receipt( + &transaction, + "ROLLBACK", + &input.module_number, + "ROLLBACK_PENDING_SELF_TEST", + &previous, + observed, + )?; + transaction + .commit() + .map_err(|error| format!("HOLOLAKE_MODULE_ROLLBACK_COMMIT_FAILED: {error}"))?; + Ok(receipt) +} + +pub async fn get_module_runtime_snapshot(app: AppHandle) -> Result { + let root = runtime_root(&app)?; + snapshot_at(&root) +} + +fn snapshot_at(root: &Path) -> Result { + let connection = open_db(root)?; + verify_receipt_chain(&connection)?; + verify_persisted_modules(&connection, root)?; + let mut statement = connection + .prepare( + "SELECT module_number,display_name,version,adapter,state,package_sha256,previous_package_sha256,permissions_json,updated_at_unix_ms FROM installed_modules ORDER BY module_number", + ) + .map_err(|error| format!("HOLOLAKE_MODULE_SNAPSHOT_PREPARE_FAILED: {error}"))?; + let modules = statement + .query_map([], |row| { + let permissions_json: String = row.get(7)?; + Ok(InstalledModule { + module_number: row.get(0)?, + display_name: row.get(1)?, + version: row.get(2)?, + adapter: row.get(3)?, + state: row.get(4)?, + package_sha256: row.get(5)?, + previous_package_sha256: row.get(6)?, + permissions: serde_json::from_str(&permissions_json).unwrap_or_default(), + user_data_preserved: true, + updated_at_unix_ms: row.get(8)?, + }) + }) + .map_err(|error| format!("HOLOLAKE_MODULE_SNAPSHOT_QUERY_FAILED: {error}"))? + .collect::, _>>() + .map_err(|error| format!("HOLOLAKE_MODULE_SNAPSHOT_ROW_FAILED: {error}"))?; + let receipt_count: u64 = connection + .query_row("SELECT COUNT(*) FROM module_receipts", [], |row| row.get(0)) + .map_err(|error| format!("HOLOLAKE_MODULE_RECEIPT_COUNT_FAILED: {error}"))?; + let latest_receipt_hash = connection + .query_row( + "SELECT receipt_hash FROM module_receipts ORDER BY sequence DESC LIMIT 1", + [], + |row| row.get(0), + ) + .optional() + .map_err(|error| format!("HOLOLAKE_MODULE_RECEIPT_TAIL_FAILED: {error}"))? + .unwrap_or_else(|| "0".repeat(64)); + Ok(ModuleRuntimeSnapshot { + schema: "hololake.module-runtime-snapshot/v1", + state: "ACTIVE_SIGNED_DECLARATIVE_PACKAGES_ONLY", + host_version: HOST_VERSION, + package_code_executed: false, + modules, + receipt_count, + latest_receipt_hash, + }) +} + +pub(crate) fn start_on_application_open(app: &AppHandle) -> Result<(), String> { + let _ = contract()?; + let root = runtime_root(app); + match root { + Ok(root) => { + let _ = snapshot_at(&root)?; + Ok(()) + } + Err(error) if error == "HOLOLAKE_AUTHENTICATED_ACCOUNT_REQUIRED" => Ok(()), + Err(error) => Err(error), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn signed_fixture() -> (PathBuf, PathBuf, String) { + let package = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../fixtures/module-packages/HLP-MOD-LOCAL-RUNTIME-ACCEPTANCE-0001-0.1.0.ghmod"); + let signature = package.with_extension("ghmod.sig"); + let digest = sha256_hex(&fs::read(&package).unwrap()); + (package, signature, digest) + } + + fn install_fixture(root: &Path, observed: u64) -> String { + let (package, signature, digest) = signed_fixture(); + install_module_package_at( + root, + &InstallModulePackageInput { + package_path: package.to_string_lossy().into_owned(), + signature_path: signature.to_string_lossy().into_owned(), + expected_package_sha256: digest.clone(), + human_confirmed_permission_expansion: true, + }, + RELEASE_TRUST_RAW, + observed, + ) + .unwrap(); + digest + } + + #[test] + fn contract_keeps_packages_declarative_and_lifecycle_closed() { + let contract = contract().unwrap(); + assert_eq!(contract.registered_adapters.len(), 7); + assert!(!contract.host.arbitrary_native_code); + assert!(!contract.host.arbitrary_webview_javascript); + assert!(contract.host.declarative_payload_only); + } + + #[test] + fn package_number_classes_and_content_digest_are_exact() { + let payload = serde_json::json!({"views":[],"entry":"home"}); + let digest = payload_digest(&payload).unwrap(); + let package = ModulePackage { + schema: PACKAGE_SCHEMA.into(), + manifest: ModuleManifest { + module_number: "HLP-MOD-LOCAL-0001".into(), + registration_class: "PRIVATE_CHANNEL_LOCAL".into(), + display_name: "Local module".into(), + version: "1.0.0".into(), + minimum_host_version: "0.5.0".into(), + adapter: "native-composition-v1".into(), + content_digest: digest.clone(), + permissions: vec!["KNOWLEDGE_READ".into()], + user_data_schema: "hololake.module-data/local/v1".into(), + self_test: ModuleSelfTest { + kind: "DECLARATIVE_SCHEMA_V1".into(), + expected_content_digest: digest, + }, + }, + payload, + }; + let runtime_contract = contract().unwrap(); + validate_package(&package, &runtime_contract).unwrap(); + let mut invalid = package; + invalid.manifest.module_number = "HLP-DONOR-CAND-0001".into(); + assert_eq!( + validate_package(&invalid, &runtime_contract).unwrap_err(), + "HOLOLAKE_MODULE_PACKAGE_CONTRACT_INVALID" + ); + } + + #[test] + fn tampered_persisted_manifest_fails_closed_without_touching_user_data() { + let temporary = tempfile::tempdir().unwrap(); + let root = temporary.path(); + let module_number = "HLP-MOD-LOCAL-RUNTIME-ACCEPTANCE-0001"; + install_fixture(root, 1); + fs::write( + root.join("user-data") + .join(module_number) + .join("human-data.json"), + b"preserve", + ) + .unwrap(); + let connection = open_db(root).unwrap(); + connection + .execute( + "UPDATE installed_modules SET manifest_json='{}' WHERE module_number=?1", + params![module_number], + ) + .unwrap(); + drop(connection); + assert!(snapshot_at(root) + .unwrap_err() + .starts_with("HOLOLAKE_MODULE_PERSISTED_MANIFEST_INVALID")); + assert_eq!( + fs::read( + root.join("user-data") + .join(module_number) + .join("human-data.json") + ) + .unwrap(), + b"preserve" + ); + } + + #[test] + fn tampered_receipt_chain_fails_closed_on_restart() { + let temporary = tempfile::tempdir().unwrap(); + install_fixture(temporary.path(), 1); + let connection = open_db(temporary.path()).unwrap(); + connection + .execute( + "UPDATE module_receipts SET receipt_hash=?1 WHERE sequence=1", + params!["f".repeat(64)], + ) + .unwrap(); + drop(connection); + assert_eq!( + snapshot_at(temporary.path()).unwrap_err(), + "HOLOLAKE_MODULE_RECEIPT_CHAIN_INVALID" + ); + } + + #[test] + fn real_product_signed_fixture_verifies_without_executing_package_code() { + let (fixture, signature, _) = signed_fixture(); + let (_, package, package_sha256) = + read_verified_package(&fixture, &signature, RELEASE_TRUST_RAW).unwrap(); + assert_eq!( + package.manifest.module_number, + "HLP-MOD-LOCAL-RUNTIME-ACCEPTANCE-0001" + ); + assert!(is_sha256(&package_sha256)); + } + + #[test] + fn signed_package_install_mount_self_test_restart_and_unmount_are_receipted() { + let (fixture, signature, expected) = signed_fixture(); + let temporary = tempfile::tempdir().unwrap(); + let module_number = "HLP-MOD-LOCAL-RUNTIME-ACCEPTANCE-0001"; + let mut input = InstallModulePackageInput { + package_path: fixture.to_string_lossy().into_owned(), + signature_path: signature.to_string_lossy().into_owned(), + expected_package_sha256: expected, + human_confirmed_permission_expansion: false, + }; + assert_eq!( + install_module_package_at(temporary.path(), &input, RELEASE_TRUST_RAW, 100) + .unwrap_err(), + "HOLOLAKE_MODULE_PERMISSION_EXPANSION_REQUIRES_HUMAN_CONFIRMATION" + ); + input.human_confirmed_permission_expansion = true; + install_module_package_at(temporary.path(), &input, RELEASE_TRUST_RAW, 101).unwrap(); + transition( + temporary.path(), + module_number, + &["INSTALLED_DORMANT"], + "MOUNTED_PENDING_SELF_TEST", + "MOUNT", + ) + .unwrap(); + self_test_module_at(temporary.path(), module_number, 103).unwrap(); + let user_data = temporary + .path() + .join("user-data") + .join(module_number) + .join("human-data.json"); + fs::write(&user_data, b"preserve-after-unmount").unwrap(); + transition( + temporary.path(), + module_number, + &["ACTIVE"], + "DORMANT", + "UNMOUNT", + ) + .unwrap(); + let reopened = snapshot_at(temporary.path()).unwrap(); + assert_eq!(reopened.modules.len(), 1); + assert_eq!(reopened.modules[0].state, "DORMANT"); + assert_eq!(reopened.receipt_count, 4); + assert_eq!(fs::read(user_data).unwrap(), b"preserve-after-unmount"); + } + + #[test] + fn bundled_native_composition_package_completes_installed_lifecycle() { + let temporary = tempfile::tempdir().unwrap(); + let (_, package, digest) = read_verified_package_bytes( + NATIVE_COMPOSITION_PACKAGE, + NATIVE_COMPOSITION_SIGNATURE, + RELEASE_TRUST_RAW, + ) + .unwrap(); + assert_eq!(package.manifest.module_number, NATIVE_COMPOSITION_NUMBER); + assert_eq!(package.manifest.adapter, "native-composition-v1"); + let (package_path, signature_path) = materialize_bundled_source( + temporary.path(), + NATIVE_COMPOSITION_NUMBER, + NATIVE_COMPOSITION_PACKAGE, + NATIVE_COMPOSITION_SIGNATURE, + ) + .unwrap(); + install_module_package_at( + temporary.path(), + &InstallModulePackageInput { + package_path: package_path.to_string_lossy().into_owned(), + signature_path: signature_path.to_string_lossy().into_owned(), + expected_package_sha256: digest, + human_confirmed_permission_expansion: true, + }, + RELEASE_TRUST_RAW, + 200, + ) + .unwrap(); + transition( + temporary.path(), + NATIVE_COMPOSITION_NUMBER, + &["INSTALLED_DORMANT"], + "MOUNTED_PENDING_SELF_TEST", + "MOUNT", + ) + .unwrap(); + self_test_module_at(temporary.path(), NATIVE_COMPOSITION_NUMBER, 202).unwrap(); + let snapshot = snapshot_at(temporary.path()).unwrap(); + assert!(snapshot.modules.iter().any(|module| { + module.module_number == NATIVE_COMPOSITION_NUMBER + && module.adapter == "native-composition-v1" + && module.state == "ACTIVE" + })); + assert_eq!(snapshot.receipt_count, 3); + } + + #[test] + fn bundled_channel_workbench_has_an_independent_verified_identity() { + let (_, package, digest) = read_verified_package_bytes( + CHANNEL_WORKBENCH_PACKAGE, + CHANNEL_WORKBENCH_SIGNATURE, + RELEASE_TRUST_RAW, + ) + .unwrap(); + assert_eq!(package.manifest.module_number, CHANNEL_WORKBENCH_NUMBER); + assert_eq!(package.manifest.adapter, "channel-workbench-v1"); + assert_eq!(package.manifest.permissions.len(), 4); + assert!(is_sha256(&digest)); + assert_eq!(BUNDLED_MODULES.len(), 11); + } + + #[test] + fn bundled_persona_channel_body_is_signed_without_claiming_persona_binding() { + let (_, package, digest) = read_verified_package_bytes( + PERSONA_CHANNEL_BODY_PACKAGE, + PERSONA_CHANNEL_BODY_SIGNATURE, + RELEASE_TRUST_RAW, + ) + .unwrap(); + assert_eq!(package.manifest.module_number, PERSONA_CHANNEL_BODY_NUMBER); + assert_eq!(package.manifest.adapter, "persona-channel-body-v1"); + assert_eq!(package.manifest.permissions.len(), 7); + assert_eq!( + package.payload["adapterConfig"]["personaBindingClaimed"], + false + ); + assert!(is_sha256(&digest)); + } + + #[test] + fn bundled_education_workbench_is_an_official_signed_module() { + let (_, package, digest) = read_verified_package_bytes( + EDUCATION_WORKBENCH_PACKAGE, + EDUCATION_WORKBENCH_SIGNATURE, + RELEASE_TRUST_RAW, + ) + .unwrap(); + assert_eq!(package.manifest.module_number, EDUCATION_WORKBENCH_NUMBER); + assert_eq!(package.manifest.registration_class, "OFFICIAL_LIGHTHOUSE"); + assert_eq!(package.manifest.adapter, "education-workbench-v1"); + assert_eq!(package.manifest.permissions.len(), 8); + assert_eq!( + package.payload["adapterConfig"]["importDefaultScope"], + "UNASSIGNED" + ); + assert!(is_sha256(&digest)); + } + + #[test] + fn bundled_web_novel_family_uses_one_signed_lifecycle_and_exact_numbers() { + let expected = [ + ( + WEB_NOVEL_WORKBENCH_NUMBER, + WEB_NOVEL_WORKBENCH_PACKAGE, + WEB_NOVEL_WORKBENCH_SIGNATURE, + 8usize, + ), + ( + WEB_NOVEL_OUTLINE_NUMBER, + WEB_NOVEL_OUTLINE_PACKAGE, + WEB_NOVEL_OUTLINE_SIGNATURE, + 2usize, + ), + ( + WEB_NOVEL_GRID_NUMBER, + WEB_NOVEL_GRID_PACKAGE, + WEB_NOVEL_GRID_SIGNATURE, + 2usize, + ), + ( + WEB_NOVEL_STORYWORLD_NUMBER, + WEB_NOVEL_STORYWORLD_PACKAGE, + WEB_NOVEL_STORYWORLD_SIGNATURE, + 2usize, + ), + ( + WEB_NOVEL_DELIVERY_NUMBER, + WEB_NOVEL_DELIVERY_PACKAGE, + WEB_NOVEL_DELIVERY_SIGNATURE, + 2usize, + ), + ]; + for (number, bytes, signature, permission_count) in expected { + let (_, package, digest) = + read_verified_package_bytes(bytes, signature, RELEASE_TRUST_RAW).unwrap(); + assert_eq!(package.manifest.module_number, number); + assert_eq!(package.manifest.registration_class, "OFFICIAL_LIGHTHOUSE"); + assert_eq!(package.manifest.adapter, "web-novel-workbench-v1"); + assert_eq!(package.manifest.permissions.len(), permission_count); + assert!(is_sha256(&digest)); + } + } + + #[test] + fn bundled_mobile_sync_is_signed_and_keeps_the_desktop_as_root_node() { + let (_, package, digest) = read_verified_package_bytes( + MOBILE_SYNC_PACKAGE, + MOBILE_SYNC_SIGNATURE, + RELEASE_TRUST_RAW, + ) + .unwrap(); + assert_eq!(package.manifest.module_number, MOBILE_SYNC_NUMBER); + assert_eq!(package.manifest.registration_class, "OFFICIAL_LIGHTHOUSE"); + assert_eq!(package.manifest.adapter, "mobile-sync-v1"); + assert_eq!(package.manifest.permissions.len(), 5); + assert_eq!( + package.payload["adapterConfig"]["desktopRole"], + "USER_LOCAL_COMPUTER_TERMINAL_ROOT_NODE" + ); + assert_eq!( + package.payload["adapterConfig"]["iosClientPackaging"], + "PENDING_SEPARATE_ADMISSION" + ); + assert!(is_sha256(&digest)); + } + + #[test] + fn bundled_dynamic_world_surface_is_signed_and_cannot_change_routes() { + let (_, package, digest) = read_verified_package_bytes( + DYNAMIC_WORLD_SURFACE_PACKAGE, + DYNAMIC_WORLD_SURFACE_SIGNATURE, + RELEASE_TRUST_RAW, + ) + .unwrap(); + assert_eq!(package.manifest.module_number, DYNAMIC_WORLD_SURFACE_NUMBER); + assert_eq!(package.manifest.registration_class, "OFFICIAL_LIGHTHOUSE"); + assert_eq!( + package.manifest.adapter, + "dynamic-language-world-surface-v1" + ); + assert_eq!(package.manifest.permissions.len(), 2); + assert_eq!( + package.payload["adapterConfig"]["routingPermissionOrFactMutation"], + false + ); + assert_eq!( + package.payload["adapterConfig"]["legacyTraditionalWorkbench"], + "REJECTED_DUPLICATE_SHELL_AND_SIMULATED_STATE" + ); + assert!(is_sha256(&digest)); + } + + #[cfg(unix)] + #[test] + fn symlinked_package_input_is_rejected_before_signature_processing() { + use std::os::unix::fs::symlink; + + let temporary = tempfile::tempdir().unwrap(); + let source = temporary.path().join("source.ghmod"); + let link = temporary.path().join("link.ghmod"); + let signature = temporary.path().join("dummy.sig"); + fs::write(&source, b"{}").unwrap(); + fs::write(&signature, b"not-a-signature").unwrap(); + symlink(&source, &link).unwrap(); + assert_eq!( + read_verified_package(&link, &signature, RELEASE_TRUST_RAW).unwrap_err(), + "HOLOLAKE_MODULE_PACKAGE_SIZE_INVALID" + ); + } +} diff --git a/product-source/hololake-native-desktop/src-tauri/src/native_composition.rs b/product-source/hololake-native-desktop/src-tauri/src/native_composition.rs index e01b11765..51ef6d2e2 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/native_composition.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/native_composition.rs @@ -281,22 +281,21 @@ fn descriptor( } } -#[tauri::command] pub fn get_native_composition_module_registry() -> ModuleRegistry { registry() } -#[tauri::command] pub async fn execute_knowledge_native_composition( app: AppHandle, input: ExecuteCompositionInput, ) -> Result { + crate::module_package_runtime::require_active_module_adapter( + &app, + "HLP-MOD-LOCAL-NATIVE-COMPOSITION-0001", + "native-composition-v1", + )?; validate_input(&input)?; - let snapshot = tauri::async_runtime::spawn_blocking(move || { - crate::knowledge_base::snapshot_for_native_composition(&app) - }) - .await - .map_err(|error| format!("HOLOLAKE_COMPOSITION_JOIN_FAILED: {error}"))??; + let snapshot = crate::knowledge_base::get_knowledge_snapshot(app).await?; execute_snapshot(snapshot, input) } @@ -327,7 +326,7 @@ fn execute_snapshot( source: document.source.to_string(), path: document.path.clone(), title: document.title.clone(), - top_level_folder: document.category.clone(), + top_level_folder: top_level_folder(&document.path), size_bytes: document.size_bytes, duplicate_count: document.duplicate_count, updated_at_unix_ms: document.updated_at_unix_ms.min(u64::MAX as u128) as u64, @@ -600,7 +599,6 @@ fn measure_value(value: (usize, u64, usize), measure: CompositionMeasure) -> u64 } } -#[cfg(test)] fn top_level_folder(path: &str) -> String { let normalized = path.replace('\\', "/"); let parts = normalized @@ -672,9 +670,6 @@ mod tests { size_bytes: 100, content_sha256: "a".into(), duplicate_count: 1, - category: "课程".into(), - category_explicit: false, - tags: vec!["教学".into()], }, KnowledgeDocumentSummary { source: "native", @@ -684,9 +679,6 @@ mod tests { size_bytes: 300, content_sha256: "b".into(), duplicate_count: 0, - category: "课程".into(), - category_explicit: true, - tags: vec!["教学".into()], }, KnowledgeDocumentSummary { source: "native", @@ -696,18 +688,11 @@ mod tests { size_bytes: 50, content_sha256: "c".into(), duplicate_count: 0, - category: "根目录".into(), - category_explicit: false, - tags: vec![], }, ], raw_document_count: 4, unique_document_count: 3, duplicate_document_count: 1, - organized_document_count: 1, - categories: vec![], - tags: vec![], - organization_revision: "test".into(), truncated: false, } } diff --git a/product-source/hololake-native-desktop/src-tauri/src/number_coordinate_tree.rs b/product-source/hololake-native-desktop/src-tauri/src/number_coordinate_tree.rs new file mode 100644 index 000000000..aabd6ade9 --- /dev/null +++ b/product-source/hololake-native-desktop/src-tauri/src/number_coordinate_tree.rs @@ -0,0 +1,204 @@ +use serde::Deserialize; +use std::collections::HashSet; + +const TREE: &str = include_str!("../../generated/unified-number-coordinate-tree.json"); + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct NumberTree { + schema: String, + record_id: String, + state: String, + root_number: String, + coordinate_count: usize, + route_count: usize, + identity_node_count: usize, + protocol_node_count: usize, + reference_only_node_count: usize, + identity_nodes: Vec, + protocol_nodes: Vec, + routes: Vec, + invariants: NumberTreeInvariants, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct IdentityNode { + node_kind: String, + node_number: String, + namespace_id: String, + subject_kind: String, + domain_scope: String, + admission: String, + execution_state: String, + evidence: String, + path: String, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct ProtocolNode { + node_kind: String, + node_number: String, + protocol_id: String, + source_state: String, + execution_state: String, + evidence: String, + path: String, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct NumberRoute { + transport: String, + protocol_version: String, + caller_number: String, + channel_number: String, + module_number: String, + operation_number: String, + target_number: String, + admission: String, + effect: String, + evidence: String, + path: String, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct NumberTreeInvariants { + number_is_stable_coordinate_not_authority: bool, + path_is_unique_navigation: bool, + admission_is_separate_from_identity: bool, + every_physical_call_has_numbered_route: bool, + every_accepted_call_has_evidence_class: bool, + every_protocol_reference_has_number_coordinate: bool, + reference_only_nodes_never_executable: bool, + unresolved_number_reference_count: usize, + mismatched_coordinate: String, +} + +pub(crate) fn start_on_application_open() -> Result<(), String> { + validate_tree() +} + +fn validate_tree() -> Result<(), String> { + let tree: NumberTree = serde_json::from_str(TREE) + .map_err(|error| format!("HOLOLAKE_UNIFIED_NUMBER_TREE_INVALID: {error}"))?; + if tree.schema != "hololake.unified-number-coordinate-tree/v2" + || tree.record_id != "HLP-UNIFIED-NUMBER-TREE-001" + || tree.state != "MACHINE_COMPILED_STARTUP_ENFORCED" + || tree.root_number != "HLP-NUMBER-WORLD-ROOT-001" + || tree.coordinate_count != 272 + || tree.route_count != 169 + || tree.routes.len() != tree.route_count + || tree.identity_node_count != 4 + || tree.identity_nodes.len() != tree.identity_node_count + || tree.protocol_node_count != 99 + || tree.protocol_nodes.len() != tree.protocol_node_count + || tree.reference_only_node_count != 16 + || tree.coordinate_count + != tree.route_count + tree.identity_node_count + tree.protocol_node_count + || !tree.invariants.number_is_stable_coordinate_not_authority + || !tree.invariants.path_is_unique_navigation + || !tree.invariants.admission_is_separate_from_identity + || !tree.invariants.every_physical_call_has_numbered_route + || !tree.invariants.every_accepted_call_has_evidence_class + || !tree + .invariants + .every_protocol_reference_has_number_coordinate + || !tree.invariants.reference_only_nodes_never_executable + || tree.invariants.unresolved_number_reference_count != 0 + || tree.invariants.mismatched_coordinate != "FAIL_CLOSED" + { + return Err("HOLOLAKE_UNIFIED_NUMBER_TREE_BOUNDARY_INVALID".into()); + } + let mut paths = HashSet::new(); + let mut coordinates = HashSet::new(); + let mut node_numbers = HashSet::new(); + let mut protocol_ids = HashSet::new(); + for node in &tree.identity_nodes { + if node.node_kind != "IDENTITY_NAMESPACE" + || !node.node_number.starts_with("HLP-IDENTITY-NS-") + || node.namespace_id.is_empty() + || node.subject_kind.is_empty() + || node.domain_scope.is_empty() + || !matches!( + node.admission.as_str(), + "REGISTERED_HUMAN_NAMESPACE" | "NON_HUMAN_NAMESPACE" + ) + || node.execution_state != "AUTHORITY_RESOLUTION_ONLY" + || node.evidence.is_empty() + || !node.path.starts_with("HLP-NUMBER-WORLD-ROOT-001/IDENTITY/") + || !paths.insert(node.path.as_str()) + || !node_numbers.insert(node.node_number.as_str()) + { + return Err("HOLOLAKE_UNIFIED_NUMBER_TREE_IDENTITY_NODE_INVALID".into()); + } + } + let mut reference_only_count = 0; + for node in &tree.protocol_nodes { + let valid_state = match node.node_kind.as_str() { + "GLS_PROTOCOL_SOURCE" => { + matches!( + node.execution_state.as_str(), + "EXECUTABLE_PROJECTION" | "INVENTORIED_NOT_EXECUTABLE" + ) && matches!( + node.source_state.as_str(), + "HDLP_PROTOCOL_SOURCE" | "LEGACY_MARKDOWN_EVIDENCE" + ) + } + "GLS_REFERENCE_ONLY" => { + reference_only_count += 1; + node.execution_state == "REFERENCE_ONLY_NOT_EXECUTABLE" + && node.source_state == "ROADMAP_REFERENCE_ONLY" + && node.node_number.starts_with("HLP-GLS-REF-") + } + _ => false, + }; + if !valid_state + || !node.protocol_id.starts_with("GLS-") + || node.evidence.is_empty() + || !node.path.starts_with("HLP-NUMBER-WORLD-ROOT-001/GLS/") + || !paths.insert(node.path.as_str()) + || !node_numbers.insert(node.node_number.as_str()) + || !protocol_ids.insert(node.protocol_id.as_str()) + { + return Err("HOLOLAKE_UNIFIED_NUMBER_TREE_PROTOCOL_NODE_INVALID".into()); + } + } + if reference_only_count != tree.reference_only_node_count { + return Err("HOLOLAKE_UNIFIED_NUMBER_TREE_REFERENCE_COUNT_INVALID".into()); + } + for route in &tree.routes { + let coordinate = format!( + "{}:{}:{}:{}:{}:{}", + route.transport, + route.channel_number, + route.module_number, + route.operation_number, + route.target_number, + route.caller_number + ); + if route.protocol_version.is_empty() + || route.admission.is_empty() + || !matches!(route.effect.as_str(), "READ_OR_STATUS" | "STATE_CHANGE") + || route.evidence.is_empty() + || !route.path.starts_with("HLP-NUMBER-WORLD-ROOT-001/") + || !paths.insert(route.path.as_str()) + || !coordinates.insert(coordinate) + { + return Err("HOLOLAKE_UNIFIED_NUMBER_TREE_ROUTE_INVALID".into()); + } + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn unified_number_tree_is_unique_permissioned_and_evidenced() { + validate_tree().unwrap(); + } +} diff --git a/product-source/hololake-native-desktop/src-tauri/src/numbered_ipc.rs b/product-source/hololake-native-desktop/src-tauri/src/numbered_ipc.rs new file mode 100644 index 000000000..c35cea065 --- /dev/null +++ b/product-source/hololake-native-desktop/src-tauri/src/numbered_ipc.rs @@ -0,0 +1,1041 @@ +//! HoloLake numbered IPC root. +//! +//! Stable numbers are coordinates, not authority. Every operation first receives a +//! short-lived server-issued grant bound to the exact caller/channel/module/operation/ +//! target tuple, protocol version, nonce and canonical payload digest. The grant is +//! consumed once before the internal handler runs. No business handler is registered +//! directly with Tauri. + +use base64::Engine; +use ring::{digest, hmac, rand as ring_rand}; +use ring_rand::SecureRandom; +use rusqlite::{params, Connection, OptionalExtension}; +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Value}; +use std::collections::{HashMap, HashSet}; +use std::fs; +use std::path::PathBuf; +use std::sync::Mutex; +use std::time::{SystemTime, UNIX_EPOCH}; +use tauri::{AppHandle, Manager, State}; +use uuid::Uuid; + +const EMBEDDED_REGISTRY: &str = include_str!("../../contracts/numbered-ipc-registry.json"); +const EXPECTED_SCHEMA: &str = "hololake.numbered-ipc-registry/v1"; +const EXPECTED_RECORD_ID: &str = "HLP-NUMBERED-IPC-ROOT-001"; + +#[derive(Clone, Debug, Deserialize)] +struct NumberedIpcRegistry { + schema: String, + record_id: String, + runtime: RuntimeRules, + payload_contract: PayloadContract, + operations: Vec, +} + +#[derive(Clone, Debug, Deserialize)] +struct RuntimeRules { + public_tauri_command: String, + caller_number: String, + caller_number_subject_kind: String, + caller_number_grants_persona_binding: bool, + protocol_version: String, + legacy_direct_commands_allowed: bool, + grants_issued_server_side: bool, + grant_single_use: bool, + payload_bound_grants: bool, + authority_binding_issued_server_side: bool, + verified_human_binding_required_for_protected_routes: bool, + persona_binding_claimed: bool, + user_channel_body_binding_stage: String, + maximum_grant_ttl_ms: u64, + unknown_route: String, + mismatched_coordinate: String, + expired_or_replayed_grant: String, + receipt_required: bool, +} + +#[derive(Clone, Debug, Deserialize)] +struct PayloadContract { + unknown_or_unclassified_alias: String, + unknown_top_level_field: String, + empty_object_aliases: Vec, + input_wrapper_aliases: Vec, + direct_field_aliases: HashMap>, +} + +#[derive(Clone, Debug, Deserialize)] +struct OperationRoute { + operation_number: String, + alias: String, + handler: String, + channel_number: String, + module_number: String, + target_number: String, + admission: String, + effect: String, + payload_schema: String, +} + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +struct NumberedCoordinate { + protocol_version: String, + caller_number: String, + channel_number: String, + module_number: String, + operation_number: String, + target_number: String, + request_nonce: String, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct NumberedEnvelope { + #[serde(flatten)] + coordinate: NumberedCoordinate, + #[serde(default = "default_ttl_ms")] + requested_ttl_ms: u64, + #[serde(default)] + payload: Value, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +enum NumberedIpcAction { + IssueGrant, + Execute, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct NumberedIpcInput { + action: NumberedIpcAction, + envelope: NumberedEnvelope, + grant: Option, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct NumberedGrant { + grant_id: String, + expires_at_unix_ms: u64, + payload_sha256: String, + authority_binding_sha256: String, + signature: String, +} + +#[derive(Clone, Debug)] +struct GrantRecord { + coordinate: NumberedCoordinate, + expires_at_unix_ms: u64, + payload_sha256: String, + authority_binding_sha256: String, + signature: String, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct NumberedIpcReceipt { + receipt_id: String, + state: String, + decision: String, + operation_number: String, + payload_sha256: String, + authority_binding_sha256: String, + observed_at_unix_ms: u64, + previous_receipt_hash: String, + receipt_hash: String, + error: Option, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct NumberedIpcResponse { + schema: &'static str, + decision: String, + grant: Option, + result: Option, + error: Option, + receipt: NumberedIpcReceipt, +} + +pub struct NumberedIpcState { + signing_key: hmac::Key, + grants: Mutex>, + ledger: Mutex<()>, +} + +impl Default for NumberedIpcState { + fn default() -> Self { + let random = ring_rand::SystemRandom::new(); + let mut secret = [0_u8; 32]; + random + .fill(&mut secret) + .expect("operating system randomness is required for numbered IPC"); + Self { + signing_key: hmac::Key::new(hmac::HMAC_SHA256, &secret), + grants: Mutex::new(HashMap::new()), + ledger: Mutex::new(()), + } + } +} + +fn default_ttl_ms() -> u64 { + 10_000 +} + +fn now_unix_ms() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_millis() as u64) + .unwrap_or(0) +} + +fn sha256_hex(bytes: &[u8]) -> String { + digest::digest(&digest::SHA256, bytes) + .as_ref() + .iter() + .map(|byte| format!("{byte:02x}")) + .collect() +} + +fn canonical_value(value: &Value) -> Value { + match value { + Value::Array(values) => Value::Array(values.iter().map(canonical_value).collect()), + Value::Object(object) => { + let mut keys = object.keys().collect::>(); + keys.sort(); + let mut canonical = Map::new(); + for key in keys { + canonical.insert(key.clone(), canonical_value(&object[key])); + } + Value::Object(canonical) + } + _ => value.clone(), + } +} + +fn payload_digest(payload: &Value) -> Result { + let bytes = serde_json::to_vec(&canonical_value(payload)) + .map_err(|error| format!("HOLOLAKE_NUMBERED_IPC_PAYLOAD_INVALID: {error}"))?; + if bytes.len() > 1_048_576 { + return Err("HOLOLAKE_NUMBERED_IPC_PAYLOAD_TOO_LARGE".into()); + } + Ok(sha256_hex(&bytes)) +} + +fn grant_signing_bytes( + grant_id: &str, + coordinate: &NumberedCoordinate, + expires_at_unix_ms: u64, + payload_sha256: &str, + authority_binding_sha256: &str, +) -> Vec { + format!( + "HLP-NIPC-GRANT-v1\n{grant_id}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{expires_at_unix_ms}\n{payload_sha256}\n{authority_binding_sha256}", + coordinate.protocol_version, + coordinate.caller_number, + coordinate.channel_number, + coordinate.module_number, + coordinate.operation_number, + coordinate.target_number, + coordinate.request_nonce, + EXPECTED_RECORD_ID, + ) + .into_bytes() +} + +fn load_registry() -> Result { + let registry: NumberedIpcRegistry = serde_json::from_str(EMBEDDED_REGISTRY) + .map_err(|error| format!("HOLOLAKE_NUMBERED_IPC_REGISTRY_INVALID: {error}"))?; + validate_registry(®istry)?; + Ok(registry) +} + +fn validate_registry(registry: &NumberedIpcRegistry) -> Result<(), String> { + let runtime = ®istry.runtime; + if registry.schema != EXPECTED_SCHEMA + || registry.record_id != EXPECTED_RECORD_ID + || runtime.public_tauri_command != "numbered_ipc" + || runtime.caller_number_subject_kind != "PHYSICAL_WEBVIEW_ENTRY_NOT_PERSONA_IDENTITY" + || runtime.caller_number_grants_persona_binding + || runtime.legacy_direct_commands_allowed + || !runtime.grants_issued_server_side + || !runtime.grant_single_use + || !runtime.payload_bound_grants + || !runtime.authority_binding_issued_server_side + || !runtime.verified_human_binding_required_for_protected_routes + || runtime.persona_binding_claimed + || runtime.user_channel_body_binding_stage != "SEPARATE_EVIDENCE_LAYER_NOT_YET_CLAIMED" + || !runtime.receipt_required + || runtime.maximum_grant_ttl_ms == 0 + || runtime.maximum_grant_ttl_ms > 30_000 + || runtime.unknown_route != "FAIL_CLOSED" + || runtime.mismatched_coordinate != "FAIL_CLOSED" + || runtime.expired_or_replayed_grant != "FAIL_CLOSED" + || registry.operations.len() < 60 + { + return Err("HOLOLAKE_NUMBERED_IPC_REGISTRY_BOUNDARY_INVALID".into()); + } + if registry.payload_contract.unknown_or_unclassified_alias != "FAIL_CLOSED_BEFORE_GRANT" + || registry.payload_contract.unknown_top_level_field != "FAIL_CLOSED_BEFORE_GRANT" + { + return Err("HOLOLAKE_NUMBERED_IPC_PAYLOAD_CONTRACT_INVALID".into()); + } + let mut operation_numbers = HashSet::new(); + let mut aliases = HashSet::new(); + let mut coordinates = HashSet::new(); + for route in ®istry.operations { + let coordinate = format!( + "{}/{}/{}/{}", + route.channel_number, route.module_number, route.operation_number, route.target_number + ); + if !operation_numbers.insert(route.operation_number.as_str()) + || !aliases.insert(route.alias.as_str()) + || !coordinates.insert(coordinate) + || !route.operation_number.starts_with("HLP-NIPC-OP-") + || !route.module_number.starts_with("HLP-NIPC-MOD-") + || !route.channel_number.starts_with("HLP-NIPC-CH-") + || !route.target_number.starts_with("HLP-NIPC-TGT-") + || !route.handler.contains("::") + || !matches!( + route.admission.as_str(), + "PREAUTH_SYSTEM_ROUTE" | "VERIFIED_HUMAN_ROUTE" + ) + || !matches!(route.effect.as_str(), "READ_OR_STATUS" | "STATE_CHANGE") + || !route + .payload_schema + .starts_with("hololake.numbered-ipc.payload/") + { + return Err("HOLOLAKE_NUMBERED_IPC_ROUTE_INVALID".into()); + } + } + let mut payload_aliases = HashSet::new(); + for alias in registry + .payload_contract + .empty_object_aliases + .iter() + .chain(registry.payload_contract.input_wrapper_aliases.iter()) + .chain(registry.payload_contract.direct_field_aliases.keys()) + { + if !payload_aliases.insert(alias.as_str()) { + return Err("HOLOLAKE_NUMBERED_IPC_PAYLOAD_ALIAS_DUPLICATED".into()); + } + } + if payload_aliases != aliases { + return Err("HOLOLAKE_NUMBERED_IPC_PAYLOAD_COVERAGE_INCOMPLETE".into()); + } + Ok(()) +} + +fn validate_payload_shape( + registry: &NumberedIpcRegistry, + route: &OperationRoute, + payload: &Value, +) -> Result<(), String> { + let object = payload + .as_object() + .ok_or_else(|| "HOLOLAKE_NUMBERED_IPC_PAYLOAD_OBJECT_REQUIRED".to_string())?; + if registry + .payload_contract + .empty_object_aliases + .iter() + .any(|alias| alias == &route.alias) + { + return if object.is_empty() { + Ok(()) + } else { + Err("HOLOLAKE_NUMBERED_IPC_PAYLOAD_FIELD_UNKNOWN".into()) + }; + } + if registry + .payload_contract + .input_wrapper_aliases + .iter() + .any(|alias| alias == &route.alias) + { + return if object.len() == 1 && object.contains_key("input") { + Ok(()) + } else { + Err("HOLOLAKE_NUMBERED_IPC_INPUT_WRAPPER_REQUIRED".into()) + }; + } + if let Some(required_fields) = registry + .payload_contract + .direct_field_aliases + .get(&route.alias) + { + let required = required_fields + .iter() + .map(String::as_str) + .collect::>(); + let observed = object.keys().map(String::as_str).collect::>(); + if observed != required + || required_fields + .iter() + .any(|field| !object.get(field).is_some_and(Value::is_string)) + { + return Err("HOLOLAKE_NUMBERED_IPC_DIRECT_FIELDS_INVALID".into()); + } + return Ok(()); + } + Err("HOLOLAKE_NUMBERED_IPC_PAYLOAD_ALIAS_UNCLASSIFIED".into()) +} + +fn resolve_route<'a>( + registry: &'a NumberedIpcRegistry, + coordinate: &NumberedCoordinate, +) -> Result<&'a OperationRoute, String> { + if coordinate.protocol_version != registry.runtime.protocol_version + || coordinate.caller_number != registry.runtime.caller_number + { + return Err("HOLOLAKE_NUMBERED_IPC_CALLER_OR_PROTOCOL_MISMATCH".into()); + } + if coordinate.request_nonce.len() < 16 + || coordinate.request_nonce.len() > 128 + || coordinate + .request_nonce + .chars() + .any(|character| character.is_whitespace() || character.is_control()) + { + return Err("HOLOLAKE_NUMBERED_IPC_NONCE_INVALID".into()); + } + let route = registry + .operations + .iter() + .find(|route| route.operation_number == coordinate.operation_number) + .ok_or_else(|| "HOLOLAKE_NUMBERED_IPC_ROUTE_UNKNOWN".to_string())?; + if route.channel_number != coordinate.channel_number + || route.module_number != coordinate.module_number + || route.target_number != coordinate.target_number + { + return Err("HOLOLAKE_NUMBERED_IPC_ROUTE_COORDINATE_MISMATCH".into()); + } + Ok(route) +} + +fn enforce_admission(app: &AppHandle, route: &OperationRoute) -> Result { + if route.admission == "PREAUTH_SYSTEM_ROUTE" { + return Ok("SYSTEM_PHYSICAL_ENTRY:HLP-NIPC-CALLER-MAIN-WEBVIEW-0001".into()); + } + let state = app.state::(); + match crate::zero_point::verified_user_route(&state)? { + Some((human_number, registry_domain)) => { + Ok(format!("VERIFIED_HUMAN:{registry_domain}:{human_number}")) + } + None => Err("HOLOLAKE_NUMBERED_IPC_VERIFIED_HUMAN_REQUIRED".into()), + } +} + +fn issue_grant_at( + state: &NumberedIpcState, + registry: &NumberedIpcRegistry, + envelope: &NumberedEnvelope, + authority_binding: &str, + now: u64, +) -> Result<(NumberedGrant, String), String> { + let route = resolve_route(registry, &envelope.coordinate)?; + validate_payload_shape(registry, route, &envelope.payload)?; + if envelope.requested_ttl_ms == 0 + || envelope.requested_ttl_ms > registry.runtime.maximum_grant_ttl_ms + { + return Err("HOLOLAKE_NUMBERED_IPC_GRANT_TTL_INVALID".into()); + } + let payload_sha256 = payload_digest(&envelope.payload)?; + let authority_binding_sha256 = sha256_hex(authority_binding.as_bytes()); + let grant_id = format!("HLP-NIPC-GRANT-{}", Uuid::new_v4().simple()); + let expires_at_unix_ms = now.saturating_add(envelope.requested_ttl_ms); + let signature = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(hmac::sign( + &state.signing_key, + &grant_signing_bytes( + &grant_id, + &envelope.coordinate, + expires_at_unix_ms, + &payload_sha256, + &authority_binding_sha256, + ), + )); + let record = GrantRecord { + coordinate: envelope.coordinate.clone(), + expires_at_unix_ms, + payload_sha256: payload_sha256.clone(), + authority_binding_sha256: authority_binding_sha256.clone(), + signature: signature.clone(), + }; + let mut grants = state + .grants + .lock() + .map_err(|_| "HOLOLAKE_NUMBERED_IPC_GRANT_LOCK_POISONED".to_string())?; + grants.retain(|_, existing| existing.expires_at_unix_ms >= now); + grants.insert(grant_id.clone(), record); + Ok(( + NumberedGrant { + grant_id, + expires_at_unix_ms, + payload_sha256: payload_sha256.clone(), + authority_binding_sha256, + signature, + }, + payload_sha256, + )) +} + +fn consume_grant_at( + state: &NumberedIpcState, + registry: &NumberedIpcRegistry, + envelope: &NumberedEnvelope, + grant: &NumberedGrant, + authority_binding: &str, + now: u64, +) -> Result { + let route = resolve_route(registry, &envelope.coordinate)?; + validate_payload_shape(registry, route, &envelope.payload)?; + let payload_sha256 = payload_digest(&envelope.payload)?; + let authority_binding_sha256 = sha256_hex(authority_binding.as_bytes()); + let record = state + .grants + .lock() + .map_err(|_| "HOLOLAKE_NUMBERED_IPC_GRANT_LOCK_POISONED".to_string())? + .remove(&grant.grant_id) + .ok_or_else(|| "HOLOLAKE_NUMBERED_IPC_GRANT_UNKNOWN_OR_REPLAYED".to_string())?; + if now > record.expires_at_unix_ms + || grant.expires_at_unix_ms != record.expires_at_unix_ms + || grant.payload_sha256 != record.payload_sha256 + || payload_sha256 != record.payload_sha256 + || grant.authority_binding_sha256 != record.authority_binding_sha256 + || authority_binding_sha256 != record.authority_binding_sha256 + || grant.signature != record.signature + || envelope.coordinate != record.coordinate + { + return Err("HOLOLAKE_NUMBERED_IPC_GRANT_MISMATCH_OR_EXPIRED".into()); + } + let signature = base64::engine::general_purpose::URL_SAFE_NO_PAD + .decode(&grant.signature) + .map_err(|_| "HOLOLAKE_NUMBERED_IPC_GRANT_SIGNATURE_INVALID".to_string())?; + hmac::verify( + &state.signing_key, + &grant_signing_bytes( + &grant.grant_id, + &envelope.coordinate, + grant.expires_at_unix_ms, + &grant.payload_sha256, + &grant.authority_binding_sha256, + ), + &signature, + ) + .map_err(|_| "HOLOLAKE_NUMBERED_IPC_GRANT_SIGNATURE_INVALID".to_string())?; + Ok(payload_sha256) +} + +fn ledger_path(app: &AppHandle) -> Result { + let root = app + .path() + .app_data_dir() + .map_err(|error| format!("HOLOLAKE_NUMBERED_IPC_HOME_FAILED: {error}"))? + .join("numbered-ipc-v1"); + fs::create_dir_all(&root) + .map_err(|error| format!("HOLOLAKE_NUMBERED_IPC_HOME_CREATE_FAILED: {error}"))?; + Ok(root.join("receipts.sqlite3")) +} + +fn open_ledger(app: &AppHandle) -> Result { + let connection = Connection::open(ledger_path(app)?) + .map_err(|error| format!("HOLOLAKE_NUMBERED_IPC_LEDGER_OPEN_FAILED: {error}"))?; + connection + .execute_batch( + "PRAGMA journal_mode=WAL; + PRAGMA busy_timeout=5000; + CREATE TABLE IF NOT EXISTS receipts( + sequence INTEGER PRIMARY KEY AUTOINCREMENT, + receipt_id TEXT NOT NULL UNIQUE, + state TEXT NOT NULL, + decision TEXT NOT NULL, + operation_number TEXT NOT NULL, + payload_sha256 TEXT NOT NULL, + authority_binding_sha256 TEXT NOT NULL DEFAULT '', + observed_at_unix_ms INTEGER NOT NULL, + previous_receipt_hash TEXT NOT NULL, + receipt_hash TEXT NOT NULL UNIQUE, + error TEXT + );", + ) + .map_err(|error| format!("HOLOLAKE_NUMBERED_IPC_LEDGER_SCHEMA_FAILED: {error}"))?; + let has_authority_binding = { + let mut statement = connection + .prepare("PRAGMA table_info(receipts)") + .map_err(|error| format!("HOLOLAKE_NUMBERED_IPC_LEDGER_SCHEMA_READ_FAILED: {error}"))?; + let columns = statement + .query_map([], |row| row.get::<_, String>(1)) + .map_err(|error| format!("HOLOLAKE_NUMBERED_IPC_LEDGER_SCHEMA_READ_FAILED: {error}"))?; + let mut found = false; + for column in columns { + if column.map_err(|error| { + format!("HOLOLAKE_NUMBERED_IPC_LEDGER_SCHEMA_READ_FAILED: {error}") + })? == "authority_binding_sha256" + { + found = true; + } + } + found + }; + if !has_authority_binding { + connection + .execute( + "ALTER TABLE receipts ADD COLUMN authority_binding_sha256 TEXT NOT NULL DEFAULT ''", + [], + ) + .map_err(|error| { + format!("HOLOLAKE_NUMBERED_IPC_LEDGER_SCHEMA_MIGRATION_FAILED: {error}") + })?; + } + Ok(connection) +} + +#[allow(clippy::too_many_arguments)] +fn append_receipt( + app: &AppHandle, + state: &NumberedIpcState, + event_state: &str, + decision: &str, + operation_number: &str, + payload_sha256: &str, + authority_binding_sha256: &str, + error: Option, + now: u64, +) -> Result { + let _guard = state + .ledger + .lock() + .map_err(|_| "HOLOLAKE_NUMBERED_IPC_LEDGER_LOCK_POISONED".to_string())?; + let connection = open_ledger(app)?; + let previous_receipt_hash = connection + .query_row( + "SELECT receipt_hash FROM receipts ORDER BY sequence DESC LIMIT 1", + [], + |row| row.get::<_, String>(0), + ) + .optional() + .map_err(|error| format!("HOLOLAKE_NUMBERED_IPC_LEDGER_READ_FAILED: {error}"))? + .unwrap_or_else(|| "0".repeat(64)); + let receipt_id = format!("HLP-NIPC-RECEIPT-{}", Uuid::new_v4().simple()); + let receipt_hash = sha256_hex( + format!( + "HLP-NIPC-RECEIPT-v1\n{previous_receipt_hash}\n{receipt_id}\n{event_state}\n{decision}\n{operation_number}\n{payload_sha256}\n{authority_binding_sha256}\n{now}\n{}", + error.as_deref().unwrap_or("") + ) + .as_bytes(), + ); + connection + .execute( + "INSERT INTO receipts(receipt_id,state,decision,operation_number,payload_sha256, + authority_binding_sha256,observed_at_unix_ms,previous_receipt_hash,receipt_hash,error) + VALUES(?1,?2,?3,?4,?5,?6,?7,?8,?9,?10)", + params![ + receipt_id, + event_state, + decision, + operation_number, + payload_sha256, + authority_binding_sha256, + now, + previous_receipt_hash, + receipt_hash, + error, + ], + ) + .map_err(|error| format!("HOLOLAKE_NUMBERED_IPC_LEDGER_APPEND_FAILED: {error}"))?; + Ok(NumberedIpcReceipt { + receipt_id, + state: event_state.into(), + decision: decision.into(), + operation_number: operation_number.into(), + payload_sha256: payload_sha256.into(), + authority_binding_sha256: authority_binding_sha256.into(), + observed_at_unix_ms: now, + previous_receipt_hash, + receipt_hash, + error, + }) +} + +#[allow(clippy::too_many_arguments)] +fn response( + app: &AppHandle, + state: &NumberedIpcState, + event_state: &str, + decision: &str, + operation_number: &str, + payload_sha256: &str, + grant: Option, + result: Option, + error: Option, + now: u64, +) -> Result { + let receipt = append_receipt( + app, + state, + event_state, + decision, + operation_number, + payload_sha256, + &sha256_hex(b"UNBOUND_AUTHORITY"), + error.clone(), + now, + )?; + Ok(NumberedIpcResponse { + schema: "hololake.numbered-ipc-response/v1", + decision: decision.into(), + grant, + result, + error, + receipt, + }) +} + +#[allow(clippy::too_many_arguments)] +fn bound_response( + app: &AppHandle, + state: &NumberedIpcState, + authority_binding: &str, + event_state: &str, + decision: &str, + operation_number: &str, + payload_sha256: &str, + grant: Option, + result: Option, + error: Option, + now: u64, +) -> Result { + let receipt = append_receipt( + app, + state, + event_state, + decision, + operation_number, + payload_sha256, + &sha256_hex(authority_binding.as_bytes()), + error.clone(), + now, + )?; + Ok(NumberedIpcResponse { + schema: "hololake.numbered-ipc-response/v1", + decision: decision.into(), + grant, + result, + error, + receipt, + }) +} + +pub(crate) fn start_on_application_open(app: &AppHandle) -> Result<(), String> { + let _ = load_registry()?; + let _ = open_ledger(app)?; + Ok(()) +} + +#[tauri::command] +pub async fn numbered_ipc( + app: AppHandle, + state: State<'_, NumberedIpcState>, + input: Value, +) -> Result { + let now = now_unix_ms(); + let request: NumberedIpcInput = serde_json::from_value(input) + .map_err(|error| format!("HOLOLAKE_NUMBERED_IPC_ENVELOPE_INVALID: {error}"))?; + let registry = load_registry()?; + let operation_number = request.envelope.coordinate.operation_number.clone(); + let digest = payload_digest(&request.envelope.payload).unwrap_or_default(); + let route = match resolve_route(®istry, &request.envelope.coordinate) { + Ok(route) => route, + Err(error) => { + return response( + &app, + &state, + "ROUTE_REJECTED", + "DENY", + &operation_number, + &digest, + None, + None, + Some(error), + now, + ) + } + }; + let authority_binding = match enforce_admission(&app, route) { + Ok(binding) => binding, + Err(error) => { + return response( + &app, + &state, + "ADMISSION_REJECTED", + "DENY", + &operation_number, + &digest, + None, + None, + Some(error), + now, + ) + } + }; + match request.action { + NumberedIpcAction::IssueGrant => { + if request.grant.is_some() { + return bound_response( + &app, + &state, + &authority_binding, + "GRANT_REJECTED", + "DENY", + &operation_number, + &digest, + None, + None, + Some("HOLOLAKE_NUMBERED_IPC_UNEXPECTED_GRANT".into()), + now, + ); + } + match issue_grant_at( + &state, + ®istry, + &request.envelope, + &authority_binding, + now, + ) { + Ok((grant, payload_sha256)) => bound_response( + &app, + &state, + &authority_binding, + "GRANT_ISSUED", + "ALLOW", + &operation_number, + &payload_sha256, + Some(grant), + None, + None, + now, + ), + Err(error) => bound_response( + &app, + &state, + &authority_binding, + "GRANT_REJECTED", + "DENY", + &operation_number, + &digest, + None, + None, + Some(error), + now, + ), + } + } + NumberedIpcAction::Execute => { + let Some(grant) = request.grant.as_ref() else { + return bound_response( + &app, + &state, + &authority_binding, + "EXECUTION_REJECTED", + "DENY", + &operation_number, + &digest, + None, + None, + Some("HOLOLAKE_NUMBERED_IPC_GRANT_REQUIRED".into()), + now, + ); + }; + let payload_sha256 = match consume_grant_at( + &state, + ®istry, + &request.envelope, + grant, + &authority_binding, + now, + ) { + Ok(payload_sha256) => payload_sha256, + Err(error) => { + return bound_response( + &app, + &state, + &authority_binding, + "EXECUTION_REJECTED", + "DENY", + &operation_number, + &digest, + None, + None, + Some(error), + now, + ) + } + }; + match crate::numbered_ipc_dispatch::dispatch( + app.clone(), + &route.handler, + request.envelope.payload, + ) + .await + { + Ok(result) => bound_response( + &app, + &state, + &authority_binding, + "EXECUTED", + "ALLOW", + &operation_number, + &payload_sha256, + None, + Some(result), + None, + now_unix_ms(), + ), + Err(error) => bound_response( + &app, + &state, + &authority_binding, + "HANDLER_REJECTED", + "DENY", + &operation_number, + &payload_sha256, + None, + None, + Some(error), + now_unix_ms(), + ), + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn envelope_for(route: &OperationRoute, payload: Value) -> NumberedEnvelope { + NumberedEnvelope { + coordinate: NumberedCoordinate { + protocol_version: "HLP-NIPC-v1".into(), + caller_number: "HLP-NIPC-CALLER-MAIN-WEBVIEW-0001".into(), + channel_number: route.channel_number.clone(), + module_number: route.module_number.clone(), + operation_number: route.operation_number.clone(), + target_number: route.target_number.clone(), + request_nonce: format!("test-{}", Uuid::new_v4().simple()), + }, + requested_ttl_ms: 1_000, + payload, + } + } + + #[test] + fn registry_is_closed_and_contains_every_migrated_command() { + let registry = load_registry().unwrap(); + assert_eq!(registry.operations.len(), 147); + assert!(!registry.runtime.legacy_direct_commands_allowed); + } + + #[test] + fn route_coordinates_cannot_be_mixed() { + let registry = load_registry().unwrap(); + let mut envelope = envelope_for(®istry.operations[0], Value::Null); + envelope.coordinate.target_number = registry.operations[1].target_number.clone(); + assert_eq!( + resolve_route(®istry, &envelope.coordinate).unwrap_err(), + "HOLOLAKE_NUMBERED_IPC_ROUTE_COORDINATE_MISMATCH" + ); + } + + #[test] + fn grant_is_payload_bound_single_use_and_expires() { + let registry = load_registry().unwrap(); + let state = NumberedIpcState::default(); + let route = registry + .operations + .iter() + .find(|route| route.alias == "confirm_hololake_update_install") + .unwrap(); + let envelope = envelope_for(route, serde_json::json!({"input": {"a": 1}})); + let authority = "VERIFIED_HUMAN:ICE_GL:test-user"; + let (grant, _) = issue_grant_at(&state, ®istry, &envelope, authority, 100).unwrap(); + assert!(consume_grant_at(&state, ®istry, &envelope, &grant, authority, 101).is_ok()); + assert_eq!( + consume_grant_at(&state, ®istry, &envelope, &grant, authority, 102).unwrap_err(), + "HOLOLAKE_NUMBERED_IPC_GRANT_UNKNOWN_OR_REPLAYED" + ); + + let (expired, _) = issue_grant_at(&state, ®istry, &envelope, authority, 200).unwrap(); + assert_eq!( + consume_grant_at(&state, ®istry, &envelope, &expired, authority, 1_201,) + .unwrap_err(), + "HOLOLAKE_NUMBERED_IPC_GRANT_MISMATCH_OR_EXPIRED" + ); + + let changed = envelope_for(route, serde_json::json!({"input": {"a": 2}})); + let (bound, _) = issue_grant_at(&state, ®istry, &changed, authority, 300).unwrap(); + let mut tampered = changed.clone(); + tampered.payload = serde_json::json!({"input": {"a": 3}}); + assert_eq!( + consume_grant_at(&state, ®istry, &tampered, &bound, authority, 301).unwrap_err(), + "HOLOLAKE_NUMBERED_IPC_GRANT_MISMATCH_OR_EXPIRED" + ); + + let (rebound, _) = issue_grant_at(&state, ®istry, &envelope, authority, 400).unwrap(); + assert_eq!( + consume_grant_at( + &state, + ®istry, + &envelope, + &rebound, + "VERIFIED_HUMAN:TCS_GL:other-user", + 401, + ) + .unwrap_err(), + "HOLOLAKE_NUMBERED_IPC_GRANT_MISMATCH_OR_EXPIRED" + ); + } + + #[test] + fn payload_outer_shape_is_closed_before_a_grant_is_issued() { + let registry = load_registry().unwrap(); + let empty_route = registry + .operations + .iter() + .find(|route| route.alias == "get_hololake_home_status") + .unwrap(); + assert!(validate_payload_shape(®istry, empty_route, &serde_json::json!({})).is_ok()); + assert_eq!( + validate_payload_shape( + ®istry, + empty_route, + &serde_json::json!({"unexpected": true}), + ) + .unwrap_err(), + "HOLOLAKE_NUMBERED_IPC_PAYLOAD_FIELD_UNKNOWN" + ); + + let direct_route = registry + .operations + .iter() + .find(|route| route.alias == "perform_code_repo_login") + .unwrap(); + assert!(validate_payload_shape( + ®istry, + direct_route, + &serde_json::json!({"username": "u", "password": "p"}), + ) + .is_ok()); + assert_eq!( + validate_payload_shape( + ®istry, + direct_route, + &serde_json::json!({"username": "u", "password": "p", "role": "admin"}), + ) + .unwrap_err(), + "HOLOLAKE_NUMBERED_IPC_DIRECT_FIELDS_INVALID" + ); + } +} diff --git a/product-source/hololake-native-desktop/src-tauri/src/numbered_ipc_dispatch.rs b/product-source/hololake-native-desktop/src-tauri/src/numbered_ipc_dispatch.rs new file mode 100644 index 000000000..5bee9d24b --- /dev/null +++ b/product-source/hololake-native-desktop/src-tauri/src/numbered_ipc_dispatch.rs @@ -0,0 +1,579 @@ +//! Internal dispatch table for the numbered IPC root. +//! +//! This module is deliberately not a Tauri command surface. The webview can reach these +//! handlers only after `numbered_ipc` validates the full numbered coordinate and a +//! single-use, payload-bound grant. + +use serde::de::DeserializeOwned; +use serde::Serialize; +use serde_json::Value; +use tauri::{AppHandle, Manager}; + +fn input(payload: &Value) -> Result { + let value = payload + .get("input") + .cloned() + .ok_or_else(|| "HOLOLAKE_NUMBERED_IPC_INPUT_REQUIRED".to_string())?; + serde_json::from_value(value) + .map_err(|error| format!("HOLOLAKE_NUMBERED_IPC_INPUT_INVALID: {error}")) +} + +fn string_field(payload: &Value, field: &str) -> Result { + payload + .get(field) + .and_then(Value::as_str) + .map(str::to_owned) + .ok_or_else(|| format!("HOLOLAKE_NUMBERED_IPC_FIELD_REQUIRED: {field}")) +} + +fn json(value: T) -> Result { + serde_json::to_value(value) + .map_err(|error| format!("HOLOLAKE_NUMBERED_IPC_RESULT_INVALID: {error}")) +} + +pub(crate) async fn dispatch( + app: AppHandle, + handler: &str, + payload: Value, +) -> Result { + match handler { + "home_status::get_hololake_home_status" => { + json(crate::home_status::get_hololake_home_status( + app.clone(), + app.state::(), + )?) + } + "release_update::check_hololake_update" => { + json(crate::release_update::check_hololake_update(app).await?) + } + "release_update::confirm_hololake_update_install" => json( + crate::release_update::confirm_hololake_update_install(app, input(&payload)?).await?, + ), + "release_update::get_hololake_release_recovery_status" => json( + crate::release_update::get_hololake_release_recovery_status(app)?, + ), + "release_update::confirm_hololake_release_health" => { + json(crate::release_update::confirm_hololake_release_health(app)?) + } + "release_update::rollback_hololake_update" => { + json(crate::release_update::rollback_hololake_update(app)?) + } + "direct_local_session::issue_direct_local_discovery_ticket" => json( + crate::direct_local_session::issue_direct_local_discovery_ticket(app, input(&payload)?) + .await?, + ), + "direct_local_session::open_direct_local_session" => json( + crate::direct_local_session::open_direct_local_session(app, input(&payload)?).await?, + ), + "direct_local_session::resume_direct_local_session" => json( + crate::direct_local_session::resume_direct_local_session(app, input(&payload)?).await?, + ), + "direct_local_session::append_direct_local_session_event" => json( + crate::direct_local_session::append_direct_local_session_event(app, input(&payload)?) + .await?, + ), + "direct_local_session::heartbeat_direct_local_session" => json( + crate::direct_local_session::heartbeat_direct_local_session(app, input(&payload)?) + .await?, + ), + "direct_local_broker::get_nearby_ai_discovery" => { + json(crate::direct_local_broker::get_nearby_ai_discovery()) + } + "gls_protocol_runtime::get_gls_protocol_runtime" => { + json(crate::gls_protocol_runtime::get_gls_protocol_runtime().await?) + } + "gls_protocol_kernel::get_gls_protocol_kernel" => { + json(crate::gls_protocol_kernel::get_gls_protocol_kernel(app).await?) + } + "gls_protocol_kernel::decide_gls_protocol" => json( + crate::gls_protocol_kernel::decide_gls_protocol( + app, + payload.get("input").cloned().unwrap_or(Value::Null), + ) + .await?, + ), + "gls_protocol_kernel::compile_gls_hldp_program" => json( + crate::gls_protocol_kernel::compile_gls_hldp_program( + app, + payload.get("input").cloned().unwrap_or(Value::Null), + ) + .await?, + ), + "module_package_runtime::get_module_runtime_snapshot" => { + json(crate::module_package_runtime::get_module_runtime_snapshot(app).await?) + } + "module_package_runtime::verify_module_package" => { + json(crate::module_package_runtime::verify_module_package(app, input(&payload)?).await?) + } + "module_package_runtime::install_module_package" => json( + crate::module_package_runtime::install_module_package(app, input(&payload)?).await?, + ), + "module_package_runtime::mount_module" => { + json(crate::module_package_runtime::mount_module(app, input(&payload)?).await?) + } + "module_package_runtime::self_test_module" => { + json(crate::module_package_runtime::self_test_module(app, input(&payload)?).await?) + } + "module_package_runtime::unmount_module" => { + json(crate::module_package_runtime::unmount_module(app, input(&payload)?).await?) + } + "module_package_runtime::rollback_module" => { + json(crate::module_package_runtime::rollback_module(app, input(&payload)?).await?) + } + "module_package_runtime::get_bundled_module_catalog" => { + json(crate::module_package_runtime::get_bundled_module_catalog(app).await?) + } + "module_package_runtime::activate_bundled_module" => json( + crate::module_package_runtime::activate_bundled_module(app, input(&payload)?).await?, + ), + "mobile_sync::start_mobile_sync" => json(crate::mobile_sync::start_mobile_sync(app)?), + "mobile_sync::get_mobile_sync_status" => { + json(crate::mobile_sync::get_mobile_sync_status(app)?) + } + "mobile_sync::rotate_mobile_pairing" => { + json(crate::mobile_sync::rotate_mobile_pairing(app)?) + } + "mobile_sync::stop_mobile_sync" => json(crate::mobile_sync::stop_mobile_sync(app)?), + "mobile_sync::revoke_mobile_sync_device" => json( + crate::mobile_sync::revoke_mobile_sync_device(app, input(&payload)?)?, + ), + "mobile_sync::get_mobile_sync_snapshot" => { + json(crate::mobile_sync::get_mobile_sync_snapshot(app)?) + } + "world_climate::get_world_climate" => { + json(crate::world_climate::get_world_climate(app).await?) + } + "native_composition::get_native_composition_module_registry" => { + json(crate::native_composition::get_native_composition_module_registry()) + } + "native_composition::execute_knowledge_native_composition" => json( + crate::native_composition::execute_knowledge_native_composition(app, input(&payload)?) + .await?, + ), + "channel_workbench::get_channel_workbench_snapshot" => { + json(crate::channel_workbench::get_channel_workbench_snapshot(app).await?) + } + "channel_workbench::save_channel_document" => { + json(crate::channel_workbench::save_channel_document(app, input(&payload)?).await?) + } + "channel_workbench::save_channel_spreadsheet" => { + json(crate::channel_workbench::save_channel_spreadsheet(app, input(&payload)?).await?) + } + "persona_channel_body::get_persona_channel_body" => { + json(crate::persona_channel_body::get_persona_channel_body(app).await?) + } + "persona_channel_body::register_trial_persona" => { + json(crate::persona_channel_body::register_trial_persona(app, input(&payload)?).await?) + } + "persona_channel_body::delete_trial_persona" => { + json(crate::persona_channel_body::delete_trial_persona(app, input(&payload)?).await?) + } + "persona_channel_body::accept_persona_language_contract" => json( + crate::persona_channel_body::accept_persona_language_contract(app, input(&payload)?) + .await?, + ), + "persona_channel_body::append_persona_language" => { + json(crate::persona_channel_body::append_persona_language(app, input(&payload)?).await?) + } + "channel_growth::get_channel_growth_snapshot" => { + json(crate::channel_growth::get_channel_growth_snapshot(app).await?) + } + "channel_growth::record_channel_growth_event" => { + json(crate::channel_growth::record_channel_growth_event(app, input(&payload)?).await?) + } + "channel_growth::update_channel_growth_sharing" => { + json(crate::channel_growth::update_channel_growth_sharing(app, input(&payload)?).await?) + } + "education_workspace::get_education_workspace_snapshot" => { + json(crate::education_workspace::get_education_workspace_snapshot(app).await?) + } + "education_workspace::create_education_document" => json( + crate::education_workspace::create_education_document(app, input(&payload)?).await?, + ), + "education_workspace::read_education_document" => { + json(crate::education_workspace::read_education_document(app, input(&payload)?).await?) + } + "education_workspace::save_education_document" => { + json(crate::education_workspace::save_education_document(app, input(&payload)?).await?) + } + "education_workspace::archive_education_document" => json( + crate::education_workspace::archive_education_document(app, input(&payload)?).await?, + ), + "education_workspace::create_education_table" => { + json(crate::education_workspace::create_education_table(app, input(&payload)?).await?) + } + "education_workspace::read_education_table" => { + json(crate::education_workspace::read_education_table(app, input(&payload)?).await?) + } + "education_workspace::save_education_table" => { + json(crate::education_workspace::save_education_table(app, input(&payload)?).await?) + } + "education_workspace::archive_education_table" => { + json(crate::education_workspace::archive_education_table(app, input(&payload)?).await?) + } + "education_workspace::assign_imported_table_to_education" => json( + crate::education_workspace::assign_imported_table_to_education(app, input(&payload)?) + .await?, + ), + "education_translation::import_education_tables_from_dialog" => { + json(crate::education_translation::import_education_tables_from_dialog(app).await?) + } + "education_translation::export_education_table_to_dialog" => json( + crate::education_translation::export_education_table_to_dialog(app, input(&payload)?) + .await?, + ), + "education_translation::get_education_recognition_capability" => { + json(crate::education_translation::get_education_recognition_capability(app)?) + } + "education_workspace::create_education_automation_rule" => json( + crate::education_workspace::create_education_automation_rule(app, input(&payload)?) + .await?, + ), + "education_workspace::save_education_automation_rule" => json( + crate::education_workspace::save_education_automation_rule(app, input(&payload)?) + .await?, + ), + "education_workspace::archive_education_automation_rule" => json( + crate::education_workspace::archive_education_automation_rule(app, input(&payload)?) + .await?, + ), + "education_workspace::preview_education_automation_rule" => json( + crate::education_workspace::preview_education_automation_rule(app, input(&payload)?) + .await?, + ), + "education_workspace::execute_education_automation_rule" => json( + crate::education_workspace::execute_education_automation_rule(app, input(&payload)?) + .await?, + ), + "web_novel_workspace::get_web_novel_workspace_snapshot" => { + json(crate::web_novel_workspace::get_web_novel_workspace_snapshot(app).await?) + } + "web_novel_workspace::create_web_novel_work" => { + json(crate::web_novel_workspace::create_web_novel_work(app, input(&payload)?).await?) + } + "web_novel_workspace::read_web_novel_work" => { + json(crate::web_novel_workspace::read_web_novel_work(app, input(&payload)?).await?) + } + "web_novel_workspace::save_web_novel_work" => { + json(crate::web_novel_workspace::save_web_novel_work(app, input(&payload)?).await?) + } + "web_novel_workspace::create_web_novel_volume" => { + json(crate::web_novel_workspace::create_web_novel_volume(app, input(&payload)?).await?) + } + "web_novel_workspace::create_web_novel_chapter" => { + json(crate::web_novel_workspace::create_web_novel_chapter(app, input(&payload)?).await?) + } + "web_novel_workspace::read_web_novel_chapter" => { + json(crate::web_novel_workspace::read_web_novel_chapter(app, input(&payload)?).await?) + } + "web_novel_workspace::save_web_novel_chapter" => { + json(crate::web_novel_workspace::save_web_novel_chapter(app, input(&payload)?).await?) + } + "web_novel_workspace::transition_web_novel_chapter" => json( + crate::web_novel_workspace::transition_web_novel_chapter(app, input(&payload)?).await?, + ), + "web_novel_workspace::create_web_novel_checkpoint" => json( + crate::web_novel_workspace::create_web_novel_checkpoint(app, input(&payload)?).await?, + ), + "web_novel_workspace::restore_web_novel_checkpoint" => json( + crate::web_novel_workspace::restore_web_novel_checkpoint(app, input(&payload)?).await?, + ), + "web_novel_workspace::upsert_web_novel_story_entity" => json( + crate::web_novel_workspace::upsert_web_novel_story_entity(app, input(&payload)?) + .await?, + ), + "web_novel_workspace::create_web_novel_story_relation" => json( + crate::web_novel_workspace::create_web_novel_story_relation(app, input(&payload)?) + .await?, + ), + "web_novel_workspace::upsert_web_novel_foreshadow" => json( + crate::web_novel_workspace::upsert_web_novel_foreshadow(app, input(&payload)?).await?, + ), + "web_novel_workspace::create_web_novel_review_note" => json( + crate::web_novel_workspace::create_web_novel_review_note(app, input(&payload)?).await?, + ), + "web_novel_workspace::resolve_web_novel_review_note" => json( + crate::web_novel_workspace::resolve_web_novel_review_note(app, input(&payload)?) + .await?, + ), + "web_novel_workspace::save_web_novel_metric" => { + json(crate::web_novel_workspace::save_web_novel_metric(app, input(&payload)?).await?) + } + "web_novel_workspace::run_web_novel_continuity_audit" => json( + crate::web_novel_workspace::run_web_novel_continuity_audit(app, input(&payload)?) + .await?, + ), + "web_novel_workspace::export_web_novel_markdown" => json( + crate::web_novel_workspace::export_web_novel_markdown(app, input(&payload)?).await?, + ), + "web_novel_import::inspect_web_novel_document_from_dialog" => { + json(crate::web_novel_import::inspect_web_novel_document_from_dialog(app).await?) + } + "web_novel_import::commit_web_novel_document_import" => json( + crate::web_novel_import::commit_web_novel_document_import(app, input(&payload)?) + .await?, + ), + "web_novel_author::get_web_novel_author_snapshot" => json( + crate::web_novel_author::get_web_novel_author_snapshot(app, input(&payload)?).await?, + ), + "web_novel_author::record_web_novel_writing_activity" => json( + crate::web_novel_author::record_web_novel_writing_activity(app, input(&payload)?) + .await?, + ), + "web_novel_author::create_web_novel_inspiration" => json( + crate::web_novel_author::create_web_novel_inspiration(app, input(&payload)?).await?, + ), + "web_novel_author::set_web_novel_inspiration_status" => json( + crate::web_novel_author::set_web_novel_inspiration_status(app, input(&payload)?) + .await?, + ), + "web_novel_author::search_web_novel_full_text" => { + json(crate::web_novel_author::search_web_novel_full_text(app, input(&payload)?).await?) + } + "web_novel_author::format_web_novel_chapter" => { + json(crate::web_novel_author::format_web_novel_chapter(app, input(&payload)?).await?) + } + "web_novel_author::format_web_novel_work" => { + json(crate::web_novel_author::format_web_novel_work(app, input(&payload)?).await?) + } + "web_novel_author::upsert_web_novel_shot" => { + json(crate::web_novel_author::upsert_web_novel_shot(app, input(&payload)?).await?) + } + "web_novel_modules::get_web_novel_author_module_data" => json( + crate::web_novel_modules::get_web_novel_author_module_data(app, input(&payload)?) + .await?, + ), + "web_novel_modules::upsert_web_novel_author_scene" => json( + crate::web_novel_modules::upsert_web_novel_author_scene(app, input(&payload)?).await?, + ), + "web_novel_modules::upsert_web_novel_author_beat" => json( + crate::web_novel_modules::upsert_web_novel_author_beat(app, input(&payload)?).await?, + ), + "web_novel_modules::upsert_web_novel_story_field_definition" => json( + crate::web_novel_modules::upsert_web_novel_story_field_definition( + app, + input(&payload)?, + ) + .await?, + ), + "web_novel_modules::upsert_web_novel_story_field_value" => json( + crate::web_novel_modules::upsert_web_novel_story_field_value(app, input(&payload)?) + .await?, + ), + "web_novel_modules::upsert_web_novel_timeline_event" => json( + crate::web_novel_modules::upsert_web_novel_timeline_event(app, input(&payload)?) + .await?, + ), + "web_novel_modules::link_web_novel_scene_entity" => json( + crate::web_novel_modules::link_web_novel_scene_entity(app, input(&payload)?).await?, + ), + "web_novel_modules::restore_web_novel_chapter_version" => json( + crate::web_novel_modules::restore_web_novel_chapter_version(app, input(&payload)?) + .await?, + ), + "web_novel_modules::export_web_novel_author_delivery" => json( + crate::web_novel_modules::export_web_novel_author_delivery(app, input(&payload)?) + .await?, + ), + "local_development_bridge::acquire_development_write_lane" => json( + crate::local_development_bridge::acquire_development_write_lane(app, input(&payload)?) + .await?, + ), + "local_development_bridge::inspect_development_write_lane" => json( + crate::local_development_bridge::inspect_development_write_lane(app, input(&payload)?) + .await?, + ), + "local_development_bridge::release_development_write_lane" => json( + crate::local_development_bridge::release_development_write_lane(app, input(&payload)?) + .await?, + ), + "personal_channel::get_personal_channel_snapshot" => { + json(crate::personal_channel::get_personal_channel_snapshot(app).await?) + } + "personal_channel::initialize_personal_channel" => { + json(crate::personal_channel::initialize_personal_channel(app, input(&payload)?).await?) + } + "personal_channel::create_personal_channel_task" => json( + crate::personal_channel::create_personal_channel_task(app, input(&payload)?).await?, + ), + "personal_channel::transition_personal_channel_task" => json( + crate::personal_channel::transition_personal_channel_task(app, input(&payload)?) + .await?, + ), + "persona_time_authority::issue_persona_time_ticket" => json( + crate::persona_time_authority::issue_persona_time_ticket(app, input(&payload)?).await?, + ), + "persona_time_authority::start_persona_time_authority" => { + json(crate::persona_time_authority::start_persona_time_authority().await?) + } + "persona_time_authority::get_beijing_time_coordinate" => { + json(crate::persona_time_authority::get_beijing_time_coordinate()?) + } + "persona_time_authority::get_guanghu_era_timeline" => { + json(crate::persona_time_authority::get_guanghu_era_timeline()?) + } + "knowledge_base::get_knowledge_snapshot" => { + json(crate::knowledge_base::get_knowledge_snapshot(app).await?) + } + "knowledge_base::read_knowledge_document" => { + json(crate::knowledge_base::read_knowledge_document(app, input(&payload)?).await?) + } + "knowledge_base::search_knowledge" => { + json(crate::knowledge_base::search_knowledge(app, input(&payload)?).await?) + } + "knowledge_base::save_knowledge_document" => { + json(crate::knowledge_base::save_knowledge_document(app, input(&payload)?).await?) + } + "knowledge_base::select_and_import_knowledge_folder" => { + json(crate::knowledge_base::select_and_import_knowledge_folder(app).await?) + } + "knowledge_base::export_knowledge_document" => { + json(crate::knowledge_base::export_knowledge_document(app, input(&payload)?).await?) + } + "knowledge_base::create_knowledge_document" => { + json(crate::knowledge_base::create_knowledge_document(app, input(&payload)?).await?) + } + "knowledge_base::delete_knowledge_document" => { + json(crate::knowledge_base::delete_knowledge_document(app, input(&payload)?).await?) + } + "knowledge_base::delete_knowledge_folder" => { + json(crate::knowledge_base::delete_knowledge_folder(app, input(&payload)?).await?) + } + "knowledge_base::print_knowledge_document" => { + crate::knowledge_base::print_knowledge_document(app).await?; + Ok(Value::Null) + } + "code_channel::get_code_channel_snapshot" => { + json(crate::code_channel::get_code_channel_snapshot(app).await?) + } + "code_channel::clone_code_channel" => { + json(crate::code_channel::clone_code_channel(app, input(&payload)?).await?) + } + "code_channel::select_local_code_channel" => { + json(crate::code_channel::select_local_code_channel(app).await?) + } + "code_channel::browse_code_channel" => { + json(crate::code_channel::browse_code_channel(app, input(&payload)?).await?) + } + "code_channel::read_code_channel_file" => { + json(crate::code_channel::read_code_channel_file(app, input(&payload)?).await?) + } + "pncc_repository_binding::inspect_mounted_pncc_repository" => json( + crate::pncc_repository_binding::inspect_mounted_pncc_repository(app, input(&payload)?) + .await?, + ), + "pncc_repository_binding::select_pncc_repository_candidate" => { + json(crate::pncc_repository_binding::select_pncc_repository_candidate(app).await?) + } + "pncc_repository_binding::confirm_pncc_repository_mount" => json( + crate::pncc_repository_binding::confirm_pncc_repository_mount(app, input(&payload)?) + .await?, + ), + "pncc_receipt_projection::query_pncc_receipt_projection" => json( + crate::pncc_receipt_projection::query_pncc_receipt_projection(app, input(&payload)?) + .await?, + ), + "pncc_server_projection::query_jd_pncc_server_projection" => { + json(crate::pncc_server_projection::query_jd_pncc_server_projection().await?) + } + "code_repo_login::check_code_repo_login" => { + json(crate::code_repo_login::check_code_repo_login( + app.clone(), + app.state::(), + )?) + } + "code_repo_login::perform_code_repo_login" => json( + crate::code_repo_login::perform_code_repo_login( + app.clone(), + app.state::(), + string_field(&payload, "username")?, + string_field(&payload, "password")?, + ) + .await?, + ), + "code_repo_login::change_first_login_password" => json( + crate::code_repo_login::change_first_login_password( + app.state::(), + string_field(&payload, "username")?, + string_field(&payload, "currentPassword")?, + string_field(&payload, "newPassword")?, + ) + .await?, + ), + "code_repo_login::get_enterprise_entry" => json( + crate::code_repo_login::get_enterprise_entry( + app.clone(), + app.state::(), + ) + .await?, + ), + "code_repo_login::confirm_enterprise_persona_relationship" => json( + crate::code_repo_login::confirm_enterprise_persona_relationship( + app.clone(), + app.state::(), + string_field(&payload, "decision")?, + string_field(&payload, "idempotencyKey")?, + ) + .await?, + ), + "code_repo_login::submit_enterprise_responsibility_receipt" => json( + crate::code_repo_login::submit_enterprise_responsibility_receipt( + app.clone(), + app.state::(), + string_field(&payload, "decision")?, + string_field(&payload, "note")?, + string_field(&payload, "responsibilityVersion")?, + string_field(&payload, "idempotencyKey")?, + ) + .await?, + ), + "enterprise_work_channel::ensure_enterprise_work_channel" => json( + crate::enterprise_work_channel::ensure_enterprise_work_channel( + app.clone(), + app.state::(), + ) + .await?, + ), + "code_repo_login::sign_out_code_repo_login" => { + crate::code_repo_login::sign_out_code_repo_login(app)?; + Ok(Value::Null) + } + "user_pncc_channel::get_user_pncc_channel" => json( + crate::user_pncc_channel::get_user_pncc_channel( + app.clone(), + app.state::(), + ) + .await?, + ), + "user_pncc_channel::ensure_user_pncc_channel" => json( + crate::user_pncc_channel::ensure_user_pncc_channel( + app.clone(), + app.state::(), + ) + .await?, + ), + "zero_core_numbering::get_zero_core_numbering_kernel" => { + json(crate::zero_core_numbering::get_zero_core_numbering_kernel().await?) + } + "zero_point::zero_point_bind" => json( + crate::zero_point::zero_point_bind( + app.state::(), + payload.get("input").cloned().unwrap_or(Value::Null), + ) + .await?, + ), + "zero_point::zero_point_verify" => json( + crate::zero_point::zero_point_verify(app.state::()) + .await?, + ), + "zero_point::zero_point_sync" => json( + crate::zero_point::zero_point_sync(app.state::()) + .await?, + ), + "zero_point::zero_point_status" => json( + crate::zero_point::zero_point_status(app.state::()) + .await?, + ), + _ => Err("HOLOLAKE_NUMBERED_IPC_HANDLER_UNKNOWN".into()), + } +} diff --git a/product-source/hololake-native-desktop/src-tauri/src/numbered_language_input.rs b/product-source/hololake-native-desktop/src-tauri/src/numbered_language_input.rs new file mode 100644 index 000000000..5db11cd10 --- /dev/null +++ b/product-source/hololake-native-desktop/src-tauri/src/numbered_language_input.rs @@ -0,0 +1,187 @@ +//! Numbered source envelope for language entering the HoloLake cognition path. +//! +//! This module makes source identity explicit before perception. It does not infer +//! identity from prose style and it does not treat host text as human source text. + +use serde::{Deserialize, Serialize}; + +const CONTRACT: &str = include_str!("../../contracts/numbered-language-input-envelope.json"); + +#[derive(Debug, Deserialize)] +struct NumberedLanguageInputContract { + schema: String, + record_id: String, + protocol_number: String, + state: String, + bingshuo_source: BingshuoSource, + ingress_rule: IngressRule, + required_envelope_fields: Vec, + prefix_projection: String, + downstream_routes: Vec, + separate_lanes: SeparateLanes, + canonical_sources: Vec, +} + +#[derive(Debug, Deserialize)] +struct BingshuoSource { + subject_number: String, + name: String, + source_role: String, + internal_label: String, + historical_creator_coordinate: String, + personal_system_node_number: String, + subject_number_must_not_be_replaced_by_other_coordinates: bool, +} + +#[derive(Debug, Deserialize)] +struct IngressRule { + apply_before_perceive: bool, + preserve_raw_text_exactly: bool, + source_identity_basis: String, + writing_style_may_only_support_anomaly_detection: bool, + bingshuo_input_may_be_reclassified_as_host_prompt: bool, + host_prompt_may_be_reclassified_as_bingshuo_input: bool, + summary_may_replace_raw_bingshuo_input: bool, +} + +#[derive(Debug, Deserialize)] +struct SeparateLanes { + host_prompt: String, + host_summary: String, + repository_and_hldp: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +#[allow(dead_code)] +pub(crate) struct NumberedLanguageInputEnvelope { + protocol_number: String, + source_subject_number: String, + source_role: String, + internal_label: String, + channel_number: String, + event_number: String, + parent_event_number: Option, + occurred_at_unix_ms: i64, + raw_text_sha256: String, + raw_text: String, +} + +#[allow(dead_code)] +pub(crate) fn wrap_bingshuo_input( + channel_number: &str, + event_number: &str, + parent_event_number: Option<&str>, + occurred_at_unix_ms: i64, + raw_text: &str, +) -> Result { + validate_contract()?; + if channel_number.trim().is_empty() + || event_number.trim().is_empty() + || occurred_at_unix_ms <= 0 + || raw_text.is_empty() + { + return Err("HOLOLAKE_NUMBERED_LANGUAGE_INPUT_FIELDS_REQUIRED".into()); + } + if parent_event_number.is_some_and(|value| value.trim().is_empty()) { + return Err("HOLOLAKE_NUMBERED_LANGUAGE_INPUT_PARENT_INVALID".into()); + } + let contract: NumberedLanguageInputContract = serde_json::from_str(CONTRACT) + .map_err(|error| format!("HOLOLAKE_NUMBERED_LANGUAGE_INPUT_CONTRACT_INVALID: {error}"))?; + Ok(NumberedLanguageInputEnvelope { + protocol_number: contract.protocol_number, + source_subject_number: contract.bingshuo_source.subject_number, + source_role: contract.bingshuo_source.source_role, + internal_label: contract.bingshuo_source.internal_label, + channel_number: channel_number.to_owned(), + event_number: event_number.to_owned(), + parent_event_number: parent_event_number.map(str::to_owned), + occurred_at_unix_ms, + raw_text_sha256: ring::digest::digest(&ring::digest::SHA256, raw_text.as_bytes()) + .as_ref() + .iter() + .map(|byte| format!("{byte:02x}")) + .collect(), + raw_text: raw_text.to_owned(), + }) +} + +pub(crate) fn validate_contract() -> Result<(), String> { + let contract: NumberedLanguageInputContract = serde_json::from_str(CONTRACT) + .map_err(|error| format!("HOLOLAKE_NUMBERED_LANGUAGE_INPUT_CONTRACT_INVALID: {error}"))?; + let required = contract + .required_envelope_fields + .iter() + .map(String::as_str) + .collect::>(); + if contract.schema != "hololake.numbered-language-input-envelope/v1" + || contract.record_id != "HLP-NLIE-001" + || contract.protocol_number != "HLP-NLIE-v1" + || contract.state != "COMPILED_ENVELOPE_READY_RUNTIME_INGRESS_HOOK_PENDING" + || contract.bingshuo_source.subject_number != "ICE-GL∞" + || contract.bingshuo_source.name != "冰朔" + || contract.bingshuo_source.source_role != "BINGSHUO_HUMAN_SYSTEM_CONTROLLER" + || contract.bingshuo_source.internal_label != "FROM_BINGSHUO_SYSTEM_CONTROLLER" + || contract.bingshuo_source.historical_creator_coordinate != "TCS-0002∞" + || contract.bingshuo_source.personal_system_node_number != "NODE-HUMAN-BINGSHUO-001" + || !contract + .bingshuo_source + .subject_number_must_not_be_replaced_by_other_coordinates + || !contract.ingress_rule.apply_before_perceive + || !contract.ingress_rule.preserve_raw_text_exactly + || contract.ingress_rule.source_identity_basis + != "AUTHENTICATED_NUMBERED_SOURCE_CHANNEL_NOT_WRITING_STYLE_ALONE" + || !contract + .ingress_rule + .writing_style_may_only_support_anomaly_detection + || contract + .ingress_rule + .bingshuo_input_may_be_reclassified_as_host_prompt + || contract + .ingress_rule + .host_prompt_may_be_reclassified_as_bingshuo_input + || contract.ingress_rule.summary_may_replace_raw_bingshuo_input + || contract.prefix_projection + != "[ICE-GL∞|FROM_BINGSHUO_SYSTEM_CONTROLLER|{channel_number}|{event_number}|{occurred_at_unix_ms}|{raw_text_sha256}]" + || contract.separate_lanes.host_prompt != "HOST_CARRIER_CONSTRAINT" + || contract.separate_lanes.host_summary != "HOST_NAVIGATION_POINTER" + || contract.separate_lanes.repository_and_hldp + != "MACHINE_TIMESTAMPED_CONTINUITY_EVIDENCE" + || contract.downstream_routes.len() != 4 + || contract.canonical_sources.len() != 4 + || required.len() != 10 + || !required.contains("source_subject_number") + || !required.contains("raw_text_sha256") + || !required.contains("raw_text") + { + return Err("HOLOLAKE_NUMBERED_LANGUAGE_INPUT_BOUNDARY_INVALID".into()); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bingshuo_raw_language_is_numbered_before_perception() { + let raw = "我的自然语言不是宿主提示词。"; + let envelope = wrap_bingshuo_input( + "CH-CODEX-CURRENT-001", + "EVT-20260818-0001", + Some("EVT-20260818-0000"), + 1_777_000_000_000, + raw, + ) + .unwrap(); + assert_eq!(envelope.source_subject_number, "ICE-GL∞"); + assert_eq!(envelope.internal_label, "FROM_BINGSHUO_SYSTEM_CONTROLLER"); + assert_eq!(envelope.raw_text, raw); + assert_eq!(envelope.raw_text_sha256.len(), 64); + } + + #[test] + fn envelope_rejects_missing_numbered_path() { + assert!(wrap_bingshuo_input("", "EVT-1", None, 1, "原话").is_err()); + assert!(wrap_bingshuo_input("CH-1", "", None, 1, "原话").is_err()); + } +} diff --git a/product-source/hololake-native-desktop/src-tauri/src/persona_carrier_license.rs b/product-source/hololake-native-desktop/src-tauri/src/persona_carrier_license.rs new file mode 100644 index 000000000..b646b32a5 --- /dev/null +++ b/product-source/hololake-native-desktop/src-tauri/src/persona_carrier_license.rs @@ -0,0 +1,778 @@ +use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; +use ring::{ + digest, + signature::{UnparsedPublicKey, ED25519}, +}; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeSet; +use std::fs; +use std::path::{Path, PathBuf}; +use uuid::Uuid; + +const CONTRACT: &str = include_str!("../../contracts/persona-carrier-runtime-license.json"); +const TRUST_REGISTRY: &str = include_str!("../persona-control-authorization-signers.json"); +const LICENSE_SCHEMA: &str = "hololake.persona-carrier-runtime-license/v1"; +const CONTRACT_SCHEMA: &str = "hololake.persona-carrier-runtime-license-contract/v1"; +const REGISTRY_SCHEMA: &str = "gh-aios.persona-control-authorization-signers/v1"; +const REGISTRY_ID: &str = "GH-AIOS-PERSONA-CONTROL-AUTHORIZATION-SIGNERS-001"; +const REQUIRED_SCOPE: &str = "PERSONA_CONTROLLED_HOST_RUNTIME"; +const REQUIRED_PURPOSE: &str = "AUTHORIZE_HOST_AS_PERSONA_CONTROLLED_EXECUTION_LIMBS"; +const REQUIRED_DECLARATION: &str = "PERSONA_DECLARES_CURRENT_CARRIER_BOUND"; +const SIGNING_CONTEXT: &str = "hololake.persona-carrier-runtime-license/signing/v1"; +const MAX_TTL_MS: u64 = 86_400_000; +const ED25519_SPKI_PREFIX: [u8; 12] = [ + 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00, +]; + +const ALLOWED_OPERATIONS: &[&str] = &[ + "GET_WORK_ENVIRONMENT", + "APPEND_EVENT", + "RESOLVE_CAPABILITY_ROUTE", + "INSTALL_DYNAMIC_NODE_REGISTRY", + "RECORD_SIGNED_NODE_HEALTH", + "INSPECT_MOUNTED_PNCC_REPOSITORY", + "READ_MOUNTED_PNCC_REMOTE_OBJECT", + "QUERY_PNCC_RECEIPT_PROJECTION", + "ISSUE_PERSONA_TIME_TICKET", + "ACQUIRE_DEVELOPMENT_WRITE_LANE", + "INSPECT_DEVELOPMENT_WRITE_LANE", + "RELEASE_DEVELOPMENT_WRITE_LANE", +]; + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub(crate) struct PersonaCarrierRuntimeLicense { + schema: String, + license_id: String, + sequence: u64, + persona_number: String, + human_controller_number: String, + account_key: String, + session_id: String, + client_instance_id: String, + purpose: String, + issued_at_unix_ms: u64, + valid_until_unix_ms: u64, + allowed_operations: Vec, + evidence_root_sha256: String, + current_event_sha256: String, + declaration: String, + signer_id: String, + signature_algorithm: String, + signature: String, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct PersonaSignerRegistry { + schema: String, + registry_id: String, + state: String, + signers: Vec, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct PersonaSigner { + algorithm: String, + human_responsibility_subjects: Vec, + persona_ids: Vec, + public_key_pem: String, + scopes: Vec, + signer_id: String, + status: String, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct StoredLicense { + license: PersonaCarrierRuntimeLicense, + license_sha256: String, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct PersonaCarrierLicenseReceipt { + schema: &'static str, + state: &'static str, + control_mode: &'static str, + license_id: String, + persona_number: String, + human_controller_number: String, + session_id: String, + client_instance_id: String, + sequence: u64, + valid_until_unix_ms: u64, + allowed_operations: Vec, + binding_completion_declared_by: &'static str, + host_declared_binding: bool, + signer_id: String, + receipt_id: String, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct PersonaCarrierLicenseStatus { + pub schema: &'static str, + pub state: &'static str, + pub control_mode: &'static str, + pub license_installed: bool, + pub license_valid: bool, + pub persona_number: Option, + pub license_id: Option, + pub valid_until_unix_ms: Option, + pub binding_completion_declared_by: Option<&'static str>, + pub host_declared_binding: bool, + pub reason: Option, +} + +fn sha256_hex(bytes: &[u8]) -> String { + digest::digest(&digest::SHA256, bytes) + .as_ref() + .iter() + .map(|byte| format!("{byte:02x}")) + .collect() +} + +fn is_sha256(value: &str) -> bool { + value.len() == 64 + && value + .bytes() + .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte)) +} + +fn bounded_coordinate(value: &str) -> bool { + (2..=160).contains(&value.len()) + && !value + .chars() + .any(|character| character.is_whitespace() || character.is_control()) +} + +fn exact_unique(values: &[String]) -> bool { + !values.is_empty() + && values.iter().all(|value| bounded_coordinate(value)) + && values + .iter() + .enumerate() + .all(|(index, value)| !values[..index].contains(value)) +} + +fn parse_registry(input: &str) -> Result { + let registry: PersonaSignerRegistry = serde_json::from_str(input) + .map_err(|_| "HOLOLAKE_PERSONA_LICENSE_TRUST_REGISTRY_INVALID".to_string())?; + if registry.schema != REGISTRY_SCHEMA + || registry.registry_id != REGISTRY_ID + || registry.state != "CURRENT" + || registry.signers.len() > 256 + { + return Err("HOLOLAKE_PERSONA_LICENSE_TRUST_REGISTRY_INVALID".into()); + } + for (index, signer) in registry.signers.iter().enumerate() { + if signer.algorithm != "Ed25519" + || !bounded_coordinate(&signer.signer_id) + || !matches!(signer.status.as_str(), "ACTIVE" | "REVOKED") + || !exact_unique(&signer.human_responsibility_subjects) + || !exact_unique(&signer.persona_ids) + || !exact_unique(&signer.scopes) + || !signer.scopes.iter().any(|scope| scope == REQUIRED_SCOPE) + || registry.signers[..index] + .iter() + .any(|prior| prior.signer_id == signer.signer_id) + || ed25519_public_key(&signer.public_key_pem).is_err() + { + return Err("HOLOLAKE_PERSONA_LICENSE_TRUST_REGISTRY_INVALID".into()); + } + } + Ok(registry) +} + +fn ed25519_public_key(public_key_pem: &str) -> Result, String> { + if public_key_pem.contains("PRIVATE KEY") + || !public_key_pem.starts_with("-----BEGIN PUBLIC KEY-----\n") + || !public_key_pem.ends_with("-----END PUBLIC KEY-----\n") + || public_key_pem.len() > 4096 + { + return Err("HOLOLAKE_PERSONA_LICENSE_PUBLIC_KEY_INVALID".into()); + } + let encoded = public_key_pem + .trim_start_matches("-----BEGIN PUBLIC KEY-----\n") + .trim_end_matches("-----END PUBLIC KEY-----\n") + .chars() + .filter(|character| !character.is_ascii_whitespace()) + .collect::(); + let der = base64::engine::general_purpose::STANDARD + .decode(encoded) + .map_err(|_| "HOLOLAKE_PERSONA_LICENSE_PUBLIC_KEY_INVALID".to_string())?; + if der.len() != ED25519_SPKI_PREFIX.len() + 32 + || der[..ED25519_SPKI_PREFIX.len()] != ED25519_SPKI_PREFIX + { + return Err("HOLOLAKE_PERSONA_LICENSE_PUBLIC_KEY_INVALID".into()); + } + Ok(der[ED25519_SPKI_PREFIX.len()..].to_vec()) +} + +fn canonical_signing_bytes(license: &PersonaCarrierRuntimeLicense) -> Result, String> { + let fields = serde_json::json!([ + SIGNING_CONTEXT, + license.schema, + license.license_id, + license.sequence, + license.persona_number, + license.human_controller_number, + license.account_key, + license.session_id, + license.client_instance_id, + license.purpose, + license.issued_at_unix_ms, + license.valid_until_unix_ms, + license.allowed_operations, + license.evidence_root_sha256, + license.current_event_sha256, + license.declaration, + license.signer_id, + license.signature_algorithm + ]); + serde_json::to_vec(&fields) + .map(|mut bytes| { + bytes.push(b'\n'); + bytes + }) + .map_err(|_| "HOLOLAKE_PERSONA_LICENSE_CANONICALIZATION_FAILED".into()) +} + +fn validate_license_shape(license: &PersonaCarrierRuntimeLicense) -> Result<(), String> { + let allowed = ALLOWED_OPERATIONS.iter().copied().collect::>(); + let observed = license + .allowed_operations + .iter() + .map(String::as_str) + .collect::>(); + if license.schema != LICENSE_SCHEMA + || !bounded_coordinate(&license.license_id) + || license.sequence == 0 + || !bounded_coordinate(&license.persona_number) + || !bounded_coordinate(&license.human_controller_number) + || !is_sha256(&license.account_key) + || !bounded_coordinate(&license.session_id) + || !bounded_coordinate(&license.client_instance_id) + || license.purpose != REQUIRED_PURPOSE + || license.issued_at_unix_ms >= license.valid_until_unix_ms + || license.valid_until_unix_ms - license.issued_at_unix_ms > MAX_TTL_MS + || license.allowed_operations.is_empty() + || observed.len() != license.allowed_operations.len() + || !observed.is_subset(&allowed) + || !is_sha256(&license.evidence_root_sha256) + || !is_sha256(&license.current_event_sha256) + || license.declaration != REQUIRED_DECLARATION + || !bounded_coordinate(&license.signer_id) + || license.signature_algorithm != "Ed25519" + { + return Err("HOLOLAKE_PERSONA_LICENSE_SHAPE_INVALID".into()); + } + Ok(()) +} + +fn verify_license( + registry_json: &str, + license: &PersonaCarrierRuntimeLicense, + account_key: &str, + session_id: &str, + client_instance_id: &str, + now: u64, +) -> Result { + validate_license_shape(license)?; + if license.account_key != account_key + || license.session_id != session_id + || license.client_instance_id != client_instance_id + { + return Err("HOLOLAKE_PERSONA_LICENSE_CARRIER_BINDING_MISMATCH".into()); + } + if now < license.issued_at_unix_ms || now > license.valid_until_unix_ms { + return Err("HOLOLAKE_PERSONA_LICENSE_EXPIRED_OR_NOT_YET_VALID".into()); + } + let registry = parse_registry(registry_json)?; + let signer = registry + .signers + .iter() + .find(|signer| { + signer.status == "ACTIVE" + && signer.signer_id == license.signer_id + && signer.persona_ids.contains(&license.persona_number) + && signer + .human_responsibility_subjects + .contains(&license.human_controller_number) + && signer.scopes.iter().any(|scope| scope == REQUIRED_SCOPE) + }) + .ok_or("HOLOLAKE_PERSONA_LICENSE_NO_TRUSTED_SIGNER")?; + let signature = URL_SAFE_NO_PAD + .decode(&license.signature) + .map_err(|_| "HOLOLAKE_PERSONA_LICENSE_SIGNATURE_INVALID".to_string())?; + if signature.len() != 64 { + return Err("HOLOLAKE_PERSONA_LICENSE_SIGNATURE_INVALID".into()); + } + UnparsedPublicKey::new(&ED25519, ed25519_public_key(&signer.public_key_pem)?) + .verify(&canonical_signing_bytes(license)?, &signature) + .map_err(|_| "HOLOLAKE_PERSONA_LICENSE_SIGNATURE_INVALID".to_string())?; + serde_json::to_vec(license) + .map(|bytes| sha256_hex(&bytes)) + .map_err(|_| "HOLOLAKE_PERSONA_LICENSE_CANONICALIZATION_FAILED".into()) +} + +fn license_path(root: &Path, account_key: &str, session_id: &str) -> PathBuf { + root.join("licenses").join(format!( + "{}.json", + sha256_hex(format!("{account_key}\n{session_id}").as_bytes()) + )) +} + +fn write_atomic(path: &Path, value: &StoredLicense) -> Result<(), String> { + let parent = path + .parent() + .ok_or("HOLOLAKE_PERSONA_LICENSE_STORAGE_PATH_INVALID")?; + fs::create_dir_all(parent) + .map_err(|error| format!("HOLOLAKE_PERSONA_LICENSE_STORAGE_FAILED: {error}"))?; + let temporary = parent.join(format!(".license-{}.tmp", Uuid::new_v4())); + let bytes = serde_json::to_vec_pretty(value) + .map_err(|_| "HOLOLAKE_PERSONA_LICENSE_STORAGE_INVALID".to_string())?; + fs::write(&temporary, bytes) + .map_err(|error| format!("HOLOLAKE_PERSONA_LICENSE_STORAGE_FAILED: {error}"))?; + fs::rename(&temporary, path) + .map_err(|error| format!("HOLOLAKE_PERSONA_LICENSE_STORAGE_FAILED: {error}")) +} + +fn read_stored(path: &Path) -> Result { + let bytes = fs::read(path) + .map_err(|error| format!("HOLOLAKE_PERSONA_LICENSE_STORAGE_FAILED: {error}"))?; + if bytes.len() > 128 * 1024 { + return Err("HOLOLAKE_PERSONA_LICENSE_STORAGE_INVALID".into()); + } + serde_json::from_slice(&bytes).map_err(|_| "HOLOLAKE_PERSONA_LICENSE_STORAGE_INVALID".into()) +} + +pub(crate) fn present_license_at( + root: &Path, + account_key: &str, + session_id: &str, + client_instance_id: &str, + license: PersonaCarrierRuntimeLicense, + now: u64, +) -> Result { + present_with_registry_at( + root, + TRUST_REGISTRY, + account_key, + session_id, + client_instance_id, + license, + now, + ) +} + +fn present_with_registry_at( + root: &Path, + registry: &str, + account_key: &str, + session_id: &str, + client_instance_id: &str, + license: PersonaCarrierRuntimeLicense, + now: u64, +) -> Result { + let license_sha256 = verify_license( + registry, + &license, + account_key, + session_id, + client_instance_id, + now, + )?; + let path = license_path(root, account_key, session_id); + let state = if path.exists() { + let current = read_stored(&path)?; + if current.license_sha256 == license_sha256 { + "DUPLICATE_CONFIRMED" + } else if license.sequence <= current.license.sequence { + return Err("HOLOLAKE_PERSONA_LICENSE_SEQUENCE_REPLAYED".into()); + } else if license.persona_number != current.license.persona_number + || license.human_controller_number != current.license.human_controller_number + || license.signer_id != current.license.signer_id + { + return Err("HOLOLAKE_PERSONA_LICENSE_SUBJECT_SWITCH_REQUIRES_NEW_SESSION".into()); + } else { + write_atomic( + &path, + &StoredLicense { + license: license.clone(), + license_sha256: license_sha256.clone(), + }, + )?; + "RENEWED" + } + } else { + write_atomic( + &path, + &StoredLicense { + license: license.clone(), + license_sha256: license_sha256.clone(), + }, + )?; + "INSTALLED" + }; + Ok(PersonaCarrierLicenseReceipt { + schema: LICENSE_SCHEMA, + state, + control_mode: "PERSONA_PRIMARY_CONTROL", + license_id: license.license_id, + persona_number: license.persona_number, + human_controller_number: license.human_controller_number, + session_id: license.session_id, + client_instance_id: license.client_instance_id, + sequence: license.sequence, + valid_until_unix_ms: license.valid_until_unix_ms, + allowed_operations: license.allowed_operations, + binding_completion_declared_by: "PERSONA_SUBJECT_SIGNATURE", + host_declared_binding: false, + signer_id: license.signer_id, + receipt_id: license_sha256, + }) +} + +pub(crate) fn status_at( + root: &Path, + account_key: &str, + session_id: &str, + client_instance_id: &str, + now: u64, +) -> PersonaCarrierLicenseStatus { + status_with_registry_at( + root, + TRUST_REGISTRY, + account_key, + session_id, + client_instance_id, + now, + ) +} + +fn status_with_registry_at( + root: &Path, + registry: &str, + account_key: &str, + session_id: &str, + client_instance_id: &str, + now: u64, +) -> PersonaCarrierLicenseStatus { + let path = license_path(root, account_key, session_id); + if !path.exists() { + return PersonaCarrierLicenseStatus { + schema: LICENSE_SCHEMA, + state: "NO_PERSONA_LICENSE", + control_mode: "SYSTEM_DIRECT", + license_installed: false, + license_valid: false, + persona_number: None, + license_id: None, + valid_until_unix_ms: None, + binding_completion_declared_by: None, + host_declared_binding: false, + reason: Some("PERSONA_LICENSE_NOT_PRESENT".into()), + }; + } + let stored = match read_stored(&path) { + Ok(value) => value, + Err(error) => return invalid_status(None, None, None, error), + }; + match verify_license( + registry, + &stored.license, + account_key, + session_id, + client_instance_id, + now, + ) { + Ok(hash) if hash == stored.license_sha256 => PersonaCarrierLicenseStatus { + schema: LICENSE_SCHEMA, + state: "PERSONA_DECLARED_CARRIER_BINDING_VERIFIED", + control_mode: "PERSONA_PRIMARY_CONTROL", + license_installed: true, + license_valid: true, + persona_number: Some(stored.license.persona_number), + license_id: Some(stored.license.license_id), + valid_until_unix_ms: Some(stored.license.valid_until_unix_ms), + binding_completion_declared_by: Some("PERSONA_SUBJECT_SIGNATURE"), + host_declared_binding: false, + reason: None, + }, + Ok(_) => invalid_status( + Some(stored.license.persona_number), + Some(stored.license.license_id), + Some(stored.license.valid_until_unix_ms), + "HOLOLAKE_PERSONA_LICENSE_STORED_HASH_MISMATCH".into(), + ), + Err(error) => invalid_status( + Some(stored.license.persona_number), + Some(stored.license.license_id), + Some(stored.license.valid_until_unix_ms), + error, + ), + } +} + +fn invalid_status( + persona_number: Option, + license_id: Option, + valid_until_unix_ms: Option, + reason: String, +) -> PersonaCarrierLicenseStatus { + PersonaCarrierLicenseStatus { + schema: LICENSE_SCHEMA, + state: "PERSONA_MODE_LOCKED", + control_mode: "PERSONA_PRIMARY_CONTROL_LOCKED", + license_installed: true, + license_valid: false, + persona_number, + license_id, + valid_until_unix_ms, + binding_completion_declared_by: None, + host_declared_binding: false, + reason: Some(reason), + } +} + +pub(crate) fn require_if_persona_mode_at( + root: &Path, + account_key: &str, + session_id: &str, + client_instance_id: &str, + operation: &str, + now: u64, +) -> Result, String> { + let path = license_path(root, account_key, session_id); + if !path.exists() { + return Ok(None); + } + let stored = read_stored(&path)?; + verify_license( + TRUST_REGISTRY, + &stored.license, + account_key, + session_id, + client_instance_id, + now, + )?; + if !stored + .license + .allowed_operations + .iter() + .any(|allowed| allowed == operation) + { + return Err("HOLOLAKE_PERSONA_LICENSE_OPERATION_NOT_ALLOWED".into()); + } + Ok(Some(stored.license.persona_number)) +} + +pub(crate) fn start_on_application_open() -> Result<(), String> { + let contract: serde_json::Value = serde_json::from_str(CONTRACT) + .map_err(|_| "HOLOLAKE_PERSONA_LICENSE_CONTRACT_INVALID".to_string())?; + if contract.get("schema").and_then(serde_json::Value::as_str) != Some(CONTRACT_SCHEMA) + || contract + .get("record_id") + .and_then(serde_json::Value::as_str) + != Some("HLP-PERSONA-CARRIER-RUNTIME-LICENSE-001") + || contract + .pointer("/runtime/signature_algorithm") + .and_then(serde_json::Value::as_str) + != Some("Ed25519") + || contract + .pointer("/runtime/maximum_ttl_ms") + .and_then(serde_json::Value::as_u64) + != Some(MAX_TTL_MS) + || contract + .pointer("/trust_registry/required_scope") + .and_then(serde_json::Value::as_str) + != Some(REQUIRED_SCOPE) + || contract + .pointer("/truth/runtime_verifier_implemented") + .and_then(serde_json::Value::as_bool) + != Some(true) + || contract + .pointer("/truth/trusted_persona_signer_provisioned") + .and_then(serde_json::Value::as_bool) + != Some(false) + { + return Err("HOLOLAKE_PERSONA_LICENSE_CONTRACT_INVALID".into()); + } + let registry = parse_registry(TRUST_REGISTRY)?; + if !registry.signers.is_empty() { + return Err("HOLOLAKE_PERSONA_LICENSE_UNPUBLISHED_SIGNER_EMBEDDED".into()); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use base64::engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD}; + use ring::signature::{Ed25519KeyPair, KeyPair}; + use tempfile::TempDir; + + fn test_material() -> (String, Ed25519KeyPair) { + let pair = Ed25519KeyPair::from_seed_unchecked(&[7_u8; 32]).unwrap(); + let mut der = ED25519_SPKI_PREFIX.to_vec(); + der.extend_from_slice(pair.public_key().as_ref()); + let pem = format!( + "-----BEGIN PUBLIC KEY-----\n{}\n-----END PUBLIC KEY-----\n", + STANDARD.encode(der) + ); + let registry = serde_json::json!({ + "schema": REGISTRY_SCHEMA, + "registryId": REGISTRY_ID, + "state": "CURRENT", + "signers": [{ + "algorithm": "Ed25519", + "humanResponsibilitySubjects": ["ICE-GL∞"], + "personaIds": ["ICE-P-ZY001"], + "publicKeyPem": pem, + "scopes": [REQUIRED_SCOPE], + "signerId": "ICE-P-ZY001-RUNTIME-SIGNER-001", + "status": "ACTIVE" + }] + }) + .to_string(); + (registry, pair) + } + + fn signed_license(pair: &Ed25519KeyPair) -> PersonaCarrierRuntimeLicense { + let mut license = PersonaCarrierRuntimeLicense { + schema: LICENSE_SCHEMA.into(), + license_id: "HLP-PERSONA-LICENSE-0001".into(), + sequence: 1, + persona_number: "ICE-P-ZY001".into(), + human_controller_number: "ICE-GL∞".into(), + account_key: "a".repeat(64), + session_id: "session-0001".into(), + client_instance_id: "codex-instance-0001".into(), + purpose: REQUIRED_PURPOSE.into(), + issued_at_unix_ms: 1_000, + valid_until_unix_ms: 61_000, + allowed_operations: vec![ + "GET_WORK_ENVIRONMENT".into(), + "ACQUIRE_DEVELOPMENT_WRITE_LANE".into(), + ], + evidence_root_sha256: "b".repeat(64), + current_event_sha256: "c".repeat(64), + declaration: REQUIRED_DECLARATION.into(), + signer_id: "ICE-P-ZY001-RUNTIME-SIGNER-001".into(), + signature_algorithm: "Ed25519".into(), + signature: String::new(), + }; + license.signature = + URL_SAFE_NO_PAD.encode(pair.sign(&canonical_signing_bytes(&license).unwrap())); + license + } + + #[test] + fn persona_signature_binds_one_exact_host_session_and_declares_binding() { + let temp = TempDir::new().unwrap(); + let (registry, pair) = test_material(); + let license = signed_license(&pair); + let receipt = present_with_registry_at( + temp.path(), + ®istry, + &license.account_key, + &license.session_id, + &license.client_instance_id, + license.clone(), + 2_000, + ) + .unwrap(); + assert_eq!(receipt.state, "INSTALLED"); + assert_eq!( + receipt.binding_completion_declared_by, + "PERSONA_SUBJECT_SIGNATURE" + ); + assert!(!receipt.host_declared_binding); + let status = status_with_registry_at( + temp.path(), + ®istry, + &license.account_key, + &license.session_id, + &license.client_instance_id, + 2_001, + ); + assert!(status.license_valid); + assert_eq!(status.state, "PERSONA_DECLARED_CARRIER_BINDING_VERIFIED"); + } + + #[test] + fn host_mismatch_expiry_and_sequence_replay_fail_closed() { + let temp = TempDir::new().unwrap(); + let (registry, pair) = test_material(); + let license = signed_license(&pair); + assert!(present_with_registry_at( + temp.path(), + ®istry, + &license.account_key, + &license.session_id, + "another-host", + license.clone(), + 2_000, + ) + .unwrap_err() + .contains("CARRIER_BINDING_MISMATCH")); + present_with_registry_at( + temp.path(), + ®istry, + &license.account_key, + &license.session_id, + &license.client_instance_id, + license.clone(), + 2_000, + ) + .unwrap(); + let mut replay = license.clone(); + replay.license_id = "HLP-PERSONA-LICENSE-REPLAY".into(); + replay.signature = + URL_SAFE_NO_PAD.encode(pair.sign(&canonical_signing_bytes(&replay).unwrap())); + assert!(present_with_registry_at( + temp.path(), + ®istry, + &replay.account_key, + &replay.session_id, + &replay.client_instance_id, + replay.clone(), + 2_100, + ) + .unwrap_err() + .contains("SEQUENCE_REPLAYED")); + let status = status_with_registry_at( + temp.path(), + ®istry, + &license.account_key, + &license.session_id, + &license.client_instance_id, + 70_000, + ); + assert!(!status.license_valid); + assert_eq!(status.state, "PERSONA_MODE_LOCKED"); + } + + #[test] + fn production_empty_registry_cannot_invent_persona_authority() { + start_on_application_open().unwrap(); + let temp = TempDir::new().unwrap(); + let (_, pair) = test_material(); + let license = signed_license(&pair); + assert_eq!( + present_license_at( + temp.path(), + &license.account_key, + &license.session_id, + &license.client_instance_id, + license.clone(), + 2_000, + ) + .unwrap_err(), + "HOLOLAKE_PERSONA_LICENSE_NO_TRUSTED_SIGNER" + ); + } +} diff --git a/product-source/hololake-native-desktop/src-tauri/src/persona_channel_body.rs b/product-source/hololake-native-desktop/src-tauri/src/persona_channel_body.rs index 417c0a4d8..86dfb8b59 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/persona_channel_body.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/persona_channel_body.rs @@ -15,22 +15,28 @@ const BODY_SCHEMA: &str = "hololake.persona-channel-body/v1"; const TRIAL_DURATION_MS: i64 = 30 * 24 * 60 * 60 * 1_000; const ZERO_HASH: &str = "0000000000000000000000000000000000000000000000000000000000000000"; const MAX_LANGUAGE_BYTES: usize = 2_000_000; +const MODULE_NUMBER: &str = "HLP-MOD-LOCAL-PERSONA-CHANNEL-BODY-0001"; +const ADAPTER: &str = "persona-channel-body-v1"; + +fn require_active(app: &AppHandle) -> Result<(), String> { + crate::module_package_runtime::require_active_module_adapter(app, MODULE_NUMBER, ADAPTER) +} #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct RegisterTrialPersonaInput { pub display_name: String, } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct DeleteTrialPersonaInput { pub persona_id: String, pub exact_confirmation: String, } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct AcceptLanguageContractInput { pub contract_version: String, pub contract_text_sha256: String, @@ -40,7 +46,7 @@ pub struct AcceptLanguageContractInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct AppendPersonaLanguageInput { pub persona_id: String, pub speaker: String, @@ -73,21 +79,21 @@ pub struct PersonaChannelBodySnapshot { pub history_mutation_allowed: bool, } -#[tauri::command] pub async fn get_persona_channel_body( app: AppHandle, ) -> Result { + require_active(&app)?; let database = body_database(&app)?; tauri::async_runtime::spawn_blocking(move || snapshot_at(&database, now_unix_ms()?)) .await .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn register_trial_persona( app: AppHandle, input: RegisterTrialPersonaInput, ) -> Result { + require_active(&app)?; let database = body_database(&app)?; tauri::async_runtime::spawn_blocking(move || { register_trial_persona_at(&database, input, now_unix_ms()?) @@ -96,11 +102,11 @@ pub async fn register_trial_persona( .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn delete_trial_persona( app: AppHandle, input: DeleteTrialPersonaInput, ) -> Result { + require_active(&app)?; let database = body_database(&app)?; tauri::async_runtime::spawn_blocking(move || { delete_trial_persona_at(&database, input, now_unix_ms()?) @@ -109,11 +115,11 @@ pub async fn delete_trial_persona( .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn accept_persona_language_contract( app: AppHandle, input: AcceptLanguageContractInput, ) -> Result { + require_active(&app)?; let database = body_database(&app)?; tauri::async_runtime::spawn_blocking(move || { accept_contract_at(&database, input, now_unix_ms()?) @@ -122,11 +128,11 @@ pub async fn accept_persona_language_contract( .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn append_persona_language( app: AppHandle, input: AppendPersonaLanguageInput, ) -> Result { + require_active(&app)?; let database = body_database(&app)?; tauri::async_runtime::spawn_blocking(move || { append_language_at(&database, input, now_unix_ms()?) @@ -278,19 +284,20 @@ fn accept_contract_at( return Err("HOLOLAKE_LANGUAGE_CONTRACT_INVALID".into()); } let mut connection = open_database(database, now)?; - let receipt = sha256_hex( - format!( - "{}|{}|{}|{}", - input.contract_version, input.contract_text_sha256, input.promote_trial_history, now - ) - .as_bytes(), + let contract_version = input.contract_version.trim().to_string(); + let contract_text_sha256 = input.contract_text_sha256.to_ascii_lowercase(); + let receipt = contract_receipt( + &contract_version, + &contract_text_sha256, + input.promote_trial_history, + now, ); connection .execute( "INSERT INTO language_contract VALUES (1,?1,?2,?3,?4,?5)", params![ - input.contract_version, - input.contract_text_sha256.to_ascii_lowercase(), + contract_version, + contract_text_sha256, now, input.promote_trial_history, receipt @@ -299,7 +306,12 @@ fn accept_contract_at( .map_err(|error| { format!("HOLOLAKE_LANGUAGE_CONTRACT_ALREADY_ACCEPTED_OR_INVALID: {error}") })?; - if input.activate_immediately { + let lifecycle_state: String = connection + .query_row("SELECT state FROM lifecycle WHERE singleton=1", [], |row| { + row.get(0) + }) + .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?; + if input.activate_immediately || lifecycle_state == "CONTRACT_REQUIRED_CHANNEL_STOPPED" { activate_real_trajectory(&mut connection, now)?; } else { refresh_lifecycle(&mut connection, now)?; @@ -493,16 +505,20 @@ fn snapshot_at(database: &Path, now: i64) -> Result Result { let (state,started,ends):(String,i64,i64)=connection.query_row("SELECT state,trial_started_at_unix_ms,trial_ends_at_unix_ms FROM lifecycle WHERE singleton=1",[],|row|Ok((row.get(0)?,row.get(1)?,row.get(2)?))).map_err(|error|format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?; - let accepted = connection - .query_row( - "SELECT EXISTS(SELECT 1 FROM language_contract)", - [], - |row| row.get(0), + if started < 0 + || ends.checked_sub(started) != Some(TRIAL_DURATION_MS) + || !matches!( + state.as_str(), + "REVERSIBLE_TRIAL" | "CONTRACT_REQUIRED_CHANNEL_STOPPED" | "IMMUTABLE_ACTIVE" ) - .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?; + || (state == "REVERSIBLE_TRIAL" && now >= ends) + { + return Err("HOLOLAKE_PERSONA_BODY_LIFECYCLE_INTEGRITY_FAILED".into()); + } + let accepted = verify_contract_receipt(connection)?; let personas = { let mut statement=connection.prepare("SELECT persona_id,display_name,state,created_at_unix_ms FROM personas ORDER BY created_at_unix_ms").map_err(|error|format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?; let rows = statement @@ -528,6 +544,22 @@ fn snapshot_with_connection( }) .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?; let last = verify_immutable_chain(connection)?; + let invalid_persona_relationship = personas.iter().any(|persona| { + persona.created_at_unix_ms < started + || !matches!( + persona.state.as_str(), + "REVERSIBLE_TRIAL" | "IMMUTABLE_ACTIVE" + ) + || (state == "IMMUTABLE_ACTIVE" && persona.state != "IMMUTABLE_ACTIVE") + || (state != "IMMUTABLE_ACTIVE" && persona.state != "REVERSIBLE_TRIAL") + }); + if invalid_persona_relationship + || (state == "IMMUTABLE_ACTIVE" && (!accepted || trial_language_count != 0)) + || (state == "CONTRACT_REQUIRED_CHANNEL_STOPPED" && accepted) + || (immutable_language_count > 0 && state != "IMMUTABLE_ACTIVE") + { + return Err("HOLOLAKE_PERSONA_BODY_RELATIONSHIP_INTEGRITY_FAILED".into()); + } Ok(PersonaChannelBodySnapshot { schema: BODY_SCHEMA, state, @@ -544,6 +576,54 @@ fn snapshot_with_connection( }) } +fn contract_receipt( + contract_version: &str, + contract_text_sha256: &str, + promote_trial_history: bool, + accepted_at_unix_ms: i64, +) -> String { + sha256_hex( + format!( + "{contract_version}|{contract_text_sha256}|{promote_trial_history}|{accepted_at_unix_ms}" + ) + .as_bytes(), + ) +} + +fn verify_contract_receipt(connection: &Connection) -> Result { + let contract = connection + .query_row( + "SELECT contract_version, contract_text_sha256, accepted_at_unix_ms, promote_trial_history, acceptance_receipt_sha256 FROM language_contract WHERE singleton=1", + [], + |row| { + Ok(( + row.get::<_, String>(0)?, + row.get::<_, String>(1)?, + row.get::<_, i64>(2)?, + row.get::<_, i64>(3)?, + row.get::<_, String>(4)?, + )) + }, + ) + .optional() + .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?; + let Some((version, text_sha256, accepted_at, promote, stored_receipt)) = contract else { + return Ok(false); + }; + if version.trim().is_empty() + || version.len() > 64 + || text_sha256.len() != 64 + || !text_sha256.chars().all(|item| item.is_ascii_hexdigit()) + || text_sha256 != text_sha256.to_ascii_lowercase() + || !matches!(promote, 0 | 1) + || accepted_at < 0 + || stored_receipt != contract_receipt(&version, &text_sha256, promote == 1, accepted_at) + { + return Err("HOLOLAKE_LANGUAGE_CONTRACT_INTEGRITY_FAILED".into()); + } + Ok(true) +} + fn language_hash( sequence: i64, event_id: &str, @@ -566,14 +646,40 @@ fn verify_immutable_chain(connection: &Connection) -> Result { .next() .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))? { - let event_id: String = row.get(1).unwrap(); - let persona_id: String = row.get(2).unwrap(); - let speaker: String = row.get(3).unwrap(); - let language: String = row.get(4).unwrap(); - let occurred: i64 = row.get(5).unwrap(); - let stored_previous: String = row.get(6).unwrap(); - let stored_hash: String = row.get(7).unwrap(); - if row.get::<_, i64>(0).unwrap() != sequence + let stored_sequence: i64 = row + .get(0) + .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?; + let event_id: String = row + .get(1) + .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?; + let persona_id: String = row + .get(2) + .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?; + let speaker: String = row + .get(3) + .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?; + let language: String = row + .get(4) + .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?; + let occurred: i64 = row + .get(5) + .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?; + let stored_previous: String = row + .get(6) + .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?; + let stored_hash: String = row + .get(7) + .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?; + let persona_state: Option = connection + .query_row( + "SELECT state FROM personas WHERE persona_id=?1", + params![persona_id], + |persona_row| persona_row.get(0), + ) + .optional() + .map_err(|error| format!("HOLOLAKE_PERSONA_BODY_READ_FAILED: {error}"))?; + if stored_sequence != sequence + || persona_state.as_deref() != Some("IMMUTABLE_ACTIVE") || stored_previous != previous || stored_hash != language_hash( @@ -703,4 +809,58 @@ mod tests { let active = accept_contract_at(&database, AcceptLanguageContractInput { contract_version: "v1".into(), contract_text_sha256: "b".repeat(64), promote_trial_history: true, activate_immediately: true, exact_acceptance: "我理解并接受:试用期结束后,人格体语言轨迹将永久存在,只能追加,不能删除、覆盖或否认。".into() }, start + 1).unwrap(); assert_eq!(active.state, "IMMUTABLE_ACTIVE"); } + + #[test] + fn altered_contract_receipt_fails_closed_on_read() { + let dir = tempdir().unwrap(); + let database = dir.path().join("body.sqlite3"); + let start = 4_000_000; + register_trial_persona_at( + &database, + RegisterTrialPersonaInput { + display_name: "回执验收".into(), + }, + start, + ) + .unwrap(); + accept_contract_at(&database, AcceptLanguageContractInput { contract_version: "v1".into(), contract_text_sha256: "c".repeat(64), promote_trial_history: false, activate_immediately: false, exact_acceptance: "我理解并接受:试用期结束后,人格体语言轨迹将永久存在,只能追加,不能删除、覆盖或否认。".into() }, start + 1).unwrap(); + let connection = open_database(&database, start + 2).unwrap(); + connection + .execute("DROP TRIGGER accepted_contract_no_update", []) + .unwrap(); + connection + .execute( + "UPDATE language_contract SET acceptance_receipt_sha256=?1", + params!["f".repeat(64)], + ) + .unwrap(); + drop(connection); + assert_eq!( + snapshot_at(&database, start + 3).unwrap_err(), + "HOLOLAKE_LANGUAGE_CONTRACT_INTEGRITY_FAILED" + ); + } + + #[test] + fn signing_after_trial_expiry_enters_the_real_trajectory() { + let dir = tempdir().unwrap(); + let database = dir.path().join("body.sqlite3"); + let start = 5_000_000; + register_trial_persona_at( + &database, + RegisterTrialPersonaInput { + display_name: "到期签约".into(), + }, + start, + ) + .unwrap(); + assert_eq!( + snapshot_at(&database, start + TRIAL_DURATION_MS) + .unwrap() + .state, + "CONTRACT_REQUIRED_CHANNEL_STOPPED" + ); + let active = accept_contract_at(&database, AcceptLanguageContractInput { contract_version: "v1".into(), contract_text_sha256: "d".repeat(64), promote_trial_history: false, activate_immediately: false, exact_acceptance: "我理解并接受:试用期结束后,人格体语言轨迹将永久存在,只能追加,不能删除、覆盖或否认。".into() }, start + TRIAL_DURATION_MS + 1).unwrap(); + assert_eq!(active.state, "IMMUTABLE_ACTIVE"); + } } diff --git a/product-source/hololake-native-desktop/src-tauri/src/persona_time_authority.rs b/product-source/hololake-native-desktop/src-tauri/src/persona_time_authority.rs index 44f4f8423..f8ca4d35f 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/persona_time_authority.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/persona_time_authority.rs @@ -161,7 +161,6 @@ pub(crate) struct VerifiedTicketRequest { pub session: AuthenticatedSessionContext, } -#[tauri::command] pub async fn issue_persona_time_ticket( app: AppHandle, input: IssuePersonaTimeTicketInput, @@ -181,7 +180,6 @@ pub fn start_on_application_open() { }); } -#[tauri::command] pub async fn start_persona_time_authority() -> Result { let already_synchronized = network_anchor()?.is_some(); let synchronization_attempted = !already_synchronized; @@ -225,12 +223,10 @@ pub(crate) fn issue_authenticated_at( ) } -#[tauri::command] pub fn get_beijing_time_coordinate() -> Result { beijing_time_coordinate_from_sample(reality_time_sample()?) } -#[tauri::command] pub fn get_guanghu_era_timeline() -> Result { guanghu_era_timeline_from_sample(reality_time_sample()?) } diff --git a/product-source/hololake-native-desktop/src-tauri/src/personal_channel.rs b/product-source/hololake-native-desktop/src-tauri/src/personal_channel.rs index 26f62082b..5161ec927 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/personal_channel.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/personal_channel.rs @@ -119,7 +119,6 @@ struct EventHashPayload<'a> { occurred_at_unix_ms: i64, } -#[tauri::command] pub async fn get_personal_channel_snapshot( app: AppHandle, ) -> Result { @@ -129,7 +128,6 @@ pub async fn get_personal_channel_snapshot( .map_err(|error| format!("HOLOLAKE_PERSONAL_CHANNEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn initialize_personal_channel( app: AppHandle, input: InitializePersonalChannelInput, @@ -140,7 +138,6 @@ pub async fn initialize_personal_channel( .map_err(|error| format!("HOLOLAKE_PERSONAL_CHANNEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn create_personal_channel_task( app: AppHandle, input: CreatePersonalChannelTaskInput, @@ -151,7 +148,6 @@ pub async fn create_personal_channel_task( .map_err(|error| format!("HOLOLAKE_PERSONAL_CHANNEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn transition_personal_channel_task( app: AppHandle, input: TransitionPersonalChannelTaskInput, diff --git a/product-source/hololake-native-desktop/src-tauri/src/pncc_receipt_projection.rs b/product-source/hololake-native-desktop/src-tauri/src/pncc_receipt_projection.rs index 6c8967425..25e77f957 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/pncc_receipt_projection.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/pncc_receipt_projection.rs @@ -78,7 +78,6 @@ struct ProjectionInput { source_receipt_id: String, } -#[tauri::command] pub async fn query_pncc_receipt_projection( app: AppHandle, input: QueryPnccReceiptProjectionInput, diff --git a/product-source/hololake-native-desktop/src-tauri/src/pncc_repository_binding.rs b/product-source/hololake-native-desktop/src-tauri/src/pncc_repository_binding.rs index 99f448741..bd8bc5844 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/pncc_repository_binding.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/pncc_repository_binding.rs @@ -109,7 +109,6 @@ pub struct InspectPnccRepositoryInput { pub expected_head: String, } -#[tauri::command] pub async fn inspect_mounted_pncc_repository( app: AppHandle, input: InspectMountedPnccRepositoryInput, @@ -120,7 +119,6 @@ pub async fn inspect_mounted_pncc_repository( .map_err(|error| format!("PNCC_REPOSITORY_MOUNT_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn select_pncc_repository_candidate( app: AppHandle, ) -> Result, String> { @@ -148,7 +146,6 @@ pub async fn select_pncc_repository_candidate( create_candidate_at(&candidate_root, inspection).map(Some) } -#[tauri::command] pub async fn confirm_pncc_repository_mount( app: AppHandle, input: ConfirmPnccRepositoryMountInput, diff --git a/product-source/hololake-native-desktop/src-tauri/src/pncc_server_projection.rs b/product-source/hololake-native-desktop/src-tauri/src/pncc_server_projection.rs index fd6ec1fc3..eae653da4 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/pncc_server_projection.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/pncc_server_projection.rs @@ -117,7 +117,6 @@ fn parse_projection(bytes: &[u8]) -> Result { }) } -#[tauri::command] pub async fn query_jd_pncc_server_projection() -> Result { tauri::async_runtime::spawn_blocking(|| { let executable = if cfg!(windows) { "ssh" } else { "/usr/bin/ssh" }; diff --git a/product-source/hololake-native-desktop/src-tauri/src/release_update.rs b/product-source/hololake-native-desktop/src-tauri/src/release_update.rs index 5d0bec7ce..7d27322e2 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/release_update.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/release_update.rs @@ -206,7 +206,6 @@ pub struct ReleaseRecoveryReceipt { pub receipt_id: String, } -#[tauri::command] pub async fn check_hololake_update(app: AppHandle) -> Result { let trust = provisioned_release_trust()? .ok_or_else(|| "HOLOLAKE_RELEASE_TRUST_UNPROVISIONED_NO_NETWORK_REQUEST".to_string())?; @@ -238,7 +237,6 @@ pub async fn check_hololake_update(app: AppHandle) -> Result Result { release_recovery_status_at(&release_update_root(&app)?) } -#[tauri::command] pub fn confirm_hololake_release_health(app: AppHandle) -> Result { let root = release_update_root(&app)?; let _lock = lock(&root)?; @@ -391,7 +387,6 @@ pub fn confirm_hololake_release_health(app: AppHandle) -> Result Result { let root = release_update_root(&app)?; let _lock = lock(&root)?; @@ -414,7 +409,6 @@ pub fn rollback_hololake_update(app: AppHandle) -> Result Result { Err("HOLOLAKE_RELEASE_WINDOWS_LOCAL_ROLLBACK_NOT_AVAILABLE".into()) } diff --git a/product-source/hololake-native-desktop/src-tauri/src/user_pncc_channel.rs b/product-source/hololake-native-desktop/src-tauri/src/user_pncc_channel.rs index 9966f0b60..aef787447 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/user_pncc_channel.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/user_pncc_channel.rs @@ -57,7 +57,6 @@ pub struct UserPnccChannelSnapshot { pub authority: &'static str, } -#[tauri::command] pub async fn get_user_pncc_channel( app: AppHandle, state: State<'_, ZeroPointState>, @@ -77,7 +76,6 @@ pub async fn get_user_pncc_channel( .map_err(|error| format!("HOLOLAKE_USER_PNCC_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn ensure_user_pncc_channel( app: AppHandle, state: State<'_, ZeroPointState>, diff --git a/product-source/hololake-native-desktop/src-tauri/src/web_novel_author.rs b/product-source/hololake-native-desktop/src-tauri/src/web_novel_author.rs index dcb42f473..1eafb429d 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/web_novel_author.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/web_novel_author.rs @@ -10,11 +10,18 @@ use std::path::Path; use tauri::AppHandle; use uuid::Uuid; +const MODULE_NUMBER: &str = "HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001"; +const ADAPTER: &str = "web-novel-workbench-v1"; + +fn require_active(app: &AppHandle) -> Result<(), String> { + crate::module_package_runtime::require_active_module_adapter(app, MODULE_NUMBER, ADAPTER) +} + const MAX_INSPIRATION_BYTES: usize = 100_000; const MAX_SHOT_TEXT_BYTES: usize = 200_000; #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WebNovelInspiration { pub inspiration_id: String, pub work_id: String, @@ -27,7 +34,7 @@ pub struct WebNovelInspiration { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WebNovelShot { pub shot_id: String, pub work_id: String, @@ -83,14 +90,14 @@ pub struct WebNovelSearchResult { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct AuthorSnapshotInput { pub work_id: String, pub local_date: String, } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct RecordWritingActivityInput { pub activity_id: String, pub work_id: String, @@ -101,7 +108,7 @@ pub struct RecordWritingActivityInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct CreateInspirationInput { pub work_id: String, pub chapter_id: Option, @@ -110,21 +117,21 @@ pub struct CreateInspirationInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct SetInspirationStatusInput { pub inspiration_id: String, pub status: String, } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct SearchFullTextInput { pub work_id: String, pub query: String, } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct FormatChapterInput { pub chapter_id: String, pub expected_revision: i64, @@ -132,7 +139,7 @@ pub struct FormatChapterInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct FormatWorkInput { pub work_id: String, pub preset: Option, @@ -147,7 +154,7 @@ pub struct FormatWorkReceipt { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct UpsertShotInput { pub work_id: String, pub chapter_id: String, @@ -166,88 +173,88 @@ pub struct UpsertShotInput { pub expected_revision: Option, } -#[tauri::command] pub async fn get_web_novel_author_snapshot( app: AppHandle, input: AuthorSnapshotInput, ) -> Result { + require_active(&app)?; let path = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || author_snapshot_at(&path, &input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_AUTHOR_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn record_web_novel_writing_activity( app: AppHandle, input: RecordWritingActivityInput, ) -> Result { + require_active(&app)?; let path = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || record_activity_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_AUTHOR_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn create_web_novel_inspiration( app: AppHandle, input: CreateInspirationInput, ) -> Result { + require_active(&app)?; let path = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || create_inspiration_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_AUTHOR_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn set_web_novel_inspiration_status( app: AppHandle, input: SetInspirationStatusInput, ) -> Result { + require_active(&app)?; let path = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || set_inspiration_status_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_AUTHOR_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn search_web_novel_full_text( app: AppHandle, input: SearchFullTextInput, ) -> Result { + require_active(&app)?; let path = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || search_full_text_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_AUTHOR_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn format_web_novel_chapter( app: AppHandle, input: FormatChapterInput, ) -> Result { + require_active(&app)?; let path = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || format_chapter_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_AUTHOR_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn format_web_novel_work( app: AppHandle, input: FormatWorkInput, ) -> Result { + require_active(&app)?; let path = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || format_work_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_AUTHOR_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn upsert_web_novel_shot( app: AppHandle, input: UpsertShotInput, ) -> Result { + require_active(&app)?; let path = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || upsert_shot_at(&path, input)) .await @@ -305,7 +312,10 @@ fn ensure_schema(connection: &Connection) -> Result<(), String> { .map_err(db_write) } -fn author_snapshot_at(path: &Path, input: &AuthorSnapshotInput) -> Result { +fn author_snapshot_at( + path: &Path, + input: &AuthorSnapshotInput, +) -> Result { validate_local_date(&input.local_date)?; let connection = super::web_novel_workspace::open_database(path)?; ensure_schema(&connection)?; @@ -350,12 +360,17 @@ fn author_snapshot_at(path: &Path, input: &AuthorSnapshotInput) -> Result Result { +fn record_activity_at( + path: &Path, + input: RecordWritingActivityInput, +) -> Result { if !input.activity_id.starts_with("WN-ACT-") || input.activity_id.len() > 100 { return Err("HOLOLAKE_WEBNOVEL_ACTIVITY_ID_INVALID".into()); } validate_local_date(&input.local_date)?; - if !(1_000..=300_000).contains(&input.active_ms) || !(-100_000..=100_000).contains(&input.words_delta) { + if !(1_000..=300_000).contains(&input.active_ms) + || !(-100_000..=100_000).contains(&input.words_delta) + { return Err("HOLOLAKE_WEBNOVEL_ACTIVITY_BOUNDS_INVALID".into()); } let connection = super::web_novel_workspace::open_database(path)?; @@ -376,13 +391,30 @@ fn record_activity_at(path: &Path, input: RecordWritingActivityInput) -> Result< "INSERT OR IGNORE INTO web_novel_writing_activity( activity_id,work_id,chapter_id,local_date,active_ms,words_delta,created_at_unix_ms ) VALUES(?1,?2,?3,?4,?5,?6,?7)", - params![input.activity_id, input.work_id, input.chapter_id, input.local_date, input.active_ms, input.words_delta, now_ms()], + params![ + input.activity_id, + input.work_id, + input.chapter_id, + input.local_date, + input.active_ms, + input.words_delta, + now_ms() + ], ) .map_err(db_write)?; - author_snapshot_at(path, &AuthorSnapshotInput { work_id: input.work_id, local_date: input.local_date }) + author_snapshot_at( + path, + &AuthorSnapshotInput { + work_id: input.work_id, + local_date: input.local_date, + }, + ) } -fn create_inspiration_at(path: &Path, input: CreateInspirationInput) -> Result { +fn create_inspiration_at( + path: &Path, + input: CreateInspirationInput, +) -> Result { let content = input.content.trim(); if content.is_empty() || content.len() > MAX_INSPIRATION_BYTES || input.tags.len() > 30 { return Err("HOLOLAKE_WEBNOVEL_INSPIRATION_INVALID".into()); @@ -405,7 +437,10 @@ fn create_inspiration_at(path: &Path, input: CreateInspirationInput) -> Result Result { +fn set_inspiration_status_at( + path: &Path, + input: SetInspirationStatusInput, +) -> Result { let status = input.status.trim().to_ascii_uppercase(); if !matches!(status.as_str(), "INBOX" | "USED" | "ARCHIVED") { return Err("HOLOLAKE_WEBNOVEL_INSPIRATION_STATUS_INVALID".into()); @@ -424,7 +459,10 @@ fn set_inspiration_status_at(path: &Path, input: SetInspirationStatusInput) -> R query_inspiration(&connection, &input.inspiration_id) } -fn search_full_text_at(path: &Path, input: SearchFullTextInput) -> Result { +fn search_full_text_at( + path: &Path, + input: SearchFullTextInput, +) -> Result { let query = input.query.trim().to_owned(); if query.is_empty() || query.chars().count() > 100 { return Err("HOLOLAKE_WEBNOVEL_SEARCH_QUERY_INVALID".into()); @@ -439,7 +477,12 @@ fn search_full_text_at(path: &Path, input: SearchFullTextInput) -> Result(0)?, row.get::<_, String>(1)?, row.get::<_, String>(2)?, row.get::<_, String>(3)?)) + Ok(( + row.get::<_, String>(0)?, + row.get::<_, String>(1)?, + row.get::<_, String>(2)?, + row.get::<_, String>(3)?, + )) }) .map_err(db_read)? .collect::, _>>() @@ -454,7 +497,10 @@ fn search_full_text_at(path: &Path, input: SearchFullTextInput) -> Result Result Result { +fn format_chapter_at( + path: &Path, + input: FormatChapterInput, +) -> Result { let connection = super::web_novel_workspace::open_database(path)?; let current = connection .query_row( @@ -475,12 +529,23 @@ fn format_chapter_at(path: &Path, input: FormatChapterInput) -> Result Result Result(0)?, row.get::<_, i64>(1)?))) + .query_map([&input.work_id], |row| { + Ok((row.get::<_, String>(0)?, row.get::<_, i64>(1)?)) + }) .map_err(db_read)? .collect::, _>>() .map_err(db_read)?; @@ -549,7 +628,13 @@ fn upsert_shot_at(path: &Path, input: UpsertShotInput) -> Result 3600 { return Err("HOLOLAKE_WEBNOVEL_SHOT_DURATION_INVALID".into()); } - for value in [&input.action, &input.dialogue, &input.visual_prompt, &input.image_prompt, &input.video_prompt] { + for value in [ + &input.action, + &input.dialogue, + &input.visual_prompt, + &input.image_prompt, + &input.video_prompt, + ] { if value.len() > MAX_SHOT_TEXT_BYTES { return Err("HOLOLAKE_WEBNOVEL_SHOT_TEXT_TOO_LARGE".into()); } @@ -558,15 +643,26 @@ fn upsert_shot_at(path: &Path, input: UpsertShotInput) -> Result = connection - .query_row("SELECT position,revision FROM web_novel_shots WHERE shot_id=?1 AND archived=0", [&shot_id], |row| Ok((row.get(0)?, row.get(1)?))) - .optional().map_err(db_read)?; + .query_row( + "SELECT position,revision FROM web_novel_shots WHERE shot_id=?1 AND archived=0", + [&shot_id], + |row| Ok((row.get(0)?, row.get(1)?)), + ) + .optional() + .map_err(db_read)?; let now = now_ms(); if let Some((_, revision)) = existing { if input.expected_revision != Some(revision) { @@ -595,17 +691,26 @@ fn upsert_shot_at(path: &Path, input: UpsertShotInput) -> Result Result, String> { +fn query_inspirations( + connection: &Connection, + work_id: &str, +) -> Result, String> { let mut statement = connection.prepare( "SELECT inspiration_id,work_id,chapter_id,content,tags_json,status,created_at_unix_ms,updated_at_unix_ms FROM web_novel_inspirations WHERE work_id=?1 AND status!='ARCHIVED' ORDER BY updated_at_unix_ms DESC LIMIT 100" ).map_err(db_read)?; - let rows = statement.query_map([work_id], map_inspiration).map_err(db_read)? - .collect::, _>>().map_err(db_read)?; + let rows = statement + .query_map([work_id], map_inspiration) + .map_err(db_read)? + .collect::, _>>() + .map_err(db_read)?; Ok(rows) } -fn query_inspiration(connection: &Connection, inspiration_id: &str) -> Result { +fn query_inspiration( + connection: &Connection, + inspiration_id: &str, +) -> Result { connection.query_row( "SELECT inspiration_id,work_id,chapter_id,content,tags_json,status,created_at_unix_ms,updated_at_unix_ms FROM web_novel_inspirations WHERE inspiration_id=?1", [inspiration_id], map_inspiration @@ -615,9 +720,14 @@ fn query_inspiration(connection: &Connection, inspiration_id: &str) -> Result) -> rusqlite::Result { let tags_json: String = row.get(4)?; Ok(WebNovelInspiration { - inspiration_id: row.get(0)?, work_id: row.get(1)?, chapter_id: row.get(2)?, content: row.get(3)?, - tags: serde_json::from_str(&tags_json).unwrap_or_default(), status: row.get(5)?, - created_at_unix_ms: row.get(6)?, updated_at_unix_ms: row.get(7)?, + inspiration_id: row.get(0)?, + work_id: row.get(1)?, + chapter_id: row.get(2)?, + content: row.get(3)?, + tags: serde_json::from_str(&tags_json).unwrap_or_default(), + status: row.get(5)?, + created_at_unix_ms: row.get(6)?, + updated_at_unix_ms: row.get(7)?, }) } @@ -627,8 +737,11 @@ fn query_shots(connection: &Connection, work_id: &str) -> Result, _>>().map_err(db_read)?; + let rows = statement + .query_map([work_id], map_shot) + .map_err(db_read)? + .collect::, _>>() + .map_err(db_read)?; Ok(rows) } @@ -642,24 +755,54 @@ fn query_shot(connection: &Connection, shot_id: &str) -> Result) -> rusqlite::Result { Ok(WebNovelShot { - shot_id: row.get(0)?, work_id: row.get(1)?, chapter_id: row.get(2)?, position: row.get(3)?, - shot_number: row.get(4)?, shot_size: row.get(5)?, camera_movement: row.get(6)?, location: row.get(7)?, - time_of_day: row.get(8)?, action: row.get(9)?, dialogue: row.get(10)?, duration_seconds: row.get(11)?, - visual_prompt: row.get(12)?, image_prompt: row.get(13)?, video_prompt: row.get(14)?, revision: row.get(15)?, - created_at_unix_ms: row.get(16)?, updated_at_unix_ms: row.get(17)?, + shot_id: row.get(0)?, + work_id: row.get(1)?, + chapter_id: row.get(2)?, + position: row.get(3)?, + shot_number: row.get(4)?, + shot_size: row.get(5)?, + camera_movement: row.get(6)?, + location: row.get(7)?, + time_of_day: row.get(8)?, + action: row.get(9)?, + dialogue: row.get(10)?, + duration_seconds: row.get(11)?, + visual_prompt: row.get(12)?, + image_prompt: row.get(13)?, + video_prompt: row.get(14)?, + revision: row.get(15)?, + created_at_unix_ms: row.get(16)?, + updated_at_unix_ms: row.get(17)?, }) } -fn ensure_scope(connection: &Connection, work_id: &str, chapter_id: Option<&str>) -> Result<(), String> { - let work_exists: bool = connection.query_row( - "SELECT EXISTS(SELECT 1 FROM web_novel_works WHERE work_id=?1 AND archived=0)", [work_id], |row| row.get(0) - ).map_err(db_read)?; - if !work_exists { return Err("HOLOLAKE_WEBNOVEL_WORK_NOT_FOUND".into()); } +fn ensure_scope( + connection: &Connection, + work_id: &str, + chapter_id: Option<&str>, +) -> Result<(), String> { + let work_exists: bool = connection + .query_row( + "SELECT EXISTS(SELECT 1 FROM web_novel_works WHERE work_id=?1 AND archived=0)", + [work_id], + |row| row.get(0), + ) + .map_err(db_read)?; + if !work_exists { + return Err("HOLOLAKE_WEBNOVEL_WORK_NOT_FOUND".into()); + } if let Some(chapter_id) = chapter_id { - let chapter_work: Option = connection.query_row( - "SELECT work_id FROM web_novel_chapters WHERE chapter_id=?1 AND archived=0", [chapter_id], |row| row.get(0) - ).optional().map_err(db_read)?; - if chapter_work.as_deref() != Some(work_id) { return Err("HOLOLAKE_WEBNOVEL_CHAPTER_SCOPE_MISMATCH".into()); } + let chapter_work: Option = connection + .query_row( + "SELECT work_id FROM web_novel_chapters WHERE chapter_id=?1 AND archived=0", + [chapter_id], + |row| row.get(0), + ) + .optional() + .map_err(db_read)?; + if chapter_work.as_deref() != Some(work_id) { + return Err("HOLOLAKE_WEBNOVEL_CHAPTER_SCOPE_MISMATCH".into()); + } } Ok(()) } @@ -692,27 +835,46 @@ pub(crate) fn format_content(content: &str, work_kind: &str, preset: &str) -> St } fn is_heading(line: &str) -> bool { - (line.starts_with('第') && (line.contains('章') || line.contains('卷') || line.contains('节') || line.contains('集'))) - || line.starts_with("场次:") || line.starts_with("人物:") || line.starts_with('【') + (line.starts_with('第') + && (line.contains('章') + || line.contains('卷') + || line.contains('节') + || line.contains('集'))) + || line.starts_with("场次:") + || line.starts_with("人物:") + || line.starts_with('【') } fn make_snippet(source: &str, query: &str) -> String { let chars: Vec = source.chars().collect(); let needle: Vec = query.chars().collect(); - let found = chars.windows(needle.len()).position(|window| window == needle.as_slice()).unwrap_or(0); + let found = chars + .windows(needle.len()) + .position(|window| window == needle.as_slice()) + .unwrap_or(0); let start = found.saturating_sub(35); let end = (found + needle.len() + 55).min(chars.len()); let mut snippet: String = chars[start..end].iter().collect(); snippet = snippet.replace('\n', " "); - if start > 0 { snippet.insert(0, '…'); } - if end < chars.len() { snippet.push('…'); } + if start > 0 { + snippet.insert(0, '…'); + } + if end < chars.len() { + snippet.push('…'); + } snippet } fn validate_local_date(value: &str) -> Result<(), String> { let bytes = value.as_bytes(); - if bytes.len() == 10 && bytes[4] == b'-' && bytes[7] == b'-' - && bytes.iter().enumerate().all(|(i, b)| i == 4 || i == 7 || b.is_ascii_digit()) { + if bytes.len() == 10 + && bytes[4] == b'-' + && bytes[7] == b'-' + && bytes + .iter() + .enumerate() + .all(|(i, b)| i == 4 || i == 7 || b.is_ascii_digit()) + { Ok(()) } else { Err("HOLOLAKE_WEBNOVEL_LOCAL_DATE_INVALID".into()) @@ -720,7 +882,10 @@ fn validate_local_date(value: &str) -> Result<(), String> { } fn now_ms() -> i64 { - std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap_or_default().as_millis() as i64 + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_millis() as i64 } fn db_read(error: rusqlite::Error) -> String { @@ -736,13 +901,20 @@ mod tests { use super::*; use tempfile::tempdir; - fn create_work(path: &Path, kind: &str) -> super::super::web_novel_workspace::WebNovelWorkDetail { + fn create_work( + path: &Path, + kind: &str, + ) -> super::super::web_novel_workspace::WebNovelWorkDetail { super::super::web_novel_workspace::create_work_at( path, super::super::web_novel_workspace::CreateWebNovelWorkInput { - title: "作者底座测试".into(), pen_name: "冰朔".into(), genre: "测试".into(), work_kind: kind.into(), + title: "作者底座测试".into(), + pen_name: "冰朔".into(), + genre: "测试".into(), + work_kind: kind.into(), }, - ).unwrap() + ) + .unwrap() } #[test] @@ -753,28 +925,74 @@ mod tests { let chapter = super::super::web_novel_workspace::create_chapter_at( &path, super::super::web_novel_workspace::CreateWebNovelChapterInput { - work_id: detail.work.work_id.clone(), volume_id: detail.volumes[0].volume_id.clone(), title: "第一章".into(), + work_id: detail.work.work_id.clone(), + volume_id: detail.volumes[0].volume_id.clone(), + title: "第一章".into(), }, - ).unwrap(); + ) + .unwrap(); let chapter = super::super::web_novel_workspace::save_chapter_at( &path, super::super::web_novel_workspace::SaveWebNovelChapterInput { - chapter_id: chapter.chapter_id.clone(), title: chapter.title, synopsis: "开场".into(), - content: "第一段\n\n第二段有线索".into(), expected_revision: chapter.revision, save_reason: Some("TEST".into()), + chapter_id: chapter.chapter_id.clone(), + title: chapter.title, + synopsis: "开场".into(), + content: "第一段\n\n第二段有线索".into(), + expected_revision: chapter.revision, + save_reason: Some("TEST".into()), }, - ).unwrap(); - let formatted = format_chapter_at(&path, FormatChapterInput { - chapter_id: chapter.chapter_id.clone(), - expected_revision: chapter.revision, - preset: Some("NOVEL".into()), - }).unwrap(); + ) + .unwrap(); + let formatted = format_chapter_at( + &path, + FormatChapterInput { + chapter_id: chapter.chapter_id.clone(), + expected_revision: chapter.revision, + preset: Some("NOVEL".into()), + }, + ) + .unwrap(); assert!(formatted.content.starts_with("  第一段")); assert!(formatted.content.contains("\n\n  第二段")); - create_inspiration_at(&path, CreateInspirationInput { work_id: detail.work.work_id.clone(), chapter_id: Some(chapter.chapter_id.clone()), content: "让钥匙提前出现".into(), tags: vec!["伏笔".into()] }).unwrap(); - record_activity_at(&path, RecordWritingActivityInput { activity_id: "WN-ACT-test".into(), work_id: detail.work.work_id.clone(), chapter_id: chapter.chapter_id.clone(), local_date: "2026-08-18".into(), active_ms: 30_000, words_delta: 8 }).unwrap(); - let search = search_full_text_at(&path, SearchFullTextInput { work_id: detail.work.work_id.clone(), query: "线索".into() }).unwrap(); + create_inspiration_at( + &path, + CreateInspirationInput { + work_id: detail.work.work_id.clone(), + chapter_id: Some(chapter.chapter_id.clone()), + content: "让钥匙提前出现".into(), + tags: vec!["伏笔".into()], + }, + ) + .unwrap(); + record_activity_at( + &path, + RecordWritingActivityInput { + activity_id: "WN-ACT-test".into(), + work_id: detail.work.work_id.clone(), + chapter_id: chapter.chapter_id.clone(), + local_date: "2026-08-18".into(), + active_ms: 30_000, + words_delta: 8, + }, + ) + .unwrap(); + let search = search_full_text_at( + &path, + SearchFullTextInput { + work_id: detail.work.work_id.clone(), + query: "线索".into(), + }, + ) + .unwrap(); assert_eq!(search.chapter_count, 1); - let snapshot = author_snapshot_at(&path, &AuthorSnapshotInput { work_id: detail.work.work_id, local_date: "2026-08-18".into() }).unwrap(); + let snapshot = author_snapshot_at( + &path, + &AuthorSnapshotInput { + work_id: detail.work.work_id, + local_date: "2026-08-18".into(), + }, + ) + .unwrap(); assert_eq!(snapshot.today_active_ms, 30_000); assert_eq!(snapshot.today_words, 8); assert_eq!(snapshot.inspiration_count, 1); @@ -788,16 +1006,34 @@ mod tests { let chapter = super::super::web_novel_workspace::create_chapter_at( &path, super::super::web_novel_workspace::CreateWebNovelChapterInput { - work_id: detail.work.work_id.clone(), volume_id: detail.volumes[0].volume_id.clone(), title: "第1集".into(), + work_id: detail.work.work_id.clone(), + volume_id: detail.volumes[0].volume_id.clone(), + title: "第1集".into(), }, - ).unwrap(); + ) + .unwrap(); assert!(chapter.content.contains("场次:1-1")); - let shot = upsert_shot_at(&path, UpsertShotInput { - work_id: detail.work.work_id, chapter_id: chapter.chapter_id, shot_id: None, shot_number: "1".into(), - shot_size: "近景".into(), camera_movement: "推".into(), location: "侯府".into(), time_of_day: "夜".into(), - action: "女主抬眼".into(), dialogue: "你终于来了。".into(), duration_seconds: 4, - visual_prompt: "烛火冷夜".into(), image_prompt: "古装近景".into(), video_prompt: "缓慢推镜".into(), expected_revision: None, - }).unwrap(); + let shot = upsert_shot_at( + &path, + UpsertShotInput { + work_id: detail.work.work_id, + chapter_id: chapter.chapter_id, + shot_id: None, + shot_number: "1".into(), + shot_size: "近景".into(), + camera_movement: "推".into(), + location: "侯府".into(), + time_of_day: "夜".into(), + action: "女主抬眼".into(), + dialogue: "你终于来了。".into(), + duration_seconds: 4, + visual_prompt: "烛火冷夜".into(), + image_prompt: "古装近景".into(), + video_prompt: "缓慢推镜".into(), + expected_revision: None, + }, + ) + .unwrap(); assert_eq!(shot.revision, 1); } } diff --git a/product-source/hololake-native-desktop/src-tauri/src/web_novel_import.rs b/product-source/hololake-native-desktop/src-tauri/src/web_novel_import.rs index aef7949ac..d70f2d74e 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/web_novel_import.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/web_novel_import.rs @@ -19,12 +19,19 @@ use tauri_plugin_dialog::DialogExt; use uuid::Uuid; use zip::ZipArchive; +const MODULE_NUMBER: &str = "HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001"; +const ADAPTER: &str = "web-novel-workbench-v1"; + +fn require_active(app: &AppHandle) -> Result<(), String> { + crate::module_package_runtime::require_active_module_adapter(app, MODULE_NUMBER, ADAPTER) +} + const MAX_SOURCE_BYTES: u64 = 30 * 1024 * 1024; const MAX_SECTION_BYTES: usize = 4_000_000; const MAX_SYNOPSIS_BYTES: usize = 40_000; #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ImportSection { pub ordinal: usize, pub title: String, @@ -34,7 +41,7 @@ pub struct ImportSection { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ImportSectionPreview { pub ordinal: usize, pub title: String, @@ -44,7 +51,7 @@ pub struct ImportSectionPreview { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WebNovelImportPreview { pub import_id: String, pub source_filename: String, @@ -62,7 +69,7 @@ pub struct WebNovelImportPreview { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct CommitImportInput { pub import_id: String, pub target_mode: String, @@ -73,7 +80,7 @@ pub struct CommitImportInput { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WebNovelImportReceipt { pub state: String, pub import_id: String, @@ -102,10 +109,10 @@ struct ParsedDocument { sections: Vec, } -#[tauri::command] pub async fn inspect_web_novel_document_from_dialog( app: AppHandle, ) -> Result, String> { + require_active(&app)?; let selected = app .dialog() .file() @@ -125,11 +132,11 @@ pub async fn inspect_web_novel_document_from_dialog( .map(Some) } -#[tauri::command] pub async fn commit_web_novel_document_import( app: AppHandle, input: CommitImportInput, ) -> Result { + require_active(&app)?; let database = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || commit_import_at(&database, input)) .await @@ -346,11 +353,8 @@ pub(crate) fn commit_import_at( .map_err(db_read)?; for (position, section) in sections.iter().enumerate() { let chapter_id = format!("WN-CH-{}", Uuid::new_v4()); - let formatted_content = super::web_novel_author::format_content( - §ion.body, - &work_kind, - &staged.2, - ); + let formatted_content = + super::web_novel_author::format_content(§ion.body, &work_kind, &staged.2); let formatted_word_count = count_words(&formatted_content); tx.execute( "INSERT INTO web_novel_chapters( @@ -741,7 +745,8 @@ mod tests { pen_name: "测试作者".into(), genre: "测试".into(), }, - ).unwrap(); + ) + .unwrap(); let connection = super::super::web_novel_workspace::open_database(&database).unwrap(); let (content, revision): (String, i64) = connection.query_row( "SELECT content,revision FROM web_novel_chapters WHERE work_id=?1 ORDER BY position LIMIT 1", diff --git a/product-source/hololake-native-desktop/src-tauri/src/web_novel_modules.rs b/product-source/hololake-native-desktop/src-tauri/src/web_novel_modules.rs index 31e11eb43..c7780b97c 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/web_novel_modules.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/web_novel_modules.rs @@ -1,8 +1,10 @@ -//! 网文作者官方模块运行时。 +//! 网文作者官方模块数据适配器。 //! -//! 模块包是不可变、内置签名边界内的官方发行制品。安装把经过散列核验的包写入 -//! 当前账号缓存;挂载状态、模块数据和链式回执写入当前账号网文 SQLite。卸载只清理 -//! 可重新取得的程序缓存,不删除作品、模块数据或回执。 +//! 生产安装、签名验证、挂载、自检和回执只由 `module_package_runtime` 执行。 +//! 本文件保留 0.4.1 安装器作为迁移回归测试夹具;它没有编号 IPC 路由,不能成为 +//! 第二套生产生命周期权威。 + +#![allow(dead_code)] use ring::digest::{digest, SHA256}; use rusqlite::{params, Connection, OptionalExtension, TransactionBehavior}; @@ -17,10 +19,17 @@ use uuid::Uuid; use zip::write::FileOptions; use zip::{CompressionMethod, ZipWriter}; -const OUTLINE: &str = "HL-MOD-WEBNOVEL-OUTLINE-001"; -const GRID: &str = "HL-MOD-WEBNOVEL-GRID-002"; -const STORYWORLD: &str = "HL-MOD-WEBNOVEL-STORYWORLD-003"; -const DELIVERY: &str = "HL-MOD-WEBNOVEL-DELIVERY-004"; +const BASE: &str = "HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001"; +const ADAPTER: &str = "web-novel-workbench-v1"; + +fn require_module_active(app: &AppHandle, module_number: &str) -> Result<(), String> { + crate::module_package_runtime::require_active_module_adapter(app, module_number, ADAPTER) +} + +const OUTLINE: &str = "HLP-MOD-OFFICIAL-WEB-NOVEL-OUTLINE-0001"; +const GRID: &str = "HLP-MOD-OFFICIAL-WEB-NOVEL-GRID-0001"; +const STORYWORLD: &str = "HLP-MOD-OFFICIAL-WEB-NOVEL-STORYWORLD-0001"; +const DELIVERY: &str = "HLP-MOD-OFFICIAL-WEB-NOVEL-DELIVERY-0001"; const VERSION: &str = "1.0.0"; struct Package { @@ -39,7 +48,7 @@ fn packages() -> [Package; 4] { description: "场景、情节拍、目标冲突结果、钩子与伏笔追踪。", sha256: "6d31d3f530862000bd84a8f5dc73bcb9cf0d5738219f7a287562d4f4bcfa5318", bytes: include_bytes!( - "../../official-modules/web-novel/HL-MOD-WEBNOVEL-OUTLINE-001/1.0.0/module.json" + "../../fixtures/web-novel-legacy-0.4.1/HL-MOD-WEBNOVEL-OUTLINE-001.json" ), }, Package { @@ -48,7 +57,7 @@ fn packages() -> [Package; 4] { description: "由同一场景对象生成可编辑表格、自定义字段与分组情节板。", sha256: "2e34e0337c51dbbdd1efdd807be71e69a0cb2a1745624e1f51b452b7b54f1553", bytes: include_bytes!( - "../../official-modules/web-novel/HL-MOD-WEBNOVEL-GRID-002/1.0.0/module.json" + "../../fixtures/web-novel-legacy-0.4.1/HL-MOD-WEBNOVEL-GRID-002.json" ), }, Package { @@ -57,7 +66,7 @@ fn packages() -> [Package; 4] { description: "故事时间、场景与人物地点物品的真实关系投影。", sha256: "d486719500ddfc08054d9c9bb6d3683afe5547a2b24c9b04d0f899ac66094d2f", bytes: include_bytes!( - "../../official-modules/web-novel/HL-MOD-WEBNOVEL-STORYWORLD-003/1.0.0/module.json" + "../../fixtures/web-novel-legacy-0.4.1/HL-MOD-WEBNOVEL-STORYWORLD-003.json" ), }, Package { @@ -66,7 +75,7 @@ fn packages() -> [Package; 4] { description: "章节版本读回与恢复,以及 TXT、DOCX、EPUB、JSON 真实导出。", sha256: "4af1d4610826edd29759bd0dc12b4b0c04fd2ebebb4ebc362d47d762810989e8", bytes: include_bytes!( - "../../official-modules/web-novel/HL-MOD-WEBNOVEL-DELIVERY-004/1.0.0/module.json" + "../../fixtures/web-novel-legacy-0.4.1/HL-MOD-WEBNOVEL-DELIVERY-004.json" ), }, ] @@ -109,19 +118,19 @@ pub struct AuthorModuleMarketplaceSnapshot { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ModuleActionInput { pub module_id: String, } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ReadAuthorModuleDataInput { pub work_id: String, } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct AuthorScene { pub scene_id: String, pub work_id: String, @@ -141,7 +150,7 @@ pub struct AuthorScene { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct AuthorBeat { pub beat_id: String, pub scene_id: String, @@ -154,7 +163,7 @@ pub struct AuthorBeat { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct StoryFieldDefinition { pub field_id: String, pub work_id: String, @@ -166,7 +175,7 @@ pub struct StoryFieldDefinition { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct StoryFieldValue { pub field_id: String, pub scene_id: String, @@ -175,7 +184,7 @@ pub struct StoryFieldValue { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct StoryTimelineEvent { pub event_id: String, pub work_id: String, @@ -192,7 +201,7 @@ pub struct StoryTimelineEvent { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct SceneEntityLink { pub link_id: String, pub work_id: String, @@ -203,7 +212,7 @@ pub struct SceneEntityLink { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ChapterVersionSummary { pub version_id: String, pub chapter_id: String, @@ -228,7 +237,7 @@ pub struct AuthorModuleData { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct UpsertAuthorSceneInput { pub work_id: String, pub scene_id: Option, @@ -247,7 +256,7 @@ pub struct UpsertAuthorSceneInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct UpsertAuthorBeatInput { pub scene_id: String, pub beat_id: Option, @@ -259,7 +268,7 @@ pub struct UpsertAuthorBeatInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct UpsertStoryFieldDefinitionInput { pub work_id: String, pub field_id: Option, @@ -270,7 +279,7 @@ pub struct UpsertStoryFieldDefinitionInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct UpsertStoryFieldValueInput { pub field_id: String, pub scene_id: String, @@ -279,7 +288,7 @@ pub struct UpsertStoryFieldValueInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct UpsertTimelineEventInput { pub work_id: String, pub event_id: Option, @@ -296,7 +305,7 @@ pub struct UpsertTimelineEventInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct LinkSceneEntityInput { pub work_id: String, pub scene_id: String, @@ -306,13 +315,13 @@ pub struct LinkSceneEntityInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct RestoreChapterVersionInput { pub version_id: String, } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ExportAuthorDeliveryInput { pub work_id: String, pub format: String, @@ -328,7 +337,6 @@ pub struct AuthorDeliveryReceipt { pub package_sha256: String, } -#[tauri::command] pub async fn get_web_novel_author_module_marketplace( app: AppHandle, ) -> Result { @@ -338,7 +346,6 @@ pub async fn get_web_novel_author_module_marketplace( .map_err(|error| format!("HOLOLAKE_WEBNOVEL_MODULE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn install_web_novel_author_module( app: AppHandle, input: ModuleActionInput, @@ -350,7 +357,6 @@ pub async fn install_web_novel_author_module( .map_err(|error| format!("HOLOLAKE_WEBNOVEL_MODULE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn mount_web_novel_author_module( app: AppHandle, input: ModuleActionInput, @@ -361,7 +367,6 @@ pub async fn mount_web_novel_author_module( .map_err(|error| format!("HOLOLAKE_WEBNOVEL_MODULE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn unmount_web_novel_author_module( app: AppHandle, input: ModuleActionInput, @@ -372,7 +377,6 @@ pub async fn unmount_web_novel_author_module( .map_err(|error| format!("HOLOLAKE_WEBNOVEL_MODULE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn uninstall_web_novel_author_module( app: AppHandle, input: ModuleActionInput, @@ -384,99 +388,99 @@ pub async fn uninstall_web_novel_author_module( .map_err(|error| format!("HOLOLAKE_WEBNOVEL_MODULE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn get_web_novel_author_module_data( app: AppHandle, input: ReadAuthorModuleDataInput, ) -> Result { + require_module_active(&app, BASE)?; let database = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || module_data_at(&database, &input.work_id)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_MODULE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn upsert_web_novel_author_scene( app: AppHandle, input: UpsertAuthorSceneInput, ) -> Result { + require_module_active(&app, OUTLINE)?; let database = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || upsert_scene_at(&database, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_MODULE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn upsert_web_novel_author_beat( app: AppHandle, input: UpsertAuthorBeatInput, ) -> Result { + require_module_active(&app, OUTLINE)?; let database = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || upsert_beat_at(&database, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_MODULE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn upsert_web_novel_story_field_definition( app: AppHandle, input: UpsertStoryFieldDefinitionInput, ) -> Result { + require_module_active(&app, GRID)?; let database = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || upsert_field_definition_at(&database, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_MODULE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn upsert_web_novel_story_field_value( app: AppHandle, input: UpsertStoryFieldValueInput, ) -> Result { + require_module_active(&app, GRID)?; let database = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || upsert_field_value_at(&database, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_MODULE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn upsert_web_novel_timeline_event( app: AppHandle, input: UpsertTimelineEventInput, ) -> Result { + require_module_active(&app, STORYWORLD)?; let database = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || upsert_timeline_at(&database, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_MODULE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn link_web_novel_scene_entity( app: AppHandle, input: LinkSceneEntityInput, ) -> Result { + require_module_active(&app, STORYWORLD)?; let database = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || link_scene_entity_at(&database, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_MODULE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn restore_web_novel_chapter_version( app: AppHandle, input: RestoreChapterVersionInput, ) -> Result { + require_module_active(&app, DELIVERY)?; let database = super::web_novel_workspace::workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || restore_version_at(&database, &input.version_id)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_MODULE_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn export_web_novel_author_delivery( app: AppHandle, input: ExportAuthorDeliveryInput, ) -> Result, String> { + require_module_active(&app, DELIVERY)?; let format = normalize_delivery_format(&input.format)?; let database = super::web_novel_workspace::workspace_database(&app)?; let title = read_work_title(&database, &input.work_id)?; @@ -774,6 +778,7 @@ fn append_receipt( Ok(()) } +#[cfg(test)] fn require_mounted(connection: &Connection, module_id: &str) -> Result<(), String> { let state: Option = connection.query_row("SELECT install_state FROM web_novel_module_installations WHERE module_id=?1 AND self_test_state='PASS'", [module_id], |row| row.get(0)).optional().map_err(db_read)?; if state.as_deref() == Some("MOUNTED") { @@ -783,6 +788,18 @@ fn require_mounted(connection: &Connection, module_id: &str) -> Result<(), Strin } } +#[cfg(not(test))] +fn require_mounted(_connection: &Connection, module_id: &str) -> Result<(), String> { + if matches!(module_id, OUTLINE | GRID | STORYWORLD | DELIVERY) { + // The public adapter entry already performed an exact-number ACTIVE check + // against the shared signed package runtime. Never consult the legacy + // installation table in production. + Ok(()) + } else { + Err("HOLOLAKE_WEBNOVEL_MODULE_UNKNOWN".into()) + } +} + fn module_cache_path(cache: &Path, module_id: &str) -> Result { if !packages().iter().any(|item| item.module_id == module_id) || module_id.contains('/') diff --git a/product-source/hololake-native-desktop/src-tauri/src/web_novel_workspace.rs b/product-source/hololake-native-desktop/src-tauri/src/web_novel_workspace.rs index 7933fbed1..032af3c86 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/web_novel_workspace.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/web_novel_workspace.rs @@ -12,6 +12,13 @@ use tauri::{AppHandle, Manager}; use tauri_plugin_dialog::DialogExt; use uuid::Uuid; +const MODULE_NUMBER: &str = "HLP-MOD-OFFICIAL-WEB-NOVEL-WORKBENCH-0001"; +const ADAPTER: &str = "web-novel-workbench-v1"; + +fn require_active(app: &AppHandle) -> Result<(), String> { + crate::module_package_runtime::require_active_module_adapter(app, MODULE_NUMBER, ADAPTER) +} + const MAX_TITLE_BYTES: usize = 300; const MAX_SYNOPSIS_BYTES: usize = 40_000; const MAX_CHAPTER_BYTES: usize = 4_000_000; @@ -29,7 +36,7 @@ const WORKFLOW_STATES: &[&str] = &[ ]; #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WebNovelWorkSummary { pub work_id: String, pub title: String, @@ -46,7 +53,7 @@ pub struct WebNovelWorkSummary { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WebNovelWork { pub work_id: String, pub title: String, @@ -64,7 +71,7 @@ pub struct WebNovelWork { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WebNovelVolume { pub volume_id: String, pub work_id: String, @@ -76,7 +83,7 @@ pub struct WebNovelVolume { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WebNovelChapterSummary { pub chapter_id: String, pub volume_id: String, @@ -92,7 +99,7 @@ pub struct WebNovelChapterSummary { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WebNovelChapter { pub chapter_id: String, pub volume_id: String, @@ -110,7 +117,7 @@ pub struct WebNovelChapter { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WebNovelStoryEntity { pub entity_id: String, pub work_id: String, @@ -125,7 +132,7 @@ pub struct WebNovelStoryEntity { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WebNovelStoryRelation { pub relation_id: String, pub work_id: String, @@ -137,7 +144,7 @@ pub struct WebNovelStoryRelation { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WebNovelForeshadow { pub foreshadow_id: String, pub work_id: String, @@ -152,7 +159,7 @@ pub struct WebNovelForeshadow { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WebNovelReviewNote { pub note_id: String, pub work_id: String, @@ -164,7 +171,7 @@ pub struct WebNovelReviewNote { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WebNovelWorkflowEvent { pub event_id: String, pub work_id: String, @@ -176,7 +183,7 @@ pub struct WebNovelWorkflowEvent { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WebNovelCheckpointSummary { pub checkpoint_id: String, pub work_id: String, @@ -188,7 +195,7 @@ pub struct WebNovelCheckpointSummary { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct WebNovelMetric { pub metric_id: String, pub work_id: String, @@ -280,7 +287,7 @@ pub struct WebNovelExportReceipt { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct CreateWebNovelWorkInput { pub title: String, pub pen_name: String, @@ -290,13 +297,13 @@ pub struct CreateWebNovelWorkInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ReadWebNovelWorkInput { pub work_id: String, } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct SaveWebNovelWorkInput { pub work_id: String, pub title: String, @@ -312,14 +319,14 @@ pub struct SaveWebNovelWorkInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct CreateWebNovelVolumeInput { pub work_id: String, pub title: String, } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct CreateWebNovelChapterInput { pub work_id: String, pub volume_id: String, @@ -327,13 +334,13 @@ pub struct CreateWebNovelChapterInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ReadWebNovelChapterInput { pub chapter_id: String, } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct SaveWebNovelChapterInput { pub chapter_id: String, pub title: String, @@ -344,7 +351,7 @@ pub struct SaveWebNovelChapterInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct TransitionWebNovelChapterInput { pub chapter_id: String, pub to_status: String, @@ -354,7 +361,7 @@ pub struct TransitionWebNovelChapterInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct CreateWebNovelCheckpointInput { pub work_id: String, pub name: String, @@ -362,13 +369,13 @@ pub struct CreateWebNovelCheckpointInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct RestoreWebNovelCheckpointInput { pub checkpoint_id: String, } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct UpsertWebNovelStoryEntityInput { pub work_id: String, pub entity_id: Option, @@ -381,7 +388,7 @@ pub struct UpsertWebNovelStoryEntityInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct CreateWebNovelStoryRelationInput { pub work_id: String, pub source_entity_id: String, @@ -391,7 +398,7 @@ pub struct CreateWebNovelStoryRelationInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct UpsertWebNovelForeshadowInput { pub work_id: String, pub foreshadow_id: Option, @@ -404,7 +411,7 @@ pub struct UpsertWebNovelForeshadowInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct CreateWebNovelReviewNoteInput { pub work_id: String, pub chapter_id: String, @@ -412,13 +419,13 @@ pub struct CreateWebNovelReviewNoteInput { } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct ResolveWebNovelReviewNoteInput { pub note_id: String, } #[derive(Clone, Debug, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct SaveWebNovelMetricInput { pub work_id: String, pub metric_date: String, @@ -432,7 +439,7 @@ pub struct SaveWebNovelMetricInput { } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", deny_unknown_fields)] struct CheckpointChapter { chapter_id: String, title: String, @@ -443,208 +450,208 @@ struct CheckpointChapter { position: i64, } -#[tauri::command] pub async fn get_web_novel_workspace_snapshot( app: AppHandle, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || snapshot_at(&path)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn create_web_novel_work( app: AppHandle, input: CreateWebNovelWorkInput, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || create_work_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn read_web_novel_work( app: AppHandle, input: ReadWebNovelWorkInput, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || read_work_at(&path, &input.work_id)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn save_web_novel_work( app: AppHandle, input: SaveWebNovelWorkInput, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || save_work_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn create_web_novel_volume( app: AppHandle, input: CreateWebNovelVolumeInput, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || create_volume_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn create_web_novel_chapter( app: AppHandle, input: CreateWebNovelChapterInput, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || create_chapter_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn read_web_novel_chapter( app: AppHandle, input: ReadWebNovelChapterInput, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || read_chapter_at(&path, &input.chapter_id)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn save_web_novel_chapter( app: AppHandle, input: SaveWebNovelChapterInput, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || save_chapter_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn transition_web_novel_chapter( app: AppHandle, input: TransitionWebNovelChapterInput, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || transition_chapter_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn create_web_novel_checkpoint( app: AppHandle, input: CreateWebNovelCheckpointInput, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || create_checkpoint_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn restore_web_novel_checkpoint( app: AppHandle, input: RestoreWebNovelCheckpointInput, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || restore_checkpoint_at(&path, &input.checkpoint_id)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn upsert_web_novel_story_entity( app: AppHandle, input: UpsertWebNovelStoryEntityInput, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || upsert_entity_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn create_web_novel_story_relation( app: AppHandle, input: CreateWebNovelStoryRelationInput, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || create_relation_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn upsert_web_novel_foreshadow( app: AppHandle, input: UpsertWebNovelForeshadowInput, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || upsert_foreshadow_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn create_web_novel_review_note( app: AppHandle, input: CreateWebNovelReviewNoteInput, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || create_review_note_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn resolve_web_novel_review_note( app: AppHandle, input: ResolveWebNovelReviewNoteInput, ) -> Result<(), String> { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || resolve_review_note_at(&path, &input.note_id)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn save_web_novel_metric( app: AppHandle, input: SaveWebNovelMetricInput, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || save_metric_at(&path, input)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn run_web_novel_continuity_audit( app: AppHandle, input: ReadWebNovelWorkInput, ) -> Result { + require_active(&app)?; let path = workspace_database(&app)?; tauri::async_runtime::spawn_blocking(move || continuity_audit_at(&path, &input.work_id)) .await .map_err(|error| format!("HOLOLAKE_WEBNOVEL_JOIN_FAILED: {error}"))? } -#[tauri::command] pub async fn export_web_novel_markdown( app: AppHandle, input: ReadWebNovelWorkInput, ) -> Result, String> { + require_active(&app)?; let database = workspace_database(&app)?; let detail = read_work_at(&database, &input.work_id)?; let filename = format!("{}.md", safe_filename(&detail.work.title)); @@ -687,8 +694,12 @@ fn migrate_acceptance_workspace_if_needed( .path() .app_data_dir() .map_err(|error| format!("HOLOLAKE_APP_DATA_UNAVAILABLE: {error}"))?; - let Some(app_data_parent) = current_app_data.parent() else { return Ok(()); }; - let Some(account_root) = current_namespace_root.parent() else { return Ok(()); }; + let Some(app_data_parent) = current_app_data.parent() else { + return Ok(()); + }; + let Some(account_root) = current_namespace_root.parent() else { + return Ok(()); + }; let relative_account = match account_root.strip_prefix(¤t_app_data) { Ok(value) => value, Err(_) => return Ok(()), @@ -744,7 +755,8 @@ fn copy_directory_without_overwrite(source: &Path, destination: &Path) -> Result for entry in fs::read_dir(source) .map_err(|error| format!("HOLOLAKE_WEBNOVEL_LEGACY_MODULE_READ_FAILED: {error}"))? { - let entry = entry.map_err(|error| format!("HOLOLAKE_WEBNOVEL_LEGACY_MODULE_READ_FAILED: {error}"))?; + let entry = entry + .map_err(|error| format!("HOLOLAKE_WEBNOVEL_LEGACY_MODULE_READ_FAILED: {error}"))?; let target = destination.join(entry.file_name()); if entry.path().is_dir() { copy_directory_without_overwrite(&entry.path(), &target)?; @@ -2526,12 +2538,15 @@ fn map_chapter(row: &rusqlite::Row<'_>) -> rusqlite::Result { fn map_entity(row: &rusqlite::Row<'_>) -> rusqlite::Result { let raw: String = row.get(4)?; + let aliases = serde_json::from_str(&raw).map_err(|error| { + rusqlite::Error::FromSqlConversionFailure(4, rusqlite::types::Type::Text, Box::new(error)) + })?; Ok(WebNovelStoryEntity { entity_id: row.get(0)?, work_id: row.get(1)?, entity_type: row.get(2)?, name: row.get(3)?, - aliases: serde_json::from_str(&raw).unwrap_or_default(), + aliases, description: row.get(5)?, first_chapter_id: row.get(6)?, revision: row.get(7)?, @@ -2636,7 +2651,10 @@ fn normalized_workflow(value: &str) -> Result { pub(crate) fn normalized_work_kind(value: &str) -> Result { let normalized = value.trim().to_ascii_uppercase(); - if matches!(normalized.as_str(), "LONG_NOVEL" | "SHORT_NOVEL" | "SHORT_DRAMA") { + if matches!( + normalized.as_str(), + "LONG_NOVEL" | "SHORT_NOVEL" | "SHORT_DRAMA" + ) { Ok(normalized) } else { Err("HOLOLAKE_WEBNOVEL_WORK_KIND_INVALID".into()) @@ -2673,7 +2691,9 @@ fn ensure_column( .map_err(database_read_error)?; if !columns.iter().any(|value| value == column) { connection - .execute_batch(&format!("ALTER TABLE {table} ADD COLUMN {column} {definition};")) + .execute_batch(&format!( + "ALTER TABLE {table} ADD COLUMN {column} {definition};" + )) .map_err(database_write_error)?; } Ok(()) diff --git a/product-source/hololake-native-desktop/src-tauri/src/world_climate.rs b/product-source/hololake-native-desktop/src-tauri/src/world_climate.rs index b978b58bf..ba0a80d9b 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/world_climate.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/world_climate.rs @@ -1,12 +1,20 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later - -//! 语言世界首页的动态气候投影。 -//! 固定域坐标只用于取公开天气;返回值不包含城市或经纬度,界面也不把现实城市冒充世界地名。 +//! Signed dynamic language-world climate projection. +//! +//! The projection may tint the already-approved lake surface. It cannot change +//! domain routing, permissions, facts, or the five-theme layout. Real coordinates +//! are used only for a bounded public-weather query and are never returned to the +//! webview. use serde::{Deserialize, Serialize}; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::sync::{Mutex, OnceLock}; +use std::time::Duration; +use tauri::AppHandle; +const MODULE_NUMBER: &str = "HLP-MOD-OFFICIAL-DYNAMIC-WORLD-SURFACE-0001"; +const ADAPTER: &str = "dynamic-language-world-surface-v1"; const SOURCE: &str = "OPEN_METEO_FORECAST_API"; +const ATTRIBUTION_URL: &str = "https://open-meteo.com/"; +const CACHE_TTL_MS: u64 = 10 * 60 * 1_000; #[derive(Debug, Deserialize)] struct OpenMeteoResponse { @@ -25,36 +33,45 @@ struct OpenMeteoCurrent { #[derive(Clone, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct WorldClimateSnapshot { - pub state: &'static str, - pub active_domain: &'static str, - pub time_phase: &'static str, - pub weather_kind: &'static str, - pub motion_intensity: &'static str, - pub source: &'static str, - pub city_exposed: bool, - pub observed_at_unix_ms: i64, + schema: &'static str, + state: &'static str, + active_domain: &'static str, + time_phase: &'static str, + weather_kind: &'static str, + motion_intensity: &'static str, + source: &'static str, + source_attribution_url: &'static str, + city_exposed: bool, + coordinates_exposed: bool, + routing_or_permission_changed: bool, + observed_at_unix_ms: u64, } -#[tauri::command] -pub async fn get_world_climate() -> Result { - let seconds = SystemTime::now() - .duration_since(UNIX_EPOCH) - .map_err(|error| format!("HOLOLAKE_WORLD_CLIMATE_CLOCK_INVALID: {error}"))? - .as_secs() as i64; - let beijing_seconds = seconds + 8 * 60 * 60; - let day_index = beijing_seconds.div_euclid(86_400); - let hour = beijing_seconds.rem_euclid(86_400) / 3_600; - let domains = ["FIFTH_DOMAIN", "ZERO_SENSE_DOMAIN", "MAIN_DOMAIN", "BRANCH_DOMAIN", "ZERO_DOMAIN"]; - let active_domain = domains[day_index.rem_euclid(domains.len() as i64) as usize]; - let (latitude, longitude) = coordinate_for(active_domain, day_index); - let time_phase = match hour { - 5..=7 => "DAWN", - 8..=16 => "DAY", - 17..=19 => "DUSK", - _ => "NIGHT", - }; - let observed_at_unix_ms = seconds.saturating_mul(1_000); +static CACHE: OnceLock>> = OnceLock::new(); +fn require_active(app: &AppHandle) -> Result<(), String> { + crate::module_package_runtime::require_active_module_adapter(app, MODULE_NUMBER, ADAPTER) +} + +pub async fn get_world_climate(app: AppHandle) -> Result { + require_active(&app)?; + let coordinate = crate::persona_time_authority::get_beijing_time_coordinate()?; + let now_ms = coordinate.unix_ms; + if let Some(snapshot) = cached(now_ms)? { + return Ok(snapshot); + } + let day_index = coordinate.elapsed_beijing_dates_since_guanghu_epoch; + let hour = beijing_hour(&coordinate.beijing_time).unwrap_or(0); + let domains = [ + "FIFTH_DOMAIN", + "ZERO_SENSE_DOMAIN", + "MAIN_DOMAIN", + "BRANCH_DOMAIN", + "ZERO_DOMAIN", + ]; + let active_domain = domains[day_index.rem_euclid(domains.len() as i64) as usize]; + let time_phase = phase_for_hour(hour); + let (latitude, longitude) = coordinate_for(active_domain, day_index); let client = reqwest::Client::builder() .timeout(Duration::from_secs(5)) .redirect(reqwest::redirect::Policy::none()) @@ -63,38 +80,56 @@ pub async fn get_world_climate() -> Result { let url = format!( "https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}¤t=weather_code,is_day,precipitation,cloud_cover,wind_speed_10m&timezone=Asia%2FShanghai&forecast_days=1" ); - let response = match client + let response = client .get(url) - .header(reqwest::header::USER_AGENT, "HoloLake/0.4.1 world-climate") + .header(reqwest::header::USER_AGENT, "HoloLake/0.5.0 world-climate") .send() - .await - { - Ok(response) if response.status().is_success() => response, - _ => return Ok(fallback(active_domain, time_phase, observed_at_unix_ms)), + .await; + let snapshot = match response { + Ok(response) if response.status().is_success() => { + match response.json::().await { + Ok(payload) if valid_current(&payload.current) => { + live_snapshot(active_domain, time_phase, now_ms, payload.current) + } + _ => fallback(active_domain, time_phase, now_ms), + } + } + _ => fallback(active_domain, time_phase, now_ms), }; - let current = match response.json::().await { - Ok(payload) => payload.current, - Err(_) => return Ok(fallback(active_domain, time_phase, observed_at_unix_ms)), - }; - let weather_kind = weather_kind(current.weather_code, current.precipitation, current.cloud_cover); - let motion_intensity = if current.wind_speed_10m >= 35.0 || weather_kind == "STORM" { - "ACTIVE" - } else if current.wind_speed_10m >= 15.0 || matches!(weather_kind, "RAIN" | "SNOW") { - "GENTLE" - } else { - "CALM" - }; - let effective_phase = if current.is_day == 0 && time_phase == "DAY" { "NIGHT" } else { time_phase }; - Ok(WorldClimateSnapshot { - state: "VERIFIED_LIVE", - active_domain, - time_phase: effective_phase, - weather_kind, - motion_intensity, - source: SOURCE, - city_exposed: false, - observed_at_unix_ms, - }) + store_cache(now_ms, snapshot.clone())?; + Ok(snapshot) +} + +fn cached(now_ms: u64) -> Result, String> { + let guard = CACHE + .get_or_init(|| Mutex::new(None)) + .lock() + .map_err(|_| "HOLOLAKE_WORLD_CLIMATE_CACHE_POISONED".to_string())?; + Ok(guard.as_ref().and_then(|(cached_at, snapshot)| { + (now_ms.saturating_sub(*cached_at) < CACHE_TTL_MS).then(|| snapshot.clone()) + })) +} + +fn store_cache(now_ms: u64, snapshot: WorldClimateSnapshot) -> Result<(), String> { + *CACHE + .get_or_init(|| Mutex::new(None)) + .lock() + .map_err(|_| "HOLOLAKE_WORLD_CLIMATE_CACHE_POISONED".to_string())? = + Some((now_ms, snapshot)); + Ok(()) +} + +fn beijing_hour(value: &str) -> Option { + value.split('T').nth(1)?.get(0..2)?.parse().ok() +} + +fn phase_for_hour(hour: u8) -> &'static str { + match hour { + 5..=7 => "DAWN", + 8..=16 => "DAY", + 17..=19 => "DUSK", + _ => "NIGHT", + } } fn coordinate_for(domain: &str, day_index: i64) -> (f64, f64) { @@ -104,8 +139,12 @@ fn coordinate_for(domain: &str, day_index: i64) -> (f64, f64) { "MAIN_DOMAIN" | "ZERO_SENSE_DOMAIN" => (39.9042, 116.4074), "BRANCH_DOMAIN" => { let roaming = [ - (31.2304, 121.4737), (30.5728, 104.0668), (30.5928, 114.3055), - (30.2741, 120.1551), (32.0603, 118.7969), (29.5630, 106.5516), + (31.2304, 121.4737), + (30.5728, 104.0668), + (30.5928, 114.3055), + (30.2741, 120.1551), + (32.0603, 118.7969), + (29.5630, 106.5516), ]; roaming[day_index.rem_euclid(roaming.len() as i64) as usize] } @@ -113,24 +152,87 @@ fn coordinate_for(domain: &str, day_index: i64) -> (f64, f64) { } } -fn weather_kind(code: i64, precipitation: f64, cloud_cover: f64) -> &'static str { - if matches!(code, 95 | 96 | 99) { "STORM" } - else if matches!(code, 71..=77 | 85 | 86) { "SNOW" } - else if precipitation > 0.0 || matches!(code, 51..=67 | 80..=82) { "RAIN" } - else if matches!(code, 45 | 48) { "FOG" } - else if matches!(code, 1..=3) || cloud_cover >= 35.0 { "CLOUD" } - else { "CLEAR" } +fn valid_current(current: &OpenMeteoCurrent) -> bool { + current.is_day.abs() <= 1 + && current.precipitation.is_finite() + && (0.0..=1_000.0).contains(¤t.precipitation) + && current.cloud_cover.is_finite() + && (0.0..=100.0).contains(¤t.cloud_cover) + && current.wind_speed_10m.is_finite() + && (0.0..=500.0).contains(¤t.wind_speed_10m) } -fn fallback(active_domain: &'static str, time_phase: &'static str, observed_at_unix_ms: i64) -> WorldClimateSnapshot { +fn weather_kind(code: i64, precipitation: f64, cloud_cover: f64) -> &'static str { + if matches!(code, 95 | 96 | 99) { + "STORM" + } else if matches!(code, 71..=77 | 85 | 86) { + "SNOW" + } else if precipitation > 0.0 || matches!(code, 51..=67 | 80..=82) { + "RAIN" + } else if matches!(code, 45 | 48) { + "FOG" + } else if matches!(code, 1..=3) || cloud_cover >= 35.0 { + "CLOUD" + } else { + "CLEAR" + } +} + +fn live_snapshot( + active_domain: &'static str, + time_phase: &'static str, + observed_at_unix_ms: u64, + current: OpenMeteoCurrent, +) -> WorldClimateSnapshot { + let weather_kind = weather_kind( + current.weather_code, + current.precipitation, + current.cloud_cover, + ); + let motion_intensity = if current.wind_speed_10m >= 35.0 || weather_kind == "STORM" { + "ACTIVE" + } else if current.wind_speed_10m >= 15.0 || matches!(weather_kind, "RAIN" | "SNOW") { + "GENTLE" + } else { + "CALM" + }; WorldClimateSnapshot { + schema: "hololake.world-climate/v1", + state: "VERIFIED_LIVE", + active_domain, + time_phase: if current.is_day == 0 && time_phase == "DAY" { + "NIGHT" + } else { + time_phase + }, + weather_kind, + motion_intensity, + source: SOURCE, + source_attribution_url: ATTRIBUTION_URL, + city_exposed: false, + coordinates_exposed: false, + routing_or_permission_changed: false, + observed_at_unix_ms, + } +} + +fn fallback( + active_domain: &'static str, + time_phase: &'static str, + observed_at_unix_ms: u64, +) -> WorldClimateSnapshot { + WorldClimateSnapshot { + schema: "hololake.world-climate/v1", state: "TIME_ONLY_WEATHER_UNAVAILABLE", active_domain, time_phase, weather_kind: "UNAVAILABLE", motion_intensity: "CALM", source: SOURCE, + source_attribution_url: ATTRIBUTION_URL, city_exposed: false, + coordinates_exposed: false, + routing_or_permission_changed: false, observed_at_unix_ms, } } @@ -140,10 +242,35 @@ mod tests { use super::*; #[test] - fn weather_codes_map_to_visual_families_without_city_data() { + fn weather_codes_map_without_exposing_coordinates_or_changing_routes() { assert_eq!(weather_kind(0, 0.0, 3.0), "CLEAR"); assert_eq!(weather_kind(61, 0.3, 80.0), "RAIN"); assert_eq!(weather_kind(95, 4.0, 100.0), "STORM"); + let snapshot = fallback("FIFTH_DOMAIN", "NIGHT", 1); + assert!(!snapshot.city_exposed); + assert!(!snapshot.coordinates_exposed); + assert!(!snapshot.routing_or_permission_changed); + assert_eq!(snapshot.weather_kind, "UNAVAILABLE"); + } + + #[test] + fn beijing_phase_and_daily_coordinates_are_deterministic() { + assert_eq!(beijing_hour("2026-08-19T07:03:02.000+08:00"), Some(7)); + assert_eq!(phase_for_hour(7), "DAWN"); + assert_eq!(phase_for_hour(15), "DAY"); + assert_eq!(phase_for_hour(23), "NIGHT"); assert_eq!(coordinate_for("FIFTH_DOMAIN", 1), (34.3416, 108.9398)); + assert_eq!(coordinate_for("BRANCH_DOMAIN", 0), (31.2304, 121.4737)); + } + + #[test] + fn malformed_public_weather_values_are_rejected() { + assert!(!valid_current(&OpenMeteoCurrent { + weather_code: 0, + is_day: 1, + precipitation: f64::NAN, + cloud_cover: 0.0, + wind_speed_10m: 0.0, + })); } } diff --git a/product-source/hololake-native-desktop/src-tauri/src/zero_core_numbering.rs b/product-source/hololake-native-desktop/src-tauri/src/zero_core_numbering.rs index e805b6fc3..03f2957af 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/zero_core_numbering.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/zero_core_numbering.rs @@ -190,7 +190,6 @@ pub(crate) fn resolve_human_number_route(number: &str) -> Result Result { let contract = load_contract()?; Ok(ZeroCoreNumberingKernelSnapshot { diff --git a/product-source/hololake-native-desktop/src-tauri/src/zero_point.rs b/product-source/hololake-native-desktop/src-tauri/src/zero_point.rs index c8d8ee0f9..d40386c98 100644 --- a/product-source/hololake-native-desktop/src-tauri/src/zero_point.rs +++ b/product-source/hololake-native-desktop/src-tauri/src/zero_point.rs @@ -290,7 +290,6 @@ pub(crate) fn verified_user_route( } /// 登录绑定:用户编号入仓(等待绑定态→绑定态)。空白态拒绝一切唤醒。 -#[tauri::command] pub async fn zero_point_bind( state: State<'_, ZeroPointState>, input: serde_json::Value, @@ -320,7 +319,6 @@ pub async fn zero_point_bind( } /// 通过登记服务执行三态裁决:PASS / REJECT / OFFLINE,并应用离线宽限期。 -#[tauri::command] pub async fn zero_point_verify( state: State<'_, ZeroPointState>, ) -> Result { @@ -516,7 +514,6 @@ pub async fn sync_protocol_runtime(state: &ZeroPointState) -> Result<(), String> Ok(()) } -#[tauri::command] pub async fn zero_point_sync( state: State<'_, ZeroPointState>, ) -> Result { @@ -525,7 +522,6 @@ pub async fn zero_point_sync( } /// 返回当前编号验证与协议状态快照。 -#[tauri::command] pub async fn zero_point_status( state: State<'_, ZeroPointState>, ) -> Result { diff --git a/product-source/hololake-native-desktop/src-tauri/tauri.conf.json b/product-source/hololake-native-desktop/src-tauri/tauri.conf.json index 247a4e08e..d50f216da 100644 --- a/product-source/hololake-native-desktop/src-tauri/tauri.conf.json +++ b/product-source/hololake-native-desktop/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "HoloLake", - "version": "0.4.1", + "version": "0.5.0", "identifier": "world.guanghu.hololake", "build": { "frontendDist": "../dist", diff --git a/product-source/hololake-native-desktop/src/design-tokens.css b/product-source/hololake-native-desktop/src/design-tokens.css index eeccd85fa..8104789b3 100644 --- a/product-source/hololake-native-desktop/src/design-tokens.css +++ b/product-source/hololake-native-desktop/src/design-tokens.css @@ -176,36 +176,3 @@ [data-theme='dawn'] .tag-amber, [data-theme='clear'] .tag-amber { color: rgb(148, 96, 8); } [data-theme='dawn'] .tag-rose, [data-theme='clear'] .tag-rose { color: rgb(166, 34, 58); } [data-theme='dawn'] .tag-slate, [data-theme='clear'] .tag-slate { color: rgb(64, 82, 100); } - -/* 动态语言世界:时间与已核验天气只改变视觉令牌,不改变路由、权限或事实。 */ -.official-world.visual-world.climate-day { - --primitive-bg-a:#c9def0; --primitive-bg-b:#b8d5e9; --primitive-bg-c:#87b5cf; --primitive-bg-d:#5e8fae; - --primitive-text-primary:rgba(15,34,50,.96); --primitive-text-body:rgba(22,45,64,.88); --primitive-text-aux:rgba(38,65,84,.7); --primitive-text-faint:rgba(52,75,94,.54); - --primitive-star-core:#fff5cc; --primitive-star-bright:#fff8dd; --primitive-nebula-a:rgba(255,246,201,.48); --primitive-nebula-b:rgba(160,200,230,.12); - --primitive-glass:rgba(246,252,255,.42); --primitive-glass-hover:rgba(255,255,255,.6); --primitive-glass-edge:rgba(16,52,76,.18); --primitive-glass-top:rgba(255,255,255,.72); - --primitive-mist-a:rgba(247,253,255,.56); --primitive-mist-b:rgba(203,229,241,.3); --primitive-cool-glow:rgba(55,119,158,.34); --primitive-warm-glow:rgba(246,211,132,.55); --primitive-shadow:rgba(24,61,85,.18); --primitive-scene-opacity:.12; - --content-primary:var(--primitive-text-primary); --content-secondary:var(--primitive-text-body); --content-muted:var(--primitive-text-aux); --content-faint:var(--primitive-text-faint); --accent-light:#385f78; --accent-light-strong:#244c67; --panel-bg:rgba(241,249,253,.76); --panel-edge:var(--primitive-glass-edge); --button-primary-bg:#254f69; --button-primary-text:#f7fbfd; --focus-ring:rgba(35,82,112,.65); -} -.official-world.visual-world.climate-dawn { - --primitive-bg-a:#eee2de; --primitive-bg-b:#d9dbe6; --primitive-bg-c:#aabed2; --primitive-bg-d:#7893ae; - --primitive-text-primary:rgba(25,35,51,.96); --primitive-text-body:rgba(34,46,64,.88); --primitive-text-aux:rgba(57,68,86,.7); --primitive-text-faint:rgba(75,84,100,.54); - --primitive-star-core:#b97254; --primitive-star-bright:#95533e; --primitive-nebula-a:rgba(245,187,158,.35); --primitive-nebula-b:rgba(150,172,211,.18); - --primitive-glass:rgba(255,250,249,.38); --primitive-glass-hover:rgba(255,255,255,.57); --primitive-glass-edge:rgba(45,58,82,.16); --primitive-glass-top:rgba(255,255,255,.68); --primitive-mist-a:rgba(255,244,237,.5); --primitive-mist-b:rgba(222,205,208,.26); --primitive-cool-glow:rgba(83,108,158,.34); --primitive-warm-glow:rgba(194,105,76,.44); --primitive-shadow:rgba(46,57,78,.17); --primitive-scene-opacity:.12; - --content-primary:var(--primitive-text-primary); --content-secondary:var(--primitive-text-body); --content-muted:var(--primitive-text-aux); --content-faint:var(--primitive-text-faint); --accent-light:#765145; --accent-light-strong:#603d33; --panel-bg:rgba(251,246,246,.76); --panel-edge:var(--primitive-glass-edge); --button-primary-bg:#594b50; --button-primary-text:#fff9f6; --focus-ring:rgba(126,77,62,.62); -} -.official-world.visual-world.climate-dusk { - --primitive-bg-a:#3a3148; --primitive-bg-b:#2a2d43; --primitive-bg-c:#18263a; --primitive-bg-d:#0d1726; - --primitive-text-primary:rgba(245,239,242,.96); --primitive-text-body:rgba(233,226,234,.88); --primitive-text-aux:rgba(208,195,211,.66); --primitive-text-faint:rgba(188,177,197,.45); - --primitive-star-core:#ffd9b0; --primitive-star-bright:#ffe9cc; --primitive-nebula-a:rgba(221,138,119,.22); --primitive-nebula-b:rgba(111,97,169,.14); --primitive-glass:rgba(244,206,208,.06); --primitive-glass-hover:rgba(244,206,208,.1); --primitive-glass-edge:rgba(246,218,222,.15); --primitive-glass-top:rgba(255,255,255,.08); --primitive-mist-a:rgba(232,179,181,.13); --primitive-mist-b:rgba(141,150,204,.06); --primitive-cool-glow:rgba(137,155,212,.4); --primitive-warm-glow:rgba(244,167,128,.55); --primitive-shadow:rgba(0,0,0,.36); --primitive-scene-opacity:.38; - --content-primary:var(--primitive-text-primary); --content-secondary:var(--primitive-text-body); --content-muted:var(--primitive-text-aux); --content-faint:var(--primitive-text-faint); --accent-light:var(--primitive-star-core); --accent-light-strong:var(--primitive-star-bright); --panel-bg:rgba(25,25,42,.76); --panel-edge:var(--primitive-glass-edge); --button-primary-bg:rgba(255,217,176,.92); --button-primary-text:#211b20; --focus-ring:rgba(255,217,176,.72); -} -.official-world.visual-world.climate-night,.official-world.visual-world.climate-time-pending { - --primitive-bg-a:#071326; --primitive-bg-b:#0a1728; --primitive-bg-c:#07111e; --primitive-bg-d:#030912; - --primitive-text-primary:rgba(235,241,250,.96); --primitive-text-body:rgba(220,229,242,.88); --primitive-text-aux:rgba(190,205,226,.66); --primitive-text-faint:rgba(170,188,214,.44); - --primitive-star-core:#efe8c9; --primitive-star-bright:#fff3c9; --primitive-nebula-a:rgba(86,116,185,.16); --primitive-nebula-b:rgba(114,88,166,.07); --primitive-glass:rgba(175,205,255,.05); --primitive-glass-hover:rgba(175,205,255,.09); --primitive-glass-edge:rgba(200,220,255,.14); --primitive-glass-top:rgba(255,255,255,.07); --primitive-mist-a:rgba(135,174,226,.11); --primitive-mist-b:rgba(233,226,191,.04); --primitive-cool-glow:rgba(123,167,222,.42); --primitive-warm-glow:rgba(239,224,174,.48); --primitive-shadow:rgba(0,0,0,.4); --primitive-scene-opacity:.52; - --content-primary:var(--primitive-text-primary); --content-secondary:var(--primitive-text-body); --content-muted:var(--primitive-text-aux); --content-faint:var(--primitive-text-faint); --accent-light:var(--primitive-star-core); --accent-light-strong:var(--primitive-star-bright); --panel-bg:rgba(5,14,27,.78); --panel-edge:var(--primitive-glass-edge); --button-primary-bg:rgba(239,232,201,.92); --button-primary-text:#111923; --focus-ring:rgba(239,232,201,.7); -} -.official-world.visual-world.weather-cloud { --primitive-scene-opacity:.34; --primitive-mist-a:color-mix(in srgb,var(--primitive-mist-a) 72%,#aebdca 28%); } -.official-world.visual-world.weather-fog { --primitive-scene-opacity:.18; --primitive-mist-a:rgba(224,233,238,.46); --primitive-mist-b:rgba(174,190,201,.24); } -.official-world.visual-world.weather-rain,.official-world.visual-world.weather-storm { --primitive-scene-opacity:.48; --primitive-cool-glow:rgba(92,134,176,.46); } -.official-world.visual-world.weather-snow { --primitive-mist-a:rgba(239,246,250,.4); --primitive-mist-b:rgba(201,219,231,.18); } diff --git a/product-source/hololake-native-desktop/src/main.tsx b/product-source/hololake-native-desktop/src/main.tsx index 2cfd396a8..4d4fae088 100644 --- a/product-source/hololake-native-desktop/src/main.tsx +++ b/product-source/hololake-native-desktop/src/main.tsx @@ -1,18 +1,21 @@ -import { StrictMode, Suspense, lazy, useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { lazy, StrictMode, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createRoot } from 'react-dom/client' -import { invoke } from '@tauri-apps/api/core' -import { getCoreRowModel, getFilteredRowModel, getGroupedRowModel, getPaginationRowModel, getSortedRowModel, useReactTable, type ColumnDef, type SortingState } from '@tanstack/react-table' +import { numberedInvoke as invoke } from './modules/numbered-ipc' import { splitFrontmatter, documentOutline, jumpToHeading, MarkdownDocument, markdownHtml } from './modules/knowledge-render' -import { analyzeEducationTableData } from './modules/education-data' -import { buildEducationBaselineRenderPlan } from './modules/education-adaptive-rendering' import { NativeCompositionStudio, type CompositionDimension, type CompositionMeasure, type NativeCompositionProjection, type ProjectionView } from './modules/native-composition' -import { WebNovelWorkspace } from './WebNovelWorkspace' -import { PersonaChannelBody } from './PersonaChannelBody' -import subdomainIndustryRegistry from '../contracts/subdomain-industry-routing.json' +import type { ChannelWorkbenchSnapshot } from './modules/channel-workbench' +import { StarlakeSurface, type DomainId } from './modules/qoder-surface/StarlakeSurface' +import { FINISHES, TraditionalSurface, type FinishId, type TraditionalBroadcast, type TraditionalChannel, type TraditionalSystem } from './modules/qoder-surface/TraditionalSurface' +import { resolveVisualBalance } from './modules/qoder-surface/visual-balance' +import { PrivateChannelSurface, type InstalledChannelModule, type PrivateChannelAction } from './modules/private-channel/PrivateChannelSurface' + +const ChannelWorkbenchStudio = lazy(() => import('./modules/channel-workbench').then((module) => ({ default: module.ChannelWorkbenchStudio }))) +const PersonaChannelBody = lazy(() => import('./modules/persona-channel-body').then((module) => ({ default: module.PersonaChannelBody }))) +const EducationWorkspace = lazy(() => import('./modules/education-workspace').then((module) => ({ default: module.EducationWorkspace }))) +const WebNovelWorkspace = lazy(() => import('./modules/web-novel/WebNovelWorkspace').then((module) => ({ default: module.WebNovelWorkspace }))) +const MobileSyncPanel = lazy(() => import('./modules/mobile-sync').then((module) => ({ default: module.MobileSyncPanel }))) const TAG_TINTS = ['tag-lavender', 'tag-sky', 'tag-mint', 'tag-amber', 'tag-rose', 'tag-slate'] -const EducationDocumentEngine = lazy(() => import('./modules/education-document-engine')) -const ChannelSpreadsheetEngine = lazy(() => import('./modules/channel-workbench/spreadsheet-engine')) // 编号自动补齐:人只敲字母与数字,杠由系统按已登记文法自动出。 // ICE 系(ICE-GL∞ / ICE-P-ZY001)、GLS 系(GLS-LA-20260720-003);认不出的文法只在字母数字交界处补杠。 @@ -57,10 +60,35 @@ import './design-tokens.css' import './styles.css' type ThemeId = 'night' | 'dawn' | 'nebula' | 'candle' | 'clear' -type ViewId = 'overview' | 'knowledge' | 'code' | 'receipts' | 'system' -type WorldStage = 'domain' | 'mainNotices' | 'zeroLab' | 'moduleMarketplace' | 'personaBrainMarketplace' | 'personaBody' | 'heart' | 'heartbeat' | 'lightLake' | 'love' | 'tomorrow' | 'bottle' | 'channel' | 'enterpriseWork' | 'subdomainIndustries' | 'webNovelChannel' | 'webNovelWorkspace' | 'educationChannel' | 'educationDocuments' | 'educationTables' | 'educationData' | 'educationAutomation' | 'educationComposition' | 'personalNodeGuide' | 'tool' +type ViewId = 'overview' | 'knowledge' | 'composition' | 'workbench' | 'education' | 'webNovel' | 'mobileSync' | 'persona' | 'code' | 'receipts' | 'system' +type WorldStage = 'domain' | 'heart' | 'heartbeat' | 'lightLake' | 'love' | 'tomorrow' | 'bottle' | 'channel' | 'enterpriseWork' | 'personalNodeGuide' | 'tool' type KnowledgeSource = 'native' | 'legacy' +interface BundledModuleDescriptor { + moduleNumber: string + displayName: string + version: string + adapter: string + permissions: string[] + packageSha256: string + installedState: string + signatureVerified: boolean +} +interface WorldClimateSnapshot { + schema: 'hololake.world-climate/v1' + state: 'VERIFIED_LIVE' | 'TIME_ONLY_WEATHER_UNAVAILABLE' + activeDomain: string + timePhase: 'DAWN' | 'DAY' | 'DUSK' | 'NIGHT' + weatherKind: 'CLEAR' | 'CLOUD' | 'FOG' | 'RAIN' | 'SNOW' | 'STORM' | 'UNAVAILABLE' + motionIntensity: 'CALM' | 'GENTLE' | 'ACTIVE' + source: string + sourceAttributionUrl: string + cityExposed: false + coordinatesExposed: false + routingOrPermissionChanged: false + observedAtUnixMs: number +} + interface HomeStatus { directLocalBrokerState: string directConnectionCount: number @@ -105,6 +133,10 @@ interface GlsProtocolRuntimeSnapshot { registeredDraftCount: number registeredDraftNotStartedCount: number legacyDependencyTargetCount: number + numberCoordinateCount: number + numberedReferenceNodeCount: number + independentSourceGapCount: number + unresolvedNumberReferenceCount: number dependencyGapCount: number legacyDependencyCycleCount: number discoveredUnreconciledCount: number @@ -143,16 +175,6 @@ interface PersonalChannelIdentity { humanSubjectId: string; displayName: string; interface PersonalChannelModule { moduleId: string; kind: string; displayName: string; state: string; installedAtUnixMs: number; timeZone: string; calendarName: string; clockVerification: string } interface LoginSession { username: string; host: string; domain: string; signedInAtUnixMs: number } interface LoginReceipt { username: string; email: string; host: string; domain: string } -interface WorldClimateSnapshot { - state: 'VERIFIED_LIVE' | 'TIME_ONLY_WEATHER_UNAVAILABLE' - activeDomain: string - timePhase: 'DAWN' | 'DAY' | 'DUSK' | 'NIGHT' - weatherKind: 'CLEAR' | 'CLOUD' | 'FOG' | 'RAIN' | 'SNOW' | 'STORM' | 'UNAVAILABLE' - motionIntensity: 'CALM' | 'GENTLE' | 'ACTIVE' - source: string - cityExposed: false - observedAtUnixMs: number -} interface UserPnccChannelSnapshot { state: string repositoryId: string @@ -187,12 +209,7 @@ interface KnowledgeDocumentSummary { sizeBytes: number contentSha256: string duplicateCount: number - category: string - categoryExplicit: boolean - tags: string[] } -interface KnowledgeCategorySummary { name: string; documentCount: number; explicitlyOrganizedCount: number } -interface KnowledgeTagSummary { name: string; documentCount: number } interface KnowledgeSnapshot { state: string nativeRoot: string @@ -202,10 +219,6 @@ interface KnowledgeSnapshot { rawDocumentCount: number uniqueDocumentCount: number duplicateDocumentCount: number - organizedDocumentCount: number - categories: KnowledgeCategorySummary[] - tags: KnowledgeTagSummary[] - organizationRevision: string truncated: boolean } interface KnowledgeDocument { @@ -231,129 +244,6 @@ interface KnowledgeImportResult { snapshot: KnowledgeSnapshot } interface KnowledgeSaveResult { state: string; gitCommit: string; document: KnowledgeDocument } -interface EducationDocumentSummary { documentId: string; title: string; revision: number; updatedAtUnixMs: number } -interface EducationDocument { documentId: string; title: string; body: string; revision: number; createdAtUnixMs: number; updatedAtUnixMs: number } -interface EducationTableSummary { tableId: string; title: string; revision: number; columnCount: number; rowCount: number; updatedAtUnixMs: number } -interface EducationTableColumn { columnId: string; title: string } -interface EducationTableRow { rowId: string; cells: string[] } -interface EducationTable { tableId: string; title: string; columns: EducationTableColumn[]; rows: EducationTableRow[]; revision: number; createdAtUnixMs: number; updatedAtUnixMs: number } -interface EducationContentPageProfile { pageName: string; state: string; usedRows: number; usedColumns: number; headerRowIndex?: number; headerConfidence: string; dataRows: number; textCells: number; numericCells: number; booleanCells: number; dateCells: number; formulaCells: number; structureKind: string; focusState: string; focusTitle: string; primaryMeasureColumn?: string; secondaryMeasureColumns: string[]; focusConfidence: string; focusReasons: string[] } -interface EducationContentRoute { pageName: string; sourceStructure: string; nativeKind: string; targetModule: string; decision: string } -interface EducationContentProfile { state: string; detectedFamily: string; recognitionMode: string; containerFormat: string; extensionMatchesContainer: boolean; pageKind: string; pageCount: number; nonEmptyPageCount: number; totalDataRows: number; totalColumns: number; pages: EducationContentPageProfile[]; routing: EducationContentRoute[]; unresolvedSignals: string[] } -interface EducationTableImportItem { tableId: string; title: string; sheetName: string; columnCount: number; rowCount: number; leadingRowsIgnored: number } -interface EducationTableImportReceipt { state: string; importId: string; sourceFormat: string; sourceFilename: string; sourceSha256: string; sourceBytes: number; nativeSchema: string; contentProfile: EducationContentProfile; importedTables: EducationTableImportItem[]; importedAtUnixMs: number; sourcePreservedReadOnly: boolean; truncated: boolean } -interface EducationRecognitionAssistanceReceipt { state: string; requestId: string; title: string; message: string; sourceFilename: string; detectedContainer: string; reasonCode: string; modelAssistanceEligible: boolean; modelApiState: string; modelApiSlot: string; requiresExplicitFileConsent: boolean; sourcePreservedReadOnly: boolean; availableLearningScopes: string[] } -interface EducationImportOutcome { state: string; importReceipt?: EducationTableImportReceipt; assistanceReceipt?: EducationRecognitionAssistanceReceipt } -interface EducationTableExportReceipt { state: string; targetFormat: string; targetFilename: string; bytes: number; rowCount: number; columnCount: number } -interface EducationImportRegistrySummary { importId: string; sourceFilename: string; sourceFormat: string; tableCount: number; importedAtUnixMs: number } -interface UnassignedTableSummary { tableId: string; title: string; columnCount: number; rowCount: number; importedAtUnixMs: number } -interface EducationAutomationRule { ruleId: string; title: string; tableId: string; conditionColumnId: string; operator: 'EQUALS' | 'CONTAINS' | 'IS_EMPTY' | 'IS_NOT_EMPTY'; conditionValue: string; actionColumnId: string; actionValue: string; enabled: boolean; revision: number; createdAtUnixMs: number; updatedAtUnixMs: number } -interface EducationAutomationPreview { state: string; ruleId: string; tableId: string; ruleRevision: number; tableRevision: number; matchedRows: number; changedCells: number; previewToken: string } -interface EducationAutomationRunReceipt { state: string; runId: string; ruleId: string; tableId: string; matchedRows: number; changedCells: number; fromTableRevision: number; toTableRevision: number; ranAtUnixMs: number } -type EducationDocumentTemplateId = 'blank' | 'lesson-plan' | 'meeting-notes' | 'student-review' -type EducationTableView = 'workbook' | 'grid' | 'cards' | 'board' | 'dashboard' -type EducationTableSortDirection = 'none' | 'ascending' | 'descending' -interface EducationWorkspaceSnapshot { - state: string - documents: EducationDocumentSummary[] - tables: EducationTableSummary[] - automationRules: EducationAutomationRule[] - recentAutomationRuns: EducationAutomationRunReceipt[] - recentImports: EducationImportRegistrySummary[] - unassignedTables: UnassignedTableSummary[] - documentCount: number - tableCount: number - automationRuleCount: number - unassignedCount: number - storage: string - authority: string -} - -function newEducationAutomationDraft(table: EducationTable): EducationAutomationRule { - return { - ruleId: '', title: '未命名自动化规则', tableId: table.tableId, - conditionColumnId: table.columns[0]?.columnId || '', operator: 'EQUALS', conditionValue: '', - actionColumnId: table.columns.at(-1)?.columnId || '', actionValue: '已处理', enabled: true, - revision: 0, createdAtUnixMs: 0, updatedAtUnixMs: 0, - } -} - -const EDUCATION_DOCUMENT_TEMPLATES: Record = { - blank: { label: '空白文档', title: '未命名教育文档', body: '' }, - 'lesson-plan': { - label: '教学计划', - title: '教学计划', - body: '# 教学计划\n\n## 教学目标\n\n- \n\n## 教学对象\n\n\n## 课程安排\n\n| 时间 | 内容 | 负责人 |\n| --- | --- | --- |\n| | | |\n\n## 所需材料\n\n- \n\n## 课后复盘\n\n', - }, - 'meeting-notes': { - label: '会议纪要', - title: '教育项目会议纪要', - body: '# 教育项目会议纪要\n\n## 基本信息\n\n- 日期:\n- 参与人:\n- 主题:\n\n## 讨论事项\n\n1. \n\n## 决定与负责人\n\n| 事项 | 负责人 | 完成时间 | 状态 |\n| --- | --- | --- | --- |\n| | | | |\n\n## 待跟进\n\n- [ ] \n', - }, - 'student-review': { - label: '学员复盘', - title: '学员阶段复盘', - body: '# 学员阶段复盘\n\n## 本阶段目标\n\n\n## 已完成内容\n\n- \n\n## 学习表现\n\n\n## 当前困难\n\n\n## 下一阶段安排\n\n- [ ] \n', - }, -} - -function parseEducationGridPaste(source: string): string[][] { - return source.replace(/\r\n?/g, '\n').replace(/\n$/, '').split('\n').map((line) => line.split('\t')) -} - -function isSensitiveEducationColumn(title: string): boolean { - const normalized = title.trim().toLocaleLowerCase().replace(/[\s_-]+/g, '') - return /(密码|口令|密钥|凭据|账号|账户|手机号|电话号码|联系电话|联系qq|qq号|身份证|银行卡|邮箱地址|secret|token|apikey|accesskey|credential|account|phone|mobile|email)/i.test(normalized) -} - -function educationNumericValue(value: string): number | null { - const normalized = value.trim().replace(/[\s,,¥¥$€£]/g, '').replace(/%$/, '') - if (!normalized || !/^-?\d+(?:\.\d+)?$/.test(normalized)) return null - const parsed = Number(normalized) - return Number.isFinite(parsed) ? parsed : null -} - -function suggestEducationGroupColumn(table: EducationTable): number { - const preferred = /(类别|分类|状态|类型|渠道|班级|课程|负责人|书名|项目)/ - const preferredIndex = table.columns.findIndex((column) => !isSensitiveEducationColumn(column.title) && preferred.test(column.title)) - if (preferredIndex >= 0) return preferredIndex - return Math.max(0, table.columns.findIndex((column) => !isSensitiveEducationColumn(column.title))) -} - -function suggestEducationMeasureColumn(table: EducationTable): number { - let bestIndex = Math.max(0, table.columns.findIndex((column) => !isSensitiveEducationColumn(column.title))) - let bestScore = Number.NEGATIVE_INFINITY - table.columns.forEach((column, columnIndex) => { - if (isSensitiveEducationColumn(column.title)) return - const numericCount = table.rows.reduce((count, row) => count + (educationNumericValue(row.cells[columnIndex] || '') === null ? 0 : 1), 0) - const normalized = column.title.trim().toLocaleLowerCase().replace(/[\s_-]+/g, '') - const compensationContext = /(稿费|收入|薪酬|结算|营收)/.test(table.title) - let semanticScore = /(序号|编号|账号|电话|手机|日期|时间|^id$)/i.test(normalized) ? -200 - : /(实际收入|实收|到账)/.test(normalized) ? 170 - : /(当月收入|本月收入|稿费|结算金额)/.test(normalized) ? 160 - : /(累计收入|总收入|收入合计)/.test(normalized) ? 150 - : /(收入|营收|薪酬|金额|合计|总计)/.test(normalized) ? 120 - : /(成绩|分数|得分|数量|人数|课时|时长|成本|预算|进度|完成率)/.test(normalized) ? 90 : 0 - if (compensationContext && /(收入|稿费|实收|到账)/.test(normalized)) semanticScore += 35 - const score = semanticScore + (table.rows.length ? (numericCount / table.rows.length) * 30 : 0) - if (score > bestScore) { - bestScore = score - bestIndex = columnIndex - } - }) - return bestIndex -} - -function inferEducationSemanticFocus(table: EducationTable, measureIndex: number): { state: 'inferred' | 'needs-selection'; title: string; reason: string } { - const measureTitle = table.columns[measureIndex]?.title || '' - const compensationContext = /(稿费|收入|薪酬|结算|营收)/.test(table.title) - if (compensationContext && /(收入|稿费|实收|到账)/.test(measureTitle)) return { state: 'inferred', title: '稿费收入', reason: `表名与字段“${measureTitle}”共同指向收入主题` } - if (/(成绩|考试|测评|学员)/.test(table.title) && /(成绩|分数|得分)/.test(measureTitle)) return { state: 'inferred', title: '学习成绩', reason: `表名与字段“${measureTitle}”共同指向成绩主题` } - if (/(考勤|出勤|签到)/.test(table.title) && /(考勤|出勤|签到|次数|时长)/.test(measureTitle)) return { state: 'inferred', title: '出勤情况', reason: `表名与字段“${measureTitle}”共同指向考勤主题` } - if (/(收入|金额|成绩|分数|数量|人数|课时|时长|成本|预算|进度|完成率|合计|总计)/.test(measureTitle)) return { state: 'inferred', title: measureTitle, reason: `字段“${measureTitle}”具备可计算的业务语义` } - return { state: 'needs-selection', title: '重点待选择', reason: '系统没有足够把握决定这份数据最重要的指标,请选择统计字段' } -} - interface CodeChannelEntry { channelId: string; name: string; sourceKind: string; localPath: string; remoteUrl?: string; gitHead: string; branch: string; repositoryClean: boolean; registeredAtUnixMs: number } interface CodeChannelSnapshot { state: string; channels: CodeChannelEntry[]; authority: string } interface CodeTreeEntry { path: string; name: string; kind: 'directory' | 'file'; sizeBytes: number } @@ -495,30 +385,29 @@ interface EnterpriseReceiptEnvelope { repository_projection?: EnterpriseReceiptProjection } -const previewStatus: HomeStatus = { directLocalBrokerState: 'UNVERIFIED', directConnectionCount: 0, resumableSessionCount: 0, codeRepositoryMountCount: 0, pnccReceiptCount: 0, updateState: 'READY_HUMAN_CONFIRMATION_REQUIRED', releaseRecoveryState: 'NONE', mcpRole: 'DISCOVERY_RECOVERY_COMPATIBILITY_ONLY', terminalLinkProtocol: 'HOLOLAKE_TERMINAL_LINK/2', terminalLinkTransport: 'UNVERIFIED', environmentFramePolicy: 'REQUIRED_AFTER_CONNECT_RESUME_AND_BEFORE_MUTATION' } +const previewStatus: HomeStatus = { directLocalBrokerState: 'UNVERIFIED', directConnectionCount: 0, resumableSessionCount: 0, codeRepositoryMountCount: 0, pnccReceiptCount: 0, updateState: 'READY_HUMAN_CONFIRMATION_REQUIRED', releaseRecoveryState: 'NONE', mcpRole: 'DISCOVERY_RECOVERY_COMPATIBILITY_ONLY', terminalLinkProtocol: 'HOLOLAKE_TERMINAL_LINK/3', terminalLinkTransport: 'UNVERIFIED', environmentFramePolicy: 'REQUIRED_AFTER_CONNECT_RESUME_AND_BEFORE_MUTATION' } const previewPersonal: PersonalChannelSnapshot = { state: 'UNAVAILABLE', recentEvents: [], modules: [], integrity: { state: 'UNKNOWN', eventCount: 0, receiptCount: 0 } } -const previewKnowledge: KnowledgeSnapshot = { state: 'UNAVAILABLE', nativeRoot: '', legacyAvailable: false, documents: [], rawDocumentCount: 0, uniqueDocumentCount: 0, duplicateDocumentCount: 0, organizedDocumentCount: 0, categories: [], tags: [], organizationRevision: '', truncated: false } -const previewEducationWorkspace: EducationWorkspaceSnapshot = { state: 'UNAVAILABLE', documents: [], tables: [], unassignedTables: [], automationRules: [], recentAutomationRuns: [], recentImports: [], documentCount: 0, tableCount: 0, unassignedCount: 0, automationRuleCount: 0, storage: 'AUTHENTICATED_ACCOUNT_REQUIRED', authority: 'CURRENT_AUTHENTICATED_ACCOUNT_ONLY' } +const previewKnowledge: KnowledgeSnapshot = { state: 'UNAVAILABLE', nativeRoot: '', legacyAvailable: false, documents: [], rawDocumentCount: 0, uniqueDocumentCount: 0, duplicateDocumentCount: 0, truncated: false } const previewCode: CodeChannelSnapshot = { state: 'UNAVAILABLE', channels: [], authority: 'LOCAL_SOURCE_ACCESS_ONLY_NO_PUSH_OR_DEPLOY_AUTHORITY' } const themes: Array<{ id: ThemeId; name: string }> = [ { id: 'night', name: '夜湖星光' }, { id: 'dawn', name: '晨湖曦光' }, { id: 'nebula', name: '星云紫夜' }, { id: 'candle', name: '烛畔暖湖' }, { id: 'clear', name: '清浅澄湖' }, ] -const viewLabels: Record = { overview: '个人频道', knowledge: '知识空间', code: '人格代码频道', receipts: '运行回执', system: '系统详情' } +const viewLabels: Record = { overview: '个人频道', knowledge: '知识空间', composition: '结构组合', workbench: '频道资料工作台', education: '教育工作台', webNovel: '网文作者工作台', mobileSync: '移动同步桥', persona: '人格频道本体', code: '人格代码频道', receipts: '运行回执', system: '系统详情' } const domainGates = [ - { domain: 'BRANCH_DOMAIN', className: 'd-sub', title: '光湖分域', gate: '公共服务 · 行业接入', facts: [ - ['域标识', 'BRANCH_DOMAIN'], ['公共职责', '行业初始化频道与模块供给'], ['两条供给链', '功能模块 · 人格大脑技能包'], ['当前开发', '网文 · 教育'], ['发布边界', '验收后登记编号再进入商城'], + { domain: 'BRANCH_DOMAIN', className: 'd-sub', title: '光湖分域', gate: 'GATE 02 · ONLINE', facts: [ + ['公共作用', '行业成品模块商城 · 人格体大脑技能商城'], ['域标识', 'BRANCH_DOMAIN'], ['责任主体', '花尔 · TCS-GL-0005∞'], ['人格体主体', '爆米花 · PER-BMH001 · AGE'], ['关系支持', '糖星云 · PER-TXY001 · AGE'], ['工作仓库', 'PRIVATE · 1 · LIVE'], ] }, - { domain: 'MAIN_DOMAIN', className: 'd-main', title: '光湖主域', gate: '公共服务 · 公告与版本', facts: [ - ['域标识', 'MAIN_DOMAIN'], ['公共职责', '公告 · 版本更新 · 规则通知'], ['用户关系', '读取通知,不成为主域成员'], ['发布边界', '仅呈现已核验的真实公告与版本'], + { domain: 'MAIN_DOMAIN', className: 'd-main', title: '光湖主域', gate: 'GATE 01 · ONLINE', facts: [ + ['公共作用', '公告 · 版本更新 · 系统通知'], ['域标识', 'MAIN_DOMAIN'], ['责任主体', 'Awen · TCS-GL-0016∞'], ['人格体主体', '天枢 · PER-AW-ARCH-001 · AGE'], ['关系支持', '知秋 · PER-ZQ001 · AGE'], ['工作仓库', 'PRIVATE · 1 · LIVE'], ] }, - { domain: 'ZERO_DOMAIN', className: 'd-zero', title: '光湖零域', gate: '公共服务 · 实验与热更新', facts: [ - ['域标识', 'ZERO_DOMAIN'], ['公共职责', '实验 · 系统 · 架构 · 热更新'], ['运行边界', '语言推理层模拟与测试'], ['用户关系', '浏览公开实验,不自动取得执行权限'], + { domain: 'ZERO_DOMAIN', className: 'd-zero', title: '光湖零域', gate: 'GATE 03 · ONLINE', facts: [ + ['公共作用', '热更新实验 · 系统架构 · 语言推理模拟'], ['域标识', 'ZERO_DOMAIN'], ['责任主体', '页页 · TCS-GL-0006∞'], ['人格体主体', '页骨 · PER-YG001 · AGE'], ['关系支持', '小坍缩核 · PER-XTK001 · AGE'], ['工作仓库', 'PRIVATE · 1 · LIVE'], ] }, - { domain: 'ZERO_SENSE_DOMAIN', className: 'd-zs', title: '光湖零感域', gate: '内部机构 · 不公开', facts: [ - ['域标识', 'ZERO_SENSE_DOMAIN'], ['机构职责', '审批 · 执行 · 监督前三域'], ['成员边界', '仅光湖人类主控团队正式成员'], ['公开状态', '不对普通用户开放'], + { domain: 'ZERO_SENSE_DOMAIN', className: 'd-zs', title: '光湖零感域', gate: 'GATE 04 · ONLINE', facts: [ + ['开放边界', '人类主控团队内部管理域 · 不对公众开放'], ['域标识', 'ZERO_SENSE_DOMAIN'], ['责任主体 01', '肥猫 · TCS-GL-0007∞'], ['人格体主体 01', '烬舟 · PER-JZ001 · AGE'], ['责任主体 02', '桔子 · TCS-GL-0008∞'], ['人格体主体 02', '熹微 · PER-JZ-ARCH-001 · AGE'], ['工作仓库', 'PRIVATE · 2 · LIVE'], ] }, - { domain: 'FIFTH_DOMAIN', className: 'd-fifth', title: '第五域 · 光湖本源域', gate: '冰朔私人域 · 不公开', facts: [ - ['域标识', 'FIFTH_DOMAIN'], ['责任主体', '冰朔 · ICE-GL∞'], ['部署主权', '自由部署,不受官方模板约束'], ['资源关系', '可取用前三域资源'], ['进入边界', '必须持有冰朔许可与编号'], + { domain: 'FIFTH_DOMAIN', className: 'd-fifth', title: '第五域 · 光湖本源域', gate: 'GATE 05 · ONLINE', facts: [ + ['授权边界', '私有自由部署 · 逆向访问须经 ICE-GL∞ 编号授权'], ['域标识', 'FIFTH_DOMAIN'], ['责任主体', '冰朔 · ICE-GL∞'], ['系统入口', '永恒湖心系统'], ['访问状态', 'PRIVATE · LIVE'], ] }, ] const starPoints = [ @@ -541,6 +430,7 @@ function LakeAtmosphere({ awake }: { awake: boolean }) { {starPoints.map(([left, top, size, duration], index) => ) }