Skip to content

fix(planning): don't plan pure-auth slices the BoringStack starter already ships#109

Merged
agjs merged 3 commits into
mainfrom
fix/planner-reserved-entities
Jul 16, 2026
Merged

fix(planning): don't plan pure-auth slices the BoringStack starter already ships#109
agjs merged 3 commits into
mainfrom
fix/planner-reserved-entities

Conversation

@agjs

@agjs agjs commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Problem

Live: for a bookmark app the planner proposed User + Bookmark. The stack ships auth/users/sessions, so the real login/signup code uses locale keys under auth.* — the model's parallel features.user.* keys were unused by construction, the i18n-locale-keys gate looped forever, and the build parked (80+ turns). (Root-caused by the reviewer diagnose panel.)

Fix (defense in depth)

  • Prompt: PLANNER_SYSTEM states auth/users/sessions already exist and must not be rebuilt (no User/Auth/Login/SignUp/Logout entity) — while explicitly permitting genuine domain entities that share a word (a billing Account, a therapy Session, a social Profile).
  • Enforcement backstop: stripReservedSlices drops slices whose id is a reserved pure-auth term (user/users/auth/authentication/login(s)/signin(s)/signup(s)/logout(s)). Deliberately narrow — ambiguous domain nouns are NOT reserved, so those products keep their features.
  • No trap on all-auth: if stripping empties the plan, proposePlan retries once (temp 0.7 may yield real slices) and returns null only if the retry is also all-auth — a finite planning failure, never a plan that re-emits the trap.

Tests (typed fixtures, no as casts)

Drops User but keeps Bookmark; drops case/plural variants (users/SignUp/LogIn/AUTH); keeps Account/Session/Profile/Credential; strips on both the first and temp-0.7 retry path; all-auth on both attempts → null (call count pinned); an all-auth first attempt retries and recovers a real second plan.

Review

Reviewer panel: PASS (after 3 iterations — the panel caught an as-cast, an over-broad silent denylist, a trap-re-emitting fallback, and a prompt/code contradiction; all fixed). Full bun run validate green.

agjs added 2 commits July 16, 2026 18:39
…lready ships

Observed live: for a bookmark-manager build the planner proposed two slices, User
and Bookmark. But BoringStack ships auth/users/sessions out of the box — the real
login/signup code references locale keys under `auth.*`. The model, scoped to build
a `features/user/**` slice, created a PARALLEL set of `features.user.errors.*` keys
that were unused by construction (real usage is `auth.*`), so the i18n-locale-keys
gate looped forever on "unused key" — add key (slice says build it) → gate says
unused → remove → re-add. The build oscillated 80+ turns at 1 error on a slice that
can never go green because it duplicates the built-in surface.

Fix (defense in depth):
- PLANNER_SYSTEM now states the stack already provides auth/users/sessions and must
  NOT propose a User/Account/Auth/Session/Login/SignUp/Profile slice — treat "a user"
  as an existing actor entities belong to.
- Enforcement backstop: stripReservedSlices() drops any slice whose entity id is a
  reserved identity concept (RESERVED_ENTITY_IDS), applied to every proposePlan
  result. If stripping would empty the plan, the original is kept (an empty plan
  builds nothing — worse than one redundant slice).

Tests: stripReservedSlices drops User keeps Bookmark; keeps all when every slice is
reserved; proposePlan strips the exact live User+Bookmark collision to Bookmark;
PLANNER_EXAMPLE proposes no reserved entity.
…odule, keep the generic planner clean

The prior version hardcoded 'the stack already provides authentication' into the
generic PLANNER_SYSTEM and stripped reserved slices on EVERY plan — leaking a
BoringStack assumption into the core planner used for other/plain builds, where it
would lie to the model and silently delete a legitimate User entity.

- Generic planner is stack-agnostic again: proposePlan takes optional IPlanConstraints
  (now in plan-types.ts) — default = no guidance, NO stripping. A plain build keeps a
  User entity (regression-tested).
- New boringstack-planning module (fully unit-tested) owns the stack specifics:
  looksLikeBoringstack (STRUCTURAL detection — apps/api + apps/ui + infra/compose, not
  a lone folder or a receipt a hand-clone lacks), the auth guidance, the pure-auth
  reserved set (ambiguous nouns Account/Session/Profile/Credential kept), and
  boringstackPlanConstraints (onStripped REQUIRED — drops are always surfaced, never
  silent).
- repl uses ONE signal (looksLikeBoringstack) for BOTH the planning interception AND
  the constraints, closing the gap where a project was planned as boringstack but not
  given the rule. The ~100-line inline block is extracted to runGreenfieldPlanning,
  dropping the line handler's cognitive complexity back under 20.
- stripReservedSlices takes the reserved set as a parameter; gen-plan scratch helper
  removed from the tree.

Tests: stack-agnostic (no constraints keeps User, base prompt says nothing about auth);
looksLikeBoringstack (all markers → true, apps/api-only → false); boringstackPlanConstraints
(guidance/reserved + onStripped plumbed); onStripped surfaces drops; runPlanning forwards
constraints; BoringStack strip/retry/null-on-all-auth.
@agjs agjs force-pushed the fix/planner-reserved-entities branch from 3e4aa94 to aa81a41 Compare July 16, 2026 20:18
@agjs agjs merged commit 5436e2e into main Jul 16, 2026
8 checks passed
@agjs agjs deleted the fix/planner-reserved-entities branch July 16, 2026 21:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant