Skip to content

Skill Synthesis Settings

All 76 skillSynthesis.* keys live in ~/.ptah/settings.json (file-based settings, not package.json). Settings → Skill Synthesis edits the core thresholds, the budget, and most of the drain queue. The triggers, the four lane sub-trees, and the three phase-3 gates have no panel UI yet — edit those directly in the settings file.

Two independent tracks read these settings, and several keys only make sense once you know which track they belong to:

  • Track 1 — single-session, fully automated. A candidate that repeats successesToPromote (3) times, clears minJudgeScore (6.0) on a scored judge verdict, and either has no replay confidence or clears the replay floor, is promoted straight to ~/.ptah/skills/<slug>/ — no human gate.
  • Track 2 — cluster, human-in-the-loop. The curator clusters similar candidates (suggestionMinClusterSize), drafts one generalized skill, judges it, and inserts a pending suggestion under Skills → Recommended. It ships only when you accept it.
KeyDefaultWhat it does
skillSynthesis.enabledtrueMaster switch. First gate the drain checks; disabling it stops both detection and promotion.
skillSynthesis.successesToPromote3Successful repetitions before Track 1 promotes a candidate directly.
skillSynthesis.dedupCosineThreshold0.85Embedding similarity above which a trajectory is treated as a duplicate of an already-active skill.
skillSynthesis.maxActiveSkills200Residency cap. The weakest resident (by evictionDecayRate-weighted recency) is demoted to dormant, never deleted, when exceeded.
skillSynthesis.candidatesDir''Override location for promoted skills. '' means ~/.ptah/skills/.
skillSynthesis.eligibilityMinTurns5Minimum session turns for the prefilter’s depth-based acceptance path (paired with prefilterMinChars).
skillSynthesis.evictionDecayRate0.95Exponential decay (0–1) applied to invocation recency when ranking residents for maxActiveSkills eviction.
skillSynthesis.generalizationContextThreshold3Distinct-context count at which the promotion bar halves: ceil(successesToPromote / 2) instead of the full successesToPromote.
skillSynthesis.dedupClusterThreshold0.78Cosine distance for Track 2’s cluster-centroid grouping. Distinct from dedupCosineThreshold, which is Track 1’s active-skill dedup.
skillSynthesis.prefilterMinEdits1Minimum file edits for the prefilter’s edit-only acceptance path (accepted alone).
skillSynthesis.prefilterMinChars800Minimum transcript length, paired with eligibilityMinTurns, for the depth-based acceptance path.
skillSynthesis.prefilterMinToolUses2Minimum tool calls for the prefilter’s tool-heavy acceptance path (accepted alone).
skillSynthesis.judgeEnabledtrueWhether the LLM judge gate runs during promotion and the suggestion pass.
skillSynthesis.minJudgeScore6.0Minimum average judge score (0–10, across five criteria) a scored verdict must clear. The judge never fabricates a score — unscored and disabled verdicts are neither a pass nor a block, regardless of this value.
skillSynthesis.judgeModel'inherit'Legacy model resolution used where a per-lane model isn’t yet wired (still read by the skill enhancer). Superseded elsewhere by the judge lane below.
skillSynthesis.maxPinnedSkills10Cap on manually pinned skills; pinning past it throws rather than silently evicting another pin.
skillSynthesis.curatorEnabledtrueWhether the periodic Track 2 curator pass runs (clustering, suggestions, auto-enhancement).
skillSynthesis.curatorIntervalHours24How often the curator pass runs.
skillSynthesis.suggestionMinClusterSize2Similar candidates required before Track 2 drafts a Recommended suggestion.
skillSynthesis.suggestionMaxCandidates200Cap on candidates considered per curator pass.
skillSynthesis.trayKeepalivefalseElectron only. Keeps the tray process alive so scheduled drains can still run with the main window closed.

Not exposed in the settings panel — edit ~/.ptah/settings.json directly. These decide when a session gets queued for analysis, not whether it is promoted.

KeyDefaultWhat it does
skillSynthesis.triggers.sessionEndtrueQueue an analysis pass when a session ends.
skillSynthesis.triggers.idleMs600000Queue an analysis pass after this many idle milliseconds.
skillSynthesis.triggers.bootScantrueScan for un-analyzed sessions on host startup.
skillSynthesis.triggers.subagentStop.enabledtrueQueue an analysis pass when a subagent run stops.
skillSynthesis.triggers.postToolUse.enabledtrueQueue an analysis pass after a burst of tool use.
skillSynthesis.triggers.postToolUse.minEditCount3Edit count that counts as a “burst” for the trigger above.
skillSynthesis.triggers.maxAnalyzesPerHour6Rate limit on analysis passes triggered this way, per workspace.

Every trigger funnels into the same prefilter queue row — queuing is free (a cheap regex pass); the token spend starts at archaeology.

The durable SQLite queue that replaced inline session-end analysis. Gate order on every tick: skillSynthesis.enabled → daily token budget → battery → foreground backoff.

