[{"data":1,"prerenderedAt":2527},["ShallowReactive",2],{"docs-sections":3,"docs-page-\u002Fdocs\u002Fscopes":123},[4,9,13,17,22,26,30,34,38,42,46,50,55,59,63,67,72,76,80,85,89,93,97,102,106,110,114,119],{"path":5,"title":6,"description":7,"group":8},"\u002Fdocs","Introduction","What tunables is, the problem it solves, and the principles it holds to.","Get started",{"path":10,"title":11,"description":12,"group":8},"\u002Fdocs\u002Fquickstart","Quickstart","Take one group of settings from constants to a screen with a history, in a Nuxt app on Postgres.",{"path":14,"title":15,"description":16,"group":8},"\u002Fdocs\u002Fpatterns","What it is for","The kinds of value tunables keeps — live limits, plans, per-workspace and per-person values, model choices, values that bound each other, costly switches — and the feature that does each.",{"path":18,"title":19,"description":20,"group":21},"\u002Fdocs\u002Fsettings-and-groups","Settings and groups","Declare each setting once, in a group edited beside what it affects, and read it with types.","Concepts",{"path":23,"title":24,"description":25,"group":21},"\u002Fdocs\u002Fkinds-and-units","Kinds and units","The eight kinds of setting, the built-in units, your own units, and how values are checked and read.",{"path":27,"title":28,"description":29,"group":21},"\u002Fdocs\u002Fchecks-and-guards","Checks and guards","Rules across settings, guards for what your app keeps, within for presets, confirmations for costly changes, and notes.",{"path":31,"title":32,"description":33,"group":21},"\u002Fdocs\u002Fhistory-and-restore","History and restore","Every change is a revision with who, when, why and each value before and after; restoring one runs the same checks as any save.",{"path":35,"title":36,"description":37,"group":21},"\u002Fdocs\u002Fpresets","Presets","Named, reusable configurations with one default per slot, assigned by reference and bounded by the settings.",{"path":39,"title":40,"description":41,"group":21},"\u002Fdocs\u002Fscopes","Scopes","Values per workspace and per person, through layers — the system, a plan, the workspace, the person — each bounded by what it inherits.",{"path":43,"title":44,"description":45,"group":21},"\u002Fdocs\u002Fcatalogs","Catalogs","Lists of allowed choices, such as permitted models, whose entries are retired rather than deleted and cannot be retired while in use.",{"path":47,"title":48,"description":49,"group":21},"\u002Fdocs\u002Fdeployment-values","Deployment values","Values a deployment sets in its environment and nobody tunes while the app runs, declared once and listed in .env.example.",{"path":51,"title":52,"description":53,"group":54},"\u002Fdocs\u002Fstorage","Storage","Postgres through your own client, the four tables and how to create them, joining your transactions, and the storage contract behind it.","Integrate",{"path":56,"title":57,"description":58,"group":54},"\u002Fdocs\u002Fserver","Server and access","Mount tunables' API as a fetch handler in Nitro, Hono, Bun or Deno, as Express middleware or as an oRPC router, with access decided by your app.",{"path":60,"title":61,"description":62,"group":54},"\u002Fdocs\u002Faccess","Who can do what","End users bound by values they never see, staff who tune groups, owners and members who change only their own scope, and the terminal.",{"path":64,"title":65,"description":66,"group":54},"\u002Fdocs\u002Ftesting","Testing","Your app's tunables in memory for its own tests, with values set where a test needs them, and how to test rules, guards and confirmations.",{"path":68,"title":69,"description":70,"group":71},"\u002Fdocs\u002Fnuxt-layer","The Nuxt layer","Ready screens for a Nuxt UI app — a group's form, All settings, history, presets, assignments and catalogs — placed beside what they affect.","Screens",{"path":73,"title":74,"description":75,"group":71},"\u002Fdocs\u002Fvue-composables","Vue composables","Headless composables for building tunables' screens in any Vue app, on any design system.",{"path":77,"title":78,"description":79,"group":71},"\u002Fdocs\u002Ftheming","Theming","The screens bring no colors, fonts or CSS of their own; they take your Nuxt UI theme.",{"path":81,"title":82,"description":83,"group":84},"\u002Fdocs\u002Fcli","The CLI","The tunables command, tunables.config.ts, and which commands need a database.","CLI",{"path":86,"title":87,"description":88,"group":84},"\u002Fdocs\u002Fcli-check","Check and generate","The commands that need no database — checking definitions, writing .env.example, and keeping explanations and defaults in step with a pdd-kit Product Model.",{"path":90,"title":91,"description":92,"group":84},"\u002Fdocs\u002Fcli-values","Read and change values","The commands that read and change stored values, through the same checks and history as the screens.",{"path":94,"title":95,"description":96,"group":84},"\u002Fdocs\u002Fcli-skills","Install skills","Install tunables' agent skills for Claude Code, Codex CLI, Cursor, Gemini CLI and GitHub Copilot, and keep them current.",{"path":98,"title":99,"description":100,"group":101},"\u002Fdocs\u002Fskills","Agent skills","Three skills that let a coding agent adopt tunables, define settings the right way and verify that every value has one home.","Skills",{"path":103,"title":104,"description":105,"group":101},"\u002Fdocs\u002Fskill-adopt","tunables-adopt","Bring tunables into an app that has none yet, from an inventory of every value to a wired, synced app with the old homes removed.",{"path":107,"title":108,"description":109,"group":101},"\u002Fdocs\u002Fskill-define","tunables-define","Add, change, rename or remove one value in an app that uses tunables, with its home, its checks, its reads and what happens to stored values.",{"path":111,"title":112,"description":113,"group":101},"\u002Fdocs\u002Fskill-verify","tunables-verify","Audit an app's tunables against their rules, report every finding with where it is and the rule it breaks, and fix only the findings you choose.",{"path":115,"title":116,"description":117,"group":118},"\u002Fdocs\u002Fapi","API reference","Every export of the tunables package, by the subpath it is imported from.","Reference",{"path":120,"title":121,"description":122,"group":118},"\u002Fdocs\u002Ftasklane-example","Tasklane, the example app","A todo app with workspaces and people as tunables — two groups, plans as a layer, a catalog of models, scopes, a rule, a confirmation, and the playground that runs it.",{"id":124,"title":40,"body":125,"description":41,"extension":2522,"group":21,"meta":2523,"navigation":210,"order":364,"path":39,"seo":2524,"stem":2525,"__hash__":2526},"docs\u002Fdocs\u002Fscopes.md",{"type":126,"value":127,"toc":2507},"minimark",[128,132,145,154,159,162,368,399,421,425,432,772,782,787,790,1013,1063,1090,1094,1101,1561,1583,1589,1648,1651,1655,1662,1793,1803,1822,1896,1925,1929,1944,2058,2089,2093,2096,2123,2131,2134,2142,2145,2149,2156,2212,2218,2222,2233,2308,2327,2337,2341,2359,2457,2463,2472,2476,2503],[129,130,40],"h1",{"id":131},"scopes",[133,134,135,136,140,141,144],"p",{},"Some settings need a different value for a part of your product: a workspace\nthat wants fewer projects than its plan allows, a person whose week starts on\nSunday. tunables keeps those values as ",[137,138,139],"strong",{},"overrides"," in the same tables, with\nthe same checks and the same history as the system's, and reads a value\nthrough its ",[137,142,143],{},"layers",": the system value, the plan the workspace is on, the\nworkspace's own value, the person's own value. The most specific layer that\nholds a field wins, and every screen says where a value comes from.",[133,146,147,148,153],{},"The examples on this page are from ",[149,150,152],"a",{"href":151},".\u002Ftasklane-example","Tasklane",", a todo\napp with workspaces and the people in them.",[155,156,158],"h2",{"id":157},"declaring-the-chain","Declaring the chain",[133,160,161],{},"Your app names its scopes once, broadest first, when it creates tunables:",[163,164,169],"pre",{"className":165,"code":166,"language":167,"meta":168,"style":168},"language-ts shiki shiki-themes tunables-panel-light tunables-panel-light tunables-panel-dark","import { createTunables } from 'tunables'\n\nexport const tunables = createTunables({\n  store,\n  groups,\n  presets,\n  scopes: ['workspace', 'user'],\n  scopeLabel: (scope, id) => names[`${scope}:${id}`] ?? `${scope} ${id}`\n})\n","ts","",[170,171,172,205,212,234,243,251,259,292,362],"code",{"__ignoreMap":168},[173,174,177,181,185,189,192,195,198,202],"span",{"class":175,"line":176},"line",1,[173,178,180],{"class":179},"sZ4mS","import",[173,182,184],{"class":183},"sp9ci"," {",[173,186,188],{"class":187},"sVz71"," createTunables ",[173,190,191],{"class":183},"}",[173,193,194],{"class":179}," from",[173,196,197],{"class":183}," '",[173,199,201],{"class":200},"sjCzm","tunables",[173,203,204],{"class":183},"'\n",[173,206,208],{"class":175,"line":207},2,[173,209,211],{"emptyLinePlaceholder":210},true,"\n",[173,213,215,218,221,224,227,231],{"class":175,"line":214},3,[173,216,217],{"class":179},"export",[173,219,220],{"class":179}," const",[173,222,223],{"class":187}," tunables ",[173,225,226],{"class":183},"=",[173,228,230],{"class":229},"sOThN"," createTunables",[173,232,233],{"class":183},"({\n",[173,235,237,240],{"class":175,"line":236},4,[173,238,239],{"class":187},"  store",[173,241,242],{"class":183},",\n",[173,244,246,249],{"class":175,"line":245},5,[173,247,248],{"class":187},"  groups",[173,250,242],{"class":183},[173,252,254,257],{"class":175,"line":253},6,[173,255,256],{"class":187},"  presets",[173,258,242],{"class":183},[173,260,262,265,268,271,274,277,279,282,284,287,289],{"class":175,"line":261},7,[173,263,264],{"class":187},"  scopes",[173,266,267],{"class":183},":",[173,269,270],{"class":183}," [",[173,272,273],{"class":183},"'",[173,275,276],{"class":200},"workspace",[173,278,273],{"class":183},[173,280,281],{"class":183},",",[173,283,197],{"class":183},[173,285,286],{"class":200},"user",[173,288,273],{"class":183},[173,290,291],{"class":183},"],\n",[173,293,295,298,300,303,306,308,311,314,317,320,323,326,328,330,332,335,338,341,344,347,350,352,354,357,359],{"class":175,"line":294},8,[173,296,297],{"class":229},"  scopeLabel",[173,299,267],{"class":183},[173,301,302],{"class":183}," (",[173,304,305],{"class":187},"scope",[173,307,281],{"class":183},[173,309,310],{"class":187}," id",[173,312,313],{"class":183},")",[173,315,316],{"class":179}," =>",[173,318,319],{"class":187}," names",[173,321,322],{"class":183},"[",[173,324,325],{"class":183},"`${",[173,327,305],{"class":187},[173,329,191],{"class":183},[173,331,267],{"class":200},[173,333,334],{"class":183},"${",[173,336,337],{"class":187},"id",[173,339,340],{"class":183},"}`",[173,342,343],{"class":183},"]",[173,345,346],{"class":183}," ??",[173,348,349],{"class":183}," `${",[173,351,305],{"class":187},[173,353,191],{"class":183},[173,355,356],{"class":183}," ${",[173,358,337],{"class":187},[173,360,361],{"class":183},"}`\n",[173,363,365],{"class":175,"line":364},9,[173,366,367],{"class":183},"})\n",[369,370,371,380],"ul",{},[372,373,374,376,377,379],"li",{},[170,375,131],{}," is the chain. A user sits inside a workspace, so a user's values\nare read on top of their workspace's. Without ",[170,378,131],{},", there are no layers.",[372,381,382,385,386,390,391,394,395,398],{},[170,383,384],{},"scopeLabel"," says how a scope reads to a person, in refusals and on the\nscreens: ",[387,388,389],"em",{},"Acme"," rather than ",[170,392,393],{},"workspace=acme",". Without it, a scope reads as\n",[387,396,397],{},"Workspace acme",". It may be async, to look the name up.",[133,400,401,402,405,406,409,410,413,414,416,417,420],{},"A ",[137,403,404],{},"scope path"," names one place in the chain: ",[170,407,408],{},"{ workspace: 'acme' }"," or\n",[170,411,412],{},"{ workspace: 'acme', user: 'dana' }",". A path names a prefix of the chain, so\nit cannot name a user without their workspace. Ids may not be empty or hold\n",[170,415,226],{}," or ",[170,418,419],{},";",".",[155,422,424],{"id":423},"declaring-what-may-be-overridden","Declaring what may be overridden",[133,426,427,428,431],{},"Nothing is overridable unless its field says so. ",[170,429,430],{},"override"," names the scopes\nthat may set their own value:",[163,433,435],{"className":165,"code":434,"language":167,"meta":168,"style":168},"export const workspace = defineGroup({\n  id: 'workspace',\n  title: 'Workspace',\n  fields: {\n    defaultView: choice({ label: 'Default view', options: ['list', 'board', 'calendar'], default: 'list', override: ['workspace', 'user'] }),\n    reminderHour: number({ label: 'Reminder time', unit: 'hour', default: 9, override: ['user'] }),\n    guestAccess: toggle({ label: 'Guests', default: false, override: ['workspace'] }),\n    suggestionModel: pick({ label: 'Suggestion model', catalog: 'models', default: 'small' })\n  }\n})\n",[170,436,437,453,468,484,494,595,659,709,762,767],{"__ignoreMap":168},[173,438,439,441,443,446,448,451],{"class":175,"line":176},[173,440,217],{"class":179},[173,442,220],{"class":179},[173,444,445],{"class":187}," workspace ",[173,447,226],{"class":183},[173,449,450],{"class":229}," defineGroup",[173,452,233],{"class":183},[173,454,455,458,460,462,464,466],{"class":175,"line":207},[173,456,457],{"class":187},"  id",[173,459,267],{"class":183},[173,461,197],{"class":183},[173,463,276],{"class":200},[173,465,273],{"class":183},[173,467,242],{"class":183},[173,469,470,473,475,477,480,482],{"class":175,"line":214},[173,471,472],{"class":187},"  title",[173,474,267],{"class":183},[173,476,197],{"class":183},[173,478,479],{"class":200},"Workspace",[173,481,273],{"class":183},[173,483,242],{"class":183},[173,485,486,489,491],{"class":175,"line":236},[173,487,488],{"class":187},"  fields",[173,490,267],{"class":183},[173,492,493],{"class":183}," {\n",[173,495,496,499,501,504,507,510,512,514,517,519,521,524,526,528,530,533,535,537,539,542,544,546,548,551,553,556,559,561,563,565,567,569,572,574,576,578,580,582,584,586,588,590,592],{"class":175,"line":245},[173,497,498],{"class":187},"    defaultView",[173,500,267],{"class":183},[173,502,503],{"class":229}," choice",[173,505,506],{"class":183},"({",[173,508,509],{"class":187}," label",[173,511,267],{"class":183},[173,513,197],{"class":183},[173,515,516],{"class":200},"Default view",[173,518,273],{"class":183},[173,520,281],{"class":183},[173,522,523],{"class":187}," options",[173,525,267],{"class":183},[173,527,270],{"class":183},[173,529,273],{"class":183},[173,531,532],{"class":200},"list",[173,534,273],{"class":183},[173,536,281],{"class":183},[173,538,197],{"class":183},[173,540,541],{"class":200},"board",[173,543,273],{"class":183},[173,545,281],{"class":183},[173,547,197],{"class":183},[173,549,550],{"class":200},"calendar",[173,552,273],{"class":183},[173,554,555],{"class":183},"],",[173,557,558],{"class":187}," default",[173,560,267],{"class":183},[173,562,197],{"class":183},[173,564,532],{"class":200},[173,566,273],{"class":183},[173,568,281],{"class":183},[173,570,571],{"class":187}," override",[173,573,267],{"class":183},[173,575,270],{"class":183},[173,577,273],{"class":183},[173,579,276],{"class":200},[173,581,273],{"class":183},[173,583,281],{"class":183},[173,585,197],{"class":183},[173,587,286],{"class":200},[173,589,273],{"class":183},[173,591,343],{"class":183},[173,593,594],{"class":183}," }),\n",[173,596,597,600,602,605,607,609,611,613,616,618,620,623,625,627,630,632,634,636,638,641,643,645,647,649,651,653,655,657],{"class":175,"line":253},[173,598,599],{"class":187},"    reminderHour",[173,601,267],{"class":183},[173,603,604],{"class":229}," number",[173,606,506],{"class":183},[173,608,509],{"class":187},[173,610,267],{"class":183},[173,612,197],{"class":183},[173,614,615],{"class":200},"Reminder time",[173,617,273],{"class":183},[173,619,281],{"class":183},[173,621,622],{"class":187}," unit",[173,624,267],{"class":183},[173,626,197],{"class":183},[173,628,629],{"class":200},"hour",[173,631,273],{"class":183},[173,633,281],{"class":183},[173,635,558],{"class":187},[173,637,267],{"class":183},[173,639,640],{"class":200}," 9",[173,642,281],{"class":183},[173,644,571],{"class":187},[173,646,267],{"class":183},[173,648,270],{"class":183},[173,650,273],{"class":183},[173,652,286],{"class":200},[173,654,273],{"class":183},[173,656,343],{"class":183},[173,658,594],{"class":183},[173,660,661,664,666,669,671,673,675,677,680,682,684,686,688,691,693,695,697,699,701,703,705,707],{"class":175,"line":261},[173,662,663],{"class":187},"    guestAccess",[173,665,267],{"class":183},[173,667,668],{"class":229}," toggle",[173,670,506],{"class":183},[173,672,509],{"class":187},[173,674,267],{"class":183},[173,676,197],{"class":183},[173,678,679],{"class":200},"Guests",[173,681,273],{"class":183},[173,683,281],{"class":183},[173,685,558],{"class":187},[173,687,267],{"class":183},[173,689,690],{"class":179}," false",[173,692,281],{"class":183},[173,694,571],{"class":187},[173,696,267],{"class":183},[173,698,270],{"class":183},[173,700,273],{"class":183},[173,702,276],{"class":200},[173,704,273],{"class":183},[173,706,343],{"class":183},[173,708,594],{"class":183},[173,710,711,714,716,719,721,723,725,727,730,732,734,737,739,741,744,746,748,750,752,754,757,759],{"class":175,"line":294},[173,712,713],{"class":187},"    suggestionModel",[173,715,267],{"class":183},[173,717,718],{"class":229}," pick",[173,720,506],{"class":183},[173,722,509],{"class":187},[173,724,267],{"class":183},[173,726,197],{"class":183},[173,728,729],{"class":200},"Suggestion model",[173,731,273],{"class":183},[173,733,281],{"class":183},[173,735,736],{"class":187}," catalog",[173,738,267],{"class":183},[173,740,197],{"class":183},[173,742,743],{"class":200},"models",[173,745,273],{"class":183},[173,747,281],{"class":183},[173,749,558],{"class":187},[173,751,267],{"class":183},[173,753,197],{"class":183},[173,755,756],{"class":200},"small",[173,758,273],{"class":183},[173,760,761],{"class":183}," })\n",[173,763,764],{"class":175,"line":364},[173,765,766],{"class":183},"  }\n",[173,768,770],{"class":175,"line":769},10,[173,771,367],{"class":183},[133,773,774,775,777,778,781],{},"A list overrides freely: a workspace may pick any default view its kind\nallows. A field with no ",[170,776,430],{},", such as ",[170,779,780],{},"suggestionModel",", is set only for\nthe whole system.",[783,784,786],"h3",{"id":785},"bounds","Bounds",[133,788,789],{},"A number can instead say, per scope, how its override relates to the value it\ninherits:",[163,791,793],{"className":165,"code":792,"language":167,"meta":168,"style":168},"export const limits = defineGroup({\n  id: 'limits',\n  title: 'Limits',\n  fields: {\n    maxProjects: number({ label: 'Projects', unit: 'count', default: 3, override: { workspace: 'at-most' } }),\n    aiSuggestionsPerDay: number({ label: 'AI suggestions a day', unit: 'count', default: 20, min: 0, override: { workspace: 'at-most', user: 'at-most' } })\n  }\n})\n",[170,794,795,810,825,840,848,917,1005,1009],{"__ignoreMap":168},[173,796,797,799,801,804,806,808],{"class":175,"line":176},[173,798,217],{"class":179},[173,800,220],{"class":179},[173,802,803],{"class":187}," limits ",[173,805,226],{"class":183},[173,807,450],{"class":229},[173,809,233],{"class":183},[173,811,812,814,816,818,821,823],{"class":175,"line":207},[173,813,457],{"class":187},[173,815,267],{"class":183},[173,817,197],{"class":183},[173,819,820],{"class":200},"limits",[173,822,273],{"class":183},[173,824,242],{"class":183},[173,826,827,829,831,833,836,838],{"class":175,"line":214},[173,828,472],{"class":187},[173,830,267],{"class":183},[173,832,197],{"class":183},[173,834,835],{"class":200},"Limits",[173,837,273],{"class":183},[173,839,242],{"class":183},[173,841,842,844,846],{"class":175,"line":236},[173,843,488],{"class":187},[173,845,267],{"class":183},[173,847,493],{"class":183},[173,849,850,853,855,857,859,861,863,865,868,870,872,874,876,878,881,883,885,887,889,892,894,896,898,900,903,905,907,910,912,915],{"class":175,"line":245},[173,851,852],{"class":187},"    maxProjects",[173,854,267],{"class":183},[173,856,604],{"class":229},[173,858,506],{"class":183},[173,860,509],{"class":187},[173,862,267],{"class":183},[173,864,197],{"class":183},[173,866,867],{"class":200},"Projects",[173,869,273],{"class":183},[173,871,281],{"class":183},[173,873,622],{"class":187},[173,875,267],{"class":183},[173,877,197],{"class":183},[173,879,880],{"class":200},"count",[173,882,273],{"class":183},[173,884,281],{"class":183},[173,886,558],{"class":187},[173,888,267],{"class":183},[173,890,891],{"class":200}," 3",[173,893,281],{"class":183},[173,895,571],{"class":187},[173,897,267],{"class":183},[173,899,184],{"class":183},[173,901,902],{"class":187}," workspace",[173,904,267],{"class":183},[173,906,197],{"class":183},[173,908,909],{"class":200},"at-most",[173,911,273],{"class":183},[173,913,914],{"class":183}," }",[173,916,594],{"class":183},[173,918,919,922,924,926,928,930,932,934,937,939,941,943,945,947,949,951,953,955,957,960,962,965,967,970,972,974,976,978,980,982,984,986,988,990,993,995,997,999,1001,1003],{"class":175,"line":253},[173,920,921],{"class":187},"    aiSuggestionsPerDay",[173,923,267],{"class":183},[173,925,604],{"class":229},[173,927,506],{"class":183},[173,929,509],{"class":187},[173,931,267],{"class":183},[173,933,197],{"class":183},[173,935,936],{"class":200},"AI suggestions a day",[173,938,273],{"class":183},[173,940,281],{"class":183},[173,942,622],{"class":187},[173,944,267],{"class":183},[173,946,197],{"class":183},[173,948,880],{"class":200},[173,950,273],{"class":183},[173,952,281],{"class":183},[173,954,558],{"class":187},[173,956,267],{"class":183},[173,958,959],{"class":200}," 20",[173,961,281],{"class":183},[173,963,964],{"class":187}," min",[173,966,267],{"class":183},[173,968,969],{"class":200}," 0",[173,971,281],{"class":183},[173,973,571],{"class":187},[173,975,267],{"class":183},[173,977,184],{"class":183},[173,979,902],{"class":187},[173,981,267],{"class":183},[173,983,197],{"class":183},[173,985,909],{"class":200},[173,987,273],{"class":183},[173,989,281],{"class":183},[173,991,992],{"class":187}," user",[173,994,267],{"class":183},[173,996,197],{"class":183},[173,998,909],{"class":200},[173,1000,273],{"class":183},[173,1002,914],{"class":183},[173,1004,761],{"class":183},[173,1006,1007],{"class":175,"line":261},[173,1008,766],{"class":183},[173,1010,1011],{"class":175,"line":294},[173,1012,367],{"class":183},[1014,1015,1016,1029],"table",{},[1017,1018,1019],"thead",{},[1020,1021,1022,1026],"tr",{},[1023,1024,1025],"th",{},"Rule",[1023,1027,1028],{},"The override may be",[1030,1031,1032,1043,1053],"tbody",{},[1020,1033,1034,1040],{},[1035,1036,1037],"td",{},[170,1038,1039],{},"'free'",[1035,1041,1042],{},"anything its kind allows",[1020,1044,1045,1050],{},[1035,1046,1047],{},[170,1048,1049],{},"'at-most'",[1035,1051,1052],{},"no more than the value it inherits",[1020,1054,1055,1060],{},[1035,1056,1057],{},[170,1058,1059],{},"'at-least'",[1035,1061,1062],{},"no less than the value it inherits",[133,1064,1065,1066,1068,1069,1071,1072,1074,1075,1078,1079,1082,1083,1086,1087,1089],{},"Acme's owner may lower ",[387,1067,867],{}," below what Acme's plan allows, never raise\nit. Dana may lower her own ",[387,1070,936],{}," below Acme's, never raise\nit. ",[170,1073,909],{}," and ",[170,1076,1077],{},"at-least"," apply to ",[170,1080,1081],{},"number"," fields only; ",[170,1084,1085],{},"createTunables","\nrefuses them on another kind, and refuses an ",[170,1088,430],{}," that names a scope not\nin the chain.",[155,1091,1093],{"id":1092},"plans-as-a-layer","Plans as a layer",[133,1095,401,1096,1100],{},[149,1097,1099],{"href":1098},".\u002Fpresets","preset set"," can be a layer of a group. Tasklane's plans hold\nthe limits a workspace gets on Free, Pro and Team:",[163,1102,1104],{"className":165,"code":1103,"language":167,"meta":168,"style":168},"export const plans = definePresets({\n  id: 'plans',\n  title: 'Plans',\n  singular: 'Plan',\n  slots: ['plan'],\n  layer: { group: 'limits', scope: 'workspace' },\n  fields: {\n    maxProjects: number({ label: 'Projects', unit: 'count', default: 3 }),\n    aiSuggestionsPerDay: number({ label: 'AI suggestions a day', unit: 'count', default: 20, min: 0 })\n  },\n  seed: [\n    { key: 'free', slot: 'plan', label: 'Free', values: { maxProjects: 3, aiSuggestionsPerDay: 5 } },\n    { key: 'pro', slot: 'plan', label: 'Pro', values: { maxProjects: 50, aiSuggestionsPerDay: 100 } },\n    { key: 'team', slot: 'plan', label: 'Team', values: { maxProjects: 500, aiSuggestionsPerDay: 500 } }\n  ]\n})\n",[170,1105,1106,1122,1137,1152,1168,1186,1222,1230,1272,1322,1327,1338,1412,1481,1550,1556],{"__ignoreMap":168},[173,1107,1108,1110,1112,1115,1117,1120],{"class":175,"line":176},[173,1109,217],{"class":179},[173,1111,220],{"class":179},[173,1113,1114],{"class":187}," plans ",[173,1116,226],{"class":183},[173,1118,1119],{"class":229}," definePresets",[173,1121,233],{"class":183},[173,1123,1124,1126,1128,1130,1133,1135],{"class":175,"line":207},[173,1125,457],{"class":187},[173,1127,267],{"class":183},[173,1129,197],{"class":183},[173,1131,1132],{"class":200},"plans",[173,1134,273],{"class":183},[173,1136,242],{"class":183},[173,1138,1139,1141,1143,1145,1148,1150],{"class":175,"line":214},[173,1140,472],{"class":187},[173,1142,267],{"class":183},[173,1144,197],{"class":183},[173,1146,1147],{"class":200},"Plans",[173,1149,273],{"class":183},[173,1151,242],{"class":183},[173,1153,1154,1157,1159,1161,1164,1166],{"class":175,"line":236},[173,1155,1156],{"class":187},"  singular",[173,1158,267],{"class":183},[173,1160,197],{"class":183},[173,1162,1163],{"class":200},"Plan",[173,1165,273],{"class":183},[173,1167,242],{"class":183},[173,1169,1170,1173,1175,1177,1179,1182,1184],{"class":175,"line":245},[173,1171,1172],{"class":187},"  slots",[173,1174,267],{"class":183},[173,1176,270],{"class":183},[173,1178,273],{"class":183},[173,1180,1181],{"class":200},"plan",[173,1183,273],{"class":183},[173,1185,291],{"class":183},[173,1187,1188,1191,1193,1195,1198,1200,1202,1204,1206,1208,1211,1213,1215,1217,1219],{"class":175,"line":253},[173,1189,1190],{"class":187},"  layer",[173,1192,267],{"class":183},[173,1194,184],{"class":183},[173,1196,1197],{"class":187}," group",[173,1199,267],{"class":183},[173,1201,197],{"class":183},[173,1203,820],{"class":200},[173,1205,273],{"class":183},[173,1207,281],{"class":183},[173,1209,1210],{"class":187}," scope",[173,1212,267],{"class":183},[173,1214,197],{"class":183},[173,1216,276],{"class":200},[173,1218,273],{"class":183},[173,1220,1221],{"class":183}," },\n",[173,1223,1224,1226,1228],{"class":175,"line":261},[173,1225,488],{"class":187},[173,1227,267],{"class":183},[173,1229,493],{"class":183},[173,1231,1232,1234,1236,1238,1240,1242,1244,1246,1248,1250,1252,1254,1256,1258,1260,1262,1264,1266,1268,1270],{"class":175,"line":294},[173,1233,852],{"class":187},[173,1235,267],{"class":183},[173,1237,604],{"class":229},[173,1239,506],{"class":183},[173,1241,509],{"class":187},[173,1243,267],{"class":183},[173,1245,197],{"class":183},[173,1247,867],{"class":200},[173,1249,273],{"class":183},[173,1251,281],{"class":183},[173,1253,622],{"class":187},[173,1255,267],{"class":183},[173,1257,197],{"class":183},[173,1259,880],{"class":200},[173,1261,273],{"class":183},[173,1263,281],{"class":183},[173,1265,558],{"class":187},[173,1267,267],{"class":183},[173,1269,891],{"class":200},[173,1271,594],{"class":183},[173,1273,1274,1276,1278,1280,1282,1284,1286,1288,1290,1292,1294,1296,1298,1300,1302,1304,1306,1308,1310,1312,1314,1316,1318,1320],{"class":175,"line":364},[173,1275,921],{"class":187},[173,1277,267],{"class":183},[173,1279,604],{"class":229},[173,1281,506],{"class":183},[173,1283,509],{"class":187},[173,1285,267],{"class":183},[173,1287,197],{"class":183},[173,1289,936],{"class":200},[173,1291,273],{"class":183},[173,1293,281],{"class":183},[173,1295,622],{"class":187},[173,1297,267],{"class":183},[173,1299,197],{"class":183},[173,1301,880],{"class":200},[173,1303,273],{"class":183},[173,1305,281],{"class":183},[173,1307,558],{"class":187},[173,1309,267],{"class":183},[173,1311,959],{"class":200},[173,1313,281],{"class":183},[173,1315,964],{"class":187},[173,1317,267],{"class":183},[173,1319,969],{"class":200},[173,1321,761],{"class":183},[173,1323,1324],{"class":175,"line":769},[173,1325,1326],{"class":183},"  },\n",[173,1328,1330,1333,1335],{"class":175,"line":1329},11,[173,1331,1332],{"class":187},"  seed",[173,1334,267],{"class":183},[173,1336,1337],{"class":183}," [\n",[173,1339,1341,1344,1347,1349,1351,1354,1356,1358,1361,1363,1365,1367,1369,1371,1373,1375,1377,1380,1382,1384,1387,1389,1391,1394,1396,1398,1400,1403,1405,1408,1410],{"class":175,"line":1340},12,[173,1342,1343],{"class":183},"    {",[173,1345,1346],{"class":187}," key",[173,1348,267],{"class":183},[173,1350,197],{"class":183},[173,1352,1353],{"class":200},"free",[173,1355,273],{"class":183},[173,1357,281],{"class":183},[173,1359,1360],{"class":187}," slot",[173,1362,267],{"class":183},[173,1364,197],{"class":183},[173,1366,1181],{"class":200},[173,1368,273],{"class":183},[173,1370,281],{"class":183},[173,1372,509],{"class":187},[173,1374,267],{"class":183},[173,1376,197],{"class":183},[173,1378,1379],{"class":200},"Free",[173,1381,273],{"class":183},[173,1383,281],{"class":183},[173,1385,1386],{"class":187}," values",[173,1388,267],{"class":183},[173,1390,184],{"class":183},[173,1392,1393],{"class":187}," maxProjects",[173,1395,267],{"class":183},[173,1397,891],{"class":200},[173,1399,281],{"class":183},[173,1401,1402],{"class":187}," aiSuggestionsPerDay",[173,1404,267],{"class":183},[173,1406,1407],{"class":200}," 5",[173,1409,914],{"class":183},[173,1411,1221],{"class":183},[173,1413,1415,1417,1419,1421,1423,1426,1428,1430,1432,1434,1436,1438,1440,1442,1444,1446,1448,1451,1453,1455,1457,1459,1461,1463,1465,1468,1470,1472,1474,1477,1479],{"class":175,"line":1414},13,[173,1416,1343],{"class":183},[173,1418,1346],{"class":187},[173,1420,267],{"class":183},[173,1422,197],{"class":183},[173,1424,1425],{"class":200},"pro",[173,1427,273],{"class":183},[173,1429,281],{"class":183},[173,1431,1360],{"class":187},[173,1433,267],{"class":183},[173,1435,197],{"class":183},[173,1437,1181],{"class":200},[173,1439,273],{"class":183},[173,1441,281],{"class":183},[173,1443,509],{"class":187},[173,1445,267],{"class":183},[173,1447,197],{"class":183},[173,1449,1450],{"class":200},"Pro",[173,1452,273],{"class":183},[173,1454,281],{"class":183},[173,1456,1386],{"class":187},[173,1458,267],{"class":183},[173,1460,184],{"class":183},[173,1462,1393],{"class":187},[173,1464,267],{"class":183},[173,1466,1467],{"class":200}," 50",[173,1469,281],{"class":183},[173,1471,1402],{"class":187},[173,1473,267],{"class":183},[173,1475,1476],{"class":200}," 100",[173,1478,914],{"class":183},[173,1480,1221],{"class":183},[173,1482,1484,1486,1488,1490,1492,1495,1497,1499,1501,1503,1505,1507,1509,1511,1513,1515,1517,1520,1522,1524,1526,1528,1530,1532,1534,1537,1539,1541,1543,1545,1547],{"class":175,"line":1483},14,[173,1485,1343],{"class":183},[173,1487,1346],{"class":187},[173,1489,267],{"class":183},[173,1491,197],{"class":183},[173,1493,1494],{"class":200},"team",[173,1496,273],{"class":183},[173,1498,281],{"class":183},[173,1500,1360],{"class":187},[173,1502,267],{"class":183},[173,1504,197],{"class":183},[173,1506,1181],{"class":200},[173,1508,273],{"class":183},[173,1510,281],{"class":183},[173,1512,509],{"class":187},[173,1514,267],{"class":183},[173,1516,197],{"class":183},[173,1518,1519],{"class":200},"Team",[173,1521,273],{"class":183},[173,1523,281],{"class":183},[173,1525,1386],{"class":187},[173,1527,267],{"class":183},[173,1529,184],{"class":183},[173,1531,1393],{"class":187},[173,1533,267],{"class":183},[173,1535,1536],{"class":200}," 500",[173,1538,281],{"class":183},[173,1540,1402],{"class":187},[173,1542,267],{"class":183},[173,1544,1536],{"class":200},[173,1546,914],{"class":183},[173,1548,1549],{"class":183}," }\n",[173,1551,1553],{"class":175,"line":1552},15,[173,1554,1555],{"class":183},"  ]\n",[173,1557,1559],{"class":175,"line":1558},16,[173,1560,367],{"class":183},[133,1562,1563,1566,1567,1571,1572,1575,1576,1074,1579,1582],{},[170,1564,1565],{},"layer: { group, scope, slot? }"," puts the set between the system value and\nthat scope's own. For a workspace, the plan it is\n",[149,1568,1570],{"href":1569},".\u002Fpresets#assigning-by-reference","assigned"," in the slot (the set's first\nslot unless ",[170,1573,1574],{},"slot"," says otherwise) applies, or the slot's default when it is\nassigned none. Its fields must be fields of the group, of the same kinds. A\nplan sets them freely; the set's own ",[170,1577,1578],{},"rules",[170,1580,1581],{},"within"," still apply.",[133,1584,1585,1586,1588],{},"So the layers of ",[387,1587,867],{}," for Dana, with Acme on Pro, are:",[1014,1590,1591,1603],{},[1017,1592,1593],{},[1020,1594,1595,1598,1601],{},[1023,1596,1597],{},"Layer",[1023,1599,1600],{},"Holds",[1023,1602,867],{},[1030,1604,1605,1618,1628,1638],{},[1020,1606,1607,1610,1615],{},[1035,1608,1609],{},"System",[1035,1611,1612,1613,1197],{},"the ",[170,1614,820],{},[1035,1616,1617],{},"3",[1020,1619,1620,1622,1625],{},[1035,1621,1163],{},[1035,1623,1624],{},"Pro, assigned to Acme",[1035,1626,1627],{},"50",[1020,1629,1630,1632,1635],{},[1035,1631,479],{},[1035,1633,1634],{},"Acme's own override",[1035,1636,1637],{},"20",[1020,1639,1640,1643,1646],{},[1035,1641,1642],{},"User",[1035,1644,1645],{},"Dana may not set Projects",[1035,1647],{},[133,1649,1650],{},"Dana reads 20.",[155,1652,1654],{"id":1653},"reading-at-a-path","Reading at a path",[133,1656,1657,1658,1661],{},"Pass the path to ",[170,1659,1660],{},"get",". It returns every field, as read through the layers:",[163,1663,1665],{"className":165,"code":1664,"language":167,"meta":168,"style":168},"const limits = await tunables.get('limits', { scope: { workspace: 'acme', user: 'dana' } })\nif (openProjects >= limits.maxProjects) throw new Error(`Your workspace may have ${limits.maxProjects} open projects.`)\n",[170,1666,1667,1735],{"__ignoreMap":168},[173,1668,1669,1672,1674,1676,1679,1683,1686,1688,1691,1693,1695,1697,1699,1701,1703,1705,1707,1709,1711,1713,1716,1718,1720,1722,1724,1726,1729,1731,1733],{"class":175,"line":176},[173,1670,1671],{"class":179},"const",[173,1673,803],{"class":187},[173,1675,226],{"class":183},[173,1677,1678],{"class":179}," await",[173,1680,1682],{"class":1681},"sq0K3"," tunables",[173,1684,420],{"class":1685},"sBd0f",[173,1687,1660],{"class":229},[173,1689,1690],{"class":183},"(",[173,1692,273],{"class":183},[173,1694,820],{"class":200},[173,1696,273],{"class":183},[173,1698,281],{"class":183},[173,1700,184],{"class":183},[173,1702,1210],{"class":187},[173,1704,267],{"class":183},[173,1706,184],{"class":183},[173,1708,902],{"class":187},[173,1710,267],{"class":183},[173,1712,197],{"class":183},[173,1714,1715],{"class":200},"acme",[173,1717,273],{"class":183},[173,1719,281],{"class":183},[173,1721,992],{"class":187},[173,1723,267],{"class":183},[173,1725,197],{"class":183},[173,1727,1728],{"class":200},"dana",[173,1730,273],{"class":183},[173,1732,914],{"class":183},[173,1734,761],{"class":183},[173,1736,1737,1740,1742,1745,1748,1751,1753,1756,1758,1761,1764,1767,1769,1772,1775,1777,1779,1781,1783,1785,1788,1790],{"class":175,"line":207},[173,1738,1739],{"class":179},"if",[173,1741,302],{"class":183},[173,1743,1744],{"class":187},"openProjects ",[173,1746,1747],{"class":183},">=",[173,1749,1750],{"class":187}," limits",[173,1752,420],{"class":183},[173,1754,1755],{"class":187},"maxProjects",[173,1757,313],{"class":183},[173,1759,1760],{"class":179}," throw",[173,1762,1763],{"class":179}," new",[173,1765,1766],{"class":229}," Error",[173,1768,1690],{"class":183},[173,1770,1771],{"class":183},"`",[173,1773,1774],{"class":200},"Your workspace may have ",[173,1776,334],{"class":183},[173,1778,820],{"class":187},[173,1780,420],{"class":183},[173,1782,1755],{"class":187},[173,1784,191],{"class":183},[173,1786,1787],{"class":200}," open projects.",[173,1789,1771],{"class":183},[173,1791,1792],{"class":183},")\n",[133,1794,1795,1796,1798,1799,1802],{},"Without a path, ",[170,1797,1660],{}," returns the system's values, as before.\n",[170,1800,1801],{},"{ lastGood: true }"," works at a path too.",[133,1804,1805,1808,1809,1812,1813,302,1815,1818,1819,1821],{},[170,1806,1807],{},"entry(id, { scope })"," returns what a scoped form needs. Beside ",[170,1810,1811],{},"values",", it\nhas ",[170,1814,305],{},[170,1816,1817],{},"{ key, path, label }",") and ",[170,1820,143],{},", one per field this scope\nmay set:",[163,1823,1825],{"className":165,"code":1824,"language":167,"meta":168,"style":168},"const entry = await tunables.entry('limits', { scope: { workspace: 'acme' } })\nentry.layers.maxProjects\n\u002F\u002F { inherited: 50, inheritedDisplay: '50', from: 'plan:plans:pro', fromLabel: 'Pro plan', overridden: true, rule: 'at-most' }\n",[170,1826,1827,1877,1890],{"__ignoreMap":168},[173,1828,1829,1831,1834,1836,1838,1840,1842,1845,1847,1849,1851,1853,1855,1857,1859,1861,1863,1865,1867,1869,1871,1873,1875],{"class":175,"line":176},[173,1830,1671],{"class":179},[173,1832,1833],{"class":187}," entry ",[173,1835,226],{"class":183},[173,1837,1678],{"class":179},[173,1839,1682],{"class":1681},[173,1841,420],{"class":1685},[173,1843,1844],{"class":229},"entry",[173,1846,1690],{"class":183},[173,1848,273],{"class":183},[173,1850,820],{"class":200},[173,1852,273],{"class":183},[173,1854,281],{"class":183},[173,1856,184],{"class":183},[173,1858,1210],{"class":187},[173,1860,267],{"class":183},[173,1862,184],{"class":183},[173,1864,902],{"class":187},[173,1866,267],{"class":183},[173,1868,197],{"class":183},[173,1870,1715],{"class":200},[173,1872,273],{"class":183},[173,1874,914],{"class":183},[173,1876,761],{"class":183},[173,1878,1879,1881,1883,1885,1887],{"class":175,"line":207},[173,1880,1844],{"class":187},[173,1882,420],{"class":183},[173,1884,143],{"class":187},[173,1886,420],{"class":183},[173,1888,1889],{"class":187},"maxProjects\n",[173,1891,1892],{"class":175,"line":214},[173,1893,1895],{"class":1894},"sKllz","\u002F\u002F { inherited: 50, inheritedDisplay: '50', from: 'plan:plans:pro', fromLabel: 'Pro plan', overridden: true, rule: 'at-most' }\n",[133,1897,1898,1901,1902,1905,1906,1909,1910,1912,1913,1916,1917,1920,1921,1924],{},[170,1899,1900],{},"from"," is ",[170,1903,1904],{},"system",", ",[170,1907,1908],{},"plan:\u003Cset>:\u003Ckey>",", or a scope key such as\n",[170,1911,393],{},"; ",[170,1914,1915],{},"fromLabel"," is how that reads. ",[170,1918,1919],{},"overridden"," says whether\nthis scope sets its own value. ",[170,1922,1923],{},"changed"," lists the fields it overrides.",[155,1926,1928],{"id":1927},"saving-at-a-scope","Saving at a scope",[133,1930,1931,1905,1934,1074,1937,1940,1941,1943],{},[170,1932,1933],{},"save",[170,1935,1936],{},"reset",[170,1938,1939],{},"restore"," take the path in ",[170,1942,305],{},". The most specific\nscope in it is where the change is made:",[163,1945,1947],{"className":165,"code":1946,"language":167,"meta":168,"style":168},"await tunables.save('limits', { maxProjects: 20 }, {\n  scope: { workspace: 'acme' },\n  actor: { id: 'olivia', name: 'Olivia' },\n  reason: 'Keep Acme small while it trials'\n})\n",[170,1948,1949,1983,2004,2040,2054],{"__ignoreMap":168},[173,1950,1951,1954,1956,1958,1960,1962,1964,1966,1968,1970,1972,1974,1976,1978,1981],{"class":175,"line":176},[173,1952,1953],{"class":179},"await",[173,1955,1682],{"class":1681},[173,1957,420],{"class":1685},[173,1959,1933],{"class":229},[173,1961,1690],{"class":183},[173,1963,273],{"class":183},[173,1965,820],{"class":200},[173,1967,273],{"class":183},[173,1969,281],{"class":183},[173,1971,184],{"class":183},[173,1973,1393],{"class":187},[173,1975,267],{"class":183},[173,1977,959],{"class":200},[173,1979,1980],{"class":183}," },",[173,1982,493],{"class":183},[173,1984,1985,1988,1990,1992,1994,1996,1998,2000,2002],{"class":175,"line":207},[173,1986,1987],{"class":187},"  scope",[173,1989,267],{"class":183},[173,1991,184],{"class":183},[173,1993,902],{"class":187},[173,1995,267],{"class":183},[173,1997,197],{"class":183},[173,1999,1715],{"class":200},[173,2001,273],{"class":183},[173,2003,1221],{"class":183},[173,2005,2006,2009,2011,2013,2015,2017,2019,2022,2024,2026,2029,2031,2033,2036,2038],{"class":175,"line":214},[173,2007,2008],{"class":187},"  actor",[173,2010,267],{"class":183},[173,2012,184],{"class":183},[173,2014,310],{"class":187},[173,2016,267],{"class":183},[173,2018,197],{"class":183},[173,2020,2021],{"class":200},"olivia",[173,2023,273],{"class":183},[173,2025,281],{"class":183},[173,2027,2028],{"class":187}," name",[173,2030,267],{"class":183},[173,2032,197],{"class":183},[173,2034,2035],{"class":200},"Olivia",[173,2037,273],{"class":183},[173,2039,1221],{"class":183},[173,2041,2042,2045,2047,2049,2052],{"class":175,"line":236},[173,2043,2044],{"class":187},"  reason",[173,2046,267],{"class":183},[173,2048,197],{"class":183},[173,2050,2051],{"class":200},"Keep Acme small while it trials",[173,2053,204],{"class":183},[173,2055,2056],{"class":175,"line":245},[173,2057,367],{"class":183},[369,2059,2060,2063,2073],{},[372,2061,2062],{},"A scope stores only the fields it overrides. The rest it inherits.",[372,2064,2065,2068,2069,2072],{},[170,2066,2067],{},"null"," stops overriding a field: the scope uses the inherited value again.\n",[170,2070,2071],{},"reset(id, keys?, { scope })"," does the same for the keys given, or for every\nfield the scope may set. A scope with nothing left keeps no row.",[372,2074,2075,2076,2078,2079,2081,2082,2085,2086,2088],{},"The group's ",[170,2077,1578],{}," run on the values as read at the path after the change.\nGuards receive the path as ",[170,2080,305],{},", and ",[170,2083,2084],{},"confirm"," compares the values as\nread there before and after: turning on ",[387,2087,679],{}," for Acme asks first, as it\ndoes for the system.",[783,2090,2092],{"id":2091},"refusals","Refusals",[133,2094,2095],{},"A scoped save is refused, naming the field, when:",[369,2097,2098,2104,2110,2116],{},[372,2099,2100,2101,419],{},"the field cannot be set at that scope:\n",[387,2102,2103],{},"\"Projects cannot be set for Dana.\"",[372,2105,2106,2107,419],{},"the value is outside its bound:\n",[387,2108,2109],{},"\"Projects may be at most 50 for Acme, the value it inherits.\"",[372,2111,2112,2113,419],{},"it would leave a narrower override outside its own bound:\n",[387,2114,2115],{},"\"Dana keeps AI suggestions a day at 3, above 2.\"",[372,2117,2118,2119,2122],{},"it would break the group's rules for a narrower scope that overrides\nsomething, as that scope reads its values:\n",[387,2120,2121],{},"\"For Dana: Due soon may not be longer than the time before done tasks are\narchived.\""," A rule that was already broken there is not blamed on this\nchange.",[133,2124,2125,2126,2128,2129,420],{},"A field that stops being overridable at a scope (its ",[170,2127,430],{}," no longer\nnames it) reads as inherited there at once. Its stored value is dropped on the\nscope's next save, or cleared with ",[170,2130,2067],{},[133,2132,2133],{},"That last check runs on every broader change, so a value is never lowered\nunder someone who already relies on it. Lowering the system value, editing a\nplan, moving a workspace to another plan, or lowering a workspace's own value\nis refused while an override below would end up outside its bound:",[163,2135,2140],{"className":2136,"code":2138,"language":2139},[2137],"language-text","Plan \"Pro\": Acme keeps Projects at 20, above 10.\nAcme keeps Projects at 20, above 3.\n","text",[170,2141,2138],{"__ignoreMap":168},[133,2143,2144],{},"The first is an edit of the Pro plan; the second, moving Acme to Free. Lower\nor clear the override first, then make the broader change.",[155,2146,2148],{"id":2147},"history-and-restore-at-a-scope","History and restore at a scope",[133,2150,2151,2152,2155],{},"Each scoped change records one revision on its target,\n",[170,2153,2154],{},"{ kind: 'group', name: 'limits', id: 'workspace=acme' }",", with only the\nfields that scope set:",[163,2157,2159],{"className":165,"code":2158,"language":167,"meta":168,"style":168},"await tunables.history({ kind: 'group', name: 'limits', id: 'workspace=acme' })\n",[170,2160,2161],{"__ignoreMap":168},[173,2162,2163,2165,2167,2169,2172,2174,2177,2179,2181,2184,2186,2188,2190,2192,2194,2196,2198,2200,2202,2204,2206,2208,2210],{"class":175,"line":176},[173,2164,1953],{"class":179},[173,2166,1682],{"class":1681},[173,2168,420],{"class":1685},[173,2170,2171],{"class":229},"history",[173,2173,506],{"class":183},[173,2175,2176],{"class":187}," kind",[173,2178,267],{"class":183},[173,2180,197],{"class":183},[173,2182,2183],{"class":200},"group",[173,2185,273],{"class":183},[173,2187,281],{"class":183},[173,2189,2028],{"class":187},[173,2191,267],{"class":183},[173,2193,197],{"class":183},[173,2195,820],{"class":200},[173,2197,273],{"class":183},[173,2199,281],{"class":183},[173,2201,310],{"class":187},[173,2203,267],{"class":183},[173,2205,197],{"class":183},[173,2207,393],{"class":200},[173,2209,273],{"class":183},[173,2211,761],{"class":183},[133,2213,2214,2215,2217],{},"Restoring a scope's revision puts that scope's own values back exactly as they\nwere: what it set then, it sets again; what it did not set, it inherits again.\nIt runs through every check, as any save does. A group's history without an\n",[170,2216,337],{}," is the system's.",[155,2219,2221],{"id":2220},"the-scoped-screen","The scoped screen",[133,2223,2224,2230,2231,267],{},[149,2225,2227],{"href":2226},".\u002Fnuxt-layer#tunablesgroupform",[170,2228,2229],{},"TunablesGroupForm"," takes a ",[170,2232,305],{},[163,2234,2239],{"className":2235,"code":2236,"filename":2237,"language":2238,"meta":168,"style":168},"language-vue shiki shiki-themes tunables-panel-light tunables-panel-light tunables-panel-dark","\u003Ctemplate>\n  \u003CTunablesGroupForm group=\"limits\" :scope=\"{ workspace: 'acme' }\" \u002F>\n\u003C\u002Ftemplate>\n","app\u002Fpages\u002Fworkspace.vue","vue",[170,2240,2241,2252,2299],{"__ignoreMap":168},[173,2242,2243,2246,2249],{"class":175,"line":176},[173,2244,2245],{"class":183},"\u003C",[173,2247,2248],{"class":200},"template",[173,2250,2251],{"class":183},">\n",[173,2253,2254,2257,2259,2261,2263,2266,2268,2270,2273,2275,2277,2279,2282,2284,2286,2288,2290,2292,2294,2296],{"class":175,"line":207},[173,2255,2256],{"class":183},"  \u003C",[173,2258,2229],{"class":200},[173,2260,1197],{"class":229},[173,2262,226],{"class":183},[173,2264,2265],{"class":183},"\"",[173,2267,820],{"class":200},[173,2269,2265],{"class":183},[173,2271,2272],{"class":183}," :",[173,2274,305],{"class":229},[173,2276,226],{"class":183},[173,2278,2265],{"class":183},[173,2280,2281],{"class":183},"{",[173,2283,902],{"class":187},[173,2285,267],{"class":183},[173,2287,197],{"class":183},[173,2289,1715],{"class":200},[173,2291,273],{"class":183},[173,2293,914],{"class":183},[173,2295,2265],{"class":183},[173,2297,2298],{"class":183}," \u002F>\n",[173,2300,2301,2304,2306],{"class":175,"line":214},[173,2302,2303],{"class":183},"\u003C\u002F",[173,2305,2248],{"class":200},[173,2307,2251],{"class":183},[133,2309,2310,2311,2314,2315,2318,2319,2322,2323,2326],{},"It shows only the fields that scope may set, with a badge naming the scope.\nEach field says where its value comes from: ",[387,2312,2313],{},"Inherited from Pro plan",", or\n",[387,2316,2317],{},"Set here · inherits 50 from Pro plan",", with its bound (",[387,2320,2321],{},"At most 50",") and a\n",[387,2324,2325],{},"Use 50 from Pro plan"," button. Its history panel shows that scope's revisions.\nA scope that may set nothing in the group says so.",[133,2328,2329,2330,2333,2334,420],{},"A screen like this is for the workspace's owner, or for the person\nthemselves, so your ",[170,2331,2332],{},"authorize"," has to let them reach it. See\n",[149,2335,61],{"href":2336},".\u002Faccess#self-service-per-scope",[155,2338,2340],{"id":2339},"from-the-terminal","From the terminal",[133,2342,2343,1905,2345,1074,2348,2350,2351,2354,2355,2358],{},[170,2344,1660],{},[170,2346,2347],{},"set",[170,2349,2171],{}," take ",[170,2352,2353],{},"--scope",", written as ",[170,2356,2357],{},"scope=id"," pairs\nseparated by commas:",[163,2360,2364],{"className":2361,"code":2362,"language":2363,"meta":168,"style":168},"language-bash shiki shiki-themes tunables-panel-light tunables-panel-light tunables-panel-dark","npx tunables get limits --scope workspace=acme\nnpx tunables set limits maxProjects=20 --scope workspace=acme --reason \"Keep Acme small while it trials\"\nnpx tunables set limits --scope workspace=acme --inherit aiSuggestionsPerDay --reason \"Back to the plan\"\nnpx tunables history limits --scope workspace=acme\n","bash",[170,2365,2366,2384,2414,2442],{"__ignoreMap":168},[173,2367,2368,2371,2373,2376,2378,2381],{"class":175,"line":176},[173,2369,2370],{"class":229},"npx",[173,2372,1682],{"class":200},[173,2374,2375],{"class":200}," get",[173,2377,1750],{"class":200},[173,2379,2380],{"class":200}," --scope",[173,2382,2383],{"class":200}," workspace=acme\n",[173,2385,2386,2388,2390,2393,2395,2398,2400,2403,2406,2409,2411],{"class":175,"line":207},[173,2387,2370],{"class":229},[173,2389,1682],{"class":200},[173,2391,2392],{"class":200}," set",[173,2394,1750],{"class":200},[173,2396,2397],{"class":200}," maxProjects=20",[173,2399,2380],{"class":200},[173,2401,2402],{"class":200}," workspace=acme",[173,2404,2405],{"class":200}," --reason",[173,2407,2408],{"class":183}," \"",[173,2410,2051],{"class":200},[173,2412,2413],{"class":183},"\"\n",[173,2415,2416,2418,2420,2422,2424,2426,2428,2431,2433,2435,2437,2440],{"class":175,"line":214},[173,2417,2370],{"class":229},[173,2419,1682],{"class":200},[173,2421,2392],{"class":200},[173,2423,1750],{"class":200},[173,2425,2380],{"class":200},[173,2427,2402],{"class":200},[173,2429,2430],{"class":200}," --inherit",[173,2432,1402],{"class":200},[173,2434,2405],{"class":200},[173,2436,2408],{"class":183},[173,2438,2439],{"class":200},"Back to the plan",[173,2441,2413],{"class":183},[173,2443,2444,2446,2448,2451,2453,2455],{"class":175,"line":236},[173,2445,2370],{"class":229},[173,2447,1682],{"class":200},[173,2449,2450],{"class":200}," history",[173,2452,1750],{"class":200},[173,2454,2380],{"class":200},[173,2456,2383],{"class":200},[163,2458,2461],{"className":2459,"code":2460,"language":2139},[2137],"maxProjects          20  (set here; inherits 50 from Pro plan)\nmaxTasksPerProject   5,000  (from Pro plan)\nattachmentMb         100 MB  (from Pro plan)\naiSuggestionsPerDay  100  (from Pro plan)\n",[170,2462,2460],{"__ignoreMap":168},[133,2464,2465,2468,2469,420],{},[170,2466,2467],{},"--inherit"," takes comma-separated keys to stop overriding. See\n",[149,2470,91],{"href":2471},".\u002Fcli-values",[155,2473,2475],{"id":2474},"where-the-rows-go","Where the rows go",[133,2477,2478,2479,2482,2483,2486,2487,2490,2491,2494,2495,420],{},"A group's system row has scope ",[170,2480,2481],{},"''"," and holds every field; a scoped row holds\nonly what it overrides, keyed by ",[170,2484,2485],{},"(id, scope)",". ",[170,2488,2489],{},"sync"," seeds system rows only.\nSee ",[149,2492,52],{"href":2493},".\u002Fstorage#the-tables",". The contract is\n",[149,2496,2500],{"href":2497,"rel":2498},"https:\u002F\u002Fgithub.com\u002Fitai-gendler\u002Ftunables\u002Fblob\u002Fmain\u002Fspec\u002Fscopes.md",[2499],"nofollow",[170,2501,2502],{},"spec\u002Fscopes.md",[2504,2505,2506],"style",{},"html pre.shiki code .sZ4mS, html code.shiki .sZ4mS{--shiki-light:#115E59;--shiki-default:#115E59;--shiki-dark:#5EEAD4}html pre.shiki code .sp9ci, html code.shiki .sp9ci{--shiki-light:#4F5D63;--shiki-default:#4F5D63;--shiki-dark:#A3AFB4}html pre.shiki code .sVz71, html code.shiki .sVz71{--shiki-light:#182025;--shiki-default:#182025;--shiki-dark:#E6EBEC}html pre.shiki code .sjCzm, html code.shiki .sjCzm{--shiki-light:#92400E;--shiki-default:#92400E;--shiki-dark:#FCD34D}html pre.shiki code .sOThN, html code.shiki .sOThN{--shiki-light:#2A3439;--shiki-light-font-weight:bold;--shiki-default:#2A3439;--shiki-default-font-weight:bold;--shiki-dark:#F6F8F8;--shiki-dark-font-weight:bold}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 .sq0K3, html code.shiki .sq0K3{--shiki-light:#182025;--shiki-light-font-weight:bold;--shiki-default:#182025;--shiki-default-font-weight:bold;--shiki-dark:#E6EBEC;--shiki-dark-font-weight:bold}html pre.shiki code .sBd0f, html code.shiki .sBd0f{--shiki-light:#4F5D63;--shiki-light-font-weight:bold;--shiki-default:#4F5D63;--shiki-default-font-weight:bold;--shiki-dark:#A3AFB4;--shiki-dark-font-weight:bold}html pre.shiki code .sKllz, html code.shiki .sKllz{--shiki-light:#4F5D63;--shiki-light-font-style:italic;--shiki-default:#4F5D63;--shiki-default-font-style:italic;--shiki-dark:#A3AFB4;--shiki-dark-font-style:italic}",{"title":168,"searchDepth":207,"depth":207,"links":2508},[2509,2510,2513,2514,2515,2518,2519,2520,2521],{"id":157,"depth":207,"text":158},{"id":423,"depth":207,"text":424,"children":2511},[2512],{"id":785,"depth":214,"text":786},{"id":1092,"depth":207,"text":1093},{"id":1653,"depth":207,"text":1654},{"id":1927,"depth":207,"text":1928,"children":2516},[2517],{"id":2091,"depth":214,"text":2092},{"id":2147,"depth":207,"text":2148},{"id":2220,"depth":207,"text":2221},{"id":2339,"depth":207,"text":2340},{"id":2474,"depth":207,"text":2475},"md",{},{"title":40,"description":41},"docs\u002Fscopes","di2VLfyoA03fKCVItUYFKtdNm_MAXnQgtQZQ2Q_r42Y",1792845119469]