@almadar/std 16.143.0 → 16.145.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 (97) 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 +154 -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-board.lolo +28 -6
  11. package/behaviors/lolo/ui/core/atoms/std-browse.lolo +23 -6
  12. package/behaviors/lolo/ui/core/atoms/std-calendar.lolo +85 -2
  13. package/behaviors/lolo/ui/core/atoms/std-file-store.lolo +17 -0
  14. package/behaviors/lolo/ui/core/atoms/std-version-history.lolo +17 -0
  15. package/behaviors/lolo/ui/core/atoms/ui-typography.lolo +1 -1
  16. package/behaviors/lolo/ui/core/molecules/ui-calendar-grid.lolo +18 -0
  17. package/behaviors/lolo/ui/core/molecules/ui-data-grid.lolo +2 -0
  18. package/behaviors/lolo/ui/core/molecules/ui-data-list.lolo +3 -2
  19. package/behaviors/lolo/ui/core/molecules/ui-filter-group.lolo +20 -0
  20. package/behaviors/lolo/ui/core/molecules/ui-search-input.lolo +7 -2
  21. package/behaviors/lolo/ui/core/molecules/ui-table-view.lolo +3 -2
  22. package/behaviors/lolo/ui/core/organisms/ui-entity-cards.lolo +2 -0
  23. package/behaviors/lolo/ui/core/organisms/ui-entity-list.lolo +2 -0
  24. package/behaviors/lolo/ui/core/organisms/ui-entity-table.lolo +2 -0
  25. package/behaviors/lolo/ui/core/organisms/ui-timeline.lolo +2 -0
  26. package/behaviors/registry/factory-signatures.json +5018 -824
  27. package/behaviors/registry/ml/atoms/std-ml-classify.orb +622 -0
  28. package/behaviors/registry/ml/atoms/std-ml-exact-check.orb +505 -0
  29. package/behaviors/registry/ml/atoms/std-ml-infer.orb +770 -0
  30. package/behaviors/registry/ml/atoms/std-ml-label-capture.orb +367 -0
  31. package/behaviors/registry/ml/atoms/std-ml-lookup.orb +473 -0
  32. package/behaviors/registry/ml/atoms/std-ml-posterior.orb +669 -0
  33. package/behaviors/registry/ml/atoms/std-ml-similarity.orb +862 -0
  34. package/behaviors/registry/ml/molecules/std-escalating-decision.orb +1553 -0
  35. package/behaviors/registry/ml/molecules/std-knowledge-tracing.orb +577 -0
  36. package/behaviors/registry/ui/core/atoms/std-board.orb +90 -20
  37. package/behaviors/registry/ui/core/atoms/std-browse.orb +83 -48
  38. package/behaviors/registry/ui/core/atoms/std-calendar.orb +238 -3
  39. package/behaviors/registry/ui/core/atoms/std-file-store.orb +40 -0
  40. package/behaviors/registry/ui/core/atoms/std-version-history.orb +40 -0
  41. package/behaviors/registry/ui/core/atoms/ui-typography.orb +1 -1
  42. package/behaviors/registry/ui/core/molecules/ui-calendar-grid.orb +27 -0
  43. package/behaviors/registry/ui/core/molecules/ui-data-grid.orb +25 -12
  44. package/behaviors/registry/ui/core/molecules/ui-data-list.orb +26 -13
  45. package/behaviors/registry/ui/core/molecules/ui-filter-group.orb +86 -0
  46. package/behaviors/registry/ui/core/molecules/ui-search-input.orb +37 -1
  47. package/behaviors/registry/ui/core/molecules/ui-table-view.orb +25 -12
  48. package/behaviors/registry/ui/core/organisms/ui-entity-cards.orb +13 -0
  49. package/behaviors/registry/ui/core/organisms/ui-entity-list.orb +21 -8
  50. package/behaviors/registry/ui/core/organisms/ui-entity-table.orb +13 -0
  51. package/behaviors/registry/ui/core/organisms/ui-timeline.orb +13 -0
  52. package/canonical-operators.json +30 -6
  53. package/dist/behaviors/behaviors-embeddings.json +134 -134
  54. package/dist/behaviors/exports-reader.js +2462 -2080
  55. package/dist/behaviors/functions/index.d.ts +725 -1
  56. package/dist/behaviors/functions/index.js +2437 -2081
  57. package/dist/behaviors/index.d.ts +1 -1
  58. package/dist/behaviors/index.js +2463 -2081
  59. package/dist/behaviors/knob-embeddings.json +1 -1
  60. package/dist/behaviors/query.js +2462 -2080
  61. package/dist/behaviors/registry/factory-signatures.json +5018 -824
  62. package/dist/behaviors/registry/ml/atoms/std-ml-classify.orb +622 -0
  63. package/dist/behaviors/registry/ml/atoms/std-ml-exact-check.orb +505 -0
  64. package/dist/behaviors/registry/ml/atoms/std-ml-infer.orb +770 -0
  65. package/dist/behaviors/registry/ml/atoms/std-ml-label-capture.orb +367 -0
  66. package/dist/behaviors/registry/ml/atoms/std-ml-lookup.orb +473 -0
  67. package/dist/behaviors/registry/ml/atoms/std-ml-posterior.orb +669 -0
  68. package/dist/behaviors/registry/ml/atoms/std-ml-similarity.orb +862 -0
  69. package/dist/behaviors/registry/ml/molecules/std-escalating-decision.orb +1553 -0
  70. package/dist/behaviors/registry/ml/molecules/std-knowledge-tracing.orb +577 -0
  71. package/dist/behaviors/registry/ui/core/atoms/std-board.orb +90 -20
  72. package/dist/behaviors/registry/ui/core/atoms/std-browse.orb +83 -48
  73. package/dist/behaviors/registry/ui/core/atoms/std-calendar.orb +238 -3
  74. package/dist/behaviors/registry/ui/core/atoms/std-file-store.orb +40 -0
  75. package/dist/behaviors/registry/ui/core/atoms/std-version-history.orb +40 -0
  76. package/dist/behaviors/registry/ui/core/atoms/ui-typography.orb +1 -1
  77. package/dist/behaviors/registry/ui/core/molecules/ui-calendar-grid.orb +27 -0
  78. package/dist/behaviors/registry/ui/core/molecules/ui-data-grid.orb +25 -12
  79. package/dist/behaviors/registry/ui/core/molecules/ui-data-list.orb +26 -13
  80. package/dist/behaviors/registry/ui/core/molecules/ui-filter-group.orb +86 -0
  81. package/dist/behaviors/registry/ui/core/molecules/ui-search-input.orb +37 -1
  82. package/dist/behaviors/registry/ui/core/molecules/ui-table-view.orb +25 -12
  83. package/dist/behaviors/registry/ui/core/organisms/ui-entity-cards.orb +13 -0
  84. package/dist/behaviors/registry/ui/core/organisms/ui-entity-list.orb +21 -8
  85. package/dist/behaviors/registry/ui/core/organisms/ui-entity-table.orb +13 -0
  86. package/dist/behaviors/registry/ui/core/organisms/ui-timeline.orb +13 -0
  87. package/dist/behaviors-embeddings.json +134 -134
  88. package/dist/index.d.ts +1 -1
  89. package/dist/index.js +2498 -2088
  90. package/dist/knob-embeddings.json +1 -1
  91. package/dist/modules/array.js +28 -0
  92. package/dist/modules/contract.js +4 -4
  93. package/dist/modules/data.js +3 -3
  94. package/dist/modules/index.js +35 -7
  95. package/dist/registry/factory-signatures.json +5018 -824
  96. package/dist/registry.js +35 -7
  97. package/package.json +6 -5
