Skip to content

feat(operad+kernel): WF21 causes ValidateCausalAcyclic + round-15 ceremony#34

Merged
MSD21091969 merged 1 commit intomasterfrom
round15/v314-2-3-4-6-fragments
Apr 26, 2026
Merged

feat(operad+kernel): WF21 causes ValidateCausalAcyclic + round-15 ceremony#34
MSD21091969 merged 1 commit intomasterfrom
round15/v314-2-3-4-6-fragments

Conversation

@MSD21091969
Copy link
Copy Markdown
Collaborator

Round-15 ceremony validator extension

Companion to ffs0 round-15 v3.14 → v3.15 ontology bump (held on wolfram/r15-ontology-v3-15-bump). Promotes 4 grammar_fragments through WF20 ceremony:

Fragment Substrate change Validator extension
v314-2-clock-type clock S2 node-type with cardinality/embedding/frame/density/cycle_period none beyond schema enums (loader.go handles via ontology.json)
v314-3-wf21-causes WF21 causes/caused-by rewrite_category ValidateCausalAcyclic (this PR)
v314-4-substrate-property substrate enum + substrate_anchor_urn on channel + knowledge_item none beyond schema (immutable + kernel-authority enforced via existing PropertySpec)
v314-6-channel-kind-video channel.kind enum extended with video + audio none

Only WF21 needs new code; the other three are data-driven via the loader.

ValidateCausalAcyclic

When a new LINK src --causes--> tgt is proposed, walk forward from tgt through existing WF21 causes-edges. If src is reached, reject — the new edge would close a cycle.

Rules:

  1. Non-WF21 LINKs pass through (no acyclicity constraint).
  2. Non-LINK rewrites (UNLINK, ADD, MUTATE) pass through.
  3. Self-LINK (src == tgt) is a 1-cycle — rejected immediately.
  4. BFS forward from tgt through outgoing edges where RewriteCategory=WF21 and SrcPort="causes" (true causes-direction; reverse caused-by side ignored to avoid over-detection).

Hooked into both runtime.Apply (single-envelope path) and runtime.ApplyProgram (atomic-batch path; uses workingState so intra-batch causes-LINKs cannot close cycles even when individual envelopes pass against pre-batch state).

Doctrine anchors

  • kb/research/spec/07-time-fabric.md §7.3.5 — WF21 lift path
  • kb/research/spec/05-external-substrates.md §5.1.5 + §5.3.5 — transitional citation surface → caused-by LINK lift
  • claim:wolfram.cross-kernel-reciprocity-as-m9-prefiguration (Z440 :8000 log_seq 385) — first pre-§M9 instance of HG-resident cross-kernel coherence

Tests

8 unit tests in internal/operad/validate_causal_acyclic_test.go:

  • TestValidateCausalAcyclic_NonWF21LinkPasses — pass-through for other LINK categories
  • TestValidateCausalAcyclic_NonLinkPasses — pass-through for ADD/MUTATE/UNLINK
  • TestValidateCausalAcyclic_SelfLinkRejected — 1-cycle
  • TestValidateCausalAcyclic_NoPathPasses — disjoint subgraphs
  • TestValidateCausalAcyclic_DirectCycleRejected — 2-cycle
  • TestValidateCausalAcyclic_TransitiveCycleRejected — 4-cycle through B→C→D→A
  • TestValidateCausalAcyclic_DAGForkPasses — fork (B→C, B→D), new edge A→B passes
  • TestValidateCausalAcyclic_NonCausesPortIgnored — defensive — only true causes-direction edges followed

All passing locally. Full go test ./... green.

Round-15 deploy sequence

  1. Merge this PR
  2. Rebuild kernel binary on Z440 + hp-laptop (go build)
  3. Merge ffs0 wolfram/r15-ontology-v3-15-bumpmain (ontology to v3.15.0)
  4. Restart 5 kernels (4 Z440 + 1 hp-laptop) on new binary + new ontology
  5. Verify /operad/node-types reports clock; /operad/rewrite-categories reports WF21; channel.kind enum includes video+audio
  6. MUTATE grammar_fragment:v314-{2,3,4,6} proposed → promoted (atomic batch)
  7. MUTATE promoted → merged
  8. Phase 2 LINK lifts: 4 claim caused-by LINKs + N derivation consumes/produces lifts from stochastic_weights.anchors[]

Round-15 vehicle: ffs0#42

…ook (round-15 v314-3)

Round-15 ceremony validator-extension companion to v314-3-wf21-causes
grammar_fragment promotion (Z440 :8000 log_seq 388, status=proposed).

Adds:
- ValidateCausalAcyclic(env, state) on Registry — BFS forward from tgt
  through outgoing WF21 causes-edges; rejects if env.SrcURN reachable.
  Handles non-WF21 LINKs (pass-through), non-LINK rewrites (pass-through),
  self-LINK 1-cycle, direct 2-cycles, transitive N-cycles, DAG forks
  (pass), and reverse-direction caused-by edges (ignored — only true
  causes-direction edges count).
- runtime.go hooks both Apply (single-envelope path) and ApplyProgram
  (atomic-batch path; uses workingState so intra-batch causes-LINKs
  cannot close cycles even when individual envelopes pass against
  pre-batch state).
- 8 unit tests covering all branches.

Doctrine anchors:
- kb/research/spec/07-time-fabric.md §7.3.5 (WF21 lift path)
- kb/research/spec/05-external-substrates.md §5.1.5 + §5.3.5
  (transitional citation surface → caused-by LINK lift)

Pairs with v314-2-clock-type + v314-4-substrate-property + v314-6-
channel-kind-video promotions; ontology.json bump v3.14.0 → v3.15.0
holds on ffs0 wolfram/r15-ontology-v3-15-bump branch pending this
PR's merge + 5-kernel rebuild.

All operad tests pass; full suite green.
Copilot AI review requested due to automatic review settings April 26, 2026 18:05
@MSD21091969 MSD21091969 merged commit 7303aa8 into master Apr 26, 2026
2 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Extends operad/kernel validation to support the new WF21 causes/caused-by rewrite category by enforcing acyclicity for WF21 LINK rewrites, and integrates the check into both single-envelope and batch apply paths.

Changes:

  • Added Registry.ValidateCausalAcyclic implementing a forward BFS to reject WF21 LINKs that would close a causes-cycle.
  • Hooked WF21 acyclicity validation into Runtime.applyWithOptions and Runtime.ApplyProgram (working-state-aware for intra-batch detection).
  • Added unit tests covering pass-through cases and cycle detection scenarios for WF21 causes edges.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
internal/operad/validate.go Adds WF21 acyclicity validator using BFS over existing causes edges
internal/kernel/runtime.go Calls new validator during LINK validation in both Apply and ApplyProgram
internal/operad/validate_causal_acyclic_test.go Adds unit tests for WF21 acyclicity behavior

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +30 to +33
state.Relations[relURN] = graph.Relation{
URN: relURN,
RewriteCategory: graph.RewriteCategory("WF21"),
SrcURN: e[0],
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

This file repeatedly uses graph.RewriteCategory("WF21") literals. To match existing usage of typed WF constants (graph.WF18/WF19/etc.) and reduce typo risk, consider adding graph.WF21 and using it throughout these tests and the validator.

Copilot uses AI. Check for mistakes.
Comment on lines +419 to +423
for relURN := range state.RelationsBySrc[cur] {
rel, ok := state.Relations[relURN]
if !ok {
continue
}
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

ValidateCausalAcyclic iterates via state.RelationsBySrc[cur] directly. Per GraphState docs, indexes can be nil/uninitialized (e.g., state loaded from JSON without Rebuild), and read paths are expected to tolerate that. As written, a nil RelationsBySrc will make the BFS see no outgoing edges and may incorrectly allow a cycle. Prefer using the GraphState accessor (RelationsFrom) or falling back to scanning state.Relations when the index is nil.

Copilot uses AI. Check for mistakes.
Comment on lines +416 to +418
for len(queue) > 0 {
cur := queue[0]
queue = queue[1:]
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

The BFS queue uses queue = queue[1:], which repeatedly reslices and can retain the underlying array, increasing allocations/GC for large traversals. Consider an index-based queue (head integer) or a simple ring buffer pattern to keep this validation cheaper on large graphs.

Suggested change
for len(queue) > 0 {
cur := queue[0]
queue = queue[1:]
head := 0
for head < len(queue) {
cur := queue[head]
head++

Copilot uses AI. Check for mistakes.
Comment on lines +404 to +406
if env.RewriteCategory != graph.RewriteCategory("WF21") {
return nil
}
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

This introduces a raw rewrite-category literal graph.RewriteCategory("WF21"). Elsewhere the codebase consistently uses typed constants (e.g., graph.WF15/WF19) to avoid typos and keep refactors centralized. Consider adding graph.WF21 in internal/graph/relation.go and using that constant here (and in tests).

Copilot uses AI. Check for mistakes.
Comment on lines +380 to +381
// rewrites. Called with the kernel read-lock held (state is consistent), in
// the same apply path as ValidateStrataLink.
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

The doc comment says this is called with the kernel read-lock held, but the current apply paths call registry LINK validations under rt.mu.Lock() (write-lock). To avoid misleading future callers, consider rewording to something like “called with the kernel lock held / state is consistent” rather than specifying a read-lock.

Suggested change
// rewrites. Called with the kernel read-lock held (state is consistent), in
// the same apply path as ValidateStrataLink.
// rewrites. Called with the kernel lock held (state is consistent), in the
// same apply path as ValidateStrataLink.

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +41
func buildAcyclicTestState(edges [][2]graph.URN) graph.GraphState {
state := graph.NewGraphState()
urnSet := map[graph.URN]struct{}{}
for _, e := range edges {
urnSet[e[0]] = struct{}{}
urnSet[e[1]] = struct{}{}
}
for urn := range urnSet {
state.Nodes[urn] = graph.Node{
URN: urn,
TypeID: "derivation",
}
graph.IndexAddNodeByType(state.NodesByType, urn, "derivation")
}
for i, e := range edges {
relURN := graph.URN("urn:moos:rel:test." + string(e[0]) + ".causes." + string(e[1]) + "." + string(rune('a'+i)))
state.Relations[relURN] = graph.Relation{
URN: relURN,
RewriteCategory: graph.RewriteCategory("WF21"),
SrcURN: e[0],
SrcPort: "causes",
TgtURN: e[1],
TgtPort: "caused-by",
CreatedAt: time.Now(),
}
graph.IndexAddRelationEndpoints(state.RelationsBySrc, state.RelationsByTgt, relURN, e[0], e[1])
}
return state
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

The tests only exercise states built via graph.NewGraphState (indexes initialized). Since GraphState indexes can be nil after JSON load (until Rebuild), it would be good to add a test case where Relations is populated but RelationsBySrc is nil, to ensure ValidateCausalAcyclic still detects cycles (or to enforce the intended fallback behavior).

Copilot uses AI. Check for mistakes.
@MSD21091969 MSD21091969 deleted the round15/v314-2-3-4-6-fragments branch April 28, 2026 19:11
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