Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .claude/board/INTEGRATION_PLANS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
## 2026-08-27 — DOC-FIRST + PINNED (D-MCAL-3): the execution gate named for what it is, `crates/lance-graph-contract/src/{mul,kanban}.rs`

`contract::mul::GateDecision` gets a "what this type actually is" section: it
is the EXECUTION / COMMIT gate, not MUL's output, and it lives in a module
called `mul` for historical reasons that have been misleading consumers for as
long as the name has existed. The consumer table is inlined from the D-MCAL-1
census — kanban phase moves, ActionState, tier-router Rest dispatch, supervisor
next-column; none routes to a compass, an exploration, or a learn-first path,
and none reads the calibration payload. The MUL-shaped output the diagram calls
for already exists as the planner's `MulGateDecision{Proceed, Sandbox, Compass}`.
NO rename: it would touch four in-tree consumers plus two external repos and
bury a semantic decision inside a mechanical diff, so the symbol move is its own
PR, tracked as `ISS-MUL-GATE-NAMED-FOR-THE-WRONG-LAYER` and blocked on D-MCAL-4
+ D-MCAL-6. Three falsifiers pin the behaviour so a future change is a diff, not
a drift: `Hold` returns None from EVERY column (F-MUL-4's red state — a
phase-stay with no learning path, OQ-MCAL-2); its anti-vacuity twin proving
`Flow` does move and does not invent successors the DAG lacks; and a routing
test proving two `Block`s with DIFFERENT (texture, flow) pairs route identically
from every column — the naming evidence in executable form.
## 2026-08-27 — DECIDED + LANDED (D-MCAL-2): the two gate-returning trait methods, `crates/lance-graph-contract/src/{mul,plan}.rs`

Fate decided on the D-MCAL-1 census, not on argument. `PlannerContract::gate_check`
Expand Down
31 changes: 31 additions & 0 deletions .claude/board/ISSUES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
# Issues Log — Open + Resolved (double-entry, append-only)

## ISS-MUL-GATE-NAMED-FOR-THE-WRONG-LAYER (2026-08-27) — OPEN

`contract::mul::GateDecision` is measured (D-MCAL-1) as the **execution /
commit gate**: every consumer commits, cancels, or defers work — kanban phase
moves (`Block` → `Prune`, the Libet veto), `ActionState::{Committed, Pending,
Cancelled}`, the tier-router's `Rest` dispatch, the supervisor's next column.
None routes to a compass, an exploration, or a learn-first path. The MUL-shaped
output the architecture diagram calls for exists separately as
`lance_graph_planner::mul::gate::MulGateDecision{Proceed, Sandbox, Compass}`.

So the type is correct and its **module name is wrong**, and the wrong name is
load-bearing: it is why two external repos reached for a MUL type to express a
consent veto and an evidence contradiction (D-MCAL-4). A name that tells a
consumer "this is where decisions about confidence go" will keep attracting
producers that have no confidence state.

**Not fixed in D-MCAL-3, deliberately.** A rename touches four in-tree
consumers plus ada-rs and MedCare-rs, and would bury a semantic decision inside
a mechanical diff. D-MCAL-3 names it correctly in prose and pins the behaviour
with falsifiers (`f_mul_4_*` in `kanban.rs`); the symbol move is its own
reviewable PR.

**Blocked on:** D-MCAL-4 (domain producers off the type) and D-MCAL-6 (consumer
builds green). Renaming before those land would break consumers twice.

**Candidate landing shape** (not decided): the type moves to a module named for
what it gates — `contract::execution_gate` or alongside `contract::kanban` —
with a deprecated re-export at `mul::GateDecision` for one cycle, per
I-LEGACY-API-FEATURE-GATED's rule that a name must not silently change meaning.


## ISS-NO-CAUSAL-SIGN-ON-EDGES (2026-08-26) — OPEN

`CausalEdge64` carries **no Inc/Dec polarity**. Measured: `grep -niE
Expand Down
4 changes: 2 additions & 2 deletions .claude/board/STATUS_BOARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ code. `mul-gate-outcome-vs-ground-v1`'s D-GATE-2/D-GATE-3 are WITHDRAWN.
| D-id | Deliverable | Status |
|---|---|---|
| D-MCAL-0 | axis restoration: coordinates stay in the assessment; unfuse the verdict payload at the trait boundary | Queued |
| D-MCAL-1 | per-symbol consumer enumeration from GitHub, classified A/B/C/D | Shipped — #1065; census at `.claude/plans/mul-consumer-census-v1.md` |
| D-MCAL-1 | per-symbol consumer enumeration from GitHub, classified A/B/C/D | In PR — census at `.claude/plans/mul-consumer-census-v1.md` |
| D-MCAL-2 | fate of `MulProvider` + `PlannerContract::gate_check` (0 in-tree impls) | In PR — `PlannerContract::gate_check` REMOVED (0 impls org-wide); `MulProvider::gate_check` DEPRECATED with migration pointer |
| D-MCAL-3 | name the execution gate what it is (doc-first; rename is a later PR) | Queued |
| D-MCAL-3 | name the execution gate what it is (doc-first; rename is a later PR) | In PR — doc-first landed + F-MUL-4 pins; rename tracked as `ISS-MUL-GATE-NAMED-FOR-THE-WRONG-LAYER` |
| D-MCAL-4 | consent veto + evidence contradiction as domain evidence, no MUL ground | Queued |
| D-MCAL-5 | if a public MUL output is needed, promote the planner's existing type — never mint a fourth | Queued |
| D-MCAL-6 | consumer-build gate: BUILD them, don't grep them | Queued |
Expand Down
102 changes: 102 additions & 0 deletions crates/lance-graph-contract/src/kanban.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,4 +446,106 @@ mod tests {
assert_eq!(ExecTarget::Native as u8, 0);
assert_eq!(ExecTarget::Elixir as u8, 3);
}

// ── D-MCAL-3 / F-MUL-4: what `advance_on_gate` actually does with the
// decision, pinned so a semantic change shows up as a diff ──

/// F-MUL-4, **red-state pin**. Under the architecture diagram a
/// *need-more-data* condition routes to learn / map / recover / sandbox.
/// This gate's `Hold` does none of those: it returns `None` from **every**
/// column, so the mailbox stays put and re-evaluates next cycle with no
/// learning path attached. That is a phase-stay, not a MUL verdict
/// (OQ-MCAL-2).
///
/// This test asserts the CURRENT behaviour on purpose. When a learn-routing
/// path lands, this test must be updated in the same commit — which is the
/// point: the change becomes visible rather than silent.
#[test]
fn f_mul_4_hold_is_a_phase_stay_with_no_learning_path() {
let hold = GateDecision::Hold {
texture: crate::mul::TrustTexture::Overconfident,
flow: crate::mul::FlowState::Anxiety,
};
for col in [
KanbanColumn::Planning,
KanbanColumn::CognitiveWork,
KanbanColumn::Evaluation,
KanbanColumn::Commit,
KanbanColumn::Plan,
KanbanColumn::Prune,
] {
assert_eq!(
col.advance_on_gate(&hold),
None,
"Hold moved {col:?} — if a learn/map/recover route was added, \
update this pin and F-MUL-4 in mul-calibration-not-verdict-v1"
);
}
}

/// Anti-vacuity twin for the pin above: `Hold` returning `None` everywhere
/// would be uninformative if the gate moved nothing at all. `Flow` must
/// advance from the columns that have a forward successor, and must not
/// invent one where the DAG has none.
#[test]
fn f_mul_4_flow_does_move_so_the_hold_pin_is_not_vacuous() {
let flow = GateDecision::Flow;
assert_eq!(
KanbanColumn::Planning.advance_on_gate(&flow),
Some(KanbanColumn::CognitiveWork)
);
assert_eq!(
KanbanColumn::CognitiveWork.advance_on_gate(&flow),
Some(KanbanColumn::Evaluation)
);
assert_eq!(
KanbanColumn::Evaluation.advance_on_gate(&flow),
Some(KanbanColumn::Commit)
);
assert_eq!(
KanbanColumn::Plan.advance_on_gate(&flow),
Some(KanbanColumn::Planning)
);
// Absorbing columns have no forward successor.
assert_eq!(KanbanColumn::Commit.advance_on_gate(&flow), None);
assert_eq!(KanbanColumn::Prune.advance_on_gate(&flow), None);
}

/// D-MCAL-3, the naming evidence in executable form: this gate's consumers
/// commit, cancel, or defer WORK — they never read the calibration payload.
/// Two `Block`s carrying different `(texture, flow)` pairs must produce
/// byte-identical routing from every column. If this ever fails, the
/// payload became load-bearing and the D-MCAL-2 deprecation needs revisiting.
#[test]
fn f_mul_4_routing_ignores_the_calibration_payload() {
let block_a = GateDecision::Block {
texture: crate::mul::TrustTexture::Uncertain,
flow: crate::mul::FlowState::Anxiety,
};
let block_b = GateDecision::Block {
texture: crate::mul::TrustTexture::Underconfident,
flow: crate::mul::FlowState::Boredom,
};
assert_ne!(block_a, block_b, "the two payloads must actually differ");
for col in [
KanbanColumn::Planning,
KanbanColumn::CognitiveWork,
KanbanColumn::Evaluation,
KanbanColumn::Commit,
KanbanColumn::Plan,
KanbanColumn::Prune,
] {
assert_eq!(
col.advance_on_gate(&block_a),
col.advance_on_gate(&block_b),
"routing from {col:?} depended on the calibration payload"
);
}
// …and the Block route itself is real where the DAG allows a veto.
assert_eq!(
KanbanColumn::Planning.advance_on_gate(&block_a),
Some(KanbanColumn::Prune)
);
assert_eq!(KanbanColumn::CognitiveWork.advance_on_gate(&block_a), None);
}
}
45 changes: 45 additions & 0 deletions crates/lance-graph-contract/src/mul.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,51 @@ pub enum FlowState {
/// whole decision is byte-packable. Use [`GateDecision::to_disc`] for the
/// bare discriminant, or [`batch::gate_decision_disc_batch`] for bulk work.
///
/// # What this type actually is (D-MCAL-3 — read before extending it)
///
/// **This is the execution / commit gate. It is not MUL's output.** It lives
/// in a module called `mul` for historical reasons, and that name has been
/// misleading consumers for as long as it has existed.
///
/// The census (`.claude/plans/mul-consumer-census-v1.md`) enumerated every
/// consumer. Each of them commits, cancels, or defers **work**:
Comment on lines +150 to +151

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 Point the census reference at an existing file

The central consumer claim links to .claude/plans/mul-consumer-census-v1.md, but a repository-wide file search and rg find no such file; the classification currently exists in §2 of .claude/plans/mul-calibration-not-verdict-v1.md. As written, readers cannot inspect the evidence supporting this doc-first semantic decision, so either link the existing plan or add the referenced census.

Useful? React with 👍 / 👎.

///
/// | consumer | what it does |
/// |---|---|
/// | [`crate::kanban::KanbanColumn::advance_on_gate`] | phase-DAG move; `Block` → `Prune` (Libet "free won't" veto) |
/// | [`crate::action::ActionInstance`] `commit` / `emit` | `ActionState::{Committed, Pending, Cancelled}` |

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 Link the actual action gate type and methods

This public documentation names and links crate::action::ActionInstance, but the module exposes ActionInvocation; its relevant methods are commit and commit_via, not commit and emit. Consequently the intra-doc link is unresolved and the consumer table directs readers to an API that does not exist.

Useful? React with 👍 / 👎.

/// | `sigma-tier-router` | `Block` → `Rest { GateBlocked }` dispatch |
/// | `lance-graph-supervisor::kanban_actor::mul_target` | next kanban column |
///
/// **Not one of them routes to a compass, an exploration, or a learn-first
/// path**, and not one of them reads `texture` or `flow` — all four
/// destructure `{ .. }`. The MUL-shaped output the architecture diagram calls
/// for already exists elsewhere, as
/// `lance_graph_planner::mul::gate::MulGateDecision{Proceed, Sandbox, Compass}`.
///
/// Two distinct things therefore wear one name, and this is the one that is
/// **not** MUL. Producers that have no calibration state should not be reaching
/// for it (D-MCAL-4); a fourth gate enum must not be minted for the shape it
/// cannot express (D-MCAL-5).
///
/// ## Why it is not renamed here
///
/// A rename touches every consumer above plus two external repos, and it would
/// bury the semantic decision inside a mechanical diff. D-MCAL-3 is
/// deliberately **doc-first**: name the thing correctly in prose now, move the
/// symbol in its own reviewable PR later. `ISS-MUL-GATE-NAMED-FOR-THE-WRONG-LAYER`
/// tracks the rename.
///
/// ## `Hold` is a phase-stay, not a MUL verdict (OQ-MCAL-2, F-MUL-4)
///
/// Under the architecture diagram there is no "hold" state at all: a
/// *need-more-data* condition routes to learn / map / recover / sandbox.
/// Measured, this type's `Hold` does none of those — `advance_on_gate` returns
/// `None`, so the mailbox stays in its column and re-evaluates next cycle with
/// **no learning path attached**. See `f_mul_4_hold_is_a_phase_stay_with_no_learning_path`,
/// which pins that as the current (red) behaviour so a future learn-routing
/// change is visible as a diff rather than a silent semantic drift.
///
/// # Why the payload is typed and not prose
///
/// `Hold` and `Block` carried `reason: String` until 2026-08-26 — five heap
Expand Down
Loading