@@ -86,6 +86,11 @@ Deno.test('renderPackageDetailPage gives pending reviewers changes, launch, runt
8686 assertStringIncludes ( body , 'Review before approval' ) ;
8787 assertStringIncludes ( body , 'What changed' ) ;
8888 assertStringIncludes ( body , 'Version 0.2.0 is pending review against previous version 0.1.0.' ) ;
89+ assertStringIncludes (
90+ body ,
91+ 'Capability changes since approved version 0.1.0: no capability changes.' ,
92+ ) ;
93+ assertStringIncludes ( body , 'Unchanged' ) ;
8994 assertStringIncludes ( body , '/admin/packages/chapter-4-asteroids/versions/0.2.0/diff' ) ;
9095 assertStringIncludes ( body , 'Open review test launch' ) ;
9196 assertStringIncludes ( body , 'Latest review test launch preview-session-review' ) ;
@@ -98,6 +103,135 @@ Deno.test('renderPackageDetailPage gives pending reviewers changes, launch, runt
98103 assertStringIncludes ( body , 'direct grade writes' ) ;
99104} ) ;
100105
106+ Deno . test ( 'renderPackageDetailPage treats first pending version as a full capability baseline review' , ( ) => {
107+ const pendingVersion = buildPackageVersionRecord ( {
108+ id : 10 ,
109+ version : '0.1.0' ,
110+ approvalStatus : 'pending' ,
111+ capabilities : [ 'read_launch_context' , 'read_activity_content' , 'finalize_attempt' ] ,
112+ } ) ;
113+ const body = renderPackageDetailPage ( {
114+ packageVersion : pendingVersion ,
115+ history : [ pendingVersion ] ,
116+ } ) ;
117+
118+ assertStringIncludes ( body , 'Capability baseline: no previously approved version exists.' ) ;
119+ assertStringIncludes ( body , 'Declared for first approval' ) ;
120+ assertStringIncludes ( body , 'Launch context' ) ;
121+ assertStringIncludes ( body , 'Reviewed app content' ) ;
122+ assertStringIncludes ( body , 'Attempt completion' ) ;
123+ } ) ;
124+
125+ Deno . test ( 'renderPackageDetailPage highlights added sensitive capabilities against the previous approved version' , ( ) => {
126+ const previousVersion = buildPackageVersionRecord ( {
127+ id : 20 ,
128+ version : '0.1.0' ,
129+ approvalStatus : 'approved' ,
130+ reviewedAt : '2026-05-15T12:00:00.000Z' ,
131+ capabilities : [ 'read_launch_context' , 'read_activity_content' , 'finalize_attempt' ] ,
132+ } ) ;
133+ const pendingVersion = buildPackageVersionRecord ( {
134+ id : 21 ,
135+ version : '0.2.0' ,
136+ approvalStatus : 'pending' ,
137+ capabilities : [
138+ 'read_launch_context' ,
139+ 'read_activity_content' ,
140+ 'submit_evidence_artifact' ,
141+ 'finalize_attempt' ,
142+ ] ,
143+ } ) ;
144+ const body = renderPackageDetailPage ( {
145+ packageVersion : pendingVersion ,
146+ history : [ pendingVersion , previousVersion ] ,
147+ } ) ;
148+
149+ assertStringIncludes ( body , 'Capability changes since approved version 0.1.0.' ) ;
150+ assertStringIncludes ( body , 'Added' ) ;
151+ assertStringIncludes ( body , 'Submitted evidence artifacts' ) ;
152+ assertStringIncludes ( body , 'Sensitive learner evidence' ) ;
153+ assertStringIncludes (
154+ body ,
155+ 'Review impact: this version newly requests Submitted evidence artifacts.' ,
156+ ) ;
157+ assertStringIncludes ( body , 'Confirm the assignment purpose, evidence or grading flow' ) ;
158+ } ) ;
159+
160+ Deno . test ( 'renderPackageDetailPage shows removed capabilities against the previous approved version' , ( ) => {
161+ const previousVersion = buildPackageVersionRecord ( {
162+ id : 30 ,
163+ version : '0.1.0' ,
164+ approvalStatus : 'approved' ,
165+ reviewedAt : '2026-05-15T12:00:00.000Z' ,
166+ capabilities : [
167+ 'read_launch_context' ,
168+ 'read_activity_content' ,
169+ 'read_local_state' ,
170+ 'write_local_state' ,
171+ 'finalize_attempt' ,
172+ ] ,
173+ } ) ;
174+ const pendingVersion = buildPackageVersionRecord ( {
175+ id : 31 ,
176+ version : '0.2.0' ,
177+ approvalStatus : 'pending' ,
178+ capabilities : [ 'read_launch_context' , 'read_activity_content' , 'finalize_attempt' ] ,
179+ } ) ;
180+ const body = renderPackageDetailPage ( {
181+ packageVersion : pendingVersion ,
182+ history : [ pendingVersion , previousVersion ] ,
183+ } ) ;
184+
185+ assertStringIncludes ( body , 'Removed' ) ;
186+ assertStringIncludes ( body , 'Resume saved progress' ) ;
187+ assertStringIncludes ( body , 'Save resumable progress' ) ;
188+ assertStringIncludes ( body , 'Added: none.' ) ;
189+ } ) ;
190+
191+ Deno . test ( 'renderPackageDetailPage ignores newer approved versions when reviewing an older pending version' , ( ) => {
192+ const newerApprovedVersion = buildPackageVersionRecord ( {
193+ id : 40 ,
194+ version : '0.3.0' ,
195+ approvalStatus : 'approved' ,
196+ reviewedAt : '2026-06-15T12:00:00.000Z' ,
197+ importedAt : '2026-06-14T12:00:00.000Z' ,
198+ capabilities : [
199+ 'read_launch_context' ,
200+ 'read_activity_content' ,
201+ 'submit_evidence_artifact' ,
202+ 'finalize_attempt' ,
203+ ] ,
204+ } ) ;
205+ const pendingVersion = buildPackageVersionRecord ( {
206+ id : 41 ,
207+ version : '0.2.0' ,
208+ approvalStatus : 'pending' ,
209+ importedAt : '2026-06-01T12:00:00.000Z' ,
210+ capabilities : [
211+ 'read_launch_context' ,
212+ 'read_activity_content' ,
213+ 'submit_evidence_artifact' ,
214+ 'finalize_attempt' ,
215+ ] ,
216+ } ) ;
217+ const olderApprovedVersion = buildPackageVersionRecord ( {
218+ id : 42 ,
219+ version : '0.1.0' ,
220+ approvalStatus : 'approved' ,
221+ reviewedAt : '2026-05-15T12:00:00.000Z' ,
222+ importedAt : '2026-05-14T12:00:00.000Z' ,
223+ capabilities : [ 'read_launch_context' , 'read_activity_content' , 'finalize_attempt' ] ,
224+ } ) ;
225+ const body = renderPackageDetailPage ( {
226+ packageVersion : pendingVersion ,
227+ history : [ newerApprovedVersion , pendingVersion , olderApprovedVersion ] ,
228+ } ) ;
229+
230+ assertStringIncludes ( body , 'Capability changes since approved version 0.1.0.' ) ;
231+ assertStringIncludes ( body , 'Submitted evidence artifacts' ) ;
232+ assertStringIncludes ( body , 'Sensitive learner evidence' ) ;
233+ } ) ;
234+
101235Deno . test ( 'renderPackageDetailPage distinguishes ordinary progress from sensitive evidence and saved file details' , ( ) => {
102236 const reviewedVersion = buildPackageVersionRecord ( {
103237 approvalStatus : 'approved' ,
0 commit comments