[{"data":1,"prerenderedAt":585},["ShallowReactive",2],{"docs-sections":3,"docs-page-\u002Fdocs\u002Fskill-verify":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":112,"body":125,"description":113,"extension":392,"group":101,"meta":579,"navigation":580,"order":581,"path":111,"seo":582,"stem":583,"__hash__":584},"docs\u002Fdocs\u002Fskill-verify.md",{"type":126,"value":127,"toc":572},"minimark",[128,131,138,143,160,167,171,182,377,380,384,387,522,534,538,548,552,558,563,568],[129,130,112],"h1",{"id":112},[132,133,134,137],"p",{},[135,136,112],"code",{}," checks that every value your app runs with has one home,\nthat every change goes through a checked save that records a revision, that\nlowering a limit cannot break what uses it, and that access stays your app's.\nIt reports every finding first, and changes nothing until you choose what to\nfix.",[139,140,142],"h2",{"id":141},"modes-and-scope","Modes and scope",[144,145,146,154],"ul",{},[147,148,149,153],"li",{},[150,151,152],"strong",{},"Check"," (the default) reports every finding, then fixes only the ones you\nname.",[147,155,156,159],{},[150,157,158],{},"Report only"," inspects and reports. It writes nothing and runs nothing\nthat changes files or stored values.",[132,161,162,163,166],{},"The scope is the whole app, or a named group, preset set, catalog, deployment\nvalue or path, or ",[150,164,165],{},"this branch",": the values its diff touches, inspected in\ntheir current code, not only the changed lines.",[139,168,170],{"id":169},"what-it-looks-for","What it looks for",[132,172,173,174,177,178,181],{},"It runs ",[135,175,176],{},"tunables check --json"," and, with a Product Model,\n",[135,179,180],{},"tunables model draft",", then searches the code for what no check can see:",[183,184,185,198],"table",{},[186,187,188],"thead",{},[189,190,191,195],"tr",{},[192,193,194],"th",{},"Finding",[192,196,197],{},"The rule it breaks",[199,200,201,216,226,236,246,256,266,276,286,296,314,324,334,347,357,367],"tbody",{},[189,202,203,209],{},[204,205,206],"td",{},[135,207,208],{},"check-failure",[204,210,211,212,215],{},"The definitions, ",[135,213,214],{},".env.example",", explanations and Product Model agree.",[189,217,218,223],{},[204,219,220],{},[135,221,222],{},"hard-coded-value",[204,224,225],{},"A value someone might tune is a setting, with its default in its definition.",[189,227,228,233],{},[204,229,230],{},[135,231,232],{},"two-homes",[204,234,235],{},"One home per value, and the definition is the one home of each default.",[189,237,238,243],{},[204,239,240],{},[135,241,242],{},"scope-bypass",[204,244,245],{},"A workspace's or a person's own value is the setting's override, read through its layers, not a table of the app's own with a precedence written by hand.",[189,247,248,253],{},[204,249,250],{},[135,251,252],{},"read-bypass",[204,254,255],{},"Settings are read through tunables, so code gets the stored, checked value.",[189,257,258,263],{},[204,259,260],{},[135,261,262],{},"stale-read",[204,264,265],{},"A setting applies while the app runs, without a restart.",[189,267,268,273],{},[204,269,270],{},[135,271,272],{},"write-bypass",[204,274,275],{},"Every change is one revision, through a checked save.",[189,277,278,283],{},[204,279,280],{},[135,281,282],{},"unguarded-dependent",[204,284,285],{},"Lowering a limit never breaks what uses it; the dependent is named.",[189,287,288,293],{},[204,289,290],{},[135,291,292],{},"unconfirmed-consequence",[204,294,295],{},"A change that costs money, redoes work or reaches people at once is confirmed first.",[189,297,298,303],{},[204,299,300],{},[135,301,302],{},"open-access",[204,304,305,306,309,310,313],{},"Access is the app's, decided on every request with ",[135,307,308],{},"actor"," and ",[135,311,312],{},"authorize","; an owner or member changes only their own scope.",[189,315,316,321],{},[204,317,318],{},[135,319,320],{},"boot-needs-database",[204,322,323],{},"Boot never connects to the database.",[189,325,326,331],{},[204,327,328],{},[135,329,330],{},"sync-missing",[204,332,333],{},"A new field is stored before code reads it.",[189,335,336,341],{},[204,337,338],{},[135,339,340],{},"env-drift",[204,342,343,344,346],{},"Every deployment value is declared once, read through its definition, and listed in ",[135,345,214],{},".",[189,348,349,354],{},[204,350,351],{},[135,352,353],{},"unexplained-field",[204,355,356],{},"A field explains itself in the product's words; with pdd-kit, from the Product Model.",[189,358,359,364],{},[204,360,361],{},[135,362,363],{},"unit-mismatch",[204,365,366],{},"A value is stored in the unit the person tunes in, and the read converts it correctly.",[189,368,369,374],{},[204,370,371],{},[135,372,373],{},"misplaced-value",[204,375,376],{},"Each value sits in the home its nature gives it.",[132,378,379],{},"Searches are leads: it reads each hit and confirms the rule is broken before it\ncounts it. A hit that is one of the documented exceptions is dropped.",[139,381,383],{"id":382},"the-report","The report",[132,385,386],{},"Findings are grouped by type and numbered across the whole report, so you can\nchoose by number:",[388,389,394],"pre",{"className":390,"code":391,"language":392,"meta":393,"style":393},"language-md shiki shiki-themes tunables-panel-light tunables-panel-light tunables-panel-dark","### two-homes (3)\n| # | Where | What | Rule broken | Fix |\n| - | ----- | ---- | ----------- | --- |\n| 4 | server\u002Fprojects.ts:41 | `maxProjects ?? 3` beside `tunables.get('limits')` | The definition is the one home of each default | Delete the fallback |\n","md","",[135,395,396,409,443,475],{"__ignoreMap":393},[397,398,401,405],"span",{"class":399,"line":400},"line",1,[397,402,404],{"class":403},"sBd0f","###",[397,406,408],{"class":407},"s4VG2"," two-homes (3)\n",[397,410,412,416,420,422,425,427,430,432,435,437,440],{"class":399,"line":411},2,[397,413,415],{"class":414},"sp9ci","|",[397,417,419],{"class":418},"sVz71"," # ",[397,421,415],{"class":414},[397,423,424],{"class":418}," Where ",[397,426,415],{"class":414},[397,428,429],{"class":418}," What ",[397,431,415],{"class":414},[397,433,434],{"class":418}," Rule broken ",[397,436,415],{"class":414},[397,438,439],{"class":418}," Fix ",[397,441,442],{"class":414},"|\n",[397,444,446,448,451,454,457,459,462,464,467,469,472],{"class":399,"line":445},3,[397,447,415],{"class":414},[397,449,450],{"class":414}," -",[397,452,453],{"class":414}," |",[397,455,456],{"class":414}," -----",[397,458,453],{"class":414},[397,460,461],{"class":414}," ----",[397,463,453],{"class":414},[397,465,466],{"class":414}," -----------",[397,468,453],{"class":414},[397,470,471],{"class":414}," ---",[397,473,474],{"class":414}," |\n",[397,476,478,480,483,485,488,490,493,497,500,503,505,508,510,512,515,517,520],{"class":399,"line":477},4,[397,479,415],{"class":414},[397,481,482],{"class":418}," 4 ",[397,484,415],{"class":414},[397,486,487],{"class":418}," server\u002Fprojects.ts:41 ",[397,489,415],{"class":414},[397,491,492],{"class":414}," `",[397,494,496],{"class":495},"sjCzm","maxProjects ?? 3",[397,498,499],{"class":414},"`",[397,501,502],{"class":418}," beside ",[397,504,499],{"class":414},[397,506,507],{"class":495},"tunables.get('limits')",[397,509,499],{"class":414},[397,511,453],{"class":414},[397,513,514],{"class":418}," The definition is the one home of each default ",[397,516,415],{"class":414},[397,518,519],{"class":418}," Delete the fallback ",[397,521,442],{"class":414},[132,523,524,525,528,529,533],{},"Each finding that needs a decision says what the decision is. The report ends\nwith the ",[135,526,527],{},"tunables check"," result and a count per type. A clean report says ",[530,531,532],"em",{},"no\nfindings in the inspected scope",", never that the app is correct, unless the\nwhole app was inspected.",[139,535,537],{"id":536},"fixing","Fixing",[132,539,540,541,544,545,547],{},"You name the findings to fix. While fixing, it routes a question of product\nmeaning through ",[135,542,543],{},"\u002Fpdd-ideate",", says what each environment's stored values need,\ndeletes the old home of any value it moves, and never weakens a check to make\na finding disappear. Then it runs ",[135,546,527],{},", your typecheck, lint and\ntests, and the searches again.",[139,549,551],{"id":550},"ask-for-it","Ask for it",[553,554,555],"blockquote",{},[132,556,557],{},"Verify our settings, report only.",[553,559,560],{},[132,561,562],{},"Check this branch's settings changes.",[553,564,565],{},[132,566,567],{},"Audit the limits group and fix what you find.",[569,570,571],"style",{},"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 .s4VG2, html code.shiki .s4VG2{--shiki-light:#115E59;--shiki-light-font-weight:bold;--shiki-default:#115E59;--shiki-default-font-weight:bold;--shiki-dark:#5EEAD4;--shiki-dark-font-weight:bold}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 .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);}",{"title":393,"searchDepth":411,"depth":411,"links":573},[574,575,576,577,578],{"id":141,"depth":411,"text":142},{"id":169,"depth":411,"text":170},{"id":382,"depth":411,"text":383},{"id":536,"depth":411,"text":537},{"id":550,"depth":411,"text":551},{},true,26,{"title":112,"description":113},"docs\u002Fskill-verify","6VdPMD0EWlCbaiCc8yVlMg0uHmzIpq-zHmjAtZq7xS4",1792845119761]