Streaming Step Progress Hierarchy
Reported by candidates from The Allen Institute for AI's online assessment. Pattern, common pitfall, and the honest play if you blank under the timer.
The Allen Institute for AI OA reported in July 2026 hands you a streaming tree problem with a nasty detail: a step can name a parent that hasn't arrived yet, so it has to sit as a temporary root and snap into place later. It's tagged simulation, and that's right. No fancy algorithm, just careful state handling across up to 200 events. You're taking this in a day or two, so know the shape now. If you blank mid-assessment, StealthCoder runs invisibly on your desktop and gives you a way back in. Here's what actually matters.
The problem
Process an ordered batch of step-progress event arrivals. Each row in events has five strings: [timestamp, stepId, parentId, runState, text] timestamp is a non-negative decimal integer. stepId identifies the step. An empty parentId declares a root; otherwise it names the intended parent step. runState and text are the current values for that step. Maintain only the latest event for each stepId. A greater timestamp replaces the current event. A smaller timestamp is stale and ignored. At an equal timestamp, the later arrival replaces the earlier arrival. Return one hierarchy snapshot after every arrival. A step whose non-empty parent is not currently known is a temporary root. As soon as that parent appears, the step attaches beneath it. A newer event may change a step's parent, state, or text. Snapshot Encoding Each snapshot is a string array containing every current step exactly once in preorder. Roots and siblings are sorted lexicographically by stepId. Encode a node as: depth:stepId:parentId:runState:text depth is 0 for each current root and increases by one down each attached edge. A temporary root keeps its missing parentId in the encoded token even though its depth is 0. Function buildProgressSnapshots(events: String[][]) → String[][] Examples Example 1 events = [["2","child","root","RUNNING","fetch"],["1","root","","RUNNING","job"],["3","child","root","DONE","fetched"]] return = [["0:child:root:RUNNING:fetch"],["0:root::RUNNING:job","1:child:root:RUNNING:fetch"],["0:root::RUNNING:job","1:child:root:DONE:fetched"]] The child first appears as a temporary root. When root arrives, the child attaches beneath it without needing another child event. The final event updates the child's state and text. Example 2 events = [["5","b","","RUNNING","new"],["3","b","","FAILED","stale"],["5","b","","DONE","tie-wins"],["1","a","","DONE","first"]] return = [["0:b::RUNNING:new"],["0:b::RUNNING:new"],["0:b::DONE:tie-wins"],["0:a::DONE:first","0:b::DONE:tie-wins"]] The timestamp-3 event is ignored as stale. The later arrival at timestamp 5 wins the tie. Once step a arrives, lexicographic root ordering places it before b. Example 3 events = [["1","a","","RUNNING","A"],["1","b","a","RUNNING","B"],["2","b","missing","DONE","moved"],["1","missing","","RUNNING","M"]] return = [["0:a::RUNNING:A"],["0:a::RUNNING:A","1:b:a:RUNNING:B"],["0:a::RUNNING:A","0:b:missing:DONE:moved"],["0:a::RUNNING:A","0:missing::RUNNING:M","1:b:missing:DONE:moved"]] Step b first belongs under a, then its newer event reparents it to an absent step, making it a temporary root. When missing arrives, b attaches beneath it. Constraints 1 <= events.length <= 200. Every event row contains exactly five strings in the documented order. Each timestamp is a decimal integer from 0 through 10^15. 1 <= stepId.length <= 40, and each stepId contains only ASCII letters, digits, underscores, or hyphens. parentId is empty or a valid step identifier distinct from stepId. 1 <= runState.length, text.length <= 80, and these fields contain only ASCII letters, digits, spaces, underscores, periods, or hyphens. After applying the latest event for every current step, existing parent links never form a cycle.
Reported by candidates. Source: FastPrep
Pattern and pitfall
The trick is to not maintain the tree incrementally. Keep a map of stepId to its latest event, applying the rule: greater timestamp replaces, smaller is ignored, equal timestamp means the later arrival wins (so use >= for replacement). After every arrival, rebuild the whole snapshot from scratch. Build a children map from parentId to a list of stepIds, but only count a step as a child if its parent exists in the map. Otherwise it's a root at depth 0 with its parentId still printed. Sort roots and each child list lexicographically, then DFS in preorder and emit depth:stepId:parentId:runState:text. With 200 events, rebuilding is cheap. The pitfalls: using > instead of >= on ties, parsing timestamps up to 10^15 as 32-bit ints, and forgetting that stale events still produce a snapshot. If the live OA rattles you, StealthCoder is the hedge for the rebuild-and-DFS part.
Drill it cold or hedge it with StealthCoder. Either way, don't walk into the OA hoping you remember the trick.
You can drill Streaming Step Progress Hierarchy cold, or you can hedge it. StealthCoder runs invisibly during screen share and surfaces a working solution in under 2 seconds. The proctor sees the IDE. They don't see what's behind it. Made for the candidate who got the OA invite this morning and has 72 hours, not six months.
Get StealthCoderRelated leaked OAs
You've seen the question.
Make sure you actually pass The Allen Institute for AI's OA.
The Allen Institute for AI reuses patterns across OAs. Made for the candidate who got the OA invite this morning and has 72 hours, not six months. Works on HackerRank, CodeSignal, CoderPad, and Karat.
Streaming Step Progress Hierarchy FAQ
What's the core trick in the Streaming Step Progress Hierarchy problem?+
Store only the latest event per stepId, then rebuild the full hierarchy after every arrival. Don't try to patch the tree in place. Reparenting and late-arriving parents make incremental updates error-prone, and 200 events means a full rebuild is trivially fast.
How do I handle ties and stale timestamps?+
Compare timestamps as 64-bit integers or as numbers parsed safely, since they go up to 10^15. If the new one is smaller, ignore it but still emit a snapshot. If it's greater or equal, replace. Equal means the later arrival wins, so your comparison must be >=.
How do temporary roots work?+
A step with a non-empty parentId that isn't in the current map is treated as a root with depth 0. It keeps its missing parentId in the encoded string. When the parent shows up, the next rebuild naturally attaches it, so you need no special logic.
Is sorting done globally or per level?+
Per sibling group. Roots are sorted lexicographically by stepId, and each parent's children are sorted the same way. Then you do a preorder DFS so each node is followed by its whole subtree before the next sibling.
How do I prepare for this in 48 hours?+
Write a clean version of this once: map of latest events, children map, sort, recursive preorder. Test it on the three examples, especially the reparenting one. Watch for empty parentId producing a double colon in the output, like 0:root::RUNNING:job.