Conversation
…idation Implements NDO Layer 0 foundation per Epic #78 sub-issues #73 and #74. Changes: - Add LifecycleStage enum (7 stages: Ideation→EndOfLife) — REQ-NDO-LC-01..LC-07 - Add PropertyRegime enum (6 variants: Private, Commons, Collective, Pool, CommonPool, Nondominium) - Add ResourceNature enum (5 variants: Physical, Digital, Service, Hybrid, Information) - Add NondominiumIdentity entry struct (permanent Layer 0 anchor, original ActionHash is stable identity) - Extend EntryTypes with NondominiumIdentity variant - Extend LinkTypes with AllNdos and AgentToNdo discovery anchors - Add validate_create_nondominium_identity (name not empty) - Add validate_update_nondominium_identity (immutability enforcement — only lifecycle_stage may change) - Add validate_delete_nondominium_identity (always Invalid — Layer 0 is permanent) - Extend validate() to handle NondominiumIdentity create/update/delete ops - Add ndo_identity coordinator module: create_ndo, get_ndo, update_lifecycle_stage - create_ndo sets initiator from agent_info, creates AllNdos and AgentToNdo links - get_ndo resolves HDK update chain iteratively via get_details loop - update_lifecycle_stage resolves chain to latest, mutates only lifecycle_stage field Closes #73 Closes #74
Security (HIGH): - validate_update_nondominium_identity now accepts Update action and checks action.author == original.initiator — only the initiator may advance the lifecycle stage - update_lifecycle_stage coordinator checks caller == current_entry.initiator before update, returns ResourceError::NotAuthor if unauthorized Refactor: - Extract resolve_latest_ndo_record() private helper to eliminate duplicated get_details chain traversal between get_ndo and update_lifecycle_stage - Replace two unwrap() calls in chain traversal with ok_or(ResourceError::EntryOperationFailed) Documentation: - Add NondominiumIdentity Entry (Layer 0) section to resource_zome.md with entry struct, all three enums, immutability rules, and discovery link types - Add NDO Layer 0 Management API section documenting create_ndo, get_ndo, update_lifecycle_stage with inputs, outputs, and authorization notes
…0 impl - Remove stale TODO intro that said NondominiumIdentity is "not started"; replace with accurate Layer 1/2 post-MVP note - Fix LifecycleStage TARGET code block in ResourceState section: 10-stage spec → 7-stage implementation (Ideation/Spec/Dev/Production/ Hibernating/Deprecated/EndOfLife) - Update IMPLEMENTATION_STATUS: add NDO Layer 0 subsection under Phase 1 Resource Management; split NDO post-MVP row into Layer 0 (complete) and Layers 1+2 (not started); annotate which REQ-NDO-L0-* are met - Note REQ-NDO-L0-05 (EconomicEvent ref) and REQ-NDO-L0-07 (facet anchors) not yet enforced - Update CLAUDE.md Phase 2 status with NDO Layer 0 complete bullet
…ules Expand LifecycleStage from 7 to 10 spec-compliant variants (Prototype, Stable, Distributed, Active added; Production renamed to Stable) and wire full integrity validation for the state machine defined in ndo_prima_materia.md §5.3. - LifecycleStage: 10 variants with phase comments (Emergence/Maturity/Operation/ Suspension/Terminal) matching spec §5.1 exactly - NondominiumIdentity: add successor_ndo_hash field (#[serde(default)]) required when transitioning to Deprecated (REQ-NDO-LC-06) - LinkTypes: add NdoToSuccessor and NdoToTransitionEvent - validate_create_nondominium_identity: block Hibernating/Deprecated/EndOfLife as initial stages; block non-None successor_ndo_hash at creation - validate_update_nondominium_identity: enforce transition allowlist (REQ-NDO-LC-04); Hibernating is the only reversible pause state; Deprecated/EndOfLife are terminal; require successor hash for Deprecated transition with must_get_valid_record check; guard successor_ndo_hash immutability once set - UpdateLifecycleStageInput: add successor_ndo_hash and transition_event_hash fields - update_lifecycle_stage: pre-flight Deprecated check; create NdoToSuccessor link on Deprecated; create NdoToTransitionEvent link when event hash provided (REQ-NDO-L0-05) - create_ndo: set successor_ndo_hash: None in struct literal - docs: update resource_zome.md with 10-stage table, reversibility note, new API surface Closes requirements: REQ-NDO-LC-04, REQ-NDO-LC-06 (partial REQ-NDO-L0-05 — link only, full cross-zome event validation deferred until zome_gouvernance calls are stable)
- Add get_all_ndos and get_my_ndos coordinator functions with GetAllNdosOutput output type, matching get_all_economic_resources and get_my_economic_resources patterns (AllNdos path anchor traversal, AgentToNdo raw link return) - Replace unwrap() in update_lifecycle_stage Deprecated branch with ok_or_else(ResourceError::InvalidInput) for centralized error handling - Fix LifecycleStage enum in resource_zome.md: restore all 10 stages (Prototype, Stable, Distributed, Active were missing; Production was a non-existent variant) - Fix 'A Production resource' prose to 'An Active resource' - Document NDO-as-identifier architecture decision: lifecycle_stage at creation reflects actual registration-time state, not chronological origin; non-Ideation initial stages are valid and intentional
Resolved conflict between feat/ndo-layer0-identity-lifecycle and dev: - Keep NDO Layer 0 section (new in this PR) - Keep detailed Post-MVP table with Layer 0 as Complete - Update summary table: NDO Layer 0 now ✅ Complete
…d clarifying comments - Fix IMPLEMENTATION_STATUS.md: LifecycleStage has 10 stages (not 7); remove wrong 'Production' stage name — correct names are Prototype, Stable, Distributed, Active - Add tie-breaking comment to resolve_latest_ndo_record explaining max_by_key behavior when two updates share the same timestamp - Add description immutability rationale comment: frozen intentionally to prevent retroactive reframing; suggests post-MVP notes field for editorial corrections - Add TODO for successor_ndo_hash type validation: currently only record existence is checked via must_get_valid_record; entry type not verified (REQ-NDO-LC-06 follow-up)
| current_entry.lifecycle_stage = input.new_stage.clone(); | ||
| current_entry.successor_ndo_hash = input.successor_ndo_hash.clone(); |
There was a problem hiding this comment.
🔴 Unconditional overwrite of successor_ndo_hash makes Deprecated → EndOfLife transition impossible
Line 202 unconditionally sets current_entry.successor_ndo_hash = input.successor_ndo_hash.clone(). When transitioning from Deprecated to EndOfLife, the caller would naturally pass successor_ndo_hash: None (since it's only required for the Deprecated transition). This overwrites the existing Some(X) with None. However, the integrity validation at dnas/nondominium/zomes/integrity/zome_resource/src/lib.rs:461-467 correctly enforces that successor_ndo_hash is immutable once set — it rejects the update because None != Some(X). The result is that the Deprecated → EndOfLife transition (the only allowed exit from Deprecated per line 488) always fails with a validation error, making Deprecated a fully terminal state and breaking the designed lifecycle.
Integrity validation that correctly rejects the overwritten field
// integrity lib.rs:461-467
if original.successor_ndo_hash.is_some()
&& new_entry.successor_ndo_hash != original.successor_ndo_hash
{
return Ok(ValidateCallbackResult::Invalid(
"NondominiumIdentity successor_ndo_hash is immutable once set".to_string(),
));
}| current_entry.lifecycle_stage = input.new_stage.clone(); | |
| current_entry.successor_ndo_hash = input.successor_ndo_hash.clone(); | |
| current_entry.lifecycle_stage = input.new_stage.clone(); | |
| if input.new_stage == LifecycleStage::Deprecated { | |
| current_entry.successor_ndo_hash = input.successor_ndo_hash.clone(); | |
| } |
Was this helpful? React with 👍 or 👎 to provide feedback.
| LifecycleStage::EndOfLife, | ||
| ], | ||
| // Hibernating is the only reversible pause state (REQ-NDO-LC-04) | ||
| LifecycleStage::Hibernating => &[LifecycleStage::Active, LifecycleStage::Deprecated], |
There was a problem hiding this comment.
🚩 Hibernating can transition to Deprecated but not EndOfLife — may be intentional restriction
The state machine at lib.rs:486 allows Hibernating → [Active, Deprecated] but not Hibernating → EndOfLife. This means a hibernating resource must either be reactivated first (Active) or deprecated with a successor before it can reach end-of-life. Going directly from Hibernating to EndOfLife requires going through either Active or Deprecated first. This seems intentional — a hibernating resource should either wake up or be formally superseded — but it's worth confirming this matches the requirements in ndo_prima_materia.md §5.3.
Was this helpful? React with 👍 or 👎 to provide feedback.
IMPLEMENTATION_STATUS.md: - Add successor_ndo_hash to NondominiumIdentity field list - Add get_all_ndos and get_my_ndos to API list - Expand REQ coverage: add deferred status for REQ-NDO-LC-02 (governance-as-operator), LC-03 (automatic EconomicEvent per transition), LC-05 (EndOfLife challenge period), LC-07 (role-based authorization per §5.3) - Note ResourceNature 5-variant extension in enum bullet integrity/zome_resource/src/lib.rs: - Fix REQ citations: validate_delete now cites L0-03 (never deletable) + L0-06 (tombstone); validate_update cites L0-04 not L0-03; initiator check cites LC-07 - Add code comment explaining ResourceNature 5-variant extension beyond spec §8.2 - Add code comment documenting successor_ndo_hash field+link dual-tracking decision (spec §8.1 has link only; field added for entry-level inspectability) coordinator/ndo_identity.rs: - Fix REQ citations: update_lifecycle_stage and initiator check cite LC-07 (not L0-03) PR body: - Add two Decisions table rows: ResourceNature extension rationale, successor_ndo_hash field+link dual-tracking rationale
Intent
NDO Layer 0 introduces a permanent identity anchor (
NondominiumIdentity) that exists from the moment a resource is conceived — before specification, before any physical instance. The existing model (EconomicResource+ResourceSpecification) only represents resources already in stable operational form. Layer 0 fills the ideation-to-end-of-life gap, giving every resource a stable DHT identity that survives the full lifecycle.This is the foundation that all future NDO Layers 1 (Specification) and 2 (Process) will attach to.
Changes
New entry type —
NondominiumIdentity(integrity)Permanent identity anchor. All fields immutable after creation except
lifecycle_stage(andsuccessor_ndo_hash, set exactly once during the Deprecated transition). The originalActionHashfromcreate_ndois the stable Layer 0 identity forever.New enums (integrity)
LifecycleStage(10 stages per spec §5.1): Ideation → Specification → Development → Prototype → Stable → Distributed → Active → Hibernating → Deprecated → EndOfLifePropertyRegime(6 variants: Private, Commons, Collective, Pool, CommonPool, Nondominium)ResourceNature(5 variants: Physical, Digital, Service, Hybrid, Information)New link types (integrity)
AllNdos— global discovery anchor (ndo_identitiespath → action hashes)AgentToNdo— per-initiator discoveryNdoToSuccessor— deprecated NDO → successor NondominiumIdentity (REQ-NDO-LC-06)NdoToTransitionEvent— NDO → triggering EconomicEvent (REQ-NDO-L0-05; link only, cross-zome validation deferred)Validation (integrity)
successor_ndo_hashmust be NoneHibernating → Activeis the only reactivation path (REQ-NDO-LC-04); Deprecated/EndOfLife are terminal; transitioning to Deprecated requiressuccessor_ndo_hashvalidated viamust_get_valid_record(REQ-NDO-LC-06);successor_ndo_hashimmutable once setInvalid— Layer 0 is permanentNew coordinator module —
ndo_identity.rscreate_ndo: creates entry, sets initiator fromagent_info, createsAllNdosandAgentToNdolinksget_ndo: resolves the HDK update chain iteratively viaget_detailsloop, always returns the latest versionget_all_ndos: global anchor traversal viaAllNdospath, returns latest version of each NDO (silent filtering for DHT unavailability)get_my_ndos: returns rawAgentToNdolinks for the calling agentupdate_lifecycle_stage: pre-flight Deprecated check; writeslifecycle_stage+successor_ndo_hash; createsNdoToSuccessoron Deprecated; createsNdoToTransitionEventwhen event hash providedDecisions
NdoUpdateslinks for chain resolution (like ResourceSpec pattern)get_detailsloop is semantically correct and spec-compliantResourceStateenumtransition_event_hashvalidation against governance zomezome_gouvernanceare currently disabled. Link is created when hash is provided; validation deferred to a follow-up once cross-zome calls are stablelifecycle_stagetoIdeationonlyActive), not forced through a syntheticIdeationentry. Forcing Ideation-only would require fabricating DHT history for brownfield resources and block migration of existingEconomicResourceentries when Layers 1/2 activate.Hibernating,Deprecated, andEndOfLifeare still rejected at creation.ResourceNaturekept to 3 spec variants (Digital, Physical, Hybrid)ServiceandInformationbecause they are first-class resource natures in the OVN/ValueFlows context — software services and knowledge assets are distinct from digital files (Hybrid) and from pure data. These additions are backwards-compatible and do not conflict with any spec constraint. The spec's 3-variant set was written before the full OVN resource taxonomy was modelled.successor_ndo_hashonly as a link (spec §8.1 approach)NdoToSuccessorlink only, with no field on the entry. This implementation stores it as both an entry field and a link: the field makes the successor inspectable on the entry itself without a link traversal (important for DHT-light clients), while the link preserves graph-query compatibility. The#[serde(default)]attribute ensures zero breaking change for existing records written before this field existed.How to test
nix develop --command bun run build:zomes # must pass with no errorsSweettest scenarios for these functions are tracked in #76 (NDO Layer 0 test suite), which can be scaffolded in parallel and will pass once #73+#74+#75 are merged.
Documentation
Updated
documentation/zomes/resource_zome.mdwith the 10-stage lifecycle table, reversibility/terminal distinction, newsuccessor_ndo_hashfield, updatedupdate_lifecycle_stageAPI surface including conditional links,get_all_ndos/get_my_ndosAPI docs, and NDO-as-identifier architecture design note.Related
Related issues:
Closes [Layer 0] NondominiumIdentity entry type with PropertyRegime and ResourceNature enums #73
Closes [Layer 0] LifecycleStage enum, update function, and immutability validation #74
Depends on: (none — first in sequence)
Follow-up: [Layer 0] DHT discovery anchors for NondominiumIdentity #75 (DHT categorization anchors), [Sweettest] NDO Layer 0 test suite #76 (Sweettest), [UI] NDO Layer 0: NdoCreateForm and NdoIdentityCard components #77 (UI)