First, note that s .+= 1 (literally equivalent to s .= s .+ 1 can never change the type of s. It simply loops through s, adds 1 to each entry, and stores the result into the entries of s (calling convert on them before saving to ensure they are compatible). So although the entries can change, s as a “binding” does not and so cannot change type.
Okay, so what is s .+ 1? Your promote_rule says that when you promote a Share{Float64} and Int, the result is a promote_type(Float64, Int) == Float64. So s[i] + 1 promotes both arguments to Float64 and then adds them, resulting in a Float64. This is why s .+ 1 (which creates a new array) results in a Matrix{Float64}.
Writing s = s .+ 1 (without the .=) does not assign the results elementwise into s. Instead, it computes s .+ 1 (which we established is a Matrix{Float64}) and then binds that matrix to the name s (forgetting anything s might have meant before).
Okay, now to your question about the Share constructor and its @assert. When we assign s .= s .+ 1 we compute (s[i] + 1)::Float64 then call convert(Share{Float64}, (s[i]+1)::Float64) as we store it into s[i]. Let’s try it:
julia> convert(Share{Float64}, s[1] + 1)
1.3852102999353921
julia> convert(Share, s[1] + 1)
ERROR: AssertionError: Shares must be between 0 and 1 inclusive
julia> @which convert(Share{Float64}, s[1] + 1)
convert(::Type{T}, x::Number) where T<:Number
@ Base number.jl:7
julia> @which convert(Share, s[1] + 1)
convert(::Type{T}, x::Number) where T<:Number
@ Base number.jl:7
So both hit the same fallback (probably defined as T(x)). If T === Share then that hits your constructor which throws the error. But T === Share{Float64} is actually more specific not the same thing, such that it goes right into the struct constructor and skips the check. Note that you yourself call Share{T}(x) in your Share(x) function, so without this behavior you would have an infinite loop there.
You might be looking for an inner constructor.
Be aware that as a mutable struct, it is possible to change (::Share).x and such changes will not trigger any constructors (or bounds checks therein). You should consider whether a (immutable) struct can work for you here. It will probably be more performant anyway. If you decide to stay mutable, you can change Base.setproperty! on your type to check the input before setting an illegal value.