Skip to content

ogar-dir-sim: compare-and-set for every property of an existing node; kind stays identity - #319

Merged
AdaWorldAPI merged 1 commit into
mainfrom
ccr-0455e606-wmtsor
Oct 7, 2026
Merged

AdaWorldAPI merged 1 commit into
mainfrom
ccr-0455e606-wmtsor

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

Summary

Closes the directory-simulation gap for a node that exists in both versions. Before this PR, its enabled flag and its location could not be changed at all. The PR adds that vocabulary only: it contains no executor, storage, Quack code or actuator.

Semantic contract

  • Two new compare-and-set changes, with the same from → to shape as SetAttribute:
    • Change::SetActive { node, from, to: Option<bool> }.
      • It can express all nine from → to pairs over enabled, disabled and unknown, and keeps the three values distinct.
      • Users only: a group has no enabled flag.
    • Change::SetLocation { node, from, to: Option<Dn128> }.
      • The location stays numeric; there are no DN strings.
      • An unknown location stays distinct from every known one.
  • Why not reuse SetAttribute. Its from/to are Option<ValueId>, which hold dictionary values. Folding a flag and a location into it would mean loosening those types until any attribute could be paired with any value.
  • Sort order. Both new changes sort after renames and before creates. Neither one frees or claims a value, so no other change depends on them.
  • NodeState::apply is the single reference semantics for any change to one node's properties. Every executor must agree with it.
    • It refuses a change whose from no longer matches the node (Stale).
    • It refuses SetActive on a group (NoEnabledFlag).
    • It refuses membership, create and delete changes (NotAPropertyChange), since those aren't property changes.
  • kind is immutable.
    • Kind is identity: users and groups are separate populations with separate ordinal spaces, and membership endpoints are typed by kind.
    • No change can express a kind change.
    • PlanError::Unconvergeable now means exactly a kind mismatch.
  • Plans.
    • SetActive lowers to Operation::SetEnabled { enabled: bool }, with an EnabledEquals(Option<bool>) precondition.
    • SetLocation lowers to Operation::MoveObject { to: Dn128 }, with a LocationEquals(Option<Dn128>) precondition.
    • Both preconditions hold the value read from the observation.
  • Changes towards "unknown". A diff can report a change to an unknown flag or location, but no actuator can write "unknown". ExecutionPlan::from_diff now returns a Result and refuses such a change as PlanError::NotActuatable, with no partial plan.

Ordering

Merge this first. lance-graph main will not compile lance-graph-dir-sim from the moment this merges until the companion lance-graph PR lands. That crate matches Change exhaustively and calls from_diff expecting the old, non-Result return. The companion PR implements the new variants there.

Tests

  • 15 tests, including six new ones:
    • all 27 combinations of (current, from, to) for SetActive;
    • group refusal;
    • location between unknown and known, and between two known locations, plus the stale cases;
    • SetAttribute compare-and-set;
    • no change alters kind;
    • sort order;
    • lowering with the observed precondition, deterministic across input order;
    • refusal of a change towards "unknown".
  • Eight disable runs, each red:
    • three runs where a check ignores from (active, location, attribute);
    • None collapsed to false;
    • a group flag allowed;
    • an unknown target actuated;
    • two runs where a precondition reads the target instead of from.
  • fmt and clippy -D warnings are clean.

No actuator or write-back code.

🤖 Generated with Claude Code

https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg


Generated by Claude Code

… kind stays identity

Change gains SetActive { from, to: Option<bool> } and SetLocation { from,
to: Option<Dn128> }, beside SetAttribute (whose ValueId-typed from/to
cannot carry a flag or a location without losing type safety). They sort
after renames and before creates: neither frees nor claims a value.

NodeState::apply is the one reference semantics for a property change:
it applies only where the node still holds its from (else Stale), keeps
all three flag values distinct, and refuses SetActive on a group (groups
carry no flag). Memberships, creates and deletes are not property
changes and are refused there.

kind is identity and immutable: separate user and group populations and
ordinal spaces, typed membership endpoints. No change can express it,
and PlanError::Unconvergeable now means exactly a kind mismatch.

Plans lower the new changes to SetEnabled { enabled: bool } and
MoveObject { to: Dn128 }, with EnabledEquals / LocationEquals
preconditions read from the observation. A change towards an unknown flag
or location is reportable by a diff but not actuatable:
ExecutionPlan::from_diff now returns Result and refuses it as
PlanError::NotActuatable (no partial plan).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 0d6cb3f5-648a-4fbd-bc33-54fa81f611f3
📥 Commits

Reviewing files that changed from the base of the PR and between 6119504 and 81df779.

📒 Files selected for processing (4)
  • crates/ogar-dir-sim/src/change.rs
  • crates/ogar-dir-sim/src/lib.rs
  • crates/ogar-dir-sim/src/plan.rs
  • docs/DIRECTORY-SIMULATION-POC.md
 _____________________________________________________________________________________________________
< Trained in all forms of code review, from the ancient art of waterfall to the modern ways of agile. >
 -----------------------------------------------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


Comment @coderabbitai help to get the list of available commands.

@cursor

cursor Bot commented Oct 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: ddbee830-53f1-4825-a9a4-99ffb6149191)

@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review October 7, 2026 17:09
@cursor

cursor Bot commented Oct 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 5d9ed9b5-6703-4918-9b7f-af8b94a31159)

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-07T17:11:45.948437Z 81df779 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@AdaWorldAPI
AdaWorldAPI merged commit c49907b into main Oct 7, 2026
4 of 5 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 81df77922d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +197 to +200
let mut ops = diff
.into_iter()
.map(PlannedOp::lower)
.collect::<Result<Vec<_>, _>>()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Choose a deterministic non-actuatable node

When a diff contains multiple SetActive or SetLocation changes whose to is None, this collect short-circuits on whichever change appears first, so reversing the same semantic diff changes the observable PlanError::NotActuatable.node. This undermines the planner's otherwise canonical, input-order-independent behavior and can make reconciliation or audit results unstable; sort before lowering or deterministically select the minimum offending node.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants