ARFC-1020: Challenge Workbench Provisioning
Status
| Draft | Draft Date: 2026-09-06 | Version: 0.7 |
Abstract
This ARFC proposes a way for an instructor to author the files a student finds waiting in a challenge’s working directory — starter source, helper scripts, fixtures, and binary artefacts such as the image in a cipher exercise — and to have the platform place them there when the directory is prepared. It separates the four kinds of authored material a lesson carries into channels defined by who runs them and who may read them: a workbench bundle that lands in the student’s own directory, a shell-initialization script that runs when a lesson terminal opens, and the existing generator and comparator bundles, which run in the grading sandbox and must never be visible to a student. Three of the four belong to the challenge variant a student was dealt; the shell-initialization script belongs instead to the terminal that opens the session. Version 1 introduces the workbench bundle and its placement rules, gives a student a way to enumerate and prepare their own challenges, widens generator authoring from a single file to many, and introduces comparator authoring.
Table of Contents
- Introduction
- Motivation
- Specification
- 3.1 Core Principles
- 3.2 Provisioning Channels
- 3.3 The Workbench Bundle
- 3.4 The Challenge Directory
- 3.5 Materialization
- 3.6 The Workbench and the Assignment
- 3.7 Shell Initialization
- 3.8 The Grading Bundles
- 3.9 Content Identity and Publication
- 3.10 API Endpoints
- 3.11 Command-Line Surface
- 3.12 Authorization Model
- 3.13 Error Handling
- Version 1 Scope
- Security Considerations
- Backward Compatibility
- References
- Author
- Revision History
1. Introduction
A programming exercise is not only a question. It is a question plus a place to work, and that place usually needs contents: a source file with a skeleton in it, a header the student is told not to modify, a sample input, sometimes a helper the instructor wrote to save everyone an hour. For a cipher or forensics exercise the contents are the exercise.
The platform can already give a student a working directory and a terminal that opens in it. What it cannot do is put anything in that directory beyond the question itself. Every exercise that needs starting material therefore asks the student to create it by hand, typically by copying it out of the lesson text — an instruction the instructor must write, and a step at which beginners lose time to transcription rather than to the subject being taught. For a binary artefact they cannot do even that.
This ARFC closes that gap, and in doing so settles a boundary question that has been implicit until now: an authored lesson carries several kinds of material, they run in different places, and two of them carry the answer key. Treating them as one undifferentiated pool of “challenge files” would be simpler to describe and unsafe to build.
2. Motivation
Current State
An instructor authoring a program-graded challenge today supplies its grading material and nothing else that reaches the student’s environment:
- A generator, which runs in the grading sandbox and produces both the input the student’s program receives and the expected output it is scored against. It is stored as an archive against a challenge variant and unpacked into the sandbox at grading time, but only its entry point is authorable.
- A comparator, which would score the student’s output where the platform’s standard comparison will not do. The platform stores one against a variant and runs it if present; there is no way to author one, so in practice every challenge uses the standard comparison.
- A startup script for a lesson terminal, which the authoring tool composes on the instructor’s behalf and publishes as an ordinary file in the instructor’s own space. It finds the challenge’s working directory, changes into it, and reports what it did. The instructor does not author it, though — being their own file — they can read it.
When a student opens the terminal, the platform prepares the working directory — realm, term, course, section, assignment, and challenge — and writes the question into it. Nothing else is written. The directory contains the question and nothing more.
Missing Capabilities
- No authored starting material. An instructor cannot place a file in the student’s working directory. Exercises that need a skeleton, a fixture, an image, or a fixed header must instruct the student to produce it themselves — which, for a binary artefact, they cannot.
- No multi-file generator authoring, and no comparator authoring at all. The generator is already an archive and already unpacked as such; it is the authoring surface that admits one file. The comparator has no authoring surface whatever.
- No authored shell initialization. The terminal’s startup behavior is composed for the instructor. An instructor who wants the shell to set an environment variable, define an alias, or print a reminder has no way to say so.
- Provisioning is reachable only through the terminal. Preparing a challenge directory from a plain shell session requires naming the challenge by an identifier that appears on no student-facing surface, so in practice the terminal is the only route in.
Design Goals
- Let the instructor author what the student starts with, as files, in the authoring tool, reviewed the same way the rest of the lesson is.
- Keep the answer key structurally out of reach. Material that runs in the grading sandbox must remain unreadable by students; material that runs as the student is readable by them by construction, and the design must not blur the two.
- Never destroy student work. Nothing the platform does of its own accord may overwrite what a student has written.
- Provision the workbench independently of the terminal, so that any entry point into a challenge produces the same directory — which requires that a student be able to name their own challenges.
- Make repeat provisioning cheap and quiet. Preparing a directory that is already prepared should place nothing, say nothing, and not grow more expensive as a student’s enrolment grows.
- Keep the load-bearing parts out of reach of a typo. The step that locates the challenge and enters its directory is not offered for editing, and an instructor’s mistake in the part that is editable must not cost a section its terminals.
3. Specification
3.1 Core Principles
P1 — Channels are separated by trust boundary, not by convenience. Authored material is grouped by who executes it and who may read it. Anything that runs as the student is readable by the student, because it lands in their own environment and no amount of care changes that. Anything that carries or can recognise the expected answer never enters that environment at all. These are different channels because they have different readers, not because they have different purposes. There are four channels of authored material, and the enumeration is exhaustive: a channel omitted from it is a channel whose reader nobody has decided. The question statement is not among them — it is a rendering of the task rather than authored files, and §3.3 gives its rule.
P2 — The workbench is placed by preparation, not by the terminal. Preparing a challenge’s working directory is what places the workbench. A lesson terminal is one way to trigger that preparation and a convenient one, but a student who prepares the challenge from a plain shell session arrives at an identical directory.
P3 — A student’s workbench files are theirs. The platform places workbench entries once and then stops. Nothing it does of its own accord overwrites what is at a completed path afterwards — whether the student edited it, replaced it, or deleted it. The single exception is a placement the platform itself left half-finished, which it completes, and even there it preserves anything the student has since put at that path before doing so (§3.5). Otherwise only the student can ask for the authored version back (§3.11); no instructor action and no republication reaches it.
P4 — Authored content is published, not generated. What the instructor writes is what the student receives, byte for byte. The platform adds nothing to authored files and interprets nothing in them.
P5 — Identical content within a channel is the same content. A bundle is identified by an identifier derived from its content, scoped to its channel. Publishing material that has not changed produces no new bundle. Identical content in two different channels is two different bundles — see §3.9, where this is load-bearing rather than incidental.
P6 — A challenge directory is found by reference, not by name. Both directions matter. Stable identifiers locate a student’s directory whatever the display names above it have since become, and a directory identifies its own challenge from what is recorded in it rather than from the names on the path to it. Renaming a challenge, an assignment, a course, or a term therefore separates nobody from their work, and leaves every command that operates from within the directory working.
P7 — What a student is given follows the variant they were dealt. A challenge may present different variants to different students. Everything a student receives for it — the question, the workbench, and the material used to grade them — belongs to their variant, not to the challenge as a whole, because a design that attached any of these to the challenge would hand two students with different questions the same starting files. Today a publication binds one variant per challenge, so in practice every student dealt a given challenge receives the same one; §4 records multi-variant fan-out as a forthcoming platform capability that this attachment is deliberately ready for.
3.2 Provisioning Channels
Four channels of authored material, distinguished by their reader and their trigger:
| Workbench bundle | Shell-initialization script | Generator bundle | Comparator bundle | |
|---|---|---|---|---|
| What it is | Files the student starts with | A script that runs when a lesson terminal opens | Produces the input the student’s program receives, and the expected output | Scores the student’s output, where the standard comparison will not do |
| Runs as | Not run by the platform — it is content | The student, in their own session | The grading sandbox, with no student identity | The grading sandbox, with no student identity |
| Trigger | Preparing the challenge directory | Every terminal open | Every attempt | Every attempt |
| Readable by the student | Yes, inherently — the files are in their directory | Yes, inherently — it runs in their session | Never | Never |
| Shape | A set of files | A single script | A set of files | A set of files |
| Scope | One challenge variant | One lesson terminal | One challenge variant | One challenge variant |
flowchart TB
A["A challenge variant"] --> W["Workbench bundle<br/>(files)"]
A --> G["Generator bundle<br/>(files)"]
A --> C["Comparator bundle<br/>(files, optional)"]
L["A lesson terminal"] --> S["Shell-initialization script<br/>(one script)"]
W --> P["Preparing the challenge directory"]
S --> T["Opening a lesson terminal"]
T --> P
G --> J["Grading an attempt"]
C --> J
P --> SD["Student's challenge directory"]
T --> SS["Student's shell session"]
J --> SB["Grading sandbox"]
SD -.->|"student can read"| SR(["Student"])
SS -.->|"student can read"| SR
SB -.->|"never reaches"| SR
style SB fill:#f8d7da,stroke:#842029,color:#1b1a17
style SR fill:#d1e7dd,stroke:#0f5132,color:#1b1a17
Three channels belong to the variant; one belongs to the terminal. The workbench, the generator and the comparator are all determined by which variant a student was dealt (P7). The shell-initialization script is not: a lesson terminal names it, a session resolves it when that terminal opens, and nothing about it is scoped to a challenge. That is why a terminal bound to no challenge may still carry one, and why the same script serves whatever variant the student turns out to have. The challenge enters only through what the script does — its generated preamble prepares the challenge directory, which is the edge from the terminal to preparation above — never through what the script is.
The comparator is a channel in its own right and not a detail of the generator. It is separately stored and optional — a challenge whose output the platform’s standard comparison can score needs none, and most will not. But an authored comparator necessarily recognises a correct answer, so it sits on the same side of the boundary as the generator, and every rule stated here for grading material covers both.
The asymmetry in the “readable by the student” row is the reason for the split. A generator contains, by definition, the means of producing the expected answer, and an authored comparator the means of recognising it. A workbench bundle and a shell-initialization script land in territory the student fully controls. Merging either of the first two with either of the last two would publish the answer key.
3.3 The Workbench Bundle
A workbench bundle is a set of files authored against a single challenge variant and delivered to a student who was dealt that variant, for a challenge that has opened for them.
An entry is a regular file. Symlinks, hard links, device nodes, and bare directory entries are not carried in a bundle, and an entry that is one is refused at authoring — instructors assemble bundles from archives, and archives carry all of these. Directories come into existence implicitly from the paths of the entries placed inside them, which means a bundle cannot author an empty one (§4).
Each entry has:
- A relative path within the challenge directory. It may name subdirectories. It may not be absolute, may not ascend out of the challenge directory, and may not name a reserved file.
- Content, delivered exactly as authored. Content may be text or binary. Text entries are authored directly; binary entries are attached, and the authoring tool reports their name, type, and size rather than pretending to edit them.
- An executable flag, indicating whether the file should be runnable when placed. It applies to regular files, which is all an entry can be. Defaults to not executable.
An entry’s path, its content, and its executable flag together are what the entry is, so two bundles differing only in a flag are two different bundles. Identity has to cover the flag rather than the content alone, or a bundle marking a helper script runnable would be indistinguishable from one that does not, and publication would treat them as the same material.
Entry order is not observable and carries no meaning. Two entries may not share a path, and — following ARFC-1018 — two paths are the same path only when they are the same sequence of characters. Two entries differing only by case are therefore distinct paths, and a bundle containing such a pair is refused at authoring: student storage distinguishes them, but the pair is a reliable source of confusion and of breakage anywhere the material is later copied, and refusing costs an instructor nothing.
Reserved files. An entry may not name any of:
- the question statement;
- the platform’s own record for the directory, which holds both what it has placed and the identifiers by which the directory knows its own challenge (§3.4) — an entry here would leave the student unable to prepare, test, or submit from that directory at all;
- the student’s own configuration overrides;
- any name matching the preserved-copy form, which is the original name followed by a reserved suffix and, where an earlier copy already exists, a number.
The reasons differ — some are the platform’s files and some are the student’s — but the effect is the same: an entry naming one is refused at authoring, and the authoring tool names both the entry and the rule.
Limits
A workbench bundle is bounded three ways: stored size, total expanded size, and entry count.
Each bound is its own figure rather than one inherited from another channel. That is the platform’s existing convention — a grading bundle’s ceiling was deliberately made its own figure rather than borrowing the one used for delivering content, precisely so the two could diverge — and it is the right convention here, because the channels are sized against different things. A grading bundle expands inside a disposable sandbox; a workbench bundle expands into a student’s home storage on shared infrastructure and stays there. The workbench figures are therefore sized against student storage quota, not against sandbox capacity.
Expanded size is measured, never declared, in both places it is checked. A bundle’s own account of how large its contents are is under the authoring side’s control and is not trusted anywhere. At authoring the bundle is expanded and measured, which is also what produces the size and file count the authoring tool reports to the instructor as they assemble it. At materialization it is measured again, as bytes are written.
Measuring in both places is what keeps the two checks from disagreeing. Were authoring to accept a bundle on its declared size that materialization then rejected on its real one, every student would fail preparation identically, on a bundle whose author passed every authoring check.
Why bounds at all, given that the author is a trusted instructor: the platform already bounds expansion and entry count for grading bundles authored by the same instructor — though it does so as they are unpacked for grading rather than as they are authored, so measuring at authoring is genuinely new here rather than merely inherited. It bounds them because a bundle is an archive and an archive’s expansion is not implied by its stored size. The argument that a trusted author needs no bound would equally retire the stored-size limit, which nobody proposes retiring. The student’s storage quota is a real backstop for bytes, but it bounds bytes only and not file count, so it cannot stand in for the entry-count bound.
Which challenges may carry one
Every challenge kind may carry a workbench bundle on any of its variants, not only program-graded ones, and the access path is identical in every case — because it was never the grading strategy that gave the student a directory. A challenge’s working directory is created when the challenge is prepared, and a lesson terminal bound to that challenge is what usually takes the student there. Binding a terminal to a challenge is already permitted for any challenge kind.
The clearest case is not a programming exercise at all. A cipher challenge places an image in the student’s workbench; they examine it with the tools in the terminal, and they type the recovered plaintext as the answer to an answer-based challenge in the lesson. Nothing about that exercise is program-graded, and all of it depends on a file arriving in a directory the student can reach.
That dependency creates one authoring hazard worth catching: a challenge may carry a workbench bundle that no terminal in the lesson points at. The authoring tool warns when it finds one. It is a warning rather than a refusal because the terminal is not the only route — a student can enumerate their challenges and prepare one directly (§3.11), and some courses will teach exactly that.
A challenge with no workbench bundle behaves exactly as a challenge behaves today.
Relationship to the submission file. A program-graded challenge already declares the file name the student is expected to submit. When a bundle places no file by that name, the authoring tool warns the instructor — this is very often a mistake. It is a warning and not a refusal, because an exercise that asks the student to create the file from nothing is a legitimate thing to assign.
The question statement is written once. It is a rendering of the task rather than authored files, so it is not one of P1’s channels and it has a rule of its own: preparation writes it when the directory has none, and leaves it alone thereafter. It needs no more than that, because the task cannot change within an assignment (§3.6) — the statement renders from the student’s variant, and their variant is settled. A student who annotates their copy therefore keeps their annotations indefinitely, and a directory prepared before this design keeps whatever statement it already holds. The one cost is that a later improvement to how statements are rendered does not reach directories that already have one, which is a fair price for never displacing a student’s notes.
3.4 The Challenge Directory
A student’s challenge directory is identified by two stable things together: the assignment it belongs to and the challenge’s own reference within it. Both are needed. The reference alone is not unique to a student’s directory, because the same reference names the same challenge in every assignment made from that publication — a student retaking a course has two directories with the same reference and the same display names, and nothing but the assignment distinguishes them.
The directory’s own record holds both identifiers, alongside what the platform has placed there. Its location is still derived from display names when it is first created, and nothing here proposes changing that; what changes is that the recorded identifiers, rather than the names on the way to it, are what find it and what tell it which challenge it holds.
This matters because every display name above a challenge is editable at any time. Were the directory found by walking realm, term, course, section, assignment, and challenge names, then an instructor renaming an assignment in week three would send every student to a newly created empty directory, with a pristine workbench and their own program stranded elsewhere. No file would be destroyed and it would read to the student as total loss.
Resolution runs both ways, and both must use the recorded identifiers. Locating a directory from a reference is one direction; the other is a command already standing in a directory working out which challenge it is in, which is how preparation, testing and submission all resolve. If that direction matches on display names, a rename breaks every one of them from a directory that already exists. Renaming is safe only when both directions are identifier-based; making one of them so and not the other would be worse than useless, because it would look safe and not be.
Directories that predate the record adopt it on first contact. Existing directories record display names and, at most, a challenge reference; none records a stable assignment identifier, and directories created by a bulk preparation of a section’s tree record no reference either. Such a directory resolves once by its display names — exactly as every command does today — and writes the identifiers it thereby learned into its record, reporting that it has done so. From then on it resolves by identifier like any other. Display-name resolution is therefore permitted exactly once per directory, to establish the record, and never as a fallback afterwards; a directory that has a record and fails to resolve from it is an error, not an invitation to guess from names.
Adoption cannot rescue a directory whose display names changed before it adopted: name-based resolution fails there, which is today’s behavior and no worse. The student’s route back is to locate the challenge by reference (§3.11), which creates or finds the directory and records the identifiers.
One case falls outside this rule. A challenge attached to an assignment by hand, rather than through a lesson publication, has no reference at all. Such a challenge cannot be located by reference, cannot carry a workbench bundle, and continues to behave exactly as it does today. §4 records this.
3.5 Materialization
Materialization is the act of writing a workbench bundle’s files into a prepared challenge directory. It happens as part of preparing the directory.
The bundle is checked before any file is. Preparation compares the workbench bundle identifier for the student’s variant against the identifier recorded in the directory. When they match — every repeat preparation, which is every terminal open after the first — nothing is placed, no file is read, and preparation completes silently.
Opening a terminal into an already-prepared challenge must not get slower as a student’s enrolment grows. The directory records the assignment its challenge belongs to, so the check consults that one assignment’s challenge listing directly instead of searching every section and assignment the student is enrolled in. Without that, the cost of the check would scale with a student’s enrolment at the most latency-sensitive moment in the product — the very cost §3.7 refuses to pay for a smaller benefit.
A path is contained when every directory component of it is a real directory the platform created or verified, and nothing along it is a link. Containment is checked on every branch below, including the branch that writes to a path the platform has not touched before — that branch is the one most likely to meet a link the student put there, and a check that ran only where the platform had already been would miss exactly the case it exists for.
A path is recorded in one of four states. Unrecorded — the platform has never considered it. Begun — a placement started. Placed — a placement finished, together with what was placed. Left to the student — the platform found the student’s own file there and did not place anything. Three would not do: an entry recorded merely as “placed” and now missing is indistinguishable between “the student deleted it” and “the write never landed,” which demand opposite responses; and a path the platform never wrote must not be recorded as one it did, or a later deletion there would be read as the student discarding the platform’s work rather than their own.
When the identifiers differ, for each entry:
IF the path is not contained THEN
leave it untouched and report it — never write through a link
ELSE IF the path is recorded as placed OR as left to the student THEN
leave it as it stands — it is the platform's completed work, or the student's own
ELSE IF the path is recorded as begun THEN
IF nothing is at the path, or what is there matches the entry THEN
place the entry — a previous placement was interrupted before it finished
ELSE
preserve what is there, then place the entry
END IF
ELSE
IF nothing is at the path THEN
place the entry
ELSE
record the path as left to the student, and report it once
END IF
END IF
The begun branch does not overwrite blind. An interrupted placement may have written the file before it was interrupted, and the student may have opened and edited it in the meantime — §3.13 leaves them a working session in which to do exactly that. So the branch places only when the path is empty or already holds what the entry would write, and otherwise preserves the student’s version first, exactly as an explicit restore does. This is the one case in which the platform completes work of its own accord over something a student has touched, and P3 names it as such.
A placement is recorded as begun before the file appears and as placed after it does. The ordering is what makes an interruption recoverable: a placement interrupted at any point leaves the path begun, which the next preparation completes. The reverse ordering would leave a placed file unrecorded, which every later preparation would classify as the student’s, permanently and invisibly.
Only one preparation of a challenge directory proceeds at a time. A preparation that finds another in progress waits for it. It may take over only by invalidating the other’s claim, so that the earlier preparation can no longer place or record anything if it later revives; a claim is invalidated when it has not been renewed, never merely because a wait has been long. A live preparation writing a large workbench must be allowed to finish. Waiting alone would let a session that died mid-preparation leave the directory permanently unpreparable, and taking over without invalidating would let two preparations write the same paths and record conflicting accounts of them — so both halves are required.
A directory with no record is read by what is standing in it. If nothing is present at any of the bundle’s entry paths, this is a first preparation and it proceeds normally. If files are standing at entry paths, every such file is the student’s, is recorded as left to them, and is reported once — the common case at adoption, where students created the file themselves (§6). Recording the determination is what stops the report recurring at every terminal open, and it is safe to record because a bundle does not change within an assignment (§3.6), so there is no later correction the record could be suppressing.
Four things follow, and each is deliberate:
- A file at a completed path is never overwritten, whether the student edited it a minute ago or last term.
- A file the student has deleted is not recreated. Deletion is an edit, and recreating it would make deletion impossible. §3.11 gives an explicit way to ask for it back.
- A placement that was interrupted is completed, and completed carefully — which is why the recorded states are four and not two.
- The platform never writes through anything that is not a regular file, at the entry’s path or above it, on any branch.
flowchart TB
Start(["Prepare challenge directory"]) --> Find["Find the directory by its recorded<br/>assignment and challenge reference,<br/>adopting or creating the record if absent"]
Find --> One["Claim sole preparation of this directory"]
One --> QS{"Directory has a<br/>question statement?"}
QS -->|No| QW["Write it"]
QS -->|Yes| Cmp
QW --> Cmp{"Variant's bundle identifier<br/>differs from recorded?"}
Cmp -->|No| Done(["Ready — nothing placed, nothing read"])
Cmp -->|Yes| Loop{"For each entry"}
Loop --> Con{"Path contained?"}
Con -->|No| Link["Leave untouched, report"]
Con -->|Yes| St{"Recorded state"}
St -->|"Placed / left to student"| Own["Leave as it stands"]
St -->|Begun| Chk{"Empty, or matches the entry?"}
Chk -->|Yes| Place["Record begun, write, record placed"]
Chk -->|No| Pres["Preserve, then place"]
St -->|Unrecorded| Free{"Anything at the path?"}
Free -->|No| Place
Free -->|Yes| Note["Record as left to the student,<br/>report once"]
Place --> Loop
Pres --> Loop
Link --> Loop
Own --> Loop
Note --> Loop
Loop -->|Done| Rec["Record the bundle identifier"]
Rec --> Done
style Own fill:#fff3cd,stroke:#664d03,color:#1b1a17
style Note fill:#fff3cd,stroke:#664d03,color:#1b1a17
style Link fill:#f8d7da,stroke:#842029,color:#1b1a17
style Done fill:#d1e7dd,stroke:#0f5132,color:#1b1a17
3.6 The Workbench and the Assignment
A workbench is fixed for the life of an assignment. An assignment is created from one lesson publication and stays bound to it, and each student’s variant is settled within that publication. Republishing a lesson produces a new publication, which existing assignments do not follow. So the workbench a student receives is the one their assignment was created with, and it does not change under them.
This rests on the workbench participating in a variant’s identity, which §3.9 states as a rule: a variant differing from another only in its workbench is a different variant. Without that rule an instructor could correct a starter file, republish, and have the new publication bind the old variant — and therefore the old workbench — because nothing else about the challenge had changed. The grading bundles already work this way, and the workbench joins them.
This has a consequence worth stating plainly, because an instructor will otherwise assume the opposite: an instructor cannot correct a starter file for students who are already working. Fixing a typo in a workbench and republishing the lesson affects assignments made from the new publication, and nothing else. Reaching the students already working means assigning the new publication to them — a decision with its own consequences for their attempts and their gradebook, and therefore a decision the instructor should be making deliberately rather than as a side effect of republishing.
The design leans into this rather than working around it:
- Materialization is a first-preparation event. After a directory records its bundle identifier, later preparations match it and do nothing. The per-entry rules in §3.5 exist for the first pass and for completing an interrupted one, not for a stream of corrections that cannot arrive.
- Nothing is ever withdrawn either. A file placed for an assignment stays placed for that assignment, so a student never finds a helper removed from under them mid-term.
- The never-overwrite rules still earn their place. They govern the adoption population, where students already hold files at paths a new bundle names, and they are what the restore flags preserve against (§3.11).
Re-pointing an existing assignment at a newer publication would give instructors the mid-term correction they will eventually ask for. It is a capability about assignments rather than about workbenches, it needs its own answers about students mid-attempt, and it is not proposed here (§4).
3.7 Shell Initialization
A lesson terminal may name a shell-initialization script: a single authored script, run in the student’s session when the terminal opens, before the interactive shell begins.
It remains a single script rather than a bundle, and the reason is worth recording, because the symmetry with the other channels is tempting:
- Everything multi-file that shell initialization would carry is better delivered by the workbench bundle, which places it on disk once instead of re-running it at every terminal open. A helper the instructor wants available is a workbench file that the initialization script invokes.
- The script’s essential work — locating the challenge and entering its directory — is small, single-file by nature, and load-bearing.
- Making it a bundle would add a resolution step to the most latency-sensitive moment in the student’s experience, in exchange for a capability the workbench bundle already provides.
The script is composed of two parts:
- A generated preamble, which locates the challenge, prepares its directory (which materializes the workbench bundle, per §3.5), and enters it. The authoring tool does not offer it for editing.
- An authored remainder, which the instructor writes. It runs after the preamble, in the challenge directory, with the workbench already in place.
A failure in the authored remainder must not cost the student their shell. A remainder that exits non-zero, or refers to something undefined, leaves the student in a working interactive shell with the failure reported — not in no shell at all. This is a change: the initialization script presently runs under semantics where the first failing command ends the session before the interactive shell begins, which is safe only while every line of that script is composed by the authoring tool. Version 1 puts instructor-written code in that position, and a mistyped command in it must degrade one student’s convenience rather than deny an entire section a terminal. Design Goal 6 protects the preamble from being edited; this protects everyone from the part that is edited.
The content the platform runs is pinned: a lesson names a specific published version of a script, and that version always yields the same bytes, so a script cannot be edited into something else underneath a lesson that has already been published. What the pin does not do is prevent the reference itself from being changed or the published file from being deleted — an instructor who deletes it still breaks that lesson’s terminal, and no design here changes that.
What a student must be able to read to obtain the script is a constraint on this channel, not an incidental detail. The script lives in the publishing instructor’s own space, and a student’s session must read it there. Whatever authorization makes that possible must not also reach the authored grading sources that sit in the same space — an instructor’s working copy of a generator is not published material, but it is in the neighbourhood, and a grant wide enough to fetch a script from a publisher’s area must not be wide enough to browse it. §5 states this as a boundary the implementation must hold.
If the preamble cannot locate the challenge — most commonly because the lesson has not opened for the student’s section, or the student is not on its roster — or if preparing the directory fails, it says so and the session continues in the student’s home directory. The authored remainder still runs. An instructor’s script must therefore not assume it is in a challenge directory; the authoring tool says so, and the preamble reports which case occurred.
A terminal that names no challenge may still carry an authored initialization script. In that case there is no preamble and no workbench.
3.8 The Grading Bundles
Two channels grade, and their runtime contracts are unchanged.
- The generator produces the input the student’s program receives and the expected output the comparison is made against. It has a fixed entry point that the sandbox invokes.
- The comparator scores the student’s output against that expected output. It is optional: a challenge whose output the platform’s standard comparison can score needs none. When one is present, it runs in the same sandbox on the same terms.
Both belong to a challenge variant — the same level the workbench occupies (P7), and for the same reason: a variant is what a student was actually dealt, so it is what determines both the question they answer and the material used to score it.
What changes is authoring, and it changes differently for the two. The generator is already an archive and already unpacked as such, so version 1 widens its authoring from its entry point alone to the set of files it already is. The comparator has no authoring surface at all today, so version 1 introduces one; the platform already stores and runs a comparator when a variant carries it, and this is what lets an instructor supply it. Entry points keep their established names and roles; other files sit beside them, in subdirectories if the instructor wants them there, and are present when the entry point runs.
Entries in both remain text in version 1. The workbench admits binary content because a cipher image is the exercise; a generator or comparator needing a binary fixture is a plausible future case but not a present one, and admitting binary content to every channel at once would widen three authoring surfaces for one demonstrated need.
No student-facing behavior changes, and no grading behavior changes.
3.9 Content Identity and Publication
Each bundle is identified by an identifier derived from its content. Two bundles with identical content in the same channel have the same identifier and are the same bundle.
A variant’s identity includes the bundles it carries. Two variants differing only in their workbench, their generator, or their comparator are different variants. The grading bundles already work this way, and the workbench joins them for the same reason: a variant is meant to name everything a student receives, so a change to any of it must be a change to the variant. This is what makes §3.6’s account of republication true — correct a starter file and nothing else, and the republication still binds a variant that carries the correction.
Identity is scoped to the channel. A workbench bundle and a generator bundle with byte-identical content are two different bundles, and neither is reachable by asking for the other. This is not incidental — it is the rule that keeps §5’s boundary intact. Without it, an instructor who puts one helper file into both the generator and the workbench so students may read it would make the two the same object, and grading material would become retrievable through the workbench’s new retrieval path.
Nothing retrieves grading material, and version 1 adds no way to. The generator’s and comparator’s unreadability today is not a policy applied to requests that ask; it is that nothing can ask. Version 1 introduces retrieval for the workbench channel and for no other, and a future proposal that wants to retrieve grading material should have to argue against this sentence rather than discover an undocumented convention.
Content-derived identity gives three properties the authoring experience depends on:
- Publishing unchanged material is free. Publication compares the identifier of what is about to be published against what is already stored; when they match, nothing further happens.
- Reuse across sections and terms costs nothing. Four assignments made from one publication refer to one bundle.
- Delivery is unambiguous. A student’s variant names exactly one workbench bundle identifier, and retrieval is by that identifier rather than by challenge, so what a student should have received is always a determinable fact.
Publication of a lesson resolves every channel of every variant. A failure in any one of them fails the publication as a whole, with the failing channel, challenge, and variant named; a lesson is never published half-provisioned.
3.10 API Endpoints
| Method | Path | operationId | Description |
|---|---|---|---|
| POST | /v2/workbench-bundles |
createWorkbenchBundleV2 |
Store an authored workbench bundle and return its content identifier. Content already stored returns the existing identifier. |
| GET | /v2/workbench-bundles/{bundleID} |
readWorkbenchBundleV2 |
Retrieve a workbench bundle the caller is entitled to receive. |
| GET | /v2/users/{userRef}/challenges |
listUserChallengesV2 |
Enumerate the caller’s own challenges, for merlin show challenges. userRef accepts only the self-reference, following the established form for self-only collections rather than taking an identifier a caller could substitute. Like every student-facing read it is answered within one asserted realm, which is why merlin show challenges takes a realm the way its sibling show commands do. |
The per-assignment challenge listing — the one preparation reads — must additionally report the workbench bundle identifier for the caller’s variant, and must withhold challenges that have not opened for the caller (§3.12). Both are changes to what an existing operation returns rather than additions to it, and one of them narrows its result set, so they follow the platform’s convention for versioning an operation whose behavior changes; whether that means a new version of the operation or an amendment to the current one is an implementation decision this ARFC does not presume. The same listing should accept the self-reference in place of a student identifier, so that a student’s own tooling need not first ask the platform who it is — without which §3.5’s cost claim is false, since every repeat preparation would cost two exchanges rather than one.
The split between the two listings is deliberate, and it is about consequence rather than cost. The per-assignment listing is read for one assignment the directory already names, and reading it settles which variant a student has been dealt — a commitment, not merely an observation. Enumeration answers a broader and lighter question, what am I assigned?, and must not deal a student a variant for every assignment in every section they are enrolled in as the price of asking it.
No route retrieves a generator or comparator bundle, and none deletes a stored bundle of any kind. Both absences are design decisions rather than omissions — see §3.9 and §3.12.
3.11 Command-Line Surface
One command is added:
| Command | Behavior |
|---|---|
merlin show challenges |
Lists the caller’s own challenges — the challenge, the assignment it belongs to, and the identifiers that name both. It joins the existing show realms / courses / sections / terms family: it takes a realm like they do, and like them it only reads. Listing does not deal the caller a variant. |
Without it, Design Goal 4 and P2 are aspirations: preparing a challenge from a plain shell requires naming it, and that reference appears on no student-facing surface today. It is also a gap independent of this proposal — a student currently has no way to enumerate their own assigned challenges from a shell.
Two existing commands gain behavior:
| Command | Change |
|---|---|
merlin locate challenge --ref <ref> [--assignment <assignment>] |
Finds or creates the challenge directory and materializes the workbench bundle per §3.5. This is the command that takes the identifiers merlin show challenges reports, and the one a student uses to rebuild a directory they no longer have. --assignment is required only where the reference names a challenge in more than one of the caller’s assignments, which happens when a student holds two assignments of the same lesson (§3.4); the refusal names the assignments to choose from. |
merlin prepare challenge |
The same materialization, on the same rules, resolving the challenge from the identifiers recorded in the current directory rather than from its display names (§3.4). |
Two flags are added to merlin prepare challenge, which resolves the challenge from where the student is standing and so needs no identifier:
| Flag | Behavior |
|---|---|
--restore-workbench |
Restores authored files that are absent — the ones the student deleted. It never touches a file that exists, so it cannot lose work. |
--force-restore-workbench |
Additionally restores files the student has changed, returning them to the authored version, preserving theirs first. |
Both flags examine every entry, whether or not the bundle identifier matches. §3.5’s identifier comparison is a shortcut for automatic preparation, where a matching identifier means the work is already done. It is not a rule about what an explicit request may look at — and it must not be, because a workbench does not change within an assignment (§3.6), so the identifier always matches by the time a student wants a file back. A restore that inherited the shortcut would report success and do nothing, leaving §3.5’s deletion rule with no remedy at all.
Neither prompts for confirmation. Running a named flag, from inside the challenge directory, is an unambiguous statement of intent, and a prompt on top of it would mostly teach students to dismiss prompts. What protects the student is not the prompt but the preservation. A student who has deleted the challenge directory outright is outside both flags’ reach, since they resolve from where the student is standing; merlin locate challenge rebuilds it, which is the other reason the identifiers have to be discoverable.
Preservation. Before replacing a file the student has changed, their version is kept under the original name followed by a reserved suffix, numbered where an earlier copy already exists, so a later restore never displaces what an earlier one preserved. Preserved copies are the student’s files: they count against their storage, the platform does not remove them, preparation reports whenever it creates one, and a bundle entry may not name one (§3.3).
merlin test and merlin submit resolve a challenge the same way these flags do, from where the student is standing, and this proposal changes neither except through §3.4’s identifier-based resolution, which fixes them across a rename.
3.12 Authorization Model
Scheduling governs when access begins. A student may enumerate, prepare, and receive the workbench for a challenge once it has opened for a section they are enrolled in — not merely once it has been released to that section. Where availability is set per section, so that two sections run the same lesson to different timetables, that timetable is what a student’s shell sees. A challenge that is released but not yet open does not appear in merlin show challenges, and naming its identifiers directly does not prepare it; a known reference is not a way around a schedule. Per-student overrides to a section’s dates exist in the platform’s data but are not honoured anywhere today; this design does not begin honouring them, and a proposal that does should cover every surface at once.
Access does not lapse while the assignment stands. Once a challenge has opened, a student keeps the question, the directory, and the workbench — after any due date, after the term, for as long as their enrolment and the assignment’s released state stand. This matches how the rest of a lesson’s content behaves, and it is the right default for material a student may want to revisit: nothing is gained by taking a worked exercise away from the person who worked it. An assignment that is later deprecated, withdrawn or archived is no longer released, and access follows the assignment; that is existing behavior and this design does not change it.
Nothing here closes a submission window. Submission deadlines are deferred in their entirety (§4): version 1 adds no deadline check and removes none, and merlin submit behaves exactly as it does today.
Authoring. An instructor who may publish a lesson may author, replace, and dissociate any of that lesson’s four channels. No separate privilege distinguishes the channels; the ability to publish the lesson is the ability to determine what its challenges contain.
Storing a bundle is its own permission. A bundle is stored before it is attached to anything, so there is no lesson in whose authorization the act could be grounded; storing therefore requires a permission of its own, held by those who author lessons, exactly as storing grading material does today. This is the one place where the rule that publishing the lesson is the authority over what its challenges contain does not reach, and saying so is better than implying an authorization that could not be checked.
Dissociation is not deletion. Removing a workbench bundle from a challenge means publishing without it, which affects assignments made from that publication onward (§3.6). Destroying a stored bundle is a different operation and is not offered to instructors. Because identity is content-derived, one stored bundle may be referenced by other challenges, other terms, and other instructors’ lessons that published identical content; and because §3.13 fails preparation when a referenced bundle cannot be retrieved, an instructor-facing deletion would convert a transient failure into a permanent one for every assigned student.
This withholds a capability from instructors; it does not assert that stored material can never be removed. An institution obliged to erase personal data from a fixture, or to take down infringing material, needs a path to do so. Version 1 does not specify one, and a later proposal that adds an administrative removal path is completing this design rather than reversing it.
Receiving a workbench bundle. A student may retrieve the workbench bundle for the variant they were dealt, for a challenge that has opened for them, in a section they are enrolled in, in the realm the request asserts. A bundle belonging to another variant of the same challenge is as inaccessible as one belonging to another student’s course.
Receiving grading material. No student may retrieve a generator or a comparator bundle in any circumstance. There is no scope, no role, and no assignment state that grants it, and per §3.9 there is no route that would carry it. This holds for the comparator exactly as for the generator: an authored comparator recognises a correct answer, and it belongs on the grading side of the boundary whether or not it literally contains the answer.
Instructor retrieval. An instructor may retrieve any workbench bundle they stored, or that belongs to a lesson they may publish — the first so that a bundle can be read back before it is attached to anything, the second so the authoring tool can load a lesson for editing. Grading bundles are retrievable by no one: the authoring tool holds its own authoring state and does not read published grading material back, so nothing about editing a lesson requires such a route to exist.
A student’s own workbench. Everything materialized into a student’s challenge directory is theirs to read, edit, and delete, as are the preserved copies §3.11 creates. The platform asserts no ownership after placing, and §3.5’s rules are the entirety of its later interest in the contents.
3.13 Error Handling
| Situation | What the user sees |
|---|---|
| A bundle entry is not a regular file, names an absolute path, ascends out of the challenge directory, collides with another entry by case, or names a reserved file | Refused at authoring, naming the entry and the rule |
| A bundle exceeds its stored-size, expanded-size, or entry-count bound | Refused at authoring, naming the bound and the measured value |
| A challenge carries a workbench bundle that no terminal in the lesson reaches | Warning at authoring, naming the challenge; publication proceeds |
| A program challenge’s bundle places no file matching the declared submission file name | Warning at authoring; publication proceeds |
| Materialization finds the student’s own file at an entry’s path | Recorded as theirs and reported once, naming the file; not reported again |
| Materialization finds a path whose leaf or any parent is a link, or which the student has replaced with a directory | Reported, naming the file and saying which it found; the path is left untouched and preparation succeeds |
| Materialization completes a placement a previous run began | Completed silently where nothing was disturbed; where the student had changed the file, their version is preserved and the preservation reported |
| Measured expansion exceeds the bound during materialization | Preparation fails, naming the bound; entries already placed remain and are recorded, so a retry resumes rather than restarts |
| Materialization cannot write a file (quota, permissions) | Preparation fails, naming the file and the cause; entries already placed remain and are recorded |
| A directory resolves by display name for the first time | Its identifiers are recorded and the adoption reported (§3.4) |
| A directory has a record but cannot resolve from it | Preparation fails, naming what it could not resolve; it does not fall back to display names |
| Another preparation of the same directory is in progress | This one waits; it proceeds only if the other’s claim goes unrenewed, never merely because the wait was long |
| A challenge is released but has not yet opened for the caller’s section | It does not appear in merlin show challenges, and preparing it is refused, indistinguishably from a challenge that is not the caller’s |
| A reference names a challenge in more than one of the caller’s assignments | Refused, listing the assignments, so the caller can name one (§3.11) |
| A challenge was attached to its assignment by hand and has no reference | It cannot be located by reference and carries no workbench; everything else about it behaves as today (§3.4) |
| The authored shell-initialization remainder fails | Reported; the student is left in a working interactive shell (§3.7) |
| A terminal’s challenge cannot be located | Reported; the session opens in the student’s home directory and the authored initialization remainder still runs |
| A workbench bundle cannot be retrieved, and none is in place | Preparation fails, naming the cause. The directory and question statement, being idempotent, remain; the directory is not reported as ready, and retrying later succeeds |
| The bundle identifier cannot be checked, and a workbench is already in place | Preparation succeeds with a warning that currency could not be verified |
| The bundle identifier cannot be checked, and no workbench is in place | Preparation fails, as for an unretrievable bundle: a challenge that has no workbench and one whose workbench is unknown must not be confused |
The last three rows differ deliberately. A missing workbench fails, because a directory that looks prepared and is missing its material is the worse outcome — for a cipher challenge whose image did not arrive, the question renders perfectly and the exercise is merely impossible, which reads to the student as their own failure rather than the platform’s. But an unverifiable workbench is not a missing one: the files are present and, because a workbench does not change within an assignment, they are the right ones. Failing loudly is right when there is nothing to work with, and wrong when there is.
A terminal whose preparation fails still opens. The session begins in the student’s home directory with the reason reported, so the student has somewhere to retry from.
4. Version 1 Scope
Included
- Workbench bundles on any challenge variant, of any challenge kind: authored regular files, text or binary, with relative paths and an optional executable flag — all three part of a bundle’s identity — and the three bounds of §3.3, measured at authoring and again at materialization.
- A variant’s identity extended to cover the bundles it carries, so that correcting a workbench and republishing binds the correction (§3.9).
- Comparator authoring introduced, and generator authoring widened from one file to many; both attached to the variant, both covered by every rule stated for grading material (§3.2, §3.8, §3.9, §5).
- Challenge directories identified by a recorded assignment identifier and challenge reference, resolving in both directions from that record, with a once-per-directory adoption path for directories that predate it (§3.4).
- Materialization during challenge preparation, with the rules of §3.5 — identifier comparison first; four recorded path states, so an interrupted placement is completed carefully and a student’s own file is never mistaken for the platform’s; sole preparation enforced by invalidating an unrenewed claim; and containment checked on every branch.
- Repeat preparation whose cost does not grow with a student’s enrolment, which requires the self-reference on the per-assignment listing (§3.10).
- Authoring warnings: a program challenge whose bundle places no file matching its declared submission file name, and a challenge whose workbench no terminal in the lesson can reach.
- Authored shell-initialization remainder, with a generated preamble the authoring tool does not offer for editing, and a failure in the remainder that cannot cost the student their shell (§3.7).
- Channel-scoped content-derived bundle identity, with publication skipping unchanged content, and no retrieval of grading material (§3.9).
- Workbench bundle identifier reported on the per-assignment challenge listing, and a separate enumeration that does not deal a student a variant.
- Access that begins with per-section scheduling rather than release, and does not lapse while the assignment stands (§3.12).
merlin show challenges, so a student can name their own challenges.--restore-workbenchfor deleted files and--force-restore-workbenchfor changed ones, examining every entry regardless of the bundle identifier, with specified preservation and no confirmation prompt.- A question statement written when a directory has none and left alone thereafter (§3.3).
Excluded
- Correcting a workbench for students already working. A workbench is fixed for the life of an assignment (§3.6). Re-pointing an existing assignment at a newer publication is the capability that would change this; it concerns assignments rather than workbenches, needs its own answers for students mid-attempt, and is not proposed here.
- Multi-variant fan-out. The workbench, generator and comparator all attach to a challenge variant (P7), which is the level that will be correct when a publication can bind several variants and deal them across a section. Today a publication binds one, so every student dealt a challenge receives the same workbench. This is a forthcoming platform capability, and this design is deliberately shaped for it; the thirty-students-thirty-ciphers exercise becomes possible when it lands, and needs nothing further from this ARFC.
- Submission deadlines. Version 1 adds no deadline enforcement of any kind and removes none. Due dates and late windows exist on assignments and are read only by listings; making them govern submission is a coherent feature in its own right — it touches every client that records an attempt, not only the shell — and is deferred to be designed as a whole.
- Workbenches on hand-attached challenges. A challenge attached to an assignment outside a lesson publication has no reference, so it cannot be located by one and carries no workbench (§3.4).
- Per-student date overrides. They exist in the platform’s data and are honoured nowhere; §3.12 does not begin honouring them.
- Lesson-level or course-level shared bundles. A file used by three challenges is authored three times in version 1. Sharing needs a scope and a precedence rule, and neither is worth settling before there is evidence of demand.
- Multi-file shell initialization, for the reasons in §3.7.
- Binary entries in grading bundles, for the reasons in §3.8.
- Instructor-facing deletion of a stored bundle, for the reasons in §3.12. An administrative removal path is deferred, not refused.
- Instructor visibility into a student’s workbench, including any count of which students have prepared a challenge. This is a question about observation of student work and belongs with the rest of that subject.
- Workbench history or diffing. The platform records what it placed so it can decide what it may touch. It is not a version control system for student work.
- Empty authored directories. A bundle’s directories come into being from the entries inside them, so a challenge wanting an empty directory ready for output must have the student or a script create it.
- Templating. Authored content is delivered byte for byte; there is no substitution of student, section, or challenge values into it.
5. Security Considerations
Answer-key confinement covers both grading channels. A generator can derive the expected output for any input — that is its function — and an authored comparator necessarily recognises a correct answer. Confining both to the grading sandbox is therefore not a hardening measure but the property that makes program grading meaningful. This design adds two channels that reach the student’s environment, and keeps them from touching either grading channel by two means: content identity is scoped per channel, so a helper file present in both a generator and a workbench does not become one object (§3.9); and nothing retrieves grading material at all, so there is nothing for a widened permission to widen onto.
Naming the comparator explicitly matters more than it might appear, and more so now that version 1 introduces its authoring. A design that spoke only of “the grader bundle” would leave an instructor who puts the expected output in a comparator outside every sentence written here, and a future reviewer with no rule to check that case against.
The shell-initialization channel reads from a publisher’s own space, and that grant must be narrow. A student’s session fetches the script from where the publishing instructor keeps it. The same space holds an instructor’s working material, including authored generator sources, which are not published but are adjacent. Whatever authorization lets a student’s session fetch a named, pinned script must not also let a student read that space generally; a grant sized to “any file in the realm” would defeat §3.9’s confinement without touching the bundle store at all. This is a constraint the implementation must satisfy, and it is stated here because the channel’s promotion to first-class status in this design is what makes it worth stating.
The workbench is student-readable by construction. Every file placed in a challenge directory is fully available to the student who owns it — binary content included, executable files included. For exercises whose subject is concealment, such as a cipher, an encoded artefact, or a forensics image, the workbench is the right home for the ciphertext and never for the key, the plaintext, or anything from which either can be recovered without doing the exercise. This is not a limitation of the channel but its definition: material a student must not be able to read is grading material.
A mistaken publication is a disclosure, not a mistake to be undone. An instructor who publishes answer material into a workbench bundle cannot retract it by dissociating the bundle, and no deletion available to them would help either — the files are already on the disks of everyone who prepared, and no platform action reaches them there. The remedy is to treat it as a disclosure: reissue the challenge if it matters.
Containment covers what an entry is, where it points, and what it passes through. An entry can only be a regular file, so a bundle cannot carry a link whose target escapes the challenge directory while its own path looks clean. Entry paths are validated at authoring and again at materialization. And materialization refuses to write through anything that is not a regular file at the entry’s path, or anything that is not a real directory above it — which closes the case where the link is the student’s own rather than the bundle’s, at the leaf or at any parent. Validating in both places is deliberate: authoring-time validation gives the instructor a good error, and materialization-time validation is what actually protects the student’s home directory, since it is the only one running on their machine.
Binary content changes nothing about trust. The platform does not interpret, execute, or inspect the contents of a workbench file. The executable flag makes a file runnable by the student, in their own session, at their own privilege — the same standing any file they create themselves already has.
Content that runs as the student. Shell-initialization scripts and executable workbench files run with the student’s own identity and privileges, in their own session, with no elevation. They can do what the student can do and nothing more. An instructor who authors a lesson is already trusted with the student’s learning environment; these channels do not extend that trust.
Enumeration follows the schedule, and commits the student to nothing. merlin show challenges is a new enumeration surface, and enumeration is more consequential than fetching one already-named challenge: it hands a student the complete list. Two properties contain it. It reports only what has opened for the caller’s section (§3.12), so a scheduled exercise is not reachable ahead of its date by a student who simply asks what they have. And it does not deal the caller a variant, so asking what one is assigned does not silently fix which version of it one will be graded against.
Student storage. Materialized files, and the preserved copies §3.11 creates, are the student’s files and count against their storage quota. The bounds in §3.3 exist so that an assignment cannot silently consume that quota, and because the quota bounds bytes rather than file count it cannot by itself bound what a bundle may place. A materialization that exceeds either the bound or the quota reports the cause.
Integrity of what was published. Because a bundle is identified by its content, and a variant’s identity covers the bundles it carries, what a variant was published with is a determinable fact rather than an inference from timestamps, and a dispute about what an exercise contained is answerable from the published record. What a particular student holds is a weaker claim: the only account of that lives in their own directory, is theirs to delete, and §4 deliberately keeps no server-side copy. The design answers “what did this exercise contain” and not “what does this student’s directory contain now.”
Rate limiting. Version 1 adds a store-and-return-identifier write, a retrieval keyed by a content identifier, and an enumeration a student’s shell may call at will. All three sit behind the platform’s per-request limits as any other endpoint does, and the retrieval in particular should be limited with the guessing concern below in mind: a caller who may ask repeatedly whether a given content identifier exists is in a different position from one who may ask once.
No new exposure through the per-assignment listing. That listing already carries the materialized question for challenges assigned to the caller, under the caller’s own enrolment. Adding a bundle identifier for the caller’s own variant exposes no relationship the caller could not already observe, and identifiers for other variants of the same challenge are not returned.
An adjacent concern this proposal does not introduce. A store-and-return-identifier endpoint lets a caller confirm a guess about content it does not hold, by observing whether their upload was already present. For the workbench this reveals nothing, since its content is student-readable by design. For the grading channels the concern is real, but that endpoint exists today and is unchanged here; it should be addressed on its own terms, and per-channel identity ensures a workbench upload can never confirm grading content.
Auditability. Authoring, replacement, and dissociation of a bundle are attributable to the instructor who performed them, as lesson publication already is.
6. Backward Compatibility
Existing lessons are unaffected. A challenge with no workbench bundle prepares as it does today: the directory and the question statement. A lesson terminal that names a startup script continues to resolve and run it. No existing lesson requires republication, and no existing assignment changes in any way.
Existing grading bundles are unaffected. A generator authored as a single entry point remains valid and is the degenerate case of a multi-file bundle. Nothing about how either grading bundle is stored, delivered, or invoked changes, and a challenge with no comparator continues to be scored by the platform’s standard comparison.
The tutorial this design was written for needs a step of its own. The published prime-numbers tutorial tells students there is “a terminal, already in the folder for this exercise, with primes.cpp waiting in it,” and its own instructions then have the student create that file by opening it in an editor. This ARFC makes the promise achievable; it does not make that tutorial accurate on its own, because the tutorial has no step in which the instructor authors a workbench. Adding one is a documentation change that should accompany this work.
Two behaviors visibly change for students, and the second is the more common at adoption. A student preparing a challenge that has a workbench bundle sees files appear that did not appear before — that is the feature. But a student who already created a file by hand at a path a new bundle also names keeps their own file: §3.5 records it as theirs and places nothing. The remedy is in their hands — --force-restore-workbench takes the authored version while preserving what they wrote — and because the determination is recorded, it is reported once rather than at every terminal open.
Existing directories adopt a record on first contact. A directory created before this design records display names and, at most, a challenge reference. Its next preparation resolves by those names one final time, records the stable identifiers, and reports that it has done so; afterwards it resolves by identifier. A directory whose display names changed before it adopted cannot resolve by name — which is today’s behavior, not a new failure — and the student’s route back is to locate the challenge by reference.
Commands resolve across a rename where they did not before. merlin prepare challenge, merlin test and merlin submit currently match a challenge from a directory by display name, so renaming an assignment or challenge breaks them from a directory that already exists. Once a directory holds its record they resolve by identifier instead. This is a fix, not a regression, and it is why P6 speaks of both directions.
A student’s shell now follows the section timetable at the opening edge. Where a challenge is released but scheduled to open later, it does not appear in merlin show challenges and cannot be prepared. This is a narrowing of an existing listing’s result set, not merely a new command’s behavior: the per-assignment challenge listing today returns rows on release alone. Institutions using per-section availability will find the shell and the lesson agreeing; those that do not see no change. Integrators are affected — see below.
Nothing changes about submission. No deadline is enforced today and none is added (§4). merlin submit behaves exactly as it does now.
The question statement is written once rather than on every preparation. Today it is rewritten each time a directory is prepared; under §3.3 it is written when the directory has none and left alone afterwards. Since the statement cannot change within an assignment, no student receives less current material than before, and annotations survive. The one visible difference is that a directory prepared before a later improvement to statement rendering keeps the older rendering.
A failing instructor-authored shell remainder no longer ends the session. Today a startup script whose command fails terminates before the interactive shell begins; that is safe only because the whole script is composed by the authoring tool. With instructor-written content in it (§3.7), the semantics change so the student keeps a working shell. Existing generated scripts are unaffected, being written not to fail.
Integrators. The per-assignment challenge listing gains a field, narrows its result set to challenges that have opened, and accepts a self-reference where it previously required an identifier. The narrowing is a behavioral change a client could notice, and §3.10 leaves the platform’s versioning convention to decide whether it lands as a new version of the operation. A new enumeration is added. No field is removed, renamed, or changed in meaning.
Degradation. A lesson published with a workbench bundle into an environment that cannot yet deliver it fails student preparation per §3.13 rather than silently omitting the workbench. That is the intended behavior and not a compatibility break.
7. References
Related ARFCs
- ARFC-1001 — URN-Based Asset Identifiers
- ARFC-1010 — Challenge Evidence and Scoring
- ARFC-1011 — Learning Objectives and Challenge Alignment
- ARFC-1018 — Case-Sensitive Asset Pathnames — the pathname-comparison rule §3.3 adopts
- ARFC-1019 — Exact String Comparison and Value Normalization
Related Platform Documentation
- Instructor tutorial: Designing a Lesson to Master Prime Numbers (
ayode-design-documentation,docs/instructor-tutorials/designing-a-lesson-to-master-prime-numbers.md) — the narrative this design makes achievable, and which needs a workbench-authoring step of its own (§6) - Orchestrated program grading — the challenge kind that motivates the workbench most directly
8. Author
ĀYŌDÈ Development Team Codermerlin Academy Architecture
9. Revision History
| Version | Date | Change |
|---|---|---|
| 0.1 | 2026-09-06 | Initial draft |
| 0.2 | 2026-09-06 | Binary bundle entries admitted for cipher and forensics challenges; workbench reachability stated and warned on; restore split into two flags; an unretrievable bundle fails preparation rather than degrading |
| 0.3 | 2026-09-06 | Materialization compares the bundle identifier before any file; provenance redesigned around recorded state rather than a stored copy; expansion and entry-count bounds reinstated, measured rather than declared; content identity scoped per channel; entries constrained to regular files; bundle deletion withdrawn in favour of dissociation; merlin show challenges added |
| 0.4 | 2026-09-07 | Deleted files no longer recreated by the materialization loop; challenge directories located by stable reference; expansion measured at authoring as well as at materialization; path containment extended to every component; enumeration separated from the listing that deals a variant; access bound to per-section scheduling; instructor retrieval scoped to workbench bundles |
| 0.5 | 2026-09-07 | The comparator recognised as a fourth channel, and the workbench, generator and comparator all attached to the challenge variant (P7); three recorded placement states; restore flags exempted from the identifier shortcut; sole preparation enforced by invalidation; the executable flag made part of bundle identity; a failure contract for the authored shell remainder |
| 0.6 | 2026-09-07 | Scoped to what the platform can deliver: a workbench is fixed for the life of an assignment (§3.6), replacing a re-materialization contract whose corrected-file outcomes had no delivery path. Multi-variant fan-out recorded as forthcoming rather than present. Submission deadlines removed entirely and deferred whole. Reference-based resolution extended to the directory→challenge direction. Directory identity stated as assignment plus reference |
| 0.7 | 2026-09-07 | A variant’s identity extended to cover the bundles it carries (§3.9), without which a workbench-only correction would republish and bind the old workbench — the remedy §3.6 depends on. An adoption path for directories that predate the record (§3.4): no existing directory records a stable assignment identifier, so the previous draft’s both-directions rule had no data to stand on and forbade the only fallback. The question-statement machinery removed: it defended against a change that cannot occur within an assignment, so the statement is now written once and left alone (§3.3), retiring a rule, an error row, a scope item and a false claim in the materialization diagram. A fourth recorded path state, so a student’s own file is not recorded as one the platform placed (§3.5). Sole preparation clarified to invalidate an unrenewed claim rather than a slow one (§3.5, §3.13). P1 qualified — the question statement is not a channel; P3 qualified — the interrupted-placement case is the one exception it has. Comparator authoring described as introduced rather than widened (§2, §3.8, §4). Hand-attached challenges, per-student date overrides and the deprecated status stated (§3.4, §3.12, §4). The shell-initialization channel’s read grant stated as a boundary constraint (§3.7, §5). Endpoint versioning of a narrowed listing left to platform convention (§3.10, §6). §6’s tutorial claim corrected: this ARFC makes the promise achievable, and the tutorial needs a workbench step of its own |