@almadar/std 16.144.0 → 16.146.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/behaviors/lolo/ml/atoms/std-ml-classify.lolo +121 -0
  2. package/behaviors/lolo/ml/atoms/std-ml-exact-check.lolo +90 -0
  3. package/behaviors/lolo/ml/atoms/std-ml-infer.lolo +141 -0
  4. package/behaviors/lolo/ml/atoms/std-ml-label-capture.lolo +75 -0
  5. package/behaviors/lolo/ml/atoms/std-ml-lookup.lolo +83 -0
  6. package/behaviors/lolo/ml/atoms/std-ml-posterior.lolo +104 -0
  7. package/behaviors/lolo/ml/atoms/std-ml-similarity.lolo +166 -0
  8. package/behaviors/lolo/ml/molecules/std-escalating-decision.lolo +271 -0
  9. package/behaviors/lolo/ml/molecules/std-knowledge-tracing.lolo +115 -0
  10. package/behaviors/lolo/ui/core/atoms/std-app-layout.lolo +14 -0
  11. package/behaviors/lolo/ui/core/atoms/std-board.lolo +20 -6
  12. package/behaviors/lolo/ui/core/atoms/std-browse.lolo +23 -6
  13. package/behaviors/lolo/ui/core/atoms/std-calendar.lolo +80 -2
  14. package/behaviors/lolo/ui/core/atoms/std-stats.lolo +1 -1
  15. package/behaviors/lolo/ui/core/atoms/std-tag-taxonomy.lolo +3 -3
  16. package/behaviors/lolo/ui/core/atoms/ui-typography.lolo +1 -1
  17. package/behaviors/lolo/ui/core/molecules/ui-calendar-grid.lolo +18 -0
  18. package/behaviors/lolo/ui/core/molecules/ui-data-grid.lolo +2 -0
  19. package/behaviors/lolo/ui/core/molecules/ui-data-list.lolo +3 -2
  20. package/behaviors/lolo/ui/core/molecules/ui-filter-group.lolo +20 -0
  21. package/behaviors/lolo/ui/core/molecules/ui-search-input.lolo +7 -2
  22. package/behaviors/lolo/ui/core/molecules/ui-table-view.lolo +3 -2
  23. package/behaviors/lolo/ui/core/organisms/ui-entity-cards.lolo +2 -0
  24. package/behaviors/lolo/ui/core/organisms/ui-entity-list.lolo +2 -0
  25. package/behaviors/lolo/ui/core/organisms/ui-entity-table.lolo +2 -0
  26. package/behaviors/lolo/ui/core/organisms/ui-timeline.lolo +2 -0
  27. package/behaviors/lolo/ui/game/atoms/std-dungeon-combat.lolo +2 -2
  28. package/behaviors/lolo/ui/game/molecules/std-worldmap-board-2d.lolo +1 -1
  29. package/behaviors/lolo/ui/game/organisms/ui-boardgame-board.lolo +2 -2
  30. package/behaviors/lolo/ui/game/organisms/ui-builder-board.lolo +2 -2
  31. package/behaviors/lolo/ui/game/organisms/ui-debugger-board.lolo +3 -3
  32. package/behaviors/lolo/ui/game/organisms/ui-physics-sandbox-board.lolo +3 -3
  33. package/behaviors/lolo/ui/game/organisms/ui-roguelike-board.lolo +1 -1
  34. package/behaviors/lolo/ui/game/organisms/ui-sequencer-board.lolo +1 -1
  35. package/behaviors/lolo/ui/learning/atoms/std-algo-heapsort.lolo +2 -2
  36. package/behaviors/lolo/ui/learning/atoms/std-algo-mst.lolo +3 -3
  37. package/behaviors/registry/factory-signatures.json +5018 -824
  38. package/behaviors/registry/ml/atoms/std-ml-classify.orb +622 -0
  39. package/behaviors/registry/ml/atoms/std-ml-exact-check.orb +505 -0
  40. package/behaviors/registry/ml/atoms/std-ml-infer.orb +770 -0
  41. package/behaviors/registry/ml/atoms/std-ml-label-capture.orb +367 -0
  42. package/behaviors/registry/ml/atoms/std-ml-lookup.orb +473 -0
  43. package/behaviors/registry/ml/atoms/std-ml-posterior.orb +669 -0
  44. package/behaviors/registry/ml/atoms/std-ml-similarity.orb +917 -0
  45. package/behaviors/registry/ml/molecules/std-escalating-decision.orb +1553 -0
  46. package/behaviors/registry/ml/molecules/std-knowledge-tracing.orb +577 -0
  47. package/behaviors/registry/ui/core/atoms/std-app-layout.orb +33 -1
  48. package/behaviors/registry/ui/core/atoms/std-board.orb +64 -20
  49. package/behaviors/registry/ui/core/atoms/std-browse.orb +83 -48
  50. package/behaviors/registry/ui/core/atoms/std-calendar.orb +213 -3
  51. package/behaviors/registry/ui/core/atoms/ui-typography.orb +1 -1
  52. package/behaviors/registry/ui/core/molecules/ui-calendar-grid.orb +27 -0
  53. package/behaviors/registry/ui/core/molecules/ui-data-grid.orb +25 -12
  54. package/behaviors/registry/ui/core/molecules/ui-data-list.orb +26 -13
  55. package/behaviors/registry/ui/core/molecules/ui-filter-group.orb +86 -0
  56. package/behaviors/registry/ui/core/molecules/ui-search-input.orb +37 -1
  57. package/behaviors/registry/ui/core/molecules/ui-table-view.orb +25 -12
  58. package/behaviors/registry/ui/core/organisms/ui-entity-cards.orb +13 -0
  59. package/behaviors/registry/ui/core/organisms/ui-entity-list.orb +21 -8
  60. package/behaviors/registry/ui/core/organisms/ui-entity-table.orb +13 -0
  61. package/behaviors/registry/ui/core/organisms/ui-timeline.orb +13 -0
  62. package/behaviors/registry/ui/game/atoms/std-dungeon-combat.orb +4 -4
  63. package/behaviors/registry/ui/game/molecules/std-worldmap-board-2d.orb +6 -9
  64. package/behaviors/registry/ui/game/organisms/ui-boardgame-board.orb +4 -4
  65. package/behaviors/registry/ui/game/organisms/ui-builder-board.orb +4 -4
  66. package/behaviors/registry/ui/game/organisms/ui-debugger-board.orb +12 -12
  67. package/behaviors/registry/ui/game/organisms/ui-physics-sandbox-board.orb +19 -19
  68. package/behaviors/registry/ui/game/organisms/ui-roguelike-board.orb +1 -1
  69. package/behaviors/registry/ui/game/organisms/ui-sequencer-board.orb +2 -2
  70. package/behaviors/registry/ui/learning/atoms/std-algo-heapsort.orb +8 -8
  71. package/canonical-operators.json +30 -6
  72. package/dist/behaviors/behaviors-embeddings.json +267 -267
  73. package/dist/behaviors/exports-reader.js +2462 -2080
  74. package/dist/behaviors/functions/index.d.ts +725 -1
  75. package/dist/behaviors/functions/index.js +2437 -2081
  76. package/dist/behaviors/index.d.ts +1 -1
  77. package/dist/behaviors/index.js +2463 -2081
  78. package/dist/behaviors/knob-embeddings.json +1 -1
  79. package/dist/behaviors/query.js +2462 -2080
  80. package/dist/behaviors/registry/factory-signatures.json +5018 -824
  81. package/dist/behaviors/registry/ml/atoms/std-ml-classify.orb +622 -0
  82. package/dist/behaviors/registry/ml/atoms/std-ml-exact-check.orb +505 -0
  83. package/dist/behaviors/registry/ml/atoms/std-ml-infer.orb +770 -0
  84. package/dist/behaviors/registry/ml/atoms/std-ml-label-capture.orb +367 -0
  85. package/dist/behaviors/registry/ml/atoms/std-ml-lookup.orb +473 -0
  86. package/dist/behaviors/registry/ml/atoms/std-ml-posterior.orb +669 -0
  87. package/dist/behaviors/registry/ml/atoms/std-ml-similarity.orb +917 -0
  88. package/dist/behaviors/registry/ml/molecules/std-escalating-decision.orb +1553 -0
  89. package/dist/behaviors/registry/ml/molecules/std-knowledge-tracing.orb +577 -0
  90. package/dist/behaviors/registry/ui/core/atoms/std-app-layout.orb +33 -1
  91. package/dist/behaviors/registry/ui/core/atoms/std-board.orb +64 -20
  92. package/dist/behaviors/registry/ui/core/atoms/std-browse.orb +83 -48
  93. package/dist/behaviors/registry/ui/core/atoms/std-calendar.orb +213 -3
  94. package/dist/behaviors/registry/ui/core/atoms/ui-typography.orb +1 -1
  95. package/dist/behaviors/registry/ui/core/molecules/ui-calendar-grid.orb +27 -0
  96. package/dist/behaviors/registry/ui/core/molecules/ui-data-grid.orb +25 -12
  97. package/dist/behaviors/registry/ui/core/molecules/ui-data-list.orb +26 -13
  98. package/dist/behaviors/registry/ui/core/molecules/ui-filter-group.orb +86 -0
  99. package/dist/behaviors/registry/ui/core/molecules/ui-search-input.orb +37 -1
  100. package/dist/behaviors/registry/ui/core/molecules/ui-table-view.orb +25 -12
  101. package/dist/behaviors/registry/ui/core/organisms/ui-entity-cards.orb +13 -0
  102. package/dist/behaviors/registry/ui/core/organisms/ui-entity-list.orb +21 -8
  103. package/dist/behaviors/registry/ui/core/organisms/ui-entity-table.orb +13 -0
  104. package/dist/behaviors/registry/ui/core/organisms/ui-timeline.orb +13 -0
  105. package/dist/behaviors/registry/ui/game/atoms/std-dungeon-combat.orb +4 -4
  106. package/dist/behaviors/registry/ui/game/molecules/std-worldmap-board-2d.orb +6 -9
  107. package/dist/behaviors/registry/ui/game/organisms/ui-boardgame-board.orb +4 -4
  108. package/dist/behaviors/registry/ui/game/organisms/ui-builder-board.orb +4 -4
  109. package/dist/behaviors/registry/ui/game/organisms/ui-debugger-board.orb +12 -12
  110. package/dist/behaviors/registry/ui/game/organisms/ui-physics-sandbox-board.orb +19 -19
  111. package/dist/behaviors/registry/ui/game/organisms/ui-roguelike-board.orb +1 -1
  112. package/dist/behaviors/registry/ui/game/organisms/ui-sequencer-board.orb +2 -2
  113. package/dist/behaviors/registry/ui/learning/atoms/std-algo-heapsort.orb +8 -8
  114. package/dist/behaviors-embeddings.json +267 -267
  115. package/dist/index.d.ts +1 -1
  116. package/dist/index.js +2498 -2088
  117. package/dist/knob-embeddings.json +1 -1
  118. package/dist/modules/array.js +28 -0
  119. package/dist/modules/contract.js +4 -4
  120. package/dist/modules/data.js +3 -3
  121. package/dist/modules/index.js +35 -7
  122. package/dist/registry/factory-signatures.json +5018 -824
  123. package/dist/registry.js +35 -7
  124. package/package.json +3 -3
