Working on the Wrong Stream, Twice in One Week
Two posts ago I built pull requests for a team of one; last post, a gate that stops bad builds being approved. This week I found the hole on the other side of the pipeline: nothing stopped me doing the work in the wrong place to begin with. Twice.
Quick recap of how Deep Ice is laid out in Perforce. Mainline sits at the top, then an epic stream, then a task stream, then one stream per sub-task. Every piece of work happens on a sub-task stream. Submitting straight into a task, epic or mainline is refused by a server trigger, and the only way up is a Helix Swarm review that, since the last post, also has to see a green build before it can be approved.
That's a lot of rails. It turns out they all sit at the end of the work.
The first time
I'd finished a sub-task on the controls screen — thirteen files of C++ and assets — and went to submit. Refused. I'd been working on the parent task stream the whole time, not the sub-task's own stream. The server trigger did exactly its job: it wouldn't let that work land on a task stream. But it only said so after a day's work was sitting in the wrong place.
Two of those thirteen files hadn't even been checked out. They were writable on disk, I'd edited them, and Perforce had no idea. They only turned up when I asked Perforce to compare the workspace against the depot.
The second time
A few days later, a smaller sub-task: four files. Same stream, same mistake, again two of them edited without a checkout. That was the point I stopped treating it as a slip and started treating it as a process gap. Once is a mistake. Twice in a week is a missing guard rail.
Moving the work without losing it
The move itself is mechanical, but there's one trap in it that has bitten me before: a shelf that looks like it holds your files but doesn't. Perforce will happily show a changelist description, and a "Shelved files" header, with nothing underneath. Revert the workspace on the strength of that and the work is gone.
So the order I used was:
- Copy every changed file to a plain folder on disk. Belt and braces.
- Open everything in a numbered changelist, including the files edited without a checkout, and shelve it.
- List the shelved files and count them. Thirteen files changed, thirteen files listed, or stop.
- Create a throwaway branch mapping from the task stream to the sub-task stream. A plain unshelve onto a different stream fails, because the shelf's paths aren't in the new stream's view; unshelving through a branch mapping translates them.
- Revert, switch the workspace to the sub-task stream, unshelve through the mapping, and let Perforce auto-merge.
- Compare every file against the disk copy before deleting anything.
That last step paid for itself: four new source files came back "different". The difference was a byte-order mark Perforce adds to UTF-8 text files — the same one the rest of the codebase already has — not a change in content. I'd rather spend thirty seconds proving that than assume it.
Moving the "no" to the first keystroke
The real fix isn't a better recovery routine. It's making the mistake impossible to make in the first place.
Perforce's permissions table has a right called open. It covers every command that opens a file into a changelist: edit, add, delete, move, and the target side of integrations. Remove it, and the server refuses all of them. So I took it away from my own user everywhere in the project's depot, then gave it back for two kinds of stream only:
=open user chris * -//europa-horror/...
=open user chris * //europa-horror/subtask-*/...
=open user chris * //europa-horror/hotfix-*/...Order matters in that table — later lines override earlier ones — so it reads as "no opening files anywhere in the project, except on sub-task and hotfix streams".
Now, if I'm on a task stream and try to check out a file — from the command line, P4V, or Unreal's own source control — the server says no straight away. The same refusal I used to get at submit time now comes at the first file I touch, which is where it's cheap.
It also doesn't touch the automation. The webhook that copies approved work up between streams, and the build agents, run as their own users, so their rights are unchanged.
What it still can't catch
A permission check only runs when you ask Perforce to do something. If a file is already writable on disk and I edit it without checking it out — which is exactly what happened to two files each time — nothing asks the server anything. The refusal only arrives when I try to open or reconcile the file later.
That's still a big improvement: "later" is now "before I've lost track of where the work is", not "at submit". And the workspace setting that keeps synced files read-only means my editor has to ask to check a file out before I can type into it, which is where the new rule bites. The gap that's left is a file I've made writable myself.
The pattern I keep coming back to
All three of the last posts are the same idea from different angles. The review step stops work merging without someone looking at it. The build gate stops a red build being approved. This one stops work starting on a stream it can never be submitted from.
Each time, the fix was the same: don't rely on me remembering. Make the system say no, as early as possible, in a way that doesn't care whose day it's been.
Deep Ice is a 1–4 player cooperative survival horror game built in Unreal Engine by Night Wraith Studio — developed in the open, guard rails and all. More as it comes.