KeyDefaultWhat it does
skillSynthesis.drain.cronExpr'*/15 * * * *'Frequent-tier cron — fires 96×/day.
skillSynthesis.drain.nightlyCronExpr'0 3 * * *'Nightly-tier cron — fires once/day.
skillSynthesis.drain.weeklyCronExpr'0 4 * * 0'Weekly-tier cron — fires once/week. The three phase-3 gates only run on this tier.
skillSynthesis.drain.maxItemsPerRun4Frequent tier’s item cap per tick, taken in one round-robin round. The settings panel’s “Max items per run (frequent tier)” field is bound to this key alone — it governs neither of the two tiers below.
skillSynthesis.drain.nightlyMaxItemsPerRun40Nightly tier’s whole-day item supply, dealt out over repeated rounds. The settings panel has its own control for this tier, separate from the frequent-tier field above.
skillSynthesis.drain.weeklyMaxItemsPerRun400Weekly tier’s whole-week item supply, dealt out over repeated rounds. The settings panel has its own control for this tier too. Sized from a measured ~163 prefilter-eligible sessions/week × the two weekly rows (judge-panel, trigger-eval) chained off each.
skillSynthesis.drain.perWorkspaceBatch1Per-workspace fairness quantum per round. Must stay 1 for every tier — raising it re-creates the starvation bug it was set to fix.
skillSynthesis.drain.foregroundBackoffMs300000How recently you must have chatted before the drain skips a tick to avoid competing with foreground work. 0 disables this gate.
skillSynthesis.drain.pauseOnBatterytrueSkip draining while the host is on battery power.
skillSynthesis.drain.maxAttempts5Retry ceiling — applies to timeout failures only. An unresolvable-auth row stalls indefinitely and never counts against this ceiling; that’s a configuration fault meant to be fixed, not given up on.
skillSynthesis.drain.staleClaimTtlMs900000How long a claimed-but-not-heartbeated row waits before another worker reclaims it. Should stay at least 3× the longest lane timeoutMs.
KeyDefaultWhat it does
skillSynthesis.budget.maxTokensPerDay2000000Hard daily token ceiling across every background stage, checked once per drain tick and once per item. 0 means unlimited. This is the only real cost control in the section — the drain’s item caps above only throttle throughput.

The most valuable keys on this page. Every background LLM call — archaeologist, synthesis, judge, replay — runs on its own lane: a declared-capability record read from skillSynthesis.<lane>.<field>. This is what lets you move background learning off your foreground Anthropic quota onto Ollama, Z.AI, Moonshot, or OpenRouter, one lane at a time, without touching your active chat provider. No lane is provider-privileged — they differ only by these eight fields, so pointing judge at a local Ollama model behaves identically in kind to pointing it at Anthropic; only capability and latency change.

Same eight fields on every lane. Not exposed in the settings panel — edit ~/.ptah/settings.json directly, e.g. skillSynthesis.judge.provider.

FieldWhat it controls
providerRegistry provider id to run this lane on. '' (default) = inherit the active workspace provider — an install that never touches this behaves exactly as it did before lanes existed.
modelConcrete model id, a bare tier alias, or '' = fall back to judgeModel resolution.
defaultTierTier alias (haiku | sonnet | opus) used when model is '' and provider is set.
structuredOutputsdk (endpoint honours JSON-Schema constrained output) or parse (manual JSON extraction — required for endpoints that can’t).
toolUserequired or none. none collapses a multi-pass lane to a single pass instead of letting a model that can’t drive tools burn the whole timeout discovering that.
timeoutMsWall-clock budget for one LLM call on this lane.
maxInputCharsPrompt input budget in characters, applied per lane.
maxPassesUpper bound on retrieval passes. Only archaeologist exceeds 1.

Per-lane defaults:

LaneprovidermodeldefaultTierstructuredOutputtoolUsetimeoutMsmaxInputCharsmaxPasses
archaeologist''''haikusdkrequired120000120004
synthesis''''haikusdknone9000080001
judge''''haikusdknone4500030001
replay''''haikusdknone9000080001

Gates (skillSynthesis.replayValidation.*, skillSynthesis.triggerEval.*, skillSynthesis.judgePanel.*)

Section titled “Gates (skillSynthesis.replayValidation.*, skillSynthesis.triggerEval.*, skillSynthesis.judgePanel.*)”

Phase 3’s empirical measurements, all weekly-tier. Each carries its own enabled rather than sharing one switch, because their costs differ: replay and judge-panel each spend a lane call per candidate, while trigger-eval’s retrieval is local-embedding only. Not exposed in the settings panel — edit ~/.ptah/settings.json directly.

KeyDefaultWhat it does
skillSynthesis.replayValidation.enabledtrueToggles the replay-validation gate — grades a candidate by replaying it against one held-out cluster member. See the note below: this gate has no producer yet.
skillSynthesis.replayValidation.minConfidence0.5Floor (0–1, same scale as the stored replay_confidence) a replay must clear to count as corroborating evidence. NULL (never measured) is not “below threshold” — only a measured value below 0.5 fails.
skillSynthesis.triggerEval.enabledtrueToggles the zero-LLM trigger-retrieval eval. Spends embedding compute only, no LLM tokens.
skillSynthesis.judgePanel.enabledtrueToggles the two-panellist judge. The second panellist is shown the candidate’s nearest description neighbours and whatever gate results are already measured on its row, and is asked a different question from the first.
skillSynthesis.judgePanel.disagreementThreshold3Point gap (0–10 scale, same scale as minJudgeScore) between the two panellists’ headline scores that escalates to a third call.
  • Lowering successesToPromote makes Track 1 promote faster, with less evidence per skill. Nothing reviews a Track 1 promotion before it lands — pair a lower value with active review of the resulting skills.
  • Lowering minJudgeScore lets weaker skills through on both tracks. The judge never fails open, so an unavailable model blocks nothing regardless of this value — it just means fewer scored verdicts to compare against it.
  • maxActiveSkills is a soft governance cap; demotion is to dormant, never deletion.
  • Moving a lane off Anthropic is the highest-leverage change on this page: set skillSynthesis.<lane>.provider to a third-party id (ollama, z-ai, moonshot, openrouter) to take that stage off your foreground quota entirely. Start with archaeologist — it’s the most expensive lane (largest maxInputChars, up to 4 passes) and the one net-new to background analysis.
  • The daily budget, not the item caps, is what stops runaway spend. Tune skillSynthesis.budget.maxTokensPerDay first; the drain caps only decide how evenly that budget gets spent across a day.