@@ -0,0 +1,154 @@
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.queryVector (array/first @entity.embeddings))
78
+ (set @entity.candidateVectors (array/drop @entity.embeddings 1))
79
+ (set @entity.status resolved)
80
+ (let ((nearest (array/nearest @entity.queryVector @entity.candidateVectors)))
81
+ (do
82
+ (set @entity.matchIndex (object/get @nearest "index"))
83
+ (set @entity.matchScore (object/get @nearest "score"))
84
+ (set @entity.matchMargin (object/get @nearest "margin"))
85
+ (if (and (>= @entity.matchScore @config.floor) (>= @entity.matchMargin @config.margin))
86
+ (do
87
+ (set @entity.reason "")
88
+ (emit SIMILARITY_MATCHED { index: @entity.matchIndex, score: @entity.matchScore }))
89
+ (do
90
+ (set @entity.reason (if (< @entity.matchScore @config.floor) "below floor" "thin margin"))
91
+ (emit SIMILARITY_ABSTAINED { reason: @entity.reason, request: @entity.request })))))
92
+
93
+ EMBED_FAILED -> resolved
94
+ (set @entity.embeddings [])
95
+ (set @entity.queryVector [])
96
+ (set @entity.candidateVectors [])
97
+ (set @entity.matchIndex 0)
98
+ (set @entity.matchScore 0)
99
+ (set @entity.matchMargin 0)
100
+ (set @entity.reason "embedding unavailable")
101
+ (set @entity.status resolved)
102
+ (emit SIMILARITY_ABSTAINED { reason: "embedding unavailable", request: @entity.request })
103
+ }
104
+
105
+ state resolved {
106
+ INIT -> resolved
107
+ (set @entity.status resolved)
108
+ RESET -> idle
109
+ (set @entity.query "")
110
+ (set @entity.candidates [])
111
+ (set @entity.embeddings [])
112
+ (set @entity.queryVector [])
113
+ (set @entity.candidateVectors [])
114
+ (set @entity.matchIndex 0)
115
+ (set @entity.matchScore 0)
116
+ (set @entity.matchMargin 0)
117
+ (set @entity.reason "")
118
+ (set @entity.request {})
119
+ (set @entity.status idle)
120
+ }
121
+
122
+ emits {
123
+ EMBEDDED {
124
+ result : [[float]]!
125
+ }
126
+ @description "Raw embeddings from llm/embed — query vector at index 0, candidate vectors following in input order"
127
+ @tier "secondary"
128
+
129
+ SIMILARITY_MATCHED -> external {
130
+ index : int!
131
+ score : float!
132
+ }
133
+ @description "Candidate matched with high confidence — score at or above floor, with a clear margin over the runner-up"
134
+ @tier "primary"
135
+
136
+ SIMILARITY_ABSTAINED -> external {
137
+ reason : string!
138
+ request : object
139
+ }
140
+ @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"
141
+ @tier "primary"
142
+ }
143
+
144
+ listens {
145
+ COMPARE {
146
+ query : string!
147
+ candidates : [string]!
148
+ request : object
149
+ }
150
+ }
151
+ }
152
+
153
+ page "/ml-similarity" as MlSimilarityPage -> SimilarityRun
154
+ }
@@ -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
+ }
@@ -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 {
@@ -72,11 +83,19 @@ orbital BoardOrbital {
72
83
  }
73
84
 
74
85
  state viewing_board {
86
+ INIT -> loading
87
+ (fetch BoardView {
88
+ emit: { failure: BoardItemsLoadFailed, success: BoardItemsLoaded }
89
+ })
90
+ (render-ui main {
91
+ title: "Loading board…"
92
+ type: loading-state
93
+ })
75
94
  OPEN_CARD -> viewing_card
76
- (set @entity.currentId ?row.id)
77
- (set @entity.currentTitle ?row.title)
78
- (set @entity.currentDescription ?row.description)
79
- (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)
80
99
  (set @entity.currentNotes ?row.notes)
81
100
  (render-ui main {
82
101
  children: [{
@@ -354,6 +373,9 @@ orbital BoardOrbital {
354
373
  OPEN_CARD {
355
374
  id : string!
356
375
  row : object!
376
+ title : string
377
+ description : string
378
+ stage : string
357
379
  }
358
380
  @description "A card within the board has been selected or activated."
359
381
  @synonyms "select, activate, view, show"
@@ -550,7 +572,7 @@ orbital BoardOrbital {
550
572
  align: center
551
573
  children: [{
552
574
  action: OPEN_CARD
553
- actionPayload: { id: @config.cardIdBinding, row: @item }
575
+ actionPayload: { description: @config.cardDescriptionBinding, id: @config.cardIdBinding, row: @item, stage: @config.cardStageBinding, title: @config.cardTitleBinding }
554
576
  icon: arrow-right
555
577
  label: Open
556
578
  type: button
@@ -662,7 +684,7 @@ orbital BoardOrbital {
662
684
  variant: primary
663
685
  }, {
664
686
  action: OPEN_CARD
665
- actionPayload: { id: @config.cardIdBinding, row: @item }
687
+ actionPayload: { description: @config.cardDescriptionBinding, id: @config.cardIdBinding, row: @item, stage: @config.cardStageBinding, title: @config.cardTitleBinding }
666
688
  className: ml-auto
667
689
  icon: arrow-right
668
690
  label: Open
@@ -305,6 +305,11 @@ orbital BrowseItemOrbital {
305
305
  @description "Actions beyond this count collapse into a '⋯' overflow menu. Set 2–3 for dense tables."
306
306
  @synonyms "inline actions limit, overflow threshold, max buttons per row"
307
307
  @tier "presentation"
308
+ bodySearch : boolean = true
309
+ @label "Show the list's own search box?"
310
+ @description "The dense/feed/gallery looks embed their own search box above the rows. Set false when the page already provides a search affordance (a toolbar Search molecule), so the surface shows one search box instead of two."
311
+ @synonyms "inline search, list search, built-in search, show search, duplicate search"
312
+ @tier "presentation"
308
313
  searchPlaceholder : string = "Search…"
309
314
  @label "Search box placeholder text"
310
315
  @description "Hint text inside the list search box (e.g. 'Search orders…')."
@@ -327,7 +332,7 @@ orbital BrowseItemOrbital {
327
332
  @description "Render-ui SExpr rendered after rows load when browseLook = table (the default). Composes the canonical data-grid call wrapped in a stack. See denseBodyContent/feedBodyContent/galleryBodyContent for the other browseLook bodies; any may be overridden directly while inheriting the same trait, state machine, emits, and listens."
328
333
  @tier "internal"
329
334
  denseBodyContent : render-ui = {
330
- children: [{
335
+ children: [(if @config.bodySearch {
331
336
  align: center
332
337
  children: [{
333
338
  className: "w-full max-w-2xl"
@@ -340,7 +345,11 @@ orbital BrowseItemOrbital {
340
345
  direction: horizontal
341
346
  gap: sm
342
347
  type: stack
343
- }, {
348
+ } {
349
+ children: []
350
+ gap: none
351
+ type: stack
352
+ }), {
344
353
  children: [@trait.DenseTableView]
345
354
  className: "rounded-lg border border-[var(--color-border)] overflow-x-auto bg-[var(--color-card)] shadow-sm"
346
355
  direction: vertical
@@ -356,7 +365,7 @@ orbital BrowseItemOrbital {
356
365
  @description "Render-ui SExpr rendered after rows load when browseLook = dense. Spreadsheet-style sticky-header data-table with a search bar; composes columns/itemActions/maxInlineActions/displayPageSize/searchPlaceholder. Folded from std-browse-dense (Phase 5.B)."
357
366
  @tier "internal"
358
367
  feedBodyContent : render-ui = {
359
- children: [{
368
+ children: [(if @config.bodySearch {
360
369
  align: center
361
370
  children: [{
362
371
  className: w-full
@@ -369,7 +378,11 @@ orbital BrowseItemOrbital {
369
378
  direction: horizontal
370
379
  gap: sm
371
380
  type: stack
372
- }, {
381
+ } {
382
+ children: []
383
+ gap: none
384
+ type: stack
385
+ }), {
373
386
  entity: ?data
374
387
  fields: []
375
388
  gap: md
@@ -451,7 +464,7 @@ orbital BrowseItemOrbital {
451
464
  @description "Render-ui SExpr rendered after rows load when browseLook = feed. Chronological card-per-item stream with avatar, title, meta, body, timestamp; composes titleBinding/descriptionBinding/categoryBinding/badgeBinding/metaBinding/searchPlaceholder/itemActions. Folded from std-browse-feed (Phase 5.B)."
452
465
  @tier "internal"
453
466
  galleryBodyContent : render-ui = {
454
- children: [{
467
+ children: [(if @config.bodySearch {
455
468
  align: center
456
469
  children: [{
457
470
  className: w-full
@@ -464,7 +477,11 @@ orbital BrowseItemOrbital {
464
477
  direction: horizontal
465
478
  gap: sm
466
479
  type: stack
467
- }, {
480
+ } {
481
+ children: []
482
+ gap: none
483
+ type: stack
484
+ }), {
468
485
  className: w-full
469
486
  entity: ?data
470
487
  fields: []