Schedule Tune
Purpose
Claude edits schedule-config.json on the user's behalf. Regular users shouldn't be editing JSON with specific schema rules — they describe what they want, Claude does the translation.
The skill is defensive: it validates every change, shows the exact before/after diff, and refuses to write anything malformed. If the user's intent is ambiguous, it asks a clarifying question rather than guessing.
Preconditions
.claude/quality/schedule-config.jsonmust exist. If it does not, stop and say: "No schedule config found. If you haven't set up BCOS scheduling yet, runcontext-onboardingStep 6. If you think config should exist, check you're in the right repo."- The file must parse as JSON. If malformed, stop and say: "schedule-config.json is malformed. I can repair it from the template — want me to reset it to defaults?" — if yes, copy from
.claude/quality/schedule-config.template.json, strip_comment/_aboutfields, write.
Supported intents
Recognise the user utterance and map to a structured edit.
1. Change a job's schedule
Utterances: "run audit twice a week", "change daydream to every 3 days", "switch architecture review to quarterly", "run index-health on weekdays only", "audit on Tuesdays and Fridays"
Edit: jobs.{job}.schedule = <new value>
Value resolution:
- Simple weekday → use shortcut (
"mon","tue"etc.) - "daily" / "every day" →
"daily" - "weekdays" / "Monday through Friday" →
"weekdays" - "weekly" alone → ambiguous, ask which day (default offered: current value if it's already a single weekday, else Monday)
- "twice a week" → ask which days (offer Tue+Fri default), resolve to
"tue,fri"shorthand (if config supports comma-separated) or raw cron"0 9 * * 2,5" - "N times a week" with N>2 → offer specific day set
- "every N days" / "every other day" →
"every-{N}d" - "monthly" →
"1st"(default 1st of month) - "quarterly" → raw cron
"0 9 1 */3 *"(first of every 3rd month) - "never" / "off" / "disable this" →
"off"OR setenabled: false— ask user which they mean. Disabling viaenabled: falsekeeps config;"off"in schedule also works but is less intuitive. Default toenabled: falseunless user insists on schedule-based off. - Raw cron string — accept as-is after validation
Ambiguity rule: if the natural-language time has more than one valid interpretation, use AskUserQuestion with specific options. Never guess.
2. Enable / disable a job
Utterances: "turn off the deep daydream", "disable architecture review", "turn index-health back on"
Edit: jobs.{job}.enabled = false | true
Edge case: if the user says "turn off X" and X is already off, say so and do nothing: "daydream-deep is already disabled."
3. Change the dispatcher time
Utterances: "run the dispatcher at 08:30", "move morning maintenance to 07:00"
This is TWO edits:
- Update
dispatcher.time_hintin config (informational) - Update the actual scheduled task's cron via
mcp__scheduled-tasks__update_scheduled_task— that's what actually moves the run
Derive the cron from the time: "08:30" → "30 8 * * *". Use the existing task ID bcos-{project} (discover by listing tasks filtered for prefix bcos-).
If the scheduled task doesn't exist, the time_hint update is pointless — say: "No bcos dispatcher task found for this repo. Want me to create one?" (offers to invoke context-onboarding Step 6.)
4. Auto-fix whitelist changes
Utterances: "stop auto-fixing trailing whitespace", "enable auto-fix for broken cross-references", "turn off all auto-fixes", "add eof-newline to auto-fix"
Edit: auto_fix.whitelist += [id] or auto_fix.whitelist -= [id]. Or auto_fix.enabled = false for "turn off all".
Validation: the fix ID must appear in .claude/skills/schedule-dispatcher/references/auto-fix-whitelist.md. If user names an ID that doesn't exist, list the supported IDs and ask which one they meant.
5. Digest destination changes
Utterances: "write the digest to a different file", "stop writing the digest file"
Edit: digest.path or digest.write_file.
Validation: path must be under docs/ (we do not let users direct the digest elsewhere). If they try to set it outside docs/, refuse and explain.
6. Tuning parameters
Utterances: "suggest less aggressively", "wait 5 green runs before suggesting reduction instead of 3"
Edit: tuning.suggest_reduce_after_green_runs (int ≥ 1), tuning.suggest_increase_if_findings_trending_up (bool).
7. Reset to defaults
Utterances: "reset maintenance schedule to defaults", "start over with the config"
Action: copy .claude/quality/schedule-config.template.json → .claude/quality/schedule-config.json. Strip _comment / _about fields during copy.
Always confirm before resetting. User's current customisations are wiped.
8. Read-only query
Utterances: "what's my maintenance schedule?", "when does audit run?", "is architecture-review enabled?"
Action: read config, summarise in plain language. Do NOT modify anything. Do NOT ask "want me to change it?" — that's for the user to initiate.
Example output: "Your current schedule: index-health daily, daydream-lessons Monday, daydream-deep Wednesday, audit-inbox Friday, architecture-review 1st of month. Dispatcher runs at 09:00 local. All jobs enabled."
The standard edit flow
Every change follows the same five-step pattern:
- Parse intent — which section, which key, what new value
- Validate — check against schema rules (see Validation Rules below)
- Propose — show the diff in human-readable form
- Confirm — use
AskUserQuestionwith Apply / Cancel (/ Change) options - Write — atomic replace, preserve
_comment/_aboutfields, preserve unrelated keys
The proposal format
Always show before→after, not just after. Use ✏️ for field edits, ✅ for enable, ❌ for disable, so the diff is scannable at a glance:
Proposed change to schedule-config.json:
✏️ jobs.audit-inbox.schedule:
"fri" → "tue,fri"
No other changes.
Enable/disable example:
Proposed change to schedule-config.json:
❌ jobs.daydream-deep.enabled:
true → false
No other changes.
If the change is on the scheduled task itself (time change):
Proposed changes:
Config file:
✏️ dispatcher.time_hint: "09:00" → "08:30"
Scheduled task bcos-leverage:
✏️ cronExpression: "0 9 * * *" → "30 8 * * *"
Both will be applied together.
Confirmation is not optional
Always ask. Even for trivial changes. The user saying "apply" once is faster than the user reversing an unintended change.
Exception: read-only queries (intent #8). Those produce output with no confirmation step.
Validation Rules
Reject any edit that violates these rules. Explain the rule when refusing.
schedule field
Valid values:
- Literal:
"daily","weekdays","weekends","off" - Weekday:
"mon","tue","wed","thu","fri","sat","sun" - Comma-separated weekday list:
"mon,wed,fri"(any non-empty subset) - Day-of-month:
"1st"through"31st", or"last" - Interval:
"every-Nd"where N ≥ 1 (e.g."every-2d","every-14d") - Raw cron: 5 whitespace-separated fields, parseable standard cron
Invalid: anything else. Empty strings, typos, non-existent days.
enabled field
Must be true or false. Not 1/0, not strings.
auto_fix.whitelist field
Each entry must be a known fix ID (defined in auto-fix-whitelist.md). Known IDs as of v1.2:
missing-last-updatedfrontmatter-field-ordertrailing-whitespaceeof-newlinebroken-xref-single-candidate
When a new fix ID is added to the framework, it becomes valid. Otherwise, reject unknown IDs with: "'{name}' is not a known auto-fix ID. Supported: {list}."
digest.path field
Mu