Skip to content

feat(resource): add NondominiumIdentity entry type with lifecycle validation#80

Merged
Soushi888 merged 8 commits intodevfrom
feat/ndo-layer0-identity-lifecycle
Apr 6, 2026
Merged

feat(resource): add NondominiumIdentity entry type with lifecycle validation#80
Soushi888 merged 8 commits intodevfrom
feat/ndo-layer0-identity-lifecycle

Conversation

@Soushi888
Copy link
Copy Markdown
Collaborator

@Soushi888 Soushi888 commented Apr 6, 2026

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 (and successor_ndo_hash, set exactly once during the Deprecated transition). The original ActionHash from create_ndo is 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 → EndOfLife
  • PropertyRegime (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_identities path → action hashes)
  • AgentToNdo — per-initiator discovery
  • NdoToSuccessor — deprecated NDO → successor NondominiumIdentity (REQ-NDO-LC-06)
  • NdoToTransitionEvent — NDO → triggering EconomicEvent (REQ-NDO-L0-05; link only, cross-zome validation deferred)

Validation (integrity)

  • Create: name must not be empty; initial stage must not be Hibernating/Deprecated/EndOfLife; successor_ndo_hash must be None
  • Update: state machine transition allowlist enforced (§5.3); Hibernating → Active is the only reactivation path (REQ-NDO-LC-04); Deprecated/EndOfLife are terminal; transitioning to Deprecated requires successor_ndo_hash validated via must_get_valid_record (REQ-NDO-LC-06); successor_ndo_hash immutable once set
  • Delete: always Invalid — Layer 0 is permanent

New coordinator module — ndo_identity.rs

  • create_ndo: creates entry, sets initiator from agent_info, creates AllNdos and AgentToNdo links
  • get_ndo: resolves the HDK update chain iteratively via get_details loop, always returns the latest version
  • get_all_ndos: global anchor traversal via AllNdos path, returns latest version of each NDO (silent filtering for DHT unavailability)
  • get_my_ndos: returns raw AgentToNdo links for the calling agent
  • update_lifecycle_stage: pre-flight Deprecated check; writes lifecycle_stage + successor_ndo_hash; creates NdoToSuccessor on Deprecated; creates NdoToTransitionEvent when event hash provided

Decisions

Option Rejected because
Use extra NdoUpdates links for chain resolution (like ResourceSpec pattern) Adds link type not in spec; get_details loop is semantically correct and spec-compliant
Include categorization anchors (#75) in this PR Scope creep; #75 extends create/update, needs its own review cycle
Remove ResourceState enum EconomicResource still uses it; OperationalState refactor is post-MVP (REQ-NDO-OS-06)
Full transition_event_hash validation against governance zome Integrity zomes cannot make cross-zome calls; coordinator cross-zome calls to zome_gouvernance are currently disabled. Link is created when hash is provided; validation deferred to a follow-up once cross-zome calls are stable
Allow Ideation/Specification → Hibernating Not shown in the §5.3 Mermaid diagram; the "Any → Hibernating" authorization row covers active work stages only
Restrict initial lifecycle_stage to Ideation only NDO is an identifier assigned at registration time, not a birth certificate. An existing physical resource registered into the system should be created at its true current stage (e.g. Active), not forced through a synthetic Ideation entry. Forcing Ideation-only would require fabricating DHT history for brownfield resources and block migration of existing EconomicResource entries when Layers 1/2 activate. Hibernating, Deprecated, and EndOfLife are still rejected at creation.
ResourceNature kept to 3 spec variants (Digital, Physical, Hybrid) The spec §8.2 defines 3 variants. This implementation adds Service and Information because 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.
Store successor_ndo_hash only as a link (spec §8.1 approach) The spec defines the successor relationship as a NdoToSuccessor link 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 errors

Sweettest 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.md with the 10-stage lifecycle table, reversibility/terminal distinction, new successor_ndo_hash field, updated update_lifecycle_stage API surface including conditional links, get_all_ndos/get_my_ndos API docs, and NDO-as-identifier architecture design note.

Related

Related issues:


Open with Devin

…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)
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 4 additional findings in Devin Review.

Open in Devin Review

Comment on lines +201 to +202
current_entry.lifecycle_stage = input.new_stage.clone();
current_entry.successor_ndo_hash = input.successor_ndo_hash.clone();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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(),
    ));
}
Suggested change
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();
}
Open in Devin Review

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],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 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.

Open in Devin Review

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant