Skip to content

Commit 0c4406b

Browse files
author
Neo
committed
Morpheus M-NARRATOR piece 2: read-only security narration surface (display only, no aggregate claim)
SecurityNarrationView renders MorpheusNarrator.narrate() statements as a "What's protecting you" section at the top of Account → Security. Read-only display: - Purely additive to SecuritySettingsView (no existing line removed/edited): a @State, an .onAppear that reads narrate() (read-only), and a Section. The prefs toggles, reset, and every other section are untouched. - The view exposes no callback/binding; it gates nothing, feeds no enforcement path, changes no wall. - NO aggregate "all secure" banner — only the individual grounded statements, each with its own tone; the header is descriptive, not a guarantee. Attention rows carry "Needs attention:" into the accessibility label so VoiceOver hears the same severity the glyph shows (per-statement, no rollup). Honesty pass: 2 raised, 1 confirmed (a11y), fixed. This completes M-NARRATOR. Flags OFF, OBSERVE, testnet.
1 parent a7b7af6 commit 0c4406b

3 files changed

Lines changed: 88 additions & 0 deletions

File tree

MTRX.xcodeproj/project.pbxproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@
238238
5B031DA597A70E826A01EA36 /* MtrxComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 020D53A9EC3924EDB2BC9913 /* MtrxComponents.swift */; };
239239
A1A1A1A1A1A1A1A1A1A1A6D1 /* StoryAvatar.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1A1A1A1A1A1A1A1A1A1A6D2 /* StoryAvatar.swift */; };
240240
D7D7D7D7D7D7D7D7D7D7D7A1 /* GuardianReviewPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D7D7D7D7D7D7D7D7D7D7A2 /* GuardianReviewPanel.swift */; };
241+
D9D9D9D9D9D9D9D9D9D9D9A1 /* SecurityNarrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9D9D9D9D9D9D9D9D9D9D9A2 /* SecurityNarrationView.swift */; };
241242
72C79D53328FA2323F5E3028 /* SubscriptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F06E34845F543EEDE7B91C1C /* SubscriptionView.swift */; };
242243
6FA529066511A5865FD08CA9 /* LaunchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 981C9E1F936DA94C2599780D /* LaunchView.swift */; };
243244
F51012B3159498482F002A6A /* NotificationCenterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2A724376917CF6778A0D91 /* NotificationCenterView.swift */; };
@@ -604,6 +605,7 @@
604605
020D53A9EC3924EDB2BC9913 /* MtrxComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "MtrxComponents.swift"; path = "UI/Components/MtrxComponents.swift"; sourceTree = SOURCE_ROOT; };
605606
A1A1A1A1A1A1A1A1A1A1A6D2 /* StoryAvatar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "StoryAvatar.swift"; path = "UI/Components/StoryAvatar.swift"; sourceTree = SOURCE_ROOT; };
606607
D7D7D7D7D7D7D7D7D7D7D7A2 /* GuardianReviewPanel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "GuardianReviewPanel.swift"; path = "UI/Components/GuardianReviewPanel.swift"; sourceTree = SOURCE_ROOT; };
608+
D9D9D9D9D9D9D9D9D9D9D9A2 /* SecurityNarrationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "SecurityNarrationView.swift"; path = "UI/Components/SecurityNarrationView.swift"; sourceTree = SOURCE_ROOT; };
607609
F06E34845F543EEDE7B91C1C /* SubscriptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "SubscriptionView.swift"; path = "UI/Views/Account/SubscriptionView.swift"; sourceTree = SOURCE_ROOT; };
608610
981C9E1F936DA94C2599780D /* LaunchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "LaunchView.swift"; path = "UI/Views/Launch/LaunchView.swift"; sourceTree = SOURCE_ROOT; };
609611
9E2A724376917CF6778A0D91 /* NotificationCenterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "NotificationCenterView.swift"; path = "UI/Views/Notifications/NotificationCenterView.swift"; sourceTree = SOURCE_ROOT; };
@@ -1949,6 +1951,7 @@
19491951
5B031DA597A70E826A01EA36 /* MtrxComponents.swift in Sources */,
19501952
A1A1A1A1A1A1A1A1A1A1A6D1 /* StoryAvatar.swift in Sources */,
19511953
D7D7D7D7D7D7D7D7D7D7D7A1 /* GuardianReviewPanel.swift in Sources */,
1954+
D9D9D9D9D9D9D9D9D9D9D9A1 /* SecurityNarrationView.swift in Sources */,
19521955
72C79D53328FA2323F5E3028 /* SubscriptionView.swift in Sources */,
19531956
6FA529066511A5865FD08CA9 /* LaunchView.swift in Sources */,
19541957
F51012B3159498482F002A6A /* NotificationCenterView.swift in Sources */,
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// SecurityNarrationView.swift
2+
// MTRX — Morpheus whole-app security narrator: display SURFACE (M-NARRATOR, piece 2)
3+
//
4+
// Renders the grounded statements from MorpheusNarrator.narrate() so the user can see
5+
// what is protecting them. Per MORPHEUS_ADVISOR_SPEC.md and the piece-2 constraints:
6+
//
7+
// • DISPLAY ONLY. It renders a [Statement] list; it owns no send/sign logic, exposes
8+
// no callback that affects anything, gates nothing, and changes no wall.
9+
// • READ-ONLY CONSUMPTION. The host reads MorpheusNarrator.narrate() and hands the
10+
// statements in; this view only displays them.
11+
// • NO FALSE REASSURANCE AT THE UI LAYER. It shows the INDIVIDUAL grounded statements,
12+
// each with its own tone. It must NOT add an aggregate "all secure" banner or any
13+
// summary that overstates the individual facts — there is no rollup here.
14+
15+
import SwiftUI
16+
17+
/// Renders the narrator's statements as rows. Designed to drop into a List Section (each
18+
/// statement becomes a row) — but it adds NO aggregate/summary claim of its own.
19+
struct SecurityNarrationView: View {
20+
let statements: [MorpheusNarrator.Statement]
21+
22+
var body: some View {
23+
ForEach(Array(statements.enumerated()), id: \.offset) { _, statement in
24+
SecurityNarrationRow(statement: statement)
25+
}
26+
}
27+
}
28+
29+
struct SecurityNarrationRow: View {
30+
let statement: MorpheusNarrator.Statement
31+
32+
var body: some View {
33+
Label {
34+
VStack(alignment: .leading, spacing: 2) {
35+
Text(statement.title)
36+
.font(.mtrxBody)
37+
.foregroundStyle(Color.labelPrimary)
38+
Text(statement.detail)
39+
.font(.mtrxCaption2)
40+
.foregroundStyle(Color.labelTertiary)
41+
}
42+
} icon: {
43+
Image(systemName: glyph)
44+
.foregroundStyle(tint)
45+
}
46+
.accessibilityElement(children: .combine)
47+
.accessibilityLabel(accessibilityText)
48+
}
49+
50+
// Carry the visual tone cue (the warning glyph) into VoiceOver so an attention row
51+
// doesn't sound identical to a neutral one. Per-statement only — no aggregate phrasing.
52+
private var accessibilityText: String {
53+
let prefix = statement.tone == .attention ? "Needs attention: " : ""
54+
return "\(prefix)\(statement.title). \(statement.detail)"
55+
}
56+
57+
// Per-statement tone styling. There is intentionally no aggregate "all secure" state —
58+
// each row reflects only the single grounded fact it carries.
59+
private var glyph: String {
60+
switch statement.tone {
61+
case .protective: return "checkmark.shield"
62+
case .neutral: return "info.circle"
63+
case .attention: return "exclamationmark.triangle"
64+
}
65+
}
66+
private var tint: Color {
67+
switch statement.tone {
68+
case .protective: return Color.statusSuccess
69+
case .neutral: return Color.labelSecondary
70+
case .attention: return Color.statusWarning
71+
}
72+
}
73+
}

