feat: expand companion platform, device controls, and memoir workflows
ci / verify (push) Canceled after 0s

Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
This commit is contained in:
Codex Local
2026-10-08 10:06:44 +08:00
parent 98fb2965d0
commit bf1233d5df
861 changed files with 94614 additions and 3209 deletions
+45 -8
View File
@@ -92,13 +92,31 @@ func TestHouseholdSubjectConsentLifecycleAndIsolation(t *testing.T) {
}
subject, err := service.CreateSubject(ctx, editor, household.ID, "saas-integration-subject-create", CreateSubjectInput{
PreferredName: "集成长者", Language: "zh-CN", Timezone: "Asia/Shanghai",
InitialConsents: InitialConsentSelection{Types: []string{ConsentTranscriptStorage, ConsentPersonalMemory}, Confirmed: true},
PreferredName: "集成长者", Language: "zh-CN", Timezone: "Asia/Shanghai",
ProfileData: map[string]any{"synthetic": true}, ConversationPreferences: map[string]any{"pace": "slow"},
})
if err != nil {
t.Fatalf("create subject: %v", err)
}
subjectID = subject.ID
initialConsents, err := service.ListConsents(ctx, editor, subject.ID)
if err != nil || len(initialConsents) != 2 {
t.Fatalf("initial memory consents: %+v err=%v", initialConsents, err)
}
for _, c := range initialConsents {
if !c.Effective || c.PolicyVersion != "local-v1" || c.ActorUserID == "" || c.NoticeDigest == "" || (c.Type != ConsentTranscriptStorage && c.Type != ConsentPersonalMemory) {
t.Fatalf("invalid initial consent: %+v", c)
}
}
var snapshotOK bool
if err := pool.QueryRow(ctx, `SELECT effective_consents @> '{"voice_chat":true,"transcript_storage":true,"personalized_memory":true,"raw_audio_storage":false,"story_extraction":false,"family_sharing":false}'::jsonb AND jsonb_array_length(source_consent_ids)=2 FROM consent_snapshot WHERE subject_id=(SELECT id FROM subject_profile WHERE public_id=$1::uuid) ORDER BY snapshot_version DESC LIMIT 1`, subject.ID).Scan(&snapshotOK); err != nil || !snapshotOK {
t.Fatalf("initial memory snapshot=%v err=%v", snapshotOK, err)
}
var grantAuditCount int
if err := pool.QueryRow(ctx, `SELECT count(*) FROM audit_event WHERE request_id='saas-integration-subject-create' AND action='consent.grant'`).Scan(&grantAuditCount); err != nil || grantAuditCount != 2 {
t.Fatalf("initial memory audit=%d err=%v", grantAuditCount, err)
}
subjects, err := service.ListSubjects(ctx, editor, household.ID)
if err != nil || len(subjects) != 1 || subjects[0].ID != subject.ID {
t.Fatalf("list subjects: %+v err=%v", subjects, err)
@@ -152,7 +170,7 @@ func TestHouseholdSubjectConsentLifecycleAndIsolation(t *testing.T) {
t.Fatalf("replay withdrawal: %+v err=%v", replayed, err)
}
consents, err := service.ListConsents(ctx, editor, subject.ID)
if err != nil || len(consents) != 1 {
if err != nil || len(consents) != 3 {
t.Fatalf("list consents: %+v err=%v", consents, err)
}
privacyInput := CreatePrivacyRequestInput{RequestType: " access ", Scope: map[string]any{"categories": []any{"profile", "consents"}}}
@@ -191,9 +209,14 @@ func TestHouseholdSubjectConsentLifecycleAndIsolation(t *testing.T) {
t.Fatalf("withdrawal request=%+v created=%v err=%v", withdrawal, created, err)
}
consents, err = service.ListConsents(ctx, editor, subject.ID)
if err != nil || len(consents) != 2 || consents[0].Effective {
if err != nil || len(consents) != 4 {
t.Fatalf("consents after batch withdrawal=%+v err=%v", consents, err)
}
for _, c := range consents {
if c.Type == ConsentStoryExtraction && c.Effective {
t.Fatalf("story consent remains active after withdrawal: %+v", c)
}
}
if _, err := service.ListPrivacyRequests(ctx, editor, subject.ID); !errors.Is(err, ErrAccessDenied) {
t.Fatalf("editor privacy list error=%v", err)
}
@@ -231,15 +254,15 @@ func TestHouseholdSubjectConsentLifecycleAndIsolation(t *testing.T) {
if err := pool.QueryRow(ctx, `SELECT count(*) FROM consent_snapshot WHERE subject_id=(SELECT id FROM subject_profile WHERE public_id=$1::uuid)`, subject.ID).Scan(&snapshotCount); err != nil {
t.Fatal(err)
}
if snapshotCount != 4 {
t.Fatalf("snapshot count=%d, want 4", snapshotCount)
if snapshotCount != 5 {
t.Fatalf("snapshot count=%d, want 5", snapshotCount)
}
var auditCount int
if err := pool.QueryRow(ctx, `SELECT count(*) FROM audit_event WHERE request_id LIKE 'saas-integration-%'`).Scan(&auditCount); err != nil {
t.Fatal(err)
}
if auditCount != 13 {
t.Fatalf("audit count=%d, want 13", auditCount)
if auditCount != 15 {
t.Fatalf("audit count=%d, want 15", auditCount)
}
_ = uuid.MustParse(household.ID)
@@ -330,6 +353,20 @@ func TestDeviceReadTwinUpdateAndIsolation(t *testing.T) {
if _, err := service.PatchDeviceTwin(ctx, editor, deviceID, "saas-integration-device-stale", original.DesiredRevision, map[string]any{}); !errors.Is(err, ErrVersionConflict) {
t.Fatalf("stale twin patch error=%v", err)
}
power := map[string]any{"power": map[string]any{"mode": "standby"}}
if _, err := service.PatchDeviceTwin(ctx, editor, deviceID, "saas-integration-power-unsupported", updated.DesiredRevision, power); !errors.Is(err, ErrDevicePowerUnsupported) {
t.Fatalf("unsupported power: %v", err)
}
if _, err := service.PatchDeviceTwin(ctx, outsider, deviceID, "saas-integration-power-foreign", updated.DesiredRevision, power); !errors.Is(err, ErrAccessDenied) {
t.Fatalf("foreign power: %v", err)
}
if _, err := pool.Exec(ctx, `UPDATE device_twin SET reported_config=reported_config || '{"power":{"mode":"active","microphone_paused":false,"backlight_percent":80,"microphone_reads":12}}'::jsonb WHERE device_id=(SELECT id FROM device WHERE public_id=$1::uuid)`, deviceID); err != nil {
t.Fatal(err)
}
sleep, err := service.PatchDeviceTwin(ctx, editor, deviceID, "saas-integration-power-supported", updated.DesiredRevision, power)
if err != nil || sleep.DesiredRevision != updated.DesiredRevision+1 || sleep.ReportedConfig["power"].(map[string]any)["mode"] != "active" {
t.Fatalf("power must stay pending until actual report: %+v %v", sleep, err)
}
}
func containsDevice(items []ManagedDevice, id string) bool {
@@ -364,7 +401,7 @@ func TestDeviceClaimIsOneTimeAndRetrySafe(t *testing.T) {
if bindingID != "" {
_, _ = pool.Exec(ctx, `DELETE FROM device_binding WHERE public_id=$1::uuid`, bindingID)
}
_, _ = pool.Exec(ctx,`UPDATE device SET activation_state='CLAIMABLE' WHERE public_id='10000000-0000-0000-0000-000000000512'::uuid AND NOT EXISTS(SELECT 1 FROM device_binding WHERE device_id=device.id AND unbound_at IS NULL)`)
_, _ = pool.Exec(ctx, `UPDATE device SET activation_state='CLAIMABLE' WHERE public_id='10000000-0000-0000-0000-000000000512'::uuid AND NOT EXISTS(SELECT 1 FROM device_binding WHERE device_id=device.id AND unbound_at IS NULL)`)
_, _ = pool.Exec(ctx, `DELETE FROM audit_event WHERE request_id='saas-integration-device-claim'`)
}()
if _, _, err := service.ClaimDevice(ctx, outsider, claimCode, householdID, subjectID, "saas-integration-outsider-claim"); !errors.Is(err, ErrAccessDenied) {