feat(resource)!: NDO Layer 1 typed governance rules, classification constraints, and OperationalState - #132
feat(resource)!: NDO Layer 1 typed governance rules, classification constraints, and OperationalState#132Soushi888 wants to merge 12 commits into
Conversation
Introduced a TypeScript service layer for Group DNA, replacing the GroupService stub with a callZome implementation. Updated group types and services to support new API functionalities. Enhanced documentation to include API references, architecture overviews, and test commands for Group DNA, ensuring clarity on the new structure and interactions within the system.
…governance framework Added comprehensive documentation for the Source-NDO, a new ontological primitive representing generative ecological systems. This update includes the `source-ndo-requirements.md` detailing its role, governance patterns, and integration within the Nondominium architecture. Enhanced existing documents to reflect the necessity of the `vf:Source` ValueFlows extension and the adaptive governance loop for ecological systems. This change aims to clarify the framework for managing ecological commons and ensure accurate representation of environmental interactions within the economic information system.
Introduced a new document detailing the integration of the `vf:Source` primitive into the Nondominium's Valueflows model. This comprehensive design outlines the purpose, structure, and implications of the `vf:Source` for generative ecological systems, distinguishing it from existing primitives. Additionally, updated the Valueflows DSL documentation to clarify the distinction between the current and planned capabilities, emphasizing the future integration of the `vf:Source` within the Nondominium architecture.
…management Added the OperationalState enum to represent the current process condition of EconomicResource instances, allowing for states such as Available, Reserved, InTransit, InStorage, InMaintenance, InUse, and PendingValidation. Implemented functionality for creating, updating, and querying resources by their operational state. Updated relevant tests and documentation to reflect these changes, enhancing the resource management capabilities within the Nondominium architecture.
…nd governance integration Revised the documentation for the Artcoin application within the Nondominium framework. Key updates include detailed descriptions of user stories for art circulation, distribution, and production, emphasizing the roles of individual patrons and venues. Enhanced governance rules and operational states for artworks are now clearly outlined, alongside the integration of Private Participation Receipts (PPRs) for tracking reputation and custody. This update aims to provide a comprehensive understanding of the Artcoin ecosystem and its alignment with Nondominium's resource-sharing capabilities.
…ules Added new modules for resource classification and governance rules within the Nondominium framework. The `constraints` module includes predicates for evaluating resource classifications, handling constraint violations, and ensuring compliance with governance rules. The `rule_data` module defines various governance rule types, including access requirements and transfer conditions. This update enhances the integrity validation process and supports the management of resources in a decentralized environment, aligning with the overarching goals of the ArtCoin project.
…mpty The Phase 0 signing guard creates a `group` clone cell on agent 1 and never removes it. The UI enumerates group clone cells straight off `appInfo`, so the leftover cell rendered as a real group in the sidebar, `hasGroups` became true, and the next test's create-or-join onboarding CTA never mounted. Disable and delete the clone in a `finally` block, and expose `appId` on SeedClient for the admin-scoped delete. Pre-existing failure on `dev` (run 31285564294), not a Layer 1 regression.
The Layer 1 constraint predicates were only ever exercised through `check_rule_data_constraints`, which takes the classification as a parameter. Nothing bound a rule's denormalized `property_regime` / `resource_nature` / `rivalry_override` to the NDO it claims to describe, so capture resistance was self-declared: an ownership-transfer rule on a Nondominium NDO passed simply by writing `Private` on the rule entry. Integrity now reads the referenced NondominiumIdentity and rejects any mismatch before evaluating constraints. All three fields are immutable on Layer 0, so the genesis record reached through the stable hash is authoritative and no update-chain walk is needed — the same read `validate_create_resource_spec` already performs. Four Sweettests cover the boundary: regime drift, nature drift, the misdeclared-regime bypass of REQ-RES-03, and a matching-classification guard so the binding cannot be over-tight. Verified red against the unfixed zome (3 failed / 2 passed) before the fix, green after (9/9). CI gains a `sweettest` job running the shared-crate unit tests and all five Sweettest targets, with `e2e` now gated behind it as the workflow comment asked. `--test-threads 2`: 6 threads with 11 conductor tests in flight gets SIGTERM'd. The empty-lobby e2e test asserts its precondition via `expectEmptyLobby` so a leaked group names itself instead of surfacing as "element not found".
…st in CI The Layer 1 lifecycle gate read `must_get_valid_record(ndo_identity_hash)`, which returns the *genesis* record. `lifecycle_stage` mutates through the update chain, so the gate judged every activation against the stage the NDO was created at: an NDO created at Ideation and advanced to Specification could never grow a spec, while one created at Active and since Deprecated still could. `ResourceSpecification` now carries `ndo_state_hash` — the NDO action the author observed — alongside the stable `ndo_identity_hash`. Integrity cannot walk an update chain forward (the set of updates grows, so validation would not replay), but backward is deterministic: every Update names exactly one predecessor via `original_action_address` and that edge never changes. `resolve_ndo_state` therefore reads the observed entry, walks back to the genesis Create, and proves the root matches `ndo_identity_hash` before gating on the stage. Capped at 64 hops; the chain is at most 10 stages in practice. The coordinator derives the field via `resolve_latest_ndo_record`, so honest clients are correct by construction, and `ndo_state_hash` is immutable on update so an edit cannot re-point a spec at a newer state to launder an activation the create-time gate rejected. Accepted limitation: an author writing directly to the DHT can present an old-but-eligible state. Two Sweettests cover both directions, verified red against the unfixed zome (0 passed / 2 failed) and green after (11/11). CI: the single sweettest job measured 61 min (run 31736241424) — 16 min compile plus 40 min of tests back-to-back. Sharded one job per target with a shared rust-cache key, and dropped the custom CARGO_TARGET_DIR: `target/native-tests` is right locally (keeps native artifacts away from the wasm build) but falls outside what rust-cache saves, so every run recompiled holochain test_utils.
Review: REQUEST CHANGESReviewed at
Request changesR1. Layer 2 capture resistance is still self-declared.
The new test demonstrates the gap rather than closing it: This is review follow-up #1 relocated, not fixed. Instead of declaring a false regime inline, the writer declares a false NDO pointer. REQ-RES-03 remains self-declared on the Layer 2 write path. Two ways out:
R2.
So a rule created under a Nondominium NDO can be updated to point at a R3. The new matrix runs R4.
R5. Documentation mapping (
R6. Line 193 still reads:
This PR adds all four ( R7. Description-to-diff gaps. Three areas are in the diff but absent from
Split them into their own PR or name them. Suggestions
NotesThe Everything else holds up well:
|
Review verdict: APPROVE (Wave 2 — merge after #129, rebase first)Deep review of the 98-file diff, focused on the four load-bearing surfaces. Typed Classification-coherence enforcement — this is the strongest part of the PR. Layer 1 activation gate —
One design question, non-blocking: Severity table for the record — ownership-transfer under uncapturable regime: Hard; under non-uncapturable non-transfer regimes: Soft; gated access on Nondominium: Soft; transport on non-physical nature: from Merge mechanics: rebase onto post-#129 dev (base will have moved by two merges + the #137 CI gate), full matrix re-run expected green, then merge. |
…fix) Five conflicts, all resolved by keeping both sides rather than picking one: - crates/shared/types.rs: #132's Rivalry/ResourceScope/OperationalState and dev's NdoDnaProperties are independent additions; both kept. - zome_resource integrity: kept #132's regime-semantics hook AND dev's ADR-013 binding check. Dropping the latter would silently remove the guarantee that a GovernanceRule's classification cannot diverge from Layer 0. - ndo_anchor tests: took dev's shared-crate import (#128 deliberately replaced the hand-kept mirror, which had already drifted on `initiator`), then re-applied #132's additions on top: rivalry_override on the NdoInput/NdoEntry mirrors and the Public-regime anchor test. That test was written against the old mirror, so it needed adapting: NdoDnaProperties has no `initiator` field, and anchor_input_from now takes the initiator as a separate argument. - ndo.service.ts: took dev's side. #132's mapListingToDescriptor is dead under the anchor model (zero callers) and its identityToDescriptor was a duplicate definition. rivalry_override survives in the retained field mapper. - IMPLEMENTATION_STATUS.md: neither side was accurate. Arbitrated against the code: 20 externs, and no get_all_groups.
Rebased onto dev (now carrying #137, #128, #138)Merged rather than rebased: this branch is 10 commits and the conflicts repeat across most of them, so a merge resolves each one once and the squash-merge flattens it anyway. Five conflicts, all resolved by keeping both sides rather than picking a winner. Full reasoning is in the merge commit message; the two that matter for review: The ADR-013 binding was at risk. The One doc claim was wrong on both sides. Note on the seven-variant
|
Layer 1 activation puts the word "Specification" on the NDO detail page in
more than one place: the identity panel's lifecycle stage, the Layer 1
specification panel, and its create modal. The multi-agent live-read test
asserted `getByText('Specification', { exact: true })`, which became a
Playwright strict-mode violation the moment Layer 1 rendered.
Add `data-testid="ndo-lifecycle-stage"` to the stage field and assert on
that. A structural selector would have worked too, but it would break again
the next time the panel is restyled; the test id says what the test means.
Local e2e: 19 passed, including the previously failing case.
Intent
Activates NDO Layer 1 (Specified) on top of the existing Layer 0 identity anchor, and closes two long-standing
TODOs in the resource zome: the untypedGovernanceRulepayload, and theResourceStateenum that conflated lifecycle maturity with operational condition.The result is that a
ResourceSpecificationnow points at the Layer 0NondominiumIdentityit activates, governance rules carry a typed schema instead of a JSON blob, and classification coherence (regime x nature x rivalry x action) is enforced by pure predicates shared between the integrity zome, the coordinator, and the UI.Branch authored by Tibi (
ndo-layer1, 6 commits), then reviewed and hardened in four further commits (see Review follow-ups).Changes
Shared crate (
crates/shared)rule_datamodule:RuleDatatagged enum with four variants (AccessRequirement,UsageLimit,TransferCondition,MaintenanceSchedule).GovernanceRuleTypeis derived from the discriminant viaRuleData::rule_type(), never stored separately.constraintsmodule: pure, hdk-free predicates over aResourceClassification(nature, regime, lifecycle, rivalry override). EmitsConstraintViolation { rule_id, message, severity }withHard/Softseverity. Current rules:nondominium_no_unilateral_capture,ownership_transfer_not_permitted_by_regime,gated_access_contradicts_permissionless_regime,no_transport_for_non_physical_nature.types:Rivalry(withResourceNature::default_rivalry()),ResourceScope(Project/Network/Public),OperationalState(Available,Reserved,InTransit,InStorage,InMaintenance,InUse,PendingValidation). NewPropertyRegimepredicates:is_rivalrous,permits_ownership_transfer,is_uncapturable,default_accessibility.Zomes
ResourceSpecificationgainsscope,ndo_identity_hash(the immutable, stable Layer 0 pointer), andndo_state_hash(the NDO action whose lifecycle stage the author observed). Both hashes are immutable after creation. Project-scoped specs skip the global discovery anchor.GovernanceRulereplacesrule_type: String+rule_data: Stringwith a typedRuleData, plusndo_identity_hashand denormalizedproperty_regime/resource_nature/rivalry_override. Integrity reads the referenced Layer 0 record and rejects any classification that contradicts it (see Review follow-ups).EconomicResource.state: ResourceStatebecomesoperational_state: OperationalState;ResourceStateis deleted.ResourcesByStatebecomesResourcesByOperationalState; lifecycle faceting stays on Layer 0'sNdoByLifecycleStage.NdoToSpecificationlink type: the Layer 0 to Layer 1 activation edge.zome_gouvernance: newtransitionmodule, plus economic-event and commitment updates carrying the constraint context.UI
SpecificationCreateModal,RuleEditorModal(typed rule authoring),CommitmentCreateForm,EconomicEventCreateForm.operational-state-labels.ts,rivalry.ts. Governance / resource services, stores, and schemas updated for the new shapes.GroupServicestub replaced with a realcallZomeservice layer for the Group DNA.CI
sweettestjob:cargo test -p nondominium_sharedplus all five Sweettest targets, sharded one job per[[test]]target with a sharedrust-cachekey.e2eis now gated behind it, as the workflow's own comment had asked for. Before this PR, the primary backend suite perCLAUDE.mdhad never gated a merge.Tests
Sweettest coverage extended in
resource,governance,ndo_layer0, and the groupndo_anchorsuite (about 800 added lines across the four), plus six negative tests added during review.Documentation
New:
Source-NDO.md,source-ndo-requirements.md,source-ndo-paper.md,source-valueflows-integration.md,complete-resource-specification.md, plus the ArtCoin application docs and user stories. Updated:requirements.md,resources.md,governance.md,ndo_prima_materia.md,implementation_plan.md,IMPLEMENTATION_STATUS.md,resource_zome.md,API_REFERENCE.md, and thevalueflows-dsl.md/vf:Sourcedesign notes.Review follow-ups
Four commits on top of Tibi's six. Two are integrity bugs found in review; both were demonstrated red before their fix landed.
1. Layer 0 classification was writer-controlled (
09224eb)validate_create_governance_rulebuilt itsResourceClassificationfromrule.property_regime/resource_nature/rivalry_override, all supplied by the caller, and never checked them against the referencedndo_identity_hash. ATransferCondition{Ownership}rule on aNondominiumNDO passed validation simply by declaringproperty_regime: Private. The capture-resistance guarantee (REQ-RES-03) was self-declared.Integrity now reads the referenced
NondominiumIdentityand rejects any mismatch before evaluating constraints. All three fields are immutable on Layer 0, so the genesis record reached through the stable hash is authoritative.validate_update_governance_ruledelegates to create, so updates are covered.2. The Layer 1 lifecycle gate read the wrong stage (
923b3d8)validate_create_resource_speccalledmust_get_valid_record(ndo_identity_hash), which returns the genesis record.lifecycle_stagemutates through the update chain, so the gate judged every activation against the creation-time stage: an NDO created atIdeationand advanced toSpecificationcould never grow a spec (the primary intended flow), while one created atActiveand sinceDeprecatedstill could.ResourceSpecificationnow carriesndo_state_hash. Integrity cannot walk an update chain forward (the set of updates grows, so validation would not replay), but backward is deterministic: every Update names exactly one predecessor viaoriginal_action_address, and that edge never changes.resolve_ndo_statereads the observed entry, walks back to the genesis Create, and proves the root equalsndo_identity_hashbefore gating on the stage. Capped at 64 hops. The coordinator derives the field viaresolve_latest_ndo_record, so honest clients are correct by construction, and immutability on update prevents re-pointing a spec at a newer state to launder a rejected activation.3. Test hardening (
09224eb,923b3d8)Six new Sweettests in
dnas/nondominium/tests/src/resource/mod.rs:governance_rule_rejects_classification_drift_from_layer0,..._nature_drift_...,nondominium_ownership_transfer_not_bypassable_by_misdeclared_regime,governance_rule_accepts_classification_matching_layer0(over-tightness guard),resource_spec_allowed_after_advancing_out_of_ideation,resource_spec_rejected_after_deprecation. Classification tests were 3 failed / 2 passed pre-fix and 9 passed post-fix; lifecycle tests 0 passed / 2 failed pre-fix and 11 passed post-fix.The pre-existing
check_rule_data_constraints_blocks_nondominium_ownership_transferlooked like it covered bug 1 but did not: it exercises the pure dry-run query, which takes classification as a parameter.4. Red e2e pipeline (
74532aa)Pre-existing on
dev(run 31285564294), not a Layer 1 regression. The Phase 0 clone-signing guard incore-flows.spec.tscreated agroupclone cell and never removed it; the UI enumerates group clone cells offappInfo, so the leftover cell rendered as a real group,hasGroupsbecame true, and the onboarding CTA never mounted. Fixed withdisableCloneCell+admin.deleteCloneCellin afinallyblock.expectEmptyLobbynow asserts the precondition explicitly so a future leak names itself instead of surfacing as "element not found". e2e went from 10 passed / 1 failed / 7 skipped to 18 passed / 0 failed.Decisions
rule_dataas a JSON stringGovernanceRuleTypemigration called for inndo_prima_materia.md.GovernanceRuleTypealongside the payloadRuleDatadiscriminant makes the mismatch unrepresentable.ResourceStatewith more variantsLifecycleStage(Layer 0, maturity) andOperationalState(instance, current process) now move independently, per REQ-NDO-OS-06.Hardblocks at integrity;Softsurfaces as coordinator and UI advice.rivalrya required stored fieldResourceNature::default_rivalry()with an immutablerivalry_overridefor the exceptions (for example a rivalrous Service slot).Read Layer 0 during rule validation(reversed in review)validate_create_resource_specalready did one sixty lines up. Denormalization is kept for the constraint evaluation, but integrity now reads Layer 0 and rejects any field that disagrees, so the declared classification is no longer trusted.ndo_state_hash. Accepted limitation: an author writing directly to the DHT can present an old-but-eligible state.Breaking changes
This changes DHT entry shapes and link types, so it is a DNA-hash-breaking change. No migration path is provided; existing test networks need to be recreated.
EconomicResource.state: ResourceStatebecomesoperational_state: OperationalState.ResourceStateis removed.GovernanceRule.rule_type/rule_datastrings become a single typedRuleData, plus three required classification fields, which integrity now checks against Layer 0.ResourceSpecificationgains requiredndo_identity_hash,ndo_state_hash, andscope.LinkTypes::ResourcesByStatebecomesResourcesByOperationalState;NdoToSpecificationis added.How to test
Verified: all five Sweettest targets green locally at
--test-threads 2(governance 4 with 2 ignored, misc 1, nondominium 11, person 2, resource 11);build:happand e2e 18/18 in CI (run 31731642447); the whole suite green in CI as one job (run 31736241424, 61 min, since sharded). The sharded pipeline's own first run is in flight on this head.Documentation
Extensive. See the Documentation subsection under Changes for the full list of added and updated files.
Related
Related issues:
NdoToSpecification. Layer 2 (NdoToProcess) keeps the epic open.GovernanceTransitionRequest,GovernanceTransitionResult,TransitionContext,EconomicResourceView, andNdoClassificationViewland incrates/shared/src/io/governance.rs.crates/shared/src/constraints.rsis the pure, hdk-free predicate engine, shared verbatim by integrity validation, the coordinator, and the UI dry-run path.sweettestCI job, gatese2ebehind it, fixes the leaked clone cell that took out 8 of 18 e2e tests, and adds six negative Sweettests. Thegroup_sweettestpackage is still outside the CI matrix, so test(v0.1.0): harden e2e and Sweettest coverage for the peer-mesh release #127 stays open.evaluate_state_transitionnow exists, but the existing write paths stay parallel to it. The refactor itself is out of scope here.NdoToSpecification,ResourcesByOperationalState) with no CreateLink/DeleteLink validation, widening security(integrity): add per-LinkType validation for CreateLink/DeleteLink across all three zomes #85's surface.LifecycleStage/OperationalStatesplit) and theGovernanceRuleTypemigration inndo_prima_materia.mddocs/sync-harmonize) waits on this oneDeferred, deliberately out of scope here:
NdoToProcess), and wiring the constraint predicates intoevaluate_transition.zome_gouvernance/src/transition.rs:7carries a liveTODO(§5 item 2)on whether existing write paths should funnel throughevaluate_state_transition.check_capture_resistanceHard-blocksConsumeonNondominium, but the Source-NDO requirements this same PR documents mandate recording extraction fromNondominium/CommonPoolsources (REQ-PROC-10, REQ-SOURCE-EVENT-01). One of the two has to give.Closes #136