Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# NOW -- Skills and crons as first-class .t27 specs (2026-09-09)

## Skills and crons as first-class .t27 specs (Closes #3544)

- `specs/skills/` (26), `specs/crons/` (33) and `specs/i18n/` (1 contract): one `.t27` module per Claude Code skill and per scheduled job that t27.ai publishes. Every constant is `pub const`; the schema is in each README. Until now both catalogs on the site came only from code scans (`SKILL.md` frontmatter, workflow YAML, `setInterval` sites); the spec now states the thing and the code is its witness.
- The site (gHashTag/trinity#975) vendors a byte-identical copy under `apps/website/public/t27/files/specs/` and generates `public/skills/spec-skills.json` and `public/crons/spec-crons.json` from it through the compiler wasm at build time -- no regex over `.t27`. Duplicate `ID` fails the build; a `RUNS` id with no skill spec is `health: fail`; code with no spec is listed as `code-only`, never invented.
- Measured: **59/59** typecheck ok under the vendored wasm (sha256 `4d9c0447b5ca2887...`), nothing discarded. That `typecheck.ok` is lenient -- it stays true for `str = 5` and for a `u16` of 70000 -- so the schema check in the generator is the gate that holds; typecheck ok is necessary, not sufficient.
- Language: the specs are English-only (LANG-EN). The first cut carried a `SUMMARY_RU` and CI's `bootstrap/build.rs` rejected it. Translations are now connected through a spec: `specs/i18n/agents-ru.t27` (module `i18n_agents_ru`, `KIND = "i18n"`) declares the locale, `SCOPE` (`specs/skills`, `specs/crons`), the translatable `FIELDS` (`SUMMARY`, `NAME`), the bundle location (`trinity:apps/website/i18n/agents.ru.json`, keyed by `ID`), `FALLBACK = "en"`, `COVERAGE_REQUIRED = false`, `ORPHANS_ALLOWED = false`. The Cyrillic payload lives only in that bundle. One file per locale; the site's generator discovers `specs/i18n/*.t27` and hardcodes no locale list. Coverage measured on the site: ru 26/26 skills, 33/33 crons, 0 orphans.
- Not claimed: the bootstrap compiler on `master` had not been run against these files before this PR's CI; every `RUNS` is `[]` with a `RUNS_NOTE`, so no cron-to-skill edge is asserted; `gate-topology` and `untrusted-input` fail on `master` for already-merged PRs #3504/#3506 too and are not addressed here.
- `specs/OWNERS.md` gains the row `skills/`, `crons/`, `i18n/` -> **T-Queen**.
1 change: 1 addition & 0 deletions specs/OWNERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
| `vsa/`, `nn/` | **N-Numeric** / ML adjacent | Bundles and attention specs |
| `demos/`, `sandbox/` | **A-Architect** / **Q-QA** | Examples; not ring gold by default |
| `isa/` | **C-Compiler** | Register alphabet |
| `skills/`, `crons/`, `i18n/` | **T-Queen** | Agent skills and scheduled jobs as `.t27` specs, plus the per-locale translation contracts (`i18n/agents-<locale>.t27`) that point at the site's text bundles; the site (gHashTag/trinity) generates its catalogs from a vendored copy |

Each subtree with substantial churn should keep a local **`OWNERS.md`** (see below).

Expand Down
26 changes: 26 additions & 0 deletions specs/crons/999-multibots-telegraf-agent-autopilot-L1118.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: Apache-2.0
; specs/crons/999-multibots-telegraf-agent-autopilot-L1118.t27 -- cron timer/999-multibots-telegraf/agent-autopilot-L1118
; Source of truth for the job card on t27.ai (#/crons?cron=timer/999-multibots-telegraf/agent-autopilot-L1118).
; The schedule was read from 999-multibots-telegraf:apps/vibee-editor/render/scripts/agent-autopilot.ts#L1118.
; A cron CONTROLS skills: RUNS names the skill IDs it launches. It is empty
; unless the source evidently invokes one -- nothing here is inferred from a name.
; phi^2 + 1/phi^2 = 3 | TRINITY

module cron_999_multibots_telegraf_agent_autopilot_L1118;

pub const KIND : str = "cron";
pub const ID : str = "timer/999-multibots-telegraf/agent-autopilot-L1118";
pub const NAME : str = "agent-autopilot-L1118";
pub const HOST : str = "timer";
pub const REPO : str = "999-multibots-telegraf";
pub const SERVICE : str = "apps/vibee-editor/render/scripts/agent-autopilot.ts:1118";
pub const SUMMARY_EN : str = "Runs one autopilot cycle -- the generation pass and then the channel tick -- and skips the tick entirely when the previous cycle is still running.";
; A setInterval inside a long-lived process: a period, not a cron expression.
pub const INTERVAL_MS : u32 = 1800000;
pub const TZ : str = "UTC";
pub const RUNS : [0]str = [];
pub const RUNS_NOTE : str = "no skill invocation found in source";
pub const ENABLED : bool = true;
pub const NOTE : str = "Daemon mode only, under --loop or AUTOPILOT_LOOP=1; AUTOPILOT_INTERVAL_MS overrides the thirty-minute period, with a one-minute floor.";
pub const ON_FAILURE : str = "log";
pub const CONTROL : str = "code-only";
26 changes: 26 additions & 0 deletions specs/crons/999-multibots-telegraf-bot-owner-billing-L474.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: Apache-2.0
; specs/crons/999-multibots-telegraf-bot-owner-billing-L474.t27 -- cron timer/999-multibots-telegraf/bot-owner-billing-L474
; Source of truth for the job card on t27.ai (#/crons?cron=timer/999-multibots-telegraf/bot-owner-billing-L474).
; The schedule was read from 999-multibots-telegraf:src/services/bot-owner-billing.ts#L474.
; A cron CONTROLS skills: RUNS names the skill IDs it launches. It is empty
; unless the source evidently invokes one -- nothing here is inferred from a name.
; phi^2 + 1/phi^2 = 3 | TRINITY

module cron_999_multibots_telegraf_bot_owner_billing_L474;

pub const KIND : str = "cron";
pub const ID : str = "timer/999-multibots-telegraf/bot-owner-billing-L474";
pub const NAME : str = "bot-owner-billing-L474";
pub const HOST : str = "timer";
pub const REPO : str = "999-multibots-telegraf";
pub const SERVICE : str = "src/services/bot-owner-billing.ts:474";
pub const SUMMARY_EN : str = "Recomputes every bot owner's debt from the payments ledger, warns them at the soft, warning and critical thresholds, and disables a bot that stays critical for three days.";
; A setInterval inside a long-lived process: a period, not a cron expression.
pub const INTERVAL_MS : u32 = 86400000;
pub const TZ : str = "UTC";
pub const RUNS : [0]str = [];
pub const RUNS_NOTE : str = "no skill invocation found in source";
pub const ENABLED : bool = true;
pub const NOTE : str = "The first run after a restart is skipped on purpose, and an owner whose payments cannot be read is left alone rather than accused.";
pub const ON_FAILURE : str = "log";
pub const CONTROL : str = "code-only";
25 changes: 25 additions & 0 deletions specs/crons/999-multibots-telegraf-check-stuck-trainings.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: Apache-2.0
; specs/crons/999-multibots-telegraf-check-stuck-trainings.t27 -- cron inngest/999-multibots-telegraf/check-stuck-trainings
; Source of truth for the job card on t27.ai (#/crons?cron=inngest/999-multibots-telegraf/check-stuck-trainings).
; The schedule was read from 999-multibots-telegraf:src/inngest_app/functions/training/checkStuckTrainings.ts#L53.
; A cron CONTROLS skills: RUNS names the skill IDs it launches. It is empty
; unless the source evidently invokes one -- nothing here is inferred from a name.
; phi^2 + 1/phi^2 = 3 | TRINITY

module cron_999_multibots_telegraf_check_stuck_trainings;

pub const KIND : str = "cron";
pub const ID : str = "inngest/999-multibots-telegraf/check-stuck-trainings";
pub const NAME : str = "check-stuck-trainings";
pub const HOST : str = "inngest";
pub const REPO : str = "999-multibots-telegraf";
pub const SERVICE : str = "src/inngest_app/functions/training/checkStuckTrainings.ts";
pub const SUMMARY_EN : str = "Finds model trainings still marked pending or processing past the stuck threshold and reconciles them against Replicate.";
pub const SCHEDULE : str = "*/30 * * * *";
pub const TZ : str = "UTC";
pub const RUNS : [0]str = [];
pub const RUNS_NOTE : str = "no skill invocation found in source";
pub const ENABLED : bool = true;
pub const NOTE : str = "Records whose Replicate id is still a placeholder are skipped, because there is nothing on the provider to ask about.";
pub const ON_FAILURE : str = "log";
pub const CONTROL : str = "inngest-dashboard";
25 changes: 25 additions & 0 deletions specs/crons/999-multibots-telegraf-ci.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: Apache-2.0
; specs/crons/999-multibots-telegraf-ci.t27 -- cron github-actions/999-multibots-telegraf/ci
; Source of truth for the job card on t27.ai (#/crons?cron=github-actions/999-multibots-telegraf/ci).
; The schedule was read from 999-multibots-telegraf:.github/workflows/ci.yml#L10.
; A cron CONTROLS skills: RUNS names the skill IDs it launches. It is empty
; unless the source evidently invokes one -- nothing here is inferred from a name.
; phi^2 + 1/phi^2 = 3 | TRINITY

module cron_999_multibots_telegraf_ci;

pub const KIND : str = "cron";
pub const ID : str = "github-actions/999-multibots-telegraf/ci";
pub const NAME : str = "CI/CD Pipeline";
pub const HOST : str = "github-actions";
pub const REPO : str = "999-multibots-telegraf";
pub const SERVICE : str = ".github/workflows/ci.yml";
pub const SUMMARY_EN : str = "Runs the whole build, type-check and test pipeline once a day so a dependency that rots without a push still gets caught.";
pub const SCHEDULE : str = "0 2 * * *";
pub const TZ : str = "UTC";
pub const RUNS : [0]str = [];
pub const RUNS_NOTE : str = "no skill invocation found in source";
pub const ENABLED : bool = true;
pub const NOTE : str = "GitHub Actions on this repository have not run since 2026-06-02 (billing); the schedule stands in the file but does not fire.";
pub const ON_FAILURE : str = "log";
pub const CONTROL : str = "code-only";
26 changes: 26 additions & 0 deletions specs/crons/999-multibots-telegraf-crmProactive-L375.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: Apache-2.0
; specs/crons/999-multibots-telegraf-crmProactive-L375.t27 -- cron timer/999-multibots-telegraf/crmProactive-L375
; Source of truth for the job card on t27.ai (#/crons?cron=timer/999-multibots-telegraf/crmProactive-L375).
; The schedule was read from 999-multibots-telegraf:src/services/crmProactive.ts#L375.
; A cron CONTROLS skills: RUNS names the skill IDs it launches. It is empty
; unless the source evidently invokes one -- nothing here is inferred from a name.
; phi^2 + 1/phi^2 = 3 | TRINITY

module cron_999_multibots_telegraf_crmProactive_L375;

pub const KIND : str = "cron";
pub const ID : str = "timer/999-multibots-telegraf/crmProactive-L375";
pub const NAME : str = "crmProactive-L375";
pub const HOST : str = "timer";
pub const REPO : str = "999-multibots-telegraf";
pub const SERVICE : str = "src/services/crmProactive.ts:375";
pub const SUMMARY_EN : str = "Runs one proactive seller turn for the owner: walks the CRM queue and puts the next draft card in front of them without being asked.";
; A setInterval inside a long-lived process: a period, not a cron expression.
pub const INTERVAL_MS : u32 = 1800000;
pub const TZ : str = "UTC";
pub const RUNS : [0]str = [];
pub const RUNS_NOTE : str = "no skill invocation found in source";
pub const ENABLED : bool = true;
pub const NOTE : str = "The period is CRM_PROACTIVE_MINUTES from src/index.ts, thirty minutes by default; the first sweep waits two minutes after start.";
pub const ON_FAILURE : str = "log";
pub const CONTROL : str = "code-only";
25 changes: 25 additions & 0 deletions specs/crons/999-multibots-telegraf-daily-sales-advisor.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: Apache-2.0
; specs/crons/999-multibots-telegraf-daily-sales-advisor.t27 -- cron inngest/999-multibots-telegraf/daily-sales-advisor
; Source of truth for the job card on t27.ai (#/crons?cron=inngest/999-multibots-telegraf/daily-sales-advisor).
; The schedule was read from 999-multibots-telegraf:src/inngest_app/functions/analytics/dailySalesAdvisor.ts#L56.
; A cron CONTROLS skills: RUNS names the skill IDs it launches. It is empty
; unless the source evidently invokes one -- nothing here is inferred from a name.
; phi^2 + 1/phi^2 = 3 | TRINITY

module cron_999_multibots_telegraf_daily_sales_advisor;

pub const KIND : str = "cron";
pub const ID : str = "inngest/999-multibots-telegraf/daily-sales-advisor";
pub const NAME : str = "daily-sales-advisor";
pub const HOST : str = "inngest";
pub const REPO : str = "999-multibots-telegraf";
pub const SERVICE : str = "src/inngest_app/functions/analytics/dailySalesAdvisor.ts";
pub const SUMMARY_EN : str = "Builds every bot owner a morning report of yesterday's revenue, the week's costs and which services their users actually bought.";
pub const SCHEDULE : str = "0 9 * * *";
pub const TZ : str = "UTC";
pub const RUNS : [0]str = [];
pub const RUNS_NOTE : str = "no skill invocation found in source";
pub const ENABLED : bool = true;
pub const NOTE : str = "One report per owner across all of their bots, assembled from the payments ledger rather than from a balance column.";
pub const ON_FAILURE : str = "log";
pub const CONTROL : str = "inngest-dashboard";
25 changes: 25 additions & 0 deletions specs/crons/999-multibots-telegraf-generate-jobs-L164.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: Apache-2.0
; specs/crons/999-multibots-telegraf-generate-jobs-L164.t27 -- cron timer/999-multibots-telegraf/generate-jobs-L164
; Source of truth for the job card on t27.ai (#/crons?cron=timer/999-multibots-telegraf/generate-jobs-L164).
; The schedule was read from 999-multibots-telegraf:apps/vibee-editor/render/generate-jobs.ts#L164.
; A cron CONTROLS skills: RUNS names the skill IDs it launches. It is empty
; unless the source evidently invokes one -- nothing here is inferred from a name.
; phi^2 + 1/phi^2 = 3 | TRINITY

module cron_999_multibots_telegraf_generate_jobs_L164;

pub const KIND : str = "cron";
pub const ID : str = "timer/999-multibots-telegraf/generate-jobs-L164";
pub const NAME : str = "generate-jobs-L164";
pub const HOST : str = "timer";
pub const REPO : str = "999-multibots-telegraf";
pub const SERVICE : str = "apps/vibee-editor/render/generate-jobs.ts:164";
pub const SUMMARY_EN : str = "Sweeps finished generation jobs out of memory an hour after they end, once a caller who lost the connection has had time to collect the result.";
; A setInterval inside a long-lived process: a period, not a cron expression.
pub const INTERVAL_MS : u32 = 300000;
pub const TZ : str = "UTC";
pub const RUNS : [0]str = [];
pub const RUNS_NOTE : str = "no skill invocation found in source";
pub const ENABLED : bool = true;
pub const ON_FAILURE : str = "log";
pub const CONTROL : str = "code-only";
24 changes: 24 additions & 0 deletions specs/crons/999-multibots-telegraf-health-check.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
; specs/crons/999-multibots-telegraf-health-check.t27 -- cron inngest/999-multibots-telegraf/health-check
; Source of truth for the job card on t27.ai (#/crons?cron=inngest/999-multibots-telegraf/health-check).
; The schedule was read from 999-multibots-telegraf:src/inngest_app/functions/monitoring/criticalErrorMonitor.ts#L243.
; A cron CONTROLS skills: RUNS names the skill IDs it launches. It is empty
; unless the source evidently invokes one -- nothing here is inferred from a name.
; phi^2 + 1/phi^2 = 3 | TRINITY

module cron_999_multibots_telegraf_health_check;

pub const KIND : str = "cron";
pub const ID : str = "inngest/999-multibots-telegraf/health-check";
pub const NAME : str = "health-check";
pub const HOST : str = "inngest";
pub const REPO : str = "999-multibots-telegraf";
pub const SERVICE : str = "src/inngest_app/functions/monitoring/criticalErrorMonitor.ts";
pub const SUMMARY_EN : str = "Probes the main API and the Inngest service every half hour and reports what answered.";
pub const SCHEDULE : str = "*/30 * * * *";
pub const TZ : str = "UTC";
pub const RUNS : [0]str = [];
pub const RUNS_NOTE : str = "no skill invocation found in source";
pub const ENABLED : bool = true;
pub const ON_FAILURE : str = "log";
pub const CONTROL : str = "inngest-dashboard";
24 changes: 24 additions & 0 deletions specs/crons/999-multibots-telegraf-log-monitor.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
; specs/crons/999-multibots-telegraf-log-monitor.t27 -- cron inngest/999-multibots-telegraf/log-monitor
; Source of truth for the job card on t27.ai (#/crons?cron=inngest/999-multibots-telegraf/log-monitor).
; The schedule was read from 999-multibots-telegraf:src/inngest_app/functions/monitoring/logMonitor.ts#L360.
; A cron CONTROLS skills: RUNS names the skill IDs it launches. It is empty
; unless the source evidently invokes one -- nothing here is inferred from a name.
; phi^2 + 1/phi^2 = 3 | TRINITY

module cron_999_multibots_telegraf_log_monitor;

pub const KIND : str = "cron";
pub const ID : str = "inngest/999-multibots-telegraf/log-monitor";
pub const NAME : str = "log-monitor";
pub const HOST : str = "inngest";
pub const REPO : str = "999-multibots-telegraf";
pub const SERVICE : str = "src/inngest_app/functions/monitoring/logMonitor.ts";
pub const SUMMARY_EN : str = "Reads the application logs once a day, has a model summarise the errors and warnings, and sends the digest to Telegram.";
pub const SCHEDULE : str = "0 10 * * *";
pub const TZ : str = "UTC";
pub const RUNS : [0]str = [];
pub const RUNS_NOTE : str = "no skill invocation found in source";
pub const ENABLED : bool = true;
pub const ON_FAILURE : str = "log";
pub const CONTROL : str = "inngest-dashboard";
26 changes: 26 additions & 0 deletions specs/crons/999-multibots-telegraf-notificationHandler-L318.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: Apache-2.0
; specs/crons/999-multibots-telegraf-notificationHandler-L318.t27 -- cron timer/999-multibots-telegraf/notificationHandler-L318
; Source of truth for the job card on t27.ai (#/crons?cron=timer/999-multibots-telegraf/notificationHandler-L318).
; The schedule was read from 999-multibots-telegraf:src/handlers/notificationHandler.ts#L318.
; A cron CONTROLS skills: RUNS names the skill IDs it launches. It is empty
; unless the source evidently invokes one -- nothing here is inferred from a name.
; phi^2 + 1/phi^2 = 3 | TRINITY

module cron_999_multibots_telegraf_notificationHandler_L318;

pub const KIND : str = "cron";
pub const ID : str = "timer/999-multibots-telegraf/notificationHandler-L318";
pub const NAME : str = "notificationHandler-L318";
pub const HOST : str = "timer";
pub const REPO : str = "999-multibots-telegraf";
pub const SERVICE : str = "src/handlers/notificationHandler.ts:318";
pub const SUMMARY_EN : str = "Drains the notification queue and delivers the Telegram messages waiting in it.";
; A setInterval inside a long-lived process: a period, not a cron expression.
pub const INTERVAL_MS : u32 = 60000;
pub const TZ : str = "UTC";
pub const RUNS : [0]str = [];
pub const RUNS_NOTE : str = "no skill invocation found in source";
pub const ENABLED : bool = true;
pub const NOTE : str = "Wrapped in an exclusive tick, so two bot processes cannot deliver the same notification twice.";
pub const ON_FAILURE : str = "log";
pub const CONTROL : str = "code-only";
25 changes: 25 additions & 0 deletions specs/crons/999-multibots-telegraf-notificationHandler-L324.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: Apache-2.0
; specs/crons/999-multibots-telegraf-notificationHandler-L324.t27 -- cron timer/999-multibots-telegraf/notificationHandler-L324
; Source of truth for the job card on t27.ai (#/crons?cron=timer/999-multibots-telegraf/notificationHandler-L324).
; The schedule was read from 999-multibots-telegraf:src/handlers/notificationHandler.ts#L324.
; A cron CONTROLS skills: RUNS names the skill IDs it launches. It is empty
; unless the source evidently invokes one -- nothing here is inferred from a name.
; phi^2 + 1/phi^2 = 3 | TRINITY

module cron_999_multibots_telegraf_notificationHandler_L324;

pub const KIND : str = "cron";
pub const ID : str = "timer/999-multibots-telegraf/notificationHandler-L324";
pub const NAME : str = "notificationHandler-L324";
pub const HOST : str = "timer";
pub const REPO : str = "999-multibots-telegraf";
pub const SERVICE : str = "src/handlers/notificationHandler.ts:324";
pub const SUMMARY_EN : str = "Deletes notification messages old enough that nobody is going to read them.";
; A setInterval inside a long-lived process: a period, not a cron expression.
pub const INTERVAL_MS : u32 = 3600000;
pub const TZ : str = "UTC";
pub const RUNS : [0]str = [];
pub const RUNS_NOTE : str = "no skill invocation found in source";
pub const ENABLED : bool = true;
pub const ON_FAILURE : str = "log";
pub const CONTROL : str = "code-only";
Loading
Loading