Skip to content

Commit 411ce15

Browse files
committed
Migrate Arcling examples to 4-file Go reference models and update tests
1 parent a3a92d4 commit 411ce15

File tree

3 files changed

+69
-0
lines changed

3 files changed

+69
-0
lines changed

examples/arcling/delfour/delfour.model.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package main
22

3+
// Delfour is a reference Arcling model written as a small CLI program.
4+
// It reads delfour.data.json, derives the neutral shopping insight,
5+
// computes the canonical envelope/hash/HMAC values, and emits either
6+
// ARC text or a JSON result object.
7+
38
import (
49
"crypto/hmac"
510
"crypto/sha256"
@@ -13,6 +18,7 @@ import (
1318
"time"
1419
)
1520

21+
// Data mirrors the input instance shape from delfour.data.json.
1622
type Data struct {
1723
CaseName string `json:"caseName"`
1824
Retailer string `json:"retailer"`
@@ -77,6 +83,7 @@ type Integrity struct {
7783
VerificationMode string `json:"verificationMode"`
7884
}
7985

86+
// Insight is the minimized payload shared with the retailer.
8087
type Insight struct {
8188
CreatedAt string `json:"createdAt"`
8289
ExpiresAt string `json:"expiresAt"`
@@ -191,6 +198,7 @@ func readJSON(path string) (Data, error) {
191198
return data, err
192199
}
193200

201+
// validate performs the structural checks that used to live in JSON Schema.
194202
func validate(data Data) error {
195203
if err := must(data.CaseName != "", "caseName is required"); err != nil {
196204
return err
@@ -234,6 +242,7 @@ func validate(data Data) error {
234242
return nil
235243
}
236244

245+
// parseTime accepts RFC3339Nano timestamps from the case instance.
237246
func parseTime(s string) time.Time {
238247
t, err := time.Parse(time.RFC3339Nano, s)
239248
if err != nil {
@@ -242,6 +251,7 @@ func parseTime(s string) time.Time {
242251
return t
243252
}
244253

254+
// findProduct resolves the scanned or recommended product by its catalog id.
245255
func findProduct(data Data, id string) *Product {
246256
for i := range data.Catalog {
247257
if data.Catalog[i].ID == id {
@@ -251,6 +261,7 @@ func findProduct(data Data, id string) *Product {
251261
return nil
252262
}
253263

264+
// deriveInsight strips the household condition down to the neutral shopping insight.
254265
func deriveInsight(data Data) Insight {
255266
return Insight{
256267
CreatedAt: data.Timestamps.CreatedAt,
@@ -266,6 +277,7 @@ func deriveInsight(data Data) Insight {
266277
}
267278
}
268279

280+
// derivePolicy builds the companion ODRL-style policy used for governance checks.
269281
func derivePolicy(data Data) Policy {
270282
return Policy{
271283
Duty: Duty{
@@ -299,6 +311,8 @@ func derivePolicy(data Data) Policy {
299311
}
300312
}
301313

314+
// canonicalEnvelope returns the exact byte string used for the integrity vector.
315+
// The field order and the lexical form of threshold (10.0) are intentional.
302316
func canonicalEnvelope(insight Insight, policy Policy) string {
303317
return fmt.Sprintf(
304318
"{\"insight\":{\"createdAt\":\"%s\",\"expiresAt\":\"%s\",\"id\":\"%s\",\"metric\":\"%s\",\"retailer\":\"%s\",\"scopeDevice\":\"%s\",\"scopeEvent\":\"%s\",\"suggestionPolicy\":\"%s\",\"threshold\":10.0,\"type\":\"%s\"},\"policy\":{\"duty\":{\"action\":\"%s\",\"constraint\":{\"leftOperand\":\"%s\",\"operator\":\"%s\",\"rightOperand\":\"%s\"}},\"permission\":{\"action\":\"%s\",\"constraint\":{\"leftOperand\":\"%s\",\"operator\":\"%s\",\"rightOperand\":\"%s\"},\"target\":\"%s\"},\"profile\":\"%s\",\"prohibition\":{\"action\":\"%s\",\"constraint\":{\"leftOperand\":\"%s\",\"operator\":\"%s\",\"rightOperand\":\"%s\"},\"target\":\"%s\"},\"type\":\"%s\"}}",
@@ -348,6 +362,8 @@ func yesNo(v bool) string {
348362
return "no"
349363
}
350364

365+
// evaluate runs the full Arcling pipeline: derive facts, select the recommendation,
366+
// build the envelope, verify integrity values, and render the final report.
351367
func evaluate(data Data) (Result, error) {
352368
var result Result
353369
if err := validate(data); err != nil {
@@ -508,6 +524,8 @@ func evaluate(data Data) (Result, error) {
508524
return result, nil
509525
}
510526

527+
// main is a tiny CLI wrapper around evaluate. It defaults to delfour.data.json,
528+
// prints ARC text, and switches to JSON output when --json is supplied.
511529
func main() {
512530
inputPath := "delfour.data.json"
513531
jsonMode := false

examples/arcling/flandor/flandor.model.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package main
22

3+
// Flandor is a reference Arcling model for the regional retooling-pulse case.
4+
// It evaluates whether a region needs intervention, selects the lowest-cost
5+
// eligible package, and emits ARC text or a JSON report.
6+
37
import (
48
"crypto/hmac"
59
"crypto/sha256"
@@ -13,6 +17,7 @@ import (
1317
"time"
1418
)
1519

20+
// Data mirrors the input instance shape from flandor.data.json.
1621
type Data struct {
1722
CaseName string `json:"caseName"`
1823
Region string `json:"region"`
@@ -233,6 +238,8 @@ func parseTime(value string) time.Time {
233238
return t
234239
}
235240

241+
// stableStringify recursively sorts map keys so the canonical envelope is
242+
// deterministic across runs and implementations.
236243
func stableStringify(value any) string {
237244
switch v := value.(type) {
238245
case nil:
@@ -260,13 +267,16 @@ func stableStringify(value any) string {
260267
}
261268
}
262269

270+
// canonicalValue converts typed structs into generic JSON-like values before
271+
// stable stringification.
263272
func canonicalValue(value any) any {
264273
b, _ := json.Marshal(value)
265274
var out any
266275
_ = json.Unmarshal(b, &out)
267276
return out
268277
}
269278

279+
// validateInstance performs the structural checks for the 4-file bundle.
270280
func validateInstance(data Data) error {
271281
if err := assertTrue(data.CaseName != "", "caseName is required"); err != nil {
272282
return err
@@ -283,6 +293,7 @@ func validateInstance(data Data) error {
283293
return nil
284294
}
285295

296+
// countTrue is used for the active-need threshold logic in the spec.
286297
func countTrue(values ...bool) int {
287298
total := 0
288299
for _, value := range values {
@@ -293,6 +304,7 @@ func countTrue(values ...bool) int {
293304
return total
294305
}
295306

307+
// The R* helpers map directly to named derivation clauses in flandor.spec.md.
296308
func clauseR1ExportWeakness(data Data) bool {
297309
for _, cluster := range data.Signals.Clusters {
298310
if cluster.ExportOrdersIndex < data.Thresholds.ExportOrdersIndexBelow {
@@ -318,6 +330,7 @@ func clauseR5NeedsRetoolingPulse(data Data, activeNeedCount int) bool {
318330
return activeNeedCount >= data.Thresholds.ActiveNeedCountAtLeast
319331
}
320332

333+
// deriveInsight produces the minimized regional signal shared with the recipient.
321334
func deriveInsight(data Data) Insight {
322335
return Insight{
323336
CreatedAt: data.Timestamps.CreatedAt,
@@ -333,6 +346,7 @@ func deriveInsight(data Data) Insight {
333346
}
334347
}
335348

349+
// derivePolicy constructs the usage restrictions paired with the insight.
336350
func derivePolicy(data Data) Policy {
337351
return Policy{
338352
Duty: Duty{
@@ -366,12 +380,16 @@ func derivePolicy(data Data) Policy {
366380
}
367381
}
368382

383+
// packageCoversAllActiveNeeds checks whether a candidate package addresses every
384+
// need that is active for this specific instance.
369385
func packageCoversAllActiveNeeds(pkg Package, exportWeakness, skillsStrain, gridStress bool) bool {
370386
return (!exportWeakness || pkg.CoversExportWeakness) &&
371387
(!skillsStrain || pkg.CoversSkillsStrain) &&
372388
(!gridStress || pkg.CoversGridStress)
373389
}
374390

391+
// clauseS1EligiblePackages filters to packages that both fit the budget and
392+
// cover the active needs.
375393
func clauseS1EligiblePackages(data Data, exportWeakness, skillsStrain, gridStress bool) []Package {
376394
eligible := make([]Package, 0)
377395
for _, pkg := range data.Packages {
@@ -383,6 +401,8 @@ func clauseS1EligiblePackages(data Data, exportWeakness, skillsStrain, gridStres
383401
return eligible
384402
}
385403

404+
// clauseS2RecommendedPackage applies the tie-breaker: choose the lowest-cost
405+
// eligible package after sorting by cost.
386406
func clauseS2RecommendedPackage(data Data, exportWeakness, skillsStrain, gridStress bool) ([]Package, *Package) {
387407
eligible := clauseS1EligiblePackages(data, exportWeakness, skillsStrain, gridStress)
388408
if len(eligible) == 0 {
@@ -404,6 +424,8 @@ func clauseG3DutyTimely(data Data) bool {
404424
return !parseTime(data.Timestamps.DutyPerformedAt).After(parseTime(data.Timestamps.ExpiresAt))
405425
}
406426

427+
// clauseM1CanonicalEnvelope returns both the structured envelope and the
428+
// deterministic string hashed/signed by the integrity clauses.
407429
func clauseM1CanonicalEnvelope(data Data) (Envelope, string) {
408430
envelope := Envelope{Insight: deriveInsight(data), Policy: derivePolicy(data)}
409431
return envelope, stableStringify(canonicalValue(envelope))
@@ -437,6 +459,8 @@ func yesNo(value bool) string {
437459
return "FAIL"
438460
}
439461

462+
// evaluate computes all derived facts, governance checks, integrity values,
463+
// and presentation fields expected by flandor.expected.json.
440464
func evaluate(data Data) (Result, error) {
441465
if err := validateInstance(data); err != nil {
442466
return Result{}, err
@@ -593,6 +617,7 @@ func derefIntString(value *int) string {
593617
return fmt.Sprintf("%d", *value)
594618
}
595619

620+
// main is the CLI entry point used by the Arcling test runner.
596621
func main() {
597622
inputPath := "flandor.data.json"
598623
jsonMode := false

examples/arcling/medior/medior.model.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package main
22

3+
// Medior is a reference Arcling model for the care-continuity bundle case.
4+
// It derives active needs from coarse signals, selects the lowest-cost eligible
5+
// package, and emits ARC text or a JSON report.
6+
37
import (
48
"crypto/hmac"
59
"crypto/sha256"
@@ -13,6 +17,7 @@ import (
1317
"time"
1418
)
1519

20+
// Data mirrors the input instance shape from medior.data.json.
1621
type Data struct {
1722
CaseName string `json:"caseName"`
1823
Region string `json:"region"`
@@ -234,6 +239,8 @@ func parseTime(value string) time.Time {
234239
return t
235240
}
236241

242+
// stableStringify recursively sorts map keys so the canonical envelope stays
243+
// byte-stable across runs and languages.
237244
func stableStringify(value any) string {
238245
switch v := value.(type) {
239246
case nil:
@@ -261,13 +268,16 @@ func stableStringify(value any) string {
261268
}
262269
}
263270

271+
// canonicalValue converts typed structs into generic JSON-like values before
272+
// stable stringification.
264273
func canonicalValue(value any) any {
265274
b, _ := json.Marshal(value)
266275
var out any
267276
_ = json.Unmarshal(b, &out)
268277
return out
269278
}
270279

280+
// validateInstance performs the structural checks for the 4-file bundle.
271281
func validateInstance(data Data) error {
272282
if err := assertTrue(data.CaseName != "", "caseName is required"); err != nil {
273283
return err
@@ -281,6 +291,7 @@ func validateInstance(data Data) error {
281291
return nil
282292
}
283293

294+
// countTrue is used for the active-need threshold logic in the spec.
284295
func countTrue(values ...bool) int {
285296
total := 0
286297
for _, value := range values {
@@ -291,6 +302,7 @@ func countTrue(values ...bool) int {
291302
return total
292303
}
293304

305+
// The R* helpers map directly to named derivation clauses in medior.spec.md.
294306
func clauseR1RenalSafetyConcern(data Data) bool {
295307
return data.Signals.Lab.Egfr < data.Thresholds.EgfrBelow
296308
}
@@ -315,6 +327,8 @@ func clauseR6NeedsContinuityBundle(data Data, activeNeedCount int) bool {
315327
return activeNeedCount >= data.Thresholds.ActiveNeedCountAtLeast
316328
}
317329

330+
// deriveInsight produces the minimized care-coordination signal shared with
331+
// the recipient.
318332
func deriveInsight(data Data) Insight {
319333
return Insight{
320334
CreatedAt: data.Timestamps.CreatedAt,
@@ -330,6 +344,7 @@ func deriveInsight(data Data) Insight {
330344
}
331345
}
332346

347+
// derivePolicy constructs the usage restrictions paired with the insight.
333348
func derivePolicy(data Data) Policy {
334349
return Policy{
335350
Duty: Duty{
@@ -363,13 +378,17 @@ func derivePolicy(data Data) Policy {
363378
}
364379
}
365380

381+
// packageCoversAllActiveNeeds checks whether a candidate package addresses every
382+
// active need in this instance.
366383
func packageCoversAllActiveNeeds(pkg Package, renalSafetyConcern, polypharmacyRisk, readmissionHistory, recentDischargeWindow bool) bool {
367384
return (!renalSafetyConcern || pkg.CoversRenalSafetyConcern) &&
368385
(!polypharmacyRisk || pkg.CoversPolypharmacyRisk) &&
369386
(!readmissionHistory || pkg.CoversReadmissionHistory) &&
370387
(!recentDischargeWindow || pkg.CoversRecentDischargeWindow)
371388
}
372389

390+
// clauseS1EligiblePackages filters to packages that both fit the budget and
391+
// cover the active needs.
373392
func clauseS1EligiblePackages(data Data, renalSafetyConcern, polypharmacyRisk, readmissionHistory, recentDischargeWindow bool) []Package {
374393
eligible := make([]Package, 0)
375394
for _, pkg := range data.Packages {
@@ -381,6 +400,8 @@ func clauseS1EligiblePackages(data Data, renalSafetyConcern, polypharmacyRisk, r
381400
return eligible
382401
}
383402

403+
// clauseS2RecommendedPackage applies the tie-breaker: choose the lowest-cost
404+
// eligible package after sorting by cost.
384405
func clauseS2RecommendedPackage(data Data, renalSafetyConcern, polypharmacyRisk, readmissionHistory, recentDischargeWindow bool) ([]Package, *Package) {
385406
eligible := clauseS1EligiblePackages(data, renalSafetyConcern, polypharmacyRisk, readmissionHistory, recentDischargeWindow)
386407
if len(eligible) == 0 {
@@ -402,6 +423,8 @@ func clauseG3DutyTimely(data Data) bool {
402423
return !parseTime(data.Timestamps.DutyPerformedAt).After(parseTime(data.Timestamps.ExpiresAt))
403424
}
404425

426+
// clauseM1CanonicalEnvelope returns both the structured envelope and the
427+
// deterministic string hashed/signed by the integrity clauses.
405428
func clauseM1CanonicalEnvelope(data Data) (Envelope, string) {
406429
envelope := Envelope{Insight: deriveInsight(data), Policy: derivePolicy(data)}
407430
return envelope, stableStringify(canonicalValue(envelope))
@@ -435,6 +458,8 @@ func yesNo(value bool) string {
435458
return "FAIL"
436459
}
437460

461+
// evaluate computes all derived facts, governance checks, integrity values,
462+
// and presentation fields expected by medior.expected.json.
438463
func evaluate(data Data) (Result, error) {
439464
if err := validateInstance(data); err != nil {
440465
return Result{}, err
@@ -589,6 +614,7 @@ func derefIntString(value *int) string {
589614
return fmt.Sprintf("%d", *value)
590615
}
591616

617+
// main is the CLI entry point used by the Arcling test runner.
592618
func main() {
593619
inputPath := "medior.data.json"
594620
jsonMode := false

0 commit comments

Comments
 (0)