UI/Views/Account/SecuritySettingsView.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import SwiftUI
1010
struct SecuritySettingsView: View {
1111
@State private var prefs = SecurityPreferences.shared
1212
@State private var showResetConfirm = false
13+
// M-NARRATOR piece 2: read-only security narration shown at the top. Display only —
14+
// it gates nothing and changes no wall.
15+
@State private var narration: [MorpheusNarrator.Statement] = []
1316

1417
var body: some View {
1518
@Bindable var prefs = prefs
@@ -23,6 +26,14 @@ struct SecuritySettingsView: View {
2326
.foregroundStyle(Color.labelSecondary)
2427
}
2528

29+
// Morpheus security narration — read-only display of what's protecting the
30+
// user. Each row is a single grounded fact; there is no aggregate "all secure".
31+
if !narration.isEmpty {
32+
Section("What's protecting you") {
33+
SecurityNarrationView(statements: narration)
34+
}
35+
}
36+
2637
// Phone verification (SMS OTP, Phase 2)
2738
Section("Verification") {
2839
NavigationLink {
@@ -119,6 +130,7 @@ struct SecuritySettingsView: View {
119130
}
120131
}
121132
.navigationTitle("Security")
133+
.onAppear { narration = MorpheusNarrator.narrate() }
122134
.navigationBarTitleDisplayMode(.inline)
123135
.confirmationDialog("Reset all security settings to the recommended defaults?",
124136
isPresented: $showResetConfirm, titleVisibility: .visible) {

0 commit comments

Comments
 (0)