review-grader
An Angular 17.3, signals-based web app.
github.com/dhruvch1244/Review-Grader ↗The current idiom, on purpose, with the footguns named
Standalone components only, built around signal()/computed()/effect()/input()/output() rather than RxJS/Zone.js patterns for component state — the framework's current idiom, chosen deliberately rather than defaulted into. Its own AGENTS.md doesn't just say "use signals," it names the two specific ways an LLM (or a developer fluent in older Angular) will get it wrong on this exact version.
`apps/web` is Angular 17.3, standalone components only (no NgModules), built
around signals (`signal()`, `computed()`, `effect()`, `input()`, `output()`)
rather than RxJS/Zone-based patterns for component state. Two things that
differ from older training data and are easy to get wrong:
- **NG0600**: `effect()` throws if it writes to a signal (directly, or
indirectly - e.g. calling a service method that does) unless the effect
is created with `effect(fn, { allowSignalWrites: true })`. ...
- **`@let` template syntax requires Angular 18.1+** and will hard-fail to
parse on this app's Angular 17.3. ...NG0600 — a footgun that fails silently
effect() throws internally if it writes to a signal without { allowSignalWrites: true } — but the effect just stops running past that point. From the outside, this looks like "the feature does nothing," not like a crash. The note that actually saves the next person isn't "don't write to signals in effects" in the abstract — it's "if an effect seems to have silently stopped working, check the browser console before assuming the logic itself is wrong." Silent failures are the ones worth documenting most, precisely because nothing else tells you to go look.
.finally(() => {
if (!cancelled) this.loading.set(false);
});
// Angular disallows signal writes inside effect() by default (NG0600);
// this effect intentionally writes loading/questions/ratings as it fetches.
}, { allowSignalWrites: true });@let and the version-behind-default trap
@let template syntax requires Angular 18.1+ and hard-fails to parse on this app's 17.3. The trap: some npm packages (icon libraries especially) ship newer compiled templates that emit @let, so a routine, unrelated dependency bump can break the build with an error — 'Incomplete block "let ..."' — that has no obvious connection to the version pin that actually caused it. Writing "we use Angular 17" in a doc doesn't stop anyone from hitting this; writing the exact error text and the exact trigger does, because that's what a developer or agent will actually search for.
- **`@let` template syntax requires Angular 18.1+** and will hard-fail to
parse on this app's Angular 17.3. Some npm packages (icon libraries in
particular) ship newer releases that emit `@let` in their compiled
templates - pin to a version that predates it if a dependency bump starts
failing with "Incomplete block \"let ...\"".