@@ -0,0 +1,166 @@
1
+ app std-ml-similarity "1.0.0"
2
+ "Retrieval rung (R3) — embed a candidate against references and gate on cosine floor + margin; abstains when not confident."
3
+
4
+ orbital MlSimilarityOrbital {
5
+
6
+ entity SimilarityComparison [runtime] {
7
+ query : string = ""
8
+ candidates : [string] = []
9
+ embeddings : [[float]] = []
10
+ queryVector : [float] = []
11
+ candidateVectors : [[float]] = []
12
+ matchIndex : int = 0
13
+ matchScore : float = 0
14
+ matchMargin : float = 0
15
+ reason : string = ""
16
+ request : object = {}
17
+ status : "idle" | "embedding" | "resolved" = idle
18
+ }
19
+
20
+ trait SimilarityRun -> SimilarityComparison [lifecycle, instance] {
21
+ initial: idle
22
+
23
+ config {
24
+ floor : float = 0.82
25
+ @label "Match floor"
26
+ @description "Minimum cosine score for a confident match"
27
+ @tier "policy"
28
+ margin : float = 0.05
29
+ @label "Match margin"
30
+ @description "Minimum gap between the best and second-best score required to trust the match"
31
+ @tier "policy"
32
+ embedModel : string = ""
33
+ @label "Embedding model"
34
+ @description "Reserved for embedding-model selection; llm/embed takes only a texts array today, so this knob is not yet wired into the call"
35
+ @tier "infra"
36
+ }
37
+
38
+ state idle {
39
+ INIT -> idle
40
+ (set @entity.query "")
41
+ (set @entity.candidates [])
42
+ (set @entity.embeddings [])
43
+ (set @entity.queryVector [])
44
+ (set @entity.candidateVectors [])
45
+ (set @entity.matchIndex 0)
46
+ (set @entity.matchScore 0)
47
+ (set @entity.matchMargin 0)
48
+ (set @entity.reason "")
49
+ (set @entity.request {})
50
+ (set @entity.status idle)
51
+
52
+ COMPARE -> embedding when (> (array/len ?candidates) 0)
53
+ (set @entity.query ?query)
54
+ (set @entity.candidates ?candidates)
55
+ (set @entity.request ?request)
56
+ (set @entity.status embedding)
57
+ (llm/embed (array/prepend ?candidates ?query) { emit: { success: EMBEDDED, failure: EMBED_FAILED } })
58
+
59
+ COMPARE -> resolved when (== (array/len ?candidates) 0)
60
+ (set @entity.query ?query)
61
+ (set @entity.candidates [])
62
+ (set @entity.embeddings [])
63
+ (set @entity.queryVector [])
64
+ (set @entity.candidateVectors [])
65
+ (set @entity.matchIndex 0)
66
+ (set @entity.matchScore 0)
67
+ (set @entity.matchMargin 0)
68
+ (set @entity.reason "empty candidate list")
69
+ (set @entity.request ?request)
70
+ (set @entity.status resolved)
71
+ (emit SIMILARITY_ABSTAINED { reason: "empty candidate list", request: @entity.request })
72
+ }
73
+
74
+ state embedding {
75
+ EMBEDDED -> resolved
76
+ (set @entity.embeddings ?result)
77
+ (set @entity.status resolved)
78
+ ;; An embed result without query + at least one candidate vector is an
79
+ ;; abstain, not a crash — the rung can prove nothing from it.
80
+ (if (< (array/len ?result) 2)
81
+ (do
82
+ (set @entity.queryVector [])
83
+ (set @entity.candidateVectors [])
84
+ (set @entity.matchIndex 0)
85
+ (set @entity.matchScore 0)
86
+ (set @entity.matchMargin 0)
87
+ (set @entity.reason "embedding result incomplete")
88
+ (emit SIMILARITY_ABSTAINED { reason: "embedding result incomplete", request: @entity.request }))
89
+ (do
90
+ (set @entity.queryVector (array/first @entity.embeddings))
91
+ (set @entity.candidateVectors (array/drop @entity.embeddings 1))
92
+ (let ((nearest (array/nearest @entity.queryVector @entity.candidateVectors)))
93
+ (do
94
+ (set @entity.matchIndex (object/get @nearest "index"))
95
+ (set @entity.matchScore (object/get @nearest "score"))
96
+ (set @entity.matchMargin (object/get @nearest "margin"))
97
+ (if (and (>= @entity.matchScore @config.floor) (>= @entity.matchMargin @config.margin))
98
+ (do
99
+ (set @entity.reason "")
100
+ (emit SIMILARITY_MATCHED { index: @entity.matchIndex, score: @entity.matchScore }))
101
+ (do
102
+ (set @entity.reason (if (< @entity.matchScore @config.floor) "below floor" "thin margin"))
103
+ (emit SIMILARITY_ABSTAINED { reason: @entity.reason, request: @entity.request })))))))
104
+
105
+ EMBED_FAILED -> resolved
106
+ (set @entity.embeddings [])
107
+ (set @entity.queryVector [])
108
+ (set @entity.candidateVectors [])
109
+ (set @entity.matchIndex 0)
110
+ (set @entity.matchScore 0)
111
+ (set @entity.matchMargin 0)
112
+ (set @entity.reason "embedding unavailable")
113
+ (set @entity.status resolved)
114
+ (emit SIMILARITY_ABSTAINED { reason: "embedding unavailable", request: @entity.request })
115
+ }
116
+
117
+ state resolved {
118
+ INIT -> resolved
119
+ (set @entity.status resolved)
120
+ RESET -> idle
121
+ (set @entity.query "")
122
+ (set @entity.candidates [])
123
+ (set @entity.embeddings [])
124
+ (set @entity.queryVector [])
125
+ (set @entity.candidateVectors [])
126
+ (set @entity.matchIndex 0)
127
+ (set @entity.matchScore 0)
128
+ (set @entity.matchMargin 0)
129
+ (set @entity.reason "")
130
+ (set @entity.request {})
131
+ (set @entity.status idle)
132
+ }
133
+
134
+ emits {
135
+ EMBEDDED {
136
+ result : [[float]]!
137
+ }
138
+ @description "Raw embeddings from llm/embed — query vector at index 0, candidate vectors following in input order"
139
+ @tier "secondary"
140
+
141
+ SIMILARITY_MATCHED -> external {
142
+ index : int!
143
+ score : float!
144
+ }
145
+ @description "Candidate matched with high confidence — score at or above floor, with a clear margin over the runner-up"
146
+ @tier "primary"
147
+
148
+ SIMILARITY_ABSTAINED -> external {
149
+ reason : string!
150
+ request : object
151
+ }
152
+ @description "No confident match — reason is one of: empty candidate list, below floor, thin margin, embedding unavailable. request echoes the originating request untouched so the next rung can project its own inputs out of it"
153
+ @tier "primary"
154
+ }
155
+
156
+ listens {
157
+ COMPARE {
158
+ query : string!
159
+ candidates : [string]!
160
+ request : object
161
+ }
162
+ }
163
+ }
164
+
165
+ page "/ml-similarity" as MlSimilarityPage -> SimilarityRun
166
+ }
@@ -0,0 +1,271 @@
1
+ app std-escalating-decision "1.0.0"
2
+ "std-escalating-decision — the intelligence ladder itself, as one reusable composition. One request enters at DECIDE and falls through the rungs in order — R0 exact check, R1 table lookup, R3 retrieval, R4 learned inference, R5 generative classification — each rung handing off to the next by abstaining, until one proves the answer. Exits DECIDED, tagged with which rung answered (rung, confidence, matched, reasoning), or DECISION_FAILED if even the generative floor could not serve it. The escalation is a chain, not a hub: each rung listens directly to the rung above it and projects its own inputs out of the carried request, so the trait graph is a DAG and no event ever returns to a rung that already ran. Domain-agnostic: it knows about candidates and categories, not students or quizzes — grading, routing, matching, moderation, and duplicate detection are all this same molecule with different knobs. R2 (the Beta posterior) is deliberately not in this chain: it is the 'decide what to do next' half of a turn and already lives in std-knowledge-tracing."
3
+
4
+ orbital EscalatingDecisionOrbital {
5
+ uses Rung0 from "std/behaviors/std-ml-exact-check"
6
+ uses Rung1 from "std/behaviors/std-ml-lookup"
7
+ uses Rung3 from "std/behaviors/std-ml-similarity"
8
+ uses Rung4 from "std/behaviors/std-ml-infer"
9
+ uses Rung5 from "std/behaviors/std-ml-classify"
10
+
11
+ ;; The entry holds no request data: the whole DECIDE payload travels down
12
+ ;; the chain as `request`, so there is nothing for this trait to remember.
13
+ entity LadderRequest [runtime] {
14
+ status : "idle" | "dispatched" = idle
15
+ }
16
+
17
+ ;; `matched` is deliberately absent: it is an exact key, a table entry, a
18
+ ;; match index, a tensor, or a category label depending on which rung served
19
+ ;; the request. `any` is honest in payload position but lowers to `string` in
20
+ ;; entity position (C-LOLO-ANY-LOWERS-TO-STRING), so the value is carried on
21
+ ;; the DECIDED payload only, never mirrored onto this entity as a lie.
22
+ entity LadderVerdict [runtime] {
23
+ rung : string = ""
24
+ confidence : number = 0
25
+ reasoning : string = ""
26
+ failReason : string = ""
27
+ status : "idle" | "decided" | "failed" = idle
28
+ }
29
+
30
+ entity ExactCheck [runtime] {
31
+ matched : boolean = false
32
+ status : "idle" | "checked" = idle
33
+ }
34
+
35
+ entity KeyLookup [runtime] {
36
+ matched : boolean = false
37
+ status : "idle" | "checked" = idle
38
+ }
39
+
40
+ entity SimilarityComparison [runtime] {
41
+ query : string = ""
42
+ candidates : [string] = []
43
+ embeddings : [[float]] = []
44
+ queryVector : [float] = []
45
+ candidateVectors : [[float]] = []
46
+ matchIndex : int = 0
47
+ matchScore : float = 0
48
+ matchMargin : float = 0
49
+ reason : string = ""
50
+ request : object = {}
51
+ status : "idle" | "embedding" | "resolved" = idle
52
+ }
53
+
54
+ entity Inference [runtime] {
55
+ input : object = {}
56
+ output : [float] = []
57
+ confidence : float = 0
58
+ violations : [object] = []
59
+ reason : string = ""
60
+ request : object = {}
61
+ status : "idle" | "inferring" | "resolved" = idle
62
+ }
63
+
64
+ entity ClassifierVerdict [runtime] {
65
+ text : string = ""
66
+ category : string = ""
67
+ confidence : float = 0
68
+ reasoning : string = ""
69
+ errorMessage : string = ""
70
+ request : object = {}
71
+ status : "idle" | "classifying" | "done" = idle
72
+ }
73
+
74
+ ;; The ladder's entry. Holds nothing the rungs need — the whole DECIDE
75
+ ;; payload travels down the chain as `request`, and each rung projects its
76
+ ;; own inputs out of it. This trait listens to no rung, which is what keeps
77
+ ;; the graph acyclic.
78
+ trait LadderEntry -> LadderRequest [lifecycle, instance] {
79
+ initial: idle
80
+
81
+ state idle {
82
+ INIT -> idle
83
+ (set @entity.status idle)
84
+
85
+ DECIDE -> dispatched
86
+ (set @entity.status dispatched)
87
+ (emit RUN_EXACT { candidate: ?candidate, key: ?key, request: ? })
88
+ }
89
+
90
+ state dispatched {
91
+ INIT -> dispatched
92
+ (set @entity.status dispatched)
93
+ RESET -> idle
94
+ (set @entity.status idle)
95
+ }
96
+
97
+ emits {
98
+ RUN_EXACT {
99
+ candidate : any!
100
+ key : any!
101
+ request : object!
102
+ }
103
+ @description "Starts the ladder at the exact-check rung (R0). request is the whole DECIDE payload, carried down the chain so each rung can project its own inputs."
104
+ @tier "secondary"
105
+ }
106
+
107
+ listens {
108
+ DECIDE {
109
+ candidate : any!
110
+ key : any!
111
+ accepted : [any]!
112
+ categories : [string]!
113
+ }
114
+ @description "Public entry point — decide the candidate, falling through the ladder until a rung can prove the answer. accepted is R1's table; categories is R3's comparison set (R5's closed label set is the ClassifyRung categories knob, a design-time decision rather than a per-request one)."
115
+ @tier "primary"
116
+ }
117
+ }
118
+
119
+ trait ExactRung = Rung0.traits.ExactCheckRun -> ExactCheck {
120
+ listens {
121
+ LadderEntry.RUN_EXACT -> CHECK
122
+ }
123
+ }
124
+
125
+ trait LookupRung = Rung1.traits.LookupRun -> KeyLookup {
126
+ listens {
127
+ ExactRung.EXACT_UNMATCHED -> LOOKUP
128
+ with { candidate: ?candidate, accepted: (object/get ?request "accepted"), request: ?request }
129
+ }
130
+ }
131
+
132
+ trait SimilarityRung = Rung3.traits.SimilarityRun -> SimilarityComparison {
133
+ listens {
134
+ LookupRung.KEY_MISS -> COMPARE
135
+ with { query: (str/concat ?candidate), candidates: (object/get ?request "categories"), request: ?request }
136
+ }
137
+ }
138
+
139
+ trait InferRung = Rung4.traits.InferenceRun -> Inference {
140
+ listens {
141
+ SimilarityRung.SIMILARITY_ABSTAINED -> INFER
142
+ with { input: ?request, request: ?request }
143
+ }
144
+ }
145
+
146
+ trait ClassifyRung = Rung5.traits.ClassifyRun -> ClassifierVerdict {
147
+ listens {
148
+ InferRung.INFER_ABSTAINED -> CLASSIFY
149
+ with { text: (str/concat (object/get ?request "candidate")), request: ?request }
150
+ }
151
+ }
152
+
153
+ ;; The sink. Listens to every rung's success plus the generative floor's
154
+ ;; failure, and emits the one public verdict. Nothing listens to it, so it
155
+ ;; closes the DAG.
156
+ trait LadderVerdictSink -> LadderVerdict [lifecycle, instance] {
157
+ initial: idle
158
+
159
+ state idle {
160
+ INIT -> idle
161
+ (set @entity.rung "")
162
+ (set @entity.confidence 0)
163
+ (set @entity.reasoning "")
164
+ (set @entity.failReason "")
165
+ (set @entity.status idle)
166
+
167
+ FROM_EXACT -> decided
168
+ (set @entity.rung "R0")
169
+ (set @entity.confidence 1)
170
+ (set @entity.reasoning "")
171
+ (set @entity.status decided)
172
+ (emit DECIDED { rung: "R0", confidence: 1, matched: ?key, reasoning: "" })
173
+
174
+ FROM_LOOKUP -> decided
175
+ (set @entity.rung "R1")
176
+ (set @entity.confidence 1)
177
+ (set @entity.reasoning "")
178
+ (set @entity.status decided)
179
+ (emit DECIDED { rung: "R1", confidence: 1, matched: ?entry, reasoning: "" })
180
+
181
+ FROM_SIMILARITY -> decided
182
+ (set @entity.rung "R3")
183
+ (set @entity.confidence ?score)
184
+ (set @entity.reasoning "")
185
+ (set @entity.status decided)
186
+ (emit DECIDED { rung: "R3", confidence: ?score, matched: ?index, reasoning: "" })
187
+
188
+ FROM_INFER -> decided
189
+ (set @entity.rung "R4")
190
+ (set @entity.confidence ?confidence)
191
+ (set @entity.reasoning "")
192
+ (set @entity.status decided)
193
+ (emit DECIDED { rung: "R4", confidence: ?confidence, matched: ?output, reasoning: "" })
194
+
195
+ FROM_CLASSIFY -> decided
196
+ (set @entity.rung "R5")
197
+ (set @entity.confidence ?confidence)
198
+ (set @entity.reasoning ?reasoning)
199
+ (set @entity.status decided)
200
+ (emit DECIDED { rung: "R5", confidence: ?confidence, matched: ?category, reasoning: ?reasoning })
201
+
202
+ FROM_CLASSIFY_FAILURE -> failed
203
+ (set @entity.failReason ?error)
204
+ (set @entity.status failed)
205
+ (emit DECISION_FAILED { reason: ?error })
206
+ }
207
+
208
+ state decided {
209
+ INIT -> decided
210
+ (set @entity.status decided)
211
+ RESET -> idle
212
+ (set @entity.rung "")
213
+ (set @entity.confidence 0)
214
+ (set @entity.reasoning "")
215
+ (set @entity.failReason "")
216
+ (set @entity.status idle)
217
+ }
218
+
219
+ state failed {
220
+ INIT -> failed
221
+ (set @entity.status failed)
222
+ RESET -> idle
223
+ (set @entity.rung "")
224
+ (set @entity.confidence 0)
225
+ (set @entity.reasoning "")
226
+ (set @entity.failReason "")
227
+ (set @entity.status idle)
228
+ }
229
+
230
+ emits {
231
+ DECIDED -> external {
232
+ rung : string!
233
+ confidence : number!
234
+ matched : any!
235
+ reasoning : string!
236
+ }
237
+ @description "The ladder proved an answer. rung names which one served it: R0 exact, R1 table, R3 retrieved, R4 learned, R5 generative."
238
+ @tier "primary"
239
+
240
+ DECISION_FAILED -> external {
241
+ reason : string!
242
+ }
243
+ @description "Even the generative floor (R5) could not answer — the classify service call itself failed."
244
+ @tier "primary"
245
+ }
246
+
247
+ listens {
248
+ FROM_EXACT { key : any! }
249
+ FROM_LOOKUP { entry : any! }
250
+ FROM_SIMILARITY { index : int! score : float! }
251
+ FROM_INFER { output : [float]! confidence : float! }
252
+ FROM_CLASSIFY { category : string! confidence : float! reasoning : string! }
253
+ FROM_CLASSIFY_FAILURE { error : string! }
254
+
255
+ ExactRung.EXACT_MATCHED -> FROM_EXACT
256
+ with { key: ?key }
257
+ LookupRung.KEY_HIT -> FROM_LOOKUP
258
+ with { entry: ?entry }
259
+ SimilarityRung.SIMILARITY_MATCHED -> FROM_SIMILARITY
260
+ with { index: ?index, score: ?score }
261
+ InferRung.INFERRED -> FROM_INFER
262
+ with { output: ?output, confidence: ?confidence }
263
+ ClassifyRung.CLASSIFIED -> FROM_CLASSIFY
264
+ with { category: ?category, confidence: ?confidence, reasoning: ?reasoning }
265
+ ClassifyRung.CLASSIFY_FAILED -> FROM_CLASSIFY_FAILURE
266
+ with { error: ?error }
267
+ }
268
+ }
269
+
270
+ page "/ladder/decide" as LadderDecidePage -> LadderEntry, ExactRung, LookupRung, SimilarityRung, InferRung, ClassifyRung, LadderVerdictSink
271
+ }
@@ -0,0 +1,115 @@
1
+ app std-knowledge-tracing "1.0.0"
2
+ "std-knowledge-tracing — turns one true/false observation ('did the learner get this right?') into a tracked belief about mastery, and captures the same observation as a labeled training example in the same motion. Composes std-ml-posterior (the R2 statistical rung — a Beta-Bernoulli belief over an unknown success rate) with std-ml-label-capture: every posterior verdict — MASTERY_REACHED, REMEDIATION_NEEDED, or the abstain EVIDENCE_INSUFFICIENT — becomes a persisted labeled example (the input skill, the posterior's own verdict, and the ground-truth outcome that produced it), so a cheaper rung can later be trained to reproduce the same call without running the statistics. Domain-agnostic: it knows about skills and observations, not students, quizzes, or courses. One instance tracks one belief (e.g. one learner × one skill); the host composes as many instances as it has beliefs to hold."
3
+ orbital KnowledgeTracingOrbital {
4
+ uses Posterior from "std/behaviors/std-ml-posterior"
5
+ uses LabelCap from "std/behaviors/std-ml-label-capture"
6
+
7
+ entity KnowledgeState [runtime, shared] {
8
+ skillId : string = ""
9
+ lastOutcome : boolean = false
10
+ alpha : float = 1
11
+ beta : float = 1
12
+ mean : float = 0
13
+ intervalLow : float = 0
14
+ intervalHigh : float = 0
15
+ observations : int = 0
16
+ status : "idle" | "insufficient" | "mastered" | "remediate" = idle
17
+ input : json = null
18
+ verdict : json = null
19
+ source : string = ""
20
+ outcome : json = null
21
+ sink : string = ""
22
+ recordedAt : number = 0
23
+ }
24
+
25
+ trait ObservationRelay -> KnowledgeState [lifecycle, instance] {
26
+ initial: idle
27
+
28
+ state idle {
29
+ INIT -> idle
30
+ (set @entity.skillId "")
31
+ (set @entity.lastOutcome false)
32
+ OBSERVE_SKILL -> idle
33
+ (set @entity.skillId ?skillId)
34
+ (set @entity.lastOutcome ?outcome)
35
+ (emit OBSERVATION_MADE { outcome: ?outcome })
36
+ VERDICT_REACHED -> idle
37
+ (emit CAPTURE_LABEL {
38
+ input: { skillId: @entity.skillId },
39
+ verdict: { status: ?status, mean: ?mean, intervalLow: ?intervalLow, intervalHigh: ?intervalHigh },
40
+ source: "posterior",
41
+ outcome: @entity.lastOutcome
42
+ })
43
+ }
44
+
45
+ emits {
46
+ OBSERVATION_MADE {
47
+ outcome : boolean!
48
+ }
49
+ @description "Forwards the raw outcome to PosteriorTracker once skillId is recorded on the shared entity."
50
+ @tier "secondary"
51
+
52
+ CAPTURE_LABEL {
53
+ input : any!
54
+ verdict : any!
55
+ source : string!
56
+ outcome : any
57
+ }
58
+ @description "The posterior's verdict reshaped into a labeled example for LabelCaptureListener."
59
+ @tier "secondary"
60
+ }
61
+
62
+ listens {
63
+ OBSERVE_SKILL {
64
+ skillId : string!
65
+ outcome : boolean!
66
+ }
67
+ @description "Public entry point — one true/false observation of a learner against one skill."
68
+ @tier "primary"
69
+
70
+ PosteriorTracker.MASTERY_REACHED -> VERDICT_REACHED
71
+ with { status: "mastered", mean: ?mean, intervalLow: ?intervalLow, intervalHigh: ?intervalHigh }
72
+ PosteriorTracker.REMEDIATION_NEEDED -> VERDICT_REACHED
73
+ with { status: "remediate", mean: ?mean, intervalLow: ?intervalLow, intervalHigh: ?intervalHigh }
74
+ PosteriorTracker.EVIDENCE_INSUFFICIENT -> VERDICT_REACHED
75
+ with { status: "insufficient", mean: ?mean, intervalLow: ?intervalLow, intervalHigh: ?intervalHigh }
76
+
77
+ VERDICT_REACHED {
78
+ status : string!
79
+ mean : float!
80
+ intervalLow : float!
81
+ intervalHigh : float!
82
+ }
83
+ @description "Any posterior verdict, tagged with which one it was."
84
+ @tier "secondary"
85
+ }
86
+ }
87
+
88
+ trait PosteriorTracker = Posterior.traits.PosteriorBelief -> KnowledgeState {
89
+ config {
90
+ priorAlpha: 1
91
+ priorBeta: 1
92
+ masteryFloor: 0.85
93
+ remediationCeiling: 0.35
94
+ intervalWidthMax: 0.25
95
+ sampleCount: 200
96
+ }
97
+
98
+ listens {
99
+ ObservationRelay.OBSERVATION_MADE -> OBSERVE
100
+ }
101
+ }
102
+
103
+ trait Labels = LabelCap.traits.LabelCaptureListener -> KnowledgeState {
104
+ config {
105
+ sink: "knowledge-tracing"
106
+ sampleRate: 1
107
+ }
108
+
109
+ listens {
110
+ ObservationRelay.CAPTURE_LABEL -> CAPTURE
111
+ }
112
+ }
113
+
114
+ page "/knowledge-tracing" as KnowledgeTracingPage -> ObservationRelay, PosteriorTracker, Labels
115
+ }
@@ -43,6 +43,7 @@ orbital AppLayoutOrbital {
43
43
  searchEvent: @config.searchEvent
44
44
  topBarActions: @config.topBarActions
45
45
  type: @config.layoutPattern
46
+ user: (if @config.viewerName { avatar: @config.viewerAvatar, email: @config.viewerEmail, name: @config.viewerName } null)
46
47
  }]
47
48
  })
48
49
  AppLayoutStateLoaded -> composing
@@ -94,6 +95,19 @@ orbital AppLayoutOrbital {
94
95
  @description "Name shown in the top-left of the chrome header."
95
96
  @synonyms "app title, product name, brand name, application name, site name"
96
97
  @tier "presentation"
98
+ viewerName : string = ""
99
+ @label "Who is signed in"
100
+ @description "Display name of the signed-in viewer, shown in the app bar's account menu — typically @user.name. Blank renders no account menu, so people can see which account (and which persona) they are viewing the app as only when it is actually known."
101
+ @synonyms "signed in user, current user, account, profile, persona, who am i"
102
+ @tier "presentation"
103
+ viewerEmail : string = ""
104
+ @label "Signed-in email"
105
+ @description "Email shown under the name in the account menu — typically @user.email."
106
+ @tier "presentation"
107
+ viewerAvatar : string = ""
108
+ @label "Signed-in avatar"
109
+ @description "Avatar image URL for the account menu."
110
+ @tier "presentation"
97
111
  navItems : [NavItem] = []
98
112
  @label "Which sections appear in the sidebar?"
99
113
  @description "Top-level navigation links shown on the left rail."
@@ -62,6 +62,17 @@ orbital BoardOrbital {
62
62
  title: "Failed to load board"
63
63
  type: error-state
64
64
  })
65
+ ;; Card move/delete and add-card save all land here from their own
66
+ ;; states; without this the persist success is dropped and the spinner
67
+ ;; never resolves (only the failure twin below used to recover).
68
+ BoardItemsSaved -> loading
69
+ (fetch BoardView {
70
+ emit: { failure: BoardItemsLoadFailed, success: BoardItemsLoaded }
71
+ })
72
+ (render-ui main {
73
+ title: "Refreshing board…"
74
+ type: loading-state
75
+ })
65
76
  BoardItemsSaveFailed -> error
66
77
  (set @entity.errorMessage ?error)
67
78
  (render-ui main {
@@ -81,10 +92,10 @@ orbital BoardOrbital {
81
92
  type: loading-state
82
93
  })
83
94
  OPEN_CARD -> viewing_card
84
- (set @entity.currentId ?row.id)
85
- (set @entity.currentTitle ?row.title)
86
- (set @entity.currentDescription ?row.description)
87
- (set @entity.currentStage ?row.stage)
95
+ (set @entity.currentId ?id)
96
+ (set @entity.currentTitle ?title)
97
+ (set @entity.currentDescription ?description)
98
+ (set @entity.currentStage ?stage)
88
99
  (set @entity.currentNotes ?row.notes)
89
100
  (render-ui main {
90
101
  children: [{
@@ -362,6 +373,9 @@ orbital BoardOrbital {
362
373
  OPEN_CARD {
363
374
  id : string!
364
375
  row : object!
376
+ title : string
377
+ description : string
378
+ stage : string
365
379
  }
366
380
  @description "A card within the board has been selected or activated."
367
381
  @synonyms "select, activate, view, show"
@@ -558,7 +572,7 @@ orbital BoardOrbital {
558
572
  align: center
559
573
  children: [{
560
574
  action: OPEN_CARD
561
- actionPayload: { id: @config.cardIdBinding, row: @item }
575
+ actionPayload: { description: @config.cardDescriptionBinding, id: @config.cardIdBinding, row: @item, stage: @config.cardStageBinding, title: @config.cardTitleBinding }
562
576
  icon: arrow-right
563
577
  label: Open
564
578
  type: button
@@ -670,7 +684,7 @@ orbital BoardOrbital {
670
684
  variant: primary
671
685
  }, {
672
686
  action: OPEN_CARD
673
- actionPayload: { id: @config.cardIdBinding, row: @item }
687
+ actionPayload: { description: @config.cardDescriptionBinding, id: @config.cardIdBinding, row: @item, stage: @config.cardStageBinding, title: @config.cardTitleBinding }
674
688
  className: ml-auto
675
689
  icon: arrow-right
676
690
  label: Open