Skip to content

web: editing, locking, or closing an assignment moves its row to the end of assignments.json #754

Description

@graphics80

Which part is affected?

Web app (classroom50.org)

What happened?

Three web writers rebuild <classroom>/assignments.json as "everything except this slug, then the updated entry", so the touched row is moved to the end of the array:

assignments: [
  ...currentAssignments.assignments.filter((a) => a.slug !== slug),
  updatedEntry,
],

The CLI is the other side of this contract and does the opposite, deliberately:

// UpsertAssignment replaces by Slug (case-sensitive: ...).
// Position preserved on replace; new slugs append.

cli/gh-teacher/internal/assignment/assignments_json.go:766, and gh teacher assignment lock writes file.Assignments[idx] = entry (lock.go:108).

The web app is inconsistent with itself too: renameAssignment already maps in place (rename.ts:292, rename.ts:396) — and says so in a comment that names the lock flip as the counter-example — while deleteAssignment is a plain filter and createAssignment/copyAssignment append genuinely new slugs, which is correct. Only these three move an existing row.

What this causes

  1. gh teacher assignment list reorders. The list command prints entries in file order — no sort (assignment.go:442) — and --json emits the array as stored. Locking hw1 in the web app moves it below hw9 in the teacher's terminal, and nothing the teacher did asked for a reorder. The web UI hides this because both its lists sort explicitly (DEFAULT_SORT = "name-asc", students due-asc), so the divergence is invisible from the side that causes it.

  2. Every flag flip is a whole-row diff. Toggling locked should be a one-line change in the config repo. Instead the commit deletes the entry from its position and re-adds it at the end — for an entry with a template block, tests, and a due date, that is a ~15-line diff for a one-field change. The config repo is the teacher's audit trail, and git log -p cs50/assignments.json no longer shows what actually changed.

  3. Same action, two files. Web app and CLI, given the same starting file and the same lock, commit different content. Comparing two classrooms, or diffing a file across the two tools, has to normalize order first.

Nothing is functionally broken: every reader looks entries up by slug, so no student, accept, or grading path misbehaves. The cost is the teacher-visible ordering in the CLI and the audit trail in the config repo.

(closed has no CLI writer at all — the CLI preserves it as "owned out of band by the web", assignment.go:878 — so for setAssignmentClosed it is points 1 and 2 that apply, not CLI parity.)

What did you expect to happen?

The web app replaces an existing entry where it sits, matching UpsertAssignment's documented "Position preserved on replace". Only genuinely new slugs append.

Steps to reproduce

  1. In a classroom with several assignments, note the order of gh teacher assignment list --org <org> --classroom <c>, or open <classroom>/assignments.json in the config repo.
  2. In the web app, lock (or close, or edit) an assignment that is not the last one in that file.
  3. Re-run gh teacher assignment list — the assignment is now printed last.
  4. git show the commit the web app made: the whole entry was removed from its position and re-added at the end, not flipped in place.

Doing the same lock with gh teacher assignment lock leaves the position untouched.

Version and environment

Web app on main (acff6b1c), any browser; teacher CLI from the same tree. Not environment-specific — it is the write shape in the three functions above.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

CLICLI tools (cli/)P3Low — nice to have / somedayWebWeb frontend (web/)enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions