gstack-developer-profile --read double-counts mode:"resources" rows into SESSION_COUNT, TIER, and NUDGE_ELIGIBLE · Issue #2067 · garrytan/gstack · GitHub
Skip to content

gstack-developer-profile --read double-counts mode:"resources" rows into SESSION_COUNT, TIER, and NUDGE_ELIGIBLE #2067

Description

@mvann

gstack-developer-profile --read double-counts mode:"resources" bookkeeping rows into SESSION_COUNT, TIER, and NUDGE_ELIGIBLE

bin/gstack-developer-profile derives a builder's SESSION_COUNT, TIER, and NUDGE_ELIGIBLE from the sessions[] array in ~/.gstack/developer-profile.json. But /office-hours appends two rows per run: the real session row (mode: "startup" or "builder"), and a separate mode: "resources" bookkeeping row recorded after resources are surfaced (office-hours/SKILL.md:1620, written via --log-session). The read path counts both.

This was masked while #1677 was live (the writer wrote to the legacy builder-profile.jsonl the reader never re-read, so SESSION_COUNT was stuck at 0). Now that the profile is unified onto developer-profile.json and sessions actually persist, the resources rows are read — so the bug is live: counts read roughly 2x real sessions.

Root cause (bin/gstack-developer-profile, current main)

The file already knows resources rows aren't sessions. Lines 236-238 filter them for LAST_*/CROSS_PROJECT, with a comment naming them "the Phase 6 auto-append":

const realSessions = sessions.filter(e => e.mode !== 'resources');

But that filter is applied to only 2 of the 4 consumers. The other two still read raw sessions:

  • Line 228: const count = sessions.length;SESSION_COUNT and TIER (thresholds >=8 inner_circle, >=4 regular, >=1 welcome_back) are inflated.
  • Line 255: const builderSessions = sessions.filter(e => e.mode !== 'startup').length; → doubly wrong: it counts resources rows (they're !== 'startup') and excludes real startup sessions. Feeds NUDGE_ELIGIBLE (line 256: builderSessions >= 3 && totalSignals >= 5), so the builder→founder nudge can arm off bookkeeping alone.

Repro

Fresh $HOME. Run /office-hours to completion 4 times as real design sessions.

  • Each run appends 1 session row + 1 mode:"resources" row → sessions.length == 8.
  • gstack-developer-profile --read reports SESSION_COUNT: 8 / TIER: inner_circle.
  • Truth: 4 real sessions, which should be TIER: regular.

Two real sessions already report SESSION_COUNT: 4 / regular when the user is actually at welcome_back. The founder nudge can fire after as few as 3 runs on bookkeeping rows rather than 3 real builder sessions.

Impact

Low-severity (greeting tier and the builder→founder nudge are personalization/tone, not crash or data loss), but it fires on the default happy path for every /office-hours user now that sessions persist: wrong-tier greeting and a premature "have you thought about whether this could be a company?" nudge.

Fix

Hoist the existing realSessions filter above count, and make the nudge count real mode:"builder" sessions:

  • count = realSessions.length → SESSION_COUNT / TIER / CROSS_PROJECT / designs all key off real sessions.
  • builderSessions = realSessions.filter(e => e.mode === 'builder').length → nudge measures repeat builders, which is what it was meant to measure.

Write path untouched; this is a read-only aggregation change. A PR with this fix plus regression tests (count, tier both-sides-of-gate boundaries, cross-project, and nudge in the presence of resources rows) is up at #1991.

Relationship to #1677

Distinct bug, sequential. #1677 was a write-path split-brain (SESSION_COUNT always 0 because writes went to a file the reader never read). Unifying the profile fixed that and exposed this one: now that rows persist and get read, the mode:"resources" rows get over-counted. #1677 = sessions don't persist; this = persisted sessions are over-counted.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions