Here's the way I would think of it (very similar to what you are already doing).
First, we want something that repeats at a constant rate (once per second). Then we want to warp it a little (creating random attack durations, leaving the other durations alone). Whenever I think of warping, I think of the InputOutputCharacteristic.
So I took the OscillatorTimeIndex as a linear time index. Then warped it through the InputOutputCharacterstic mapping InValues:
{| atkDur p |
atkDur := (1 s random seed: 997) abs vmin: (1 - !DecayDur - !ReleaseDur).
(Array with: -1
with: (p := -1 + (2 * atkDur))
with: (p := (p + (2 * !DecayDur)) vmin: 1)
with: (1 - (2 * !ReleaseDur) vmax: p)
with: 1)}
so the start and end times (-1 and 1) are fixed. The interior points are warped, depending on the random duration of the first segment. We pick a random atkDur, then the release segment has to move right or left depending on the length of that atkDur segment. The releaseDur segment is computed relative to the last point (which is the end of the envelope).
The OutValues give the levels of the ADSR: 0 at the begining, 1 by the end of the attack, a sustain level under control of a fader, and 0 at the end of the envelope.
0 1 !Sustain !Sustain 0
This gives a steady, repeating envelope whose internal segments are warped according to the random duration of the attack.
Here's an example.