Symptom
Reproducible cold-session behavior on `/tmp/tmb-marketplace`:
- Session 1: `@bro hello` → onboarding asks 3 questions → user picks Anonymous for identity → identity row NOT written (count stays 0), but `branching_model`/`pr_target`/`protected_branches` config rows ARE written (count = 3) + `tmb_onboarding_complete` ledger event recorded.
- Session 2 (cold, no `--resume`): `@bro ` → bro re-triggers full onboarding — same 3 questions, Anonymous user has to answer them all again.
This will repeat on EVERY cold session for any Anonymous user. Pure friction; no value created by re-asking the same questions.
Root cause — TWO compounding bugs
Bug A: identity_set is not called for Anonymous
`tmb_first-run-onboarding` skill writes `identity_set` only when the user provides a name. Anonymous = skip = no row written. So `identity_get` returns null forever.
Bug B: first-action chain uses OR not AND
`CLAUDE.md` first-action chain says:
if `config_get("branching_model")` returns null OR `identity_get().created_at` is null → invoke onboarding
The OR means EITHER missing triggers full onboarding. Anonymous users hit Bug A → identity perpetually null → Bug B fires onboarding on every session.
Confirming evidence (from `/tmp/tmb-marketplace/.claude/tmb/trajectory.db` after session 1):
```
identity: 0 rows
config: 3 rows (branching_model, pr_target, protected_branches)
ledger: includes tmb_onboarding_complete event
issues: open + closed mix from 2 prior tasks
tasks: 2 closed
validation: 2 pass rows from pr-reviewer
```
Bro has CLEAR evidence that onboarding ran (ledger event), but the first-action check ignores the ledger and only looks at identity row + config row presence.
Three fix options
A. Write an Anonymous identity row (smallest)
When the user picks "Anonymous" in onboarding, still call `identity_set(human_name='Anonymous')` (or a sentinel value like `'anonymous'`). Then `identity_get().created_at` is non-null → first-action chain doesn't re-fire onboarding.
- Pro: 1-line skill change.
- Con: "Anonymous" becomes a sentinel value in the identity table; need to handle it in display ("Hey, Anonymous" feels weird).
B. Make first-action chain check the ledger (more semantic)
Change the rule to: invoke onboarding only if NO `tmb_onboarding_complete` ledger event exists. Identity + config are CONSEQUENCES of onboarding, not preconditions.
- Pro: ledger is the audit-trail anchor; this is its purpose.
- Con: needs a new ledger query in CLAUDE.md / bro's first-action; very small change.
C. Combination — A for identity persistence + B for ledger-as-source-of-truth (most robust)
A solves the identity table being empty for Anonymous. B makes the first-action check resilient to ANY future flag-row-missing issue. Together: belt + braces.
Bonus UX polish
If bro detects that onboarding ALREADY ran (per fix B), and the user is on a cold session, bro should ALSO call `issue_resume` first and surface any open issues:
Welcome back. I see issue #1 (`@bro hello` placeholder) is still open from a prior session, and tasks 1+2 (`feat/python-cli-todo` + `feat/list-limit-flag`) are closed and pushed. What's next?
That's a 5-second context-restore for the Human, no questions asked. Connects naturally to #45 (codebase memory) and #94 (architecture_regen on small projects).
Acceptance
Origin
Filed by @ZaxShen during v0.3.2 cold-session test 3 — bro's third interaction with the same project triggered full re-onboarding instead of resuming. User has now been asked the same 3 questions across 3 sessions for the same project.
Symptom
Reproducible cold-session behavior on `/tmp/tmb-marketplace`:
This will repeat on EVERY cold session for any Anonymous user. Pure friction; no value created by re-asking the same questions.
Root cause — TWO compounding bugs
Bug A: identity_set is not called for Anonymous
`tmb_first-run-onboarding` skill writes `identity_set` only when the user provides a name. Anonymous = skip = no row written. So `identity_get` returns null forever.
Bug B: first-action chain uses OR not AND
`CLAUDE.md` first-action chain says:
The OR means EITHER missing triggers full onboarding. Anonymous users hit Bug A → identity perpetually null → Bug B fires onboarding on every session.
Confirming evidence (from `/tmp/tmb-marketplace/.claude/tmb/trajectory.db` after session 1):
```
identity: 0 rows
config: 3 rows (branching_model, pr_target, protected_branches)
ledger: includes tmb_onboarding_complete event
issues: open + closed mix from 2 prior tasks
tasks: 2 closed
validation: 2 pass rows from pr-reviewer
```
Bro has CLEAR evidence that onboarding ran (ledger event), but the first-action check ignores the ledger and only looks at identity row + config row presence.
Three fix options
A. Write an Anonymous identity row (smallest)
When the user picks "Anonymous" in onboarding, still call `identity_set(human_name='Anonymous')` (or a sentinel value like `'anonymous'`). Then `identity_get().created_at` is non-null → first-action chain doesn't re-fire onboarding.
B. Make first-action chain check the ledger (more semantic)
Change the rule to: invoke onboarding only if NO `tmb_onboarding_complete` ledger event exists. Identity + config are CONSEQUENCES of onboarding, not preconditions.
C. Combination — A for identity persistence + B for ledger-as-source-of-truth (most robust)
A solves the identity table being empty for Anonymous. B makes the first-action check resilient to ANY future flag-row-missing issue. Together: belt + braces.
Bonus UX polish
If bro detects that onboarding ALREADY ran (per fix B), and the user is on a cold session, bro should ALSO call `issue_resume` first and surface any open issues:
That's a 5-second context-restore for the Human, no questions asked. Connects naturally to #45 (codebase memory) and #94 (architecture_regen on small projects).
Acceptance
Origin
Filed by @ZaxShen during v0.3.2 cold-session test 3 — bro's third interaction with the same project triggered full re-onboarding instead of resuming. User has now been asked the same 3 questions across 3 sessions for the same project.