# Report Student Header Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Add a consistent PDF/HTML page header to both exam sprint reports that displays the student name from `StudentName`, with the logo area left empty for a future asset. **Architecture:** The report renderers already own final HTML generation. Add template placeholders for `studentName` and `generatedAtText`, fill them from the existing report content or payload contract, and keep escaping at the renderer boundary. The left logo slot is CSS-only empty space in both templates. **Tech Stack:** Java 17 records, Spring component renderers, static HTML templates, JUnit 5, AssertJ. --- ## Files - Modify: `abilities/exam-sprint/infrastructure/src/test/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/rendering/outlook/ClasspathOutlookExamSprintReportRendererTest.java` - Modify: `abilities/exam-sprint/infrastructure/src/test/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/rendering/achievement/ClasspathAchievementExamSprintReportRendererTest.java` - Modify: `abilities/exam-sprint/infrastructure/src/main/resources/templates/outlook-exam-sprint-report-template.html` - Modify: `abilities/exam-sprint/infrastructure/src/main/resources/templates/achievement-exam-sprint-report-template.html` - Modify: `abilities/exam-sprint/infrastructure/src/main/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/rendering/outlook/ClasspathOutlookExamSprintReportRenderer.java` - Modify: `abilities/exam-sprint/infrastructure/src/main/java/cn/yunzhixue/ability/center/examsprint/infrastructure/report/rendering/achievement/ClasspathAchievementExamSprintReportRenderer.java` ## Task 1: Add failing renderer tests for the shared page header - [ ] **Step 1: Add Outlook header assertions** In `ClasspathOutlookExamSprintReportRendererTest`, add a test that renders `callerVocabularyPayload()` and asserts: ```java assertThat(html) .contains("class=\"report-header\"") .contains("class=\"header-logo\"") .contains("个人学情报告") .contains("class=\"header-student-name\">20260318测试") .contains("class=\"header-generated-at\"") .doesNotContain("{{studentName}}") .doesNotContain("{{generatedAtText}}"); ``` - [ ] **Step 2: Add Achievement header assertions** In `ClasspathAchievementExamSprintReportRendererTest`, add a test that renders `sampleContent()` and asserts: ```java assertThat(html) .contains("class=\"report-header\"") .contains("class=\"header-logo\"") .contains("个人学情报告") .contains("class=\"header-student-name\">测试临考") .contains("class=\"header-generated-at\"") .doesNotContain("{{studentName}}") .doesNotContain("{{generatedAtText}}"); ``` - [ ] **Step 3: Add escaping coverage** Extend existing escaping tests so malicious student names are escaped. For achievement, create content with `studentName` set to `测试` and assert escaped text appears. For outlook, mutate `StudentName` in the JsonNode to the same value and assert escaped text appears. - [ ] **Step 4: Run focused tests and verify RED** Run: ```bash ./mvnw -pl abilities/exam-sprint/infrastructure -Dtest=ClasspathOutlookExamSprintReportRendererTest,ClasspathAchievementExamSprintReportRendererTest test ``` Expected: tests fail because `report-header`, `studentName`, and `generatedAtText` placeholders do not exist or are not replaced yet. ## Task 2: Implement template placeholders and renderer replacements - [ ] **Step 1: Add shared header markup to both templates** Insert this block immediately after `