fix(web): keep an assignment's row in place on write - #755
Merged
rongxin-liu merged 4 commits intoSep 7, 2026
Conversation
The edit, lock, and close writers rebuilt assignments.json as everything
except the slug plus the updated entry, moving the row to the end. The
CLI replaces in place ("Position preserved on replace"), and its
`assignment list` prints file order, so a web-side lock reordered the
teacher's listing and made every flag flip a whole-row diff.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes an ordering/diff-hygiene issue in the web app’s writers for <classroom>/assignments.json by updating existing assignment entries in-place rather than removing and re-appending them (which previously moved the touched row to the end of the array). This aligns web write behavior with the CLI contract (“Position preserved on replace; new slugs append”) and improves the auditability of config repo diffs and CLI list output order.
Changes:
- Added a
replaceAssignmentEntryhelper to replace the first matching assignment row by slug while preserving array position. - Updated
editAssignment,setAssignmentLock, andsetAssignmentClosedto use the helper instead offilter + push. - Added regression tests ensuring row position is preserved (and that duplicate-slug manifests only rewrite the first match), plus updated a related comment in
rename.ts.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| web/src/domain/assignments/createEdit.ts | Introduces replaceAssignmentEntry and uses it in the three affected writers to preserve row position. |
| web/src/domain/assignments/rename.ts | Updates an explanatory comment to remove the now-stale “filter+push” reference. |
| web/src/domain/assignments.test.ts | Adds regression tests covering position preservation for edit/lock/close, plus a duplicate-slug scenario. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
rongxin-liu
self-requested a review
August 26, 2026 23:29
Merged
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
editAssignment,setAssignmentLock, andsetAssignmentClosedrebuilt<classroom>/assignments.jsonas "every entry except this slug, then the updated entry", so touching an assignment moved its row to the end of the array. They now go through a smallreplaceAssignmentEntryhelper that replaces the row where it sits.That matches the CLI, which is the other side of this contract and writes the rule down:
UpsertAssignment("Position preserved on replace; new slugs append",assignments_json.go) replaces the first match in place, andgh teacher assignment lockassigns back into the same index. It also matches the web app's ownrenameAssignment, which already maps in place;deleteAssignmentis a plain filter andcreateAssignment/copyAssignmentappend new slugs, both already correct.What the old shape cost teachers:
gh teacher assignment listprints entries in file order (no sort), so lockinghw1in the web app moved it belowhw9in the terminal and in--jsonoutput.git log -p assignments.jsonno longer showed what actually changed.No reader is affected: every consumer looks entries up by slug, so this is an ordering and diff-hygiene fix, not a behavior change for students or grading.
The helper replaces the first match only, like
UpsertAssignment: a hand-edited manifest can hold two rows for one slug, and each caller builds its entry from the first of them, so replacing both would overwrite the second row's own content. (The old filter+push silently collapsed such duplicates into one row, dropping the second row's content; keeping both matches the CLI and preserves the teacher's data.)Also updates the comment at
rename.ts:286, which cited "the lock flip's filter+push" as the counter-example for mapping in place. That pattern no longer exists after this change, and the comment now namescreateEdit.tsandUpsertAssignmentrather than a symbol that isn't in that file (addresses the Copilot review thread).Tests
Each of the three writers gets a regression test asserting the surrounding rows keep their positions, plus one for the duplicate-slug manifest. All three position tests fail on
main(the old code produces[hw2, hw1]for edit and[hw0, hw2, hw1]for lock/close against the new fixtures). Adding neighbor rows to the shared lock/close fixtures does not weaken any existing assertion in those blocks.Merged
mainRebased onto current
mainvia merge (102 commits, including #827 team assignments, #839 lock-from-form, #771 repo_visibility, and #855, all of which touchedcreateEdit.ts). No textual conflicts. Re-verified after the merge that:replaceAssignmentEntry, and no writer added onmainreintroduced filter+push (deleteAssignmentremains the only.filteron slug, as intended);findand throws before reaching the helper, so the miss branch stays unreachable;updateRefwithforce: false) is unchanged, so the concurrent web/CLI write window is identical to before.Closes #754
Type of change
Checklist
cd web && npm run check(web only, no Go or Python change). Re-run on the merged tree:tsc,eslint,prettier,arch:validateclean; 5192 unit tests pass, including all 253 inassignments.test.ts.schemas/*.schema.jsonand every mirror (Go / Python / TypeScript), and the parity tests pass. n/a: no schema or field change; this aligns the web app with the CLI's existing write behavior.