Tech-Talk Report Card Generator
YOU MUST EXECUTE THIS WORKFLOW. Do not just describe it.
Generate a technical report card with A-F grades across 9 categories for iOS/Swift codebases.
All findings use the Issue Rating Table format. Do not use prose severity tags like [HIGH] or [MED] — always render the full rating table.
Step 1: Before Starting
📖 Run the opening interview from ../shared/session-setup.md § Opening interview.
It defines the CLAUDE.md and Timeline questions (plus the timeline grading adjustment)
shared by all report-card skills.
Focus question — specific to this skill. Use this as the third question:
{ "question": "Any categories to emphasize or skip?", "header": "Focus", "options": [ {"label": "Full analysis (Recommended)", "description": "Grade all 9 categories"}, {"label": "Skip accessibility", "description": "Not prioritizing accessibility now"}, {"label": "Emphasize performance", "description": "Users report slowness or battery drain"}, {"label": "Emphasize security", "description": "Handling sensitive data or preparing for review"} ], "multiSelect": true }
CLAUDE.md summary depth for this skill: 3-5 technical bullets.
Freshness
📖 Read ../shared/scan-discipline.md § Freshness and follow it. In short: current
source only, never .agents//scratch//prior reports, with one exception for reading a
previous report's grades only in Step 2. The shared file is authoritative — do not
re-inline the rule here.
Step 2: Trend Check
Check for a previous report to enable grade trend comparison:
Glob pattern=".agents/research/*-tech-reportcard.md"
If found, read ONLY the grade summary line from the most recent report. Do not read or reuse any findings — those must come fresh from scanning.
Step 3: Codebase Exploration
Scan the project structure and key configuration files:
- Project metrics — File counts, LOC (estimate via
wc -l), targets, schemes - Architecture — Main modules, patterns (MVC/MVVM/etc.), frameworks used
- App purpose — What the app does, primary user flows
- State management — How data flows between layers (@Observable, SwiftData, etc.)
Step 4: Automated Scans
Run all grep patterns below before compiling findings. Every section includes false-positive guidance — read flagged files before reporting any finding.
4.1 Architecture
bash# Large files (>500 lines) — maintainability signal # After finding files, check line counts with wc -l Glob pattern="**/*.swift" # Missing @MainActor on ObservableObject # FALSE POSITIVE: ObservableObject subclass that delegates to @MainActor property is fine Grep pattern="class.*:.*ObservableObject" glob="**/*.swift" # Deep view body nesting — read file and check body complexity # Only flag if view body exceeds ~80 lines or has >3 levels of conditional nesting Grep pattern="var body.*some View" glob="**/*View*.swift"
4.2 Code Quality
bash# Force casts — crash risk # FALSE POSITIVE: as! in test code or after is/guard let check is safe Grep pattern="as!" glob="**/*.swift" # Force unwraps (excluding IBOutlets and test files) # FALSE POSITIVE: dictionary literals, known-safe patterns, test assertions Grep pattern="[^I]!" glob="**/*.swift" # Bare try? swallowing errors silently # FALSE POSITIVE: try? in optional chaining where nil is the expected fallback # INTENTIONAL: try? for operations where failure is acceptable (e.g., file deletion, optional conversion) Grep pattern="try\?" glob="**/*.swift" # TODO/FIXME/HACK markers — self-documented technical debt # These are INTENTIONAL markers, not bugs. Count them for code health grading # but do not list individual TODOs as issues in the Issue Rating Table Grep pattern="(TODO|FIXME|HACK|XXX):" glob="**/*.swift"
4.3 Security
bash# Hardcoded secrets Grep pattern="(api[_-]?key|apikey|secret[_-]?key|client[_-]?secret|password|token)\s*[:=]\s*[\"'][^\"']+[\"']" glob="**/*.swift" -i # Sensitive data in UserDefaults Grep pattern="(UserDefaults|@AppStorage).*\b(password|token|secret|apiKey|credential)" glob="**/*.swift" -i # HTTP URLs (non-HTTPS) # FALSE POSITIVE: http://localhost, XML namespace URIs, protocol-detection logic Grep pattern="http://" glob="**/*.swift" # Logging sensitive data Grep pattern="(print|NSLog|os_log|Logger).*\b(password|token|secret|key|credential)" glob="**/*.swift" -i
4.4 Performance
NOTE: These patterns produce CANDIDATES. Verify each by reading the file.
bash# @Query without predicate (full table scans) # CLASSIFY: COUNT_ONLY (.count access → use fetchCount), FILTER_THEN_USE, or FULL_ACCESS # INTENTIONAL: views that genuinely need all records (e.g., "show all items" list) are OK Grep pattern="@Query\s+(private\s+)?var" glob="**/*.swift" # Main thread file I/O # FALSE POSITIVE: FileManager in async methods or Task.detached is fine # Only flag synchronous file I/O in view body, computed properties, or onAppear Grep pattern="(FileManager|Data\(contentsOf|String\(contentsOf)" glob="**/*View*.swift" # Missing [weak self] in closures # ⚠️ NO LOOKAHEAD — see "Regex constraint" below. These return every closure with a # capture list; you must read each hit and check whether the list contains weak/unowned. # FALSE POSITIVE: SwiftUI struct views don't need weak self — only flag in classes Grep pattern="\{\s*\[" glob="**/*ViewModel*.swift" Grep pattern="\{\s*\[" glob="**/*Manager*.swift" Grep pattern="\{\s*\[" glob="**/*Service*.swift" # Timer usage (potential battery drain) Grep pattern="Timer\.(scheduledTimer|publish)" glob="**/*.swift" # Continuous location Grep pattern="startUpdatingLocation" glob="**/*.swift"
4.5 Concurrency (Swift 6 Readiness)
NOTE: Each hit MUST be verified by reading the file. See Verification Rule below.
bash# Missing @MainActor on ViewModel # ⚠️ NO LOOKAHEAD, and note WHY a line-scoped regex cannot answer this at all: # `@MainActor` sits ABOVE the declaration, so any same-line test is structurally blind # to it. Match declarations, then READ each hit. # ⚠️ When reading, scan the WHOLE attribute stack, not one line up. Real declarations # stack 3-4 attributes (`@available` / `@MainActor` / `@Observable` / `final class`), # so a 1-line window reports correctly-annotated types as violations. Read until you # hit a blank line, a `}`, or an `import`. # ⚠️ A file named *ViewModel.swift need not CONTAIN a ViewModel — enum namespaces of # static helpers are commonly parked there. No type, no finding. Grade on the types # this pattern returns, never on the filename inventory. Grep pattern="(final )?(class|struct) \w*ViewModel" glob="**/*ViewModel.swift" # Dispatch to main thread (legacy pattern) # CLASSIFY: animation delay (asyncAfter) vs state update (async) vs layout workaround # Only state updates without asyncAfter are true migration candidates Grep pattern="DispatchQueue\.main\.(async|sync)" glob="**/*.swift" # Actor isolation issues # FALSE POSITIVE: nonisolated on UIKit delegate protocol methods is REQUIRED Grep pattern="nonisolated.*func" glob="**/*.swift"
4.6 Accessibility
bash# Fixed font sizes (breaks Dynamic Type) # INTENTIONAL: some hardcoded sizes prevent clipping in constrained containers # Read each hit — classify as CONFIRMED (should migrate) or INTENTIONAL (constrained layout) Grep pattern="\.font\(\.system\(size:" glob="**/*.swift" # Check for accessibilityLabel coverage Grep pattern="\.accessibilityLabel" glob="**/*.swift" output_mode="count" # Check for accessibilityIdentifier (UI testing support) Grep pattern="\.accessibilityIdentifier" glob="**/*.swift" output_mode="count" # Images that may need accessibility descriptions # Read flagged files — decorative images should use .accessibilityHidden(true) Grep pattern="Image\(systemName:" glob="**/*.swift" Grep pattern="Image\(\"" glob="**/*.swift"
4.7 Testing
bash# Test file inventory Glob pattern="**/*Tests.swift" Glob pattern="**/*Test.swift" Glob pattern="**/*UITests*.swift" # Framework usage Grep pattern="import Testing" glob="**/*Test*.swift" output_mode="count" Grep pattern="import XCTest" glob="**/*Test*.swift" output_mode="count" # Async test support Grep pattern="@Test.*async" glob="**/*Test*.swift" output_mode="count"
4.8 UI/UX Patterns
bash# Deprecated APIs Grep pattern="@available.*deprecated" glob="**/*.swift" Grep pattern="UIApplication\.shared\.open" glob="**/*.swift" # Missing loading/error states — views with async calls but no loading indicator Grep pattern="\.task\s*\{" glob="**/*View*.swift" # Platform conditionals — check for consistent behavior Grep pattern="#if.*os\(" glob="**/*.swift"
4.9 Data & Persistence
bash# SwiftData models Grep pattern="@Model" glob="**/*.swift" # Migration support Grep pattern="VersionedSchema" glob="**/*.swift" Grep pattern="SchemaMigrationPlan" glob="**/*.swift" # Core Data usage (legacy check) Grep pattern="NSManagedObject|NSPersistentContainer" glob="**/*.swift" # UserDefaults for non-trivial data (should use proper persistence) # INTENTIONAL: simple preferences (theme, sort order, last-opened tab) are appropriate for UserDefaults Grep pattern="UserDefaults\.standard\.(set|object)" glob="**/*.swift"
Step 5: Verification Rule (CRITICAL)
📖 Read ../shared/scan-discipline.md and follow it in full before compiling any
finding. It is the source of truth for three rules this skill depends on:
- § Regex constraint — no lookahead/lookbehind, ever. Look-around matches nothing under the default Grep engine and exits 0, so an un-run scan grades A under Step 6's "0-1 confirmed findings" rule. Silent, and biased toward a flattering grade.
- § Verification Rule — grep produces CANDIDATES; read and classify each hit as CONFIRMED / FALSE_POSITIVE / INTENTIONAL before it becomes a finding. Never report a grep count as an issue count.
- § Reading traps — read the whole attribute stack, not one line up; a filename is not a type.
Do not re-inline those rules here. They are shared with plain-talk
specifically so a fix lands in both skills at once.
Step 6: Grading
Grade Scale
| Grade | Meaning | Guideline |
|---|---|---|
| A | Excellent | Best practices, minimal issues. 0-1 confirmed findings. |
| B | Good | Solid, minor gaps. 2-4 low/medium findings. |
| C | Adequate | Functional but notable gaps. Multiple medium findings or 1+ high. |
| D | Poor | Significant issues. Multiple high findings. |
| F | Failing | Critical problems, not production-ready. |
Use +/- modifiers (e.g., B+, C-) for granularity. Convert to points: A=4, B=3, C=2, D=1, F=0 (with +/- as ±0.3).
Category Weights
| Category | Weight | What to Evaluate |
|---|---|---|
| Architecture | 15% | Separation of concerns, file sizes, module boundaries, dependency direction |
| Code Quality | 10% | Force unwraps, force casts, error swallowing, TODO density, naming |
| Performance | 15% | Main-thread blocking, @Query efficiency, memory patterns, energy |
| Concurrency | 10% | @MainActor coverage, DispatchQueue legacy, Swift 6 readiness |
| Security | 15% | Hardcoded secrets, storage safety, network security, logging |
| Accessibility | 10% | Dynamic Type, VoiceOver labels, accessibility identifiers |
| Testing | 15% | Coverage breadth, framework choice, async test support |
| UI/UX | 5% | Deprecated APIs, loading/error states, platform parity |
| Data/Persistence | 5% | Schema migrations, model design, storage choices |
Overall Grade Calculation
Multiply each category's point value by its weight, sum, and convert back:
Overall = (Arch × 0.15) + (Quality × 0.10) + (Perf × 0.15) + (Concurrency × 0.10) + (Security × 0.15) + (A11y × 0.10) + (Testing × 0.15) + (UI × 0.05) + (Data × 0.05)
Convert: 3.7+ = A-, 3.3-3.69 = B+, 3.0-3.29 = B, 2.7-2.99 = B-, etc.
Timeline Adjustment
- Pre-release: Double-weight Security and Performance findings
- Post-release: Standard weights
- Planning: Double-weight Architecture and Testing findings
Step 7: Output Format
Structure the report as follows. Write to .agents/research/YYYY-MM-DD-tech-reportcard.md.
1. Executive Summary (2-3 sentences)
A narrative overview a developer can read in 10 seconds. Example:
This codebase has clean architecture and strong security practices but carries significant concurrency debt ahead of Swift 6. Testing coverage is the biggest gap — only ViewModels have tests. Accessibility needs attention before App Store submission.
2. CLAUDE.md Summary (if included)
3-5 bullet points from CLAUDE.md. If excluded, note: "CLAUDE.md was excluded per user request."
3. Project Metrics
Swift Files: 142 | LOC: ~28k | Architecture: MVVM | Persistence: SwiftData Unit Tests: 47 | UI Tests: 12 | Test Framework: Swift Testing
4. Grade Summary Line
Overall: B+ (Arch A- | Quality B+ | Perf B | Concurrency B- | Security A | A11y C+ | Testing C+ | UI B+ | Data B)
5. Trend Comparison (if previous report exists)
vs Previous (YYYY-MM-DD): Architecture: B+ → A- (↑) | Testing: C → C+ (↑) | Security: A → A (→)
6. Per-Category Grades
For each category, provide:
- Grade and 1-sentence summary
- Strengths — what's done well (bullet list)
- Findings — confirmed issues only (NOT grep counts)
Example:
### Architecture: A- Clean MVVM with proper separation of concerns. **Strengths:** - ViewModels use @MainActor correctly for SwiftData access - Dependency injection via protocols enables testability **Findings:** - ItemListViewModel.swift (892 lines) exceeds 500-line threshold - 2 circular import patterns between Managers/ and Views/
7. Issue Rating Table
Consolidate ALL findings into a single Issue Rating Table, sorted by urgency descending then ROI:
| # | Finding | Urgency | Risk: Fix | Risk: No Fix | ROI | Blast Radius | Fix Effort | |---|---------|---------|-----------|-------------|-----|-------------|------------| | 1 | ... | 🔴 Critical | ... | ... | ... | ... | ... | | 2 | ... | 🟡 High | ... | ... | ... | ... | ... |
📖 Column definitions, indicator scales, and the one-table hard rule:
../shared/rating-system.md — the source of truth, shared with
plain-talk. Quick reference:
- Urgency: 🔴 CRITICAL (pre-launch blocker/crash risk) · 🟡 HIGH (fix before release) · 🟢 MEDIUM (schedule it) · ⚪ LOW (nice-to-have)
- Risk Fix/No Fix: 🔴 Critical · 🟡 High · 🟢 Medium · ⚪ Low
- ROI: 🟠 Excellent · 🟢 Good · 🟡 Marginal · 🔴 Poor
- Blast Radius: 🔴 Critical · 🟡 High · 🟢 Medium · ⚪ Low
- Fix Effort: Trivial / Small / Medium / Large
8. Next Steps
Group by timeline:
Immediate (This Week): - [Finding #1] — one-line rationale Short-term (This Month): - [Finding #3] — one-line rationale Medium-term (This Quarter): - [Finding #5] — one-line rationale
Step 8: Follow-up
📖 Use the menu in ../shared/session-setup.md § Follow-up menu, including its
handling of each option. Nothing here diverges from the shared version.
Troubleshooting
| Problem | Solution |
|---|---|
| Too many grep hits to verify | Narrow glob pattern (e.g., **/*View*.swift instead of **/*.swift) |
| Category has no findings | Grade A — note "No issues detected" and list what was scanned |
| Can't determine architecture pattern | Read the app entry point and 3-4 representative views to infer |
| Previous report has different categories | Compare only matching categories in trend |


