Release 0.15.0: Amplitude Analytics + Session Replay (with #21 Part C identity lifecycle)
Wave 5 / Track A. Roadmap item #13. Folds in #21 Part C user-identity- lifecycle work inline rather than as a follow-up release (operator ask: "implement Amplitude best practices from the very get-go"). Wraps @amplitude/unified with: - consent-gated lazy init (v0.13.0 cookie banner; SDK never loads without explicit analytics opt-in) - amplitude.initAll(KEY, { analytics: { autocapture: true }, sessionReplay: { sampleRate: 1 } }) — vendor-recommended shape - identify({ user_id, properties? }) with set vs setOnce semantics - setUserProperties(properties) for mid-session state changes - anonymize() clears both user_id binding AND property cache Wired into App.jsx (identify on me.user with role / permission_state / passcode_set / device_trusted as set; first_sign_in_at / account_created_at as setOnce; Page Viewed on route change; Sign-out fires User Signed Out + anonymize), Login.jsx (User Signed In + Beta Access Requested), and the per-feature surfaces (ProposeModal, PRModal, RFCView, RFCDiscussionPanel, PRView, Admin). Binding: VITE_AMPLITUDE_API_KEY via `flotilla overlay set` (bundle-embedded public key like VITE_TURNSTILE_SITE_KEY — not secret). Mid-Session-L correction: original dispatch brief specified secret-set; vendor guidance + bundle visibility settled it as overlay. PII discipline: no email, no display_name, no gitea_login, no free-text fields ever sent. Properties are opaque ids + enums + timestamps + booleans only. Subagent ξ shipped the wrapper structure + nine-event taxonomy on feature/v0.15.0-amplitude (0fd8c52+6cfbf69post-correction). Driver-side integration extended the wrapper with setUserProperties + identify properties for the #21 Part C identity-lifecycle work. Frontend build verified green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -44,6 +44,7 @@ import ChangePanel, { diffWords } from './ChangePanel.jsx'
|
||||
import PRModal from './PRModal.jsx'
|
||||
import GraduateDialog from './GraduateDialog.jsx'
|
||||
import { claimOwnership } from '../api'
|
||||
import { EVENTS, track } from '../lib/analytics'
|
||||
|
||||
const MANUAL_IDLE_MS = 5 * 60 * 1000 // §8.6 idle window; exact value is impl detail.
|
||||
const MANUAL_DEBOUNCE_MS = 800
|
||||
@@ -121,7 +122,15 @@ export default function RFCView({ viewer }) {
|
||||
const [drawerOpen, setDrawerOpen] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
getRFC(slug).then(setEntry).catch(err => setError(err.message))
|
||||
getRFC(slug).then(entry => {
|
||||
setEntry(entry)
|
||||
// v0.15.0 — analytics: fire RFC Viewed once per slug load.
|
||||
// We key on the slug param rather than the loaded entry so a
|
||||
// re-render doesn't double-fire; the slug is the stable
|
||||
// identifier. id is included for join-friendliness in the
|
||||
// Amplitude dashboard.
|
||||
track(EVENTS.RFC_VIEWED, { rfc_slug: slug, rfc_id: entry?.id })
|
||||
}).catch(err => setError(err.message))
|
||||
listModels(slug)
|
||||
.then(({ models, default: def }) => {
|
||||
setModels(models || [])
|
||||
|
||||
Reference in New Issue
Block a user