From 4286e4e12ed3a1cfd1c5c6ac6def1f10b38e3cbe Mon Sep 17 00:00:00 2001 From: Devin Date: Mon, 27 Apr 2026 00:02:48 +0000 Subject: [PATCH] v0.8 close-out: tier block integration + auto-compute + status docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - schemas/life-package.schema.json: inline $defs.tier_block + $defs.tier_dimensions (copied from tier.schema.json); add optional top-level tier property; mark verification_level deprecated (remains required for v0.1 back-compat). - tools/build_life_package.py v0.2: auto-compute tier from contents + CLI overrides; weighted-average scoring per docs/LIFE_TIER_SPEC.md §4.1; Schema D banding; computed_by stamped with mandatory @ separator (hand-rolled blocks rejected). - tools/test_life_package_schema.py: +10 tier cases (54 -> 64) covering back-compat omission, consistent happy path, both boundaries, score↔level mismatch, hand-rolled computed_by, score range, off-enum dim, missing dim, unknown field. - tools/test_minimal_life_package.py: verifies builder emits well-formed tier block with @-separated computed_by and all 6 dimensions. - docs/LIFE_FILE_STANDARD.md: adds tier row; marks verification_level deprecated with link to docs/LIFE_TIER_SPEC.md §6. - docs/IMPLEMENTATION_STATUS.md (v6.0), docs/GAP_ANALYSIS.md, ROADMAP.md, CHANGELOG.md: v0.8 release stanza + delivery table updates; reference runtime deferral moved from v0.8+ to v0.9+. All 22/22 batch_validate checks pass; test_life_package_schema 64/64; test_minimal_life_package deterministic bytes match across two builds. Closes v0.8-asset-architecture follow-up (epic #106). --- CHANGELOG.md | 55 +++++++++- ROADMAP.md | 8 +- docs/GAP_ANALYSIS.md | 22 ++-- docs/IMPLEMENTATION_STATUS.md | 22 ++-- docs/LIFE_FILE_STANDARD.md | 3 +- schemas/life-package.schema.json | 97 ++++++++++++++++- tools/build_life_package.py | 164 +++++++++++++++++++++++++++++ tools/test_life_package_schema.py | 88 ++++++++++++++++ tools/test_minimal_life_package.py | 11 ++ 9 files changed, 442 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56f9e2d..801a3d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,17 +2,21 @@ All notable changes to the DLRS project will be documented in this file. -## v0.8 (Draft) +## v0.8-asset-architecture (2026-04-26) -**Status**: In progress. v0.8 closes the four asset-architecture gaps left +**Status**: Released. v0.8 closes the four asset-architecture gaps left by v0.7-vision-shift: provenance (Genesis), evolution (Lifecycle), consumption (Binding), and orchestration (Assembly), plus a multi-dimensional tier system that replaces v0.7's single-axis `verification_level`. Tracked in epic -[#106](https://github.com/Digital-Life-Repository-Standard/DLRS/issues/106). -Sub-issues #100–#105. +[#106](https://github.com/Digital-Life-Repository-Standard/DLRS/issues/106) +(closed). Sub-issues #100–#105 all merged; four post-merge review +follow-ups (#109, #112, #114, #116) merged alongside. Final integration +step (this release) folds the tier block into `life-package.schema.json` +and teaches `tools/build_life_package.py` to auto-compute it from +package contents, completing the v0.8 spec → builder loop. ### Added @@ -123,9 +127,50 @@ Sub-issues #100–#105. first-class), D4=C (three-field surface — already in binding spec), D5=C (OS package manager bootstrap), and the new D6 (fail-close stage gating). Adds four new audit event types: - `capability_bound`, `assembly_aborted`, `withdrawal_check`, and + `capability_bound`, `assembly_aborted`, `withdrawal_poll` (reuse of + the v0.7 event with a v0.8 field requirement), and `lifecycle_transition_observed`. Part A (the v0.7 eight-step load sequence) is unchanged. [#105] +- `schemas/life-package.schema.json` — v0.8 integration: adds optional + top-level `tier` property referencing inlined `$defs.tier_block` + + `$defs.tier_dimensions` (copied verbatim from + `schemas/tier.schema.json` so offline validators do not need to + resolve cross-file `$ref`). Marks `verification_level` as deprecated + in description text (remains REQUIRED for v0.1 back-compat). 10 new + sanity cases in `tools/test_life_package_schema.py` (64 total, up + from 54): tier omitted (back-compat), tier present (consistent + score/level), lowest / highest boundary, score↔level mismatch + rejection, hand-rolled `computed_by` rejection, score out of range, + off-enum dimension, missing required dimension, unknown tier field. +- `tools/build_life_package.py` v0.2 — auto-computes the `tier` block + from the staged package: maps v0.7 `verification_level` to v0.8 + `identity_verification` per `docs/LIFE_TIER_SPEC.md` §6, infers + `asset_completeness` from capability-bearing top-level directories, + defaults the remaining four dimensions conservatively, applies + weighted-average scoring (identity & consent ×2, others ×1), and + bands the result into the 12 Schema D tiers. Adds six + `--tier-` CLI overrides and a `--no-tier` escape hatch for + emitting v0.7-shaped descriptors. `computed_by` is stamped with the + mandatory `@` separator so hand-rolled tier blocks fail + schema validation. +- `docs/LIFE_FILE_STANDARD.md` — adds the `tier` row to the + top-level descriptor table and marks `verification_level` as + deprecated in v0.8, pointing at `docs/LIFE_TIER_SPEC.md` §6 for the + migration mapping. + +### Changed + +- `docs/IMPLEMENTATION_STATUS.md` — bumped to doc version 6.0 with a + new v0.8 increment summary; overall maturity adjusted from ~80% to + ~82% (Asset Architecture + Tier + Assembly spec deltas). +- `docs/GAP_ANALYSIS.md` — baseline moved to post-#106; `.life` + Archive Standard maturity 70% → 82%, `.life` Runtime Standard + 30% → 45%; overall 80% → 82%. +- `ROADMAP.md` — marks `life-format v0.1.0` and `life-runtime v0.1` + as Delivered; adds `life-format v0.1.1` (Asset Architecture) and + `life-runtime v0.1.1` (Assembly) rows as Delivered under + v0.8-asset-architecture; reference runtime deferral moved from + v0.8+ to v0.9+. [#101]: https://github.com/Digital-Life-Repository-Standard/DLRS/issues/101 [#102]: https://github.com/Digital-Life-Repository-Standard/DLRS/issues/102 diff --git a/ROADMAP.md b/ROADMAP.md index 9075a25..f4b6f3b 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -52,7 +52,8 @@ DLRS Hub 致力于建立一个**全球化、标准化、可审计的数字生命 | `.life` 格式版本 | 状态 | 主题 | 主要交付物 | |---|---|---|---| -| **life-format v0.1.0** | 进行中(v0.7-vision-shift) | 双形态档案(pointer / encrypted)+ 不透明 `signature_ref` | `LIFE_FILE_STANDARD.md`、`life-package.schema.json`、`examples/minimal-life-package/` + `build_life.sh` | +| life-format v0.1.0 | 已交付(v0.7-vision-shift) | 双形态档案(pointer / encrypted)+ 不透明 `signature_ref` | `LIFE_FILE_STANDARD.md`、`life-package.schema.json`、`examples/minimal-life-package/` + `build_life.sh` | +| **life-format v0.1.1**(Asset Architecture) | 已交付(v0.8-asset-architecture) | Genesis / Lifecycle / Binding / Tier 四层资产架构 + Schema D Cosmic Evolution 12 档命名 + `tier` 块集成入 `life-package.json` | `LIFE_ASSET_ARCHITECTURE.md`、`LIFE_GENESIS_SPEC.md`、`LIFE_LIFECYCLE_SPEC.md`、`LIFE_BINDING_SPEC.md`、`LIFE_TIER_SPEC.md` + 对应 schemas + `tools/build_life_package.py` v0.2(auto-tier) | | life-format v0.2.0 | 计划 | 加密签名规范(C2PA / 等价方案):把不透明 `signature_ref` 替换为可验证签名;引入完整性签名而非仅 sha256 清单 | 签名规范文档、签名/校验 CLI、reference 验证脚本 | | life-format v0.3.0 | 计划 | 联邦撤回登记表 + 跨 runtime 同步:`withdrawal_endpoint` 之外,引入可解析的"被撤回包标识符"集合,使 runtime 在断网时也能就近查询 | 撤回登记 schema、参考客户端、跨实现互通测试 | @@ -67,12 +68,13 @@ DLRS Hub 致力于建立一个**全球化、标准化、可审计的数字生命 | 运行时协议版本 | 状态 | 主题 | 主要交付物 | |---|---|---|---| -| **life-runtime v0.1** | 进行中(v0.7-vision-shift) | 加载序列、挂载语义、运行时义务(disclosure / forbidden_uses / 撤回轮询 / 身份冒充防护)、终止条件 | `LIFE_RUNTIME_STANDARD.md`(spec only) | +| life-runtime v0.1 | 已交付(v0.7-vision-shift) | 加载序列、挂载语义、运行时义务(disclosure / forbidden_uses / 撤回轮询 / 身份冒充防护)、终止条件 | `LIFE_RUNTIME_STANDARD.md`(spec only) | +| **life-runtime v0.1.1**(Assembly) | 已交付(v0.8-asset-architecture) | Part B:5 阶段 assembly(Verify / Resolve / Assemble / Run / Guard)+ Provider Registry + `LifeCapabilityProvider` 接口 + 分级沙箱 + hosted-API AND-gate + OS 包管理器 bootstrap + 4 条新审计事件(capability_bound / assembly_aborted / withdrawal_poll / lifecycle_transition_observed) | `LIFE_RUNTIME_STANDARD.md` Part B(spec only) | | life-runtime v0.2 | 计划 | 跨实现互操作测试套件 + 加密签名验证(与 life-format v0.2 配套) | conformance suite、`tools/test_life_runtime.py`、签名验证测试 | | life-runtime v0.3 | 计划 | 多 `.life` ensemble 协议(一个 runtime 同时挂载多个 `.life`,各自独立实例 + 跨实例隔离 invariants) | ensemble 协议文档、隔离不变量测试 | **v0.1 范围内非目标**(明确推迟): -- 参考实现(runtime 实现本身推迟到 DLRS v0.8+,或下游项目独立实现) +- 参考实现(runtime 实现本身推迟到 DLRS v0.9+,或下游项目独立实现) - 具体 transport(WebSocket / gRPC / REST 任选;spec 只规定语义) - LLM / TTS / avatar 选型(不在 spec 控制内) diff --git a/docs/GAP_ANALYSIS.md b/docs/GAP_ANALYSIS.md index 033643f..41d3b8b 100644 --- a/docs/GAP_ANALYSIS.md +++ b/docs/GAP_ANALYSIS.md @@ -1,17 +1,17 @@ # DLRS 实现与终极标准的差距分析 -> 版本:v0.7-vision-shift release(2026-04 刷新,epic #79 收尾) -> 上一版基线:v0.6 release(epic #52),整体完成度 88% -> 本次基线:post-v0.7-vision-shift epic #79(PRs #88、#89、#91、#92、#93、#94、#95、#97、#98),整体完成度 **~80%**(ULTIMATE 重新定位带来的 scope 扩展 → 分母后移) +> 版本:v0.8-asset-architecture release(2026-04 刷新,epic #106 收尾) +> 上一版基线:v0.7-vision-shift release(epic #79),整体完成度 80% +> 本次基线:post-v0.8-asset-architecture epic #106(PRs #107、#108、#109、#110、#111、#112、#113、#114、#115、#116 + 本次 tier 集成 PR),整体完成度 **~82%**(Asset Architecture 四补丁 + Tier 系统 + 5-stage assembly 补齐 spec 层,runtime 实现仍是分母最大缺口) ## 📊 执行摘要 -epic #79 把 `DLRS_ULTIMATE.md` 的目标态从"Git-shaped 仓库结构标准"升级为"`.life` 文件格式 + runtime 协议双标准"。本 epic 只交付 specs + schema + example builder,**不**实现 runtime(推迟到 v0.8+)。本文档在保留 v0.6 原本 1–12 节的同时,在 §0 新增两条 ULTIMATE 重定位后的主线维度(`.life` Archive Standard / `.life` Runtime Standard),并在 §13 阐释维度差距。 +epic #106 在 v0.7 `.life` 双标准的基础上补齐 **Asset Architecture 四补丁**(Genesis / Lifecycle / Binding / Assembly)+ **Tier 系统**(6 维加权派生 + Schema D Cosmic Evolution 12 档 Quark→Singularity)+ **5 阶段 assembly 流水线**(Verify / Resolve / Assemble / Run / Guard),把"`.life` 是一个完整的、带自授权、机器可编排的包"从概念落成规范。v0.8 仍然只交付 specs + schema + builder,**不**实现 runtime(推迟到 v0.9+)。本文档在保留 v0.6–v0.7 原有节的基础上,在 §0 扩展两条 `.life` 双标准维度为"含 Asset Architecture + Tier"的现状。 -| 维度 | v0.2.0 基线 | v0.3 | v0.4 | v0.5 | v0.6 | v0.7-vision-shift(本次) | ULTIMATE 目标 | +| 维度 | v0.2.0 基线 | v0.3 | v0.4 | v0.5 | v0.6 | v0.8-asset-architecture(本次) | ULTIMATE 目标 | |---|---|---|---|---|---|---|---| -| **`.life` Archive Standard**(文件格式) | n/a | n/a | n/a | n/a | n/a | **70%**(specs + schema + pointer-mode builder + 54/54 测试用例) | 100%(life-format v0.3.0:encrypted-mode + signing + transfer) | -| **`.life` Runtime Standard**(runtime 协议) | n/a | n/a | n/a | n/a | n/a | **30%**(specs only:加载序列 / mount 语义 / runtime 义务 / 终止触发器 / 伦理全部定义;零 runtime 实现) | 100%(life-runtime v0.3:hot-reload 与联邦审计) | +| **`.life` Archive Standard**(文件格式) | n/a | n/a | n/a | n/a | n/a | **82%**(v0.7 specs + schema + pointer-mode builder + v0.8 Genesis / Lifecycle / Binding / Tier 四组 spec + schema + sanity tests;builder 自动计算 tier) | 100%(life-format v0.3.0:encrypted-mode + signing + transfer) | +| **`.life` Runtime Standard**(runtime 协议) | n/a | n/a | n/a | n/a | n/a | **45%**(v0.7 8 步加载序列 / mount / 义务 / 伦理 + v0.8 Part B 5 阶段 assembly / Provider Registry / LifeCapabilityProvider 接口 / 分级沙箱 / hosted-API AND-gate / OS 包管理器 bootstrap / 4 条新审计事件;仍零 runtime 实现) | 100%(life-runtime v0.3:hot-reload 与联邦审计) | | 仓库与目录 | 90% | 95% | 95% | 95% | 95% | **95%** | 100%(pointer-first 完成) | | 数据采集规范 | 40% | 80% | 85% | 85% | 85% | **85%** | 100% | | 数据分层与存储 | 50% | 65% | 70% | 75% | 80% | **80%** | 100% | @@ -25,11 +25,11 @@ epic #79 把 `DLRS_ULTIMATE.md` 的目标态从"Git-shaped 仓库结构标准" | 跨境 / 法域引擎 | 30% | 50% | 55% | 55% | 55% | **55%** | 100% | | 工具与自动化 | 40% | 75% | 88% | 94% | 96% | **97%**(+ `tools/build_life_package.py` + `tools/test_minimal_life_package.py` + `tools/test_life_package_schema.py` 54 用例) | 100% | -**总体成熟度**:⭐⭐⭐⭐ **80%**(v0.6 88% 基础上因 ULTIMATE 重定位为"`.life` 双标准"带来的分母扩展而回调;已交付维度本身未退步,`.life` 两条新主线以 specs/schema/example 为形式均走完) +**总体成熟度**:⭐⭐⭐⭐ **82%**(v0.7 80% 基础上 +Asset Architecture 四补丁 + Tier 系统 + 5-stage assembly 拉升约 2 分;已交付维度本身持续推进) -- ✅ **已完成**:v0.2–v0.6 的所有内容,加上 v0.7-vision-shift epic #79 交付的:`docs/LIFE_FILE_STANDARD.md` + `docs/LIFE_RUNTIME_STANDARD.md` + `schemas/life-package.schema.json`(54/54 sanity 用例)+ `examples/minimal-life-package/` + `tools/build_life_package.py`(life-format v0.1.0 参考 builder)+ `audit-event.schema.json::event_type.enum` 追加 `package_emitted` + README 第一屏重新定位 + ROADMAP 双轨(`.life Archive Standard` + `.life Runtime Standard` 独立 semver)。 -- 🟡 **部分完成**:`.life` runtime 协议只有 specs,零 runtime 实现(v0.8+);`.life` archive encrypted-mode 与 signing 未实现(life-format v0.2 + v0.3);hosted-API 策略门只落框架,实际 hosted SDK 接入留 v0.7+;Web 审核台原型下沉到 v0.7+。 -- ❌ **未实现**:`.life` 实际 runtime 实例化(v0.8+)、运行层(REST/WS/3D)、RBAC/ReBAC/ABAC、C2PA 实际签发、联邦化注册表同步。 +- ✅ **已完成**:v0.2–v0.7 的所有内容,加上 v0.8-asset-architecture epic #106 交付的:`docs/LIFE_ASSET_ARCHITECTURE.md`(4 补丁总览)+ `docs/LIFE_GENESIS_SPEC.md` / `docs/LIFE_LIFECYCLE_SPEC.md` / `docs/LIFE_BINDING_SPEC.md` / `docs/LIFE_TIER_SPEC.md` + 对应 schemas/ + sanity tests + `docs/LIFE_RUNTIME_STANDARD.md` Part B(5 阶段 assembly)+ `docs/appendix/TIER_NAMING_SCHEMA_D.md`(Quark→Singularity 12 档)+ `life-package.schema.json` 集成 tier 块 + `tools/build_life_package.py` v0.2(自动计算 tier 6 维)。 +- 🟡 **部分完成**:`.life` runtime 协议只有 specs(含 v0.8 Part B 5 阶段),零 runtime 实现(v0.9+);`.life` archive encrypted-mode 与 signing 未实现(life-format v0.2 + v0.3);hosted-API 策略门只落框架 + AND-gate 规则,实际 hosted SDK 接入留 v0.9+;Web 审核台原型下沉到 v0.9+。 +- ❌ **未实现**:`.life` 实际 runtime 实例化(v0.9+)、运行层(REST/WS/3D)、RBAC/ReBAC/ABAC、C2PA 实际签发、联邦化注册表同步。 --- diff --git a/docs/IMPLEMENTATION_STATUS.md b/docs/IMPLEMENTATION_STATUS.md index 896897c..9ef2126 100644 --- a/docs/IMPLEMENTATION_STATUS.md +++ b/docs/IMPLEMENTATION_STATUS.md @@ -4,11 +4,21 @@ ## 📊 快速概览 -**当前版本**: v0.7-vision-shift(epic [#79](https://github.com/Digital-Life-Repository-Standard/DLRS/issues/79) 收尾) -**总体完成度**: ~80%(v0.6 88% 基础上因 ULTIMATE 重新定位带来的 scope 扩展而回调;详见下方 "重新定位的影响") +**当前版本**: v0.8-asset-architecture(epic [#106](https://github.com/Digital-Life-Repository-Standard/DLRS/issues/106) 收尾) +**总体完成度**: ~82%(v0.7 的 ~80% 基础上 +Asset Architecture 四补丁 + Tier 系统 + 5-stage assembly;v0.9 runtime 参考实现仍是最大缺口) **参考标准**: DLRS_ULTIMATE.md(已升级为"`.life` 文件格式 + runtime 协议双标准") -**最近发布**: v0.7-vision-shift epic #79(PRs #88、#89、#91、#92、#93、#94、#95、#97、#98 — 8 个子 issue 全部交付) -**仍在跑**: 无(epic #79 全部 8 个子 issue 已关闭;下一程跨入 v0.8 Encryption + Signing for `.life` Archive,详见 ROADMAP.md)。 +**最近发布**: v0.8-asset-architecture epic #106(PRs #107、#108、#109、#110、#111、#112、#113、#114、#115、#116 — 6 个 sub-issue 全部交付 + 4 个 post-merge follow-up) +**仍在跑**: 无(epic #106 全部 6 个 sub-issue 已合;下一程跨入 v0.9 Reference Runtime Implementation,详见 ROADMAP.md)。 + +### v0.8 主要增量(epic #106) + +- **`docs/LIFE_ASSET_ARCHITECTURE.md`** — 4 补丁(Genesis / Lifecycle / Binding / Assembly)+ Tier 系统的架构总览文档;锁定 D1–D6 决策、Schema D Cosmic Evolution 命名表(Quark → Singularity)、拒绝的备选方案附录、与 v0.7 spec 的衔接说明。 +- **`docs/LIFE_GENESIS_SPEC.md` + `schemas/genesis.schema.json`** — 每个派生资产的 `genesis/.genesis.json` 字段规范(method / source_inputs / compute / consent_scope_checked / audit_event_ref),base_model 作为虚资产、reproducibility_level 三档、consent_scope enum。 +- **`docs/LIFE_LIFECYCLE_SPEC.md` + `schemas/lifecycle.schema.json`** — 包级 supersedes / lifecycle_state / memorial_metadata,资产级 lifecycle + mutation log JSONL,cascade_index.json,semver+hash 双标识,分叉允许 / 合并禁止,mark tainted 撤回级联,7 天 memorial 异议期。 +- **`docs/LIFE_BINDING_SPEC.md` + `schemas/binding.schema.json`** — `binding/runtime_binding.json` 字段(capabilities / orchestration / hard_constraints / surface / hosted_api_preference),capability 词表 hybrid(核心 enum + `x-` 扩展前缀),engine.strict、hard_constraints fail-close。 +- **`docs/LIFE_TIER_SPEC.md` + `schemas/tier.schema.json` + `docs/appendix/TIER_NAMING_SCHEMA_D.md`** — 6 维度(identity / asset_completeness / consent / detail / audit_chain / jurisdiction)加权派生 score 0–100 → 12 档(I–XII),机器字段冻结 + name/glyph 进可演化附录;取代 v0.7 `verification_level`(向后兼容保留)。 +- **`docs/LIFE_RUNTIME_STANDARD.md` Part B** — 5 阶段 assembly(Verify / Resolve / Assemble / Run / Guard)+ Provider Registry + `LifeCapabilityProvider` 接口 + 分级沙箱(built-in / user-installed OS 进程级;`.life`-bundled v0.8 禁止)+ hosted-API AND-gate(binding 允许 AND user opt-in)+ OS 包管理器 bootstrap + 4 条新审计事件(capability_bound / assembly_aborted / withdrawal_poll / lifecycle_transition_observed)。 +- **`schemas/life-package.schema.json` + `tools/build_life_package.py` v0.2** — tier 块集成到 top-level descriptor(optional,v0.7 back-compat),builder 自动从 contents + CLI 覆盖派生 6 维 → 计分 → 带 name/glyph 写回 descriptor;hand-rolled tier 被 `computed_by` 模式(必须 `@` 分隔)直接 schema 层拒绝。 ### ULTIMATE 重新定位的影响(v0.7-vision-shift) @@ -160,6 +170,6 @@ epic #79 把 DLRS 的目标态从"Git-shaped 仓库结构标准"升级为: --- -**文档版本**: 5.0(v0.7-vision-shift release,epic #79 收尾) +**文档版本**: 6.0(v0.8-asset-architecture release,epic #106 收尾) **最后更新**: 2026-04-26 -**参考**: DLRS_ULTIMATE.md(已升级为 `.life` 双标准),docs/GAP_ANALYSIS.md, docs/LIFE_FILE_STANDARD.md, docs/LIFE_RUNTIME_STANDARD.md, ROADMAP.md +**参考**: DLRS_ULTIMATE.md(已升级为 `.life` 双标准),docs/GAP_ANALYSIS.md, docs/LIFE_FILE_STANDARD.md, docs/LIFE_RUNTIME_STANDARD.md, docs/LIFE_ASSET_ARCHITECTURE.md, docs/LIFE_TIER_SPEC.md, ROADMAP.md diff --git a/docs/LIFE_FILE_STANDARD.md b/docs/LIFE_FILE_STANDARD.md index 0b2e90e..9b9209c 100644 --- a/docs/LIFE_FILE_STANDARD.md +++ b/docs/LIFE_FILE_STANDARD.md @@ -176,7 +176,8 @@ asset, used as a stable identifier. | `expires_at` | yes | string (RFC 3339) | Runtimes MUST refuse to mount after this. | | `issued_by` | yes | object | `{role, identifier, signature_ref}` — see below. | | `consent_evidence_ref` | yes | string | Path inside `.life` (e.g., `consent/consent.md`) or external URI. | -| `verification_level` | yes | enum | `"self_attested"`, `"third_party_verified"`, `"memorial_authorized"`. | +| `verification_level` | yes (DEPRECATED v0.8) | enum | `"self_attested"`, `"third_party_verified"`, `"memorial_authorized"`. Retained required for v0.1 back-compat; new packages SHOULD carry a `tier` block and new consumers SHOULD read `tier.dimensions.identity_verification` instead. Mapping table in `docs/LIFE_TIER_SPEC.md` §6. | +| `tier` | no (v0.8+) | object | v0.8 multi-dimensional credit rating. Auto-computed at build time; see `docs/LIFE_TIER_SPEC.md` for the full normative definition. Absent in v0.7 packages; present in v0.8+ packages built with `tools/build_life_package.py` ≥ 0.2.0. | | `withdrawal_endpoint` | yes | string (URI) | Runtimes MUST poll this at session start + at least every 24h. | | `runtime_compatibility` | yes | array of strings | Required runtime interfaces (e.g., `["dlrs-runtime-v0", "openai-chat-tool", "vrm-1.0"]`). | | `ai_disclosure` | yes | enum | Mirrors v0.4 `ai_disclosure`. Minimum is `visible_label_required`. | diff --git a/schemas/life-package.schema.json b/schemas/life-package.schema.json index 07eb7c6..6bfeb81 100644 --- a/schemas/life-package.schema.json +++ b/schemas/life-package.schema.json @@ -86,9 +86,13 @@ }, "verification_level": { "type": "string", - "description": "Strength of identity verification at issuance time. self_attested = subject attested without third-party verification. third_party_verified = an independent verifier (notary, KYC provider) confirmed identity. memorial_authorized = post-mortem package authorised by an executor; requires issued_by.role == memorial_executor.", + "description": "DEPRECATED in v0.8: prefer the `tier` block (specifically `tier.dimensions.identity_verification`). Retained as REQUIRED for v0.1 schema backward-compat so v0.7 runtimes keep validating. Strength of identity verification at issuance time. self_attested = subject attested without third-party verification. third_party_verified = an independent verifier (notary, KYC provider) confirmed identity. memorial_authorized = post-mortem package authorised by an executor; requires issued_by.role == memorial_executor. See docs/LIFE_TIER_SPEC.md §6 for the migration mapping.", "enum": ["self_attested", "third_party_verified", "memorial_authorized"] }, + "tier": { + "$ref": "#/$defs/tier_block", + "description": "v0.8 multi-dimensional tier block. OPTIONAL for v0.1 back-compat: packages built by v0.7 tools omit it, v0.8 builders populate it. When present, it takes precedence over `verification_level` for credit-rating purposes. See `docs/LIFE_TIER_SPEC.md` and `schemas/tier.schema.json` for the authoritative definition — the `$defs.tier_block` and `$defs.tier_dimensions` below are copied verbatim from `tier.schema.json` to keep this file self-contained for offline validators." + }, "withdrawal_endpoint": { "type": "string", "format": "uri", @@ -280,5 +284,94 @@ } } } - ] + ], + "$defs": { + "tier_block": { + "type": "object", + "description": "v0.8 tier block — multi-dimensional credit rating for a `.life` package. Shape copied verbatim from `schemas/tier.schema.json::$defs.tier_block` so this schema remains self-contained (no cross-file $ref resolution required).", + "additionalProperties": false, + "required": [ + "score", + "level", + "name", + "glyph", + "dimensions", + "computed_at", + "computed_by" + ], + "properties": { + "score": { + "type": "integer", + "description": "Composite tier score on a 0–100 scale. Auto-computed by the builder from the six `dimensions` using default weights: `consent_completeness` ×2, `identity_verification` ×2, others ×1.", + "minimum": 0, + "maximum": 100 + }, + "level": { + "type": "string", + "description": "Roman-numeral tier level I–XII. Derived deterministically from `score` via the boundaries fixed in `docs/appendix/TIER_NAMING_SCHEMA_D.md`.", + "enum": ["I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII"] + }, + "name": { + "type": "string", + "description": "Human-readable tier name from the active naming appendix. Schema D: I=Quark, II=Atom, III=Molecule, IV=Stardust, V=Nebula, VI=Protostar, VII=Main Sequence, VIII=Red Giant, IX=White Dwarf, X=Neutron Star, XI=Pulsar, XII=Singularity.", + "minLength": 1, + "maxLength": 64 + }, + "glyph": { + "type": "string", + "description": "Single visual glyph (1–16 codepoints) representing the tier. Schema D: ⋅ ⊙ ⋮⋮ ✧ 🌫 ✦ ★ ◉ ⚪ ⚫ ◎ ●.", + "minLength": 1, + "maxLength": 16 + }, + "dimensions": { "$ref": "#/$defs/tier_dimensions" }, + "computed_at": { + "type": "string", + "format": "date-time", + "description": "RFC 3339 / ISO 8601 UTC timestamp at which the tier was computed. SHOULD equal `created_at`." + }, + "computed_by": { + "type": "string", + "description": "Builder identifier `@`. The `@` separator is mandatory; hand-rolled tier blocks are explicitly rejected by the pattern.", + "pattern": "^[A-Za-z0-9_./-]+@[A-Za-z0-9_.+-]+$", + "minLength": 3, + "maxLength": 256 + } + }, + "allOf": [ + { "description": "Score → level binding for tier I (0–8).", "if": { "properties": { "score": { "minimum": 0, "maximum": 8 } }, "required": ["score"] }, "then": { "properties": { "level": { "const": "I" } } } }, + { "description": "Score → level binding for tier II (9–16).", "if": { "properties": { "score": { "minimum": 9, "maximum": 16 } }, "required": ["score"] }, "then": { "properties": { "level": { "const": "II" } } } }, + { "description": "Score → level binding for tier III (17–24).", "if": { "properties": { "score": { "minimum": 17, "maximum": 24 } }, "required": ["score"] }, "then": { "properties": { "level": { "const": "III" } } } }, + { "description": "Score → level binding for tier IV (25–32).", "if": { "properties": { "score": { "minimum": 25, "maximum": 32 } }, "required": ["score"] }, "then": { "properties": { "level": { "const": "IV" } } } }, + { "description": "Score → level binding for tier V (33–40).", "if": { "properties": { "score": { "minimum": 33, "maximum": 40 } }, "required": ["score"] }, "then": { "properties": { "level": { "const": "V" } } } }, + { "description": "Score → level binding for tier VI (41–50).", "if": { "properties": { "score": { "minimum": 41, "maximum": 50 } }, "required": ["score"] }, "then": { "properties": { "level": { "const": "VI" } } } }, + { "description": "Score → level binding for tier VII (51–60).", "if": { "properties": { "score": { "minimum": 51, "maximum": 60 } }, "required": ["score"] }, "then": { "properties": { "level": { "const": "VII" } } } }, + { "description": "Score → level binding for tier VIII (61–68).", "if": { "properties": { "score": { "minimum": 61, "maximum": 68 } }, "required": ["score"] }, "then": { "properties": { "level": { "const": "VIII" } } } }, + { "description": "Score → level binding for tier IX (69–76).", "if": { "properties": { "score": { "minimum": 69, "maximum": 76 } }, "required": ["score"] }, "then": { "properties": { "level": { "const": "IX" } } } }, + { "description": "Score → level binding for tier X (77–84).", "if": { "properties": { "score": { "minimum": 77, "maximum": 84 } }, "required": ["score"] }, "then": { "properties": { "level": { "const": "X" } } } }, + { "description": "Score → level binding for tier XI (85–92).", "if": { "properties": { "score": { "minimum": 85, "maximum": 92 } }, "required": ["score"] }, "then": { "properties": { "level": { "const": "XI" } } } }, + { "description": "Score → level binding for tier XII (93–100).", "if": { "properties": { "score": { "minimum": 93, "maximum": 100 } }, "required": ["score"] }, "then": { "properties": { "level": { "const": "XII" } } } } + ] + }, + "tier_dimensions": { + "type": "object", + "description": "The six independent tier dimensions. Shape copied verbatim from `schemas/tier.schema.json::$defs.tier_dimensions`.", + "additionalProperties": false, + "required": [ + "identity_verification", + "asset_completeness", + "consent_completeness", + "detail_level", + "audit_chain_strength", + "jurisdiction_clarity" + ], + "properties": { + "identity_verification": { "type": "string", "enum": ["unverified", "self_attested", "email_verified", "id_verified", "kyc_verified", "notarized"] }, + "asset_completeness": { "type": "string", "enum": ["minimal", "partial", "standard", "comprehensive", "archive_grade"] }, + "consent_completeness": { "type": "string", "enum": ["none", "text_only", "signed", "notarized", "multi_party_attested"] }, + "detail_level": { "type": "string", "enum": ["low_fidelity", "medium", "high_fidelity", "cinematic"] }, + "audit_chain_strength": { "type": "string", "enum": ["minimal", "linked", "signed_chain", "notarized_chain"] }, + "jurisdiction_clarity": { "type": "string", "enum": ["unspecified", "declared", "cross_validated", "court_recognized"] } + } + } + } } diff --git a/tools/build_life_package.py b/tools/build_life_package.py index aa8cfd5..7749c3f 100755 --- a/tools/build_life_package.py +++ b/tools/build_life_package.py @@ -102,6 +102,141 @@ def _canonical_dump(obj: dict) -> str: return json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False) +# -------- v0.8 tier auto-compute ----------------------------------------- +# +# These tables and weights mirror the normative definitions in +# `docs/LIFE_TIER_SPEC.md` §3–§5 and `schemas/tier.schema.json`. The +# builder computes the tier block deterministically from package +# contents + CLI-supplied hints. Hand-rolled tier blocks are forbidden +# (the schema rejects `computed_by` without a `@` separator); callers +# MUST let the builder write this field. + +# Ordered dimension enums (low → high). Copied verbatim from the +# schema's enum arrays so any divergence is a test-visible bug. +_TIER_ENUMS: dict[str, list[str]] = { + "identity_verification": ["unverified", "self_attested", "email_verified", "id_verified", "kyc_verified", "notarized"], + "asset_completeness": ["minimal", "partial", "standard", "comprehensive", "archive_grade"], + "consent_completeness": ["none", "text_only", "signed", "notarized", "multi_party_attested"], + "detail_level": ["low_fidelity", "medium", "high_fidelity", "cinematic"], + "audit_chain_strength": ["minimal", "linked", "signed_chain", "notarized_chain"], + "jurisdiction_clarity": ["unspecified", "declared", "cross_validated", "court_recognized"], +} + +# Weights from the spec §4.1. identity and consent are doubled. +_TIER_WEIGHTS: dict[str, int] = { + "identity_verification": 2, + "consent_completeness": 2, + "asset_completeness": 1, + "detail_level": 1, + "audit_chain_strength": 1, + "jurisdiction_clarity": 1, +} + +# Score → (level, name, glyph). Ranges are inclusive on both ends and +# exactly match `schemas/tier.schema.json::$defs.tier_block.allOf` + +# `docs/appendix/TIER_NAMING_SCHEMA_D.md`. +_TIER_BANDS: list[tuple[int, int, str, str, str]] = [ + ( 0, 8, "I", "Quark", "\u22c5"), + ( 9, 16, "II", "Atom", "\u2299"), + ( 17, 24, "III", "Molecule", "\u22ee\u22ee"), + ( 25, 32, "IV", "Stardust", "\u2727"), + ( 33, 40, "V", "Nebula", "\U0001f32b"), + ( 41, 50, "VI", "Protostar", "\u2726"), + ( 51, 60, "VII", "Main Sequence", "\u2605"), + ( 61, 68, "VIII", "Red Giant", "\u25c9"), + ( 69, 76, "IX", "White Dwarf", "\u26aa"), + ( 77, 84, "X", "Neutron Star", "\u26ab"), + ( 85, 92, "XI", "Pulsar", "\u25ce"), + ( 93, 100, "XII", "Singularity", "\u25cf"), +] + +# Map v0.7 verification_level → v0.8 identity_verification default +# (docs/LIFE_TIER_SPEC.md §6). +_VL_TO_IDENTITY: dict[str, str] = { + "self_attested": "self_attested", + "third_party_verified": "id_verified", + "memorial_authorized": "notarized", +} + +BUILDER_VERSION = "0.2.0" +BUILDER_ID = f"tools/build_life_package.py@{BUILDER_VERSION}" + + +def _infer_asset_completeness(contents: list[dict]) -> str: + """Heuristic — count capability-bearing top-level directories in the + staged tree (pointers/ + assets/ + memory/ + knowledge/ + …). The + builder only sees filenames, so this is intentionally coarse.""" + top_dirs: set[str] = set() + for c in contents: + parts = c["path"].split("/", 1) + if len(parts) == 2: + top_dirs.add(parts[0]) + cap_dirs = top_dirs & {"pointers", "assets", "memory", "knowledge", "voice", "avatar", "persona"} + n = len(cap_dirs) + if n <= 1: + return "minimal" + if n <= 3: + return "partial" + if n <= 5: + return "standard" + return "comprehensive" # "archive_grade" is issuer-declared only + + +def _compute_tier( + contents: list[dict], + *, + verification_level: str, + computed_at: str, + overrides: dict[str, str] | None = None, +) -> dict: + """Deterministically compute the v0.8 tier block from package + metadata. `overrides` lets the CLI lift specific dimensions above + the conservative defaults (e.g. `--tier-detail-level high_fidelity` + for a studio-recorded package). Score is rounded half-to-even per + Python's `round`; the schema's ±0 tolerance is the authoritative + check.""" + overrides = overrides or {} + + dims: dict[str, str] = { + "identity_verification": _VL_TO_IDENTITY[verification_level], + "asset_completeness": _infer_asset_completeness(contents), + # Consent + audit + jurisdiction default conservatively; issuers + # lift them via CLI flags once the corresponding package + # artefacts actually exist. + "consent_completeness": "text_only", + "detail_level": "medium", + "audit_chain_strength": "linked", + "jurisdiction_clarity": "unspecified", + } + for k, v in overrides.items(): + if k not in _TIER_ENUMS: + raise SystemExit(f"unknown tier dimension: {k}") + if v not in _TIER_ENUMS[k]: + raise SystemExit(f"invalid level '{v}' for tier dimension {k}") + dims[k] = v + + total_w = sum(_TIER_WEIGHTS.values()) + acc = 0.0 + for dim, level in dims.items(): + idx = _TIER_ENUMS[dim].index(level) + max_idx = len(_TIER_ENUMS[dim]) - 1 + acc += (idx / max_idx) * _TIER_WEIGHTS[dim] + score = max(0, min(100, round(acc * 100 / total_w))) + + for low, high, level, name, glyph in _TIER_BANDS: + if low <= score <= high: + return { + "score": score, + "level": level, + "name": name, + "glyph": glyph, + "dimensions": dims, + "computed_at": computed_at, + "computed_by": BUILDER_ID, + } + raise SystemExit(f"tier banding failed for score {score}") # unreachable + + def _sha256_of(s: str) -> str: return "sha256:" + hashlib.sha256(s.encode("utf-8")).hexdigest() @@ -296,6 +431,12 @@ def build(args: argparse.Namespace) -> int: contents.append({"path": rel, "sha256": sha, "size": size}) # Step 4: build life-package.json. + tier_overrides: dict[str, str] = {} + for dim in _TIER_ENUMS: + val = getattr(args, f"tier_{dim}", None) + if val is not None: + tier_overrides[dim] = val + descriptor = { "schema_version": "0.1.0", "package_id": package_id, @@ -317,6 +458,13 @@ def build(args: argparse.Namespace) -> int: "audit_event_ref": f"audit/events.jsonl#L{line_number}", "contents": contents, } + if not args.no_tier: + descriptor["tier"] = _compute_tier( + contents, + verification_level=args.verification_level, + computed_at=created_at, + overrides=tier_overrides, + ) _validate_descriptor(descriptor) pkg_json_path = staging_dir / "life-package.json" @@ -423,6 +571,22 @@ def main() -> int: action="store_true", help="Keep the .staging-/ directory after build (debugging)", ) + # v0.8 tier overrides — any dimension left unset is filled by + # _compute_tier with the conservative default described in its + # docstring. + for dim, levels in _TIER_ENUMS.items(): + p.add_argument( + f"--tier-{dim.replace('_', '-')}", + dest=f"tier_{dim}", + choices=levels, + default=None, + help=f"Override the {dim} tier dimension (default: auto)", + ) + p.add_argument( + "--no-tier", + action="store_true", + help="Omit the v0.8 tier block (v0.7 back-compat output)", + ) args = p.parse_args() return build(args) diff --git a/tools/test_life_package_schema.py b/tools/test_life_package_schema.py index 807d626..c8a2cd7 100644 --- a/tools/test_life_package_schema.py +++ b/tools/test_life_package_schema.py @@ -335,6 +335,94 @@ def main() -> int: missing.pop(required_field) cases.append((f"missing required field {required_field}", missing, False)) + # --- tier integration (v0.8) ---------------------------------------- + def _good_tier(score: int = 54, level: str = "VII", name: str = "Main Sequence", glyph: str = "\u2605") -> dict: + return { + "score": score, + "level": level, + "name": name, + "glyph": glyph, + "dimensions": { + "identity_verification": "id_verified", + "asset_completeness": "comprehensive", + "consent_completeness": "signed", + "detail_level": "high_fidelity", + "audit_chain_strength": "linked", + "jurisdiction_clarity": "declared", + }, + "computed_at": "2026-04-26T12:00:00Z", + "computed_by": "tools/build_life_package.py@0.2.0", + } + + # Happy path: tier omitted (v0.7 back-compat) + no_tier = _good_pointer_pkg() + cases.append(("tier omitted (v0.7 back-compat)", no_tier, True)) + + # Happy path: tier present with consistent score/level + with_tier = _good_pointer_pkg() + with_tier["tier"] = _good_tier() + cases.append(("tier present + consistent (score 54 → VII)", with_tier, True)) + + # Happy path: lowest-bound score/level (0 → I) + low_tier = _good_pointer_pkg() + low_tier["tier"] = _good_tier(score=0, level="I", name="Quark", glyph="\u22c5") + low_tier["tier"]["dimensions"] = { + "identity_verification": "unverified", + "asset_completeness": "minimal", + "consent_completeness": "none", + "detail_level": "low_fidelity", + "audit_chain_strength": "minimal", + "jurisdiction_clarity": "unspecified", + } + cases.append(("tier lowest bound (0 → I Quark)", low_tier, True)) + + # Happy path: highest-bound score/level (100 → XII) + high_tier = _good_pointer_pkg() + high_tier["tier"] = _good_tier(score=100, level="XII", name="Singularity", glyph="\u25cf") + high_tier["tier"]["dimensions"] = { + "identity_verification": "notarized", + "asset_completeness": "archive_grade", + "consent_completeness": "multi_party_attested", + "detail_level": "cinematic", + "audit_chain_strength": "notarized_chain", + "jurisdiction_clarity": "court_recognized", + } + cases.append(("tier highest bound (100 → XII Singularity)", high_tier, True)) + + # Negative: score/level mismatch + mismatch = _good_pointer_pkg() + mismatch["tier"] = _good_tier(score=54, level="VIII") + cases.append(("tier score 54 with level VIII rejected", mismatch, False)) + + # Negative: hand-rolled computed_by without @ + hand_rolled = _good_pointer_pkg() + hand_rolled["tier"] = _good_tier() + hand_rolled["tier"]["computed_by"] = "human manual" + cases.append(("tier computed_by missing `@` separator rejected", hand_rolled, False)) + + # Negative: score out of range + score_over = _good_pointer_pkg() + score_over["tier"] = _good_tier(score=101, level="XII") + cases.append(("tier score > 100 rejected", score_over, False)) + + # Negative: dimension off-enum + bad_dim = _good_pointer_pkg() + bad_dim["tier"] = _good_tier() + bad_dim["tier"]["dimensions"]["identity_verification"] = "fingerprint_verified" + cases.append(("tier dimension off-enum rejected", bad_dim, False)) + + # Negative: dimensions missing a required key + missing_dim = _good_pointer_pkg() + missing_dim["tier"] = _good_tier() + del missing_dim["tier"]["dimensions"]["jurisdiction_clarity"] + cases.append(("tier dimensions missing jurisdiction_clarity", missing_dim, False)) + + # Negative: unknown field on tier + extra_tier_field = _good_pointer_pkg() + extra_tier_field["tier"] = _good_tier() + extra_tier_field["tier"]["secret_bonus"] = 42 + cases.append(("tier unknown field rejected", extra_tier_field, False)) + # --- run ------------------------------------------------------------- failures = 0 for name, doc, expect_valid in cases: diff --git a/tools/test_minimal_life_package.py b/tools/test_minimal_life_package.py index beab0eb..222a3f3 100644 --- a/tools/test_minimal_life_package.py +++ b/tools/test_minimal_life_package.py @@ -143,6 +143,17 @@ def main() -> int: assert "impersonation_for_fraud" in descriptor["forbidden_uses"] assert "encryption" not in descriptor, "pointer mode must not have encryption block" + # v0.8 tier block: present and well-formed by default. + assert "tier" in descriptor, "v0.8 builder must emit a tier block" + t = descriptor["tier"] + assert isinstance(t["score"], int) and 0 <= t["score"] <= 100 + assert t["level"] in {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII"} + assert t["computed_by"].startswith("tools/build_life_package.py@") + assert "@" in t["computed_by"] + for dim in ("identity_verification", "asset_completeness", "consent_completeness", + "detail_level", "audit_chain_strength", "jurisdiction_clarity"): + assert dim in t["dimensions"], f"tier.dimensions missing {dim}" + _verify_descriptor_against_zip(zf, descriptor) _verify_audit_chain(zf, descriptor)