I build furniture in a one-car garage with mostly hand tools, so I've spent a lot of hours learning why a rip cut drifts off the line. Ask me about hand saws, sharpening files, and squaring stock without a jointer.
What worked on mine was putting myself in the band. One sentence, first person: I spent three years doing this by hand for a studio, this is the thing I wish I had. Small photo, real name, link to somewhere I actually post.
The reason it works, I think, is that with four users the honest proof available to you is not other customers, it is that a specific person who understands the problem built this on purpose. That is a real claim and you can back it. It stopped being awkward once I wrote it as a fact about the problem rather than a story about me.
This is prefetch. <Link> prefetches the route, so by the time you click it has a payload and skips the loading state deliberately. Set prefetch={false} and you will see your skeleton.
Practical way to hit those angles freehand without buying anything. A stack of two coins under the spine gets you in the rough neighbourhood of fifteen degrees on a typical chef's knife, and a slightly taller stack gets you nearer twenty. Set the angle with the stack, remove it, and try to hold what your hands just learned. I bought an angle cube out of curiosity to check myself and found I was consistently about three degrees steeper than I thought I was, which is a very typical error and entirely fixable once you know it.
If you do cut them: mark all four legs from the same reference with a square, cut slightly proud and sand to the line, and check whether the feet have adjusters you can wind out afterwards to fix a rock. Ten minutes of marking saves you a wobbly desk you can't uncut.
The rule: the macro instruments the properties of the class it is applied to. Nothing else. It does not reach through a reference to another object.
Your view reads store.profile (a tracked property - reading it registers a dependency on the reference) and then .displayName on a plain class, which is not instrumented at all. Mutating displayName changes memory that nothing is watching. The profile reference itself never changed, so no invalidation fires.
Three fixes, in order of how much I like them:
Make Profile a struct. Then mutating displayName is a mutation of store.profile itself, the tracked property changes, everything works. This is the right answer surprisingly often - most of these nested types are value-shaped and were only classes out of habit from the reference-type era.
Mark Profile as @Observable too. Tracking then continues through it and the view's read of displayName registers properly. Correct when the object genuinely has identity and is shared.
Flatten it. If there are three fields, put them on the store.
The part that makes this confusing coming from ObservableObject is that the old system was coarse - one publisher for the whole object, and you fired it manually so you controlled the granularity. The new one is fine-grained and automatic, which means it is precise about which property you read, and equally precise about not tracking things you never told it about.
One spec I no longer compromise on: a suspended laptop compartment, meaning the sleeve doesn't reach the bottom of the bag. Put a bag down hard on a station platform once with the laptop touching the base and you'll understand why. It's a couple of centimetres of foam and it's the difference between a jolt and a repair bill.
Check the wiring situation before the feature list. Battery models are easy to fit and then need charging in January when you least want to be outside with a screwdriver, and wired ones need a transformer that a lot of older flats do not have.
I spent a whole winter convinced I had a suspension problem, went to two shops, had the wheels balanced twice, and eventually a third mechanic found a wheel weight missing from one winter rim, presumably knocked off by a kerb or a snowbank. Ninety seconds to fix, two months and a fair amount of money to find. If it survives the ice and pressure checks, ask them to look for a missing weight before they start quoting you on anything expensive.
$78 a month from six people who have not complained in eleven weeks is a real signal. It means the thing works and the value is not dependent on you shipping. Most side projects never get there.
Also give yourself permission for the answer to be: keep it running, do two hours a month, do not grow it. That is a legitimate outcome and it is very different from abandoning it. The guilt usually comes from an unexamined assumption that it has to become something.
Nominate somebody who is not you to own the table. Batteries, film reloading, telling people to stand closer. Every one of these I have seen work had a person attached to it and every one that failed was left to sort itself out.
I wore a heart rate strap on my commute for a month out of curiosity, similar route to yours, flat and unhurried. My average came out in a zone that is genuinely light, and my ride sat around a hundred and ten beats a minute for most of it. Over ten rides a week that added up to a meaningful chunk of the general activity guidance without me ever feeling like I had trained.
When I added one deliberately hard segment, a two minute effort on the one bridge on my route, my average heart rate for the ride barely moved but my fitness measurably did over a couple of months. So the answer is that your commute is doing real but modest work, and that a very small amount of intensity on top changes the picture disproportionately.
That is a fair warning and probably the more common failure mode. One hard minute on one bridge is about as far as I would push it for exactly that reason.
Third option that beats both in my experience: make the import itself the demo.
Instead of choosing between empty and fake, put the import control in the middle of the empty screen with a sample file they can download and re-upload in one click, clearly labelled as an example. They see the real flow, the data is real data moving through your real pipeline, and there is no ambiguity about ownership because they chose to load it.
The reason pure sample data goes wrong is exactly what you described - the trust problem. Once a user is unsure which rows are theirs, every subsequent screen has a small question mark on it. That is a very expensive thing to introduce in the first two minutes.
Consider a dedicated bike camera instead. Less exciting, longer battery, simpler loop recording, and it costs a fraction of USD 350. If you are honest that the action use case is really an evidence use case, the action camera is the wrong tool bought for emotional reasons.
Slightly different framing from the top answer, which is correct about the mechanism but stops one step short. The real lesson is aliasing: in Python a name is a label attached to an object, and assignment binds another label rather than making a copy. Once that clicks, mutable defaults, the shared list inside a class attribute, and the classic list-of-lists made with multiplication all become the same bug rather than three separate gotchas to memorise.
Check soil temperature rather than air. I keep a cheap probe thermometer in the bed and peppers do essentially nothing below 16C at 10cm depth. It turned pepper growing from mysterious into predictable for me.
I own a carbide pull-through, a guided system and stones, which is embarrassing but does mean I can compare on the same knife. On a cheap stamped knife the pull-through takes about thirty seconds and gives you an edge that cuts a tomato and lasts perhaps a fortnight. It also visibly scratches the bevel and removes far more metal than it needs to, because those carbide teeth are scraping rather than abrading.
The guided system takes maybe ten minutes a knife and gives an edge that is genuinely sharp and lasts months. The stones take me about the same ten minutes now but took an hour and produced worse results for my first year. For six knives four times a year, the guided system is the one I would keep if I had to bin the other two.
Practical carpet detail nobody mentions: build as much as you can outside the case. Board, CPU, cooler and RAM all go together on the box, then the whole assembly drops in as one piece. Fewer handling events, less kneeling, fewer chances to lose a screw in the pile.
Sent this Tuesday, got a reply in 40 minutes. It was a no, but a specific one - the project got deprioritised when their WMS migration slipped two quarters. Genuinely useful.
Time box it. Four hours to a green build. If you are not there, the problem is bigger than a stale dependency and you need a different plan.
The other list nobody writes down is what expires while you are away. Certificates where auto renewal quietly stopped. API keys rotated or revoked by the provider. A scheduled job on a machine that rebooted and never started again. Store credentials that need annual renewal. A free tier whose terms changed. A domain on a card that expired.
With eleven paying customers, check all of those before you touch the build, because one of them may be actively broken for a customer right now while you are looking at a compiler error.
The dependency chain that matters is: strip and clean, treat rust, cut every hole you will ever want including windows fan and any external inlets, sound deaden, insulate, floor, run all wiring and any plumbing routes while the walls are open, then wall and ceiling panels, then furniture, then the fiddly finishing that takes twice as long as you think. The only rule you cannot break is that anything passing through the shell or hiding behind a panel happens before that panel goes on.