diff --git a/adapters/go/adapter.go b/adapters/go/adapter.go new file mode 100644 index 0000000..0ed5a75 --- /dev/null +++ b/adapters/go/adapter.go @@ -0,0 +1,53 @@ +// adapter.go — LongHun Standard Adapter for Go +package lhstandard + +import ( + "fmt" + "time" +) + +type Adapter struct { + UID, Device, Locale string + dnaGen *DNAGenerator + audit *AuditWrapper + validator *Validator +} + +func NewAdapter(uid, device, locale string) *Adapter { + if uid == "" { uid = "9622" } + if device == "" { device = "HM-9622-001" } + if locale == "" { locale = "Asia/Shanghai" } + return &Adapter{ + UID:uid, Device:device, Locale:locale, + dnaGen:NewDNAGenerator(uid,device,locale), + audit:NewAuditWrapper(uid), + validator:NewValidator(), + } +} + +func (a *Adapter) Wrap(data interface{}, taskType, persona, action, version string) map[string]interface{} { + if taskType == "" { taskType = "default" } + if persona == "" { persona = "P04" } + if action == "" { action = "WRAP" } + if version == "" { version = "V1.0" } else { _ = version } + dna := a.dnaGen.Generate(taskType, action, version) + audit := a.audit.Wrap(data, taskType, persona) + return map[string]interface{}{ + "dna":dna,"audit":audit,"payload":data,"meta":map[string]interface{}{ + "adapter_version":"1.0.0","uid":a.UID,"device":a.Device, + "task_type":taskType,"persona":persona, + "generated_at":time.Now().UTC().Format(time.RFC3339),"format":"longhun-v∞", + }, + } +} + +func (a *Adapter) Validate(wrapped map[string]interface{}) ValidationResult { + return a.validator.Validate(wrapped) +} + +func (a *Adapter) GetSchemas() (map[string]interface{}, map[string]interface{}) { + dnaSchema := map[string]interface{}{"type":"string","description":"v∞ DNA traceability code"} + auditSchema := map[string]interface{}{"type":"object","required":requiredAudit} + return dnaSchema, auditSchema +} +var _ = fmt.Sprintf diff --git a/adapters/go/adapter_test.go b/adapters/go/adapter_test.go new file mode 100644 index 0000000..2df5993 --- /dev/null +++ b/adapters/go/adapter_test.go @@ -0,0 +1,136 @@ +package lhstandard + +import "testing" + +func assert(t *testing.T, cond bool, msg string) { if !cond { t.Error(msg) } } + +func TestDNAGeneratorInstance(t *testing.T) { + g := NewDNAGenerator("","","") + if g.UID != "9622" { t.Error("default uid") } + if g.Device != "HM-9622-001" { t.Error("default device") } +} + +func TestDNAGeneratorCustom(t *testing.T) { + g := NewDNAGenerator("9999","T1","") + if g.UID != "9999" || g.Device != "T1" { t.Error("custom") } +} + +func TestGeneratePrefix(t *testing.T) { + g := NewDNAGenerator("","","") + dna := g.Generate("","","") + if len(dna) == 0 || dna[:10] != "#LongHun⚡️" { t.Error("prefix") } +} + +func TestGenerateDnaUnique(t *testing.T) { + g := NewDNAGenerator("","","") + seen := make(map[string]bool) + for i := 0; i < 10; i++ { + dna := g.Generate("code","GEN",fmt.Sprintf("V%d",i)) + if seen[dna] { t.Error("duplicate dna") } + seen[dna] = true + } +} + +func TestAuditWrapperInstance(t *testing.T) { + w := NewAuditWrapper("") + if w.UID != "9622" { t.Error("default uid") } +} + +func TestAuditWrap(t *testing.T) { + w := NewAuditWrapper("") + r := w.Wrap(map[string]interface{}{"x":1},"","") + if r["audit_version"] != "v1.0" { t.Error("version") } + if r["uid"] != "UID9622" { t.Error("uid") } +} + +func TestAuditSignature(t *testing.T) { + w := NewAuditWrapper("") + r := w.Wrap(map[string]interface{}{},"","") + sig := r["behavior_signature"].(map[string]interface{}) + for _, k := range []string{"P","F","T","E","C","R","A","X","Y","Z"} { + if _, ok := sig[k]; !ok { t.Error("missing sig key:",k) } + } +} + +func TestAuditPattern(t *testing.T) { + w := NewAuditWrapper("") + r := w.Wrap(map[string]interface{}{},"","") + if r["behavior_pattern"] != "MODE-StableDisciplined" { t.Error("pattern") } +} + +func TestValidatorNil(t *testing.T) { + v := NewValidator() + r := v.Validate(nil) + if r.Valid { t.Error("nil should be invalid") } +} + +func TestValidatorValid(t *testing.T) { + a := NewAdapter("","","") + w := a.Wrap(map[string]interface{}{"h":"w"},"","","","") + v := NewValidator() + r := v.Validate(w) + if !r.Valid { t.Error("should be valid:", r.Errors) } +} + +func TestValidatorMissingDNA(t *testing.T) { + v := NewValidator() + r := v.Validate(map[string]interface{}{"audit":map[string]interface{}{},"payload":map[string]interface{}{},"meta":map[string]interface{}{}}) + if r.Valid { t.Error("missing dna should be invalid") } +} + +func TestQuickValidate(t *testing.T) { + a := NewAdapter("","","") + w := a.Wrap(map[string]interface{}{},"","","","") + if !QuickValidate(w) { t.Error("quick should pass") } + if QuickValidate(nil) { t.Error("nil should fail") } +} + +func TestAdapterInstance(t *testing.T) { + a := NewAdapter("","","") + if a.UID != "9622" || a.Device != "HM-9622-001" { t.Error("defaults") } +} + +func TestAdapterWrap(t *testing.T) { + a := NewAdapter("","","") + r := a.Wrap(map[string]interface{}{"code":"x"},"","","","") + for _, k := range []string{"dna","audit","payload","meta"} { + if _, ok := r[k]; !ok { t.Error("missing key:",k) } + } +} + +func TestAdapterValidate(t *testing.T) { + a := NewAdapter("","","") + w := a.Wrap(map[string]interface{}{},"","","","") + r := a.Validate(w) + if !r.Valid { t.Error("self-validate fail:", r.Errors) } +} + +func TestGetSchemas(t *testing.T) { + a := NewAdapter("","","") + d, au := a.GetSchemas() + if d == nil || au == nil { t.Error("schemas should not be nil") } +} + +func TestUIDConsistency(t *testing.T) { + a := NewAdapter("8888","","") + w := a.Wrap(map[string]interface{}{},"","","","") + meta := w["meta"].(map[string]interface{}) + audit := w["audit"].(map[string]interface{}) + if meta["uid"] != "8888" { t.Error("meta uid") } + if audit["uid"] != "UID8888" { t.Error("audit uid") } +} + +func TestClassifyDD(t *testing.T) { + sig := map[string]interface{}{"F":"Unfulfilled","X":"OverExplain"} + if classify(sig) != "MODE-DefensiveDefaulter" { t.Error("dd") } +} + +func TestClassifyID(t *testing.T) { + sig := map[string]interface{}{"F":"Unfulfilled","Y":"Indifferent"} + if classify(sig) != "MODE-InternalDestroyer" { t.Error("id") } +} + +func TestClassifyFluc(t *testing.T) { + sig := map[string]interface{}{"Z":3.0} + if classify(sig) != "MODE-Fluctuating" { t.Error("fluc") } +} diff --git a/adapters/go/audit_wrapper.go b/adapters/go/audit_wrapper.go new file mode 100644 index 0000000..3cd0e23 --- /dev/null +++ b/adapters/go/audit_wrapper.go @@ -0,0 +1,72 @@ +// audit_wrapper.go — seven-factor behavioral audit metadata +package lhstandard + +import ( + "crypto/sha256" + "encoding/json" + "fmt" + "sort" + "time" +) + +var labelMap = map[string]map[string]string{ + "P": {"HasPromise":"7F-P-有承诺","NoPromise":"7F-P-无承诺"}, + "F": {"Fulfilled":"7F-F-已兑现","Unfulfilled":"7F-F-未兑现","Partial":"7F-F-部分兑现"}, + "E": {"Willing":"7F-E-心甘情愿","Perfunctory":"7F-E-敷衍","Resentful":"7F-E-怨恨","Numb":"7F-E-麻木"}, + "A": {"Self":"7F-A-自己","Partner":"7F-A-伴侣","Family":"7F-A-家庭","Outsider":"7F-A-外人","Public":"7F-A-公众"}, + "X": {"OverExplain":"7F-X-过度解释","Silent":"7F-X-沉默","Genuine":"7F-X-真诚","Indifferent":"7F-X-冷漠"}, + "Y": {"Changed":"7F-Y-改正","Resisted":"7F-Y-抗拒","Indifferent":"7F-Y-无视","NoResponse":"7F-Y-无响应"}, +} + +type AuditWrapper struct{ UID string } +func NewAuditWrapper(uid string) *AuditWrapper { + if uid == "" { uid = "9622" } + return &AuditWrapper{uid} +} + +func (w *AuditWrapper) Wrap(payload interface{}, taskType, persona string) map[string]interface{} { + if taskType == "" { taskType = "default" } + if persona == "" { persona = "P04" } + sig := map[string]interface{}{"P":"HasPromise","F":"Fulfilled","T":0.0,"E":"Willing","C":0,"R":0,"A":"Self","X":"Genuine","Y":"NoResponse","Z":1.0} + pattern := classify(sig) + labels := makeLabels(sig, pattern) + color := determineColor(pattern, int(sig["R"].(int))) + pj, _ := json.Marshal(payload) + ph := fmt.Sprintf("%x", sha256.Sum256(pj))[:16] + return map[string]interface{}{ + "audit_version":"v1.0","uid":fmt.Sprintf("UID%s",w.UID),"persona":persona,"task_type":taskType, + "behavior_signature":sig,"behavior_pattern":pattern,"behavior_labels":labels, + "color":color,"timestamp":time.Now().UTC().Format(time.RFC3339),"payload_hash":ph, + } +} + +func classify(sig map[string]interface{}) string { + f,_ := sig["F"].(string); x,_ := sig["X"].(string); a,_ := sig["A"].(string) + y,_ := sig["Y"].(string); z,_ := sig["Z"].(float64) + if f=="Unfulfilled" && x=="OverExplain" { return "MODE-DefensiveDefaulter" } + if f=="Fulfilled" && a=="Outsider" { return "MODE-ExternalTrustSpender" } + if f=="Unfulfilled" && y=="Indifferent" { return "MODE-InternalDestroyer" } + if z > 2.0 { return "MODE-Fluctuating" } + return "MODE-StableDisciplined" +} + +func makeLabels(sig map[string]interface{}, pattern string) []string { + var labels []string + for _, factor := range []string{"P","F","E","A","X","Y"} { + if v, ok := sig[factor].(string); ok { + if lm, ok2 := labelMap[factor]; ok2 { + if lb, ok3 := lm[v]; ok3 { labels = append(labels, lb) } + } + } + } + labels = append(labels, pattern) + return labels +} + +func determineColor(pattern string, repeat int) string { + if pattern == "MODE-InternalDestroyer" { return "🔴" } + if pattern == "MODE-Fluctuating" && repeat > 3 { return "🟡" } + if pattern == "MODE-DefensiveDefaulter" && repeat > 2 { return "🟡" } + return "🟢" +} +var _ = sort.Ints diff --git a/adapters/go/dna_generator.go b/adapters/go/dna_generator.go new file mode 100644 index 0000000..6842a48 --- /dev/null +++ b/adapters/go/dna_generator.go @@ -0,0 +1,93 @@ +// dna_generator.go — v∞ DNA traceability code generation +package lhstandard + +import ( + "crypto/sha256" + "fmt" + "math" + "time" +) + +var tianGan = []string{"Jia","Yi","Bing","Ding","Wu","Ji","Geng","Xin","Ren","Gui"} +var diZhi = []string{"Zi","Chou","Yin","Mao","Chen","Si","Wu","Wei","Shen","You","Xu","Hai"} +var shiChen = []string{"ZiShi","ChouShi","YinShi","MaoShi","ChenShi","SiShi","WuShi","WeiShi","ShenShi","YouShi","XuShi","HaiShi"} + +type Hexagram struct{ Symbol, EnName, CnName, Domain string } + +var hexagrams = []Hexagram{ + {"䷀","Qian","乾","governance"},{"䷁","Kun","坤","archive"}, + {"䷂","Zhun","屯","init"},{"䷃","Meng","蒙","learn"}, + {"䷄","Xu","需","async"},{"䷅","Song","讼","legal"}, + {"䷜","Kan","坎","engine"},{"䷝","Li","离","audit"}, + {"䷲","Zhen","震","security"},{"䷳","Gen","艮","privacy"}, + {"䷸","Xun","巽","deploy"},{"䷹","Dui","兑","trust"}, + {"䷾","JiJi","既济","complete"},{"䷿","WeiJi","未济","progress"}, +} + +var taskHexagramMap = map[string]string{ + "default":"governance","code":"engine","deploy":"deploy","audit":"audit", + "security":"security","archive":"archive","init":"init","learn":"learn", + "legal":"legal","privacy":"privacy","trust":"trust","complete":"complete","progress":"progress", +} + +type DNAGenerator struct { + UID, Device, Locale string +} + +func NewDNAGenerator(uid, device, locale string) *DNAGenerator { + if uid == "" { uid = "9622" } + if device == "" { device = "HM-9622-001" } + if locale == "" { locale = "Asia/Shanghai" } + return &DNAGenerator{uid, device, locale} +} + +func (g *DNAGenerator) Generate(taskType, action, version string) string { + if taskType == "" { taskType = "default" } + if action == "" { action = "WRAP" } + if version == "" { version = "V1.0" } + now := time.Now().UTC() + stem := g.computeStemBranch(now) + hex := g.selectHexagram(taskType) + body := fmt.Sprintf("ADAPTER-%s-%s-%s", toUpper(taskType), toUpper(action), version) + raw := fmt.Sprintf("%s%s%s%s%s%s%s%s%s", stem["year"],stem["month"],stem["day"],stem["shichen"],hex.Symbol,hex.EnName,body,g.Device,now.Format(time.RFC3339)) + h := sha256.Sum256([]byte(raw)) + return fmt.Sprintf("#LongHun⚡️%s·%s·%s·%s·%s%s-%s-%x", stem["year"],stem["month"],stem["day"],stem["shichen"],hex.Symbol,hex.EnName,body,h[:4]) +} + +func toUpper(s string) string { + r := []rune(s) + for i, c := range r { + if c >= 'a' && c <= 'z' { r[i] = c - 32 } + } + return string(r) +} + +func (g *DNAGenerator) computeStemBranch(dt time.Time) map[string]string { + cycleYear, cycleMonth := 1984, []int{2,4,6,8,10,0,2,4,6,8,10,0} + y := dt.Year(); m := int(dt.Month()); doy := dt.YearDay(); h := dt.Hour() + ys := (y-cycleYear) % 10; if ys < 0 { ys += 10 } + yb := (y-cycleYear) % 12; if yb < 0 { yb += 12 } + mb := cycleMonth[(y-cycleYear)%10]; if mb < 0 { mb += 10 } + ms := (mb + m - 1) % 10; if ms < 0 { ms += 10 } + mbr := (m + 1) % 12 + ds := (y - 1900 + (y-1900)/4 + doy) % 10; if ds < 0 { ds += 10 } + db := (y - 1900 + (y-1900)/4 + doy) % 12; if db < 0 { db += 12 } + si := h / 2 + return map[string]string{ + "year": tianGan[ys]+diZhi[yb], "month": tianGan[ms]+diZhi[mbr], + "day": tianGan[ds]+diZhi[db], "shichen": shiChen[si], + } +} + +func (g *DNAGenerator) selectHexagram(taskType string) Hexagram { + domain := taskHexagramMap[taskType] + if domain == "" { domain = "governance" } + for _, h := range hexagrams { + if h.Domain == domain { return h } + } + return hexagrams[0] +} + +func absMod(a, b int) int { r := a % b; if r < 0 { r += b }; return r } +var _ = absMod // suppress unused +var _ = math.Abs diff --git a/adapters/go/go.mod b/adapters/go/go.mod new file mode 100644 index 0000000..42dad5c --- /dev/null +++ b/adapters/go/go.mod @@ -0,0 +1,3 @@ +module github.com/UID9622/lh-standard-adapter/adapters/go + +go 1.19 diff --git a/adapters/go/schemas.go b/adapters/go/schemas.go new file mode 100644 index 0000000..691a9a4 --- /dev/null +++ b/adapters/go/schemas.go @@ -0,0 +1,3 @@ +// schemas.go — DNA and Audit JSON Schemas +package lhstandard +// Schemas are dynamically generated by GetSchemas() for runtime flexibility. diff --git a/adapters/go/validator.go b/adapters/go/validator.go new file mode 100644 index 0000000..658d24d --- /dev/null +++ b/adapters/go/validator.go @@ -0,0 +1,99 @@ +// validator.go — DNA and audit format validation +package lhstandard + +import ( + "regexp" + "strings" +) + +var dnaRegex = regexp.MustCompile(`^#LongHun⚡️([A-Z][a-zA-Z]+)·([A-Z][a-zA-Z]+)·([A-Z][a-zA-Z]+)·([A-Z][a-zA-Z]+)·([\x{4DC0}-\x{4DFF}][A-Za-z]+)-(.+)-([a-f0-9]{8})$`) + +var requiredTop = []string{"dna","audit","payload","meta"} +var requiredAudit = []string{"audit_version","uid","behavior_signature","behavior_pattern","behavior_labels","color"} +var requiredSig = []string{"P","F","T","E","C","R","A","X","Y","Z"} + +var validP = map[string]bool{"HasPromise":true,"NoPromise":true} +var validF = map[string]bool{"Fulfilled":true,"Unfulfilled":true,"Partial":true} +var validE = map[string]bool{"Willing":true,"Perfunctory":true,"Resentful":true,"Numb":true} +var validA = map[string]bool{"Self":true,"Partner":true,"Family":true,"Outsider":true,"Public":true} +var validX = map[string]bool{"OverExplain":true,"Silent":true,"Genuine":true,"Indifferent":true} +var validY = map[string]bool{"Changed":true,"Resisted":true,"Indifferent":true,"NoResponse":true} +var validColors = map[string]bool{"🟢":true,"🟡":true,"🔴":true} +var validPatterns = map[string]bool{"MODE-DefensiveDefaulter":true,"MODE-ExternalTrustSpender":true,"MODE-InternalDestroyer":true,"MODE-Fluctuating":true,"MODE-StableDisciplined":true} + +type Validator struct{ Errors, Warnings []string } +type ValidationResult struct{ Valid bool; Errors, Warnings []string; Summary string } + +func NewValidator() *Validator { return &Validator{} } + +func (v *Validator) Validate(wrapped map[string]interface{}) ValidationResult { + v.Errors, v.Warnings = nil, nil + if wrapped == nil { v.Errors = append(v.Errors, "Input is nil"); return v.result() } + for _, k := range requiredTop { + if _, ok := wrapped[k]; !ok { v.Errors = append(v.Errors, "Missing top key: "+k) } + } + dna, _ := wrapped["dna"].(string) + if dna == "" { v.Errors = append(v.Errors, "DNA empty") } else { + if !dnaRegex.MatchString(dna) { v.Errors = append(v.Errors, "DNA regex fail: "+dna[:min(60,len(dna))]) } + } + audit, _ := wrapped["audit"].(map[string]interface{}) + if audit == nil { v.Errors = append(v.Errors, "Audit not object") } else { v.validateAudit(audit) } + meta, _ := wrapped["meta"].(map[string]interface{}) + if meta != nil && audit != nil { + mu, _ := meta["uid"].(string) + au, _ := audit["uid"].(string) + auClean := strings.TrimPrefix(au, "UID") + if mu != "" && auClean != "" && mu != auClean { + v.Errors = append(v.Errors, "UID mismatch: "+mu+" vs "+au) + } + } + return v.result() +} + +func (v *Validator) validateAudit(a map[string]interface{}) { + for _, k := range requiredAudit { + if _, ok := a[k]; !ok { v.Errors = append(v.Errors, "Missing audit key: "+k) } + } + sig, _ := a["behavior_signature"].(map[string]interface{}) + if sig == nil { v.Errors = append(v.Errors, "sig not object") } else { + for _, k := range requiredSig { + if _, ok := sig[k]; !ok { v.Errors = append(v.Errors, "Missing sig key: "+k) } + } + v.validateSigValues(sig) + } + if p, ok := a["behavior_pattern"].(string); ok && p != "" && !validPatterns[p] { + v.Warnings = append(v.Warnings, "Unknown pattern: "+p) + } + if c, ok := a["color"].(string); ok && c != "" && !validColors[c] { + v.Warnings = append(v.Warnings, "Unknown color: "+c) + } +} + +func (v *Validator) validateSigValues(sig map[string]interface{}) { + if s, ok := sig["P"].(string); ok && !validP[s] { v.Warnings = append(v.Warnings, "Invalid P: "+s) } + if s, ok := sig["F"].(string); ok && !validF[s] { v.Warnings = append(v.Warnings, "Invalid F: "+s) } + if _, ok := sig["T"].(float64); !ok { v.Warnings = append(v.Warnings, "Invalid T") } + if s, ok := sig["E"].(string); ok && !validE[s] { v.Warnings = append(v.Warnings, "Invalid E: "+s) } + if _, ok := sig["C"].(float64); !ok { v.Warnings = append(v.Warnings, "Invalid C") } + if _, ok := sig["R"].(float64); !ok { v.Warnings = append(v.Warnings, "Invalid R") } + if s, ok := sig["A"].(string); ok && !validA[s] { v.Warnings = append(v.Warnings, "Invalid A: "+s) } + if s, ok := sig["X"].(string); ok && !validX[s] { v.Warnings = append(v.Warnings, "Invalid X: "+s) } + if s, ok := sig["Y"].(string); ok && !validY[s] { v.Warnings = append(v.Warnings, "Invalid Y: "+s) } + if _, ok := sig["Z"].(float64); !ok { v.Warnings = append(v.Warnings, "Invalid Z") } +} + +func (v *Validator) result() ValidationResult { + valid := len(v.Errors) == 0 + sum := "✅ VALID" + if !valid { sum = "❌ INVALID" } + return ValidationResult{Valid:valid, Errors:v.Errors, Warnings:v.Warnings, Summary:sum} +} + +func QuickValidate(wrapped map[string]interface{}) bool { + if wrapped == nil { return false } + dna, _ := wrapped["dna"].(string) + if dna == "" { return false } + _, ok := wrapped["audit"] + if !ok { return false } + return dnaRegex.MatchString(dna) +}