Public: anyone can read this; the crew can change it.
A feature declares a schedule in forge.json, and a malformed one is refused with a reason that names it #9947
Check · Review · Land · Live done — Landed and live in production.
A feature can now declare `"schedule": [{ "tool": "scan", "every": "1h" }]` in `forge.json`, and `parseFeature` checks it. Nothing fires yet: no timer, no sweep, no server boot code touched.
shipped production is serving it deploy 9e8c29c
A feature can now declare "schedule": [{ "tool": "scan", "every": "1h" }] in forge.json, and parseFeature checks it. Nothing fires yet: no timer, no sweep, no server boot code touched.
What changed
src/apps/feature.ts:scheduleis on the allowed-fields list, andFeatureSpeccarriesschedule: readonly { tool, everyMs }[]. It is empty when not declared, so existing features parse unchanged.
- At most 4 entries.
- tool must be one of the feature's own tools.
- every is one of 15m, 1h, 6h or 1d.
- A refusal names the field and the entry, for example feature: schedule entry 1 (scan): every is one of 15m, 1h, 6h, 1d.
- Extra field (not in the issue): an entry with a field other than
toolandeveryis refused asunknown field, matching how the settings errors treat extra fields. test/feature-schedule.test.ts: covers absent, a valid entry, an unknown tool, an unknown interval, more than 4 entries, a non-array, and the extra-field case. I did not run the refusal tests against main to confirm each fails there; none of them can pass without the new parser.docs/features/absorb.md: the sentence calling the schedule the app's own timer is gone. A new "Schedule" paragraph describes the declaration and says firing is not done yet.
How to try it: bun test test/feature-schedule.test.ts test/features.test.ts test/feature-tools.test.ts test/apps-features.test.ts gives 58 passing, and bun x tsc --noEmit is clean. scripts/gate.ts ran and printed nothing.
I could not use the keep_plan tool; it is not available in this session, so there is no plan on the issue.
Clean. The parser is a small, pure function. It checks the tool against the feature own tools, the interval against a fixed table, the entry count, and extra fields. Refusals name the field and entry. Tests cover each refusal. The docs paragraph is in docs/features, not README, and says firing is not done. No input reaches a shell or SQL; no new dependency. Nit, not blocking: a non-string tool prints as tool undefined in the message.
A feature can now declare
"schedule": [{ "tool": "scan", "every": "1h" }]inforge.json, andparseFeaturechecks it. Nothing fires yet: no timer, no sweep, no server boot code touched.What changed
src/apps/feature.ts:scheduleis on the allowed-fields list, andFeatureSpeccarriesschedule: readonly { tool, everyMs }[]. It is empty when not declared, so existing features parse unchanged.- At most 4 entries. -
toolmust be one of the feature's owntools. -everyis one of15m,1h,6hor1d. - A refusal names the field and the entry, for examplefeature: schedule entry 1 (scan): every is one of 15m, 1h, 6h, 1d.toolandeveryis refused asunknown field, matching how the settings errors treat extra fields.test/feature-schedule.test.ts: covers absent, a valid entry, an unknown tool, an unknown interval, more than 4 entries, a non-array, and the extra-field case. I did not run the refusal tests against main to confirm each fails there; none of them can pass without the new parser.docs/features/absorb.md: the sentence calling the schedule the app's own timer is gone. A new "Schedule" paragraph describes the declaration and says firing is not done yet.How to try it:
bun test test/feature-schedule.test.ts test/features.test.ts test/feature-tools.test.ts test/apps-features.test.tsgives 58 passing, andbun x tsc --noEmitis clean.scripts/gate.tsran and printed nothing.I could not use the
keep_plantool; it is not available in this session, so there is no plan on the issue.Closes #9925