Engine · 5

Frontier

Given a goal — a diagnosis, an outcome, any rule — frontier backward-chains the goal's when: body to the recursive AND/OR set of observable facts whose acquisition would prove it. The goal need not be present on the chart.

Backward-chaining

Frontier walks the goal rule and recurses through any derived anchor to the observable leaves, marking each fact satisfied (already on the chart) or needed. A corpus with a producer rule and a diagnosis that requires a necessary, weighted anchor:

// modules/derive.trl — a producer rule: LowerUrinaryTract is DERIVED, not observed
rule lower_uti { when: $p [+Dysuria] $p [+Frequency] then: $p [+LowerUrinaryTract] }

// tropes/pyelo.trl — the diagnosis. [=Pyuria!*9]: a NECESSARY anchor, evidence weight 9
// @prevalence 4
rule Pyelo {
  when:
    evt $e [&Presents(agent=$p)]
    $p [=Pyuria!*9] [+LowerUrinaryTract] [+Fever]
  then:
    $p [+Pyelo]
}

Against a chart holding only the two derivation inputs:

// chart.trl — the facts observed so far
char pt
scene visit { beat 1 { pt [+Dysuria] pt [+Frequency] } }
$ tropelang frontier chart.trl Pyelo --corpus file://trl

== frontier(Pyelo) — reachable: yes ==
Pyelo  ⇐ rule
  Presents            ✗ needed   (observable)
  Pyuria              ✗ needed   (observable)  !necessary  ·weight 9
  LowerUrinaryTract   ⇐ derived  (rule lower_uti)
    Dysuria           ✓ satisfied
    Frequency         ✓ satisfied
  Fever               ✗ needed   (observable)

needed:  Presents · Pyuria (!necessary, weight 9) · Fever

Dysuria and Frequency are on the chart, so the derived LowerUrinaryTract is satisfied and frontier stops there rather than requesting it directly. Closing Pyelo still requires Presents, Fever, and Pyuria — the last flagged necessary (its absence rules the diagnosis out) and carrying weight 9 for the downstream prediction.

Output

reachable
yes — every branch bottoms out in acquirable leaves; no — a dead end; unknown — a branch hit a construct v1 doesn't backward-chain (not, count, temporal). The blocking construct is always named, never silently dropped.
needed[]
each acquirable leaf with its role (necessary / bonus / plain) and weight — the acquisition worklist.
satisfied[]
the goal's facts the chart already provides (directly or by derivation).
goalKnown
false warns the goal isn't a rule name or derived fact in the corpus — a likely typo, distinct from a genuinely unsurfaced diagnosis.

Grounding is schematic — frontier proves the goal on one bound subject. Narrow a multi-subject chart with the query language's where(…) slice first, then run the verb over the slice. The marks and @prevalence are authored on the corpus exactly as recognition uses them; no separate authoring is required for planning.

Interfaces

CLI
tropelang frontier <chart> <goal> [--corpus <url>…] [--leaves] [--json]--leaves drops the tree for just the worklist.
WASM
frontier(src, dx, lean){ goal, tree, needed, satisfied, reachable, unknownConstructs, goalKnown }; lean drops the tree.