Skip to content

Per-edge blocking policy (block|warn|hide) and OR-groups, once edges have identity #224

Open

Description

@Kuldeep2822k

Context

#222 / #223 made inference non-gating by note: a note whose depends_on came from a listing document's order carries depends_on_source: toc, and all of that note's edges are advisory. That is the coarse version of the problem, and it is correct for v0.5.x because the writer stamps one label per note.

It is too coarse in two ways that will bite as soon as the sources mix.

Gap 1 — the policy is per note, but it is a property of each edge

depends_on is string[]. A note can hold one authored prerequisite and one inferred predecessor at the same time, and today a single label decides the gating behaviour of the whole list. Three consequences:

The model that fits is per-edge policy: each edge carries block | warn | hide, where warn is "show it in palee plan, do not lock the note" — which is what Moodle and Canvas surface for an unmet prerequisite — and hide remains today's behaviour for authored edges. Storage would move from a note-level depends_on_source to a per-edge shape, e.g.

depends_on:
  - id: T-linear-algebra
    via: authored
    policy: block
  - id: T-svd
    via: toc
    policy: warn

with today's plain string list remaining the canonical shorthand for policy: block, so existing vaults parse unchanged.

Gap 2 — AND-only composition

areDependenciesSatisfied requires every prerequisite. Real curricula are not AND-only:

  • Waterloo's prerequisite grammar has "at least n of the following", which is a min_count, not a set of independent edges.
  • OpenDSA models a lesson with fan-in where any one of several backgrounds suffices.
  • LOM's relation kinds distinguish Requires from IsPartOf; the second is membership, not a gate, and today both would be written as depends_on.
  • Airflow solved the identical problem on a completely different domain with trigger_rule (all_success / one_success / none_failed_min_one_success) on the task, not the edges.

The corresponding schema addition is an OR-group:

depends_on_any_of:
  - [T-python-basics, T-julia-basics]
min_count: 1

Precedents worth copying rather than reinventing

SCORM/SCORM 2004 sequencing already specifies this exactly: <imsss:preCondition> with <action>conditional</action> for soft gates versus <action>deny</action> for hard ones, and an <or> operator over <sequencingRule> trees. LRMI/LOM describe prerequisites as relation metadata rather than as blocking state. Whatever we land on should be expressible as a subset of the SCORM rule model rather than a bespoke grammar, in case vault interoperability ever matters.

Why deferred

This is a frontmatter schema addition, which means a palee_schema version question, a migration path for existing vaults, and an invariant update (INV-46/47 both speak about edges as though they were uniform). #222/#223 removed the harm the schema would have mitigated, by taking the gating away from inference entirely. Doing the schema first, before the hub work in the #205 plan settles which edges exist at all, would be designing a grammar for an edge set that is still moving.

Sequencing: after the hub rework (enumerating document becomes the predecessor, so edges get a stable identity and an authoring source), when per-edge metadata has something to attach to.

Environment

Activity

  1. added
    enhancementNew feature or request
    coreCore source code changes
    status: blockedBlocked by another issue or external dependency
    P2Medium-High Priority (Scheduled for release)
    on Sep 27, 2026
  2. Kuldeep2822k commented on Sep 30, 2026

    @Kuldeep2822k
    OwnerAuthor

    Verification round - still genuinely blocked, but not on the blocker this issue names

    Re-read against feat/73-tier0-hygiene @ 0d222bf.

    The stated precondition was "once edges have identity", and depends_on_source does not supply it. It is a note-level key (src/types.ts:568) written once per note (src/cli/adopt.ts:1021-1023), while depends_on remains a string[] (src/types.ts:133,557) read as a whole-list short-circuit (src/engine/dependency.ts:771-789). Provenance is a property of the note's list, not of an edge, so per-edge block|warn|hide still has nothing to attach to.

    Gap 1 is partly mooted rather than fixed: a declared edge replaces the inferred one (src/cli/adopt.ts:677-690) because one label covers the whole list, and 04f1910 (#240) narrowed declaration reading to ## Prerequisites links only. No policy, via, or block|warn|hide exists anywhere in src. Gap 2 (depends_on_any_of / min_count) is unimplemented and unchanged - every dependency is required at src/engine/dependency.ts:773-787.

    Two real blockers, and I would re-label the issue onto them:

    1. A palee_schema version decision. Edge objects with per-edge metadata cannot be read by SUPPORTED_SCHEMA_VERSION = 1 (src/engine/topic-id.ts:16); anything else is rejected at src/validation/rules/valid-palee-schema.ts:73. That is a breaking-format ruling, not an implementation task.
    2. auto-chain produces semantically false prerequisites (measured: 98.2% of edges on 10 real study vaults) - tiered fix design #205, still open, which decides which edges exist at all. Sizing a policy surface before the edge set is settled would be wasted.

    Keeping status: blocked. Nothing in #214's stack unblocks this - the label work makes #224 legible, not implementable.

  3. Kuldeep2822k commented on Oct 1, 2026

    @Kuldeep2822k
    OwnerAuthor

    Unblocking this needs one ruling, not code. Here is the decision in the form it can be answered yes or no to.

    What is actually blocked

    Per-edge block | warn | hide and OR-groups need an edge to carry a property. Today depends_on is string[] (src/types.ts:557 area) with one note-level depends_on_source (:587) written once per note (src/cli/adopt.ts:1020-1024) and read as a whole-list short-circuit (src/engine/dependency.ts:771). Provenance is a property of the note's list, so block|warn|hide has nothing to attach to — and an OR-group cannot be expressed at all, because every entry is required.

    The blocker is therefore not "edges have identity" (that label work landed) but palee_schema v2: SUPPORTED_SCHEMA_VERSION = 1 (src/engine/topic-id.ts) and anything else is rejected at src/validation/rules/valid-palee-schema.ts. Edge objects are a frontmatter format change, and a format change is a release decision.

    The ruling, stated small

    Adopt v2 as an additive read, exclusive write.

    • Read: v1 notes keep working unchanged — depends_on: [T-a] plus optional depends_on_source is interpreted as edges all sharing one source, which is exactly the current semantics. No migration required to open an existing vault.
    • Write: a note whose edges genuinely differ (one authored, one enumerated) writes depends_on: [{ id, source, policy }] and palee_schema: 2. A note whose edges are uniform keeps writing v1, so most vaults never change on disk.
    • Cost, honestly: every consumer of depends_on sees a union type — the loader's normalisation, validate's cycle and dangling rules, plan's readiness, roadmap's explicit-wins check, and the tests that deep-compare frontmatter. That is the real price, and it is why this is a release-sized change rather than a feature.
    • What it buys immediately: per-edge policy (the warn tier this issue wants for enumeration edges, instead of today's all-or-nothing advisory set), and depends_on_any_of for courses with interchangeable prerequisites.

    What I would not do

    A parallel key (depends_on_policy: {...} alongside depends_on) avoids the schema bump and buys nothing: two sources of truth for one edge, and every reader must consult both forever. The label field already shows how that goes — it had a writer and no reader for a release.

    If the answer is "not now"

    Then this issue should say so and close, because nothing else here is pending: block|warn|hide and OR-groups both wait on the format, and the current advisory set already covers the two cases that motivated the request (toc, tie). Leaving it status: blocked with no ruling is the worst of the three outcomes.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium-High Priority (Scheduled for release)coreCore source code changesenhancementNew feature or requestpriority: mediumMedium priorityrelease: minorMinor releasestatus: blockedBlocked by another issue or external dependency

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions