Turning product data into user understanding, without the hallucinations.
Problem
I'veseenthispatternacrossalotofproductteams.Theydotheresearch,buttherestoftheteamendsupignoringtheresults.Notbecausetheydon'twanttouseit,butbecausetheycan'tevenconnectwhattheyfoundwiththedecisionsinfrontofthem.ThepersonaendsupinaFigmafileoraPPT.TheinterviewtranscriptsliveinaNotiondocthatnoteventhepersonwhodidtheinterviewgoesbacktorevisit.
Irealizedthegapwasn'teffort,itwashowthatdatareachedtheteam.SoIbuiltResense:tohelpproductteamsgetvaluefromwhattheyalreadyhad,withoutneedingaspecialisttointerpretitorspendinghoursonapersonaartifactthatendsupinthebottomdrawer.
The AI stack
ThegenerationpipelinemapsrawproductdatathroughJTBDandanti-optimismguardrails,structuringitintoSyntheticUsersviaOpenRouterwithClaudeSonnetinproductionandGeminiFlashindevelopment.Ifoundthetwo-modelstrategythroughtesting:deviterationneededspeed,productionneededreasoningqualityforcomplexsynthesis.Theyservedifferentjobs.
RAG-poweredevaluationretrievesrelevantsourcechunkstogroundpatternanalysisinactualuserdata.Thisiswhatletsnon-specialistproductteamstrusttheoutput:everyinsighttracesbacktoasource.
Promptcachingdoestwothings:itreducestokencostandcutsresponselatencyby63%.Onaresearchtoolwherepeoplearewaitingforasynthesisbeforeameeting,thatspeedmattersasmuchasthecontentitself.
EveryrequestgoesthroughaBYOKlayerforthefreetier:usersbringtheirownAPIkey(OpenRouter,Anthropic,OpenAI,orGoogle),whichneverleavesthebrowser.Auto-detectionrouteskeystotherightprovider.Theserverneverstoresorlogsguestkeys.
The design decisions
Calibratingtrustwasthehardestdesignproblem.LLMsfeelauthoritativeevenwhenthey'reguessing,sothebiggestriskintheproductwasdeliveringoutputsthatteamsusedwithoutquestioning.Ilandedonthreesystemsthataddressthisdirectly.
Sourcecitationsperattribute:everyinsightinaSyntheticUserprofilelinksbacktothesourceitcamefrom.Inferredfieldsaretagged[Inferred]intheoutput.Usersseeexactlywhatcamefromtheirdataandwhatwasmodelinference.
Theanti-optimismguardrailisanexplicitprohibitionlistinthesystemprompt:fourpatternsthatmakeagenerationinvalid.Thelayerforcesthemodeltodescribefrictionpointsasspecificsituations,when,where,whathappens.Ifafrictionpointistoovague,thegenerationfailsvalidation.
Thetriangulationscoremeasuressourcediversity(behavioral,attitudinal,contextual)withamultiplier:onesourcetypeyields0.6xconfidence,threetypesyield1.0x.TheConfidenceBadgecomponentrendersthisasawarning,informational,orconfirmationstate,andshowsexactlywhichsourcetypesaremissing.
Evaluationandimprovementsareoffthecriticalpath,triggeredon-demand.Themodelcallforprofilescloses,thenuserscantriggeraseparatecallforpatternsandrecommendationswhentheyneedit.Twosecondsondemandkeepsthemainloopfast,whichiswheretheteamspendsmostoftheirtime.
The failure mode
Whenthemodeldoesn'thaveenoughdatatogenerateareliableSyntheticUser,itsaysso.IbuiltaDataHintpanelthatsurfacesthreesignals:ananalysisofwhat'smissing,thespecificdatatypesneeded,andasuggestedresearchquestiontogogetthem.Thepanelusesprogressivedisclosure.Theanalysisisvisiblebydefault.Thegapsandsuggestedquestionexpandoninteraction.
Idecidedthatshowingalow-qualitySyntheticUserwithnosignalsofuncertaintywasmoredangerousthanbeinghonestaboutinsufficientdata.Teamsthattrustbadresearchmakeworsedecisions,andavoidingthatwasexactlythepoint.
BeforeanyuserdatareachestheLLM,ananonymizerstripsPII:emails,BrazilianCPFnumbers,phonenumbers,IPaddresses,andnamesincontext.Themodelreceivesonlyanonymizedtext.Dataatrestusesfield-levelencryption.Ihavenoaccesstorawusercontent.Thatcostsmeadminconvenience.Itwasworthit.
How I work with AI
Resensedidn'tstartasaproduct.Itstartedasmyownprocess.SyntheticUsersarehowIgetafirstreadonwhoI'mdesigningforinhoursinsteadofweeks,thenIvalidatetheonesthatmatterwithrealpeople.Astartingpoint,notareplacementforresearch.
Thesameinstinctrunsthroughtherestofmywork.IprototypenavigableflowsinClaudeCodeandCursor,soengineeringgetssomethingtheycanclickthroughinsteadofastaticscreen,whichiswherehandoffsusuallybreak.AndIbuiltadesignagentthatcarriesmydesignprinciplesandasetofSyntheticUsersascontext,runningconceptandusabilitychecksagainstthembeforeIsitdownwitharealuser.WhenIcompareditsverdictstorealsessions,theylandedcloseenoughtotrustasafirstpass.
AIisn'tastepIboltontotheend.It'sintheresearch,theprototype,andthetest,soIspendmytimeonthedecisionsinsteadofthesetup.
What's next
IwantSyntheticUsersthatevolveontheirownasnewproductdatacomesin,withoutmanualregeneration.Andaresearchagentthatlearnsfromcorrectionsignalsandsuggestswhattovalidatenextbasedonthegapsitalreadyflagged.Thegoalisasystemthatgetsmoreusefulthemoretheteamusesit.
Saving links doesn't make you smarter. Learning from them does.
Problem
IwasthatpersonwithhundredsofsavedlinksI'dneveropenagain.Isavedarticles,videos,references.ButwhenIneededsomething,I'dendupsearchingforatitleIbarelyremembered,infoldersIbarelyorganized.ThetoolIusedorganizedwhatIsaved.Theproblemwasitdidn'thelpmelearnfromitorfinditwhenIactuallyneededit.
SoIbuiltCoisas.spacearoundonequestion:whatifthecontentyousavedcouldsurfaceitselfwhenyouneedit,withoutyouhavingtorememberitexisted?
The AI stack
Categorizationrunsongpt-4o-miniviaOpenRouterattemperature0.2.Ifoundthattemperaturethroughtesting:highertemperaturesintroducedcategorydrift,thesamelinkcategorizeddifferentlyonrepeatsaves.0.2givestheconsistencythatacategorizationfeatureneedstobetrusted.
Theconversationalretrievallayerusesthecategoryasascopedcontextsignal,aRAG-likeapproachfastenoughforconsumerUXandaccurateenoughtobeuseful.Itestedwithfulldocumentembeddinganditwasslowerthantheactualquerypatternsjustified.
Whencontextisthin,retrievalfallsbackthroughthreelevels:visiblecontentfromtheactivecategory(last3days),thenallcontentfromthatcategory,thenallcontentregardlessofcategory.Thefallbackstrategykeepsresponsesgroundedevenforuserswhojuststarted.
TheFocoTimematchingalgorithmusessemanticAI,so"LearnReact"surfaceslinksabouthooks,components,andJSX,includinglinksthatnevermentionReactinthetitle.ResultsarecachedinKVwithhash-basedinvalidation:whenanewlinkissaved,thehashchanges,cacheinvalidates,andthenextmatchrequestregenerates.
The design decisions
Streamingwasnon-negotiableforme.Anycategorizationthatrequiredwaitingwouldbreakthesavehabit.Theproductonlyworksifsavingfeelsinstant.Thelinkappearsimmediatelyinthegridwithapendingstate,categorizationcompletesinthebackground,statusprogressesfrompendingtoprocessedtoread.
Iscopedtheconversationalinterfacebycategoryonpurpose.Open-endedchatoverallsavedcontentproducedirrelevantanswersintesting,sonarrowingthecontextwasthedecisionthatmadethefeaturework.Whenusersswitchcategorieswhileachatisopen,aninlinewarningappearswithtwooptions:keepthecurrentcontextorre-groundtheconversationinthenewcategory.
The/resumircommandusesFibonacci-basedprogressivedisclosure:thefirstcallreturns5links.Userscanaskformore,andthesequenceexpandsto8,13,21,34.Usersdiscoverwhattheyhaveatthepacetheyneed.
The failure mode
Whenthemodelcan'tcategorizealinkconfidently(paywalledpages,ambiguouscontent,brokenURLs),thelinklandsinanUncategorizedstatewithamanualoverrideoption.Thelinkisalwaysthere,alwaysaccessible.TheAIisanadditivelayerontopofthedata,sowhenitfails,thelinkstaysavailable.
TheAIproviderlayerhasafallback:ifOpenRouterreturnsa402(creditsexhausted),thesystemautomaticallyroutestoOpenAIwitha1-hourretrywindow.ServicestatusistrackedinKV:provider,lasterror,failedattempts,lastsuccesstime.Usersneverseeaproviderswitch.
Contentextractionusessilentfailure:ifittimesoutat10seconds,thelinkstillworkswithitsmetadata.Iprioritizedresponsespeedhere,becausealinkinthegridalreadyservestheuser,evenwithoutthefullcontentextracted.
Row-levelsecurityisenforcedatthedatabaselevelandintheapplicationlayer.EveryuserdatatablehasRLSpoliciesboundtoauth.uid().Noapplicationbugcanleakoneuser'sdatatoanother.
What's next
IwanttoadddeepreadingviaFirecrawl,extractingactualcontentfromsavedlinksbeyondthemetadata.AndGmailOAuthtopullnewslettersandthreadsdirectlyintothelibrary,turningtheinboxintoalearningfeed.WhatI'mmostexcitedaboutisthereadinglayer,becausethat'swhatwillmaketheAIresponsesgenuinelyuseful,groundedinwhatyouactuallyread.
