Ah, in your example you need pivoting to get it to reveal the rank:
julia> δ = 0.000001; A = full(Diagonal([1.0, 1.0+δ, 2.0]))+y*pert
julia> Q₁, T = hessenberg(A-I);
julia> Q₂ = qr(T, Val(true)).Q;
julia> q = Q₁*Q₂[:,end-1:end];
julia> norm(A*q - q)
8.941524032091245e-5
Looking at T, the choice of pivots in rank-revealing QR should be robust to perturbation:
julia> T
3×3 Array{Float64,2}:
1.08233e-5 3.53086e-5 1.69407e-21
3.53086e-5 0.0651445 0.246914
0.0 0.246914 0.934715