[{"data":1,"prerenderedAt":2036},["ShallowReactive",2],{"navigation":3,"page:\u002Fguides\u002Fdrift-and-canaries":1019,"surround:\u002Fguides\u002Fdrift-and-canaries":2027},[4,21,82,133,154,1010],[5,6,7,8],"Getting started","\u002Fgetting-started","i-lucide-rocket",[9,12,15,18],[10,6,11,11],"What Kaabist is",0,[13,14,11,11],"API keys and modes","keys-and-modes",[16,17,11,11],"Your first job","first-job",[19,20,11,11],"Understanding the envelope","envelope",[22,23,24,25],"Concepts","\u002Fconcepts","i-lucide-book-open",[26,28,31,34,37,40,43,46,49,52,55,58,61,64,67,70,73,76,79],[27,23,11,11],"Overview",[29,30,11,11],"Programs and versions","programs-and-versions",[32,33,11,11],"Tiers and escalation","tiers-and-escalation",[35,36,11,11],"The http tier","http-tier",[38,39,11,11],"The browser tier","browser-tier",[41,42,11,11],"Sessions","sessions",[44,45,11,11],"Human in the loop","human-in-the-loop",[47,48,11,11],"How healing works","self-healing",[50,51,11,11],"Obstacles and captchas","obstacles",[53,54,11,11],"Validation and warnings","validation-and-warnings",[56,57,11,11],"Credits and billing","credits-and-billing",[59,60,11,11],"Limits","limits",[62,63,11,11],"Locators","locators",[65,66,11,11],"Snapshots","snapshots",[68,69,11,11],"Egress and proxies","egress-and-proxies",[71,72,11,11],"Queueing and fairness","queueing",[74,75,11,11],"Accounts, organisations and roles","accounts-and-roles",[77,78,11,11],"Data retention","data-retention",[80,81,11,11],"Documents and PDFs","documents-and-pdfs",[83,84,85,86],"Guides","\u002Fguides","i-lucide-map",[87,88,91,94,97,100,103,106,109,112,115,118,121,124,127,130],[27,84,11,11],[89,90,11,11],"Authenticated flows","authenticated-flows",[92,93,11,11],"Hosted resolve pages","hosted-resolve",[95,96,11,11],"Lists and pagination","lists-and-pagination",[98,99,11,11],"JSON and network locators","json-and-network-locators",[101,102,11,11],"Schedules and change detection","schedules-and-change-detection",[104,105,11,11],"Webhooks","webhooks",[107,108,11,11],"Bring your own providers","byo-providers",[110,111,11,11],"Testing: replay, dry-run and test mode","testing",[113,114,11,11],"Side-effect safety","side-effect-safety",[116,117,11,11],"Security & data handling","security-and-data",[119,120,11,11],"Migrating from other tools","migrating",[122,123,11,11],"Drift detection and canaries","drift-and-canaries",[125,126,11,11],"SDKs","sdks",[128,129,11,11],"The kaab CLI","cli",[131,132,11,11],"Coding agents (MCP)","mcp",[134,135,136,137],"Panel","\u002Fpanel","i-lucide-layout-dashboard",[138,139,142,145,148,151],[27,135,11,11],[140,141,11,11],"The program builder","builder",[143,144,11,11],"Picking and recording","picking-and-recording",[146,147,11,11],"Taking over a job","takeover",[149,150,11,11],"Getting help","support",[152,153,11,11],"Referrals","referrals",[155,156,157,158],"Reference","\u002Freference","i-lucide-library",[159,160,520,834,837,840,1006,1007],[27,156,11,11],[161,162,11,163],"API","api",[164,166,169,212,222,274,297,316,347,376,389,396,403,410,426,447,454],[27,165,11,11],"\u002Freference\u002Fapi",[167,168,11,11],"Interactive reference","explorer",[170,171,11,172],"Jobs","jobs",[173,176,179,182,185,188,191,194,197,200,203,206,209],[174,175,11,11],"Create job","create-job",[177,178,11,11],"List jobs","list-jobs",[180,181,11,11],"Create job batch","create-job-batch",[183,184,11,11],"Estimate job","estimate-job",[186,187,11,11],"Get job","get-job",[189,190,11,11],"List job steps","list-job-steps",[192,193,11,11],"Get job snapshot","get-job-snapshot",[195,196,11,11],"Get job screenshot","get-job-screenshot",[198,199,11,11],"Get job artifact","get-job-artifact",[201,202,11,11],"Submit job input","submit-job-input",[204,205,11,11],"Resume job","resume-job",[207,208,11,11],"Takeover job","takeover-job",[210,211,11,11],"Cancel job","cancel-job",[213,214,11,215],"Batches","batches",[216,219],[217,218,11,11],"Get batch","get-batch",[220,221,11,11],"List batch jobs","list-batch-jobs",[223,224,11,225],"Programs","programs",[226,229,232,235,238,241,244,247,250,253,256,259,262,265,268,271],[227,228,11,11],"Create program","create-program",[230,231,11,11],"List programs","list-programs",[233,234,11,11],"Lint program","lint-program",[236,237,11,11],"Get program","get-program",[239,240,11,11],"Update program","update-program",[242,243,11,11],"Delete program","delete-program",[245,246,11,11],"List program versions","list-program-versions",[248,249,11,11],"Create program version","create-program-version",[251,252,11,11],"Update program version","update-program-version",[254,255,11,11],"Delete program version","delete-program-version",[257,258,11,11],"Get program version","get-program-version",[260,261,11,11],"Publish program version","publish-program-version",[263,264,11,11],"Diff program version","diff-program-version",[266,267,11,11],"Replay program version","replay-program-version",[269,270,11,11],"Set program alias","set-program-alias",[272,273,11,11],"Delete program alias","delete-program-alias",[41,42,11,275],[276,279,282,285,288,291,294],[277,278,11,11],"Create session","create-session",[280,281,11,11],"List sessions","list-sessions",[283,284,11,11],"Get session","get-session",[286,287,11,11],"Update session","update-session",[289,290,11,11],"Delete session","delete-session",[292,293,11,11],"Verify session","verify-session",[295,296,11,11],"Refresh session","refresh-session",[298,299,11,300],"Proposals","proposals",[301,304,307,310,313],[302,303,11,11],"List proposals","list-proposals",[305,306,11,11],"Get proposal","get-proposal",[308,309,11,11],"Approve proposal","approve-proposal",[311,312,11,11],"Reject proposal","reject-proposal",[314,315,11,11],"Edit proposal","edit-proposal",[317,318,11,319],"Schedules","schedules",[320,323,326,329,332,335,338,341,344],[321,322,11,11],"Create schedule","create-schedule",[324,325,11,11],"List schedules","list-schedules",[327,328,11,11],"Get schedule","get-schedule",[330,331,11,11],"Update schedule","update-schedule",[333,334,11,11],"Delete schedule","delete-schedule",[336,337,11,11],"Pause schedule","pause-schedule",[339,340,11,11],"Resume schedule","resume-schedule",[342,343,11,11],"List schedule runs","list-schedule-runs",[345,346,11,11],"Get schedule run diff","get-schedule-run-diff",[104,105,11,348],[349,352,355,358,361,364,367,370,373],[350,351,11,11],"Create webhook","create-webhook",[353,354,11,11],"List webhooks","list-webhooks",[356,357,11,11],"Get webhook","get-webhook",[359,360,11,11],"Update webhook","update-webhook",[362,363,11,11],"Delete webhook","delete-webhook",[365,366,11,11],"Rotate webhook secret","rotate-webhook-secret",[368,369,11,11],"List webhook deliveries","list-webhook-deliveries",[371,372,11,11],"Resend webhook delivery","resend-webhook-delivery",[374,375,11,11],"Test webhook","test-webhook",[377,378,11,379],"Organisation","org",[380,383,386],[381,382,11,11],"Rotate org webhook secret","rotate-org-webhook-secret",[384,385,11,11],"Get org","get-org",[387,388,11,11],"Request data deletion","request-data-deletion",[390,391,11,392],"Credits","credits",[393],[394,395,11,11],"Get credits","get-credits",[397,398,11,399],"Ledger","ledger",[400],[401,402,11,11],"List ledger entries","list-ledger-entries",[404,405,11,406],"Usage","usage",[407],[408,409,11,11],"Get usage","get-usage",[411,412,11,413],"API keys","api-keys",[414,417,420,423],[415,416,11,11],"List API keys","list-api-keys",[418,419,11,11],"Create API key","create-api-key",[421,422,11,11],"Rotate API key","rotate-api-key",[424,425,11,11],"Revoke API key","revoke-api-key",[427,108,11,428],"BYO providers",[429,432,435,438,441,444],[430,431,11,11],"List BYO providers","list-byo-providers",[433,434,11,11],"Create BYO provider","create-byo-provider",[436,437,11,11],"Get BYO provider","get-byo-provider",[439,440,11,11],"Update BYO provider","update-byo-provider",[442,443,11,11],"Delete BYO provider","delete-byo-provider",[445,446,11,11],"Test BYO provider","test-byo-provider",[448,449,11,450],"Sites","sites",[451],[452,453,11,11],"Get site profile","get-site-profile",[455,456,11,457],"Objects","schemas",[458,460,463,466,469,470,473,476,479,482,485,487,490,493,496,499,502,505,508,511,514,517],[27,459,11,11],"\u002Freference\u002Fapi\u002Fschemas",[461,462,11,11],"ApiKey","api-key",[464,465,11,11],"Batch","batch",[467,468,11,11],"ByoProvider","byo-provider",[390,391,11,11],[471,472,11,11],"ErrorResponse","error-response",[474,475,11,11],"ExtractSchema","extract-schema",[477,478,11,11],"Job","job",[480,481,11,11],"LedgerEntry","ledger-entry",[483,484,11,11],"LintResult","lint-result",[486,378,11,11],"Org",[488,489,11,11],"Program","program",[491,492,11,11],"ProgramDefinition","program-definition",[494,495,11,11],"ProgramVersion","program-version",[497,498,11,11],"Proposal","proposal",[500,501,11,11],"Schedule","schedule",[503,504,11,11],"Session","session",[506,507,11,11],"SiteProfile","site-profile",[509,510,11,11],"StepRecord","step-record",[512,513,11,11],"Webhook","webhook",[515,516,11,11],"WebhookDelivery","webhook-delivery",[518,519,11,11],"WebhookEvent","webhook-event",[521,522,11,523],"DSL","dsl",[524,526,529,532,535,538,603,637,640,751,754,756],[27,525,11,11],"\u002Freference\u002Fdsl",[527,528,11,11],"Inputs","inputs",[530,531,11,11],"Options","options",[533,534,11,11],"Policies","policies",[536,537,11,11],"Steps","steps",[539,540,11,541],"Actions","actions",[542,544,546,548,550,552,554,556,558,560,562,565,568,570,573,575,577,579,582,585,587,589,591,594,596,598,600],[27,543,11,11],"\u002Freference\u002Fdsl\u002Factions",[545,545,11,11],"fill",[547,547,11,11],"click",[549,549,11,11],"press",[551,551,11,11],"hover",[553,553,11,11],"select",[555,555,11,11],"check",[557,557,11,11],"uncheck",[559,559,11,11],"upload",[561,561,11,11],"scroll",[563,564,11,11],"wait_for","wait-for",[566,567,11,11],"set_session","set-session",[569,569,11,11],"extract",[571,572,11,11],"capture_response","capture-response",[574,574,11,11],"evaluate",[576,576,11,11],"paginate",[578,578,11,11],"download",[580,581,11,11],"read_pdf","read-pdf",[583,584,11,11],"set_vars","set-vars",[586,586,11,11],"sleep",[588,588,11,11],"reload",[590,590,11,11],"goto",[592,593,11,11],"await_input","await-input",[595,595,11,11],"checkpoint",[597,597,11,11],"screenshot",[599,599,11,11],"when",[601,602,11,11],"loop_until","loop-until",[604,605,11,606],"Conditions","conditions",[607,609,611,613,615,617,619,621,623,625,627,629,631,633,635],[27,608,11,11],"\u002Freference\u002Fdsl\u002Fconditions",[610,610,11,11],"url",[612,612,11,11],"title",[614,614,11,11],"visible",[616,616,11,11],"hidden",[618,618,11,11],"exists",[620,620,11,11],"absent",[622,622,11,11],"text",[624,624,11,11],"count",[626,626,11,11],"network",[628,628,11,11],"var",[630,630,11,11],"landmark",[632,632,11,11],"all",[634,634,11,11],"any",[636,636,11,11],"not",[638,639,11,11],"Targets","targets",[641,642,11,643],"Fields","fields",[644,646,649,651,654,656,659,661,663,666,668,671,672,674,677,679,681,683,685,687,690,693,695,698,701,703,704,707,709,712,714,716,718,720,722,724,726,728,731,733,734,736,738,740,743,745,747,749],[27,645,11,11],"\u002Freference\u002Fdsl\u002Ffields",[647,648,11,11],"allow_suffix","allow-suffix",[650,650,11,11],"attr",[652,653,11,11],"base_url","base-url",[655,655,11,11],"candidates",[657,658,11,11],"case_insensitive","case-insensitive",[660,660,11,11],"country",[662,662,11,11],"currency",[664,665,11,11],"date_format","date-format",[667,667,11,11],"decimal",[669,670,11,11],"false_values","false-values",[642,642,11,11],[673,673,11,11],"flags",[675,676,11,11],"follow_redirects","follow-redirects",[678,678,11,11],"frame",[680,680,11,11],"json",[682,682,11,11],"locale",[684,684,11,11],"many",[686,686,11,11],"max",[688,689,11,11],"max_items","max-items",[691,692,11,11],"max_length","max-length",[694,694,11,11],"min",[696,697,11,11],"min_items","min-items",[699,700,11,11],"min_length","min-length",[702,702,11,11],"name",[626,626,11,11],[705,706,11,11],"null_markers","null-markers",[708,708,11,11],"nullable",[710,711,11,11],"on_invalid","on-invalid",[713,713,11,11],"pattern",[715,715,11,11],"presence",[717,717,11,11],"regex",[719,719,11,11],"required",[721,721,11,11],"role",[723,723,11,11],"sample",[725,725,11,11],"selector",[727,727,11,11],"source",[729,730,11,11],"strip_fragment","strip-fragment",[732,732,11,11],"testid",[622,622,11,11],[735,735,11,11],"thousands",[737,737,11,11],"timezone",[739,739,11,11],"transforms",[741,742,11,11],"true_values","true-values",[744,744,11,11],"type",[746,746,11,11],"unique",[748,748,11,11],"values",[750,750,11,11],"xpath",[752,753,11,11],"Assertions","assertions",[755,739,11,11],"Transforms",[757,758,11,759],"Lint codes","lint",[760,762,765,768,771,774,777,780,783,786,789,792,795,798,801,804,807,810,813,816,819,822,825,828,831],[27,761,11,11],"\u002Freference\u002Fdsl\u002Flint",[763,764,11,11],"E001","e001",[766,767,11,11],"E002","e002",[769,770,11,11],"E003","e003",[772,773,11,11],"E004","e004",[775,776,11,11],"E005","e005",[778,779,11,11],"E006","e006",[781,782,11,11],"E007","e007",[784,785,11,11],"E008","e008",[787,788,11,11],"E009","e009",[790,791,11,11],"E010","e010",[793,794,11,11],"E011","e011",[796,797,11,11],"E012","e012",[799,800,11,11],"E013","e013",[802,803,11,11],"E014","e014",[805,806,11,11],"W001","w001",[808,809,11,11],"W002","w002",[811,812,11,11],"W003","w003",[814,815,11,11],"W004","w004",[817,818,11,11],"W005","w005",[820,821,11,11],"W006","w006",[823,824,11,11],"W007","w007",[826,827,11,11],"W008","w008",[829,830,11,11],"W009","w009",[832,833,11,11],"W010","w010",[835,836,11,11],"Codes","codes",[838,839,11,11],"Events","events",[841,129,11,842],"CLI",[843,845,847,850,853,856,859,862,865,868,871,874,877,880,883,886,889,892,895,898,901,904,907,910,913,916,919,922,925,928,931,934,937,940,943,946,949,952,955,958,961,964,967,970,973,976,979,982,985,988,991,994,997,1000,1003],[27,844,11,11],"\u002Freference\u002Fcli",[846,846,11,11],"kaab",[848,849,11,11],"kaab auth","kaab-auth",[851,852,11,11],"kaab auth login","kaab-auth-login",[854,855,11,11],"kaab auth logout","kaab-auth-logout",[857,858,11,11],"kaab auth status","kaab-auth-status",[860,861,11,11],"kaab completion","kaab-completion",[863,864,11,11],"kaab completion bash","kaab-completion-bash",[866,867,11,11],"kaab completion fish","kaab-completion-fish",[869,870,11,11],"kaab completion powershell","kaab-completion-powershell",[872,873,11,11],"kaab completion zsh","kaab-completion-zsh",[875,876,11,11],"kaab credits","kaab-credits",[878,879,11,11],"kaab jobs","kaab-jobs",[881,882,11,11],"kaab jobs cancel","kaab-jobs-cancel",[884,885,11,11],"kaab jobs get","kaab-jobs-get",[887,888,11,11],"kaab jobs input","kaab-jobs-input",[890,891,11,11],"kaab jobs list","kaab-jobs-list",[893,894,11,11],"kaab jobs resume","kaab-jobs-resume",[896,897,11,11],"kaab jobs run","kaab-jobs-run",[899,900,11,11],"kaab jobs snapshot","kaab-jobs-snapshot",[902,903,11,11],"kaab jobs steps","kaab-jobs-steps",[905,906,11,11],"kaab jobs takeover","kaab-jobs-takeover",[908,909,11,11],"kaab open","kaab-open",[911,912,11,11],"kaab open job","kaab-open-job",[914,915,11,11],"kaab programs","kaab-programs",[917,918,11,11],"kaab programs alias","kaab-programs-alias",[920,921,11,11],"kaab programs alias set","kaab-programs-alias-set",[923,924,11,11],"kaab programs diff","kaab-programs-diff",[926,927,11,11],"kaab programs lint","kaab-programs-lint",[929,930,11,11],"kaab programs list","kaab-programs-list",[932,933,11,11],"kaab programs pull","kaab-programs-pull",[935,936,11,11],"kaab programs push","kaab-programs-push",[938,939,11,11],"kaab proposals","kaab-proposals",[941,942,11,11],"kaab proposals approve","kaab-proposals-approve",[944,945,11,11],"kaab proposals list","kaab-proposals-list",[947,948,11,11],"kaab proposals reject","kaab-proposals-reject",[950,951,11,11],"kaab proposals show","kaab-proposals-show",[953,954,11,11],"kaab schedules","kaab-schedules",[956,957,11,11],"kaab schedules create","kaab-schedules-create",[959,960,11,11],"kaab schedules list","kaab-schedules-list",[962,963,11,11],"kaab schedules pause","kaab-schedules-pause",[965,966,11,11],"kaab schedules resume","kaab-schedules-resume",[968,969,11,11],"kaab schedules runs","kaab-schedules-runs",[971,972,11,11],"kaab sessions","kaab-sessions",[974,975,11,11],"kaab sessions create","kaab-sessions-create",[977,978,11,11],"kaab sessions delete","kaab-sessions-delete",[980,981,11,11],"kaab sessions list","kaab-sessions-list",[983,984,11,11],"kaab sessions verify","kaab-sessions-verify",[986,987,11,11],"kaab usage","kaab-usage",[989,990,11,11],"kaab version","kaab-version",[992,993,11,11],"kaab webhooks","kaab-webhooks",[995,996,11,11],"kaab webhooks create","kaab-webhooks-create",[998,999,11,11],"kaab webhooks deliveries","kaab-webhooks-deliveries",[1001,1002,11,11],"kaab webhooks list","kaab-webhooks-list",[1004,1005,11,11],"kaab webhooks test","kaab-webhooks-test",[125,126,11,11],[1008,1009,11,11],"Plans and pricing","plans",[1011,1012,1013,1014],"Changelog","\u002Fchangelog","i-lucide-history",[1015,1016],[27,1012,11,11],[1017,1018,11,11],"Deprecations","deprecations",{"id":1020,"title":122,"body":1021,"breaking":2017,"date":2018,"description":2019,"extension":2020,"generated":2017,"meta":2021,"navigation":2022,"path":2023,"seo":2024,"stem":2025,"version":2018,"__hash__":2026},"docs\u002F3.guides\u002F12.drift-and-canaries.md",{"type":1022,"value":1023,"toc":2002},"minimark",[1024,1028,1033,1036,1048,1052,1059,1062,1101,1104,1107,1166,1173,1177,1184,1272,1338,1351,1355,1358,1399,1403,1406,1410,1425,1430,1443,1596,1602,1606,1620,1624,1646,1650,1664,1884,1895,1899,1954,1957,1998],[1025,1026,1027],"p",{},"Drift detection watches the data your published programs return, and canaries run your programs on\na schedule so that a changed site is noticed before your own jobs depend on it. Together they turn\nthe quiet failure, a field that slowly stops filling while every job still succeeds, into a signal,\na webhook and a healing proposal waiting for review.",[1029,1030,1032],"h2",{"id":1031},"when-to-use-it","When to use it",[1025,1034,1035],{},"Rely on drift detection for every published program that runs regularly: it needs no setup and\nworks from your own traffic. Configure a canary for a program you run rarely (weekly, monthly, on\ndemand) or one that signs in, because statistics need recent runs to compare and a rare program\nwould otherwise tell you it broke a week late.",[1025,1037,1038,1039,1043,1044,1047],{},"Neither is a test suite. To check a program against known pages before you publish it, use\n",[1040,1041,111],"a",{"href":1042},"\u002Fguides\u002Ftesting","; to review and approve the fixes either one produces, see\n",[1040,1045,48],{"href":1046},"\u002Fconcepts\u002Fself-healing",".",[1029,1049,1051],{"id":1050},"statistics-from-your-own-traffic","Statistics, from your own traffic",[1025,1053,1054,1055,1047],{},"Every job records, per field, how many items it was looked for in, how many produced a value, how\nmany parsed, how long the lists were and what the numbers looked like. Those counts are folded into\nan hourly rolling picture of each ",[1056,1057,1058],"strong",{},"published program version",[1025,1060,1061],{},"Once an hour, each version with enough recent traffic (at least 20 jobs in the last seven days, not\ncounting canaries) is compared against itself:",[1063,1064,1065,1078],"table",{},[1066,1067,1068],"thead",{},[1069,1070,1071,1075],"tr",{},[1072,1073,1074],"th",{},"Window",[1072,1076,1077],{},"What it is",[1079,1080,1081,1093],"tbody",{},[1069,1082,1083,1087],{},[1084,1085,1086],"td",{},"baseline",[1084,1088,1089,1090],{},"the trailing 7 days, ",[1056,1091,1092],{},"ending 6 hours ago",[1069,1094,1095,1098],{},[1084,1096,1097],{},"window",[1084,1099,1100],{},"the last 6 hours",[1025,1102,1103],{},"The gap between them is the point. A baseline that included the present would drift toward whatever\nbroke, and a field that has been empty for three days would look normal.",[1025,1105,1106],{},"A finding is opened when:",[1063,1108,1109,1119],{},[1066,1110,1111],{},[1069,1112,1113,1116],{},[1072,1114,1115],{},"Signal",[1072,1117,1118],{},"Fires when",[1079,1120,1121,1136,1146,1156],{},[1069,1122,1123,1129],{},[1084,1124,1125],{},[1126,1127,1128],"code",{},"fill_rate_drop",[1084,1130,1131,1132,1135],{},"the fill rate fell by 30 points ",[1056,1133,1134],{},"and"," to below 90 % of its baseline",[1069,1137,1138,1143],{},[1084,1139,1140],{},[1126,1141,1142],{},"parse_rate_drop",[1084,1144,1145],{},"the same, for values your parser could read",[1069,1147,1148,1153],{},[1084,1149,1150],{},[1126,1151,1152],{},"list_len_drop",[1084,1154,1155],{},"the median list length halved",[1069,1157,1158,1163],{},[1084,1159,1160],{},[1126,1161,1162],{},"value_shift",[1084,1164,1165],{},"the median value moved by more than a factor of ten (with at least 30 numbers on each side)",[1025,1167,1168,1169,1172],{},"Both rate rules need ",[1056,1170,1171],{},"two"," conditions, because either alone is wrong in a way you would see the\nsame week: an absolute drop alone fires on every noisy sparse field, and a relative drop alone fires\nwhen 4 % becomes 2 %. A window with fewer than ten samples produces nothing at all: a quiet night\nis not evidence.",[1029,1174,1176],{"id":1175},"canaries-before-you-have-any-traffic-at-all","Canaries, before you have any traffic at all",[1025,1178,1179,1180,1183],{},"A ",[1056,1181,1182],{},"canary"," is Kaabist running your program itself, on a schedule, against the site.",[1185,1186,1190],"pre",{"className":1187,"code":1188,"language":680,"meta":1189,"style":1189},"language-json shiki shiki-themes kaab-code kaab-code kaab-code","{\n  \"options\": {\n    \"canary\": { \"enabled\": true, \"cron\": \"0 *\u002F6 * * *\", \"url\": \"https:\u002F\u002Fshop.example.com\u002Fp\u002F1\" }\n  }\n}\n","",[1126,1191,1192,1201,1215,1260,1266],{"__ignoreMap":1189},[1193,1194,1197],"span",{"class":1195,"line":1196},"line",1,[1193,1198,1200],{"class":1199},"sTzsn","{\n",[1193,1202,1204,1208,1212],{"class":1195,"line":1203},2,[1193,1205,1207],{"class":1206},"sqn18","  \"options\"",[1193,1209,1211],{"class":1210},"sQm2-",":",[1193,1213,1214],{"class":1199}," {\n",[1193,1216,1218,1221,1223,1226,1229,1231,1235,1238,1241,1243,1247,1249,1252,1254,1257],{"class":1195,"line":1217},3,[1193,1219,1220],{"class":1206},"    \"canary\"",[1193,1222,1211],{"class":1210},[1193,1224,1225],{"class":1199}," { ",[1193,1227,1228],{"class":1206},"\"enabled\"",[1193,1230,1211],{"class":1210},[1193,1232,1234],{"class":1233},"sHJIl"," true",[1193,1236,1237],{"class":1210},",",[1193,1239,1240],{"class":1206}," \"cron\"",[1193,1242,1211],{"class":1210},[1193,1244,1246],{"class":1245},"suOy4"," \"0 *\u002F6 * * *\"",[1193,1248,1237],{"class":1210},[1193,1250,1251],{"class":1206}," \"url\"",[1193,1253,1211],{"class":1210},[1193,1255,1256],{"class":1245}," \"https:\u002F\u002Fshop.example.com\u002Fp\u002F1\"",[1193,1258,1259],{"class":1199}," }\n",[1193,1261,1263],{"class":1195,"line":1262},4,[1193,1264,1265],{"class":1199},"  }\n",[1193,1267,1269],{"class":1195,"line":1268},5,[1193,1270,1271],{"class":1199},"}\n",[1273,1274,1275,1300,1320,1326,1332],"ul",{},[1276,1277,1278,1281,1282,1285,1286,1288,1289,1291,1292,1295,1296,1299],"li",{},[1056,1279,1280],{},"On by default"," for a program used in the last seven days that has somewhere to point: a\n",[1126,1283,1284],{},"canary.url",", or a program that is only ",[1126,1287,590],{}," and ",[1126,1290,569],{}," and so has no side effects. Set\n",[1126,1293,1294],{},"enabled: false"," to opt out, ",[1126,1297,1298],{},"true"," to opt in for anything else.",[1276,1301,1302,1306,1307,1309,1310,1313,1314,1316,1317,1319],{},[1056,1303,1304],{},[1126,1305,610],{}," is passed to the run as the input named ",[1126,1308,610],{},", so a program whose first step is\n",[1126,1311,1312],{},"{ \"goto\": \"{{inputs.url}}\" }"," probes that page. A program whose ",[1126,1315,590],{}," is a fixed address keeps\nprobing that address. ",[1126,1318,528],{}," supplies any other inputs the run needs.",[1276,1321,1322,1325],{},[1056,1323,1324],{},"Every six hours"," by default, in UTC.",[1276,1327,1328,1331],{},[1056,1329,1330],{},"Half price."," A canary bills compute at 50 %, and nothing else changes: same tier, same program,\nsame ledger. Canary runs are excluded from your usage figures and from the baselines above, so\nthey never flatter or distort your own numbers.",[1276,1333,1334,1337],{},[1056,1335,1336],{},"Never when you are out of credits",", and never on a program that already has an open drift\nsignal.",[1025,1339,1340,1341,1343,1344,1347,1348,1350],{},"The canary can be set in the program's ",[1126,1342,531],{}," or on the program itself (",[1126,1345,1346],{},"PATCH \u002Fv1\u002Fprograms\u002F{id}","\nwith ",[1126,1349,1182],{},"), which overrides the definition key by key. Credentials for a canary of a logged-in\nflow are stored encrypted on the program, not in its definition.",[1029,1352,1354],{"id":1353},"what-happens-when-something-is-found","What happens when something is found",[1025,1356,1357],{},"Four things, at once:",[1359,1360,1361,1371,1381,1390],"ol",{},[1276,1362,1179,1363,1366,1367,1370],{},[1056,1364,1365],{},"drift signal"," opens, carrying both windows, so you can read ",[1126,1368,1369],{},"97% → 20% over 340 samples","\nrather than \"drift detected\". One signal per field per kind, however long it stays broken.",[1276,1372,1373,1374,1377,1378,1380],{},"The ",[1056,1375,1376],{},"healer is asked to look",", at the actual pages the failing runs captured. By the time your\nnext job runs, a proposal is usually already in your review queue\n(",[1040,1379,48],{"href":1046},").",[1276,1382,1179,1383,1389],{},[1056,1384,1385,1388],{},[1126,1386,1387],{},"program.drift_detected"," webhook"," fires, once. It also fires when a canary run fails.",[1276,1391,1392,1393,1398],{},"Jobs of that version start carrying a ",[1056,1394,1395,1397],{},[1126,1396,1128],{}," warning"," with the detail, so a client\nthat checks warnings finds out without subscribing to anything.",[1029,1400,1402],{"id":1401},"when-it-goes-away","When it goes away",[1025,1404,1405],{},"A signal closes when the field is healthy for two consecutive windows: two rather than one, because\na program that runs four times a night can produce a quiet hour that looks like a recovery. It also\ncloses when you publish a healed version or dismiss it, and it expires after thirty days so that a\nreview queue reflects this month's problems.",[1029,1407,1409],{"id":1408},"worked-example","Worked example",[1025,1411,1412,1413,1416,1417,1420,1421,1424],{},"A shop at ",[1126,1414,1415],{},"https:\u002F\u002Fshop.example.com"," ships a redesign that moves every locator and keeps every\nvalue: the price moves from ",[1126,1418,1419],{},".price"," to ",[1126,1422,1423],{},"span[data-col=amount]",". This walkthrough follows a program\nthrough that redesign.",[1426,1427,1429],"h3",{"id":1428},"_1-publish-a-program-with-a-canary","1. Publish a program with a canary",[1025,1431,1432,1433,1288,1436,1438,1439,1442],{},"The program reads ",[1126,1434,1435],{},"h1",[1126,1437,1419],{}," from product ",[1126,1440,1441],{},"{{inputs.product_id}}"," of the shop, one locator\nper field. Turn its canary on, probing product 1 every six hours:",[1444,1445,1446],"code-group",{},[1185,1447,1452],{"className":1448,"code":1449,"filename":1450,"language":1451,"meta":1189,"style":1189},"language-ts shiki shiki-themes kaab-code kaab-code kaab-code","\u002F\u002F Probe the published program every six hours; a failing probe sends program.drift_detected.\nconst updated = await kaab.programs.update(program.id, {\n  canary: { enabled: true, cron: \"0 *\u002F6 * * *\", inputs: { product_id: \"1\" } },\n});\nconsole.log(`${updated.name}: canary ${JSON.stringify(updated.canary)}`);\n","TypeScript","ts",[1126,1453,1454,1460,1497,1541,1546],{"__ignoreMap":1189},[1193,1455,1456],{"class":1195,"line":1196},[1193,1457,1459],{"class":1458},"sKog0","\u002F\u002F Probe the published program every six hours; a failing probe sends program.drift_detected.\n",[1193,1461,1462,1465,1468,1471,1474,1477,1480,1482,1485,1488,1490,1493,1495],{"class":1195,"line":1203},[1193,1463,1464],{"class":1206},"const",[1193,1466,1467],{"class":1233}," updated",[1193,1469,1470],{"class":1206}," =",[1193,1472,1473],{"class":1206}," await",[1193,1475,1476],{"class":1233}," kaab",[1193,1478,1047],{"class":1479},"sJugJ",[1193,1481,224],{"class":1233},[1193,1483,1484],{"class":1479},".update",[1193,1486,1487],{"class":1199},"(",[1193,1489,489],{"class":1233},[1193,1491,1492],{"class":1199},".id",[1193,1494,1237],{"class":1210},[1193,1496,1214],{"class":1199},[1193,1498,1499,1502,1504,1507,1509,1511,1513,1516,1518,1520,1522,1525,1527,1530,1532,1535,1538],{"class":1195,"line":1217},[1193,1500,1501],{"class":1199},"  canary",[1193,1503,1211],{"class":1206},[1193,1505,1506],{"class":1199}," { enabled",[1193,1508,1211],{"class":1206},[1193,1510,1234],{"class":1233},[1193,1512,1237],{"class":1210},[1193,1514,1515],{"class":1199}," cron",[1193,1517,1211],{"class":1206},[1193,1519,1246],{"class":1245},[1193,1521,1237],{"class":1210},[1193,1523,1524],{"class":1199}," inputs",[1193,1526,1211],{"class":1206},[1193,1528,1529],{"class":1199}," { product_id",[1193,1531,1211],{"class":1206},[1193,1533,1534],{"class":1245}," \"1\"",[1193,1536,1537],{"class":1199}," } }",[1193,1539,1540],{"class":1210},",\n",[1193,1542,1543],{"class":1195,"line":1262},[1193,1544,1545],{"class":1199},"});\n",[1193,1547,1548,1551,1554,1556,1559,1562,1565,1568,1571,1574,1576,1579,1582,1584,1586,1589,1591,1593],{"class":1195,"line":1268},[1193,1549,1550],{"class":1233},"console",[1193,1552,1553],{"class":1479},".log",[1193,1555,1487],{"class":1199},[1193,1557,1558],{"class":1245},"`",[1193,1560,1561],{"class":1206},"${",[1193,1563,1564],{"class":1233},"updated",[1193,1566,1567],{"class":1199},".name",[1193,1569,1570],{"class":1206},"}",[1193,1572,1573],{"class":1245},": canary ",[1193,1575,1561],{"class":1206},[1193,1577,1578],{"class":1233},"JSON",[1193,1580,1581],{"class":1479},".stringify",[1193,1583,1487],{"class":1199},[1193,1585,1564],{"class":1233},[1193,1587,1588],{"class":1199},".canary)",[1193,1590,1570],{"class":1206},[1193,1592,1558],{"class":1245},[1193,1594,1595],{"class":1199},");\n",[1025,1597,1598,1599,1047],{},"When the version is created, Kaabist stores the page it was confirmed on and a fingerprint of each\nmatched element: the ground truth the healer works from. Before the redesign, every run fills\n",[1126,1600,1601],{},"price",[1426,1603,1605],{"id":1604},"_2-the-site-ships-a-redesign","2. The site ships a redesign",[1025,1607,1608,1609,1612,1613,1616,1617,1619],{},"The shop's product page changes its markup. Before, the price was\n",[1126,1610,1611],{},"\u003Cspan class=\"price\">1.337,40 TL\u003C\u002Fspan>","; after, it is\n",[1126,1614,1615],{},"\u003Cspan data-col=\"amount\">1.337,40 TL\u003C\u002Fspan>",". The value is the same, but the program's ",[1126,1618,1419],{},"\nlocator now points at nothing.",[1426,1621,1623],{"id":1622},"_3-the-next-run-notices","3. The next run notices",[1025,1625,1626,1628,1629,1631,1632,1635,1636,1639,1640,1643,1644,1047],{},[1126,1627,1419],{}," no longer matches. The healer compares the page with the ground truth and finds the\nelement that holds the same kind of value in the same place: ",[1126,1630,1423],{},". When it is\nconfident enough, the run uses the new locator for this job only and carries a\n",[1126,1633,1634],{},"healed_locator_used"," warning; when it is not, the job parks ",[1126,1637,1638],{},"needs_attention"," with the reason\n",[1126,1641,1642],{},"drift",". A canary run that fails also fires ",[1126,1645,1387],{},[1426,1647,1649],{"id":1648},"_4-review-the-proposal","4. Review the proposal",[1025,1651,1652,1653,1656,1657,1659,1660,1663],{},"Either way, a ",[1126,1654,1655],{},"relocate"," proposal for ",[1126,1658,1601],{}," is pending, with the old and new locators, the values\neach one read and the healer's confidence. List what is pending, then approve the proposal with\n",[1126,1661,1662],{},"promote: true"," (or reject it with a reason):",[1444,1665,1666],{},[1185,1667,1669],{"className":1448,"code":1668,"filename":1450,"language":1451,"meta":1189,"style":1189},"\u002F\u002F What healing found and is waiting on a person: the old and new locator, and how sure it is.\nlet pending = 0;\nfor await (const proposal of kaab.proposals.list({ status: \"pending\", limit: 25 })) {\n  pending++;\n  console.log(\n    `${proposal.id} ${proposal.kind} ${proposal.target} confidence ${proposal.confidence} (${proposal.source})`,\n  );\n  \u002F\u002F Then: kaab.proposals.approve(proposal.id, { promote: true }) or .reject(id, { reason })\n  if (pending >= 25) break;\n}\nconsole.log(`${pending} pending proposal(s)`);\n",[1126,1670,1671,1676,1693,1741,1751,1761,1822,1828,1834,1856,1861],{"__ignoreMap":1189},[1193,1672,1673],{"class":1195,"line":1196},[1193,1674,1675],{"class":1458},"\u002F\u002F What healing found and is waiting on a person: the old and new locator, and how sure it is.\n",[1193,1677,1678,1681,1684,1687,1690],{"class":1195,"line":1203},[1193,1679,1680],{"class":1206},"let",[1193,1682,1683],{"class":1199}," pending ",[1193,1685,1686],{"class":1206},"=",[1193,1688,1689],{"class":1233}," 0",[1193,1691,1692],{"class":1199},";\n",[1193,1694,1695,1698,1700,1703,1705,1708,1711,1713,1715,1717,1720,1723,1725,1728,1730,1733,1735,1738],{"class":1195,"line":1217},[1193,1696,1697],{"class":1206},"for",[1193,1699,1473],{"class":1206},[1193,1701,1702],{"class":1199}," (",[1193,1704,1464],{"class":1206},[1193,1706,1707],{"class":1233}," proposal",[1193,1709,1710],{"class":1206}," of",[1193,1712,1476],{"class":1233},[1193,1714,1047],{"class":1479},[1193,1716,299],{"class":1233},[1193,1718,1719],{"class":1479},".list",[1193,1721,1722],{"class":1199},"({ status",[1193,1724,1211],{"class":1206},[1193,1726,1727],{"class":1245}," \"pending\"",[1193,1729,1237],{"class":1210},[1193,1731,1732],{"class":1199}," limit",[1193,1734,1211],{"class":1206},[1193,1736,1737],{"class":1233}," 25",[1193,1739,1740],{"class":1199}," })) {\n",[1193,1742,1743,1746,1749],{"class":1195,"line":1262},[1193,1744,1745],{"class":1199},"  pending",[1193,1747,1748],{"class":1206},"++",[1193,1750,1692],{"class":1199},[1193,1752,1753,1756,1758],{"class":1195,"line":1268},[1193,1754,1755],{"class":1233},"  console",[1193,1757,1553],{"class":1479},[1193,1759,1760],{"class":1199},"(\n",[1193,1762,1764,1767,1769,1771,1773,1775,1778,1780,1783,1785,1787,1789,1792,1794,1797,1799,1801,1804,1806,1808,1810,1812,1815,1817,1820],{"class":1195,"line":1763},6,[1193,1765,1766],{"class":1245},"    `",[1193,1768,1561],{"class":1206},[1193,1770,498],{"class":1233},[1193,1772,1492],{"class":1199},[1193,1774,1570],{"class":1206},[1193,1776,1777],{"class":1206}," ${",[1193,1779,498],{"class":1233},[1193,1781,1782],{"class":1199},".kind",[1193,1784,1570],{"class":1206},[1193,1786,1777],{"class":1206},[1193,1788,498],{"class":1233},[1193,1790,1791],{"class":1199},".target",[1193,1793,1570],{"class":1206},[1193,1795,1796],{"class":1245}," confidence ",[1193,1798,1561],{"class":1206},[1193,1800,498],{"class":1233},[1193,1802,1803],{"class":1199},".confidence",[1193,1805,1570],{"class":1206},[1193,1807,1702],{"class":1245},[1193,1809,1561],{"class":1206},[1193,1811,498],{"class":1233},[1193,1813,1814],{"class":1199},".source",[1193,1816,1570],{"class":1206},[1193,1818,1819],{"class":1245},")`",[1193,1821,1540],{"class":1210},[1193,1823,1825],{"class":1195,"line":1824},7,[1193,1826,1827],{"class":1199},"  );\n",[1193,1829,1831],{"class":1195,"line":1830},8,[1193,1832,1833],{"class":1458},"  \u002F\u002F Then: kaab.proposals.approve(proposal.id, { promote: true }) or .reject(id, { reason })\n",[1193,1835,1837,1840,1843,1846,1848,1851,1854],{"class":1195,"line":1836},9,[1193,1838,1839],{"class":1206},"  if",[1193,1841,1842],{"class":1199}," (pending ",[1193,1844,1845],{"class":1206},">=",[1193,1847,1737],{"class":1233},[1193,1849,1850],{"class":1199},") ",[1193,1852,1853],{"class":1206},"break",[1193,1855,1692],{"class":1199},[1193,1857,1859],{"class":1195,"line":1858},10,[1193,1860,1271],{"class":1199},[1193,1862,1864,1866,1868,1870,1872,1874,1877,1879,1882],{"class":1195,"line":1863},11,[1193,1865,1550],{"class":1233},[1193,1867,1553],{"class":1479},[1193,1869,1487],{"class":1199},[1193,1871,1558],{"class":1245},[1193,1873,1561],{"class":1206},[1193,1875,1876],{"class":1199},"pending",[1193,1878,1570],{"class":1206},[1193,1880,1881],{"class":1245}," pending proposal(s)`",[1193,1883,1595],{"class":1199},[1025,1885,1886,1887,1890,1891,1894],{},"Promoting publishes a new program version with the new locator and moves the alias ",[1126,1888,1889],{},"latest"," to it\n(",[1126,1892,1893],{},"production"," moves only when you move it). The next run on the redesigned page succeeds without\nwarnings.",[1029,1896,1898],{"id":1897},"pitfalls","Pitfalls",[1273,1900,1901,1919,1925,1931,1939],{},[1276,1902,1903,1906,1907,1288,1909,1911,1912,1914,1915,1918],{},[1056,1904,1905],{},"A canary with nowhere to go."," A program with steps other than ",[1126,1908,590],{},[1126,1910,569],{}," gets no\ncanary by default, because Kaabist will not repeat actions on its own. Give it a ",[1126,1913,1284],{}," and\n",[1126,1916,1917],{},"enabled: true"," only if a run has no side effects.",[1276,1920,1921,1924],{},[1056,1922,1923],{},"Expecting a signal from a handful of runs."," Fewer than 20 jobs a week, or fewer than ten in the\nlast six hours, produce no statistics. Use a canary for rare programs.",[1276,1926,1927,1930],{},[1056,1928,1929],{},"Dismissing a signal you have not fixed."," It closes, and the next finding opens a new one only\nwhen the numbers cross the thresholds again.",[1276,1932,1933,1938],{},[1056,1934,1935,1936,1047],{},"Ignoring ",[1126,1937,1634],{}," A healed job succeeded with a locator that was not approved.\nReview the proposal; until you promote it, every run heals again.",[1276,1940,1941,1947,1948,1950,1951,1953],{},[1056,1942,1943,1944,1946],{},"Promoting and expecting ",[1126,1945,1893],{}," to follow."," Promotion moves ",[1126,1949,1889],{},"; move ",[1126,1952,1893],{},"\nyourself when you are ready.",[1029,1955,155],{"id":1956},"reference",[1273,1958,1959,1971,1980,1992],{},[1276,1960,1961,1288,1967],{},[1040,1962,1964,1965],{"href":1963},"\u002Freference\u002Fdsl\u002Foptions","Program options: ",[1126,1966,1182],{},[1040,1968,1970],{"href":1969},"\u002Freference\u002Fapi\u002Fprograms\u002Fupdate-program","update a program",[1276,1972,1973,1540,1976],{},[1040,1974,302],{"href":1975},"\u002Freference\u002Fapi\u002Fproposals\u002Flist-proposals",[1040,1977,1979],{"href":1978},"\u002Freference\u002Fapi\u002Fproposals\u002Fapprove-proposal","approve a proposal",[1276,1981,1982,1288,1988],{},[1040,1983,1985,1986],{"href":1984},"\u002Freference\u002Fevents","Events: ",[1126,1987,1387],{},[1040,1989,1991],{"href":1990},"\u002Freference\u002Fcodes#warnings","warnings",[1276,1993,1994,1997],{},[1040,1995,32],{"href":1996},"\u002Fconcepts\u002Ftiers-and-escalation",", the other half of \"why did this job not\nreturn what I expected\"",[1999,2000,2001],"style",{},"html pre.shiki code .sTzsn, html code.shiki .sTzsn{--shiki-light:var(--kaab-code-foreground);--shiki-default:var(--kaab-code-foreground);--shiki-dark:var(--kaab-code-foreground)}html pre.shiki code .sqn18, html code.shiki .sqn18{--shiki-light:var(--kaab-code-token-keyword);--shiki-default:var(--kaab-code-token-keyword);--shiki-dark:var(--kaab-code-token-keyword)}html pre.shiki code .sQm2-, html code.shiki .sQm2-{--shiki-light:var(--kaab-code-token-punctuation);--shiki-default:var(--kaab-code-token-punctuation);--shiki-dark:var(--kaab-code-token-punctuation)}html pre.shiki code .sHJIl, html code.shiki .sHJIl{--shiki-light:var(--kaab-code-token-constant);--shiki-default:var(--kaab-code-token-constant);--shiki-dark:var(--kaab-code-token-constant)}html pre.shiki code .suOy4, html code.shiki .suOy4{--shiki-light:var(--kaab-code-token-string-expression);--shiki-default:var(--kaab-code-token-string-expression);--shiki-dark:var(--kaab-code-token-string-expression)}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sKog0, html code.shiki .sKog0{--shiki-light:var(--kaab-code-token-comment);--shiki-default:var(--kaab-code-token-comment);--shiki-dark:var(--kaab-code-token-comment)}html pre.shiki code .sJugJ, html code.shiki .sJugJ{--shiki-light:var(--kaab-code-token-function);--shiki-default:var(--kaab-code-token-function);--shiki-dark:var(--kaab-code-token-function)}",{"title":1189,"searchDepth":1217,"depth":1217,"links":2003},[2004,2005,2006,2007,2008,2009,2015,2016],{"id":1031,"depth":1203,"text":1032},{"id":1050,"depth":1203,"text":1051},{"id":1175,"depth":1203,"text":1176},{"id":1353,"depth":1203,"text":1354},{"id":1401,"depth":1203,"text":1402},{"id":1408,"depth":1203,"text":1409,"children":2010},[2011,2012,2013,2014],{"id":1428,"depth":1217,"text":1429},{"id":1604,"depth":1217,"text":1605},{"id":1622,"depth":1217,"text":1623},{"id":1648,"depth":1217,"text":1649},{"id":1897,"depth":1203,"text":1898},{"id":1956,"depth":1203,"text":155},false,null,"How Kaabist notices that a program has quietly started returning worse data, and how it finds out before your next job does.","md",{},true,"\u002Fguides\u002Fdrift-and-canaries",{"title":122,"description":2019},"3.guides\u002F12.drift-and-canaries","DIuI8EWVuAeO-k4oxosQ5TogMmNX_jQe_CNzJP0q2Y4",[2028,2032],{"title":119,"path":2029,"stem":2030,"description":2031,"children":-1},"\u002Fguides\u002Fmigrating","3.guides\u002F11.migrating","Move Playwright or Puppeteer scripts, scraping-API calls and no-code scrapers to Kaabist programs, with a concept map and a worked conversion.",{"title":125,"path":2033,"stem":2034,"description":2035,"children":-1},"\u002Fguides\u002Fsdks","3.guides\u002F13.sdks","The official TypeScript, Python and Go clients: install, authenticate, run a job, wait for it, answer it, and verify webhooks.",1793593418126]